File Coverage

blib/lib/Math/Geometry/IntersectionArea.pm
Criterion Covered Total %
statement 15 65 23.0
branch 0 18 0.0
condition 0 21 0.0
subroutine 5 8 62.5
pod 1 2 50.0
total 21 114 18.4


line stmt bran cond sub pod time code
1             package Math::Geometry::IntersectionArea;
2              
3             our $VERSION = '0.01';
4              
5 1     1   20520 use strict;
  1         1  
  1         39  
6 1     1   6 use warnings;
  1         2  
  1         28  
7 1     1   6 use Carp;
  1         6  
  1         300  
8 1     1   35 use 5.010;
  1         3  
  1         97  
9 1     1   1138 use Math::Vector::Real;
  1         34193  
  1         711  
10              
11             require Exporter;
12             our @ISA = qw(Exporter);
13             our @EXPORT_OK = qw(intersection_area_circle_rectangle
14             intersection_area_circle_polygon);
15              
16             sub _ia2_circle_segment { # ia2 = intersection area * 2
17 0     0     my ($r2, $a, $b) = @_;
18              
19             # finds the oriented area of the intersection of the circle of
20             # radius sqrt(r2) centered on the origin O and the
21             # triangle(O,a,b).
22              
23             # Cases:
24             #
25             # 0: /-----\
26             # / \
27             # | x---x |
28             #
29             #
30             # 1: /-----\
31             # / \ x---x
32             # | |
33             #
34             #
35             # 2: /-----\
36             # x---x / \
37             # | |
38             #
39             #
40             # 3: x---x
41             #
42             # /-----\
43             #
44             #
45             # 4: /-----\
46             # / \
47             # x-|-------|-x
48             #
49             #
50             # 5: /-----\
51             # / \
52             # x-|---x |
53             #
54             #
55             # 6: /-----\
56             # / \
57             # | x---|-x
58             #
59              
60             # The two points where the segment intersects the circle are found
61             # solving the following cuadratic equation:
62             #
63             # norm(a + alfa * ab) = d
64             #
65             #
66             # The coeficientes c2, c1, c0 are deduced from the formula
67             # above such that:
68             #
69             # c2 * alfa**2 + 2 * c1 * alfa + c0 = 0
70             #
71             # And then the clasical formula for quadratic equation solved is
72             # used:
73             #
74             # alfa0 = 1/c2 + (-$c1 + sqrt($c1*$c1 - 4 * $c0 * $c2))
75             # alfa1 = 1/c2 + (-$c1 - sqrt($c1*$c1 - 4 * $c0 * $c2))
76              
77 0           my $ab = $b - $a;
78 0 0         my $c2 = $ab->norm2 or return 0; # a and b are the same point
79 0           my $c1 = $a * $ab;
80 0           my $c0 = $a->norm2 - $r2;
81 0           my $discriminant = $c1 * $c1 - $c0 * $c2;
82 0 0         if ($discriminant > 0) { # otherwise case 3
83 0           my $inv_c2 = 1/$c2;
84 0           my $sqrt_discriminant = sqrt($discriminant);
85 0           my $alfa0 = $inv_c2 * (-$c1 - $sqrt_discriminant);
86 0           my $alfa1 = $inv_c2 * (-$c1 + $sqrt_discriminant);
87 0 0 0       if ($alfa0 < 1 and $alfa1 > 0) { # otherwise cases 1 and 2
88             # cases 0, 4, 5 and 6
89 0           my $beta = 0;
90 0           my ($p0, $p1);
91 0 0         if ($alfa0 > 0) {
92 0           $p0 = $a + $alfa0 * $ab;
93 0           $beta += atan2($a, $p0);
94             }
95             else {
96 0           $p0 = $a;
97             }
98 0 0         if ($alfa1 <= 1) {
99 0           $p1 = $a + $alfa1 * $ab;
100 0           $beta += atan2($p1, $b);
101             }
102             else {
103 0           $p1 = $b;
104             }
105 0           return $p0->[0] * $p1->[1] - $p0->[1] * $p1->[0] + $beta * $r2;
106             }
107             }
108              
109             # else the sector is fully contained inside the triangle (cases 1, 2 and 3)
110 0           return atan2($a, $b) * $r2
111             }
112              
113             sub intersection_area_circle_rectangle {
114 0 0   0 0   @_ == 4 or croak 'Usage: intersection_area_circle_rectangle($o, $r, $v0, $v1)';
115 0           my $O = V(@{shift()}); # accept plain array refs as vectors
  0            
116 0           my $r = shift;
117 0           my ($a, $c) = Math::Vector::Real->box($_[0] - $O, $_[1] - $O);
118              
119             # fast check:
120 0 0 0       return 0 if ($a->[0] >= $r or $c->[0] <= -$r or
      0        
      0        
121             $a->[1] >= $r or $c->[1] <= -$r);
122              
123 0           my $b = V($a->[0], $c->[1]);
124 0           my $d = V($c->[0], $a->[1]);
125 0           my $r2 = $r * $r;
126 0           abs(0.5 * (_ia2_circle_segment($r2, $a, $b) +
127             _ia2_circle_segment($r2, $b, $c) +
128             _ia2_circle_segment($r2, $c, $d) +
129             _ia2_circle_segment($r2, $d, $a)));
130             }
131              
132             sub intersection_area_circle_polygon {
133 0 0   0 1   @_ > 4 or return 0;
134 0           my $O = V(@{shift()}); # accept plain array refs as vectors
  0            
135 0           my $r = shift;
136              
137             # fast check:
138 0           my ($v0, $v1) = Math::Vector::Real->box(@_);
139 0           $v0 -= $O;
140 0           $v1 -= $O;
141 0 0 0       return 0 if ($v0->[0] >= $r or $v1->[0] <= -$r or
      0        
      0        
142             $v0->[1] >= $r or $v1->[1] <= -$r);
143              
144 0           my $A = 0;
145 0           my $r2 = $r * $r;
146 0           my $last = $_[-1] - $O;
147 0           for (@_) {
148 0           my $p = $_ - $O;
149 0           $A += _ia2_circle_segment($r2, $last, $p);
150 0           $last = $p;
151             }
152 0           return abs(0.5 * $A);
153             }
154              
155             1;
156              
157             __END__