This method works on the basis that the sum of the angles between lines drawn from the point under test, to each of the vertices of the polygon, will add up to 360 degrees, if the point is inside the polygon and be less than 360 if it is outside.
This is easy to visualise for convex polygons,less so for concave ones. The tests below seem to indicate that this also works for them too, but my math isn't strong enough to give a rigorous proof of this.
You'll see from the second group of results that points that lie exactly on an edge of the polygon--to the limits of perls floats to represent them accurately--are treated as being outside of it.
#! perl -slw
use strict;
use constant X => 0;
use constant Y => 1;
use constant PI => atan2(0,-1);
use constant TWOPI => 2*PI;
sub mapAdjPairs (&@) {
my $code = shift;
map { local ($a, $b) = (shift, $_[0]); $code->() } 0 .. @_-2;
}
sub Angle{
my ($x1, $y1, $x2, $y2) = @_;
my $dtheta = atan2($y1, $x1) - atan2($y2, $x2);
$dtheta -= TWOPI while $dtheta > PI;
$dtheta += TWOPI while $dtheta < - PI;
return $dtheta;
}
sub PtInPoly{
my ($poly, $pt) = @_;
my $angle=0;
mapAdjPairs{
$angle += Angle(
$a->[X] - $pt->[X],
$a->[Y] - $pt->[Y],
$b->[X] - $pt->[X],
$b->[Y] - $pt->[Y]
)
} @$poly, $poly->[0];
return !(abs($angle) < PI);
}
my @pts = ( [-1,1], [1,1], [1,-1], [-1,-1] );
print '[0,0] should be in the polygon:', PtInPoly( \@pts, [0,0]) ? 'It
+ is' : 'It is not';
print '[2,0] should be out of polygon:', PtInPoly( \@pts, [2,0]) ? 'It
+ is not' : 'It is';
my @diamond = ( [0,1], [1,2], [2,1], [1,0] );
print "[$_->[0],$_->[1]] is "
, PtInPoly(\@diamond, $_) ? 'inside' : 'outside'
for [0.49999999999,0.49999999999], [0.5,0.5], [0.50000000001,0.500
+00000001];
my @concave = ( [0,3],[3,0],[5,2],[4,3],[3,2],[2,3],[3,4],[2,5] );
print '[2,2] is ', PtInPoly(\@concave, [2,2]) ? 'inside' : 'outside';
print '[3,3] is ', PtInPoly(\@concave, [3,3]) ? 'inside' : 'outside';
print '[2,4] is ', PtInPoly(\@concave, [2,4]) ? 'inside' : 'outside';
print '[4,2] is ', PtInPoly(\@concave, [4,2]) ? 'inside' : 'outside';
__END__
C:\test>temp
[0,0] should be in the polygon:It is
[2,0] should be out of polygon:It is
[0.49999999999,0.49999999999] is outside
[0.5,0.5] is outside
[0.50000000001,0.50000000001] is inside
[2,2] is inside
[3,3] is outside
[2,4] is inside
[4,2] is inside
Examine what is said, not who speaks.
The 7th Rule of perl club is -- pearl clubs are easily damaged. Use a diamond club instead. |