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             # This code is part of Perl distribution Math-Polygon version 2.00.
2             # The POD got stripped from this file by OODoc version 3.03.
3             # For contributors see file ChangeLog.
4              
5             # This software is copyright (c) 2004-2025 by Mark Overmeer.
6              
7             # This is free software; you can redistribute it and/or modify it under
8             # the same terms as the Perl 5 programming language system itself.
9             # SPDX-License-Identifier: Artistic-1.0-Perl OR GPL-1.0-or-later
10              
11             #oodist: *** DO NOT USE THIS VERSION FOR PRODUCTION ***
12             #oodist: This file contains OODoc-style documentation which will get stripped
13             #oodist: during its release in the distribution. You can use this file for
14             #oodist: testing, however the code of this development version may be broken!
15              
16             # Algorithm by Dan Sunday
17             # - http://geometryalgorithms.com/Archive/algorithm_0109/algorithm_0109.htm
18             # Original contributed implementation in Perl by Jari Turkia.
19              
20             package Math::Polygon::Convex;{
21             our $VERSION = '2.00';
22             }
23              
24 1     1   136333 use parent 'Exporter';
  1         326  
  1         8  
25              
26 1     1   75 use strict;
  1         2  
  1         30  
27 1     1   6 use warnings;
  1         2  
  1         84  
28              
29 1     1   550 use Log::Report 'math-polygon';
  1         139838  
  1         5  
30 1     1   1075 use Math::Polygon ();
  1         5  
  1         673  
31              
32             our @EXPORT = qw/
33             chainHull_2D
34             /;
35              
36             #--------------------
37              
38             # is_left(): tests if a point is Left|On|Right of an infinite line.
39             # >0 for P2 left of the line through P0 and P1
40             # =0 for P2 on the line
41             # <0 for P2 right of the line
42             # See: the January 2001 Algorithm on Area of Triangles
43             # http://geometryalgorithms.com/Archive/algorithm_0101/algorithm_0101.htm
44              
45             sub is_left($$$)
46 19     19 0 20 { my ($P0, $P1, $P2) = @_;
47              
48 19         46 ($P1->[0] - $P0->[0]) * ($P2->[1] - $P0->[1])
49             - ($P2->[0] - $P0->[0]) * ($P1->[1] - $P0->[1]);
50             }
51              
52             sub chainHull_2D(@)
53 1 50   1 1 200125 { my @P = sort { $a->[0] <=> $b->[0] || $a->[1] <=> $b->[1] } @_;
  23         34  
54 1         2 my @H; # output poly
55              
56             # Get the indices of points with min x-coord and min|max y-coord
57 1         3 my $xmin = $P[0][0];
58 1         2 my ($minmin, $minmax) = (0, 0);
59 1   66     10 $minmax++ while $minmax < @P-1 && $P[$minmax+1][0]==$xmin;
60              
61 1 50       3 if($minmax == @P-1) # degenerate case: all x-coords == xmin
62 0         0 { push @H, $P[$minmin];
63 0 0       0 push @H, $P[$minmax] if $P[$minmax][1] != $P[$minmin][1];
64 0         0 push @H, $P[$minmin];
65 0         0 return Math::Polygon->new(@H);
66             }
67              
68 1         2 push @H, $P[$minmin];
69              
70             # Get the indices of points with max x-coord and min|max y-coord
71 1         3 my $maxmin = my $maxmax = @P-1;
72 1         2 my $xmax = $P[$maxmax][0];
73 1   33     7 $maxmin-- while $maxmin >= 1 && $P[$maxmin-1][0]==$xmax;
74              
75             # Compute the lower hull
76 1         5 for(my $i = $minmax+1; $i <= $maxmin; $i++)
77             { # the lower line joins P[minmin] with P[maxmin]
78             # ignore P[i] above or on the lower line
79 8 100 100     18 next if $i < $maxmin && is_left($P[$minmin], $P[$maxmin], $P[$i]) >= 0;
80              
81             pop @H
82 6   100     11 while @H >= 2 && is_left($H[-2], $H[-1], $P[$i]) < 0;
83              
84 6         23 push @H, $P[$i];
85             }
86              
87 1 50       3 push @H, $P[$maxmax]
88             if $maxmax != $maxmin;
89              
90             # Next, compute the upper hull on the stack H above the bottom hull
91 1         2 my $bot = @H-1; # the bottom point of the upper hull stack
92 1         5 for(my $i = $maxmin-1; $i >= $minmax; --$i)
93             { # the upper line joins P[maxmax] with P[minmax]
94             # ignore P[i] below or on the upper line
95 8 100 66     15 next if $i > $minmax && is_left($P[$maxmax], $P[$minmax], $P[$i]) >= 0;
96              
97             pop @H
98 1   33     3 while @H-1 > $bot && is_left($H[-2], $H[-1], $P[$i]) < 0;
99              
100 1         3 push @H, $P[$i];
101             }
102              
103 1 50       4 push @H, $P[$minmin]
104             if $minmax != $minmin; # joining endpoint onto stack
105              
106             # Remove duplicate points.
107 1         29 for(my $i = @H-1; $i > 1; $i--)
108 5   66     14 { splice @H, $i, 1
109             while $H[$i][0]==$H[$i-1][0] && $H[$i][1]==$H[$i-1][1];
110             }
111              
112 1         25 Math::Polygon->new(@H);
113             }
114              
115             1;