File Coverage

blib/lib/Math/Polygon/Convex.pm
Criterion Covered Total %
statement 42 46 91.3
branch 8 14 57.1
condition 14 21 66.6
subroutine 7 7 100.0
pod 1 2 50.0
total 72 90 80.0


line stmt bran cond sub pod time code
1             # Copyrights 2004-2018 by [Mark Overmeer].
2             # For other contributors see ChangeLog.
3             # See the manual pages for details on the licensing terms.
4             # Pod stripped from pm file by OODoc 2.02.
5             # This code is part of distribution Math::Polygon. Meta-POD processed with
6             # OODoc into POD and HTML manual-pages. See README.md
7             # Copyright Mark Overmeer. Licensed under the same terms as Perl itself.
8              
9             # Algorithm by Dan Sunday
10             # - http://geometryalgorithms.com/Archive/algorithm_0109/algorithm_0109.htm
11             # Original implementation in Perl by Jari Turkia.
12              
13             package Math::Polygon::Convex;
14 1     1   52805 use vars '$VERSION';
  1         7  
  1         57  
15             $VERSION = '1.10';
16              
17 1     1   5 use base 'Exporter';
  1         2  
  1         110  
18              
19 1     1   5 use strict;
  1         1  
  1         44  
20 1     1   4 use warnings;
  1         2  
  1         20  
21              
22 1     1   354 use Math::Polygon;
  1         2  
  1         393  
23              
24             our @EXPORT = qw/
25             chainHull_2D
26             /;
27              
28              
29             # is_left(): tests if a point is Left|On|Right of an infinite line.
30             # >0 for P2 left of the line through P0 and P1
31             # =0 for P2 on the line
32             # <0 for P2 right of the line
33             # See: the January 2001 Algorithm on Area of Triangles
34             # http://geometryalgorithms.com/Archive/algorithm_0101/algorithm_0101.htm
35              
36             sub is_left($$$)
37 19     19 0 31 { my ($P0, $P1, $P2) = @_;
38              
39 19         67 ($P1->[0] - $P0->[0]) * ($P2->[1] - $P0->[1])
40             - ($P2->[0] - $P0->[0]) * ($P1->[1] - $P0->[1]);
41             }
42              
43             sub chainHull_2D(@)
44 1 50   1 1 67 { my @P = sort { $a->[0] <=> $b->[0] || $a->[1] <=> $b->[1] } @_;
  23         51  
45 1         1 my @H; # output poly
46              
47             # Get the indices of points with min x-coord and min|max y-coord
48 1         2 my $xmin = $P[0][0];
49 1         3 my ($minmin, $minmax) = (0, 0);
50 1   66     11 $minmax++ while $minmax < @P-1 && $P[$minmax+1][0]==$xmin;
51              
52 1 50       4 if($minmax == @P-1) # degenerate case: all x-coords == xmin
53 0         0 { push @H, $P[$minmin];
54 0 0       0 push @H, $P[$minmax] if $P[$minmax][1] != $P[$minmin][1];
55 0         0 push @H, $P[$minmin];
56 0         0 return Math::Polygon->new(@H);
57             }
58              
59 1         3 push @H, $P[$minmin];
60              
61             # Get the indices of points with max x-coord and min|max y-coord
62 1         2 my $maxmin = my $maxmax = @P-1;
63 1         1 my $xmax = $P[$maxmax][0];
64 1   33     5 $maxmin-- while $maxmin >= 1 && $P[$maxmin-1][0]==$xmax;
65              
66             # Compute the lower hull
67 1         6 for(my $i = $minmax+1; $i <= $maxmin; $i++)
68             { # the lower line joins P[minmin] with P[maxmin]
69             # ignore P[i] above or on the lower line
70 8 100 100     17 next if $i < $maxmin
71             && is_left($P[$minmin], $P[$maxmin], $P[$i]) >= 0;
72              
73             pop @H
74 6   100     14 while @H >= 2 && is_left($H[-2], $H[-1], $P[$i]) < 0;
75            
76 6         9 push @H, $P[$i];
77             }
78              
79 1 50       3 push @H, $P[$maxmax]
80             if $maxmax != $maxmin;
81              
82             # Next, compute the upper hull on the stack H above the bottom hull
83 1         2 my $bot = @H-1; # the bottom point of the upper hull stack
84 1         3 for(my $i = $maxmin-1; $i >= $minmax; --$i)
85             { # the upper line joins P[maxmax] with P[minmax]
86             # ignore P[i] below or on the upper line
87 8 100 66     13 next if $i > $minmax
88             && is_left($P[$maxmax], $P[$minmax], $P[$i]) >= 0;
89              
90             pop @H
91 1   33     4 while @H-1 > $bot && is_left($H[-2], $H[-1], $P[$i]) < 0;
92              
93 1         3 push @H, $P[$i];
94             }
95              
96 1 50       2 push @H, $P[$minmin]
97             if $minmax != $minmin; # joining endpoint onto stack
98              
99             # Remove duplicate points.
100 1         3 for(my $i = @H-1; $i > 1; $i--)
101 5   66     15 { splice @H, $i, 1
102             while $H[$i][0]==$H[$i-1][0] && $H[$i][1]==$H[$i-1][1];
103             }
104              
105 1         6 Math::Polygon->new(@H);
106             }
107              
108             1;