File Coverage

blib/lib/CGI/Imagemap.pm
Criterion Covered Total %
statement 72 81 88.8
branch 25 40 62.5
condition 23 32 71.8
subroutine 10 10 100.0
pod 2 3 66.6
total 132 166 79.5


line stmt bran cond sub pod time code
1             package CGI::Imagemap;
2 2     2   51312 use strict;
  2         4  
  2         79  
3 2     2   12 use vars '$VERSION';
  2         3  
  2         2544  
4             $VERSION = 2.0;
5              
6             sub new {
7 2     2 0 50 return bless {
8             DEFAULT => undef,
9             ISMAP => [],
10             MINDIST => -1
11             }, shift;
12             }
13              
14             sub addmap {
15 3     3 1 7 my $self = shift;
16 3 50       13 if( $_[0] eq '-file' ){
17 0 0       0 open(my $map, $_[1]) || die("Unable to open map '$_[1]': $!");
18 0         0 @_ = grep {! /^\s*(?:#|$)/ } <$map>;
  0         0  
19 0         0 close($map);
20             }
21 3         5 push @{$self->{ISMAP}}, @_;
  3         17  
22             }
23              
24              
25             sub action {
26 8     8 1 17 my($self, $x, $y) = @_;
27              
28 8         14 $self->{MINDIST} = -1;
29            
30 8 50       23 die "No map specified" unless defined($self->{ISMAP});
31              
32 8         10 POINT: foreach ( @{$self->{ISMAP}} ){
  8         18  
33 31         82 my ($shape, $URI, $points) = split(/\s+/, $_, 3);
34 31 100 66     72 $self->{DEFAULT} ||= $URI if $shape =~ /default/i;
35 31 100 100     86 $self->_rect ($x, $y, $points) && return $URI if $shape =~ /rect/i;
36 30 100 100     75 $self->_poly ($x, $y, $points) && return $URI if $shape =~ /poly/i;
37 29 100 100     62 $self->_circle($x, $y, $points) && return $URI if $shape =~ /circle/i;
38 27 100 100     62 $self->_oval ($x, $y, $points) && return $URI if $shape =~ /oval/i;
39 26 100 66     76 $self->_point ($x, $y, $points) &&
40             ($self->{DEFAULT} = $URI) if $shape =~ /point/i;
41             }
42 3         14 return $self->{DEFAULT};
43             }
44              
45             # set default action if this point is the closest so far
46             # does not check for validity of parameters
47             sub _point {
48 8     8   11 my($self, $x, $y, $target) = @_;
49 8         14 my($dist2, $Tx, $Ty) = 0;
50              
51 8         33 ($Tx, $Ty) = $target =~ m/(\d+),\s*(\d+)/;
52 8         18 $dist2 = ($x - $Tx)**2 + ($y - $Ty)**2;
53              
54 8 100 100     31 if( $self->{MINDIST} == -1 || $dist2 < $self->{MINDIST} ){
55 7         9 $self->{MINDIST} = $dist2;
56 7         28 return 1;
57             }
58 1         4 return 0;
59             }
60              
61             # return true if point is in given rectangle
62             sub _rect {
63 7     7   10 my($self, $x, $y, $target) = @_;
64 7         36 my($ulx,$uly,$llx,$lly) = $target =~ m/(\d+),(\d+)\s+(\d+),(\d+)/;
65              
66 7   66     78 return ($x >= $ulx && $y >= $uly && $x <= $llx && $y <= $lly);
67             }
68              
69             sub _oval{
70 6     6   7 my($self, $x, $y, $target) = @_;
71 6         33 my($cx, $cy, $major, $minor) = $target =~ m/(\d+),(\d+)\s+(\d+),(\d+)/;
72              
73             # Ellipse equation: (x-$cx)^2/major^2 + (y-$cy)^2/minor^2 = 1
74             # If the point ($x,$y) plugged into the equation is > 1 =>
75             # pt is outside, if <=1, pt. is inside..
76 6         31 return (($x-$cx)**2/$major**2 + ($y-$cy)**2/$minor**2)<=1;
77             }
78              
79             # return true if point is in circle
80             sub _circle {
81 5     5   10 my($self, $x, $y, $target) = @_;
82 5         20 my($cx,$cy,$ex,$ey) = $target =~ m/(\d+),(\d+)\s+(\d+),(\d+)/;
83              
84 5         9 my($distanceP,$distanceE);
85              
86             # compare squares of distance from center of edgepoint and given point
87              
88 5         11 $distanceP = ($cx - $x)**2 + ($cy - $y)**2;
89 5         9 $distanceE = ($cx - $ex)**2 + ($cy - $ey)**2;
90              
91 5         22 return ($distanceP <= $distanceE);
92             }
93              
94             # return true if point is in given polygon
95             sub _poly {
96 3     3   7 my($self, $x, $y, $target) = @_;
97 3         5 my($pn, @px, @py) = 0;
98 3         5 my($i,$intersections,$dy,$dx,$b,$m,$x1,$y1,$x2,$y2);
99              
100             # We'll treat the test point as the origin, so translate each
101             # point in the polygon appropriately
102 3         16 while($target =~ s/\s*(\d+),(\d+),?//) {
103 9         20 $px[$pn] = $1 - $x;
104 9         16 $py[$pn] = $2 - $y;
105 9         37 $pn++;
106             }
107              
108             # A polygon with less than 3 points is an error
109 3 50       7 return 0 if $pn < 3;
110              
111             # Close the polygon
112 3         5 $px[$pn] = $px[0];
113 3         4 $py[$pn] = $py[0];
114              
115             # Now count the number of line segments in the polygon that intersect
116             # the left side of the X axis. If it's an odd number we are inside the
117             # polygon.
118              
119             # Assume no intersection
120 3         4 $intersections=0;
121              
122 3         8 for($i = 0; $i < $pn; $i++) {
123 9         9 $x1 = $px[$i ]; $y1 = $py[$i ];
  9         11  
124 9         10 $x2 = $px[$i+1]; $y2 = $py[$i+1];
  9         23  
125              
126             # Line is completely to the right of the Y axis
127 9 100 100     37 next if( ($x1>0) && ($x2>0) );
128              
129             # Line doesn't intersect the X axis at all
130 5 50 33     31 next if( (($y1<=>0)==($y2<=>0)) && (($y1!=0)&&($y2!=0)) );
      66        
131              
132             # Special case.. if the Y on the bottom=0, we ignore this intersection
133             # (otherwise a line endpoint counts as 2 hits instead of 1)
134 1 50       4 if( $y2>$y1 ){
    50          
135 0 0       0 next if $y2==0;
136             }
137             elsif( $y1>$y2 ){
138 1 50       3 next if $y1==0;
139             }
140             else {
141             # Horizontal span overlaying the X axis. Consider it an intersection
142             # iff. it extends into the left side of the X axis
143 0 0 0     0 $intersections++ if ( ($x1 < 0) || ($x2 < 0) );
144 0         0 next;
145             }
146              
147             # We know line must intersect the X axis, so see where
148 1         2 $dx = $x2 - $x1;
149              
150             # Special case.. if a vertical line, it intersects
151 1 50       3 unless ( $dx ) {
152 0         0 $intersections++;
153 0         0 next;
154             }
155              
156 1         1 $dy = $y2 - $y1;
157 1         2 $m = $dy / $dx;
158 1         3 $b = $y2 - $m * $x2;
159 1 50       3 next if ( ( (0 - $b) / $m ) > 0 );
160              
161 1         3 $intersections++;
162             }
163              
164             # If there were an odd number of intersections to the left of the origin
165             # (the clicked-on point) then it is within the polygon
166 3         18 return ($intersections % 2);
167             }
168              
169             1;
170              
171             __END__