Math-Geometry-IntersectionArea
view release on metacpan or search on metacpan
lib/Math/Geometry/IntersectionArea.pm view on Meta::CPAN
use warnings;
use Carp;
use 5.010;
use Math::Vector::Real;
require Exporter;
our @ISA = qw(Exporter);
our @EXPORT_OK = qw(intersection_area_circle_rectangle
intersection_area_circle_polygon);
sub _ia2_circle_segment { # ia2 = intersection area * 2
my ($r2, $a, $b) = @_;
# finds the oriented area of the intersection of the circle of
# radius sqrt(r2) centered on the origin O and the
# triangle(O,a,b).
# Cases:
#
# 0: /-----\
# / \
# | x---x |
#
#
# 1: /-----\
# / \ x---x
# | |
#
#
# 2: /-----\
# x---x / \
# | |
#
#
# 3: x---x
#
# /-----\
#
#
# 4: /-----\
# / \
# x-|-------|-x
#
#
# 5: /-----\
# / \
# x-|---x |
#
#
# 6: /-----\
# / \
# | x---|-x
#
# The two points where the segment intersects the circle are found
# solving the following cuadratic equation:
#
# norm(a + alfa * ab) = d
#
#
# The coeficientes c2, c1, c0 are deduced from the formula
# above such that:
#
# c2 * alfa**2 + 2 * c1 * alfa + c0 = 0
#
# And then the clasical formula for quadratic equation solved is
# used:
#
# alfa0 = 1/c2 + (-$c1 + sqrt($c1*$c1 - 4 * $c0 * $c2))
# alfa1 = 1/c2 + (-$c1 - sqrt($c1*$c1 - 4 * $c0 * $c2))
my $ab = $b - $a;
my $c2 = $ab->norm2 or return 0; # a and b are the same point
my $c1 = $a * $ab;
my $c0 = $a->norm2 - $r2;
my $discriminant = $c1 * $c1 - $c0 * $c2;
if ($discriminant > 0) { # otherwise case 3
my $inv_c2 = 1/$c2;
my $sqrt_discriminant = sqrt($discriminant);
my $alfa0 = $inv_c2 * (-$c1 - $sqrt_discriminant);
my $alfa1 = $inv_c2 * (-$c1 + $sqrt_discriminant);
if ($alfa0 < 1 and $alfa1 > 0) { # otherwise cases 1 and 2
# cases 0, 4, 5 and 6
my $beta = 0;
my ($p0, $p1);
if ($alfa0 > 0) {
$p0 = $a + $alfa0 * $ab;
$beta += atan2($a, $p0);
}
else {
$p0 = $a;
}
if ($alfa1 <= 1) {
$p1 = $a + $alfa1 * $ab;
$beta += atan2($p1, $b);
}
else {
$p1 = $b;
}
return $p0->[0] * $p1->[1] - $p0->[1] * $p1->[0] + $beta * $r2;
}
}
# else the sector is fully contained inside the triangle (cases 1, 2 and 3)
return atan2($a, $b) * $r2
}
sub intersection_area_circle_rectangle {
@_ == 4 or croak 'Usage: intersection_area_circle_rectangle($o, $r, $v0, $v1)';
my $O = V(@{shift()}); # accept plain array refs as vectors
my $r = shift;
my ($a, $c) = Math::Vector::Real->box($_[0] - $O, $_[1] - $O);
# fast check:
return 0 if ($a->[0] >= $r or $c->[0] <= -$r or
$a->[1] >= $r or $c->[1] <= -$r);
my $b = V($a->[0], $c->[1]);
my $d = V($c->[0], $a->[1]);
my $r2 = $r * $r;
abs(0.5 * (_ia2_circle_segment($r2, $a, $b) +
_ia2_circle_segment($r2, $b, $c) +
_ia2_circle_segment($r2, $c, $d) +
_ia2_circle_segment($r2, $d, $a)));
}
( run in 1.358 second using v1.01-cache-2.11-cpan-140bd7fdf52 )