File Coverage

blib/lib/Math/Geometry/Planar.pm
Criterion Covered Total %
statement 1669 2011 82.9
branch 418 604 69.2
condition 151 285 52.9
subroutine 100 102 98.0
pod 44 63 69.8
total 2382 3065 77.7


line stmt bran cond sub pod time code
1             #
2             # Copyright (c) 2002 Danny Van de Pol - Alcatel Telecom Belgium
3             # danny.vandepol@alcatel.be
4             #
5             # Free usage under the same Perl Licence condition.
6             #
7              
8             package Math::Geometry::Planar;
9              
10             $VERSION = '1.18';
11              
12 1         83 use vars qw(
13             $VERSION
14             @ISA
15             @EXPORT
16             @EXPORT_OK
17             $precision
18 1     1   8750 );
  1         2  
19              
20 1     1   5 use strict;
  1         2  
  1         27  
21 1     1   902 use Math::Geometry::Planar::GPC;
  1         12716  
  1         104  
22 1     1   1154 use Math::Geometry::Planar::Offset;
  1         4187  
  1         61  
23 1     1   8 use Carp;
  1         2  
  1         63  
24 1     1   1135 use POSIX;
  1         8073  
  1         7  
25              
26             $precision = 7;
27              
28             require Exporter;
29             @ISA = qw(Exporter);
30             @EXPORT = qw(
31             SegmentLength Determinant DotProduct CrossProduct
32             TriangleArea Colinear
33             SegmentIntersection LineIntersection RayIntersection
34             SegmentLineIntersection RayLineIntersection
35             SegmentRayIntersection
36             Perpendicular PerpendicularFoot
37             DistanceToLine DistanceToSegment
38             Gpc2Polygons GpcClip
39             CircleToPoly ArcToPoly CalcAngle
40             );
41             @EXPORT_OK = qw($precision);
42              
43             =pod
44              
45             =head1 NAME
46              
47             Math::Geometry::Planar - A collection of planar geometry functions
48              
49             =head1 SYNOPSIS
50              
51             use Math::Geometry::Planar;
52             $polygon = Math::Geometry::Planar->new; creates a new polygon object;
53             $contour = Math::Geometry::Planar->new; creates a new contour object;
54              
55             =head4 Formats
56              
57             A point is a reference to an array holding the x and y coordinates of the point.
58              
59             $point = [$x_coord,$y_coord];
60              
61             A polygon is a reference to an (ordered) array of points. The first point is the
62             begin and end point of the polygon. The points can be given in any direction
63             (clockwise or counter clockwise).
64              
65             A contour is a reference to an array of polygons. By convention, the first polygon
66             is the outer shape, all other polygons represent holes in the outer shape. The outer
67             shape must enclose all holes !
68             Using this convention, the points can be given in any direction, however, keep
69             in mind that some functions (e.g. triangulation) require that the outer polygons
70             are entered in counter clockwise order and the inner polygons (holes) in clock
71             wise order. The points, polygons, add_polygons methods will automatically set the
72             right order of points.
73             No points can be assigned to an object that already has polygons assigned to and
74             vice versa.
75              
76             $points = [[$x1,$y1],[$x2,$y2], ... ];
77             $polygon->points($points); # assign points to polygon object
78             $points1 = [[$x1,$y1],[$x2,$y2], ... ];
79             $points2 = [[ax1,by1],[ax2,by2], ... ];
80             $contour->polygons([$points1,$points2, ...]); # assign polgyons to contour object
81              
82             =head1 METHODS
83              
84             The available methods are:
85              
86             =head4 $polygon->points(arg);
87              
88             Returns the polygon points if no argument is entered.
89             If the argument is a refence to a points array, sets the points for a polygon object.
90              
91             =head4 $contour->polygons(arg);
92              
93             Returns the contour polygons if no argument is entered.
94             If the argument is a refence to a polygons array, sets the polygons for a contour object.
95              
96             =head4 $contour->num_polygons;
97              
98             Returns the total number of polygons in the contour.
99              
100             =head4 $contour->add_polygons(arg);
101              
102             Adds a list of polygons to a contour object (if the contour object doesn't have any
103             polygons yet, the very first polygon reference from the list is used as the outer
104             shape). Returns the total number of polygons in the contour.
105              
106             =head4 $contour->get_polygons(arg_1,arg_2, ... );
107              
108             Returns a list of polygons where each element of the list corresponds to the polygon
109             at index arg_x - starting at 0, the outer shape. If the index arg_x is out of range,
110             the corresponding value in the result list wil be undefined. If no argument is
111             entered, a full list of all polygons is returned. Please note that this method returns
112             a list rather then a reference.
113              
114             =head4 $polygon->cleanup;
115              
116             Remove colinear points from the polygon/contour.
117              
118             =head4 $polygon->isconvex;
119              
120             Returns true if the polygon/contour is convex. A contour is considered to be convex if
121             the outer shape is convex.
122              
123             =head4 $polygon->issimple;
124              
125             Returns true if the polygon/contour is simple. A contour is considered to be simple if
126             all it's polygons are simple.
127              
128             =head4 $polygon->perimeter;
129              
130             Returns the perimeter of the polygon/contour. The perimeter of a contour is the perimeter
131             of the outer shape.
132              
133             =head4 $polygon->area;
134              
135             Returns the signed area of the polygon/contour (positive if the points are in counter
136             clockwise order). The area of a contour is the area of the outer shape minus the sum
137             of the area of the holes.
138              
139             =head4 $polygon->centroid;
140              
141             Returns the centroid (center of gravity) of the polygon/contour.
142              
143             =head4 $polygon->isinside($point);
144              
145             Returns true if point is inside the polygon/contour (a point is inside a contour if
146             it is inside the outer polygon and not inside a hole).
147              
148             =head4 $polygon->rotate($angle,$center);
149              
150             Returns polygon/contour rotated $angle (in radians) around $center.
151             If no center is entered, rotates around the origin.
152              
153             =head4 $polygon->move($dx,$dy);
154              
155             Returns polygon/contour moved $dx in x direction and $dy in y direction.
156              
157             =head4 $polygon->mirrorx($center);
158              
159             Returns polygon/contour mirrored in x direction
160             with (vertical) axis of reflection through point $center.
161             If no center is entered, axis is the Y-axis.
162              
163             =head4 $polygon->mirrory($center);
164              
165             Returns polygon/contour mirrored in y direction
166             with (horizontal) axis of reflection through point $center.
167             If no center is entered, axis is the X-axis.
168              
169             =head4 $polygon->mirror($axos);
170              
171             Returns polygon mirrored/contour along axis $axis (= array with 2 points defining
172             axis of reflection).
173              
174             =head4 $polygon->scale($csale,$center);
175              
176             Returns polygon/contour scaled by a factor $scale, center of scaling is $scale.
177             If no center is entered, center of scaling is the origin.
178              
179             =head4 $polygon->bbox;
180              
181             Returns the polygon's/contour's bounding box.
182              
183             =head4 $polygon->minrectangle;
184              
185             Returns the polygon's/contour's minimal (area) enclosing rectangle.
186              
187             =head4 $polygon->convexhull;
188              
189             Returns a polygon representing the convex hull of the polygon/contour.
190              
191             =head4 $polygon->convexhull2;
192              
193             Returns a polygon representing the convex hull of an arbitrary set of points
194             (works also on a contour, however a contour is a set of polygons and polygons
195             are ordered sets of points so the method above will be faster)
196              
197             =head4 $polygon->triangulate;
198              
199             Triangulates a polygon/contour based on Raimund Seidel's algorithm:
200             'A simple and fast incremental randomized algorithm for computing trapezoidal
201             decompositions and for triangulating polygons'
202             Returns a list of polygons (= the triangles)
203              
204             =head4 $polygon->offset_polygon($distance);
205              
206             Returns reference to an array of polygons representing the original polygon
207             offsetted by $distance
208              
209             =head4 $polygon->convert2gpc;
210              
211             Converts a polygon/contour to a gpc structure and returns the resulting gpc structure
212              
213             =head1 EXPORTS
214              
215             =head4 SegmentLength[$p1,$p2];
216              
217             Returns the length of the segment (vector) p1p2
218              
219             =head4 Determinant(x1,y1,x2,y2);
220              
221             Returns the determinant of the matrix with rows x1,y1 and x2,y2 which is x1*y2 - y1*x2
222              
223             =head4 DotProduct($p1,$p2,$p3,$p4);
224              
225             Returns the vector dot product of vectors p1p2 and p3p4
226             or the dot product of p1p2 and p2p3 if $p4 is ommited from the argument list
227              
228             =head4 CrossProduct($p1,$p2,$p3);
229              
230             Returns the vector cross product of vectors p1p2 and p1p3
231              
232             =head4 TriangleArea($p1,$p2,$p3);
233              
234             Returns the signed area of the triangle p1p2p3
235              
236             =head4 Colinear($p1,$p2,$p3);
237              
238             Returns true if p1,p2 and p3 are colinear
239              
240             =head4 SegmentIntersection($p1,$p2,$p3,$p4);
241              
242             Returns the intersection point of segments p1p2 and p3p4,
243             false if segments don't intersect
244              
245             =head4 LineIntersection($p1,$p2,$p3,$p4);
246              
247             Returns the intersection point of lines p1p2 and p3p4,
248             false if lines don't intersect (parallel lines)
249              
250             =head4 RayIntersection($p1,$p2,$p3,$p4);
251              
252             Returns the intersection point of rays p1p2 and p3p4,
253             false if lines don't intersect (parallel rays)
254             p1 (p3) is the startpoint of the ray and p2 (p4) is
255             a point on the ray.
256              
257             =head4 RayLineIntersection($p1,$p2,$p3,$p4);
258              
259             Returns the intersection point of ray p1p2 and line p3p4,
260             false if lines don't intersect (parallel rays)
261             p1 is the startpoint of the ray and p2 is a point on the ray.
262              
263             =head4 SegmentLineIntersection($p1,$p2,$p3,$p4);
264              
265             Returns the intersection point of segment p1p2 and line p3p4,
266             false if lines don't intersect (parallel rays)
267              
268             =head4 SegmentRayIntersection($p1,$p2,$p3,$p4);
269              
270             Returns the intersection point of segment p1p2 and ray p3p4,
271             false if lines don't intersect (parallel rays)
272             p3 is the startpoint of the ray and p4 is a point on the ray.
273              
274             =head4 Perpendicular($p1,$p2,$p3,$p4);
275              
276             Returns true if lines (segments) p1p2 and p3p4 are perpendicular
277              
278             =head4 PerpendicularFoot($p1,$p2,$p3);
279              
280             Returns the perpendicular foot of p3 on line p1p2
281              
282             =head4 DistanceToLine($p1,$p2,$p3);
283              
284             Returns the perpendicular distance of p3 to line p1p2
285              
286             =head4 DistanceToSegment($p1,$p2,$p3);
287              
288             Returns the distance of p3 to segment p1p2. Depending on the point's
289             position, this is the distance to one of the endpoints or the
290             perpendicular distance to the segment.
291              
292             =head4 Gpc2Polygons($gpc_contour);
293              
294             Converts a gpc contour structure to an array of contours and returns the array
295              
296             =head4 GpcClip($operation,$gpc_contour_1,$gpc_contour_2);
297              
298             $operation is DIFFERENCE, INTERSECTION, XOR or UNION
299             $gpc_polygon_1 is the source polygon
300             $gpc_polygon_2 is the clip polygon
301              
302             Returns a gpc polygon structure which is the result of the gpc clipping operation
303              
304             =head4 CircleToPoly($i,$p1,$p2,$p3);
305              
306             Converts the circle through points p1p2p3 to a polygon with i segments
307              
308             =head4 CircleToPoly($i,$center,$p1);
309              
310             Converts the circle with center through point p1 to a polygon with i segments
311              
312             =head4 CircleToPoly($i,$center,$radius);
313              
314             Converts the circle with center and radius to a polygon with i segments
315              
316             =head4 ArcToPoly($i,$p1,$p2,$p3);
317              
318             Converts the arc with begin point p1, intermediate point p2 and end point p3
319             to a (non-closed !) polygon with i segments
320              
321             =head4 ArcToPoly($i,$center,$p1,$p2,$direction);
322              
323             Converts the arc with center, begin point p1 and end point p2 to a
324             (non-closed !) polygon with i segments. If direction is 0, the arc
325             is traversed counter clockwise from p1 to p2, clockwise if direction is 1
326              
327             =cut
328              
329             require 5.005;
330              
331             my $delta = 10 ** (-$precision);
332              
333             ################################################################################
334             #
335             # calculate length of a line segment
336             #
337             # args : reference to array with 2 points defining line segment
338             #
339             sub SegmentLength {
340 16     16 0 56 my $pointsref = $_[0];
341 16         31 my @points = @$pointsref;
342 16 50       36 if (@points != 2) {
343 0         0 carp("Need 2 points for a segment length calculation");
344 0         0 return;
345             }
346 16         19 my @a = @{$points[0]};
  16         41  
347 16         17 my @b = @{$points[1]};
  16         25  
348 16         47 my $length = sqrt(DotProduct([$points[0],$points[1],$points[0],$points[1]]));
349 16         74 return $length;
350             }
351             ################################################################################
352             #
353             # The determinant for the matrix | x1 y1 |
354             # | x2 y2 |
355             #
356             # args : x1,y1,x2,y2
357             #
358             sub Determinant {
359 468     468 1 673 my ($x1,$y1,$x2,$y2) = @_;
360 468         895 return ($x1*$y2 - $x2*$y1);
361             }
362             ################################################################################
363             #
364             # vector dot product
365             # calculates dotproduct vectors p1p2 and p3p4
366             # The dot product of a and b is written as a.b and is
367             # defined by a.b = |a|*|b|*cos q
368             #
369             # args : reference to an array with 4 points p1,p2,p3,p4 defining 2 vectors
370             # a = vector p1p2 and b = vector p3p4
371             # or
372             # reference to an array with 3 points p1,p2,p3 defining 2 vectors
373             # a = vector p1p2 and b = vector p1p3
374             #
375             sub DotProduct {
376 27     27 1 34 my $pointsref = $_[0];
377 27         52 my @points = @$pointsref;
378 27         36 my (@p1,@p2,@p3,@p4);
379 27 50       47 if (@points == 4) {
    0          
380 27         34 @p1 = @{$points[0]};
  27         50  
381 27         31 @p2 = @{$points[1]};
  27         41  
382 27         30 @p3 = @{$points[2]};
  27         43  
383 27         31 @p4 = @{$points[3]};
  27         45  
384             } elsif (@points == 3) {
385 0         0 @p1 = @{$points[0]};
  0         0  
386 0         0 @p2 = @{$points[1]};
  0         0  
387 0         0 @p3 = @{$points[0]};
  0         0  
388 0         0 @p4 = @{$points[2]};
  0         0  
389             } else {
390 0         0 carp("Need 3 or 4 points for a dot product");
391 0         0 return;
392             }
393 27         152 return ($p2[0]-$p1[0])*($p4[0]-$p3[0]) + ($p2[1]-$p1[1])*($p4[1]-$p3[1]);
394             }
395             ################################################################################
396             #
397             # returns vector cross product of vectors p1p2 and p1p3
398             # using Cramer's rule
399             #
400             # args : reference to an array with 3 points p1,p2 and p3
401             #
402             sub CrossProduct {
403 128     128 1 160 my $pointsref = $_[0];
404 128         232 my @points = @$pointsref;
405 128 50       246 if (@points != 3) {
406 0         0 carp("Need 3 points for a cross product");
407 0         0 return;
408             }
409 128         143 my @p1 = @{$points[0]};
  128         303  
410 128         137 my @p2 = @{$points[1]};
  128         205  
411 128         141 my @p3 = @{$points[2]};
  128         227  
412 128         339 my $det_p2p3 = &Determinant($p2[0], $p2[1], $p3[0], $p3[1]);
413 128         287 my $det_p1p3 = &Determinant($p1[0], $p1[1], $p3[0], $p3[1]);
414 128         254 my $det_p1p2 = &Determinant($p1[0], $p1[1], $p2[0], $p2[1]);
415 128         528 return ($det_p2p3-$det_p1p3+$det_p1p2);
416             }
417             ################################################################################
418             #
419             # The Cramer's Rule for area of a triangle is
420             # | x1 y1 1 |
421             # A = 1/2 * | x2 y2 1 |
422             # | x3 y3 1 |
423             # Which is 'half of the cross product of vectors ab and ac.
424             # The cross product of the vectors ab and ac is a vector perpendicular to the
425             # plane defined by ab and bc with a magnitude equal to the area of the
426             # parallelogram defined by a, b, c and ab + bc (vector sum)
427             # Don't forget that: (ab x ac) = - (ac x ab) (x = cross product)
428             # Which just means that if you reverse the vectors in the cross product,
429             # the resulting vector points in the opposite direction
430             # The direction of the resulting vector can be found with the "right hand rule"
431             # This can be used to determine the order of points a, b and c:
432             # clockwise or counter clockwise
433             #
434             # args : reference to an array with 3 points p1.p2,p3
435             #
436             sub TriangleArea {
437 7     7 1 10 my $pointsref = $_[0];
438 7         15 my @points = @$pointsref;
439 7 50       16 if (@points != 3) { # need 3 points for a triangle ...
440 0         0 carp("A triangle should have exactly 3 points");
441 0         0 return;
442             }
443 7         15 return CrossProduct($pointsref)/2;
444             }
445             ################################################################################
446             #
447             # Check if 3 points are colinear
448             # Points are colinear if triangle area is 0
449             # Triangle area is crossproduct/2 so we can check the crossproduct instead
450             #
451             # args : reference to an array with 3 points p1.p2,p3
452             #
453             sub Colinear {
454 12     12 1 18 my $pointsref = $_[0];
455 12         19 my @points = @$pointsref;
456 12 50       36 if (@points != 3) {
457 0         0 carp("Colinear only checks colinearity for 3 points");
458 0         0 return;
459             }
460             # check the area of the triangle to find
461 12         23 return (abs(CrossProduct($pointsref)) < $delta);
462             }
463             ################################################################################
464             #
465             # calculate intersection point of 2 line segments
466             # returns false if segments don't intersect
467             # The theory:
468             #
469             # Parametric representation of a line
470             # if p1 (x1,y1) and p2 (x2,y2) are 2 points on a line and
471             # P1 is the vector from (0,0) to (x1,y1)
472             # P2 is the vector from (0,0) to (x2,y2)
473             # then the parametric representation of the line is P = P1 + k (P2 - P1)
474             # where k is an arbitrary scalar constant.
475             # for a point on the line segement (p1,p2) value of k is between 0 and 1
476             #
477             # for the 2 line segements we get
478             # Pa = P1 + k (P2 - P1)
479             # Pb = P3 + l (P4 - P3)
480             #
481             # For the intersection point Pa = Pb so we get the following equations
482             # x1 + k (x2 - x1) = x3 + l (x4 - x3)
483             # y1 + k (y2 - y1) = y3 + l (y4 - y3)
484             # Which using Cramer's Rule results in
485             # (x4 - x3)(y1 - y3) - (y4 - x3)(x1 - x3)
486             # k = ---------------------------------------
487             # (y4 - y3)(x2 - x1) - (x4 - x3)(y2 - y1)
488             # and
489             # (x2 - x1)(y1 - y3) - (y2 - y1)(x1 - x3)
490             # l = ---------------------------------------
491             # (y4 - y3)(x2 - x1) - (x4 - x3)(y2 - y1)
492             #
493             # Note that the denominators are equal. If the denominator is 9,
494             # the lines are parallel. Intersection is detected by checking if
495             # both k and l are between 0 and 1.
496             #
497             # The intersection point p5 (x5,y5) is:
498             # x5 = x1 + k (x2 - x1)
499             # y5 = y1 + k (y2 - y1)
500             #
501             # 'Touching' segments are considered as not intersecting
502             #
503             # args : reference to an array with 4 points p1,p2,p3,p4
504             #
505             sub SegmentIntersection {
506 11     11 1 57 my $pointsref = $_[0];
507 11         20 my @points = @$pointsref;
508 11 50       30 if (@points != 4) {
509 0         0 carp("SegmentIntersection needs 4 points");
510 0         0 return;
511             }
512 11         12 my @p1 = @{$points[0]}; # p1,p2 = segment 1
  11         23  
513 11         12 my @p2 = @{$points[1]};
  11         17  
514 11         12 my @p3 = @{$points[2]}; # p3,p4 = segment 2
  11         18  
515 11         12 my @p4 = @{$points[3]};
  11         17  
516 11         11 my @p5;
517 11         31 my $n1 = Determinant(($p3[0]-$p1[0]),($p3[0]-$p4[0]),($p3[1]-$p1[1]),($p3[1]-$p4[1]));
518 11         36 my $n2 = Determinant(($p2[0]-$p1[0]),($p3[0]-$p1[0]),($p2[1]-$p1[1]),($p3[1]-$p1[1]));
519 11         31 my $d = Determinant(($p2[0]-$p1[0]),($p3[0]-$p4[0]),($p2[1]-$p1[1]),($p3[1]-$p4[1]));
520 11 50       31 if (abs($d) < $delta) {
521 0         0 return 0; # parallel
522             }
523 11 100 100     70 if (!(($n1/$d < 1) && ($n2/$d < 1) &&
      100        
      66        
524             ($n1/$d > 0) && ($n2/$d > 0))) {
525 8         56 return 0;
526             }
527 3         41 $p5[0] = $p1[0] + $n1/$d * ($p2[0] - $p1[0]);
528 3         8 $p5[1] = $p1[1] + $n1/$d * ($p2[1] - $p1[1]);
529 3         17 return \@p5; # intersection point
530             }
531             ################################################################################
532             #
533             # Intersection point of 2 lines - (almost) identical as for Segments
534             # each line is defined by 2 points
535             #
536             # args : reference to an array with 4 points p1,p2,p3,p4
537             #
538             sub LineIntersection {
539 15     15 1 64 my $pointsref = $_[0];
540 15         29 my @points = @$pointsref;
541 15 50       32 if (@points < 4) {
542 0         0 carp("LineIntersection needs 4 points");
543 0         0 return;
544             }
545 15         20 my @p1 = @{$points[0]}; # p1,p2 = line 1
  15         28  
546 15         19 my @p2 = @{$points[1]};
  15         21  
547 15         19 my @p3 = @{$points[2]}; # p3,p4 = line 2
  15         24  
548 15         16 my @p4 = @{$points[3]};
  15         25  
549 15         18 my @p5;
550 15         41 my $n1 = Determinant(($p3[0]-$p1[0]),($p3[0]-$p4[0]),($p3[1]-$p1[1]),($p3[1]-$p4[1]));
551 15         47 my $d = Determinant(($p2[0]-$p1[0]),($p3[0]-$p4[0]),($p2[1]-$p1[1]),($p3[1]-$p4[1]));
552 15 100       41 if (abs($d) < $delta) {
553 1         15 return 0; # parallel
554             }
555 14         38 $p5[0] = $p1[0] + $n1/$d * ($p2[0] - $p1[0]);
556 14         30 $p5[1] = $p1[1] + $n1/$d * ($p2[1] - $p1[1]);
557 14         64 return \@p5; # intersection point
558             }
559             ################################################################################
560             #
561             # Intersection point of 2 rays
562             #
563             # args : reference to an array with 4 points p1,p2,p3,p4
564             #
565             # Parametric representation of a ray
566             # if p1 (x1,y1) is the startpoint of the ray
567             # and p2 (x2,y2) are is a point on the ray then
568             # P1 is the vector from (0,0) to (x1,y1)
569             # P2 is the vector from (0,0) to (x2,y2)
570             # then the parametric representation of the ray is P = P1 + k (P2 - P1)
571             # where k is an arbitrary scalar constant.
572             # for a point on the line segement (p1,p2) value of k is positive
573             #
574             # (A ray is often represented as a single point and a direction # 'theta'
575             # in this case, one can easily define a second point as
576             # x2 = x1 + cos(theta) and y2 = y2 + sin(theta) )
577             #
578             # for the 2 rays we get
579             # Pa = P1 + k (P2 - P1)
580             # Pb = P3 + l (P4 - P3)
581             #
582             # Touching rays are considered as not intersectin
583             #
584             sub RayIntersection {
585 2     2 1 52 my $pointsref = $_[0];
586 2         6 my @points = @$pointsref;
587 2 50       5 if (@points != 4) {
588 0         0 carp("RayIntersection needs 4 points");
589 0         0 return;
590             }
591 2         3 my @p1 = @{$points[0]}; # p1,p2 = segment 1 (startpoint is p1)
  2         4  
592 2         3 my @p2 = @{$points[1]};
  2         3  
593 2         3 my @p3 = @{$points[2]}; # p3,p4 = segment 2 (startpoint is p3)
  2         11  
594 2         1 my @p4 = @{$points[3]};
  2         4  
595 2         1 my @p5;
596 2         8 my $n1 = Determinant(($p3[0]-$p1[0]),($p3[0]-$p4[0]),($p3[1]-$p1[1]),($p3[1]-$p4[1]));
597 2         6 my $n2 = Determinant(($p2[0]-$p1[0]),($p3[0]-$p1[0]),($p2[1]-$p1[1]),($p3[1]-$p1[1]));
598 2         6 my $d = Determinant(($p2[0]-$p1[0]),($p3[0]-$p4[0]),($p2[1]-$p1[1]),($p3[1]-$p4[1]));
599 2 50       6 if (abs($d) < $delta) {
600 0         0 return 0; # parallel
601             }
602 2 100 66     10 if (!( ($n1/$d > 0) && ($n2/$d > 0))) {
603 1         17 return 0;
604             }
605 1         4 $p5[0] = $p1[0] + $n1/$d * ($p2[0] - $p1[0]);
606 1         2 $p5[1] = $p1[1] + $n1/$d * ($p2[1] - $p1[1]);
607 1         5 return \@p5; # intersection point
608             }
609             ################################################################################
610             #
611             # Intersection point of a segment and a line
612             #
613             # args : reference to an array with 4 points p1,p2,p3,p4
614             #
615             sub SegmentLineIntersection {
616 2     2 1 42 my $pointsref = $_[0];
617 2         3 my @points = @$pointsref;
618 2 50       6 if (@points != 4) {
619 0         0 carp("SegmentLineIntersection needs 4 points");
620 0         0 return;
621             }
622 2         2 my @p1 = @{$points[0]}; # p1,p2 = segment
  2         11  
623 2         2 my @p2 = @{$points[1]};
  2         4  
624 2         2 my @p3 = @{$points[2]}; # p3,p4 = line
  2         3  
625 2         4 my @p4 = @{$points[3]};
  2         3  
626 2         2 my @p5;
627 2         7 my $n1 = Determinant(($p3[0]-$p1[0]),($p3[0]-$p4[0]),($p3[1]-$p1[1]),($p3[1]-$p4[1]));
628 2         5 my $d = Determinant(($p2[0]-$p1[0]),($p3[0]-$p4[0]),($p2[1]-$p1[1]),($p3[1]-$p4[1]));
629 2 50       6 if (abs($d) < $delta) {
630 0         0 return 0; # parallel
631             }
632 2 100 66     11 if (!(($n1/$d < 1) && ($n1/$d > 0))) {
633 1         13 return 0;
634             }
635 1         4 $p5[0] = $p1[0] + $n1/$d * ($p2[0] - $p1[0]);
636 1         2 $p5[1] = $p1[1] + $n1/$d * ($p2[1] - $p1[1]);
637 1         5 return \@p5; # intersection point
638             }
639             ################################################################################
640             #
641             # Intersection point of a ray and a line
642             #
643             # args : reference to an array with 4 points p1,p2,p3,p4
644             #
645             sub RayLineIntersection {
646 2     2 1 57 my $pointsref = $_[0];
647 2         3 my @points = @$pointsref;
648 2 50       6 if (@points != 4) {
649 0         0 carp("RayLineIntersection needs 4 points");
650 0         0 return;
651             }
652 2         3 my @p1 = @{$points[0]}; # p1,p2 = ray (startpoint p1)
  2         5  
653 2         2 my @p2 = @{$points[1]};
  2         5  
654 2         2 my @p3 = @{$points[2]}; # p3,p4 = line
  2         4  
655 2         3 my @p4 = @{$points[3]};
  2         3  
656 2         4 my @p5;
657 2         7 my $n1 = Determinant(($p3[0]-$p1[0]),($p3[0]-$p4[0]),($p3[1]-$p1[1]),($p3[1]-$p4[1]));
658 2         9 my $d = Determinant(($p2[0]-$p1[0]),($p3[0]-$p4[0]),($p2[1]-$p1[1]),($p3[1]-$p4[1]));
659 2 50       7 if (abs($d) < $delta) {
660 0         0 return 0; # parallel
661             }
662 2 100       8 if (!($n1/$d > 0)) {
663 1         6 return 0;
664             }
665 1         3 $p5[0] = $p1[0] + $n1/$d * ($p2[0] - $p1[0]);
666 1         4 $p5[1] = $p1[1] + $n1/$d * ($p2[1] - $p1[1]);
667 1         5 return \@p5; # intersection point
668             }
669             ################################################################################
670             #
671             # Intersection point of a segment and a ray
672             #
673             # args : reference to an array with 4 points p1,p2,p3,p4
674             #
675             sub SegmentRayIntersection {
676 2     2 1 46 my $pointsref = $_[0];
677 2         7 my @points = @$pointsref;
678 2 50       5 if (@points != 4) {
679 0         0 carp("SegmentRayIntersection needs 4 points");
680 0         0 return;
681             }
682 2         2 my @p1 = @{$points[0]}; # p1,p2 = segment
  2         6  
683 2         3 my @p2 = @{$points[1]};
  2         5  
684 2         2 my @p3 = @{$points[2]}; # p3,p4 = ray (startpoint p3)
  2         11  
685 2         3 my @p4 = @{$points[3]};
  2         2  
686 2         4 my @p5;
687 2         7 my $n1 = Determinant(($p3[0]-$p1[0]),($p3[0]-$p4[0]),($p3[1]-$p1[1]),($p3[1]-$p4[1]));
688 2         6 my $n2 = Determinant(($p2[0]-$p1[0]),($p3[0]-$p1[0]),($p2[1]-$p1[1]),($p3[1]-$p1[1]));
689 2         6 my $d = Determinant(($p2[0]-$p1[0]),($p3[0]-$p4[0]),($p2[1]-$p1[1]),($p3[1]-$p4[1]));
690 2 50       7 if (abs($d) < $delta) {
691 0         0 return 0; # parallel
692             }
693 2 100 33     23 if (!(($n1/$d < 1) && ($n1/$d > 0) && ($n2/$d > 0))) {
      66        
694 1         6 return 0;
695             }
696 1         4 $p5[0] = $p1[0] + $n1/$d * ($p2[0] - $p1[0]);
697 1         4 $p5[1] = $p1[1] + $n1/$d * ($p2[1] - $p1[1]);
698 1         14 return \@p5; # intersection point
699             }
700             ################################################################################
701             #
702             # returns true if 2 lines (segments) are perpendicular
703             # Lines are perpendicular if dot product is 0
704             #
705             # args : reference to an array with 4 points p1,p2,p3,p4
706             # p1p2 = line 1
707             # p3p4 = line 2
708             #
709             sub Perpendicular {
710 2     2 1 5 my $pointsref = $_[0];
711 2         5 my @points = @$pointsref;
712 2 50       6 if (@points != 4) {
713 0         0 carp("Perpendicular needs 4 points defining 2 lines or segments");
714 0         0 return;
715             }
716 2         7 return (abs(DotProduct([$points[0],$points[1],$points[2],$points[3]])) < $delta);
717             }
718             ################################################################################
719             #
720             # Calculates the 'perpendicular foot' of a point on a line
721             #
722             # args: reference to array with 3 points p1,p2,p3
723             # p1p2 = line
724             # p3 = point for which perpendicular foot is to be calculated
725             #
726             sub PerpendicularFoot {
727 13     13 1 18 my $pointsref = $_[0];
728 13         30 my @points = @$pointsref;
729 13 50       29 if (@points != 3) {
730 0         0 carp("PerpendicularFoot needs 3 points defining a line and a point");
731 0         0 return;
732             }
733 13         22 my @p1 = @{$points[0]}; # p1,p2 = line
  13         27  
734 13         13 my @p2 = @{$points[1]};
  13         25  
735 13         13 my @p3 = @{$points[2]}; # p3 point
  13         20  
736             # vector penpenidular to line
737 13         17 my @v;
738 13         17 $v[0] = $p2[1] - $p1[1]; # y2-y1
739 13         20 $v[1] = - ($p2[0] - $p1[0]); # -(x2-x1);
740             # p4 = v + p3 is a second point of the line perpendicular to p1p2 going through p3
741 13         15 my @p4;
742 13         20 $p4[0] = $p3[0] + $v[0];
743 13         22 $p4[1] = $p3[1] + $v[1];
744 13         47 return LineIntersection([\@p1,\@p2,\@p3,\@p4]);
745             }
746             ################################################################################
747             #
748             # Calculate distance from point p to line segment p1p2
749             #
750             # args: reference to array with 3 points: p1,p2,p3
751             # p1p2 = segment
752             # p3 = point for which distance is to be calculated
753             # returns distance from p3 to line segment p1p2
754             # which is the smallest value from:
755             # distance p3p1
756             # distance p3p2
757             # perpendicular distance from p3 to line p1p2
758             #
759             sub DistanceToSegment {
760 3     3 1 5 my $pointsref = $_[0];
761 3         7 my @points = @$pointsref;
762 3 50       7 if (@points < 3) {
763 0         0 carp("DistanceToSegment needs 3 points defining a segment and a point");
764 0         0 return;
765             }
766             # the perpendicular distance is the height of the parallelogram defined
767             # by the 3 points devided by the base
768             # Note the this is a signed value so it can be used to check at which
769             # side the point is located
770             # we use dot products to find out where point is located1G/dotpro
771 3         8 my $d1 = DotProduct([$points[0],$points[1],$points[0],$points[2]]);
772 3         10 my $d2 = DotProduct([$points[0],$points[1],$points[0],$points[1]]);
773 3         10 my $dp = CrossProduct([$points[2],$points[0],$points[1]]) / sqrt $d2;
774 3 100       27 if ($d1 <= 0) {
    100          
775 1         4 return SegmentLength([$points[2],$points[0]]);
776             } elsif ($d2 <= $d1) {
777 1         4 return SegmentLength([$points[2],$points[1]]);
778             } else {
779 1         6 return $dp;
780             }
781             }
782             ################################################################################
783             #
784             # Calculate distance from point p to line p1p2
785             #
786             # args: reference to array with 3 points: p1,p2,p3
787             # p1p2 = line
788             # p3 = point for which distance is to be calculated
789             # returns 2 numbers
790             # - perpendicular distance from p3 to line p1p2
791             # - distance from p3 to line segment p1p2
792             # which is the smallest value from:
793             # distance p3p1
794             # distance p3p2
795             #
796             sub DistanceToLine {
797 1     1 1 33 my $pointsref = $_[0];
798 1         3 my @points = @$pointsref;
799 1 50       3 if (@points < 3) {
800 0         0 carp("DistanceToLine needs 3 points defining a line and a point");
801 0         0 return;
802             }
803             # the perpendicular distance is the height of the parallelogram defined
804             # by the 3 points devided by the base
805             # Note the this is a signed value so it can be used to check at which
806             # side the point is located
807             # we use dot products to find out where point is located1G/dotpro
808 1         4 my $d = DotProduct([$points[0],$points[1],$points[0],$points[1]]);
809 1         4 my $dp = CrossProduct([$points[2],$points[0],$points[1]]) / sqrt $d;
810 1         4 return $dp;
811             }
812             ################################################################################
813             #
814             # Initializer
815             #
816             sub new {
817 43     43 0 7045 my $invocant = shift;
818 43   33     171 my $class = ref($invocant) || $invocant;
819 43         76 my $self = { @_ };
820 43         114 bless($self,$class);
821 43         91 return $self;
822             }
823             ################################################################################
824             #
825             # args: reference to polygon object
826             #
827             sub points {
828 144     144 1 1068 my Math::Geometry::Planar $self = shift;
829 144 100       302 if (@_) {
830 49 50       97 if ($self->get_polygons) {
831 0         0 carp("Object is a contour - can't add points");
832 0         0 return;
833             } else {
834             # delete existing info
835 49         94 $self->{points} = ();
836 49         85 my $pointsref = shift;
837             # normalize (a single polygon has only an outer shape
838             # -> make points order counter clockwise)
839 49 100       94 if (PolygonArea($pointsref) > 0) {
840 29         58 $self->{points} = $pointsref;
841             } else {
842 20         22 $self->{points} = [reverse @{$pointsref}];
  20         67  
843             }
844             }
845             }
846 144         362 return $self->{points};
847             }
848             ################################################################################
849             #
850             # args: reference to polygon object
851             #
852             sub polygons {
853 22     22 1 523 my Math::Geometry::Planar $self = shift;
854 22 100       49 if (@_) {
855 13 50       29 if ($self->points) {
856 0         0 carp("Object is a polygon - can't add polygons");
857 0         0 return;
858             } else {
859             # delete existing info
860 13         27 $self->{polygons} = ();
861 13         29 my $polygons = shift;
862 13         14 my @polygonrefs = @{$polygons};
  13         41  
863 13         35 $self->add_polygons(@polygonrefs);
864             }
865             }
866 22         48 return $self->{polygons};
867             }
868             ################################################################################
869             #
870             # args: none
871             # returns the number of polygons in the contour
872             #
873             sub num_polygons {
874 32     32 1 41 my Math::Geometry::Planar $self = shift;
875 32         43 my $polygons = $self->{polygons};
876 32 100       90 return 0 if (! $polygons);
877 13         14 return scalar @{$polygons};
  13         45  
878             }
879             ################################################################################
880             #
881             # args: list of references to polygons
882             # returns the number of polygons in the contour
883             #
884             sub add_polygons {
885 23     23 1 82 my Math::Geometry::Planar $self = shift;
886 23 50       50 return if (! @_); # nothing to add
887             # can't add polygons to a polygon object
888 23 50       45 if ($self->points) {
889 0         0 carp("Object is a polygon - can't add polygons");
890 0         0 return;
891             }
892             # first polygon is outer polygon
893 23 100       50 if (! $self->num_polygons) {
894 19         41 my $outer = shift;
895             # counter clockwise for outer polygon
896 19 100       46 if (PolygonArea($outer) < 0) {
897 12         20 push @{$self->{polygons}}, [reverse @{$outer}];
  12         30  
  12         43  
898             } else {
899 7         10 push @{$self->{polygons}}, $outer;
  7         20  
900             }
901             }
902             # inner polygon(s)
903 23         78 while (@_) {
904             # clockwise for inner polygon
905 10         12 my $inner = shift;
906 10 100       18 if (PolygonArea($inner) > 0) {
907 3         83 push @{$self->{polygons}}, [reverse @{$inner}];
  3         6  
  3         13  
908             } else {
909 7         11 push @{$self->{polygons}}, $inner;
  7         22  
910             }
911             }
912 23         24 return scalar @{$self->{polygons}};
  23         91  
913             }
914             ################################################################################
915             #
916             # args: list of indices
917             # returns list of polygons indicated by indices
918             # (list value at position n is undefined if the index at position
919             # n is out of range)
920             # list of all polygons indicated by indices
921             #
922             sub get_polygons {
923 67     67 1 116 my Math::Geometry::Planar $self = shift;
924 67         64 my @result;
925 67         108 my $polygons = $self->{polygons};
926 67 100       197 return if (! $polygons);
927 18         23 my $i = 0;
928 18 100       36 if (@_) {
929 6         14 while (@_) {
930 7         9 my $index = int shift;
931 7 50 33     25 if ($index >= 0 && $index < num_polygons($self)) {
932 7         8 $result[$i] = ${$polygons}[$index];
  7         12  
933             } else {
934 0         0 $result[$i] = undef;
935             }
936 7         20 $i++;
937             }
938 6         16 return @result;
939             } else {
940 12         23 return @{$polygons};
  12         36  
941             }
942             }
943             ################################################################################
944             # cleanup polygon = remove colinear points
945             #
946             # args: reference to polygon or contour object
947             #
948             sub cleanup {
949 2     2 1 9 my ($self) = @_;
950 2         3 my $pointsref = $self->points;
951 2 100       5 if ($pointsref) { # polygon object
952 1         2 my @points = @$pointsref;
953 1   66     7 for (my $i=0 ; $i< @points && @points > 2 ;$i++) {
954 5 100       21 if (Colinear([$points[$i-2],$points[$i-1],$points[$i]])) {
955 1         4 splice @points,$i-1,1;
956 1         6 $i--;
957             }
958             }
959             # replace polygon points
960 1         4 $self->points([@points]);
961 1         5 return [@points];
962             } else { # contour object
963 1         3 my @polygonrefs = $self->get_polygons;
964 1         4 for (my $j = 0; $j < @polygonrefs; $j++) {
965 1         2 $pointsref = $polygonrefs[$j];
966 1         2 my @points = @$pointsref;
967 1   66     6 for (my $i=0 ; $i< @points && @points > 2 ;$i++) {
968 5 100       17 if (Colinear([$points[$i-2],$points[$i-1],$points[$i]])) {
969 1         2 splice @points,$i-1,1;
970 1         7 $i--;
971             }
972             }
973 1         14 $polygonrefs[$j] = [@points];
974             }
975 1         4 $self->polygons([@polygonrefs]);
976 1         6 return [@polygonrefs];
977             }
978             }
979             ################################################################################
980             #
981             # Ah - more vector algebra
982             # We consider every set of 3 subsequent points p1,p2,p3 on the polygon and calculate
983             # the vector product of the vectors p1p2 and p1p3. All these products should
984             # have the same sign. If the sign changes, the polygon is not convex
985             #
986             # make sure to remove colinear points first before calling perimeter
987             # (I prefer not to include the call to cleanup)
988             #
989             # args: reference to polygon or contour object
990             # (for a contour we only check the outer shape)
991             #
992             sub isconvex {
993 3     3 1 8 my ($self) = @_;
994 3         7 my $pointsref = $self->points;
995 3 100       12 if (! $pointsref) {
996 1         2 $pointsref = ($self->get_polygons(0))[0];
997 1 50       3 return if (! $pointsref); # empty object
998             }
999 3         5 my @points = @$pointsref;
1000 3 100       12 return 1 if (@points < 5); # every poly with a less then 5 points is convex
1001 2         3 my $prev = 0;
1002 2         8 for (my $i = 0 ; $i < @points ; $i++) {
1003 9         28 my $tmp = CrossProduct([$points[$i-2],$points[$i-1],$points[$i]]);
1004             # check if sign is different from pervious one(s)
1005 9 100 33     84 if ( ($prev < 0 && $tmp > 0) ||
      100        
      33        
1006             ($prev > 0 && $tmp < 0) ) {
1007 1         6 return 0;
1008             }
1009 8         24 $prev = $tmp;
1010             }
1011 1         5 return 1;
1012             }
1013             ################################################################################
1014             #
1015             # Brute force attack:
1016             # just check intersection for every segment versus every other segment
1017             # so for a polygon with n ponts this will take n**2 intersection calculations
1018             # I added a few simple improvements: to boost speed:
1019             # - don't check adjacant segments
1020             # - don't check against 'previous' segments (if we checked segment x versus y,
1021             # we don't need to check y versus x anymore)
1022             # Results in (n-2)*(n-1)/2 - 1 checks which is close to n**2/2 for large n
1023             #
1024             # make sure to remove colinear points first before calling perimeter
1025             # (I prefer not to include the call to cleanup)
1026             #
1027             # args: reference to polygon or contour object
1028             # (a contour is considered to be simple if all it's shapes are simple)
1029             #
1030             sub IsSimplePolygon {
1031 5     5 0 8 my ($pointsref) = @_;
1032 5         10 my @points = @$pointsref;
1033 5 50       11 return 1 if (@points < 4); # triangles are simple polygons ...
1034 5         13 for (my $i = 0 ; $i < @points-2 ; $i++) {
1035             # check versus all next non-adjacant edges
1036 8         19 for (my $j = $i+2 ; $j < @points ; $j++) {
1037             # don't check first versus last segment (adjacant)
1038 11 100 100     73 next if ($i == 0 && $j == @points-1);
1039 8 100       29 if (SegmentIntersection([$points[$i-1],$points[$i],$points[$j-1],$points[$j]])) {
1040 2         13 return 0;
1041             }
1042             }
1043             }
1044 3         19 return 1;
1045             }
1046             ################################################################################
1047             #
1048             # Check if polyogn or contour is simple
1049             sub issimple {
1050 4     4 1 7 my ($self) = @_;
1051 4         8 my $pointsref = $self->points;
1052 4 100       10 if ($pointsref) {
1053 2         5 return IsSimplePolygon($pointsref);
1054             } else {
1055 2         17 my @polygonrefs = $self->get_polygons;
1056 2         5 my @result;
1057 2         5 foreach (@polygonrefs) {
1058 3 100       19 return 0 if (! IsSimplePolygon($_));
1059             }
1060 1         7 return 1;
1061             }
1062             }
1063             ################################################################################
1064             # makes only sense for simple polygons
1065             # make sure to remove colinear points first before calling perimeter
1066             # (I prefer not to include the call to colinear)
1067             #
1068             # args: reference to polygon or contour object
1069             # returns the perimeter of the polygon or the perimeter of the outer shape of
1070             # the contour
1071             #
1072             sub perimeter {
1073 2     2 1 7 my ($self) = @_;
1074 2         5 my $pointsref = $self->points;
1075 2 100       7 if (! $pointsref) {
1076 1         4 $pointsref = ($self->get_polygons(0))[0];
1077 1 50       5 return if (! $pointsref); # empty object
1078             }
1079 2         90 my @points = @$pointsref;
1080 2         4 my $perimeter = 0;
1081 2 50       7 if ($pointsref) {
1082 2         4 my @points = @$pointsref;
1083 2 50       7 if (@points < 3) { # no perimeter for lines and points
1084 0         0 carp("Can't calculate perimeter: polygon should have at least 3 points");
1085 0         0 return;
1086             }
1087 2         6 for (my $index=0;$index < @points; $index++) {
1088 8         26 $perimeter += SegmentLength([$points[$index-1],$points[$index]]);
1089             }
1090             }
1091 2         9 return $perimeter;
1092             }
1093             ################################################################################
1094             # makes only sense for simple polygons
1095             # make sure to remove colinear points first before calling area
1096             # returns a signed value, can be used to find out whether
1097             # the order of points is clockwise or counter clockwise
1098             # (I prefer not to include the call to colinear)
1099             #
1100             # args: reference to an array of points
1101             #
1102             sub PolygonArea {
1103 80     80 0 117 my $pointsref = $_[0];
1104 80         156 my @points = @$pointsref;
1105 80 50       165 if (@points < 3) { # no area for lines and points
1106 0         0 carp("Can't calculate area: polygon should have at least 3 points");
1107 0         0 return;
1108             }
1109 80         112 push @points,$points[0]; # provide closure
1110 80         108 my $area = 0;
1111 80         163 while(@points >= 2){
1112 361         708 $area += $points[0]->[0]*$points[1]->[1] - $points[1]->[0]*$points[0]->[1];
1113 361         860 shift @points;
1114             }
1115 80         289 return $area/2.0;
1116             }
1117             ################################################################################
1118             # Calculates the area of a polygon or a contour
1119             # Makes only sense for simple polygons
1120             # Returns a signed value so it can be used to find out whether
1121             # the order of points in a polygon is clockwise or counter
1122             # clockwise.
1123             #
1124             # args: reference to polygon or contour object
1125             #
1126             sub area {
1127 2     2 1 50 my ($self) = @_;
1128 2         5 my $pointsref = $self->points;
1129 2         22 my $area = 0;
1130 2 100       7 if ($pointsref) {
1131 1         3 $area = PolygonArea($pointsref);
1132             } else {
1133 1         4 my @polygonrefs = $self->get_polygons;
1134 1         4 foreach (@polygonrefs) {
1135 1         8 $area += PolygonArea($_);
1136             }
1137             }
1138 2         9 return $area;
1139             }
1140             ################################################################################
1141             #
1142             # calculate the centroid of a polygon or contour
1143             # (a.k.a. the center of mass a.k.a. the center of gravity)
1144             #
1145             # The centroid is calculated as the weighted sum of the centroids
1146             # of a partition of the polygon into triangles. The centroid of a
1147             # triangle is simply the average of its three vertices, i.e., it
1148             # has coordinates (x1 + x2 + x3)/3 and (y1 + y2 + y3)/3.
1149             # In fact, the triangulation need not be a partition, but rather
1150             # can use positively and negatively oriented triangles (with positive
1151             # and negative areas), as is used when computing the area of a polygon
1152             #
1153             # makes only sense for simple polygons
1154             # make sure to remove colinear points first before calling centroid
1155             # (I prefer not to include the call to cleanup)
1156             #
1157             # args: reference to polygon object
1158             #
1159             sub centroid {
1160 1     1 1 2 my ($self) = @_;
1161 1         11 my @triangles = $self->triangulate;
1162              
1163 1 50       4 if (! @triangles) { # no result from triangulation
1164 0         0 carp("Nothing to calculate centroid for");
1165 0         0 return;
1166             }
1167              
1168 1         3 my @c;
1169             my $total_area;
1170             # triangulate
1171 1         2 foreach my $triangleref (@triangles) {
1172 6         7 my @triangle = @{$triangleref->points};
  6         14  
1173 6         17 my $area = TriangleArea([$triangle[0],$triangle[1],$triangle[2]]);
1174             # weighted centroid = area * centroid = area * sum / 3
1175             # we postpone division by 3 till we divide by total area to
1176             # minimize number of calculations
1177 6         20 $c[0] += ($triangle[0][0]+$triangle[1][0]+$triangle[2][0]) * $area;
1178 6         13 $c[1] += ($triangle[0][1]+$triangle[1][1]+$triangle[2][1]) * $area;
1179 6         12 $total_area += $area;
1180             }
1181 1         3 $c[0] = $c[0]/($total_area*3);
1182 1         3 $c[1] = $c[1]/($total_area*3);
1183 1         30 return \@c;
1184             }
1185             ################################################################################
1186             #
1187             # The winding number method has been cused here. Seems to
1188             # be the most accurate one and, if well written, it matches
1189             # the performance of the crossing number method.
1190             # The winding number method counts the number of times a polygon
1191             # winds around the point. If the result is 0, the points is outside
1192             # the polygon.
1193             #
1194             # args: reference to polygon object
1195             # reference to a point
1196             #
1197             sub IsInsidePolygon {
1198 10     10 0 17 my ($pointsref,$pointref) = @_;
1199 10         22 my @points = @$pointsref;
1200 10 50       25 if (@points < 3) { # polygon should at least have 3 points ...
1201 0         0 carp("Can't run inpolygon: polygon should have at least 3 points");
1202 0         0 return;
1203             }
1204 10 50       22 if (! $pointref) {
1205 0         0 carp("Can't run inpolygon: no point entered");
1206 0         0 return;
1207             }
1208 10         19 my @point = @$pointref;
1209 10         12 my $wn; # thw winding number counter
1210 10         24 for (my $i = 0 ; $i < @points ; $i++) {
1211 52         170 my $cp = CrossProduct([$points[$i-1],$points[$i],$pointref]);
1212             # if colinear and in between the 2 points of the polygon
1213             # segment, it's on the perimeter and considered inside
1214 52 100       136 if ($cp == 0) {
1215 3 0 33     43 if (
      0        
      33        
1216             ((($points[$i-1][0] <= $point[0] &&
1217             $point[0] <= $points[$i][0])) ||
1218             (($points[$i-1][0] >= $point[0] &&
1219             $point[0] >= $points[$i][0])))
1220             &&
1221             ((($points[$i-1][1] <= $$pointref[1] &&
1222             $point[1] <= $points[$i][1])) ||
1223             (($points[$i-1][1] >= $point[1] &&
1224             $point[1] >= $points[$i][1])))
1225             ) {
1226 0         0 return 1;
1227             }
1228             }
1229 52 100       111 if ($points[$i-1][1] <= $point[1]) { # start y <= P.y
1230 29 100       98 if ($points[$i][1] > $point[1]) { # // an upward crossing
1231 8 100       20 if ($cp > 0) {
1232             # point left of edge
1233 6         21 $wn++; # have a valid up intersect
1234             }
1235             }
1236             } else { # start y > P.y (no test needed)
1237 23 100       68 if ($points[$i][1] <= $point[1]) { # a downward crossing
1238 8 100       29 if ($cp < 0) {
1239             # point right of edge
1240 2         8 $wn--; # have a valid down intersect
1241             }
1242             }
1243             }
1244             }
1245 10         52 return $wn;
1246             }
1247             ################################################################################
1248             #
1249             # Check if polygon inside polygon or contour
1250             # (for a contour, a point is inside when it's within the outer shape and
1251             # not within one of the inner shapes (holes) )
1252             sub isinside {
1253 8     8 1 21 my ($self,$pointref) = @_;
1254 8         16 my $pointsref = $self->points;
1255 8 100       35 if ($pointsref) {
1256 2         7 return IsInsidePolygon($pointsref,$pointref);
1257             } else {
1258 6         15 my @polygonrefs = $self->get_polygons;
1259 6 100       19 return 0 if (! IsInsidePolygon($polygonrefs[0],$pointref));
1260 4         5 my @result;
1261 4         13 for (my $i = 1; $i <@polygonrefs; $i++) {
1262 2 100       15 return 0 if (IsInsidePolygon($polygonrefs[$i],$pointref));
1263             }
1264 3         13 return 1;
1265             }
1266             }
1267             ################################################################################
1268             #
1269             # a counter clockwise rotation over an angle a is given by the formula
1270             #
1271             # / x2 \ / cos(a) -sin(a) \ / x1 \
1272             # | | = | | | |
1273             # \ y2 / \ sin(a) cos(a) / \ y1 /
1274             #
1275             # args: reference to polygon object
1276             # angle (in radians)
1277             # reference to center point (use origin if no center point entered)
1278             #
1279             sub RotatePolygon {
1280 1     1 0 3 my ($pointsref,$angle,$center) = @_;
1281 1         2 my $xc = 0;
1282 1         2 my $yc = 0;
1283 1 50       15 if ($center) {
1284 1         4 my @point = @$center;
1285 1         2 $xc = $point[0];
1286 1         3 $yc = $point[1];
1287             }
1288 1 50       6 if ($pointsref) {
1289 1         3 my @points = @$pointsref;
1290 1         3 my @result;
1291 1         14 for (my $i = 0 ; $i < @points ; $i++) {
1292 4         64 my $x = $xc + cos($angle)*($points[$i][0] - $xc) - sin($angle)*($points[$i][1] - $yc);
1293 4         17 my $y = $yc + sin($angle)*($points[$i][0] - $xc) + cos($angle)*($points[$i][1] - $yc);
1294 4         9 $result[$i][0] = $x;
1295 4         15 $result[$i][1] = $y;
1296             }
1297 1         7 return [@result];
1298             }
1299             }
1300             ################################################################################
1301             #
1302             # rotate jpolygon or contour
1303             #
1304             sub rotate {
1305 1     1 1 3 my ($self,$angle,$center) = @_;
1306 1         5 my $rotate = Math::Geometry::Planar->new;
1307 1         5 my $pointsref = $self->points;
1308 1 50       6 if ($pointsref) {
1309 1         5 $rotate->points(RotatePolygon($pointsref,$angle,$center));
1310             } else {
1311 0         0 my @polygonrefs = $self->get_polygons;
1312 0         0 my @result;
1313 0         0 foreach (@polygonrefs) {
1314 0         0 $rotate->add_polygons(RotatePolygon($_,$angle,$center));
1315             }
1316             }
1317 1         4 return $rotate;
1318             }
1319             ################################################################################
1320             #
1321             # move a polygon over a distance in x and y direction
1322             #
1323             # args: reference to polygon object
1324             # X offset
1325             # y offset
1326             #
1327             sub MovePolygon {
1328 1     1 0 3 my ($pointsref,$dx,$dy) = @_;
1329 1 50       5 if ($pointsref) {
1330 1         3 my @points = @$pointsref;
1331 1         7 for (my $i = 0 ; $i < @points ; $i++) {
1332 4         8 $points[$i][0] = $points[$i][0] + $dx;
1333 4         11 $points[$i][1] = $points[$i][1] + $dy;
1334             }
1335 1         6 return [@points];
1336             }
1337             }
1338             ################################################################################
1339             #
1340             # Move polygon or contour
1341             #
1342             sub move {
1343 1     1 1 8 my ($self,$dx,$dy) = @_;
1344 1         5 my $move = Math::Geometry::Planar->new;
1345 1         4 my $pointsref = $self->points;
1346 1 50       4 if ($pointsref) {
1347 1         4 $move->points(MovePolygon($pointsref,$dx,$dy));
1348             } else {
1349 0         0 my @polygonrefs = $self->get_polygons;
1350 0         0 my @result;
1351 0         0 foreach (@polygonrefs) {
1352 0         0 $move->add_polygons(MovePolygon($_,$dx,$dy));
1353             }
1354             }
1355 1         5 return $move;
1356             }
1357             ################################################################################
1358             #
1359             # mirror in x direction - vertical axis through point referenced by $center
1360             # if no center entered, use y axis
1361             #
1362             # args: reference to polygon object
1363             # reference to center
1364             #
1365             sub MirrorXPolygon {
1366 1     1 0 3 my ($pointsref,$center) = @_;
1367 1         4 my @points = @$pointsref;
1368 1 50       4 if (@points == 0) { # nothing to mirror
1369 0         0 carp("Nothing to mirror ...");
1370 0         0 return;
1371             }
1372 1         20 my $xc = 0;
1373 1         2 my $yc = 0;
1374 1 50       4 if ($center) {
1375 1         3 my @point = @$center;
1376 1         2 $xc = $point[0];
1377 1         3 $yc = $point[1];
1378             }
1379 1         5 for (my $i = 0 ; $i < @points ; $i++) {
1380 4         12 $points[$i][0] = 2*$xc - $points[$i][0];
1381             }
1382 1         5 return [@points];
1383             }
1384             ################################################################################
1385             #
1386             # mirror polygon or contour in x direction
1387             # (vertical axis through point referenced by $center)
1388             sub mirrorx {
1389 1     1 1 10 my ($self,$dx,$dy) = @_;
1390 1         4 my $mirrorx = Math::Geometry::Planar->new;
1391 1         4 my $pointsref = $self->points;
1392 1 50       5 if ($pointsref) {
1393 1         4 $mirrorx->points(MirrorXPolygon($pointsref,$dx,$dy));
1394             } else {
1395 0         0 my @polygonrefs = $self->get_polygons;
1396 0         0 my @result;
1397 0         0 foreach (@polygonrefs) {
1398 0         0 $mirrorx->add_polygons(MirrorXPolygon($_,$dx,$dy));
1399             }
1400             }
1401 1         5 return $mirrorx;
1402             }
1403             ################################################################################
1404             #
1405             # mirror in y direction - horizontal axis through point referenced by $center
1406             # if no center entered, use x axis
1407             #
1408             # args: reference to polygon object
1409             # reference to center
1410             #
1411             sub MirrorYPolygon {
1412 1     1 0 2 my ($pointsref,$center) = @_;
1413 1         4 my @points = @$pointsref;
1414 1 50       16 if (@points == 0) { # nothing to mirror
1415 0         0 carp("Nothing to mirror ...");
1416 0         0 return;
1417             }
1418 1         3 my $xc = 0;
1419 1         2 my $yc = 0;
1420 1 50       5 if ($center) {
1421 1         4 my @point = @$center;
1422 1         3 $xc = $point[0];
1423 1         2 $yc = $point[1];
1424             }
1425 1         5 for (my $i = 0 ; $i < @points ; $i++) {
1426 4         14 $points[$i][1] = 2*$yc - $points[$i][1];
1427             }
1428 1         7 return [@points];
1429             }
1430             ################################################################################
1431             #
1432             # mirror polygon or contour in x direction
1433             # (vertical axis through point referenced by $center)
1434             sub mirrory {
1435 1     1 1 11 my ($self,$dx,$dy) = @_;
1436 1         5 my $mirrory = Math::Geometry::Planar->new;
1437 1         12 my $pointsref = $self->points;
1438 1 50       4 if ($pointsref) {
1439 1         5 $mirrory->points(MirrorYPolygon($pointsref,$dx,$dy));
1440             } else {
1441 0         0 my @polygonrefs = $self->get_polygons;
1442 0         0 my @result;
1443 0         0 foreach (@polygonrefs) {
1444 0         0 $mirrory->add_polygons(MirrorYPolygon($_,$dx,$dy));
1445             }
1446             }
1447 1         4 return $mirrory;
1448             }
1449             ################################################################################
1450             #
1451             # mirror around axis determined by 2 points (p1p2)
1452             #
1453             # args: reference to polygon object
1454             # reference to array with to points defining reflection axis
1455             #
1456             sub MirrorPolygon {
1457 1     1 0 3 my ($pointsref,$axisref) = @_;
1458 1         3 my @points = @$pointsref;
1459 1         4 my @axis = @$axisref;
1460 1 50       3 if (@axis != 2) { # need 2 points defining axis
1461 0         0 carp("Can't mirror: 2 points need to define axis");
1462 0         0 return;
1463             }
1464 1         3 my $p1ref = $axis[0];
1465 1         2 my $p2ref = $axis[1];
1466 1         3 my @p1 = @$p1ref;
1467 1         2 my @p2 = @$p2ref;
1468 1 50       4 if (@points == 0) { # nothing to mirror
1469 0         0 carp("Nothing to mirror ...");
1470 0         0 return;
1471             }
1472 1         12 for (my $i = 0 ; $i < @points ; $i++) {
1473 4         15 my $footref = PerpendicularFoot([\@p1,\@p2,$points[$i]]);
1474 4         13 my @foot = @$footref;
1475 4         13 $points[$i][0] = $foot[0] - ($points[$i][0] - $foot[0]);
1476 4         17 $points[$i][1] = $foot[1] - ($points[$i][1] - $foot[1]);
1477             }
1478 1         7 return [@points];
1479             }
1480             ################################################################################
1481             #
1482             # mirror polygon or contour around axis determined by 2 points (p1p2)
1483             #
1484             sub mirror {
1485 1     1 1 10 my ($self,$axisref) = @_;
1486 1         4 my $mirror = Math::Geometry::Planar->new;
1487 1         5 my $pointsref = $self->points;
1488 1 50       5 if ($pointsref) {
1489 1         3 $mirror->points(MirrorPolygon($pointsref,$axisref));
1490             } else {
1491 0         0 my @polygonrefs = $self->get_polygons;
1492 0         0 my @result;
1493 0         0 foreach (@polygonrefs) {
1494 0         0 $mirror->add_polygons(MirrorPolygon($_,$axisref));
1495             }
1496             }
1497 1         5 return $mirror;
1498             }
1499             ################################################################################
1500             #
1501             # scale polygon from center
1502             # I would choose the centroid ...
1503             #
1504             # args: reference to polygon object
1505             # scale factor
1506             # reference to center point
1507             #
1508             sub ScalePolygon {
1509 1     1 0 3 my ($pointsref,$scale,$center) = @_;
1510 1         2 my @points = @$pointsref;
1511 1 50       5 if (@points == 0) { # nothing to scale
1512 0         0 carp("Nothing to scale ...");
1513 0         0 return;
1514             }
1515 1         2 my $xc = 0;
1516 1         2 my $yc = 0;
1517 1 50       4 if ($center) {
1518 1         3 my @point = @$center;
1519 1         2 $xc = $point[0];
1520 1         2 $yc = $point[1];
1521             }
1522             # subtract center, scale and add center again
1523 1         5 for (my $i = 0 ; $i < @points ; $i++) {
1524 4         9 $points[$i][0] = $scale * ($points[$i][0] - $xc) + $xc;
1525 4         20 $points[$i][1] = $scale * ($points[$i][1] - $yc) + $yc;
1526             }
1527 1         7 return [@points];
1528             }
1529             ################################################################################
1530             #
1531             # scale polygon from center
1532             # I would choose the centroid ...
1533             #
1534             sub scale {
1535 1     1 1 10 my ($self,$factor,$center) = @_;
1536 1         5 my $scale = Math::Geometry::Planar->new;
1537 1         4 my $pointsref = $self->points;
1538 1 50       4 if ($pointsref) {
1539 1         4 $scale->points(ScalePolygon($pointsref,$factor,$center));
1540             } else {
1541 0         0 my @polygonrefs = $self->get_polygons;
1542 0         0 my @result;
1543 0         0 foreach (@polygonrefs) {
1544 0         0 $scale->add_polygons(ScalePolygon($_,$factor,$center));
1545             }
1546             }
1547 1         10 return $scale;
1548             }
1549             ################################################################################
1550             #
1551             # The "bounding box" of a set of points is the box with horizontal
1552             # and vertical edges that contains all points
1553             #
1554             # args: reference to array of points or a contour
1555             # returns reference to array of 4 points representing bounding box
1556             #
1557             sub bbox {
1558 1     1 1 9 my ($self) = @_;
1559 1         3 my $bbox = Math::Geometry::Planar->new;
1560 1         5 my $pointsref = $self->points;
1561 1 50       4 if (! $pointsref) {
1562 1         4 $pointsref = ($self->get_polygons(0))[0];
1563 1 50       4 return if (! $pointsref); # empty object
1564             }
1565 1         4 my @points = @$pointsref;
1566 1 50       3 if (@points < 3) { # polygon should at least have 3 points ...
1567 0         0 carp("Can't determine bbox: polygon should have at least 3 points");
1568 0         0 return;
1569             }
1570 1         3 my $min_x = $points[0][0];
1571 1         2 my $min_y = $points[0][1];
1572 1         2 my $max_x = $points[0][0];
1573 1         2 my $max_y = $points[0][1];
1574 1         18 for (my $i = 1 ; $i < @points ; $i++) {
1575 4 50       9 $min_x = $points[$i][0] if ($points[$i][0] < $min_x);
1576 4 50       7 $min_y = $points[$i][1] if ($points[$i][1] < $min_y);
1577 4 100       9 $max_x = $points[$i][0] if ($points[$i][0] > $max_x);
1578 4 100       13 $max_y = $points[$i][1] if ($points[$i][1] > $max_y);
1579             }
1580 1         13 $bbox->points([[$min_x,$min_y],
1581             [$min_x,$max_y],
1582             [$max_x,$max_y],
1583             [$max_x,$min_y]]);
1584 1         6 return $bbox;
1585             }
1586             ################################################################################
1587             #
1588             # The "minimal enclosing rectangle" of a set of points is the box with minimal area
1589             # that contains all points.
1590             # We'll use the rotating calipers method here which works only on convex polygons
1591             # so before calling minbbox, create the convex hull first for the set of points
1592             # (taking into account whether or not the set of points represents a polygon).
1593             #
1594             # args: reference to array of points representing a convex polygon
1595             # returns reference to array of 4 points representing minimal bounding rectangle
1596             #
1597             sub minrectangle {
1598 2     2 1 90 my ($self) = @_;
1599 2         6 my $minrectangle = Math::Geometry::Planar->new;
1600 2         5 my $pointsref = $self->points;
1601 2 50       5 if (! $pointsref) {
1602 2         6 $pointsref = ($self->get_polygons(0))[0];
1603 2 50       6 return if (! $pointsref); # empty object
1604             }
1605 2         5 my @points = @$pointsref;
1606 2 50       6 if (@points < 3) { # polygon should at least have 3 points ...
1607 0         0 carp("Can't determine minrectangle: polygon should have at least 3 points");
1608 0         0 return;
1609             }
1610 2         3 my $d;
1611             # scan all segments and for each segment, calculate the area of the bounding
1612             # box that has one side coinciding with the segment
1613 2         2 my $min_area = 0;
1614 2         3 my @indices;
1615 2         6 for (my $i = 0 ; $i < @points ; $i++) {
1616             # for each segment, find the point (vertex) at the largest perpendicular distance
1617             # the opposite side of the current rectangle runs through this point
1618 12         13 my $mj; # index of point at maximum distance
1619 12         11 my $maxj = 0; # maximum distance (squared)
1620             # Get coefficients of the implicit line equation ax + by +c = 0
1621             # Do NOT normalize since scaling by a constant
1622             # is irrelevant for just comparing distances.
1623 12         26 my $a = $points[$i-1][1] - $points[$i][1];
1624 12         25 my $b = $points[$i][0] - $points[$i-1][0];
1625 12         26 my $c = $points[$i-1][0] * $points[$i][1] - $points[$i][0] * $points[$i-1][1];
1626             # loop through point array testing for max distance to current segment
1627 12         32 for (my $j = -1 ; $j < @points-1 ; $j++) {
1628 74 100 100     256 next if ($j == $i || $j == $i-1); # exclude points of current segment
1629             # just use dist squared (sqrt not needed for comparison)
1630             # since the polygon is convex, all points are at the same side
1631             # so we don't need to take the absolute value for dist
1632 52         85 my $dist = $a * $points[$j][0] + $b * $points[$j][1] + $c;
1633 52 100       120 if ($dist > $maxj) { # this point is further
1634 25         24 $mj = $j; # so have a new maximum
1635 25         56 $maxj = $dist;
1636             }
1637             }
1638             # the line -bx+ay+c=0 is perpendicular to ax+by+c=0
1639             # now find index of extreme points corresponding to perpendicular line
1640             # initialize to first point (note that points of current segment could
1641             # be one or even both of the extreme points)
1642 12         14 my $mk = 0;
1643 12         10 my $ml = 0;
1644 12         33 my $mink = -$b * $points[0][0] + $a * $points[0][1] + $c;
1645 12         19 my $maxl = -$b * $points[0][0] + $a * $points[0][1] + $c;
1646 12         30 for (my $j = 1 ; $j < @points ; $j++) {
1647             # use signed dist to get extreme points
1648 62         101 my $dist = -$b * $points[$j][0] + $a * $points[$j][1] + $c;
1649 62 100       100 if ($dist < $mink) { # this point is further
1650 15         19 $mk = $j; # so have a new maximum
1651 15         15 $mink = $dist;
1652             }
1653 62 100       151 if ($dist > $maxl) { # this point is further
1654 15         16 $ml = $j; # so have a new maximum
1655 15         34 $maxl = $dist;
1656             }
1657             }
1658             # now $maxj/sqrt(a**2+b**2) is the height of the current rectangle
1659             # and (|$mink| + |$maxl|)/sqrt(a**2+b**2) is the width
1660             # since area is width*height we can waste the costly sqrt function
1661 12         28 my $area = abs($maxj * ($mink-$maxl)) / ($a**2 +$b**2);
1662 12 100 100     65 if ($area < $min_area || ! $min_area) {
1663 3         5 $min_area = $area;
1664 3         11 @indices = ($i,$mj,$mk,$ml);
1665             }
1666             }
1667 2         5 my ($i,$j,$k,$l) = @indices;
1668             # Finally, get the corners of the minimum enclosing rectangle
1669 2         11 my $p1 = PerpendicularFoot([$points[$i-1],$points[$i],$points[$k]]);
1670 2         11 my $p2 = PerpendicularFoot([$points[$i-1],$points[$i],$points[$l]]);
1671             # now we calculate the second point on the line parallel to
1672             # the segment i going through the vertex j
1673 2         12 my $p = [$points[$j][0]+$points[$i-1][0]-$points[$i][0],
1674             $points[$j][1]+$points[$i-1][1]-$points[$i][1]];
1675 2         9 my $p3 = PerpendicularFoot([$points[$j],$p,$points[$l]]);
1676 2         10 my $p4 = PerpendicularFoot([$points[$j],$p,$points[$k]]);
1677 2         13 $minrectangle->points([$p1,$p2,$p3,$p4]);
1678 2         13 return $minrectangle;
1679             }
1680             ################################################################################
1681             #
1682             # triangulate polygon or contour
1683             #
1684             # args: polygon or contour object
1685             # returns a reference to an array triangles
1686             #
1687             sub triangulate {
1688 2     2 1 18 my ($self) = @_;
1689 2         6 my $pointsref = $self->points;
1690 2         4 my @triangles;
1691 2 100       7 if ($pointsref) {
1692 1         2 @triangles = @{TriangulatePolygon([$pointsref])};
  1         4  
1693             } else {
1694 1         4 my $polygonrefs = $self->polygons;
1695 1 50       5 if ($polygonrefs) {
1696 1         2 @triangles = @{TriangulatePolygon($polygonrefs)};
  1         6  
1697             }
1698             }
1699 2         7 my @result;
1700 2         7 foreach (@triangles) {
1701 14         44 my $triangle = Math::Geometry::Planar->new;
1702 14         33 $triangle->points($_);
1703 14         27 push @result,$triangle;
1704             }
1705 2         17 return @result;
1706             }
1707             ################################################################################
1708             #
1709             # convexhull using the Melkman algorithm
1710             # (the set of input points represent a polygon and are thus ordered
1711             #
1712             # args: reference to ordered array of points representing a polygon
1713             # or contour (for a contour, we calculate the hull for the
1714             # outer shape)
1715             # returns a reference to an array of the convex hull vertices
1716             #
1717             sub convexhull {
1718 1     1 1 9 my ($self) = @_;
1719 1         4 my $pointsref = $self->points;
1720 1 50       6 if (! $pointsref) {
1721 0         0 $pointsref = ($self->get_polygons(0))[0];
1722 0 0       0 return if (! $pointsref); # empty object
1723             }
1724 1         8 my @points = @$pointsref;
1725 1 50       5 return ([@points]) if (@points < 5); # need at least 5 points
1726             # initialize a deque D[] from bottom to top so that the
1727             # 1st tree vertices of V[] are a counterclockwise triangle
1728 1         3 my @result;
1729 1         2 my $bot = @points-2;
1730 1         2 my $top = $bot+3; # initial bottom and top deque indices
1731 1         2 $result[$bot] = $points[2]; # 3rd vertex is at both bot and top
1732 1         3 $result[$top] = $points[2]; # 3rd vertex is at both bot and top
1733 1 50       6 if (CrossProduct([$points[0], $points[1], $points[2]]) > 0) {
1734 0         0 $result[$bot+1] = $points[0];
1735 0         0 $result[$bot+2] = $points[1]; # ccw vertices are: 2,0,1,2
1736             } else {
1737 1         14 $result[$bot+1] = $points[1];
1738 1         4 $result[$bot+2] = $points[0]; # ccw vertices are: 2,1,0,2
1739             }
1740              
1741             # compute the hull on the deque D[]
1742 1         6 for (my $i=3; $i < @points; $i++) { # process the rest of vertices
1743             # test if next vertex is inside the deque hull
1744 4 100 66     15 if ((CrossProduct([$result[$bot], $result[$bot+1], $points[$i]]) > 0) &&
1745             (CrossProduct([$result[$top-1], $result[$top], $points[$i]]) > 0) ) {
1746 1         3 last; # skip an interior vertex
1747             }
1748              
1749             # incrementally add an exterior vertex to the deque hull
1750             # get the rightmost tangent at the deque bot
1751 3         17 while (CrossProduct([$result[$bot], $result[$bot+1], $points[$i]]) <= 0) {
1752 4         17 ++$bot; # remove bot of deque
1753             }
1754 3         10 $result[--$bot] = $points[$i]; # insert $points[i] at bot of deque
1755              
1756             # get the leftmost tangent at the deque top
1757 3         11 while (CrossProduct([$result[$top-1], $result[$top], $points[$i]]) <= 0) {
1758 1         6 --$top; # pop top of deque
1759             }
1760 3         14 $result[++$top] = $points[$i]; #/ push $points[i] onto top of deque
1761             }
1762              
1763             # transcribe deque D[] to the output hull array H[]
1764 1         3 my @returnval;
1765 1         16 for (my $h = 0; $h <= ($top-$bot-1); $h++) {
1766 4         13 $returnval[$h] = $result[$bot + $h];
1767             }
1768 1         8 my $hull = Math::Geometry::Planar->new;
1769 1         6 $hull->points([@returnval]);
1770 1         6 return $hull;
1771             }
1772             ################################################################################
1773             #
1774             # convexhull using Andrew's monotone chain 2D convex hull algorithm
1775             # returns a reference to an array of the convex hull vertices
1776             #
1777             # args: reference to array of points (doesn't really need to be a polygon)
1778             # (also works for a contour - however, since a contour should consist
1779             # of polygons - which are ordered sets of points - the algorithm
1780             # above will be faster)
1781             # returns a reference to an array of the convex hull vertices
1782             #
1783             sub convexhull2 {
1784 1     1 1 9 my ($self) = @_;
1785 1         4 my $pointsref = $self->points;
1786 1 50       4 if (! $pointsref) {
1787 0         0 $pointsref = ($self->get_polygons(0))[0];
1788 0 0       0 return if (! $pointsref); # empty object
1789             }
1790 1         4 my @points = @$pointsref;
1791 1 50       6 return ([@points]) if (@points < 5); # need at least 5 points
1792             # first, sort the points by increasing x and y-coordinates
1793 1         12 @points = sort ByXY (@points);
1794             # Get the indices of points with min x-coord and min|max y-coord
1795 1         2 my @hull;
1796 1         2 my $bot = 0;
1797 1         2 my $top = -1;
1798 1         3 my $minmin = 0;
1799 1         3 my $minmax;
1800 1         2 my $xmin = $points[0][0];
1801 1         5 for (my $i = 1 ; $i < @points ; $i++) {
1802 2 100       8 if ($points[$i][0] != $xmin) {
1803 1         10 $minmax = $i - 1;
1804             last
1805 1         3 }
1806             }
1807 1 50       3 if ($minmax == @points-1) { # degenerate case: all x-coords == xmin
1808 0         0 $hull[++$top] = $points[$minmin];
1809 0 0       0 if ($points[$minmax][1] != $points[$minmin][1]) { # a nontrivial segment
1810 0         0 $hull[$==$top] = $points[$minmax];
1811 0         0 return [@points];
1812             }
1813             }
1814              
1815             # Get the indices of points with max x-coord and min|max y-coord
1816 1         3 my $maxmin = 0;
1817 1         3 my $maxmax = @points - 1;
1818 1         4 my $xmax = $points[@points-1][0];
1819 1         5 for (my $i = @points - 2 ; $i >= 0 ; $i--) {
1820 1 50       6 if ($points[$i][0] != $xmax) {
1821 1         3 $maxmin = $i + 1;
1822 1         2 last;
1823             }
1824             }
1825              
1826             # Compute the lower hull on the stack @lower
1827 1         3 $hull[++$top] = $points[$minmin]; # push minmin point onto stack
1828 1         1 my $i = $minmax;
1829 1         4 while (++$i <= $maxmin) {
1830             # the lower line joins points[minmin] with points[maxmin]
1831 8 100 100     23 if (CrossProduct([$points[$minmin],$points[$maxmin],$points[$i]]) >= 0 && $i < $maxmin) {
1832 3         10 next; # ignore points[i] above or on the lower line
1833             }
1834 5         17 while ($top > 0) { # there are at least 2 points on the stack
1835             # test if points[i] is left of the line at the stack top
1836 6 100       21 if (CrossProduct([$hull[$top-1], $hull[$top], $points[$i]]) > 0) {
1837 4         6 last; # points[i] is a new hull vertex
1838             } else {
1839 2         6 $top--;
1840             }
1841             }
1842 5         16 $hull[++$top] = $points[$i]; # push points[i] onto stack
1843             }
1844              
1845             # Next, compute the upper hull on the stack above the bottom hull
1846 1 50       6 if ($maxmax != $maxmin) { # if distinct xmax points
1847 0         0 $hull[++$top] = $points[$maxmax]; # push maxmax point onto stack
1848             }
1849 1         2 $bot = $top;
1850 1         11 $i = $maxmin;
1851 1         40 while (--$i >= $minmax) {
1852             # the upper line joins points[maxmax] with points[minmax]
1853 8 100 100     25 if (CrossProduct([$points[$maxmax],$points[$minmax],$points[$i]]) >= 0 && $i > $minmax) {
1854 4         11 next; # ignore points[i] below or on the upper line
1855             }
1856 4         12 while ($top > $bot) { # at least 2 points on the upper stack
1857             # test if points[i] is left of the line at the stack top
1858 4 100       27 if (CrossProduct([$hull[$top-1],$hull[$top],$points[$i]]) > 0) {
1859 2         3 last; # points[i] is a new hull vertex
1860             } else {
1861 2         7 $top--;
1862             }
1863             }
1864 4         11 $hull[++$top] = $points[$i]; # push points[i] onto stack
1865             }
1866 1         5 $#hull = $top;
1867 1 50       4 if ($minmax == $minmin) {
1868 0         0 shift @hull; # remove joining endpoint from stack
1869             }
1870 1         6 my $hull = Math::Geometry::Planar->new;
1871 1         6 $hull->points([@hull]);
1872 1         7 return $hull;
1873             }
1874             ################################################################################
1875             #
1876             # Offset polygons
1877             #
1878             sub offset_polygon {
1879 0     0 1 0 my ($self,$offset,$canvas) = @_;
1880 0         0 my $offset_polygons;
1881 0         0 my $pointsref = $self->points;
1882 0 0       0 if ($pointsref) {
1883 0         0 return [OffsetPolygon($pointsref,$offset,$canvas)];
1884             } else {
1885 0         0 carp("Can't offset contours - only polygons");
1886 0         0 return;
1887             }
1888             }
1889             ################################################################################
1890             #
1891             # Sorting function to surt points first by X coordinate, then by Y coordinate
1892             #
1893             sub ByXY {
1894 22     22 0 31 my @p1 = @$a;
1895 22         26 my @p2 = @$b;
1896 22         25 my $result = $p1[0] <=> $p2[0];
1897 22 100       42 if ($result){
1898 19         29 return $result;
1899             } else {
1900 3         8 return $p1[1] <=> $p2[1];
1901             }
1902             }
1903             ################################################################################
1904             #
1905             # convert polygon/contour to gpc contour
1906             #
1907             sub convert2gpc {
1908 5     5 1 27 my ($self,$dx,$dy) = @_;
1909 5         6 my @polygons;
1910 5         11 my $pointsref = $self->points;
1911 5 100       13 if ($pointsref) {
1912 3         4 push @polygons,$pointsref;
1913             } else {
1914 2         7 @polygons = $self->get_polygons;
1915             }
1916 5         14 foreach (@polygons) {
1917 8         8 my @points = @{$_};
  8         15  
1918 8 50       25 if (@points < 3) { # need at least 3 points
1919 0         0 carp("Can't convert to gpc structure: polygon should have at least 3 points");
1920 0         0 return;
1921             }
1922             }
1923 5         38 my $contour = Math::Geometry::Planar::GPC::new_gpc_polygon();
1924 5         14 Math::Geometry::Planar::GPC::gpc_polygon_num_contours_set($contour,scalar(@polygons));
1925             # array for hole pointers
1926 5         21 my $hole_array = Math::Geometry::Planar::GPC::int_array(scalar(@polygons));
1927 5         23 Math::Geometry::Planar::GPC::gpc_polygon_hole_set($contour,$hole_array);
1928 5         20 my $vlist = Math::Geometry::Planar::GPC::gpc_vertex_list_array(scalar(@polygons));
1929 5         472 for (my $i = 0; $i < @polygons; $i++) {
1930 8 100       21 if ($i == 0) {
1931 5         11 Math::Geometry::Planar::GPC::int_set($hole_array,$i,0);
1932             } else {
1933 3         19 Math::Geometry::Planar::GPC::int_set($hole_array,$i,1);
1934             }
1935 8         11 my @points = @{$polygons[$i]};
  8         20  
1936 8         8 my @gpc_vertexlist;
1937 8         12 foreach my $vertex (@points) {
1938 32         76 my $v = Math::Geometry::Planar::GPC::new_gpc_vertex();
1939 32         76 Math::Geometry::Planar::GPC::gpc_vertex_x_set($v,$$vertex[0]);
1940 32         47 Math::Geometry::Planar::GPC::gpc_vertex_y_set($v,$$vertex[1]);
1941 32         57 push @gpc_vertexlist,$v;
1942             }
1943 8         22 my $va = create_gpc_vertex_array(@gpc_vertexlist);
1944 8         31 my $vl = Math::Geometry::Planar::GPC::new_gpc_vertex_list();
1945 8         18 Math::Geometry::Planar::GPC::gpc_vertex_list_vertex_set($vl,$va);
1946 8         18 Math::Geometry::Planar::GPC::gpc_vertex_list_num_vertices_set($vl,scalar(@points));
1947 8         53 Math::Geometry::Planar::GPC::gpc_vertex_list_set($vlist,$i,$vl);
1948             }
1949 5         15 Math::Geometry::Planar::GPC::gpc_polygon_contour_set($contour,$vlist);
1950 5         23 return $contour;
1951             }
1952             ################################################################################
1953             #
1954             # convert gpc object to a set of contours
1955             # A gpc contour object can consist of multiple outer shapes each having holes,
1956             #
1957             sub Gpc2Polygons {
1958 6     6 1 31 my ($gpc) = @_;
1959 6         7 my @result; # array with contours
1960             my @inner; # array holding the inner polygons
1961 0         0 my @outer; # array holding the outer polygons
1962 6         14 my $num_contours = Math::Geometry::Planar::GPC::gpc_polygon_num_contours_get($gpc);
1963 6         19 my $contour = Math::Geometry::Planar::GPC::gpc_polygon_contour_get($gpc);
1964 6         16 my $hole_array = Math::Geometry::Planar::GPC::gpc_polygon_hole_get($gpc);
1965             # for each shape of the gpc object
1966 6         20 for (my $i = 0 ; $i < $num_contours ; $i++) {
1967 10         18 my @polygon;
1968             # get the hole flag
1969 10         22 my $hole = Math::Geometry::Planar::GPC::int_get($hole_array,$i);
1970             # get the vertices
1971 10         27 my $vl = Math::Geometry::Planar::GPC::gpc_vertex_list_get($contour,$i);
1972 10         23 my $num_vertices = Math::Geometry::Planar::GPC::gpc_vertex_list_num_vertices_get($vl);
1973 10         27 my $va = Math::Geometry::Planar::GPC::gpc_vertex_list_vertex_get($vl);
1974 10         25 for (my $j = 0 ; $j < $num_vertices ; $j++) {
1975 60         143 my $v = Math::Geometry::Planar::GPC::gpc_vertex_get($va,$j);
1976 60         115 my $x = Math::Geometry::Planar::GPC::gpc_vertex_x_get($v);
1977 60         95 my $y = Math::Geometry::Planar::GPC::gpc_vertex_y_get($v);
1978 60         198 push @polygon,[$x,$y];
1979             }
1980             # create lists of inner and outer shapes
1981 10 100       19 if ($hole) {
1982 2         7 push @inner,[@polygon];
1983             } else {
1984 8         44 push @outer,[@polygon];
1985             }
1986             }
1987             # shortcut: if there is only one outer shape, we're done
1988 6 100       13 if (@outer == 1) {
1989 4         13 my $obj = Math::Geometry::Planar->new;
1990 4         13 $obj->add_polygons(@outer,@inner);
1991 4         6 push @result,$obj;
1992             } else {
1993 2         6 foreach (@outer) {
1994             # create contour for each outer shape
1995 4         16 my $obj = Math::Geometry::Planar->new;
1996 4         14 $obj->polygons([$_]);
1997 4         9 push @result,$obj;
1998             # if an inner shape has at least one point inside this
1999             # outer shape, it belongs to this outer shape (so all
2000             # points are inside it)
2001 4         7 my $i = 0;
2002 4         13 while ($i < @inner) {
2003 3         5 my @polygon = @{$inner[$i]};
  3         8  
2004 3 100       13 if ($obj->isinside($polygon[0])) {
2005 2         5 $obj->add_polygons($inner[$i]);
2006 2         10 splice @inner,$i,1;
2007             } else {
2008 1         4 $i++;
2009             }
2010             }
2011             }
2012             }
2013 6         34 return @result;
2014             }
2015             ################################################################################
2016             #
2017             # gpc polygon clipping operatins
2018             #
2019             sub GpcClip {
2020 6     6 1 305 my ($op,$gpc_poly_1,$gpc_poly_2) = @_;
2021 6         22 my $result = Math::Geometry::Planar::GPC::new_gpc_polygon();
2022             SWITCH: {
2023 6 100       8 ($op eq "DIFFERENCE") && do {
  6         19  
2024 2         69 Math::Geometry::Planar::GPC::gpc_polygon_clip(0,$gpc_poly_1,$gpc_poly_2,$result);
2025 2         12 return $result;
2026             };
2027 4 100       11 ($op eq "INTERSECTION") && do {
2028 1         12 Math::Geometry::Planar::GPC::gpc_polygon_clip(1,$gpc_poly_1,$gpc_poly_2,$result);
2029 1         4 return $result;
2030             };
2031 3 100       8 ($op eq "XOR") && do {
2032 1         20 Math::Geometry::Planar::GPC::gpc_polygon_clip(2,$gpc_poly_1,$gpc_poly_2,$result);
2033 1         4 return $result;
2034             };
2035 2 50       5 ($op eq "UNION") && do {
2036 2         26 Math::Geometry::Planar::GPC::gpc_polygon_clip(3,$gpc_poly_1,$gpc_poly_2,$result);
2037 2         6 return $result;
2038             };
2039 0         0 return;
2040             }
2041             }
2042             ###############################################################################
2043             #
2044             # create gpc vertex array pointer
2045             #
2046             sub create_gpc_vertex_array {
2047 8     8 0 13 my $len = scalar(@_);
2048 8         22 my $va = Math::Geometry::Planar::GPC::gpc_vertex_array($len);
2049 8         17 for (my $i=0; $i<$len; $i++) {
2050 32         35 my $val = shift;
2051 32         75 Math::Geometry::Planar::GPC::gpc_vertex_set($va,$i,$val);
2052             }
2053 8         12 return $va;
2054             }
2055             ################################################################################
2056             #
2057             my $pi = atan2(1,1) * 4;
2058             #
2059             ################################################################################
2060             #
2061             # convert a circle to a polygon
2062             # arguments: first argument is the number of segments,
2063             # the other arguments are:
2064             # p1,p2,p3 : 3 points on the circle
2065             # or
2066             # center,p1 : center and a point on the circle
2067             # or
2068             # center,radius : the center and the radius of the circle
2069             #
2070             sub CircleToPoly {
2071 3     3 1 346 my @args = @_;
2072 3         5 my @result;
2073 3         3 my ($segments,$p1,$p2,$p3,$center,$radius);
2074 3 100       13 if (@args == 4) { # 3 points
    50          
2075 1         5 ($segments,$p1,$p2,$p3) = @args;
2076 1         5 $center = CalcCenter($p1,$p2,$p3);
2077 1         6 $radius = SegmentLength([$p1,$center]);
2078             } elsif (@args == 3) {
2079 2 100       6 if (ref $args[2]) { # center + 1 point
2080 1         3 ($segments,$center,$p1) = @args;
2081 1         4 $radius = SegmentLength([$p1,$center]);
2082             } else { # center + radius
2083 1         3 ($segments,$center,$radius) = @args;
2084             }
2085             } else {
2086 0         0 return;
2087             }
2088 3         10 my $angle = ($pi * 2) / $segments;
2089 3         11 for (my $i = 0 ; $i < $segments ; $i++) {
2090 24         68 push @result, [${$center}[0] + $radius * cos($angle * $i),
  24         114  
2091 24         26 ${$center}[1] + $radius * sin($angle * $i)]
2092             }
2093 3         9 my $poly = Math::Geometry::Planar->new;
2094 3         21 $poly->points([@result]);
2095 3         15 return $poly;
2096             }
2097             ################################################################################
2098             #
2099             # convert an arc to a polygon
2100             # arguments: first argument is the number of segments,
2101             # the other arguments are:
2102             # p1,p2,p3 : startpoint, intermediate point, endpoint
2103             # or
2104             # $center,p1,p2,$dir : center, startpoint, endpoint, direction
2105             # direction 0 counter clockwise
2106             # 1 clockwise
2107             # Note: the return value is a set of points, NOT a closed polygon !!!
2108             #
2109             sub ArcToPoly {
2110 3     3 1 262 my @args = @_;
2111 3         4 my @result;
2112 3         5 my ($segments,$p1,$p2,$p3,$center,$direction);
2113 0         0 my ($radius,$angle);
2114 0         0 my ($start_angle, $end_angle);
2115 3 100       11 if (@args == 4) { # 3 points
    50          
2116 1         3 ($segments,$p1,$p2,$p3) = @args;
2117 1         3 $center = CalcCenter($p1,$p2,$p3);
2118 1         5 $radius = SegmentLength([$p1,$center]);
2119             # calculate start and end angles
2120 1         6 $start_angle = CalcAngle($center,$p1);
2121 1         4 my $mid_angle = CalcAngle($center,$p2);
2122 1         4 $end_angle = CalcAngle($center,$p3);
2123 1 0 33     16 if ( (($mid_angle < $start_angle) && ($start_angle < $end_angle)) ||
      0        
      33        
      0        
      0        
2124             (($start_angle < $end_angle) && ($end_angle < $mid_angle)) ||
2125             (($end_angle < $mid_angle) && ($mid_angle < $start_angle)) ) {
2126 1         3 $direction = 1;
2127             }
2128 1         3 $angle = $end_angle - $start_angle;
2129             } elsif (@args == 5) { # center, begin, end, direction
2130 2         5 ($segments,$center,$p1,$p3,$direction) = @args;
2131 2         6 $radius = SegmentLength([$p1,$center]);
2132             # calculate start and end angles
2133 2         6 $start_angle = CalcAngle($center,$p1);
2134 2         5 $end_angle = CalcAngle($center,$p3);
2135 2         3 $angle = $end_angle - $start_angle;
2136             } else {
2137 0         0 return;
2138             }
2139              
2140 3 100       7 if ($direction) { # clockwise
2141 2 100       7 if ($angle > 0) {
2142 1         3 $angle = $angle - ($pi * 2);
2143             }
2144             } else {
2145 1 50       5 if ($angle < 0) {
2146 1         3 $angle = $angle + ($pi * 2);
2147             }
2148             }
2149 3         4 $angle = $angle / $segments;
2150              
2151 3         4 push @result,$p1; # start point
2152 3         14 for (my $i = 1 ; $i < $segments ; $i++) {
2153 11         30 push @result, [${$center}[0] + $radius * cos($start_angle + $angle * $i),
  11         48  
2154 11         12 ${$center}[1] + $radius * sin($start_angle + $angle * $i)]
2155             }
2156 3         5 push @result,$p3; # end point
2157 3         13 return [@result];
2158             }
2159             ################################################################################
2160             #
2161             # Calculate the center of a circle going through 3 points
2162             #
2163             sub CalcCenter {
2164 2     2 0 6 my ($p1_ref, $p2_ref, $p3_ref) = @_;
2165 2         4 my ($x1,$y1) = @{$p1_ref};
  2         4  
2166 2         5 my ($x2,$y2) = @{$p2_ref};
  2         6  
2167 2         4 my ($x3,$y3) = @{$p3_ref};
  2         4  
2168             # calculate midpoints of line segments p1p2 p2p3
2169 2         6 my $u1 = ($x1 + $x2)/2;
2170 2         6 my $v1 = ($y1 + $y2)/2;
2171 2         3 my $u2 = ($x2 + $x3)/2;
2172 2         3 my $v2 = ($y2 + $y3)/2;
2173             # linear equations y = a + bx
2174 2         4 my ($a1,$a2);
2175 0         0 my ($b1,$b2);
2176             # intersect (center) coordinates
2177 0         0 my ($xi,$yi);
2178             # slope of perpendicular = -1/slope
2179 2 50       6 if ($y1 != $y2) {
2180 2         5 $b1 = - ($x1 - $x2)/($y1 - $y2);
2181 2         6 $a1 = $v1 - $b1 * $u1;
2182             } else {
2183 0         0 $xi = $u1;
2184             }
2185 2 50       5 if ($y2 != $y3) {
2186 2         4 $b2 = - ($x2 - $x3)/($y2 - $y3);
2187 2         6 $a2 = $v2 - $b2 * $u2;
2188             } else {
2189 0         0 $xi = $u2;
2190             }
2191             # parallel lines (colinear is also parallel)
2192 2 50 33     33 return if ($b1 == $b2 || (!$b1 && !$b2));
      33        
2193 2 50       15 $xi = - ($a1 - $a2)/($b1 - $b2) if (!$xi);
2194 2 50       6 $yi = $a1 + $b1 * $xi if ($b1);
2195 2 50       7 $yi = $a2 + $b2 * $xi if ($b1);
2196 2         7 return [($xi,$yi)];
2197             }
2198             ################################################################################
2199             #
2200             # calculate angel of vector p1p2
2201             #
2202             sub CalcAngle {
2203 7     7 0 9 my ($p1_ref,$p2_ref) = @_;
2204 7         9 my ($x1,$y1) = @{$p1_ref};
  7         13  
2205 7         7 my ($x2,$y2) = @{$p2_ref};
  7         12  
2206 7 50 66     33 return 0 if ($y1 == $y2 && $x1 == $x2);
2207 7 50 66     24 return 0 if ($y1 == $y2 && $x1 < $x2);
2208 7 100 66     27 return $pi if ($y1 == $y2 && $x1 > $x2);
2209 4 100 66     23 return $pi/2 if ($x1 == $x2 && $y1 < $y2);
2210 1 50 33     9 return ($pi *3)/2 if ($x1 == $x2 && $y1 > $y2);
2211 0         0 my $angle = atan2($y2-$y1,$x2-$x1);
2212 0         0 return $angle;
2213             }
2214             ################################################################################
2215             #
2216             # This program is an implementation of a fast polygon
2217             # triangulation algorithm based on the paper "A simple and fast
2218             # incremental randomized algorithm for computing trapezoidal
2219             # decompositions and for triangulating polygons" by Raimund Seidel.
2220             #
2221             # The algorithm handles simple polygons with holes. The input is
2222             # specified as contours. The outermost contour is anti-clockwise, while
2223             # all the inner contours must be clockwise. No point should be repeated
2224             # in the input. A sample input file 'data_1' is provided.
2225             #
2226             # The output is a reference to a list of triangles. Each triangle
2227             # is ar reference to an array fo three points, each point is a reference
2228             # to an array holdign the x and y coordinates of the point.
2229             # The number of output triangles produced for a polygon with n points is,
2230             # (n - 2) + 2*(#holes)
2231             #
2232             # The program is a translation to perl of the C program written by
2233             # Narkhede A. and Manocha D., Fast polygon triangulation algorithm based
2234             # on Seidel's Algorithm, UNC-CH, 1994.
2235             # Note that in this perl version, there are no statically allocated arrays
2236             # so the only limit is the amount of (virtual) memory available.
2237             #
2238             # See also:
2239             #
2240             # R. Seidel
2241             # "A simple and Fast Randomized Algorithm for Computing Trapezoidal
2242             # Decompositions and for Triangulating Polygons"
2243             # "Computational Geometry Theory & Applications"
2244             # Number = 1, Year 1991, Volume 1, Pages 51-64
2245             #
2246             # J. O'Rourke
2247             # "Computational Geometry in {C}"
2248             # Cambridge University Press - 1994
2249             #
2250             # Input specified as a contour with the restrictions mentioned above:
2251             # - first polygon is the outer shape and must be anti-clockwise.
2252             # - next polygons are inner shapels (holes) must be clockwise.
2253             # - Inner and outer shapes must be simple .
2254             #
2255             # Every contour is specified by giving all its points in order. No
2256             # point shoud be repeated. i.e. if the outer contour is a square,
2257             # only the four distinct endpoints shopudl be specified in order.
2258             #
2259             # Returns a reference to an array holding the triangles.
2260             #
2261              
2262             my $C_EPS = 1e-10; # tolerance value: Used for making
2263             # all decisions about collinearity or
2264             # left/right of segment. Decrease
2265             # this value if the input points are
2266             # spaced very close together
2267              
2268             my $INFINITY = 1<<29;
2269              
2270             my $TRUE = 1;
2271             my $FALSE = 0;
2272              
2273             my $T_X = 1;
2274             my $T_Y = 2;
2275             my $T_SINK = 3;
2276              
2277             my $ST_VALID = 1;
2278             my $ST_INVALID = 2;
2279              
2280             my $FIRSTPT = 1;
2281             my $LASTPT = 2;
2282              
2283             my $S_LEFT = 1;
2284             my $S_RIGHT = 2;
2285              
2286             my $SP_SIMPLE_LRUP = 1; # for splitting trapezoids
2287             my $SP_SIMPLE_LRDN = 2;
2288             my $SP_2UP_2DN = 3;
2289             my $SP_2UP_LEFT = 4;
2290             my $SP_2UP_RIGHT = 5;
2291             my $SP_2DN_LEFT = 6;
2292             my $SP_2DN_RIGHT = 7;
2293             my $SP_NOSPLIT = -1;
2294              
2295             my $TRI_LHS = 1;
2296             my $TRI_RHS = 2;
2297             my $TR_FROM_UP = 1; # for traverse-direction
2298             my $TR_FROM_DN = 2;
2299              
2300             my $choose_idx = 1;
2301             my @permute;
2302             my $q_idx;
2303             my $tr_idx;
2304             my @qs; # Query structure
2305             my @tr; # Trapezoid structure
2306             my @seg; # Segment table
2307              
2308             my @mchain; # Table to hold all the monotone
2309             # polygons . Each monotone polygon
2310             # is a circularly linked list
2311             my @vert; # chain init. information. This
2312             # is used to decide which
2313             # monotone polygon to split if
2314             # there are several other
2315             # polygons touching at the same
2316             # vertex
2317             my @mon; # contains position of any vertex in
2318             # the monotone chain for the polygon
2319             my @visited;
2320             my @op; # contains the resulting list of triangles
2321             # and their vertex number
2322             my ($chain_idx, $op_idx, $mon_idx);
2323              
2324             sub TriangulatePolygon {
2325              
2326 2     2 0 4 $choose_idx = 1;
2327 2         27 @seg = ();
2328 2         34 @mchain = ();
2329 2         20 @vert = ();
2330 2         33 @mon = ();
2331 2         6 @visited = ();
2332 2         7 @op = ();
2333              
2334 2         3 my ($polygonrefs) = @_;
2335 2         3 my @polygons = @{$polygonrefs};
  2         6  
2336              
2337 2         6 my $ccount = 0;
2338 2         3 my $i = 1;
2339 2         8 while ($ccount < @polygons) {
2340 3         5 my @vertexarray = @{$polygons[$ccount]};
  3         16  
2341 3         6 my $npoints = @vertexarray;
2342 3         7 my $first = $i;
2343 3         5 my $last = $first + $npoints - 1;
2344 3         9 for (my $j = 0; $j < $npoints; $j++, $i++) {
2345 16         16 my @vertex = @{$vertexarray[$j]};
  16         31  
2346 16         45 $seg[$i]{v0}{x} = $vertex[0];
2347 16         29 $seg[$i]{v0}{y} = $vertex[1];
2348 16 100       44 if ($i == $last) {
    100          
2349 3         11 $seg[$i]{next} = $first;
2350 3         7 $seg[$i]{prev} = $i-1;
2351 3         5 my %tmp = %{$seg[$i]{v0}};
  3         17  
2352 3         8 $seg[$i-1]{v1} = \%tmp;
2353             } elsif ($i == $first) {
2354 3         7 $seg[$i]{next} = $i+1;
2355 3         8 $seg[$i]{prev} = $last;
2356 3         5 my %tmp = %{$seg[$i]{v0}};
  3         17  
2357 3         11 $seg[$last]{v1} = \%tmp;
2358             } else {
2359 10         15 $seg[$i]{prev} = $i-1;
2360 10         15 $seg[$i]{next} = $i+1;
2361 10         10 my %tmp = %{$seg[$i]{v0}};
  10         28  
2362 10         23 $seg[$i-1]{v1} = \%tmp;
2363             }
2364 16         50 $seg[$i]{is_inserted} = $FALSE;
2365             }
2366 3         10 $ccount++;
2367             }
2368              
2369 2         4 my $n = $i-1;
2370              
2371 2         14 _generate_random_ordering($n);
2372 2         9 _construct_trapezoids($n);
2373 2         10 my $nmonpoly = _monotonate_trapezoids($n);
2374 2         7 my $ntriangles = _triangulate_monotone_polygons($n, $nmonpoly);
2375             # now get the coordinates for all the triangles
2376 2         5 my @result;
2377 2         16 for (my $i = 0; $i < $ntriangles; $i++) {
2378 14         13 my @vertices = @{$op[$i]};
  14         29  
2379 14         99 my $triangle = [[$seg[$vertices[0]]{v0}{x},$seg[$vertices[0]]{v0}{y}],
2380             [$seg[$vertices[1]]{v0}{x},$seg[$vertices[1]]{v0}{y}],
2381             [$seg[$vertices[2]]{v0}{x},$seg[$vertices[2]]{v0}{y}]];
2382 14         52 push @result,$triangle;
2383             }
2384 2         15 return [@result];;
2385             }
2386              
2387             # Generate a random permutation of the segments 1..n
2388             sub _generate_random_ordering {
2389 2     2   5 @permute = ();
2390 2         3 my ($n) = @_;
2391 2         3 my @input;
2392 2         5 for (my $i = 1 ; $i <= $n ; $i++) {
2393 16         31 $input[$i] = $i;
2394             }
2395 2         3 my $i = 1;
2396 2         8 for (my $i = 1 ; $i <= $n ; $i++) {
2397 16         99 my $m = int rand($#input) + 1;
2398 16         25 $permute[$i] = $input[$m];
2399 16         44 splice @input,$m,1;
2400             }
2401             }
2402              
2403             # Return the next segment in the generated random ordering of all the
2404             # segments in S
2405             sub _choose_segment {
2406 16     16   50 return $permute[$choose_idx++];
2407             }
2408              
2409             # Return a new node to be added into the query tree
2410             sub _newnode {
2411 92     92   157 return $q_idx++;
2412             }
2413              
2414             # Return a free trapezoid
2415             sub _newtrap {
2416 47     47   112 $tr[$tr_idx]{lseg} = -1;
2417 47         60 $tr[$tr_idx]{rseg} = -1;
2418 47         72 $tr[$tr_idx]{state} = $ST_VALID;
2419             # next statements added to prevent 'uninitialized' warnings
2420 47         79 $tr[$tr_idx]{d0} = 0;
2421 47         57 $tr[$tr_idx]{d1} = 0;
2422 47         66 $tr[$tr_idx]{u0} = 0;
2423 47         59 $tr[$tr_idx]{u1} = 0;
2424 47         118 $tr[$tr_idx]{usave} = 0;
2425 47         63 $tr[$tr_idx]{uside} = 0;
2426 47         87 return $tr_idx++;
2427             }
2428              
2429             # Floating point number comparison
2430             sub _fp_equal {
2431 312     312   416 my ($X, $Y, $POINTS) = @_;
2432 312         311 my ($tX, $tY);
2433 312         1039 $tX = sprintf("%.${POINTS}g", $X);
2434 312         695 $tY = sprintf("%.${POINTS}g", $Y);
2435 312         1575 return $tX eq $tY;
2436             }
2437              
2438             # Return the maximum of the two points
2439             sub _max {
2440 2     2   4 my ($v0_ref, $v1_ref) = @_;
2441 2         3 my %v0 = %{$v0_ref};
  2         9  
2442 2         4 my %v1 = %{$v1_ref};
  2         14  
2443 2 100       14 if ($v0{y} > $v1{y} + $C_EPS) {
    50          
2444 1         9 return \%v0;
2445             } elsif (_fp_equal($v0{y}, $v1{y}, $precision)) {
2446 0 0       0 if ($v0{x} > $v1{x} + $C_EPS) {
2447 0         0 return \%v0;
2448             } else {
2449 0         0 return \%v1;
2450             }
2451             } else {
2452 1         6 return \%v1;
2453             }
2454             }
2455              
2456             # Return the minimum of the two points
2457             sub _min {
2458 2     2   5 my ($v0_ref, $v1_ref) = @_;
2459 2         4 my %v0 = %{$v0_ref};
  2         7  
2460 2         4 my %v1 = %{$v1_ref};
  2         5  
2461 2 100       14 if ($v0{y} < $v1{y} - $C_EPS) {
    50          
2462 1         11 return \%v0;
2463             } elsif (_fp_equal($v0{y}, $v1{y}, $precision)) {
2464 0 0       0 if ($v0{x} < $v1{x}) {
2465 0         0 return \%v0;
2466             } else {
2467 0         0 return \%v1;
2468             }
2469             } else {
2470 1         7 return \%v1;
2471             }
2472             }
2473              
2474             sub _greater_than {
2475 154     154   186 my ($v0_ref, $v1_ref) = @_;
2476 154         153 my %v0 = %{$v0_ref};
  154         413  
2477 154         210 my %v1 = %{$v1_ref};
  154         324  
2478 154 100       554 if ($v0{y} > $v1{y} + $C_EPS) {
    100          
2479 61         247 return 1;
2480             } elsif ($v0{y} < $v1{y} - $C_EPS) {
2481 54         196 return 0;
2482             } else {
2483 39         178 return ($v0{x} > $v1{x});
2484             }
2485             }
2486              
2487             sub _equal_to {
2488 134     134   182 my ($v0_ref, $v1_ref) = @_;
2489 134         137 my %v0 = %{$v0_ref};
  134         352  
2490 134         172 my %v1 = %{$v1_ref};
  134         291  
2491 134   100     304 return ( _fp_equal($v0{y}, $v1{y}, $precision) &&
2492             _fp_equal($v0{x}, $v1{x}, $precision) );
2493             }
2494              
2495             sub _greater_than_equal_to {
2496 88     88   121 my ($v0_ref, $v1_ref) = @_;
2497 88         90 my %v0 = %{$v0_ref};
  88         260  
2498 88         115 my %v1 = %{$v1_ref};
  88         200  
2499 88 100       313 if ($v0{y} > $v1{y} + $C_EPS) {
    100          
2500 24         93 return 1;
2501             } elsif ($v0{y} < $v1{y} - $C_EPS) {
2502 7         34 return 0;
2503             } else {
2504 57         262 return ($v0{x} >= $v1{x});
2505             }
2506             }
2507              
2508             sub _less_than {
2509 32     32   43 my ($v0_ref, $v1_ref) = @_;
2510 32         39 my %v0 = %{$v0_ref};
  32         115  
2511 32         45 my %v1 = %{$v1_ref};
  32         91  
2512 32 100       126 if ($v0{y} < $v1{y} - $C_EPS) {
    100          
2513 5         17 return 1;
2514             } elsif ($v0{y} > $v1{y} + $C_EPS) {
2515 10         28 return 0;
2516             } else {
2517 17         74 return ($v0{x} < $v1{x});
2518             }
2519             }
2520              
2521             # Initilialise the query structure (Q) and the trapezoid table (T)
2522             # when the first segment is added to start the trapezoidation. The
2523             # query-tree starts out with 4 trapezoids, one S-node and 2 Y-nodes
2524             #
2525             # 4
2526             # -----------------------------------
2527             # \
2528             # 1 \ 2
2529             # \
2530             # -----------------------------------
2531             # 3
2532             #
2533              
2534             sub _init_query_structure {
2535 2     2   5 my ($segnum) = @_;
2536              
2537 2         4 my ($i1,$i2,$i3,$i4,$i5,$i6,$i7,$root);
2538 0         0 my ($t1,$t2,$t3,$t4);
2539              
2540 2         52 @qs = ();
2541 2         60 @tr = ();
2542              
2543 2         5 $q_idx = $tr_idx = 1;
2544              
2545 2         7 $i1 = _newnode();
2546 2         6 $qs[$i1]{nodetype} = $T_Y;
2547              
2548 2         3 my %tmpmax = %{_max($seg[$segnum]{v0}, $seg[$segnum]{v1})}; # root
  2         13  
2549 2         14 $qs[$i1]{yval} = {x => $tmpmax{x} , y => $tmpmax{y}};
2550 2         3 $root = $i1;
2551              
2552 2         4 $qs[$i1]{right} = $i2 = _newnode();
2553 2         7 $qs[$i2]{nodetype} = $T_SINK;
2554 2         4 $qs[$i2]{parent} = $i1;
2555              
2556 2         5 $qs[$i1]{left} = $i3 = _newnode();
2557 2         5 $qs[$i3]{nodetype} = $T_Y;
2558 2         5 my %tmpmin = %{_min($seg[$segnum]{v0}, $seg[$segnum]{v1})}; # root
  2         8  
2559 2         17 $qs[$i3]{yval} = {x => $tmpmin{x} , y => $tmpmin{y}};
2560 2         4 $qs[$i3]{parent} = $i1;
2561              
2562 2         5 $qs[$i3]{left} = $i4 = _newnode();
2563 2         5 $qs[$i4]{nodetype} = $T_SINK;
2564 2         3 $qs[$i4]{parent} = $i3;
2565              
2566 2         10 $qs[$i3]{right} = $i5 = _newnode();
2567 2         6 $qs[$i5]{nodetype} = $T_X;
2568 2         4 $qs[$i5]{segnum} = $segnum;
2569 2         3 $qs[$i5]{parent} = $i3;
2570              
2571 2         4 $qs[$i5]{left} = $i6 = _newnode();
2572 2         11 $qs[$i6]{nodetype} = $T_SINK;
2573 2         3 $qs[$i6]{parent} = $i5;
2574              
2575 2         4 $qs[$i5]{right} = $i7 = _newnode();
2576 2         5 $qs[$i7]{nodetype} = $T_SINK;
2577 2         3 $qs[$i7]{parent} = $i5;
2578              
2579 2         6 $t1 = _newtrap(); # middle left
2580 2         4 $t2 = _newtrap(); # middle right
2581 2         4 $t3 = _newtrap(); # bottom-most
2582 2         6 $t4 = _newtrap(); # topmost
2583              
2584 2         14 $tr[$t1]{hi} = {x => $qs[$i1]{yval}{x} , y => $qs[$i1]{yval}{y}};
2585 2         12 $tr[$t2]{hi} = {x => $qs[$i1]{yval}{x} , y => $qs[$i1]{yval}{y}};
2586 2         14 $tr[$t4]{lo} = {x => $qs[$i1]{yval}{x} , y => $qs[$i1]{yval}{y}};
2587 2         8 $tr[$t1]{lo} = {x => $qs[$i3]{yval}{x} , y => $qs[$i3]{yval}{y}};
2588 2         10 $tr[$t2]{lo} = {x => $qs[$i3]{yval}{x} , y => $qs[$i3]{yval}{y}};
2589 2         16 $tr[$t3]{hi} = {x => $qs[$i3]{yval}{x} , y => $qs[$i3]{yval}{y}};
2590 2         6 $tr[$t4]{hi} = {x => $INFINITY , y => $INFINITY};
2591 2         8 $tr[$t3]{lo} = {x => -1 * $INFINITY , y => -1 * $INFINITY};
2592 2         5 $tr[$t1]{rseg} = $tr[$t2]{lseg} = $segnum;
2593 2         5 $tr[$t1]{u0} = $tr[$t2]{u0} = $t4;
2594 2         4 $tr[$t1]{d0} = $tr[$t2]{d0} = $t3;
2595 2         6 $tr[$t4]{d0} = $tr[$t3]{u0} = $t1;
2596 2         4 $tr[$t4]{d1} = $tr[$t3]{u1} = $t2;
2597              
2598 2         5 $tr[$t1]{sink} = $i6;
2599 2         4 $tr[$t2]{sink} = $i7;
2600 2         4 $tr[$t3]{sink} = $i4;
2601 2         3 $tr[$t4]{sink} = $i2;
2602              
2603 2         6 $tr[$t1]{state} = $tr[$t2]{state} = $ST_VALID;
2604 2         4 $tr[$t3]{state} = $tr[$t4]{state} = $ST_VALID;
2605              
2606 2         4 $qs[$i2]{trnum} = $t4;
2607 2         11 $qs[$i4]{trnum} = $t3;
2608 2         4 $qs[$i6]{trnum} = $t1;
2609 2         3 $qs[$i7]{trnum} = $t2;
2610              
2611 2         5 $seg[$segnum]{is_inserted} = $TRUE;
2612 2         8 return $root;
2613             }
2614              
2615             # Update the roots stored for each of the endpoints of the segment.
2616             # This is done to speed up the location-query for the endpoint when
2617             # the segment is inserted into the trapezoidation subsequently
2618             #
2619             sub _find_new_roots {
2620 32     32   42 my ($segnum) = @_;
2621              
2622 32 100       99 return if ($seg[$segnum]{is_inserted});
2623              
2624 14         35 $seg[$segnum]{root0} = _locate_endpoint($seg[$segnum]{v0}, $seg[$segnum]{v1}, $seg[$segnum]{root0});
2625 14         55 $seg[$segnum]{root0} = $tr[$seg[$segnum]{root0}]{sink};
2626              
2627 14         37 $seg[$segnum]{root1} = _locate_endpoint($seg[$segnum]{v1}, $seg[$segnum]{v0}, $seg[$segnum]{root1});
2628 14         72 $seg[$segnum]{root1} = $tr[$seg[$segnum]{root1}]{sink};
2629             }
2630              
2631             # Main routine to perform trapezoidation
2632             sub _construct_trapezoids {
2633 2     2   4 my ($nseg) = @_; #
2634              
2635             # Add the first segment and get the query structure and trapezoid
2636             # list initialised
2637              
2638 2         7 my $root = _init_query_structure(_choose_segment());
2639              
2640 2         8 for (my $i = 1 ; $i <= $nseg; $i++) {
2641 16         45 $seg[$i]{root0} = $seg[$i]{root1} = $root;
2642             }
2643 2         13 for (my $h = 1; $h <= _math_logstar_n($nseg); $h++) {
2644 4         15 for (my $i = _math_N($nseg, $h -1) + 1; $i <= _math_N($nseg, $h); $i++) {
2645 10         20 _add_segment(_choose_segment());
2646             }
2647             # Find a new root for each of the segment endpoints
2648 4         12 for (my $i = 1; $i <= $nseg; $i++) {
2649 32         55 _find_new_roots($i);
2650             }
2651             }
2652 2         7 for (my $i = _math_N($nseg, _math_logstar_n($nseg)) + 1; $i <= $nseg; $i++) {
2653 4         7 _add_segment(_choose_segment());
2654             }
2655             }
2656              
2657             # Add in the new segment into the trapezoidation and update Q and T
2658             # structures. First locate the two endpoints of the segment in the
2659             # Q-structure. Then start from the topmost trapezoid and go down to
2660             # the lower trapezoid dividing all the trapezoids in between .
2661             #
2662              
2663             sub _add_segment {
2664 14     14   20 my ($segnum) = @_;
2665              
2666 14         14 my ($tu, $tl, $sk, $tfirst, $tlast, $tnext);
2667 0         0 my ($tfirstr, $tlastr, $tfirstl, $tlastl);
2668 0         0 my ($i1, $i2, $t, $t1, $t2, $tn);
2669 14         17 my $tritop = 0;
2670 14         15 my $tribot = 0;
2671 14         14 my $is_swapped = 0;
2672 14         16 my $tmptriseg;
2673 14         16 my %s = %{$seg[$segnum]};
  14         79  
2674              
2675 14 100       48 if (_greater_than($s{v1}, $s{v0})) { # Get higher vertex in v0
2676 7         10 my %tmp;
2677 7         8 %tmp = %{$s{v0}};
  7         23  
2678 7         33 $s{v0} = {x => $s{v1}{x} , y => $s{v1}{y}};
2679 7         21 $s{v1} = {x => $tmp{x} , y => $tmp{y}};
2680 7         11 my $tmp = $s{root0};
2681 7         10 $s{root0} = $s{root1};
2682 7         8 $s{root1} = $tmp;
2683 7         16 $is_swapped = 1;
2684             }
2685              
2686 14 100       42 if (($is_swapped) ? !_inserted($segnum, $LASTPT) :
    100          
2687             !_inserted($segnum, $FIRSTPT)) { # insert v0 in the tree
2688 8         8 my $tmp_d;
2689              
2690 8         23 $tu = _locate_endpoint($s{v0}, $s{v1}, $s{root0});
2691 8         22 $tl = _newtrap(); # tl is the new lower trapezoid
2692 8         13 $tr[$tl]{state} = $ST_VALID;
2693 8         13 my %tmp = %{$tr[$tu]};
  8         84  
2694 8         16 my %tmphi = %{$tmp{hi}};
  8         27  
2695 8         16 my %tmplo = %{$tmp{lo}};
  8         23  
2696 8         17 $tr[$tl] = \%tmp;
2697 8         37 $tr[$tl]{hi} = {x => $tmphi{x} , y => $tmphi{y}};
2698 8         25 $tr[$tl]{lo} = {x => $tmplo{x} , y => $tmplo{y}};
2699 8         26 $tr[$tu]{lo} = {x => $s{v0}{x} , y => $s{v0}{y}};
2700 8         30 $tr[$tl]{hi} = {x => $s{v0}{x} , y => $s{v0}{y}};
2701 8         18 $tr[$tu]{d0} = $tl;
2702 8         14 $tr[$tu]{d1} = 0;
2703 8         13 $tr[$tl]{u0} = $tu;
2704 8         11 $tr[$tl]{u1} = 0;
2705              
2706 8 100 66     42 if ((($tmp_d = $tr[$tl]{d0}) > 0) && ($tr[$tmp_d]{u0} == $tu)) {
2707 7         12 $tr[$tmp_d]{u0} = $tl;
2708             }
2709 8 50 66     39 if ((($tmp_d = $tr[$tl]{d0}) > 0) && ($tr[$tmp_d]{u1} == $tu)) {
2710 0         0 $tr[$tmp_d]{u1} = $tl;
2711             }
2712              
2713 8 100 66     37 if ((($tmp_d = $tr[$tl]{d1}) > 0) && ($tr[$tmp_d]{u0} == $tu)) {
2714 4         7 $tr[$tmp_d]{u0} = $tl;
2715             }
2716 8 50 66     32 if ((($tmp_d = $tr[$tl]{d1}) > 0) && ($tr[$tmp_d]{u1} == $tu)) {
2717 0         0 $tr[$tmp_d]{u1} = $tl;
2718             }
2719              
2720             # Now update the query structure and obtain the sinks for the
2721             # two trapezoids
2722              
2723 8         17 $i1 = _newnode(); # Upper trapezoid sink
2724 8         16 $i2 = _newnode(); # Lower trapezoid sink
2725 8         14 $sk = $tr[$tu]{sink};
2726              
2727 8         14 $qs[$sk]{nodetype} = $T_Y;
2728 8         32 $qs[$sk]{yval} = {x => $s{v0}{x} , y=> $s{v0}{y}};
2729 8         14 $qs[$sk]{segnum} = $segnum; # not really reqd ... maybe later
2730 8         18 $qs[$sk]{left} = $i2;
2731 8         10 $qs[$sk]{right} = $i1;
2732              
2733 8         20 $qs[$i1]{nodetype} = $T_SINK;
2734 8         22 $qs[$i1]{trnum} = $tu;
2735 8         12 $qs[$i1]{parent} = $sk;
2736              
2737 8         16 $qs[$i2]{nodetype} = $T_SINK;
2738 8         17 $qs[$i2]{trnum} = $tl;
2739 8         18 $qs[$i2]{parent} = $sk;
2740              
2741 8         13 $tr[$tu]{sink} = $i1;
2742 8         11 $tr[$tl]{sink} = $i2;
2743 8         23 $tfirst = $tl;
2744             } else { # v0 already present
2745             # Get the topmost intersecting trapezoid
2746 6         17 $tfirst = _locate_endpoint($s{v0}, $s{v1}, $s{root0});
2747 6         19 $tritop = 1;
2748             }
2749              
2750              
2751 14 100       44 if (($is_swapped) ? !_inserted($segnum, $FIRSTPT) :
    100          
2752             !_inserted($segnum, $LASTPT)) { # insert v1 in the tree
2753 4         8 my $tmp_d;
2754              
2755 4         11 $tu = _locate_endpoint($s{v1}, $s{v0}, $s{root1});
2756 4         13 $tl = _newtrap(); # tl is the new lower trapezoid
2757 4         9 $tr[$tl]{state} = $ST_VALID;
2758 4         5 my %tmp = %{$tr[$tu]};
  4         27  
2759 4         8 my %tmphi = %{$tmp{hi}};
  4         13  
2760 4         6 my %tmplo = %{$tmp{lo}};
  4         10  
2761 4         9 $tr[$tl] = \%tmp;
2762 4         19 $tr[$tl]{hi} = {x => $tmphi{x} , y => $tmphi{y}};
2763 4         12 $tr[$tl]{lo} = {x => $tmplo{x} , y => $tmplo{y}};
2764 4         15 $tr[$tu]{lo} = {x => $s{v1}{x} , y => $s{v1}{y}};
2765 4         15 $tr[$tl]{hi} = {x => $s{v1}{x} , y => $s{v1}{y}};
2766 4         9 $tr[$tu]{d0} = $tl;
2767 4         7 $tr[$tu]{d1} = 0;
2768 4         7 $tr[$tl]{u0} = $tu;
2769 4         6 $tr[$tl]{u1} = 0;
2770              
2771 4 100 66     24 if ((($tmp_d = $tr[$tl]{d0}) > 0) && ($tr[$tmp_d]{u0} == $tu)) {
2772 3         6 $tr[$tmp_d]{u0} = $tl;
2773             }
2774 4 50 66     25 if ((($tmp_d = $tr[$tl]{d0}) > 0) && ($tr[$tmp_d]{u1} == $tu)) {
2775 0         0 $tr[$tmp_d]{u1} = $tl;
2776             }
2777              
2778 4 100 66     33 if ((($tmp_d = $tr[$tl]{d1}) > 0) && ($tr[$tmp_d]{u0} == $tu)) {
2779 2         3 $tr[$tmp_d]{u0} = $tl;
2780             }
2781 4 50 66     23 if ((($tmp_d = $tr[$tl]{d1}) > 0) && ($tr[$tmp_d]{u1} == $tu)) {
2782 0         0 $tr[$tmp_d]{u1} = $tl;
2783             }
2784              
2785             # Now update the query structure and obtain the sinks for the
2786             # two trapezoids
2787              
2788 4         11 $i1 = _newnode(); # Upper trapezoid sink
2789 4         8 $i2 = _newnode(); # Lower trapezoid sink
2790 4         7 $sk = $tr[$tu]{sink};
2791              
2792 4         7 $qs[$sk]{nodetype} = $T_Y;
2793 4         15 $qs[$sk]{yval} = {x => $s{v1}{x} , y => $s{v1}{y}};
2794 4         8 $qs[$sk]{segnum} = $segnum; # not really reqd ... maybe later
2795 4         7 $qs[$sk]{left} = $i2;
2796 4         7 $qs[$sk]{right} = $i1;
2797              
2798 4         9 $qs[$i1]{nodetype} = $T_SINK;
2799 4         6 $qs[$i1]{trnum} = $tu;
2800 4         5 $qs[$i1]{parent} = $sk;
2801              
2802 4         9 $qs[$i2]{nodetype} = $T_SINK;
2803 4         9 $qs[$i2]{trnum} = $tl;
2804 4         6 $qs[$i2]{parent} = $sk;
2805              
2806 4         6 $tr[$tu]{sink} = $i1;
2807 4         5 $tr[$tl]{sink} = $i2;
2808 4         9 $tlast = $tu;
2809             } else { # v1 already present
2810             # Get the lowermost intersecting trapezoid
2811 10         26 $tlast = _locate_endpoint($s{v1}, $s{v0}, $s{root1});
2812 10         30 $tribot = 1;
2813             }
2814              
2815             # Thread the segment into the query tree creating a new X-node
2816             # First, split all the trapezoids which are intersected by s into
2817             # two
2818              
2819 14         27 $t = $tfirst; # topmost trapezoid
2820              
2821 14   100     58 while (($t > 0) &&
2822             _greater_than_equal_to($tr[$t]{lo}, $tr[$tlast]{lo})) {
2823             # traverse from top to bot
2824 27         33 my ($t_sav, $tn_sav);
2825 27         44 $sk = $tr[$t]{sink};
2826 27         47 $i1 = _newnode(); # left trapezoid sink
2827 27         49 $i2 = _newnode(); # right trapezoid sink
2828              
2829 27         41 $qs[$sk]{nodetype} = $T_X;
2830 27         45 $qs[$sk]{segnum} = $segnum;
2831 27         43 $qs[$sk]{left} = $i1;
2832 27         38 $qs[$sk]{right} = $i2;
2833              
2834 27         90 $qs[$i1]{nodetype} = $T_SINK; # left trapezoid (use existing one)
2835 27         36 $qs[$i1]{trnum} = $t;
2836 27         41 $qs[$i1]{parent} = $sk;
2837              
2838 27         59 $qs[$i2]{nodetype} = $T_SINK; # right trapezoid (allocate new)
2839 27         45 $qs[$i2]{trnum} = $tn = _newtrap();
2840 27         136 $tr[$tn]{state} = $ST_VALID;
2841 27         36 $qs[$i2]{parent} = $sk;
2842              
2843 27 100       53 if ($t == $tfirst) {
2844 14         21 $tfirstr = $tn;
2845             }
2846 27 100       64 if (_equal_to($tr[$t]{lo}, $tr[$tlast]{lo})) {
2847 14         18 $tlastr = $tn;
2848             }
2849              
2850 27         41 my %tmp = %{$tr[$t]};
  27         220  
2851 27         54 my %tmphi = %{$tmp{hi}};
  27         89  
2852 27         40 my %tmplo = %{$tmp{lo}};
  27         68  
2853 27         47 $tr[$tn] = \%tmp;
2854 27         118 $tr[$tn]{hi} = {x => $tmphi{x} , y => $tmphi{y}};
2855 27         86 $tr[$tn]{lo} = {x => $tmplo{x} , y => $tmplo{y}};
2856 27         40 $tr[$t]{sink} = $i1;
2857 27         36 $tr[$tn]{sink} = $i2;
2858 27         27 $t_sav = $t;
2859 27         30 $tn_sav = $tn;
2860              
2861             # error
2862              
2863 27 50 33     199 if (($tr[$t]{d0} <= 0) && ($tr[$t]{d1} <= 0)) { # case cannot arise
    100 66        
    50 33        
2864 0         0 print "add_segment: error\n";
2865              
2866             # only one trapezoid below. partition t into two and make the
2867             # two resulting trapezoids t and tn as the upper neighbours of
2868             # the sole lower trapezoid
2869              
2870             } elsif (($tr[$t]{d0} > 0) && ($tr[$t]{d1} <= 0)) { # Only one trapezoid below
2871 17 100 66     80 if (($tr[$t]{u0} > 0) && ($tr[$t]{u1} > 0)) { # continuation of a chain from abv.
2872 8 100       19 if ($tr[$t]{usave} > 0) { # three upper neighbours
2873 1 50       5 if ($tr[$t]{uside} == $S_LEFT) {
2874 1         3 $tr[$tn]{u0} = $tr[$t]{u1};
2875 1         3 $tr[$t]{u1} = -1;
2876 1         2 $tr[$tn]{u1} = $tr[$t]{usave};
2877              
2878 1         3 $tr[$tr[$t]{u0}]{d0} = $t;
2879 1         4 $tr[$tr[$tn]{u0}]{d0} = $tn;
2880 1         3 $tr[$tr[$tn]{u1}]{d0} = $tn;
2881             } else { # intersects in the right
2882 0         0 $tr[$tn]{u1} = -1;
2883 0         0 $tr[$tn]{u0} = $tr[$t]{u1};
2884 0         0 $tr[$t]{u1} = $tr[$t]{u0};
2885 0         0 $tr[$t]{u0} = $tr[$t]{usave};
2886              
2887 0         0 $tr[$tr[$t]{u0}]{d0} = $t;
2888 0         0 $tr[$tr[$t]{u1}]{d0} = $t;
2889 0         0 $tr[$tr[$tn]{u0}]{d0} = $tn;
2890             }
2891              
2892 1         3 $tr[$t]{usave} = $tr[$tn]{usave} = 0;
2893             } else { # No usave.... simple case
2894 7         14 $tr[$tn]{u0} = $tr[$t]{u1};
2895 7         12 $tr[$t]{u1} = $tr[$tn]{u1} = -1;
2896 7         13 $tr[$tr[$tn]{u0}]{d0} = $tn;
2897             }
2898             } else { # fresh seg. or upward cusp
2899 9         21 my $tmp_u = $tr[$t]{u0};
2900 9         11 my ($td0, $td1);
2901 9 100 66     40 if ((($td0 = $tr[$tmp_u]{d0}) > 0) &&
2902             (($td1 = $tr[$tmp_u]{d1}) > 0)) { # upward cusp
2903 3 100 66     18 if (($tr[$td0]{rseg} > 0) &&
2904             !_is_left_of($tr[$td0]{rseg}, $s{v1})) {
2905 1         4 $tr[$t]{u0} = $tr[$t]{u1} = $tr[$tn]{u1} = -1;
2906 1         5 $tr[$tr[$tn]{u0}]{d1} = $tn;
2907             } else { # cusp going leftwards
2908 2         8 $tr[$tn]{u0} = $tr[$tn]{u1} = $tr[$t]{u1} = -1;
2909 2         5 $tr[$tr[$t]{u0}]{d0} = $t;
2910             }
2911             } else { # fresh segment
2912 6         12 $tr[$tr[$t]{u0}]{d0} = $t;
2913 6         15 $tr[$tr[$t]{u0}]{d1} = $tn;
2914             }
2915             }
2916              
2917 17 100 100     58 if (_fp_equal($tr[$t]{lo}{y}, $tr[$tlast]{lo}{y}, $precision) &&
      100        
2918             _fp_equal($tr[$t]{lo}{x}, $tr[$tlast]{lo}{x}, $precision) && $tribot) {
2919             # bottom forms a triangle
2920              
2921 3 100       8 if ($is_swapped) {
2922 1         4 $tmptriseg = $seg[$segnum]{prev};
2923             } else {
2924 2         5 $tmptriseg = $seg[$segnum]{next};
2925             }
2926              
2927 3 50 33     19 if (($tmptriseg > 0) && _is_left_of($tmptriseg, $s{v0})) { # L-R downward cusp
2928 3         12 $tr[$tr[$t]{d0}]{u0} = $t;
2929 3         10 $tr[$tn]{d0} = $tr[$tn]{d1} = -1;
2930             } else { # R-L downward cusp
2931 0         0 $tr[$tr[$tn]{d0}]{u1} = $tn;
2932 0         0 $tr[$t]{d0} = $tr[$t]{d1} = -1;
2933             }
2934             } else {
2935 14 100 66     82 if (($tr[$tr[$t]{d0}]{u0} > 0) && ($tr[$tr[$t]{d0}]{u1} > 0)) {
2936 3 100       11 if ($tr[$tr[$t]{d0}]{u0} == $t) { # passes thru LHS
2937 1         4 $tr[$tr[$t]{d0}]{usave} = $tr[$tr[$t]{d0}]{u1};
2938 1         5 $tr[$tr[$t]{d0}]{uside} = $S_LEFT;
2939             } else {
2940 2         7 $tr[$tr[$t]{d0}]{usave} = $tr[$tr[$t]{d0}]{u0};
2941 2         4 $tr[$tr[$t]{d0}]{uside} = $S_RIGHT;
2942             }
2943             }
2944 14         26 $tr[$tr[$t]{d0}]{u0} = $t;
2945 14         28 $tr[$tr[$t]{d0}]{u1} = $tn;
2946             }
2947              
2948 17         39 $t = $tr[$t]{d0};
2949              
2950             } elsif (($tr[$t]{d0} <= 0) && ($tr[$t]{d1} > 0)) { # Only one trapezoid below
2951 0 0 0     0 if (($tr[$t]{u0} > 0) && ($tr[$t]{u1} > 0)) { # continuation of a chain from abv.
2952 0 0       0 if ($tr[$t]{usave} > 0) { # three upper neighbours
2953 0 0       0 if ($tr[$t]{uside} == $S_LEFT) {
2954 0         0 $tr[$tn]{u0} = $tr[$t]{u1};
2955 0         0 $tr[$t]{u1} = -1;
2956 0         0 $tr[$tn]{u1} = $tr[$t]{usave};
2957              
2958 0         0 $tr[$tr[$t]{u0}]{d0} = $t;
2959 0         0 $tr[$tr[$tn]{u0}]{d0} = $tn;
2960 0         0 $tr[$tr[$tn]{u1}]{d0} = $tn;
2961             } else { # intersects in the right
2962 0         0 $tr[$tn]{u1} = -1;
2963 0         0 $tr[$tn]{u0} = $tr[$t]{u1};
2964 0         0 $tr[$t]{u1} = $tr[$t]{u0};
2965 0         0 $tr[$t]{u0} = $tr[$t]{usave};
2966              
2967 0         0 $tr[$tr[$t]{u0}]{d0} = $t;
2968 0         0 $tr[$tr[$t]{u1}]{d0} = $t;
2969 0         0 $tr[$tr[$tn]{u0}]{d0} = $tn;
2970             }
2971              
2972 0         0 $tr[$t]{usave} = $tr[$tn]{usave} = 0;
2973              
2974             } else { # No usave.... simple case
2975 0         0 $tr[$tn]{u0} = $tr[$t]{u1};
2976 0         0 $tr[$t]{u1} = $tr[$tn]{u1} = -1;
2977 0         0 $tr[$tr[$tn]{u0}]{d0} = $tn;
2978             }
2979             } else { # fresh seg. or upward cusp
2980 0         0 my $tmp_u = $tr[$t]{u0};
2981 0         0 my ($td0,$td1);
2982 0 0 0     0 if ((($td0 = $tr[$tmp_u]{d0}) > 0) &&
2983             (($td1 = $tr[$tmp_u]{d1}) > 0)) { # upward cusp
2984 0 0 0     0 if (($tr[$td0]{rseg} > 0) &&
2985             !_is_left_of($tr[$td0]{rseg}, $s{v1})) {
2986 0         0 $tr[$t]{u0} = $tr[$t]{u1} = $tr[$tn]{u1} = -1;
2987 0         0 $tr[$tr[$tn]{u0}]{d1} = $tn;
2988             } else {
2989 0         0 $tr[$tn]{u0} = $tr[$tn]{u1} = $tr[$t]{u1} = -1;
2990 0         0 $tr[$tr[$t]{u0}]{d0} = $t;
2991             }
2992             } else { # fresh segment
2993 0         0 $tr[$tr[$t]{u0}]{d0} = $t;
2994 0         0 $tr[$tr[$t]{u0}]{d1} = $tn;
2995             }
2996             }
2997              
2998 0 0 0     0 if (_fp_equaL($tr[$t]{lo}{y}, $tr[$tlast]{lo}{y}, $precision) &&
      0        
2999             _fp_equal($tr[$t]{lo}{x}, $tr[$tlast]{lo}{x}, $precision) && $tribot) {
3000             # bottom forms a triangle
3001 0         0 my $tmpseg;
3002              
3003 0 0       0 if ($is_swapped) {
3004 0         0 $tmptriseg = $seg[$segnum]{prev};
3005             } else {
3006 0         0 $tmptriseg = $seg[$segnum]{next};
3007             }
3008              
3009 0 0 0     0 if (($tmpseg > 0) && _is_left_of($tmpseg, $s{v0})) {
3010             # L-R downward cusp
3011 0         0 $tr[$tr[$t]{d1}]{u0} = $t;
3012 0         0 $tr[$tn]{d0} = $tr[$tn]{d1} = -1;
3013             } else {
3014             # R-L downward cusp
3015 0         0 $tr[$tr[$tn]{d1}]{u1} = $tn;
3016 0         0 $tr[$t]{d0} = $tr[$t]{d1} = -1;
3017             }
3018             } else {
3019 0 0 0     0 if (($tr[$tr[$t]{d1}]{u0} > 0) && ($tr[$tr[$t]{d1}]{u1} > 0)) {
3020 0 0       0 if ($tr[$tr[$t]{d1}]{u0} == $t) { # passes thru LHS
3021 0         0 $tr[$tr[$t]{d1}]{usave} = $tr[$tr[$t]{d1}]{u1};
3022 0         0 $tr[$tr[$t]{d1}]{uside} = $S_LEFT;
3023             } else {
3024 0         0 $tr[$tr[$t]{d1}]{usave} = $tr[$tr[$t]{d1}]{u0};
3025 0         0 $tr[$tr[$t]{d1}]{uside} = $S_RIGHT;
3026             }
3027             }
3028 0         0 $tr[$tr[$t]{d1}]{u0} = $t;
3029 0         0 $tr[$tr[$t]{d1}]{u1} = $tn;
3030             }
3031              
3032 0         0 $t = $tr[$t]{d1};
3033              
3034             # two trapezoids below. Find out which one is intersected by
3035             # this segment and proceed down that one
3036              
3037             } else {
3038 10         24 my $tmpseg = $tr[$tr[$t]{d0}]{rseg};
3039 10         15 my ($y0,$yt);
3040 0         0 my %tmppt;
3041 0         0 my ($tnext, $i_d0, $i_d1);
3042              
3043 10         11 $i_d0 = $i_d1 = $FALSE;
3044 10 50       29 if (_fp_equal($tr[$t]{lo}{y}, $s{v0}{y}, $precision)) {
3045 0 0       0 if ($tr[$t]{lo}{x} > $s{v0}{x}) {
3046 0         0 $i_d0 = $TRUE;
3047             } else {
3048 0         0 $i_d1 = $TRUE;
3049             }
3050             } else {
3051 10         28 $tmppt{y} = $y0 = $tr[$t]{lo}{y};
3052 10         32 $yt = ($y0 - $s{v0}{y})/($s{v1}{y} - $s{v0}{y});
3053 10         32 $tmppt{x} = $s{v0}{x} + $yt * ($s{v1}{x} - $s{v0}{x});
3054              
3055 10 100       43 if (_less_than(\%tmppt, $tr[$t]{lo})) {
3056 2         5 $i_d0 = $TRUE;
3057             } else {
3058 8         13 $i_d1 = $TRUE;
3059             }
3060             }
3061              
3062             # check continuity from the top so that the lower-neighbour
3063             # values are properly filled for the upper trapezoid
3064              
3065 10 100 66     67 if (($tr[$t]{u0} > 0) && ($tr[$t]{u1} > 0)) { # continuation of a chain from abv.
3066 8 100       18 if ($tr[$t]{usave} > 0) { # three upper neighbours
3067 2 50       8 if ($tr[$t]{uside} == $S_LEFT) {
3068 0         0 $tr[$tn]{u0} = $tr[$t]{u1};
3069 0         0 $tr[$t]{u1} = -1;
3070 0         0 $tr[$tn]{u1} = $tr[$t]{usave};
3071              
3072 0         0 $tr[$tr[$t]{u0}]{d0} = $t;
3073 0         0 $tr[$tr[$tn]{u0}]{d0} = $tn;
3074 0         0 $tr[$tr[$tn]{u1}]{d0} = $tn;
3075             } else { # intersects in the right
3076 2         5 $tr[$tn]{u1} = -1;
3077 2         4 $tr[$tn]{u0} = $tr[$t]{u1};
3078 2         4 $tr[$t]{u1} = $tr[$t]{u0};
3079 2         4 $tr[$t]{u0} = $tr[$t]{usave};
3080              
3081 2         5 $tr[$tr[$t]{u0}]{d0} = $t;
3082 2         4 $tr[$tr[$t]{u1}]{d0} = $t;
3083 2         4 $tr[$tr[$tn]{u0}]{d0} = $tn;
3084             }
3085              
3086 2         4 $tr[$t]{usave} = $tr[$tn]{usave} = 0;
3087             } else { # No usave.... simple case
3088 6         13 $tr[$tn]{u0} = $tr[$t]{u1};
3089 6         11 $tr[$tn]{u1} = -1;
3090 6         7 $tr[$t]{u1} = -1;
3091 6         12 $tr[$tr[$tn]{u0}]{d0} = $tn;
3092             }
3093             } else { # fresh seg. or upward cusp
3094 2         5 my $tmp_u = $tr[$t]{u0};
3095 2         5 my ($td0, $td1);
3096 2 50 33     13 if ((($td0 = $tr[$tmp_u]{d0}) > 0) &&
3097             (($td1 = $tr[$tmp_u]{d1}) > 0)) { # upward cusp
3098 0 0 0     0 if (($tr[$td0]{rseg} > 0) &&
3099             !_is_left_of($tr[$td0]{rseg}, $s{v1})) {
3100 0         0 $tr[$t]{u0} = $tr[$t]{u1} = $tr[$tn]{u1} = -1;
3101 0         0 $tr[$tr[$tn]{u0}]{d1} = $tn;
3102             } else {
3103 0         0 $tr[$tn]{u0} = $tr[$tn]{u1} = $tr[$t]{u1} = -1;
3104 0         0 $tr[$tr[$t]{u0}]{d0} = $t;
3105             }
3106             } else { # fresh segment
3107 2         6 $tr[$tr[$t]{u0}]{d0} = $t;
3108 2         6 $tr[$tr[$t]{u0}]{d1} = $tn;
3109             }
3110             }
3111              
3112 10 100 66     33 if (_fp_equal($tr[$t]{lo}{y}, $tr[$tlast]{lo}{y}, $precision) &&
    100 66        
3113             _fp_equal($tr[$t]{lo}{x}, $tr[$tlast]{lo}{x}, $precision) && $tribot) {
3114             # this case arises only at the lowest trapezoid.. i.e.
3115             # tlast, if the lower endpoint of the segment is
3116             # already inserted in the structure
3117              
3118 7         24 $tr[$tr[$t]{d0}]{u0} = $t;
3119 7         12 $tr[$tr[$t]{d0}]{u1} = -1;
3120 7         11 $tr[$tr[$t]{d1}]{u0} = $tn;
3121 7         12 $tr[$tr[$t]{d1}]{u1} = -1;
3122              
3123 7         12 $tr[$tn]{d0} = $tr[$t]{d1};
3124 7         11 $tr[$t]{d1} = $tr[$tn]{d1} = -1;
3125              
3126 7         13 $tnext = $tr[$t]{d1};
3127             } elsif ($i_d0) { # intersecting d0
3128 2         4 $tr[$tr[$t]{d0}]{u0} = $t;
3129 2         4 $tr[$tr[$t]{d0}]{u1} = $tn;
3130 2         4 $tr[$tr[$t]{d1}]{u0} = $tn;
3131 2         4 $tr[$tr[$t]{d1}]{u1} = -1;
3132              
3133             # new code to determine the bottom neighbours of the
3134             # newly partitioned trapezoid
3135              
3136 2         3 $tr[$t]{d1} = -1;
3137              
3138 2         6 $tnext = $tr[$t]{d0};
3139             } else { # intersecting d1
3140 1         5 $tr[$tr[$t]{d0}]{u0} = $t;
3141 1         3 $tr[$tr[$t]{d0}]{u1} = -1;
3142 1         3 $tr[$tr[$t]{d1}]{u0} = $t;
3143 1         3 $tr[$tr[$t]{d1}]{u1} = $tn;
3144              
3145             # new code to determine the bottom neighbours of the
3146             # newly partitioned trapezoid
3147              
3148 1         3 $tr[$tn]{d0} = $tr[$t]{d1};
3149 1         2 $tr[$tn]{d1} = -1;
3150              
3151 1         2 $tnext = $tr[$t]{d1};
3152             }
3153              
3154 10         29 $t = $tnext;
3155             }
3156              
3157 27         148 $tr[$t_sav]{rseg} = $tr[$tn_sav]{lseg} = $segnum;
3158             } # end-while
3159              
3160             # Now combine those trapezoids which share common segments. We can
3161             # use the pointers to the parent to connect these together. This
3162             # works only because all these new trapezoids have been formed
3163             # due to splitting by the segment, and hence have only one parent
3164              
3165 14         19 $tfirstl = $tfirst;
3166 14         14 $tlastl = $tlast;
3167 14         34 merge_trapezoids($segnum, $tfirstl, $tlastl, $S_LEFT);
3168 14         26 merge_trapezoids($segnum, $tfirstr, $tlastr, $S_RIGHT);
3169              
3170 14         90 $seg[$segnum]{is_inserted} = $TRUE;
3171             }
3172              
3173             # Returns true if the corresponding endpoint of the given segment is
3174             # already inserted into the segment tree. Use the simple test of
3175             # whether the segment which shares this endpoint is already inserted
3176              
3177             sub _inserted {
3178 28     28   40 my ($segnum, $whichpt) = @_;
3179 28 100       54 if ($whichpt == $FIRSTPT) {
3180 14         48 return $seg[$seg[$segnum]{prev}]{is_inserted};
3181             } else {
3182 14         46 return $seg[$seg[$segnum]{next}]{is_inserted};
3183             }
3184             }
3185              
3186             # This is query routine which determines which trapezoid does the
3187             # point v lie in. The return value is the trapezoid number.
3188             #
3189              
3190             sub _locate_endpoint {
3191 150     150   200 my ($v_ref, $vo_ref, $r) = @_;
3192 150         149 my %v = %{$v_ref};
  150         489  
3193 150         191 my %vo = %{$vo_ref};
  150         376  
3194 150         175 my %rptr = %{$qs[$r]};
  150         553  
3195              
3196             SWITCH: {
3197 150 100       200 ($rptr{nodetype} == $T_SINK) && do {
  150         313  
3198 56         392 return $rptr{trnum};
3199             };
3200 94 100       189 ($rptr{nodetype} == $T_Y) && do {
3201 77 100       149 if (_greater_than(\%v, $rptr{yval})) { # above
    100          
3202 29         64 return _locate_endpoint(\%v, \%vo, $rptr{right});
3203             } elsif (_equal_to(\%v, $rptr{yval})) { # the point is already
3204             # inserted.
3205 16 100       39 if (_greater_than(\%vo, $rptr{yval})) { # above
3206 10         28 return _locate_endpoint(\%v, \%vo, $rptr{right});
3207             } else {
3208 6         17 return _locate_endpoint(\%v, \%vo, $rptr{left}); # below
3209             }
3210             } else {
3211 32         124 return _locate_endpoint(\%v, \%vo, $rptr{left}); # below
3212             }
3213             };
3214 17 50       31 ($rptr{nodetype} == $T_X) && do {
3215 17 100 100     50 if (_equal_to(\%v, $seg[$rptr{segnum}]{v0}) ||
    100          
3216             _equal_to(\%v, $seg[$rptr{segnum}]{v1})) {
3217 6 50       17 if (_fp_equal($v{y}, $vo{y}, $precision)) { # horizontal segment
    100          
3218 0 0       0 if ($vo{x} < $v{x}) {
3219 0         0 return _locate_endpoint(\%v, \%vo, $rptr{left}); # left
3220             } else {
3221 0         0 return _locate_endpoint(\%v, \%vo, $rptr{right}); # right
3222             }
3223             } elsif (_is_left_of($rptr{segnum}, \%vo)) {
3224 5         19 return _locate_endpoint(\%v, \%vo, $rptr{left}); # left
3225             } else {
3226 1         6 return _locate_endpoint(\%v, \%vo, $rptr{right}); # right
3227             }
3228             } elsif (_is_left_of($rptr{segnum}, \%v)) {
3229 9         36 return _locate_endpoint(\%v, \%vo, $rptr{left}); # left
3230             } else {
3231 2         8 return _locate_endpoint(\%v, \%vo, $rptr{right}); # right
3232             }
3233             };
3234             # default
3235 0         0 croak("Haggu !!!!!");
3236             }
3237             }
3238              
3239             # Thread in the segment into the existing trapezoidation. The
3240             # limiting trapezoids are given by tfirst and tlast (which are the
3241             # trapezoids containing the two endpoints of the segment. Merges all
3242             # possible trapezoids which flank this segment and have been recently
3243             # divided because of its insertion
3244             #
3245              
3246             sub merge_trapezoids {
3247 28     28 0 49 my ($segnum, $tfirst, $tlast, $side) = @_;
3248 28         29 my ($t, $tnext, $cond);
3249 0         0 my $ptnext;
3250              
3251             # First merge polys on the LHS
3252 28         29 $t = $tfirst;
3253             # while (($t > 0) && _greater_than_equal_to($tr[$t]{lo}, $tr[$tlast]{lo})) {
3254 28         62 while ($t > 0) {
3255 54 50       122 last if (! _greater_than_equal_to($tr[$t]{lo}, $tr[$tlast]{lo}));
3256 54 100       105 if ($side == $S_LEFT) {
3257 27   66     174 $cond = (((($tnext = $tr[$t]{d0}) > 0) && ($tr[$tnext]{rseg} == $segnum)) ||
3258             ((($tnext = $tr[$t]{d1}) > 0) && ($tr[$tnext]{rseg} == $segnum)));
3259             } else {
3260 27   66     165 $cond = (((($tnext = $tr[$t]{d0}) > 0) && ($tr[$tnext]{lseg} == $segnum)) ||
3261             ((($tnext = $tr[$t]{d1}) > 0) && ($tr[$tnext]{lseg} == $segnum)));
3262             }
3263 54 100       85 if ($cond) {
3264 26 100 100     123 if (($tr[$t]{lseg} == $tr[$tnext]{lseg}) &&
3265             ($tr[$t]{rseg} == $tr[$tnext]{rseg})) { # good neighbours
3266             # merge them
3267             # Use the upper node as the new node i.e. t
3268 13         23 $ptnext = $qs[$tr[$tnext]{sink}]{parent};
3269 13 100       35 if ($qs[$ptnext]{left} == $tr[$tnext]{sink}) {
3270 9         17 $qs[$ptnext]{left} = $tr[$t]{sink};
3271             } else {
3272 4         9 $qs[$ptnext]{right} = $tr[$t]{sink}; # redirect parent
3273             }
3274             # Change the upper neighbours of the lower trapezoids
3275 13 50       35 if (($tr[$t]{d0} = $tr[$tnext]{d0}) > 0) {
3276 13 50       31 if ($tr[$tr[$t]{d0}]{u0} == $tnext) {
    0          
3277 13         21 $tr[$tr[$t]{d0}]{u0} = $t;
3278             } elsif ($tr[$tr[$t]{d0}]{u1} == $tnext) {
3279 0         0 $tr[$tr[$t]{d0}]{u1} = $t;
3280             }
3281             }
3282 13 50       40 if (($tr[$t]{d1} = $tr[$tnext]{d1}) > 0) {
3283 0 0       0 if ($tr[$tr[$t]{d1}]{u0} == $tnext) {
    0          
3284 0         0 $tr[$tr[$t]{d1}]{u0} = $t;
3285             } elsif ($tr[$tr[$t]{d1}]{u1} == $tnext) {
3286 0         0 $tr[$tr[$t]{d1}]{u1} = $t;
3287             }
3288             }
3289 13         55 $tr[$t]{lo} = {x => $tr[$tnext]{lo}{x} , y=> $tr[$tnext]{lo}{y}};
3290 13         47 $tr[$tnext]{state} = 2; # invalidate the lower
3291             # trapezium
3292             } else { #* not good neighbours
3293 13         32 $t = $tnext;
3294             }
3295             } else { #* do not satisfy the outer if
3296 28         78 $t = $tnext;
3297             }
3298             } # end-while
3299             }
3300              
3301             # Retun TRUE if the vertex v is to the left of line segment no.
3302             # segnum. Takes care of the degenerate cases when both the vertices
3303             # have the same y--cood, etc.
3304             #
3305              
3306             sub _is_left_of {
3307 23     23   37 my ($segnum, $v_ref) = @_;
3308 23         28 my %s = %{$seg[$segnum]};
  23         111  
3309 23         39 my $area;
3310 23         23 my %v = %{$v_ref};
  23         59  
3311              
3312 23 100       58 if (_greater_than($s{v1}, $s{v0})) { # seg. going upwards
3313 13 100       34 if (_fp_equal($s{v1}{y}, $v{y}, $precision)) {
    100          
3314 9 50       25 if ($v{x} < $s{v1}{x}) {
3315 9         13 $area = 1;
3316             } else {
3317 0         0 $area = -1;
3318             }
3319             } elsif (_fp_equal($s{v0}{y}, $v{y}, $precision)) {
3320 2 50       8 if ($v{x} < $s{v0}{x}) {
3321 2         2 $area = 1;
3322             } else{
3323 0         0 $area = -1;
3324             }
3325             } else {
3326 2         10 $area = _Cross($s{v0}, $s{v1}, \%v);
3327             }
3328             } else { # v0 > v1
3329 10 100       32 if (_fp_equal($s{v1}{y}, $v{y}, $precision)) {
    100          
3330 2 50       10 if ($v{x} < $s{v1}{x}) {
3331 2         3 $area = 1;
3332             } else {
3333 0         0 $area = -1;
3334             }
3335             } elsif (_fp_equal($s{v0}{y}, $v{y}, $precision)) {
3336 4 50       16 if ($v{x} < $s{v0}{x}) {
3337 4         7 $area = 1;
3338             } else {
3339 0         0 $area = -1;
3340             }
3341             } else {
3342 4         13 $area = _Cross($s{v1}, $s{v0}, \%v);
3343             }
3344             }
3345 23 100       58 if ($area > 0) {
3346 19         83 return $TRUE;
3347             } else {
3348 4         20 return $FALSE;
3349             };
3350             }
3351              
3352             sub _Cross {
3353 14     14   21 my ($v0_ref, $v1_ref, $v2_ref) = @_;
3354 14         16 my %v0 = %{$v0_ref};
  14         41  
3355 14         19 my %v1 = %{$v1_ref};
  14         37  
3356 14         18 my %v2 = %{$v2_ref};
  14         45  
3357 14         68 return ( ($v1{x} - $v0{x}) * ($v2{y} - $v0{y}) -
3358             ($v1{y} - $v0{y}) * ($v2{x} - $v0{x}) );
3359             }
3360              
3361             # Get log*n for given n
3362             sub _math_logstar_n {
3363 8     8   16 my ($n) = @_;
3364 8         11 my $i = 0;
3365 8         31 for ($i = 0 ; $n >= 1 ; $i++) {
3366 24         79 $n = log($n)/log(2); # log2
3367             }
3368 8         27 return ($i - 1);
3369             }
3370              
3371             sub _math_N {
3372 20     20   30 my ($n,$h) = @_;
3373 20         28 my $v = $n;
3374 20         53 for (my $i = 0 ; $i < $h; $i++) {
3375 28         75 $v = log($v)/log(2); # log2
3376             }
3377 20         121 return (ceil($n/$v));
3378             }
3379              
3380             # This function returns TRUE or FALSE depending upon whether the
3381             # vertex is inside the polygon or not. The polygon must already have
3382             # been triangulated before this routine is called.
3383             # This routine will always detect all the points belonging to the
3384             # set (polygon-area - polygon-boundary). The return value for points
3385             # on the boundary is not consistent!!!
3386             #
3387              
3388             sub is_point_inside_polygon {
3389 0     0 0 0 my @vertex = @_;
3390 0         0 my %v;
3391 0         0 my ($trnum, $rseg);
3392              
3393 0         0 %v = {x => $vertex[0] , y => $vertex[1]};
3394              
3395 0         0 $trnum = _locate_endpoint(&v, &v, 1);
3396 0         0 my %t = %{$tr[$trnum]};
  0         0  
3397              
3398 0 0       0 if ($t{state} == $ST_INVALID) {
3399 0         0 return $FALSE;
3400             }
3401              
3402 0 0 0     0 if (($t{lseg} <= 0) || ($t{rseg} <= 0)) {
3403 0         0 return $FALSE;
3404             }
3405 0         0 $rseg = $t{rseg};
3406 0         0 return _greater_than_equal_to($seg[$rseg]{v1}, $seg[$rseg]{v0});
3407             }
3408              
3409             sub _Cross_Sine {
3410 18     18   23 my ($v0_ref, $v1_ref) = @_;
3411 18         27 my %v0 = %{$v0_ref};
  18         62  
3412 18         24 my %v1 = %{$v1_ref};
  18         52  
3413 18         76 return ($v0{x} * $v1{y} - $v1{x} * $v0{y});
3414             }
3415              
3416             sub _Length {
3417 36     36   41 my ($v0_ref) = @_;
3418 36         45 my %v0 = %{$v0_ref};
  36         86  
3419 36         214 return (sqrt($v0{x} * $v0{x} + $v0{y} * $v0{y}));
3420             }
3421              
3422             sub _Dot {
3423 18     18   28 my ($v0_ref, $v1_ref) = @_;
3424 18         17 my %v0 = %{$v0_ref};
  18         39  
3425 18         30 my %v1 = %{$v1_ref};
  18         41  
3426 18         74 return ($v0{x} * $v1{x} + $v0{y} * $v1{y})
3427             }
3428              
3429             # Function returns TRUE if the trapezoid lies inside the polygon
3430             sub inside_polygon {
3431 18     18 0 25 my ($t_ref) = @_;
3432 18         22 my %t = %{$t_ref};
  18         129  
3433 18         42 my $rseg = $t{rseg};
3434 18 100       42 if ($t{state} == $ST_INVALID) {
3435 5         30 return 0;
3436             }
3437 13 100 100     53 if (($t{lseg} <= 0) || ($t{rseg} <= 0)) {
3438 9         47 return 0;
3439             }
3440 4 100 66     36 if ((($t{u0} <= 0) && ($t{u1} <= 0)) ||
      66        
      66        
3441             (($t{d0} <= 0) && ($t{d1} <= 0))) { # triangle
3442 2         9 return (_greater_than($seg[$rseg]{v1}, $seg[$rseg]{v0}));
3443             }
3444 2         13 return 0;
3445             }
3446              
3447             # return a new mon structure from the table
3448             sub _newmon {
3449 9     9   23 return ++$mon_idx;
3450             }
3451              
3452             # return a new chain element from the table
3453             sub _new_chain_element {
3454 14     14   20 return ++$chain_idx;
3455             }
3456              
3457             sub _get_angle {
3458 18     18   26 my ($vp0_ref, $vpnext_ref, $vp1_ref) = @_;
3459 18         20 my %vp0 = %{$vp0_ref};
  18         52  
3460 18         27 my %vpnext = %{$vpnext_ref};
  18         44  
3461 18         22 my %vp1 = %{$vp1_ref};
  18         42  
3462              
3463 18         20 my ($v0, $v1);
3464              
3465 18         72 $v0 = {x => $vpnext{x} - $vp0{x} , y => $vpnext{y} - $vp0{y}};
3466 18         65 $v1 = {x => $vp1{x} - $vp0{x} , y => $vp1{y} - $vp0{y}};
3467              
3468 18 100       40 if (_Cross_Sine($v0, $v1) >= 0) { # sine is positive
3469 15         31 return _Dot($v0, $v1)/_Length($v0)/_Length($v1);
3470             } else {
3471 3         9 return (-1 * _Dot($v0, $v1)/_Length($v0)/_Length($v1) - 2);
3472             }
3473             }
3474              
3475             # (v0, v1) is the new diagonal to be added to the polygon. Find which
3476             # chain to use and return the positions of v0 and v1 in p and q
3477             sub _get_vertex_positions {
3478 7     7   9 my ($v0, $v1) = @_;
3479              
3480 7         10 my (%vp0, %vp1);
3481 0         0 my ($angle, $temp);
3482 0         0 my ($tp, $tq);
3483              
3484 7         11 %vp0 = %{$vert[$v0]};
  7         37  
3485 7         11 %vp1 = %{$vert[$v1]};
  7         35  
3486              
3487             # p is identified as follows. Scan from (v0, v1) rightwards till
3488             # you hit the first segment starting from v0. That chain is the
3489             # chain of our interest
3490              
3491 7         10 $angle = -4.0;
3492 7         22 for (my $i = 0; $i < 4; $i++) {
3493 28 100       81 next if (! $vp0{vnext}[$i]); # prevents 'uninitialized' warnings
3494 9 50       22 if ($vp0{vnext}[$i] <= 0) {
3495 0         0 next;
3496             }
3497 9 50       31 if (($temp = _get_angle($vp0{pt}, $vert[$vp0{vnext}[$i]]{pt}, $vp1{pt})) > $angle) {
3498 9         12 $angle = $temp;
3499 9         26 $tp = $i;
3500             }
3501             }
3502              
3503             # $ip_ref = \$tp;
3504              
3505             # Do similar actions for q
3506              
3507 7         8 $angle = -4.0;
3508 7         19 for (my $i = 0; $i < 4; $i++) {
3509 28 100       76 next if (! $vp1{vnext}[$i]); # prevents 'uninitialized' warnings
3510 9 50       25 if ($vp1{vnext}[$i] <= 0) {
3511 0         0 next;
3512             }
3513 9 50       28 if (($temp = _get_angle($vp1{pt}, $vert[$vp1{vnext}[$i]]{pt}, $vp0{pt})) > $angle) {
3514 9         13 $angle = $temp;
3515 9         27 $tq = $i;
3516             }
3517             }
3518              
3519             # $iq_ref = \$tq;
3520              
3521 7         25 return ($tp,$tq);
3522              
3523             }
3524              
3525             # v0 and v1 are specified in anti-clockwise order with respect to
3526             # the current monotone polygon mcur. Split the current polygon into
3527             # two polygons using the diagonal (v0, v1)
3528             #
3529             sub _make_new_monotone_poly {
3530 7     7   10 my ($mcur, $v0, $v1) = @_;
3531              
3532 7         9 my ($p, $q, $ip, $iq);
3533 7         15 my $mnew = _newmon;
3534 7         11 my ($i, $j, $nf0, $nf1);
3535              
3536 7         9 my %vp0 = %{$vert[$v0]};
  7         40  
3537 7         12 my %vp1 = %{$vert[$v1]};
  7         27  
3538              
3539 7         23 ($ip,$iq) = _get_vertex_positions($v0, $v1);
3540              
3541 7         15 $p = $vp0{vpos}[$ip];
3542 7         12 $q = $vp1{vpos}[$iq];
3543              
3544             # At this stage, we have got the positions of v0 and v1 in the
3545             # desired chain. Now modify the linked lists
3546              
3547 7         17 $i = _new_chain_element; # for the new list
3548 7         11 $j = _new_chain_element;
3549              
3550 7         20 $mchain[$i]{vnum} = $v0;
3551 7         24 $mchain[$j]{vnum} = $v1;
3552              
3553 7         18 $mchain[$i]{next} = $mchain[$p]{next};
3554 7         14 $mchain[$mchain[$p]{next}]{prev} = $i;
3555 7         13 $mchain[$i]{prev} = $j;
3556 7         29 $mchain[$j]{next} = $i;
3557 7         50 $mchain[$j]{prev} = $mchain[$q]{prev};
3558 7         13 $mchain[$mchain[$q]{prev}]{next} = $j;
3559              
3560 7         9 $mchain[$p]{next} = $q;
3561 7         9 $mchain[$q]{prev} = $p;
3562              
3563 7         10 $nf0 = $vp0{nextfree};
3564 7         18 $nf1 = $vp1{nextfree};
3565              
3566 7         12 $vert[$v0]{vnext}[$ip] = $v1;
3567              
3568 7         11 $vert[$v0]{vpos}[$nf0] = $i;
3569 7         17 $vert[$v0]{vnext}[$nf0] = $mchain[$mchain[$i]{next}]{vnum};
3570 7         12 $vert[$v1]{vpos}[$nf1] = $j;
3571 7         10 $vert[$v1]{vnext}[$nf1] = $v0;
3572              
3573 7         11 $vert[$v0]{nextfree}++;
3574 7         10 $vert[$v1]{nextfree}++;
3575              
3576 7         11 $mon[$mcur] = $p;
3577 7         12 $mon[$mnew] = $i;
3578 7         21 return $mnew;
3579             }
3580              
3581             # Main routine to get monotone polygons from the trapezoidation of
3582             # the polygon.
3583             #
3584              
3585             sub _monotonate_trapezoids {
3586 2     2   4 my ($n) = @_;
3587              
3588 2         4 my $tr_start;
3589              
3590             # First locate a trapezoid which lies inside the polygon
3591             # and which is triangular
3592             my $i;
3593 2         10 for ($i = 1; $i < $#tr; $i++) {
3594 18 100       39 if (inside_polygon($tr[$i])) {
3595 2         6 last;
3596             }
3597             }
3598 2         10 $tr_start = $i;
3599              
3600             # Initialise the mon data-structure and start spanning all the
3601             # trapezoids within the polygon
3602              
3603 2         8 for (my $i = 1; $i <= $n; $i++) {
3604 16         232 $mchain[$i]{prev} = $seg[$i]{prev};
3605 16         32 $mchain[$i]{next} = $seg[$i]{next};
3606 16         24 $mchain[$i]{vnum} = $i;
3607 16         68 $vert[$i]{pt} = {x => $seg[$i]{v0}{x} , y => $seg[$i]{v0}{y}};
3608 16         38 $vert[$i]{vnext}[0] = $seg[$i]{next}; # next vertex
3609 16         35 $vert[$i]{vpos}[0] = $i; # locn. of next vertex
3610 16         39 $vert[$i]{nextfree} = 1;
3611             }
3612              
3613 2         3 $chain_idx = $n;
3614 2         3 $mon_idx = 0;
3615 2         3 $mon[0] = 1; # position of any vertex in the first chain
3616              
3617             # traverse the polygon
3618 2 100       10 if ($tr[$tr_start]{u0} > 0) {
    50          
3619 1         5 _traverse_polygon(0, $tr_start, $tr[$tr_start]{u0}, $TR_FROM_UP);
3620             } elsif ($tr[$tr_start]{d0} > 0) {
3621 1         4 _traverse_polygon(0, $tr_start, $tr[$tr_start]{d0}, $TR_FROM_DN);
3622             }
3623              
3624             # return the number of polygons created
3625 2         5 return _newmon;
3626             }
3627              
3628             # recursively visit all the trapezoids
3629             sub _traverse_polygon {
3630 62     62   92 my ($mcur, $trnum, $from, $dir) = @_;
3631              
3632 62 100       116 if (!$trnum) { # patch dvdp
3633 5         8 return 0;
3634             }
3635 57         59 my %t = %{$tr[$trnum]};
  57         365  
3636 57         114 my ($howsplit, $mnew);
3637 0         0 my ($v0, $v1, $v0next, $v1next);
3638 0         0 my ($retval, $tmp);
3639 57         62 my $do_switch = $FALSE;
3640              
3641 57 100 100     288 if (($trnum <= 0) || $visited[$trnum]) {
3642 42         116 return 0;
3643             }
3644              
3645 15         24 $visited[$trnum] = $TRUE;
3646              
3647             # We have much more information available here.
3648             # rseg: goes upwards
3649             # lseg: goes downwards
3650              
3651             # Initially assume that dir = TR_FROM_DN (from the left)
3652             # Switch v0 and v1 if necessary afterwards
3653              
3654             # special cases for triangles with cusps at the opposite ends.
3655             # take care of this first
3656 15 100 66     135 if (($t{u0} <= 0) && ($t{u1} <= 0)) {
    100 66        
    100 66        
    50 33        
3657 2 50 33     14 if (($t{d0} > 0) && ($t{d1} > 0)) { # downward opening triangle
3658 0         0 $v0 = $tr[$t{d1}]{lseg};
3659 0         0 $v1 = $t{lseg};
3660 0 0       0 if ($from == $t{d1}) {
3661 0         0 $do_switch = $TRUE;
3662 0         0 $mnew = _make_new_monotone_poly($mcur, $v1, $v0);
3663 0         0 _traverse_polygon($mcur, $t{d1}, $trnum, $TR_FROM_UP);
3664 0         0 _traverse_polygon($mnew, $t{d0}, $trnum, $TR_FROM_UP);
3665             } else {
3666 0         0 $mnew = _make_new_monotone_poly($mcur, $v0, $v1);
3667 0         0 _traverse_polygon($mcur, $t{d0}, $trnum, $TR_FROM_UP);
3668 0         0 _traverse_polygon($mnew, $t{d1}, $trnum, $TR_FROM_UP);
3669             }
3670             } else {
3671 2         6 $retval = $SP_NOSPLIT; # Just traverse all neighbours
3672 2         18 _traverse_polygon($mcur, $t{u0}, $trnum, $TR_FROM_DN);
3673 2         7 _traverse_polygon($mcur, $t{u1}, $trnum, $TR_FROM_DN);
3674 2         7 _traverse_polygon($mcur, $t{d0}, $trnum, $TR_FROM_UP);
3675 2         4 _traverse_polygon($mcur, $t{d1}, $trnum, $TR_FROM_UP);
3676             }
3677             } elsif (($t{d0} <= 0) && ($t{d1} <= 0)) {
3678 2 50 33     13 if (($t{u0} > 0) && ($t{u1} > 0)) { # upward opening triangle
3679 0         0 $v0 = $t{rseg};
3680 0         0 $v1 = $tr[$t{u0}]{rseg};
3681 0 0       0 if ($from == $t{u1}) {
3682 0         0 $do_switch = $TRUE;
3683 0         0 $mnew = _make_new_monotone_poly($mcur, $v1, $v0);
3684 0         0 _traverse_polygon($mcur, $t{u1}, $trnum, $TR_FROM_DN);
3685 0         0 _traverse_polygon($mnew, $t{u0}, $trnum, $TR_FROM_DN);
3686             } else {
3687 0         0 $mnew = _make_new_monotone_poly($mcur, $v0, $v1);
3688 0         0 _traverse_polygon($mcur, $t{u0}, $trnum, $TR_FROM_DN);
3689 0         0 _traverse_polygon($mnew, $t{u1}, $trnum, $TR_FROM_DN);
3690             }
3691             } else {
3692 2         3 $retval = $SP_NOSPLIT; # Just traverse all neighbours
3693 2         32 _traverse_polygon($mcur, $t{u0}, $trnum, $TR_FROM_DN);
3694 2         6 _traverse_polygon($mcur, $t{u1}, $trnum, $TR_FROM_DN);
3695 2         6 _traverse_polygon($mcur, $t{d0}, $trnum, $TR_FROM_UP);
3696 2         5 _traverse_polygon($mcur, $t{d1}, $trnum, $TR_FROM_UP);
3697             }
3698             } elsif (($t{u0} > 0) && ($t{u1} > 0)) {
3699 1 50 33     9 if (($t{d0} > 0) && ($t{d1} > 0)) { # downward + upward cusps
3700 0         0 $v0 = $tr[$t{d1}]{lseg};
3701 0         0 $v1 = $tr[$t{u0}]{rseg};
3702 0         0 $retval = $SP_2UP_2DN;
3703 0 0 0     0 if ((($dir == $TR_FROM_DN) && ($t{d1} == $from)) ||
      0        
      0        
3704             (($dir == $TR_FROM_UP) && ($t{u1} == $from))) {
3705 0         0 $do_switch = $TRUE;
3706 0         0 $mnew = _make_new_monotone_poly($mcur, $v1, $v0);
3707 0         0 _traverse_polygon($mcur, $t{u1}, $trnum, $TR_FROM_DN);
3708 0         0 _traverse_polygon($mcur, $t{d1}, $trnum, $TR_FROM_UP);
3709 0         0 _traverse_polygon($mnew, $t{u0}, $trnum, $TR_FROM_DN);
3710 0         0 _traverse_polygon($mnew, $t{d0}, $trnum, $TR_FROM_UP);
3711             } else {
3712 0         0 $mnew = _make_new_monotone_poly($mcur, $v0, $v1);
3713 0         0 _traverse_polygon($mcur, $t{u0}, $trnum, $TR_FROM_DN);
3714 0         0 _traverse_polygon($mcur, $t{d0}, $trnum, $TR_FROM_UP);
3715 0         0 _traverse_polygon($mnew, $t{u1}, $trnum, $TR_FROM_DN);
3716 0         0 _traverse_polygon($mnew, $t{d1}, $trnum, $TR_FROM_UP);
3717             }
3718             } else { #* only downward cusp
3719 1 50       7 if (_equal_to($t{lo}, $seg[$t{lseg}]{v1})) {
3720 0         0 $v0 = $tr[$t{u0}]{rseg};
3721 0         0 $v1 = $seg[$t{lseg}]{next};
3722              
3723 0         0 $retval = $SP_2UP_LEFT;
3724 0 0 0     0 if (($dir == $TR_FROM_UP) && ($t{u0} == $from)) {
3725 0         0 $do_switch = $TRUE;
3726 0         0 $mnew = _make_new_monotone_poly($mcur, $v1, $v0);
3727 0         0 _traverse_polygon($mcur, $t{u0}, $trnum, $TR_FROM_DN);
3728 0         0 _traverse_polygon($mnew, $t{d0}, $trnum, $TR_FROM_UP);
3729 0         0 _traverse_polygon($mnew, $t{u1}, $trnum, $TR_FROM_DN);
3730 0         0 _traverse_polygon($mnew, $t{d1}, $trnum, $TR_FROM_UP);
3731             } else {
3732 0         0 $mnew = _make_new_monotone_poly($mcur, $v0, $v1);
3733 0         0 _traverse_polygon($mcur, $t{u1}, $trnum, $TR_FROM_DN);
3734 0         0 _traverse_polygon($mcur, $t{d0}, $trnum, $TR_FROM_UP);
3735 0         0 _traverse_polygon($mcur, $t{d1}, $trnum, $TR_FROM_UP);
3736 0         0 _traverse_polygon($mnew, $t{u0}, $trnum, $TR_FROM_DN);
3737             }
3738             } else {
3739 1         4 $v0 = $t{rseg};
3740 1         3 $v1 = $tr[$t{u0}]{rseg};
3741 1         2 $retval = $SP_2UP_RIGHT;
3742 1 50 33     9 if (($dir == $TR_FROM_UP) && ($t{u1} == $from)) {
3743 1         3 $do_switch = $TRUE;
3744 1         3 $mnew = _make_new_monotone_poly($mcur, $v1, $v0);
3745 1         4 _traverse_polygon($mcur, $t{u1}, $trnum, $TR_FROM_DN);
3746 1         4 _traverse_polygon($mnew, $t{d1}, $trnum, $TR_FROM_UP);
3747 1         5 _traverse_polygon($mnew, $t{d0}, $trnum, $TR_FROM_UP);
3748 1         4 _traverse_polygon($mnew, $t{u0}, $trnum, $TR_FROM_DN);
3749             } else {
3750 0         0 $mnew = _make_new_monotone_poly($mcur, $v0, $v1);
3751 0         0 _traverse_polygon($mcur, $t{u0}, $trnum, $TR_FROM_DN);
3752 0         0 _traverse_polygon($mcur, $t{d0}, $trnum, $TR_FROM_UP);
3753 0         0 _traverse_polygon($mcur, $t{d1}, $trnum, $TR_FROM_UP);
3754 0         0 _traverse_polygon($mnew, $t{u1}, $trnum, $TR_FROM_DN);
3755             }
3756             }
3757             }
3758             } elsif (($t{u0} > 0) || ($t{u1} > 0)) { # no downward cusp
3759 10 100 66     48 if (($t{d0} > 0) && ($t{d1} > 0)) { # only upward cusp
3760 1 50       5 if (_equal_to($t{hi}, $seg[$t{lseg}]{v0})) {
3761 1         2 $v0 = $tr[$t{d1}]{lseg};
3762 1         2 $v1 = $t{lseg};
3763 1         2 $retval = $SP_2DN_LEFT;
3764 1 50 33     6 if (!(($dir == $TR_FROM_DN) && ($t{d0} == $from))) {
3765 1         2 $do_switch = $TRUE;
3766 1         4 $mnew = _make_new_monotone_poly($mcur, $v1, $v0);
3767 1         4 _traverse_polygon($mcur, $t{u1}, $trnum, $TR_FROM_DN);
3768 1         4 _traverse_polygon($mcur, $t{d1}, $trnum, $TR_FROM_UP);
3769 1         4 _traverse_polygon($mcur, $t{u0}, $trnum, $TR_FROM_DN);
3770 1         3 _traverse_polygon($mnew, $t{d0}, $trnum, $TR_FROM_UP);
3771             } else {
3772 0         0 $mnew = _make_new_monotone_poly($mcur, $v0, $v1);
3773 0         0 _traverse_polygon($mcur, $t{d0}, $trnum, $TR_FROM_UP);
3774 0         0 _traverse_polygon($mnew, $t{u0}, $trnum, $TR_FROM_DN);
3775 0         0 _traverse_polygon($mnew, $t{u1}, $trnum, $TR_FROM_DN);
3776 0         0 _traverse_polygon($mnew, $t{d1}, $trnum, $TR_FROM_UP);
3777             }
3778             } else {
3779 0         0 $v0 = $tr[$t{d1}]{lseg};
3780 0         0 $v1 = $seg[$t{rseg}]{next};
3781              
3782 0         0 $retval = $SP_2DN_RIGHT;
3783 0 0 0     0 if (($dir == $TR_FROM_DN) && ($t{d1} == $from)) {
3784 0         0 $do_switch = $TRUE;
3785 0         0 $mnew = _make_new_monotone_poly($mcur, $v1, $v0);
3786 0         0 _traverse_polygon($mcur, $t{d1}, $trnum, $TR_FROM_UP);
3787 0         0 _traverse_polygon($mnew, $t{u1}, $trnum, $TR_FROM_DN);
3788 0         0 _traverse_polygon($mnew, $t{u0}, $trnum, $TR_FROM_DN);
3789 0         0 _traverse_polygon($mnew, $t{d0}, $trnum, $TR_FROM_UP);
3790             } else {
3791 0         0 $mnew = _make_new_monotone_poly($mcur, $v0, $v1);
3792 0         0 _traverse_polygon($mcur, $t{u0}, $trnum, $TR_FROM_DN);
3793 0         0 _traverse_polygon($mcur, $t{d0}, $trnum, $TR_FROM_UP);
3794 0         0 _traverse_polygon($mcur, $t{u1}, $trnum, $TR_FROM_DN);
3795 0         0 _traverse_polygon($mnew, $t{d1}, $trnum, $TR_FROM_UP);
3796             }
3797             }
3798             } else { # no cusp
3799 9 100 100     29 if (_equal_to($t{hi}, $seg[$t{lseg}]{v0}) &&
    100 100        
3800             _equal_to($t{lo}, $seg[$t{rseg}]{v0})) {
3801 2         5 $v0 = $t{rseg};
3802 2         4 $v1 = $t{lseg};
3803 2         3 $retval = $SP_SIMPLE_LRDN;
3804 2 50       5 if ($dir == $TR_FROM_UP) {
3805 0         0 $do_switch = $TRUE;
3806 0         0 $mnew = _make_new_monotone_poly($mcur, $v1, $v0);
3807 0         0 _traverse_polygon($mcur, $t{u0}, $trnum, $TR_FROM_DN);
3808 0         0 _traverse_polygon($mcur, $t{u1}, $trnum, $TR_FROM_DN);
3809 0         0 _traverse_polygon($mnew, $t{d1}, $trnum, $TR_FROM_UP);
3810 0         0 _traverse_polygon($mnew, $t{d0}, $trnum, $TR_FROM_UP);
3811             } else {
3812 2         6 $mnew = _make_new_monotone_poly($mcur, $v0, $v1);
3813 2         17 _traverse_polygon($mcur, $t{d1}, $trnum, $TR_FROM_UP);
3814 2         5 _traverse_polygon($mcur, $t{d0}, $trnum, $TR_FROM_UP);
3815 2         5 _traverse_polygon($mnew, $t{u0}, $trnum, $TR_FROM_DN);
3816 2         6 _traverse_polygon($mnew, $t{u1}, $trnum, $TR_FROM_DN);
3817             }
3818             } elsif (_equal_to($t{hi}, $seg[$t{rseg}]{v1}) &&
3819             _equal_to($t{lo}, $seg[$t{lseg}]{v1})) {
3820 3         8 $v0 = $seg[$t{rseg}]{next};
3821 3         7 $v1 = $seg[$t{lseg}]{next};
3822              
3823 3         4 $retval = $SP_SIMPLE_LRUP;
3824 3 50       9 if ($dir == $TR_FROM_UP) {
3825 0         0 $do_switch = $TRUE;
3826 0         0 $mnew = _make_new_monotone_poly($mcur, $v1, $v0);
3827 0         0 _traverse_polygon($mcur, $t{u0}, $trnum, $TR_FROM_DN);
3828 0         0 _traverse_polygon($mcur, $t{u1}, $trnum, $TR_FROM_DN);
3829 0         0 _traverse_polygon($mnew, $t{d1}, $trnum, $TR_FROM_UP);
3830 0         0 _traverse_polygon($mnew, $t{d0}, $trnum, $TR_FROM_UP);
3831             } else {
3832 3         8 $mnew = _make_new_monotone_poly($mcur, $v0, $v1);
3833 3         58 _traverse_polygon($mcur, $t{d1}, $trnum, $TR_FROM_UP);
3834 3         8 _traverse_polygon($mcur, $t{d0}, $trnum, $TR_FROM_UP);
3835 3         9 _traverse_polygon($mnew, $t{u0}, $trnum, $TR_FROM_DN);
3836 3         7 _traverse_polygon($mnew, $t{u1}, $trnum, $TR_FROM_DN);
3837             }
3838             } else { # no split possible
3839 4         11 $retval = $SP_NOSPLIT;
3840 4         11 _traverse_polygon($mcur, $t{u0}, $trnum, $TR_FROM_DN);
3841 4         12 _traverse_polygon($mcur, $t{d0}, $trnum, $TR_FROM_UP);
3842 4         9 _traverse_polygon($mcur, $t{u1}, $trnum, $TR_FROM_DN);
3843 4         8 _traverse_polygon($mcur, $t{d1}, $trnum, $TR_FROM_UP);
3844             }
3845             }
3846             }
3847              
3848 15         44 return $retval;
3849             }
3850              
3851             # For each monotone polygon, find the ymax and ymin (to determine the
3852             # two y-monotone chains) and pass on this monotone polygon for greedy
3853             # triangulation.
3854             # Take care not to triangulate duplicate monotone polygons
3855              
3856             sub _triangulate_monotone_polygons {
3857 2     2   5 my ($nvert, $nmonpoly) = @_;
3858              
3859 2         4 my ($ymax, $ymin);
3860 0         0 my ($p, $vfirst, $posmax, $posmin, $v);
3861 0         0 my ($vcount, $processed);
3862              
3863 2         3 $op_idx = 0;
3864 2         9 for (my $i = 0; $i < $nmonpoly; $i++) {
3865 9         11 $vcount = 1;
3866 9         9 $processed = $FALSE;
3867 9         18 $vfirst = $mchain[$mon[$i]]{vnum};
3868 9         59 $ymax = {x => $vert[$vfirst]{pt}{x} , y => $vert[$vfirst]{pt}{y}};
3869 9         44 $ymin = {x => $vert[$vfirst]{pt}{x} , y => $vert[$vfirst]{pt}{y}};
3870 9         17 $posmax = $posmin = $mon[$i];
3871 9         16 $mchain[$mon[$i]]{marked} = $TRUE;
3872 9         15 $p = $mchain[$mon[$i]]{next};
3873 9         28 while (($v = $mchain[$p]{vnum}) != $vfirst) {
3874 23 100       47 if ($mchain[$p]{marked}) {
3875 1         2 $processed = $TRUE;
3876 1         2 last; # break from while
3877             } else {
3878 22         41 $mchain[$p]{marked} = $TRUE;
3879             }
3880              
3881 22 100       47 if (_greater_than($vert[$v]{pt}, $ymax)) {
3882 4         16 $ymax = {x => $vert[$v]{pt}{x} , y => $vert[$v]{pt}{y}};
3883 4         9 $posmax = $p;
3884             }
3885 22 100       53 if (_less_than($vert[$v]{pt}, $ymin)) {
3886 11         39 $ymin = {x => $vert[$v]{pt}{x} , y => $vert[$v]{pt}{y}};
3887 11         24 $posmin = $p;
3888             }
3889 22         38 $p = $mchain[$p]{next};
3890 22         56 $vcount++;
3891             }
3892              
3893 9 100       17 if ($processed) { # Go to next polygon
3894 1         3 next;
3895             }
3896              
3897 8 100       16 if ($vcount == 3) { # already a triangle
3898 6         16 $op[$op_idx][0] = $mchain[$p]{vnum};
3899 6         14 $op[$op_idx][1] = $mchain[$mchain[$p]{next}]{vnum};
3900 6         9 $op[$op_idx][2] = $mchain[$mchain[$p]{prev}]{vnum};
3901 6         17 $op_idx++;
3902             } else { # triangulate the polygon
3903 2         6 $v = $mchain[$mchain[$posmax]{next}]{vnum};
3904 2 100       7 if (_equal_to($vert[$v]{pt}, $ymin)) { # LHS is a single line
3905 1         4 _triangulate_single_polygon($nvert, $posmax, $TRI_LHS);
3906             } else {
3907 1         4 _triangulate_single_polygon($nvert, $posmax, $TRI_RHS);
3908             }
3909             }
3910             }
3911              
3912 2         7 return $op_idx;
3913             }
3914              
3915             # A greedy corner-cutting algorithm to triangulate a y-monotone
3916             # polygon in O(n) time.
3917             # Joseph O-Rourke, Computational Geometry in C.
3918             #
3919             sub _triangulate_single_polygon {
3920 2     2   3 my ($nvert, $posmax, $side) = @_;
3921              
3922 2         4 my $v;
3923             my @rc;
3924 2         2 my $ri = 0; # reflex chain
3925 2         3 my ($endv, $tmp, $vpos);
3926              
3927 2 100       6 if ($side == $TRI_RHS) { # RHS segment is a single segment
3928 1         3 $rc[0] = $mchain[$posmax]{vnum};
3929 1         2 $tmp = $mchain[$posmax]{next};
3930 1         2 $rc[1] = $mchain[$tmp]{vnum};
3931 1         2 $ri = 1;
3932              
3933 1         2 $vpos = $mchain[$tmp]{next};
3934 1         3 $v = $mchain[$vpos]{vnum};
3935              
3936 1 50       5 if (($endv = $mchain[$mchain[$posmax]{prev}]{vnum}) == 0) {
3937 0         0 $endv = $nvert;
3938             }
3939             } else { # LHS is a single segment
3940 1         2 $tmp = $mchain[$posmax]{next};
3941 1         3 $rc[0] = $mchain[$tmp]{vnum};
3942 1         3 $tmp = $mchain[$tmp]{next};
3943 1         2 $rc[1] = $mchain[$tmp]{vnum};
3944 1         2 $ri = 1;
3945              
3946 1         2 $vpos = $mchain[$tmp]{next};
3947 1         2 $v = $mchain[$vpos]{vnum};
3948              
3949 1         3 $endv = $mchain[$posmax]{vnum};
3950             }
3951              
3952 2   100     9 while (($v != $endv) || ($ri > 1)) {
3953 12 100       20 if ($ri > 0) { # reflex chain is non-empty
3954 8 100       24 if (_Cross($vert[$v]{pt}, $vert[$rc[$ri - 1]]{pt}, $vert[$rc[$ri]]{pt}) > 0) {
3955             # convex corner: cut if off
3956 6         15 $op[$op_idx][0] = $rc[$ri - 1];
3957 6         10 $op[$op_idx][1] = $rc[$ri];
3958 6         7 $op[$op_idx][2] = $v;
3959 6         7 $op_idx++;
3960 6         19 $ri--;
3961             } else { # non-convex
3962             # add v to the chain
3963 2         3 $ri++;
3964 2         5 $rc[$ri] = $v;
3965 2         4 $vpos = $mchain[$vpos]{next};
3966 2         11 $v = $mchain[$vpos]{vnum};
3967             }
3968             } else { # reflex-chain empty: add v to the
3969             # reflex chain and advance it
3970 4         5 $rc[++$ri] = $v;
3971 4         6 $vpos = $mchain[$vpos]{next};
3972 4         11 $v = $mchain[$vpos]{vnum};
3973             }
3974             } # end-while
3975              
3976             # reached the bottom vertex. Add in the triangle formed
3977 2         15 $op[$op_idx][0] = $rc[$ri - 1];
3978 2         4 $op[$op_idx][1] = $rc[$ri];
3979 2         5 $op[$op_idx][2] = $v;
3980 2         3 $op_idx++;
3981 2         11 $ri--;
3982              
3983             }
3984              
3985             1;