1 unit clipper;
2 
3 (*******************************************************************************
4 *                                                                              *
5 * Author    :  Angus Johnson                                                   *
6 * Version   :  6.4.2                                                           *
7 * Date      :  27 February 2017                                                *
8 * Website   :  http://www.angusj.com                                           *
9 * Copyright :  Angus Johnson 2010-2017                                         *
10 *                                                                              *
11 * License:                                                                     *
12 * Use, modification & distribution is subject to Boost Software License Ver 1. *
13 * http://www.boost.org/LICENSE_1_0.txt                                         *
14 *                                                                              *
15 * Attributions:                                                                *
16 * The code in this library is an extension of Bala Vatti's clipping algorithm: *
17 * "A generic solution to polygon clipping"                                     *
18 * Communications of the ACM, Vol 35, Issue 7 (July 1992) PP 56-63.             *
19 * http://portal.acm.org/citation.cfm?id=129906                                 *
20 *                                                                              *
21 * Computer graphics and geometric modeling: implementation and algorithms      *
22 * By Max K. Agoston                                                            *
23 * Springer; 1 edition (January 4, 2005)                                        *
24 * http://books.google.com/books?q=vatti+clipping+agoston                       *
25 *                                                                              *
26 * See also:                                                                    *
27 * "Polygon Offsetting by Computing Winding Numbers"                            *
28 * Paper no. DETC2005-85513 PP. 565-575                                         *
29 * ASME 2005 International Design Engineering Technical Conferences             *
30 * and Computers and Information in Engineering Conference (IDETC/CIE2005)      *
31 * September 24-28, 2005 , Long Beach, California, USA                          *
32 * http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf              *
33 *                                                                              *
34 *******************************************************************************)
35 
36 //use_int32: When enabled 32bit ints are used instead of 64bit ints. This
37 //improve performance but coordinate values are limited to the range +/- 46340
38 //{$DEFINE use_int32}
39 
40 //use_xyz: adds a Z member to IntPoint (with only a minor cost to performance)
41 //{$DEFINE use_xyz}
42 
43 //use_lines: Enables open path clipping (with a very minor cost to performance)
44 {$DEFINE use_lines}
45 
46 {$IFDEF FPC}
47   {$DEFINE INLINING}
48   {$DEFINE UInt64Support}
49 {$ELSE}
50   // enable LEGACYIFEND for Delphi XE4+
51   {$IF CompilerVersion >= 25.0}
52     {$LEGACYIFEND ON}
53   {$IFEND}
54 
55   // use generic lists for NextGen compiler
56   {$IFDEF NEXTGEN}
57     {$DEFINE USEGENERICS}
58   {$ENDIF}
59 
60   {$IFDEF ConditionalExpressions}
61     {$IF CompilerVersion >= 15} //Delphi 7
62       {$DEFINE UInt64Support} //nb: Delphi7 only marginally supports UInt64.
63     {$IFEND}
64     {$IF CompilerVersion >= 18} //Delphi 2007
65       //Inline has been supported since D2005.
66       //However D2005 and D2006 have an Inline codegen bug (QC41166).
67       //http://www.thedelphigeek.com/2007/02/nasty-inline-codegen-bug-in-bds-2006.html
68       {$DEFINE INLINING}
69     {$IFEND}
70   {$ENDIF}
71 {$ENDIF}
72 
73 interface
74 
75 uses
76   SysUtils, Types, Classes,
77   {$IFDEF USEGENERICS}
78     Generics.Collections, Generics.Defaults,
79   {$ENDIF}
80   Math;
81 
82 const
83   def_arc_tolerance = 0.25;
84 
85 type
86 {$IFDEF use_int32}
87 {$IF CompilerVersion < 20} //Delphi 2009
88   cInt = Integer; //Int32 supported since D2009.
89 {$ELSE}
90   cInt = Int32;
91 {$IFEND}
92 {$ELSE}
93   cInt = Int64;
94 {$ENDIF}
95 
96   PIntPoint = ^TIntPoint;
97 {$IFDEF use_xyz}
98   TIntPoint = record X, Y, Z: cInt; end;
99 {$ELSE}
100   TIntPoint = record X, Y: cInt; end;
101 {$ENDIF}
102 
103   TIntRect = record Left, Top, Right, Bottom: cInt; end;
104 
105   TDoublePoint = record X, Y: Double; end;
106   TArrayOfDoublePoint = array of TDoublePoint;
107 
108 {$IFDEF use_xyz}
109   TZFillCallback =
110     procedure (const E1Bot, E1Top, E2Bot, E2Top: TIntPoint; var Pt: TIntPoint);
111 {$ENDIF}
112 
113   TInitOption = (ioReverseSolution, ioStrictlySimple, ioPreserveCollinear);
114   TInitOptions = set of TInitOption;
115 
116   TClipType = (ctIntersection, ctUnion, ctDifference, ctXor);
117   TPolyType = (ptSubject, ptClip);
118   //By far the most widely used winding rules for polygon filling are
119   //EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32)
120   //Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL)
121   //see http://glprogramming.com/red/chapter11.html
122   TPolyFillType = (pftEvenOdd, pftNonZero, pftPositive, pftNegative);
123 
124   //TJoinType & TEndType are used by OffsetPaths()
125   TJoinType = (jtSquare, jtRound, jtMiter);
126   TEndType = (etClosedPolygon, etClosedLine,
127     etOpenButt, etOpenSquare, etOpenRound); //and etSingle still to come
128 
129   TPath = array of TIntPoint;
130   TPaths = array of TPath;
131 
132   TPolyNode = class;
133   TArrayOfPolyNode = array of TPolyNode;
134 
135   TPolyNode = class
136   private
137     FPath    : TPath;
138     FParent  : TPolyNode;
139     FIndex   : Integer;
140     FCount   : Integer;
141     FBuffLen : Integer;
142     FIsOpen  : Boolean;
143     FChilds  : TArrayOfPolyNode;
144     FJoinType: TJoinType; //used by ClipperOffset only
145     FEndType : TEndType;  //used by ClipperOffset only
GetChildnull146     function  GetChild(Index: Integer): TPolyNode;
IsHoleNodenull147     function  IsHoleNode: boolean;
148     procedure AddChild(PolyNode: TPolyNode);
GetNextSiblingUpnull149     function  GetNextSiblingUp: TPolyNode;
150   public
GetNextnull151     function  GetNext: TPolyNode;
152     property  ChildCount: Integer read FCount;
153     property  Childs[index: Integer]: TPolyNode read GetChild;
154     property  Parent: TPolyNode read FParent;
155     property  IsHole: Boolean read IsHoleNode;
156     property  IsOpen: Boolean read FIsOpen;
157     property  Contour: TPath read FPath;
158   end;
159 
160   TPolyTree = class(TPolyNode)
161   private
162     FAllNodes: TArrayOfPolyNode; //container for ALL PolyNodes
GetTotalnull163     function GetTotal: Integer;
164   public
165     procedure Clear;
GetFirstnull166     function GetFirst: TPolyNode;
167     destructor Destroy; override;
168     property Total: Integer read GetTotal;
169   end;
170 
171   //the definitions below are used internally ...
172   TEdgeSide = (esLeft, esRight);
173   TDirection = (dRightToLeft, dLeftToRight);
174 
175   POutPt = ^TOutPt;
176 
177   PEdge = ^TEdge;
178   TEdge = record
179     Bot  : TIntPoint;      //bottom
180     Curr : TIntPoint;      //current (updated for every new scanbeam)
181     Top  : TIntPoint;      //top
182     Dx   : Double;         //inverse of slope
183     PolyType : TPolyType;
184     Side     : TEdgeSide;  //side only refers to current side of solution poly
185     WindDelta: Integer;    //1 or -1 depending on winding direction
186     WindCnt  : Integer;
187     WindCnt2 : Integer;    //winding count of the opposite PolyType
188     OutIdx   : Integer;
189     Next     : PEdge;
190     Prev     : PEdge;
191     NextInLML: PEdge;
192     PrevInAEL: PEdge;
193     NextInAEL: PEdge;
194     PrevInSEL: PEdge;
195     NextInSEL: PEdge;
196   end;
197 
198   PEdgeArray = ^TEdgeArray;
199   TEdgeArray = array[0.. MaxInt div sizeof(TEdge) -1] of TEdge;
200 
201   PScanbeam = ^TScanbeam;
202   TScanbeam = record
203     Y    : cInt;
204     Next : PScanbeam;
205   end;
206 
207   PMaxima = ^TMaxima;
208   TMaxima = record
209     X     : cInt;
210     Next  : PMaxima;
211     Prev  : PMaxima;
212   end;
213 
214   PIntersectNode = ^TIntersectNode;
215   TIntersectNode = record
216     Edge1: PEdge;
217     Edge2: PEdge;
218     Pt   : TIntPoint;
219   end;
220 
221   PLocalMinimum = ^TLocalMinimum;
222   TLocalMinimum = record
223     Y         : cInt;
224     LeftBound : PEdge;
225     RightBound: PEdge;
226   end;
227 
228   //OutRec: contains a path in the clipping solution. Edges in the AEL will
229   //carry a pointer to an OutRec when they are part of the clipping solution.
230   POutRec = ^TOutRec;
231   TOutRec = record
232     Idx         : Integer;
233     BottomPt    : POutPt;
234     IsHole      : Boolean;
235     IsOpen      : Boolean;
236     //The 'FirstLeft' field points to another OutRec that contains or is the
237     //'parent' of OutRec. It is 'first left' because the ActiveEdgeList (AEL) is
238     //parsed left from the current edge (owning OutRec) until the owner OutRec
239     //is found. This field simplifies sorting the polygons into a tree structure
240     //which reflects the parent/child relationships of all polygons.
241     //This field should be renamed Parent, and will be later.
242     FirstLeft   : POutRec;
243     Pts         : POutPt;
244     PolyNode    : TPolyNode;
245   end;
246 
247   TOutPt = record
248     Idx      : Integer;
249     Pt       : TIntPoint;
250     Next     : POutPt;
251     Prev     : POutPt;
252   end;
253 
254   PJoin = ^TJoin;
255   TJoin = record
256     OutPt1   : POutPt;
257     OutPt2   : POutPt;
258     OffPt    : TIntPoint; //offset point (provides slope of common edges)
259   end;
260 
261   {$IFDEF USEGENERICS}
262   TEgdeList = TList<PEdgeArray>;
263   TLocMinList = TList<PLocalMinimum>;
264   TPolyOutList = TList<POutRec>;
265   TJoinList = TList<PJoin>;
266   TIntersecList = TList<PIntersectNode>;
267   {$ELSE}
268   TEgdeList = TList;
269   TLocMinList = TList;
270   TPolyOutList = TList;
271   TJoinList = TList;
272   TIntersecList = TList;
273   {$ENDIF}
274 
275   TClipperBase = class
276   private
277     FEdgeList         : TEgdeList;
278     FPolyOutList      : TPolyOutList;
279     FScanbeam         : PScanbeam;    //scanbeam list
280     FUse64BitRange    : Boolean;      //see LoRange and HiRange consts notes below
281     FHasOpenPaths     : Boolean;
282     procedure DisposeLocalMinimaList;
283     procedure DisposePolyPts(PP: POutPt);
ProcessBoundnull284     function ProcessBound(E: PEdge; NextIsForward: Boolean): PEdge;
285   protected
286     FLocMinList       : TLocMinList;
287     FCurrentLocMinIdx : Integer;
288     FPreserveCollinear : Boolean;
289     FActiveEdges      : PEdge;        //active Edge list
290     procedure Reset; virtual;
291     procedure InsertScanbeam(const Y: cInt);
PopScanbeamnull292     function PopScanbeam(out Y: cInt): Boolean;
LocalMinimaPendingnull293     function LocalMinimaPending: Boolean;
PopLocalMinimanull294     function PopLocalMinima(Y: cInt;
295       out LocalMinima: PLocalMinimum): Boolean;
296     procedure DisposeScanbeamList;
CreateOutRecnull297     function CreateOutRec: POutRec;
298     procedure DisposeOutRec(Index: Integer);
299     procedure DisposeAllOutRecs;
300     procedure SwapPositionsInAEL(E1, E2: PEdge);
301     procedure DeleteFromAEL(E: PEdge);
302     procedure UpdateEdgeIntoAEL(var E: PEdge);
303     property HasOpenPaths: Boolean read FHasOpenPaths;
304   public
305     constructor Create; virtual;
306     destructor Destroy; override;
307     procedure Clear; virtual;
308 
AddPathnull309     function AddPath(const Path: TPath; PolyType: TPolyType; Closed: Boolean): Boolean; virtual;
AddPathsnull310     function AddPaths(const Paths: TPaths; PolyType: TPolyType; Closed: Boolean): Boolean;
311     //PreserveCollinear: Prevents removal of 'inner' vertices when three or
312     //more vertices are collinear in solution polygons.
313     property PreserveCollinear: Boolean
314       read FPreserveCollinear write FPreserveCollinear;
315   end;
316 
317   TClipper = class(TClipperBase)
318   private
319     FJoinList         : TJoinList;
320     FGhostJoinList    : TJoinList;
321     FIntersectList    : TIntersecList;
322     FSortedEdges      : PEdge;        //used for temporary sorting
323     FClipType         : TClipType;
324     FMaxima           : PMaxima;      //maxima XPos list
325     FClipFillType     : TPolyFillType;
326     FSubjFillType     : TPolyFillType;
327     FExecuteLocked    : Boolean;
328     FReverseOutput    : Boolean;
329     FStrictSimple      : Boolean;
330     FUsingPolyTree    : Boolean;
331 {$IFDEF use_xyz}
332     FZFillCallback    : TZFillCallback;
333 {$ENDIF}
334     procedure InsertMaxima(const X: cInt);
335     procedure DisposeMaximaList;
336     procedure SetWindingCount(Edge: PEdge);
IsEvenOddFillTypenull337     function IsEvenOddFillType(Edge: PEdge): Boolean;
IsEvenOddAltFillTypenull338     function IsEvenOddAltFillType(Edge: PEdge): Boolean;
339     procedure AddEdgeToSEL(Edge: PEdge);
PopEdgeFromSELnull340     function PopEdgeFromSEL(out E: PEdge): Boolean;
341     procedure CopyAELToSEL;
342     procedure InsertLocalMinimaIntoAEL(const BotY: cInt);
343     procedure SwapPositionsInSEL(E1, E2: PEdge);
344     procedure ProcessHorizontal(HorzEdge: PEdge);
345     procedure ProcessHorizontals;
ProcessIntersectionsnull346     function ProcessIntersections(const TopY: cInt): Boolean;
347     procedure BuildIntersectList(const TopY: cInt);
348     procedure ProcessIntersectList;
349     procedure IntersectEdges(E1,E2: PEdge; Pt: TIntPoint);
350     procedure DoMaxima(E: PEdge);
FixupIntersectionOrdernull351     function FixupIntersectionOrder: Boolean;
352     procedure ProcessEdgesAtTopOfScanbeam(const TopY: cInt);
IsContributingnull353     function IsContributing(Edge: PEdge): Boolean;
GetLastOutPtnull354     function GetLastOutPt(E: PEdge): POutPt;
355     procedure AddLocalMaxPoly(E1, E2: PEdge; const Pt: TIntPoint);
AddLocalMinPolynull356     function AddLocalMinPoly(E1, E2: PEdge; const Pt: TIntPoint): POutPt;
AddOutPtnull357     function AddOutPt(E: PEdge; const Pt: TIntPoint): POutPt;
GetOutRecnull358     function GetOutRec(Idx: integer): POutRec;
359     procedure AppendPolygon(E1, E2: PEdge);
360     procedure DisposeIntersectNodes;
BuildResultnull361     function BuildResult: TPaths;
BuildResult2null362     function BuildResult2(PolyTree: TPolyTree): Boolean;
363     procedure FixupOutPolygon(OutRec: POutRec);
364     procedure FixupOutPolyline(OutRec: POutRec);
365     procedure SetHoleState(E: PEdge; OutRec: POutRec);
366     procedure AddJoin(Op1, Op2: POutPt; const OffPt: TIntPoint);
367     procedure ClearJoins;
368     procedure AddGhostJoin(OutPt: POutPt; const OffPt: TIntPoint);
369     procedure ClearGhostJoins;
JoinPointsnull370     function JoinPoints(Jr: PJoin; OutRec1, OutRec2: POutRec): Boolean;
371     procedure FixupFirstLefts1(OldOutRec, NewOutRec: POutRec);
372     procedure FixupFirstLefts2(InnerOutRec, OuterOutRec: POutRec);
373     procedure FixupFirstLefts3(OldOutRec, NewOutRec: POutRec);
374     procedure DoSimplePolygons;
375     procedure JoinCommonEdges;
376     procedure FixHoleLinkage(OutRec: POutRec);
377   protected
ExecuteInternalnull378     function ExecuteInternal: Boolean; virtual;
379   public
Executenull380     function Execute(clipType: TClipType;
381       out solution: TPaths;
382       FillType: TPolyFillType = pftEvenOdd): Boolean; overload;
Executenull383     function Execute(clipType: TClipType;
384       out solution: TPaths;
385       subjFillType: TPolyFillType;
386       clipFillType: TPolyFillType): Boolean; overload;
Executenull387     function Execute(clipType: TClipType;
388       out PolyTree: TPolyTree;
389       FillType: TPolyFillType = pftEvenOdd): Boolean; overload;
Executenull390     function Execute(clipType: TClipType;
391       out PolyTree: TPolyTree;
392       subjFillType: TPolyFillType;
393       clipFillType: TPolyFillType): Boolean; overload;
394     constructor Create(InitOptions: TInitOptions = []); reintroduce; overload;
395     destructor Destroy; override;
396     //ReverseSolution: reverses the default orientation
397     property ReverseSolution: Boolean read FReverseOutput write FReverseOutput;
398     //StrictlySimple: when false (the default) solutions are 'weakly' simple
399     property StrictlySimple: Boolean read FStrictSimple write FStrictSimple;
400 {$IFDEF use_xyz}
TZFillCallbacknull401     property ZFillFunction: TZFillCallback read FZFillCallback write FZFillCallback;
402 {$ENDIF}
403   end;
404 
405   TClipperOffset = class
406   private
407     FDelta: Double;
408     FSinA, FSin, FCos: Extended;
409     FMiterLim, FStepsPerRad: Double;
410     FNorms: TArrayOfDoublePoint;
411     FSolution: TPaths;
412     FOutPos: Integer;
413     FInP: TPath;
414     FOutP: TPath;
415 
416     FLowest: TIntPoint; //X = Path index, Y = Path offset (to lowest point)
417     FPolyNodes: TPolyNode;
418     FMiterLimit: Double;
419     FArcTolerance: Double;
420 
421     procedure AddPoint(const Pt: TIntPoint);
422     procedure DoSquare(J, K: Integer);
423     procedure DoMiter(J, K: Integer; R: Double);
424     procedure DoRound(J, K: Integer);
425     procedure OffsetPoint(J: Integer;
426       var K: Integer; JoinType: TJoinType);
427 
428     procedure FixOrientations;
429     procedure DoOffset(Delta: Double);
430   public
431     constructor Create(MiterLimit: Double = 2; ArcTolerance: Double = def_arc_tolerance);
432     destructor Destroy; override;
433     procedure AddPath(const Path: TPath; JoinType: TJoinType; EndType: TEndType);
434     procedure AddPaths(const Paths: TPaths; JoinType: TJoinType; EndType: TEndType);
435     procedure Clear;
436     procedure Execute(out solution: TPaths; Delta: Double); overload;
437     procedure Execute(out solution: TPolyTree; Delta: Double); overload;
438     property MiterLimit: double read FMiterLimit write FMiterLimit;
439     property ArcTolerance: double read FArcTolerance write FArcTolerance;
440 
441   end;
442 
Orientationnull443 function Orientation(const Pts: TPath): Boolean; overload;
Areanull444 function Area(const Pts: TPath): Double; overload;
PointInPolygonnull445 function PointInPolygon (const pt: TIntPoint; const poly: TPath): Integer; overload;
GetBoundsnull446 function GetBounds(const polys: TPaths): TIntRect;
447 
448 {$IFDEF use_xyz}
IntPointnull449 function IntPoint(const X, Y: Int64; Z: Int64 = 0): TIntPoint; overload;
IntPointnull450 function IntPoint(const X, Y: Double; Z: Double = 0): TIntPoint; overload;
451 {$ELSE}
IntPointnull452 function IntPoint(const X, Y: cInt): TIntPoint; overload;
IntPointnull453 function IntPoint(const X, Y: Double): TIntPoint; overload;
454 {$ENDIF}
455 
DoublePointnull456 function DoublePoint(const X, Y: Double): TDoublePoint; overload;
DoublePointnull457 function DoublePoint(const Ip: TIntPoint): TDoublePoint; overload;
458 
ReversePathnull459 function ReversePath(const Pts: TPath): TPath;
ReversePathsnull460 function ReversePaths(const Pts: TPaths): TPaths;
461 
462 //SimplifyPolygon converts a self-intersecting polygon into a simple polygon.
SimplifyPolygonnull463 function SimplifyPolygon(const Poly: TPath; FillType: TPolyFillType = pftEvenOdd): TPaths;
SimplifyPolygonsnull464 function SimplifyPolygons(const Polys: TPaths; FillType: TPolyFillType = pftEvenOdd): TPaths;
465 
466 //CleanPolygon removes adjacent vertices closer than the specified distance.
CleanPolygonnull467 function CleanPolygon(const Poly: TPath; Distance: double = 1.415): TPath;
CleanPolygonsnull468 function CleanPolygons(const Polys: TPaths; Distance: double = 1.415): TPaths;
469 
MinkowskiSumnull470 function MinkowskiSum(const Pattern, Path: TPath; PathIsClosed: Boolean): TPaths; overload;
MinkowskiSumnull471 function MinkowskiSum(const Pattern: TPath; const Paths: TPaths;
472   PathFillType: TPolyFillType; PathIsClosed: Boolean): TPaths; overload;
MinkowskiDiffnull473 function MinkowskiDiff(const Poly1, Poly2: TPath): TPaths;
474 
PolyTreeToPathsnull475 function PolyTreeToPaths(PolyTree: TPolyTree): TPaths;
ClosedPathsFromPolyTreenull476 function ClosedPathsFromPolyTree(PolyTree: TPolyTree): TPaths;
OpenPathsFromPolyTreenull477 function OpenPathsFromPolyTree(PolyTree: TPolyTree): TPaths;
478 
479 const
placesnull480   //The SlopesEqual function places the most limits on coordinate values
481   //So, to avoid overflow errors, they must not exceed the following values...
482   //Also, if all coordinates are within +/-LoRange, then calculations will be
483   //faster. Otherwise using Int128 math will render the library ~10-15% slower.
484 {$IFDEF use_int32}
485   LoRange: cInt = 46340;
486   HiRange: cInt = 46340;
487 {$ELSE}
488   LoRange: cInt = $B504F333;          //3.0e+9
489   HiRange: cInt = $3FFFFFFFFFFFFFFF;  //9.2e+18
490 {$ENDIF}
491 
492 implementation
493 
494 //NOTE: The Clipper library has been developed with software that uses an
495 //inverted Y axis display. Therefore 'above' and 'below' in the code's comments
496 //will reflect this. For example: given coord A (0,20) and coord B (0,10),
497 //A.Y would be considered BELOW B.Y to correctly understand the comments.
498 
499 const
500   Horizontal: Double = -3.4e+38;
501 
502   Unassigned : Integer = -1;
503   Skip       : Integer = -2; //flag for the edge that closes an open path
504   Tolerance  : double = 1.0E-15;
505   Two_Pi     : double = 2 * PI;
506 
507 resourcestring
508   rsDoMaxima = 'DoMaxima error';
509   rsUpdateEdgeIntoAEL = 'UpdateEdgeIntoAEL error';
510   rsHorizontal = 'ProcessHorizontal error';
511   rsInvalidInt = 'Coordinate exceeds range bounds';
512   rsIntersect = 'Intersection error';
513   rsOpenPath  = 'AddPath: Open paths must be subject.';
514   rsOpenPath2  = 'AddPath: Open paths have been disabled.';
515   rsOpenPath3  = 'Error: TPolyTree struct is needed for open path clipping.';
516   rsPolylines = 'Error intersecting polylines';
517   rsClipperOffset = 'Error: No PolyTree assigned';
518 
519 //------------------------------------------------------------------------------
520 // TPolyNode methods ...
521 //------------------------------------------------------------------------------
522 
GetChildnull523 function TPolyNode.GetChild(Index: Integer): TPolyNode;
524 begin
525   if (Index < 0) or (Index >= FCount) then
526     raise Exception.Create('TPolyNode range error: ' + inttostr(Index));
527   Result := FChilds[Index];
528 end;
529 //------------------------------------------------------------------------------
530 
531 procedure TPolyNode.AddChild(PolyNode: TPolyNode);
532 begin
533   if FCount = FBuffLen then
534   begin
535     Inc(FBuffLen, 16);
536     SetLength(FChilds, FBuffLen);
537   end;
538   PolyNode.FParent := self;
539   PolyNode.FIndex := FCount;
540   FChilds[FCount] := PolyNode;
541   Inc(FCount);
542 end;
543 //------------------------------------------------------------------------------
544 
IsHoleNodenull545 function TPolyNode.IsHoleNode: boolean;
546 var
547   Node: TPolyNode;
548 begin
549   Result := True;
550   Node := FParent;
551   while Assigned(Node) do
552   begin
553     Result := not Result;
554     Node := Node.FParent;
555   end;
556 end;
557 //------------------------------------------------------------------------------
558 
GetNextnull559 function TPolyNode.GetNext: TPolyNode;
560 begin
561   if FCount > 0 then
562     Result := FChilds[0] else
563     Result := GetNextSiblingUp;
564 end;
565 //------------------------------------------------------------------------------
566 
GetNextSiblingUpnull567 function TPolyNode.GetNextSiblingUp: TPolyNode;
568 begin
569   if not Assigned(FParent) then //protects against TPolyTree.GetNextSiblingUp()
570     Result := nil
571   else if FIndex = FParent.FCount -1 then
572       Result := FParent.GetNextSiblingUp
573   else
574       Result := FParent.Childs[FIndex +1];
575 end;
576 
577 //------------------------------------------------------------------------------
578 // TPolyTree methods ...
579 //------------------------------------------------------------------------------
580 
581 destructor TPolyTree.Destroy;
582 begin
583   Clear;
584   inherited;
585 end;
586 //------------------------------------------------------------------------------
587 
588 procedure TPolyTree.Clear;
589 var
590   I: Integer;
591 begin
592   for I := 0 to high(FAllNodes) do FAllNodes[I].Free;
593   FAllNodes := nil;
594   FBuffLen := 16;
595   SetLength(FChilds, FBuffLen);
596   FCount := 0;
597 end;
598 //------------------------------------------------------------------------------
599 
GetFirstnull600 function TPolyTree.GetFirst: TPolyNode;
601 begin
602   if FCount > 0 then
603     Result := FChilds[0] else
604     Result := nil;
605 end;
606 //------------------------------------------------------------------------------
607 
GetTotalnull608 function TPolyTree.GetTotal: Integer;
609 begin
610   Result := length(FAllNodes);
611   //with negative offsets, ignore the hidden outer polygon ...
612   if (Result > 0) and (FAllNodes[0] <> FChilds[0]) then dec(Result);
613 end;
614 
615 {$IFNDEF use_int32}
616 
617 //------------------------------------------------------------------------------
618 // UInt64 math support for Delphi 6
619 //------------------------------------------------------------------------------
620 
621 {$OVERFLOWCHECKS OFF}
622 {$IFNDEF UInt64Support}
623 function CompareUInt64(const i, j: Int64): Integer;
624 begin
625   if Int64Rec(i).Hi < Int64Rec(j).Hi then
626     Result := -1
627   else if Int64Rec(i).Hi > Int64Rec(j).Hi then
628     Result := 1
629   else if Int64Rec(i).Lo < Int64Rec(j).Lo then
630     Result := -1
631   else if Int64Rec(i).Lo > Int64Rec(j).Lo then
632     Result := 1
633   else
634     Result := 0;
635 end;
636 {$ENDIF}
637 
638 function UInt64LT(const i, j: Int64): Boolean; {$IFDEF INLINING} inline; {$ENDIF}
639 begin
640 {$IFDEF UInt64Support}
641   Result := UInt64(i) < UInt64(j);
642 {$ELSE}
643   Result := CompareUInt64(i, j) = -1;
644 {$ENDIF}
645 end;
646 
647 function UInt64GT(const i, j: Int64): Boolean; {$IFDEF INLINING} inline; {$ENDIF}
648 begin
649 {$IFDEF UInt64Support}
650   Result := UInt64(i) > UInt64(j);
651 {$ELSE}
652   Result := CompareUInt64(i, j) = 1;
653 {$ENDIF}
654 end;
655 {$OVERFLOWCHECKS ON}
656 
657 //------------------------------------------------------------------------------
658 // Int128 Functions ...
659 //------------------------------------------------------------------------------
660 
661 const
662   Mask32Bits = $FFFFFFFF;
663 
664 type
665 
666   //nb: TInt128.Lo is typed Int64 instead of UInt64 to provide Delphi 7
667   //compatability. However while UInt64 isn't a recognised type in
668   //Delphi 7, it can still be used in typecasts.
669   TInt128 = record
670     Hi   : Int64;
671     Lo   : Int64;
672   end;
673 
674 {$OVERFLOWCHECKS OFF}
675 procedure Int128Negate(var Val: TInt128);
676 begin
677   if Val.Lo = 0 then
678   begin
679     Val.Hi := -Val.Hi;
680   end else
681   begin
682     Val.Lo := -Val.Lo;
683     Val.Hi := not Val.Hi;
684   end;
685 end;
686 //------------------------------------------------------------------------------
687 
Int128null688 function Int128(const val: Int64): TInt128; overload;
689 begin
690   Result.Lo := val;
691   if val < 0 then
692     Result.Hi := -1 else
693     Result.Hi := 0;
694 end;
695 //------------------------------------------------------------------------------
696 
Int128Equalnull697 function Int128Equal(const Int1, Int2: TInt128): Boolean;
698 begin
699   Result := (Int1.Lo = Int2.Lo) and (Int1.Hi = Int2.Hi);
700 end;
701 //------------------------------------------------------------------------------
702 
Int128LessThannull703 function Int128LessThan(const Int1, Int2: TInt128): Boolean;
704 begin
705   if (Int1.Hi <> Int2.Hi) then Result := Int1.Hi < Int2.Hi
706   else Result := UInt64LT(Int1.Lo, Int2.Lo);
707 end;
708 //------------------------------------------------------------------------------
709 
Int128IsNegativenull710 function Int128IsNegative(const Int: TInt128): Boolean;
711 begin
712   Result := Int.Hi < 0;
713 end;
714 //------------------------------------------------------------------------------
715 
Int128IsPositivenull716 function Int128IsPositive(const Int: TInt128): Boolean;
717 begin
718   Result := (Int.Hi > 0) or ((Int.Hi = 0) and (Int.Lo <> 0));
719 end;
720 //------------------------------------------------------------------------------
721 
Int128Addnull722 function Int128Add(const Int1, Int2: TInt128): TInt128;
723 begin
724   Result.Lo := Int1.Lo + Int2.Lo;
725   Result.Hi := Int1.Hi + Int2.Hi;
726   if UInt64LT(Result.Lo, Int1.Lo) then Inc(Result.Hi);
727 end;
728 //------------------------------------------------------------------------------
729 
Int128Subnull730 function Int128Sub(const Int1, Int2: TInt128): TInt128;
731 begin
732   Result.Hi := Int1.Hi - Int2.Hi;
733   Result.Lo := Int1.Lo - Int2.Lo;
734   if UInt64GT(Result.Lo, Int1.Lo) then Dec(Result.Hi);
735 end;
736 //------------------------------------------------------------------------------
737 
Int128Mulnull738 function Int128Mul(Int1, Int2: Int64): TInt128;
739 var
740   A, B, C: Int64;
741   Int1Hi, Int1Lo, Int2Hi, Int2Lo: Int64;
742   Negate: Boolean;
743 begin
744   //save the Result's sign before clearing both sign bits ...
745   Negate := (Int1 < 0) <> (Int2 < 0);
746   if Int1 < 0 then Int1 := -Int1;
747   if Int2 < 0 then Int2 := -Int2;
748 
749   Int1Hi := Int1 shr 32;
750   Int1Lo := Int1 and Mask32Bits;
751   Int2Hi := Int2 shr 32;
752   Int2Lo := Int2 and Mask32Bits;
753 
754   A := Int1Hi * Int2Hi;
755   B := Int1Lo * Int2Lo;
756   //because the high (sign) bits in both int1Hi & int2Hi have been zeroed,
757   //there's no risk of 64 bit overflow in the following assignment
758   //(ie: $7FFFFFFF*$FFFFFFFF + $7FFFFFFF*$FFFFFFFF < 64bits)
759   C := Int1Hi*Int2Lo + Int2Hi*Int1Lo;
760   //Result = A shl 64 + C shl 32 + B ...
761   Result.Hi := A + (C shr 32);
762   A := C shl 32;
763 
764   Result.Lo := A + B;
765   if UInt64LT(Result.Lo, A) then
766     Inc(Result.Hi);
767 
768   if Negate then Int128Negate(Result);
769 end;
770 //------------------------------------------------------------------------------
771 
Int128Divnull772 function Int128Div(Dividend, Divisor: TInt128{; out Remainder: TInt128}): TInt128;
773 var
774   Cntr: TInt128;
775   Negate: Boolean;
776 begin
777   if (Divisor.Lo = 0) and (Divisor.Hi = 0) then
778     raise Exception.create('int128Div error: divide by zero');
779 
780   Negate := (Divisor.Hi < 0) <> (Dividend.Hi < 0);
781   if Dividend.Hi < 0 then Int128Negate(Dividend);
782   if Divisor.Hi < 0 then Int128Negate(Divisor);
783 
784   if Int128LessThan(Divisor, Dividend) then
785   begin
786     Result.Hi := 0;
787     Result.Lo := 0;
788     Cntr.Lo := 1;
789     Cntr.Hi := 0;
790     //while (Dividend >= Divisor) do
791     while not Int128LessThan(Dividend, Divisor) do
792     begin
793       //divisor := divisor shl 1;
794       Divisor.Hi := Divisor.Hi shl 1;
795       if Divisor.Lo < 0 then Inc(Divisor.Hi);
796       Divisor.Lo := Divisor.Lo shl 1;
797 
798       //Cntr := Cntr shl 1;
799       Cntr.Hi := Cntr.Hi shl 1;
800       if Cntr.Lo < 0 then Inc(Cntr.Hi);
801       Cntr.Lo := Cntr.Lo shl 1;
802     end;
803     //Divisor := Divisor shr 1;
804     Divisor.Lo := Divisor.Lo shr 1;
805     if Divisor.Hi and $1 = $1 then
806       Int64Rec(Divisor.Lo).Hi := Cardinal(Int64Rec(Divisor.Lo).Hi) or $80000000;
807     Divisor.Hi := Divisor.Hi shr 1;
808 
809     //Cntr := Cntr shr 1;
810     Cntr.Lo := Cntr.Lo shr 1;
811     if Cntr.Hi and $1 = $1 then
812       Int64Rec(Cntr.Lo).Hi := Cardinal(Int64Rec(Cntr.Lo).Hi) or $80000000;
813     Cntr.Hi := Cntr.Hi shr 1;
814 
815     //while (Cntr > 0) do
816     while not ((Cntr.Hi = 0) and (Cntr.Lo = 0)) do
817     begin
818       //if ( Dividend >= Divisor) then
819       if not Int128LessThan(Dividend, Divisor) then
820       begin
821         //Dividend := Dividend - Divisor;
822         Dividend := Int128Sub(Dividend, Divisor);
823 
824         //Result := Result or Cntr;
825         Result.Hi := Result.Hi or Cntr.Hi;
826         Result.Lo := Result.Lo or Cntr.Lo;
827       end;
828       //Divisor := Divisor shr 1;
829       Divisor.Lo := Divisor.Lo shr 1;
830       if Divisor.Hi and $1 = $1 then
831         Int64Rec(Divisor.Lo).Hi := Cardinal(Int64Rec(Divisor.Lo).Hi) or $80000000;
832       Divisor.Hi := Divisor.Hi shr 1;
833 
834       //Cntr := Cntr shr 1;
835       Cntr.Lo := Cntr.Lo shr 1;
836       if Cntr.Hi and $1 = $1 then
837         Int64Rec(Cntr.Lo).Hi := Cardinal(Int64Rec(Cntr.Lo).Hi) or $80000000;
838       Cntr.Hi := Cntr.Hi shr 1;
839     end;
840     if Negate then Int128Negate(Result);
841     //Remainder := Dividend;
842   end
843   else if (Divisor.Hi = Dividend.Hi) and (Divisor.Lo = Dividend.Lo) then
844   begin
845     if Negate then Result := Int128(-1) else Result := Int128(1);
846   end else
847   begin
848     Result := Int128(0);
849   end;
850 end;
851 //------------------------------------------------------------------------------
852 
Int128AsDoublenull853 function Int128AsDouble(val: TInt128): Double;
854 const
855   shift64: Double = 18446744073709551616.0;
856 var
857   lo: Int64;
858 begin
859   if (val.Hi < 0) then
860   begin
861     lo := -val.Lo;
862     if lo = 0 then
863       Result := val.Hi * shift64 else
864       Result := -(not val.Hi * shift64 + UInt64(lo));
865   end else
866     Result := val.Hi * shift64 + UInt64(val.Lo);
867 end;
868 //------------------------------------------------------------------------------
869 
870 {$OVERFLOWCHECKS ON}
871 
872 {$ENDIF}
873 
874 //------------------------------------------------------------------------------
875 // Miscellaneous Functions ...
876 //------------------------------------------------------------------------------
877 
PointCountnull878 function PointCount(Pts: POutPt): Integer;
879 var
880   P: POutPt;
881 begin
882   Result := 0;
883   if not Assigned(Pts) then Exit;
884   P := Pts;
885   repeat
886     Inc(Result);
887     P := P.Next;
888   until P = Pts;
889 end;
890 //------------------------------------------------------------------------------
891 
PointsEqualnull892 function PointsEqual(const P1, P2: TIntPoint): Boolean; {$IFDEF INLINING} inline; {$ENDIF}
893 begin
894   Result := (P1.X = P2.X) and (P1.Y = P2.Y);
895 end;
896 //------------------------------------------------------------------------------
897 
898 {$IFDEF use_xyz}
IntPointnull899 function IntPoint(const X, Y: Int64; Z: Int64 = 0): TIntPoint;
900 begin
901   Result.X := X;
902   Result.Y := Y;
903   Result.Z := Z;
904 end;
905 //------------------------------------------------------------------------------
906 
IntPointnull907 function IntPoint(const X, Y: Double; Z: Double = 0): TIntPoint;
908 begin
909   Result.X := Round(X);
910   Result.Y := Round(Y);
911   Result.Z := Round(Z);
912 end;
913 //------------------------------------------------------------------------------
914 
915 {$ELSE}
916 
IntPointnull917 function IntPoint(const X, Y: cInt): TIntPoint;
918 begin
919   Result.X := X;
920   Result.Y := Y;
921 end;
922 //------------------------------------------------------------------------------
923 
IntPointnull924 function IntPoint(const X, Y: Double): TIntPoint;
925 begin
926   Result.X := Round(X);
927   Result.Y := Round(Y);
928 end;
929 
930 {$ENDIF}
931 //------------------------------------------------------------------------------
932 
DoublePointnull933 function DoublePoint(const X, Y: Double): TDoublePoint;
934 begin
935   Result.X := X;
936   Result.Y := Y;
937 end;
938 //------------------------------------------------------------------------------
939 
DoublePointnull940 function DoublePoint(const Ip: TIntPoint): TDoublePoint;
941 begin
942   Result.X := Ip.X;
943   Result.Y := Ip.Y;
944 end;
945 //------------------------------------------------------------------------------
946 
Areanull947 function Area(const Pts: TPath): Double;
948 var
949   I, J, Cnt: Integer;
950   D: Double;
951 begin
952   Result := 0.0;
953   Cnt := Length(Pts);
954   if (Cnt < 3) then Exit;
955   J := cnt - 1;
956   for I := 0 to Cnt -1 do
957   begin
958     D := (Pts[j].X + Pts[i].X);
959     Result := Result + D * (Pts[j].Y - Pts[i].Y);
960     J := I;
961   end;
962   Result := -Result * 0.5;
963 end;
964 //------------------------------------------------------------------------------
965 
Areanull966 function Area(Op: POutPt): Double; overload;
967 var
968   op2: POutPt;
969   d2: Double;
970 begin
971   Result := 0;
972   op2 := op;
973   if Assigned(op2) then
974     repeat
975       d2 := op2.Prev.Pt.X + op2.Pt.X;
976       Result := Result + d2 * (op2.Prev.Pt.Y - op2.Pt.Y);
977       op2 := op2.Next;
978     until op2 = op;
979   Result := Result * 0.5;
980 end;
981 //------------------------------------------------------------------------------
982 
Areanull983 function Area(OutRec: POutRec): Double; overload;
984 begin
985   result := Area(OutRec.Pts);
986 end;
987 //------------------------------------------------------------------------------
988 
Orientationnull989 function Orientation(const Pts: TPath): Boolean;
990 begin
991   Result := Area(Pts) >= 0;
992 end;
993 //------------------------------------------------------------------------------
994 
ReversePathnull995 function ReversePath(const Pts: TPath): TPath;
996 var
997   I, HighI: Integer;
998 begin
999   HighI := high(Pts);
1000   SetLength(Result, HighI +1);
1001   for I := 0 to HighI do
1002     Result[I] := Pts[HighI - I];
1003 end;
1004 //------------------------------------------------------------------------------
1005 
ReversePathsnull1006 function ReversePaths(const Pts: TPaths): TPaths;
1007 var
1008   I, J, highJ: Integer;
1009 begin
1010   I := length(Pts);
1011   SetLength(Result, I);
1012   for I := 0 to I -1 do
1013   begin
1014     highJ := high(Pts[I]);
1015     SetLength(Result[I], highJ+1);
1016     for J := 0 to highJ do
1017       Result[I][J] := Pts[I][highJ - J];
1018   end;
1019 end;
1020 //------------------------------------------------------------------------------
1021 
GetBoundsnull1022 function GetBounds(const polys: TPaths): TIntRect;
1023 var
1024   I,J,Len: Integer;
1025 begin
1026   Len := Length(polys);
1027   I := 0;
1028   while (I < Len) and (Length(polys[I]) = 0) do inc(I);
1029   if (I = Len) then
1030   begin
1031     with Result do begin Left := 0; Top := 0; Right := 0; Bottom := 0; end;
1032     Exit;
1033   end;
1034   Result.Left := polys[I][0].X;
1035   Result.Right := Result.Left;
1036   Result.Top := polys[I][0].Y;
1037   Result.Bottom := Result.Top;
1038   for I := I to Len -1 do
1039     for J := 0 to High(polys[I]) do
1040     begin
1041       if polys[I][J].X < Result.Left then Result.Left := polys[I][J].X
1042       else if polys[I][J].X > Result.Right then Result.Right := polys[I][J].X;
1043       if polys[I][J].Y < Result.Top then Result.Top := polys[I][J].Y
1044       else if polys[I][J].Y > Result.Bottom then Result.Bottom := polys[I][J].Y;
1045     end;
1046 end;
1047 //------------------------------------------------------------------------------
1048 
PointInPolygonnull1049 function PointInPolygon (const pt: TIntPoint; const poly: TPath): Integer;
1050 var
1051   i, cnt: Integer;
1052   d, d2, d3: double; //use cInt ???
1053   ip, ipNext: TIntPoint;
1054 begin
1055   //returns 0 if false, +1 if true, -1 if pt ON polygon boundary
1056   //http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf
1057   //nb: if poly bounds are known, test them first before calling this function.
1058   result := 0;
1059   cnt := Length(poly);
1060   if cnt < 3 then Exit;
1061   ip := poly[0];
1062   for i := 1 to cnt do
1063   begin
1064     if i < cnt then ipNext := poly[i]
1065     else ipNext := poly[0];
1066 
1067     if (ipNext.Y = pt.Y) then
1068     begin
1069       if (ipNext.X = pt.X) or ((ip.Y = pt.Y) and
1070         ((ipNext.X > pt.X) = (ip.X < pt.X))) then
1071       begin
1072         result := -1;
1073         Exit;
1074       end;
1075     end;
1076 
1077     if ((ip.Y < pt.Y) <> (ipNext.Y < pt.Y)) then
1078     begin
1079       if (ip.X >= pt.X) then
1080       begin
1081         if (ipNext.X > pt.X) then
1082           result := 1 - result
1083         else
1084         begin
1085           d2 := (ip.X - pt.X);
1086           d3 := (ipNext.X - pt.X);
1087           d := d2 * (ipNext.Y - pt.Y) - d3 * (ip.Y - pt.Y);
1088           if (d = 0) then begin result := -1; Exit; end;
1089           if ((d > 0) = (ipNext.Y > ip.Y)) then
1090             result := 1 - result;
1091         end;
1092       end else
1093       begin
1094         if (ipNext.X > pt.X) then
1095         begin
1096           d2 := (ip.X - pt.X);
1097           d3 := (ipNext.X - pt.X);
1098           d := d2 * (ipNext.Y - pt.Y) - d3 * (ip.Y - pt.Y);
1099           if (d = 0) then begin result := -1; Exit; end;
1100           if ((d > 0) = (ipNext.Y > ip.Y)) then
1101             result := 1 - result;
1102         end;
1103       end;
1104     end;
1105     ip := ipNext;
1106   end;
1107 end;
1108 //---------------------------------------------------------------------------
1109 
1110 //See "The Point in Polygon Problem for Arbitrary Polygons" by Hormann & Agathos
1111 //http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf
PointInPolygonnull1112 function PointInPolygon (const pt: TIntPoint; ops: POutPt): Integer; overload;
1113 var
1114   d, d2, d3: double; //nb: double not cInt avoids potential overflow errors
1115   opStart: POutPt;
1116   pt1, ptN: TIntPoint;
1117 begin
1118   //returns 0 if false, +1 if true, -1 if pt ON polygon boundary
1119   result := 0;
1120   opStart := ops;
1121   pt1.X := ops.Pt.X; pt1.Y := ops.Pt.Y;
1122   repeat
1123     ops := ops.Next;
1124     ptN.X := ops.Pt.X; ptN.Y := ops.Pt.Y;
1125 
1126     if (ptN.Y = pt.Y) then
1127     begin
1128       if (ptN.X = pt.X) or ((pt1.Y = pt.Y) and
1129         ((ptN.X > pt.X) = (pt1.X < pt.X))) then
1130       begin
1131         result := -1;
1132         Exit;
1133       end;
1134     end;
1135 
1136     if ((pt1.Y < pt.Y) <> (ptN.Y < pt.Y)) then
1137     begin
1138       if (pt1.X >= pt.X) then
1139       begin
1140         if (ptN.X > pt.X) then
1141           result := 1 - result
1142         else
1143         begin
1144           d2 := (pt1.X - pt.X);
1145           d3 := (ptN.X - pt.X);
1146           d := d2 * (ptN.Y - pt.Y) - d3 * (pt1.Y - pt.Y);
1147           if (d = 0) then begin result := -1; Exit; end;
1148           if ((d > 0) = (ptN.Y > pt1.Y)) then
1149             result := 1 - result;
1150         end;
1151       end else
1152       begin
1153         if (ptN.X > pt.X) then
1154         begin
1155           d2 := (pt1.X - pt.X);
1156           d3 := (ptN.X - pt.X);
1157           d := d2 * (ptN.Y - pt.Y) - d3 * (pt1.Y - pt.Y);
1158           if (d = 0) then begin result := -1; Exit; end;
1159           if ((d > 0) = (ptN.Y > pt1.Y)) then
1160             result := 1 - result;
1161         end;
1162       end;
1163     end;
1164     pt1 := ptN;
1165   until ops = opStart;
1166 end;
1167 //---------------------------------------------------------------------------
1168 
Poly2ContainsPoly1null1169 function Poly2ContainsPoly1(OutPt1, OutPt2: POutPt): Boolean;
1170 var
1171   res: integer;
1172   op: POutPt;
1173 begin
1174   op := OutPt1;
1175   repeat
1176     //nb: PointInPolygon returns 0 if false, +1 if true, -1 if pt on polygon
1177     res := PointInPolygon(op.Pt, OutPt2);
1178     if (res >= 0) then
1179     begin
1180       Result := res > 0;
1181       Exit;
1182     end;
1183     op := op.Next;
1184   until op = OutPt1;
1185   Result := true; //all points on line => result = true
1186 end;
1187 //---------------------------------------------------------------------------
1188 
SlopesEqualnull1189 function SlopesEqual(E1, E2: PEdge;
1190   UseFullInt64Range: Boolean): Boolean; overload;
1191 begin
1192 {$IFNDEF use_int32}
1193   if UseFullInt64Range then
1194     Result := Int128Equal(Int128Mul(E1.Top.Y-E1.Bot.Y, E2.Top.X-E2.Bot.X),
1195       Int128Mul(E1.Top.X-E1.Bot.X, E2.Top.Y-E2.Bot.Y))
1196   else
1197 {$ENDIF}
1198     Result := (E1.Top.Y-E1.Bot.Y) * (E2.Top.X-E2.Bot.X) =
1199       (E1.Top.X-E1.Bot.X) * (E2.Top.Y-E2.Bot.Y);
1200 end;
1201 //---------------------------------------------------------------------------
1202 
SlopesEqualnull1203 function SlopesEqual(const Pt1, Pt2, Pt3: TIntPoint;
1204   UseFullInt64Range: Boolean): Boolean; overload;
1205 begin
1206 {$IFNDEF use_int32}
1207   if UseFullInt64Range then
1208     Result := Int128Equal(
1209       Int128Mul(Pt1.Y-Pt2.Y, Pt2.X-Pt3.X), Int128Mul(Pt1.X-Pt2.X, Pt2.Y-Pt3.Y))
1210   else
1211 {$ENDIF}
1212     Result := (Pt1.Y-Pt2.Y)*(Pt2.X-Pt3.X) = (Pt1.X-Pt2.X)*(Pt2.Y-Pt3.Y);
1213 end;
1214 //---------------------------------------------------------------------------
1215 
SlopesEqualnull1216 function SlopesEqual(const L1a, L1b, L2a, L2b: TIntPoint;
1217   UseFullInt64Range: Boolean): Boolean; overload;
1218 begin
1219 {$IFNDEF use_int32}
1220   if UseFullInt64Range then
1221     Result := Int128Equal(
1222       Int128Mul(L1a.Y-L1b.Y, L2a.X-L2b.X), Int128Mul(L2a.Y-L2b.Y, L1a.X-L1b.X))
1223   else
1224 {$ENDIF}
1225   //dy1 * dx2 = dy2 * dx1
1226   Result := (L1a.Y-L1b.Y)*(L2a.X-L2b.X) = (L2a.Y-L2b.Y)*(L1a.X-L1b.X);
1227 end;
1228 
1229 (*****************************************************************************
1230 *  Dx:                  0(90�)                       Slope:   0  = Dx: -inf  *
1231 *                       |                            Slope: 0.5  = Dx:   -2  *
1232 *      +inf (180�) <--- o ---> -inf (0�)             Slope: 2.0  = Dx: -0.5  *
1233 *                                                    Slope: inf  = Dx:    0  *
1234 *****************************************************************************)
1235 
GetDxnull1236 function GetDx(const Pt1, Pt2: TIntPoint): Double;
1237 begin
1238   if (Pt1.Y = Pt2.Y) then Result := Horizontal
1239   else Result := (Pt2.X - Pt1.X)/(Pt2.Y - Pt1.Y);
1240 end;
1241 //---------------------------------------------------------------------------
1242 
1243 procedure SetDx(E: PEdge); {$IFDEF INLINING} inline; {$ENDIF}
1244 var
1245   dy: cInt;
1246 begin
1247   dy := (E.Top.Y - E.Bot.Y);
1248   if dy = 0 then E.Dx := Horizontal
1249   else E.Dx := (E.Top.X - E.Bot.X)/dy;
1250 end;
1251 //---------------------------------------------------------------------------
1252 
IsHorizontalnull1253 function IsHorizontal(E: PEdge): Boolean; {$IFDEF INLINING} inline; {$ENDIF}
1254 begin
1255   Result := E.Dx = Horizontal;
1256 end;
1257 //------------------------------------------------------------------------------
1258 
1259 procedure Swap(var val1, val2: cInt); {$IFDEF INLINING} inline; {$ENDIF}
1260 var
1261   tmp: cInt;
1262 begin
1263   tmp := val1;
1264   val1 := val2;
1265   val2 := tmp;
1266 end;
1267 //---------------------------------------------------------------------------
1268 
1269 procedure SwapSides(Edge1, Edge2: PEdge); {$IFDEF INLINING} inline; {$ENDIF}
1270 var
1271   Side: TEdgeSide;
1272 begin
1273   Side :=  Edge1.Side;
1274   Edge1.Side := Edge2.Side;
1275   Edge2.Side := Side;
1276 end;
1277 //------------------------------------------------------------------------------
1278 
1279 procedure SwapPolyIndexes(Edge1, Edge2: PEdge);
1280 {$IFDEF INLINING} inline; {$ENDIF}
1281 var
1282   OutIdx: Integer;
1283 begin
1284   OutIdx :=  Edge1.OutIdx;
1285   Edge1.OutIdx := Edge2.OutIdx;
1286   Edge2.OutIdx := OutIdx;
1287 end;
1288 //------------------------------------------------------------------------------
1289 
NextIsHorznull1290 function NextIsHorz(Edge: PEdge): Boolean;
1291 {$IFDEF INLINING} inline; {$ENDIF}
1292 begin
1293   Result := assigned(Edge.NextInLML) and (Edge.NextInLML.Dx = Horizontal);
1294 end;
1295 //------------------------------------------------------------------------------
1296 
NextIsHorzAtnull1297 function NextIsHorzAt(Edge: PEdge; Y: cInt): Boolean;
1298 {$IFDEF INLINING} inline; {$ENDIF}
1299 begin
1300   Result := (Edge.Top.Y = Y) and assigned(Edge.NextInLML) and
1301     (Edge.NextInLML.Dx = Horizontal);
1302 end;
1303 //------------------------------------------------------------------------------
1304 
TopXnull1305 function TopX(Edge: PEdge; const currentY: cInt): cInt;
1306 {$IFDEF INLINING} inline; {$ENDIF}
1307 begin
1308   if currentY = Edge.Top.Y then Result := Edge.Top.X
1309   else if Edge.Top.X = Edge.Bot.X then Result := Edge.Bot.X
1310   else Result := Edge.Bot.X + Round(Edge.Dx*(currentY - Edge.Bot.Y));
1311 end;
1312 //------------------------------------------------------------------------------
1313 
1314 {$IFDEF use_xyz}
1315 Procedure SetZ(var Pt: TIntPoint; E1, E2: PEdge; ZFillFunc: TZFillCallback);
1316 begin
1317   if (Pt.Z <> 0) or not assigned(ZFillFunc) then Exit
1318   else if PointsEqual(Pt, E1.Bot) then Pt.Z := E1.Bot.Z
1319   else if PointsEqual(Pt, E2.Bot) then Pt.Z := E2.Bot.Z
1320   else if PointsEqual(Pt, E1.Top) then Pt.Z := E1.Top.Z
1321   else if PointsEqual(Pt, E2.Top) then Pt.Z := E2.Top.Z
1322   else ZFillFunc(E1.Bot, E1.Top, E2.Bot, E2.Top, Pt);
1323 end;
1324 //------------------------------------------------------------------------------
1325 {$ENDIF}
1326 
1327 procedure IntersectPointEx(Edge1, Edge2: PEdge; out ip: TIntPoint);
1328 var
1329   B1,B2,M: Double;
1330 begin
1331 {$IFDEF use_xyz}
1332   ip.Z := 0;
1333 {$ENDIF}
1334   if (edge1.Dx = edge2.Dx) then
1335   begin
1336     ip.Y := edge1.Curr.Y;
1337     ip.X := TopX(edge1, ip.Y);
1338     Exit;
1339   end;
1340 
1341   if Edge1.Dx = 0 then
1342   begin
1343     ip.X := Edge1.Bot.X;
1344     if Edge2.Dx = Horizontal then
1345       ip.Y := Edge2.Bot.Y
1346     else
1347     begin
1348       with Edge2^ do B2 := Bot.Y - (Bot.X/Dx);
1349       ip.Y := round(ip.X/Edge2.Dx + B2);
1350     end;
1351   end
1352   else if Edge2.Dx = 0 then
1353   begin
1354     ip.X := Edge2.Bot.X;
1355     if Edge1.Dx = Horizontal then
1356       ip.Y := Edge1.Bot.Y
1357     else
1358     begin
1359       with Edge1^ do B1 := Bot.Y - (Bot.X/Dx);
1360       ip.Y := round(ip.X/Edge1.Dx + B1);
1361     end;
1362   end else
1363   begin
1364     with Edge1^ do B1 := Bot.X - Bot.Y * Dx;
1365     with Edge2^ do B2 := Bot.X - Bot.Y * Dx;
1366     M := (B2-B1)/(Edge1.Dx - Edge2.Dx);
1367     ip.Y := round(M);
1368     if Abs(Edge1.Dx) < Abs(Edge2.Dx) then
1369       ip.X := round(Edge1.Dx * M + B1)
1370     else
1371       ip.X := round(Edge2.Dx * M + B2);
1372   end;
1373 
1374   //The precondition - E.Curr.X > eNext.Curr.X - indicates that the two edges do
1375   //intersect below TopY (and hence below the tops of either Edge). However,
1376   //when edges are almost parallel, rounding errors may cause False positives -
1377   //indicating intersections when there really aren't any. Also, floating point
1378   //imprecision can incorrectly place an intersect point beyond/above an Edge.
1379   //Therfore, further adjustment to IP is warranted ...
1380   if (ip.Y < Edge1.Top.Y) or (ip.Y < Edge2.Top.Y) then
1381   begin
1382     //Find the lower top of the two edges and compare X's at this Y.
1383     //If Edge1's X is greater than Edge2's X then it's fair to assume an
1384     //intersection really has occurred...
1385     if (Edge1.Top.Y > Edge2.Top.Y) then
1386       ip.Y := edge1.Top.Y else
1387       ip.Y := edge2.Top.Y;
1388     if Abs(Edge1.Dx) < Abs(Edge2.Dx) then
1389       ip.X := TopX(Edge1, ip.Y) else
1390       ip.X := TopX(Edge2, ip.Y);
1391   end;
1392   //finally, don't allow 'ip' to be BELOW curr.Y (ie bottom of scanbeam) ...
1393   if (ip.Y > Edge1.Curr.Y) then
1394   begin
1395     ip.Y := Edge1.Curr.Y;
1396     if (abs(Edge1.Dx) > abs(Edge2.Dx)) then //ie use more vertical edge
1397       ip.X := TopX(Edge2, ip.Y) else
1398       ip.X := TopX(Edge1, ip.Y);
1399   end;
1400 end;
1401 //------------------------------------------------------------------------------
1402 
1403 procedure ReversePolyPtLinks(PP: POutPt);
1404 var
1405   Pp1,Pp2: POutPt;
1406 begin
1407   if not Assigned(PP) then Exit;
1408   Pp1 := PP;
1409   repeat
1410     Pp2:= Pp1.Next;
1411     Pp1.Next := Pp1.Prev;
1412     Pp1.Prev := Pp2;
1413     Pp1 := Pp2;
1414   until Pp1 = PP;
1415 end;
1416 //------------------------------------------------------------------------------
1417 
Pt2IsBetweenPt1AndPt3null1418 function Pt2IsBetweenPt1AndPt3(const Pt1, Pt2, Pt3: TIntPoint): Boolean;
1419 begin
1420   //nb: assumes collinearity.
1421   if PointsEqual(Pt1, Pt3) or PointsEqual(Pt1, Pt2) or PointsEqual(Pt3, Pt2) then
1422     Result := False
1423   else if (Pt1.X <> Pt3.X) then
1424     Result := (Pt2.X > Pt1.X) = (Pt2.X < Pt3.X)
1425   else
1426     Result := (Pt2.Y > Pt1.Y) = (Pt2.Y < Pt3.Y);
1427 end;
1428 //------------------------------------------------------------------------------
1429 
GetOverlapnull1430 function GetOverlap(const A1, A2, B1, B2: cInt; out Left, Right: cInt): Boolean;
1431 begin
1432   if (A1 < A2) then
1433   begin
1434     if (B1 < B2) then begin Left := Max(A1,B1); Right := Min(A2,B2); end
1435     else begin Left := Max(A1,B2); Right := Min(A2,B1); end;
1436   end else
1437   begin
1438     if (B1 < B2) then begin Left := Max(A2,B1); Right := Min(A1,B2); end
1439     else begin Left := Max(A2,B2); Right := Min(A1,B1); end
1440   end;
1441   Result := Left < Right;
1442 end;
1443 //------------------------------------------------------------------------------
1444 
1445 procedure UpdateOutPtIdxs(OutRec: POutRec);
1446 var
1447   op: POutPt;
1448 begin
1449   op := OutRec.Pts;
1450   repeat
1451     op.Idx := OutRec.Idx;
1452     op := op.Prev;
1453   until op = OutRec.Pts;
1454 end;
1455 //------------------------------------------------------------------------------
1456 
1457 procedure RangeTest(const Pt: TIntPoint; var Use64BitRange: Boolean);
1458 begin
1459   if Use64BitRange then
1460   begin
1461     if (Pt.X > HiRange) or (Pt.Y > HiRange) or
1462       (-Pt.X > HiRange) or (-Pt.Y > HiRange) then
1463         raise exception.Create(rsInvalidInt);
1464   end
1465   else if (Pt.X > LoRange) or (Pt.Y > LoRange) or
1466     (-Pt.X > LoRange) or (-Pt.Y > LoRange) then
1467   begin
1468     Use64BitRange := true;
1469     RangeTest(Pt, Use64BitRange);
1470   end;
1471 end;
1472 //------------------------------------------------------------------------------
1473 
1474 procedure ReverseHorizontal(E: PEdge);
1475 begin
1476   //swap horizontal edges' top and bottom x's so they follow the natural
1477   //progression of the bounds - ie so their xbots will align with the
1478   //adjoining lower Edge. [Helpful in the ProcessHorizontal() method.]
1479   Swap(E.Top.X, E.Bot.X);
1480 {$IFDEF use_xyz}
1481   Swap(E.Top.Z, E.Bot.Z);
1482 {$ENDIF}
1483 end;
1484 //------------------------------------------------------------------------------
1485 
1486 procedure InitEdge(E, Next, Prev: PEdge;
1487   const Pt: TIntPoint); {$IFDEF INLINING} inline; {$ENDIF}
1488 begin
1489   E.Curr := Pt;
1490   E.Next := Next;
1491   E.Prev := Prev;
1492   E.OutIdx := Unassigned;
1493 end;
1494 //------------------------------------------------------------------------------
1495 
1496 procedure InitEdge2(E: PEdge; PolyType: TPolyType);
1497 {$IFDEF INLINING} inline; {$ENDIF}
1498 begin
1499   if E.Curr.Y >= E.Next.Curr.Y then
1500   begin
1501     E.Bot := E.Curr;
1502     E.Top := E.Next.Curr;
1503   end else
1504   begin
1505     E.Top := E.Curr;
1506     E.Bot := E.Next.Curr;
1507   end;
1508   SetDx(E);
1509   E.PolyType := PolyType;
1510 end;
1511 //------------------------------------------------------------------------------
1512 
RemoveEdgenull1513 function RemoveEdge(E: PEdge): PEdge; {$IFDEF INLINING} inline; {$ENDIF}
1514 begin
1515   //removes E from double_linked_list (but without disposing from memory)
1516   E.Prev.Next := E.Next;
1517   E.Next.Prev := E.Prev;
1518   Result := E.Next;
1519   E.Prev := nil; //flag as removed (see ClipperBase.Clear)
1520 end;
1521 //------------------------------------------------------------------------------
1522 
FindNextLocMinnull1523 function FindNextLocMin(E: PEdge): PEdge; {$IFDEF INLINING} inline; {$ENDIF}
1524 var
1525   E2: PEdge;
1526 begin
1527   while True do
1528   begin
1529     while not PointsEqual(E.Bot, E.Prev.Bot) or
1530       PointsEqual(E.Curr, E.Top) do E := E.Next;
1531     if (E.Dx <> Horizontal) and (E.Prev.Dx <> Horizontal) then break;
1532     while (E.Prev.Dx = Horizontal) do E := E.Prev;
1533     E2 := E; //E2 == first horizontal
1534     while (E.Dx = Horizontal) do E := E.Next;
1535     if (E.Top.Y = E.Prev.Bot.Y) then Continue; //ie just an intermediate horz.
1536     //E == first edge past horizontals
1537     if E2.Prev.Bot.X < E.Bot.X then E := E2;
1538     //E is first horizontal when CW and first past horizontals when CCW
1539     break;
1540   end;
1541   Result := E;
1542 end;
1543 //------------------------------------------------------------------------------
1544 
GetUnitNormalnull1545 function GetUnitNormal(const Pt1, Pt2: TIntPoint): TDoublePoint;
1546 var
1547   Dx, Dy, F: Double;
1548 begin
1549   if (Pt2.X = Pt1.X) and (Pt2.Y = Pt1.Y) then
1550   begin
1551     Result.X := 0;
1552     Result.Y := 0;
1553     Exit;
1554   end;
1555 
1556   Dx := (Pt2.X - Pt1.X);
1557   Dy := (Pt2.Y - Pt1.Y);
1558   F := 1 / Hypot(Dx, Dy);
1559   Dx := Dx * F;
1560   Dy := Dy * F;
1561   Result.X := Dy;
1562   Result.Y := -Dx
1563 end;
1564 
1565 //------------------------------------------------------------------------------
1566 // TClipperBase methods ...
1567 //------------------------------------------------------------------------------
1568 
1569 constructor TClipperBase.Create;
1570 begin
1571   inherited;
1572   FEdgeList := TEgdeList.Create;
1573   FLocMinList := TLocMinList.Create;
1574   FPolyOutList := TPolyOutList.Create;
1575   FCurrentLocMinIdx := 0;
1576   FUse64BitRange := False; //ie default is False
1577 end;
1578 //------------------------------------------------------------------------------
1579 
1580 destructor TClipperBase.Destroy;
1581 begin
1582   Clear;
1583   DisposeScanbeamList;
1584   FPolyOutList.Free;
1585   FEdgeList.Free;
1586   FLocMinList.Free;
1587   inherited;
1588 end;
1589 //------------------------------------------------------------------------------
1590 
TClipperBase.ProcessBoundnull1591 function TClipperBase.ProcessBound(E: PEdge; NextIsForward: Boolean): PEdge;
1592 var
1593   EStart, Horz: PEdge;
1594   locMin: PLocalMinimum;
1595 begin
1596   Result := E;
1597   if (E.OutIdx = Skip) then
1598   begin
1599     //check if there are edges beyond the skip edge in the bound and if so
1600     //create another LocMin and calling ProcessBound once more ...
1601     if NextIsForward then
1602     begin
1603       while (E.Top.Y = E.Next.Bot.Y) do
1604         E := E.Next;
1605       //don't include top horizontals here ...
1606       while (E <> Result) and (E.Dx = Horizontal) do E := E.Prev;
1607     end else
1608     begin
1609       while (E.Top.Y = E.Prev.Bot.Y) do E := E.Prev;
1610       while (E <> Result) and (E.Dx = Horizontal) do E := E.Next;
1611     end;
1612     if E = Result then
1613     begin
1614       if NextIsForward then Result := E.Next
1615       else Result := E.Prev;
1616     end else
1617     begin
1618       if NextIsForward then
1619         E := Result.Next else
1620         E := Result.Prev;
1621       new(locMin);
1622       locMin.Y := E.Bot.Y;
1623       locMin.LeftBound := nil;
1624       locMin.RightBound := E;
1625       E.WindDelta := 0;
1626       Result := ProcessBound(E, NextIsForward);
1627       FLocMinList.Add(locMin);
1628     end;
1629     Exit;
1630   end;
1631 
1632   if (E.Dx = Horizontal) then
1633   begin
1634     //We need to be careful with open paths because this may not be a
1635     //true local minima (ie E may be following a skip edge).
1636     //Also, consecutive horz. edges may start heading left before going right.
1637     if NextIsForward then EStart := E.Prev
1638     else EStart := E.Next;
1639     if (EStart.Dx = Horizontal) then //ie an adjoining horizontal skip edge
1640     begin
1641       if (EStart.Bot.X <> E.Bot.X) and (EStart.Top.X <> E.Bot.X) then
1642         ReverseHorizontal(E);
1643     end
1644     else if (EStart.Bot.X <> E.Bot.X) then
1645         ReverseHorizontal(E);
1646   end;
1647 
1648   EStart := E;
1649   if NextIsForward then
1650   begin
1651     while (Result.Top.Y = Result.Next.Bot.Y) and (Result.Next.OutIdx <> Skip) do
1652       Result := Result.Next;
1653     if (Result.Dx = Horizontal) and (Result.Next.OutIdx <> Skip) then
1654     begin
1655       //nb: at the top of a bound, horizontals are added to the bound
1656       //only when the preceding edge attaches to the horizontal's left vertex
1657       //unless a Skip edge is encountered when that becomes the top divide
1658       Horz := Result;
1659       while (Horz.Prev.Dx = Horizontal) do Horz := Horz.Prev;
1660       if (Horz.Prev.Top.X > Result.Next.Top.X) then Result := Horz.Prev;
1661     end;
1662     while (E <> Result) do
1663     begin
1664       e.NextInLML := e.Next;
1665       if (E.Dx = Horizontal) and (e <> EStart) and
1666         (E.Bot.X <> E.Prev.Top.X) then ReverseHorizontal(E);
1667       E := E.Next;
1668     end;
1669     if (e <> EStart) and (E.Dx = Horizontal) and (E.Bot.X <> E.Prev.Top.X) then
1670       ReverseHorizontal(E);
1671     Result := Result.Next; //move to the edge just beyond current bound
1672   end else
1673   begin
1674     while (Result.Top.Y = Result.Prev.Bot.Y) and (Result.Prev.OutIdx <> Skip) do
1675       Result := Result.Prev;
1676     if (Result.Dx = Horizontal) and (Result.Prev.OutIdx <> Skip) then
1677     begin
1678       Horz := Result;
1679       while (Horz.Next.Dx = Horizontal) do Horz := Horz.Next;
1680       if (Horz.Next.Top.X = Result.Prev.Top.X) or
1681         (Horz.Next.Top.X > Result.Prev.Top.X) then Result := Horz.Next;
1682     end;
1683     while (E <> Result) do
1684     begin
1685       e.NextInLML := e.Prev;
1686       if (e.Dx = Horizontal) and (e <> EStart) and
1687         (E.Bot.X <> E.Next.Top.X) then ReverseHorizontal(E);
1688       E := E.Prev;
1689     end;
1690     if (e <> EStart) and (E.Dx = Horizontal) and (E.Bot.X <> E.Next.Top.X) then
1691       ReverseHorizontal(E);
1692     Result := Result.Prev; //move to the edge just beyond current bound
1693   end;
1694 end;
1695 //------------------------------------------------------------------------------
1696 
AddPathnull1697 function TClipperBase.AddPath(const Path: TPath;
1698   PolyType: TPolyType; Closed: Boolean): Boolean;
1699 var
1700   I, HighI: Integer;
1701   Edges: PEdgeArray;
1702   E, E2, EMin, EStart, ELoopStop: PEdge;
1703   IsFlat, leftBoundIsForward: Boolean;
1704   locMin: PLocalMinimum;
1705 begin
1706 {$IFDEF use_lines}
1707   if not Closed and (polyType = ptClip) then
1708     raise exception.Create(rsOpenPath);
1709 {$ELSE}
1710   if not Closed then raise exception.Create(rsOpenPath2);
1711 {$ENDIF}
1712 
1713   Result := false;
1714   IsFlat := true;
1715 
1716   //1. Basic (first) edge initialization ...
1717   HighI := High(Path);
1718   if Closed then
1719     while (HighI > 0) and PointsEqual(Path[HighI],Path[0]) do Dec(HighI);
1720   while (HighI > 0) and PointsEqual(Path[HighI],Path[HighI -1]) do Dec(HighI);
1721   if (Closed and (HighI < 2)) or (not Closed and (HighI < 1)) then Exit;
1722 
1723   GetMem(Edges, sizeof(TEdge)*(HighI +1));
1724   try
1725     FillChar(Edges^, sizeof(TEdge)*(HighI +1), 0);
1726     Edges[1].Curr := Path[1];
1727     RangeTest(Path[0], FUse64BitRange);
1728     RangeTest(Path[HighI], FUse64BitRange);
1729     InitEdge(@Edges[0], @Edges[1], @Edges[HighI], Path[0]);
1730     InitEdge(@Edges[HighI], @Edges[0], @Edges[HighI-1], Path[HighI]);
1731     for I := HighI - 1 downto 1 do
1732     begin
1733       RangeTest(Path[I], FUse64BitRange);
1734       InitEdge(@Edges[I], @Edges[I+1], @Edges[I-1], Path[I]);
1735     end;
1736   except
1737     FreeMem(Edges);
1738     raise; //Range test fails
1739   end;
1740   EStart := @Edges[0];
1741 
1742   //2. Remove duplicate vertices, and (when closed) collinear edges ...
1743   E := EStart;
1744   ELoopStop := EStart;
1745   while (E <> E.Next) do //ie in case loop reduces to a single vertex
1746   begin
1747     //allow matching start and end points when not Closed ...
1748     if PointsEqual(E.Curr, E.Next.Curr) and
1749       (Closed or (E.Next <> EStart)) then
1750     begin
1751       if E = EStart then EStart := E.Next;
1752       E := RemoveEdge(E);
1753       ELoopStop := E;
1754       Continue;
1755     end;
1756     if (E.Prev = E.Next) then
1757       Break //only two vertices
1758     else if Closed and
1759       SlopesEqual(E.Prev.Curr, E.Curr, E.Next.Curr, FUse64BitRange) and
1760       (not FPreserveCollinear or
1761       not Pt2IsBetweenPt1AndPt3(E.Prev.Curr, E.Curr, E.Next.Curr)) then
1762     begin
1763       //Collinear edges are allowed for open paths but in closed paths
1764       //the default is to merge adjacent collinear edges into a single edge.
1765       //However, if the PreserveCollinear property is enabled, only overlapping
1766       //collinear edges (ie spikes) will be removed from closed paths.
1767       if E = EStart then EStart := E.Next;
1768       E := RemoveEdge(E);
1769       E := E.Prev;
1770       ELoopStop := E;
1771       Continue;
1772     end;
1773     E := E.Next;
1774     //todo - manage open paths which start and end at same point
1775     if (E = eLoopStop) then Break;
1776     if E = ELoopStop then Break;
1777   end;
1778 
1779   if (not Closed and (E = E.Next)) or (Closed and (E.Prev = E.Next)) then
1780   begin
1781     FreeMem(Edges);
1782     Exit;
1783   end;
1784 
1785   if not Closed then
1786   begin
1787     FHasOpenPaths := true;
1788     EStart.Prev.OutIdx := Skip;
1789   end;
1790 
1791   //3. Do second stage of edge initialization ...
1792   E := EStart;
1793   repeat
1794     InitEdge2(E, polyType);
1795     E := E.Next;
1796     if IsFlat and (E.Curr.Y <> EStart.Curr.Y) then IsFlat := false;
1797   until E = EStart;
1798   //4. Finally, add edge bounds to LocalMinima list ...
1799 
1800   //Totally flat paths must be handled differently when adding them
1801   //to LocalMinima list to avoid endless loops etc ...
1802   if (IsFlat) then
1803   begin
1804     if Closed then
1805     begin
1806       FreeMem(Edges);
1807       Exit;
1808     end;
1809     new(locMin);
1810     locMin.Y := E.Bot.Y;
1811     locMin.LeftBound := nil;
1812     locMin.RightBound := E;
1813     locMin.RightBound.Side := esRight;
1814     locMin.RightBound.WindDelta := 0;
1815     while true do
1816     begin
1817       if E.Bot.X <> E.Prev.Top.X then ReverseHorizontal(E);
1818       if E.Next.OutIdx = Skip then break;
1819       E.NextInLML := E.Next;
1820       E := E.Next;
1821     end;
1822     FLocMinList.Add(locMin);
1823     Result := true;
1824     FEdgeList.Add(Edges);
1825     Exit;
1826   end;
1827 
1828   Result := true;
1829   FEdgeList.Add(Edges);
1830   EMin := nil;
1831 
1832   //workaround to avoid an endless loop in the while loop below when
1833   //open paths have matching start and end points ...
1834   if PointsEqual(E.Prev.Bot, E.Prev.Top) then E := E.Next;
1835 
1836   while true do
1837   begin
1838     E := FindNextLocMin(E);
1839     if (E = EMin) then break
1840     else if (EMin = nil) then EMin := E;
1841 
1842     //E and E.Prev now share a local minima (left aligned if horizontal).
1843     //Compare their slopes to find which starts which bound ...
1844     new(locMin);
1845     locMin.Y := E.Bot.Y;
1846     if (E.Dx < E.Prev.Dx) then
1847     begin
1848       locMin.LeftBound := E.Prev;
1849       locMin.RightBound := E; //can be horz when CW
1850       leftBoundIsForward := false; //Q.nextInLML = Q.prev
1851     end else
1852     begin
1853       locMin.LeftBound := E;
1854       locMin.RightBound := E.Prev; //can be horz when CCW
1855       leftBoundIsForward := true; //Q.nextInLML = Q.next
1856     end;
1857 
1858     if not Closed then locMin.LeftBound.WindDelta := 0
1859     else if (locMin.LeftBound.Next = locMin.RightBound) then
1860       locMin.LeftBound.WindDelta := -1
1861     else locMin.LeftBound.WindDelta := 1;
1862     locMin.RightBound.WindDelta := -locMin.LeftBound.WindDelta;
1863 
1864     E := ProcessBound(locMin.LeftBound, leftBoundIsForward);
1865     if E.OutIdx = Skip then E := ProcessBound(E, leftBoundIsForward);
1866 
1867     E2 := ProcessBound(locMin.RightBound, not leftBoundIsForward);
1868     if E2.OutIdx = Skip then E2 := ProcessBound(E2, not leftBoundIsForward);
1869 
1870     if (locMin.LeftBound.OutIdx = Skip) then locMin.LeftBound := nil
1871     else if (locMin.RightBound.OutIdx = Skip) then locMin.RightBound := nil;
1872     FLocMinList.Add(locMin);
1873 
1874     if not leftBoundIsForward then E := E2;
1875   end;
1876 end;
1877 //------------------------------------------------------------------------------
1878 
AddPathsnull1879 function TClipperBase.AddPaths(const Paths: TPaths;
1880   PolyType: TPolyType; Closed: Boolean): Boolean;
1881 var
1882   I: Integer;
1883 begin
1884   Result := False;
1885   for I := 0 to high(Paths) do
1886     if AddPath(Paths[I], PolyType, Closed) then Result := True;
1887 end;
1888 //------------------------------------------------------------------------------
1889 
1890 procedure TClipperBase.Clear;
1891 var
1892   I: Integer;
1893 begin
1894   DisposeLocalMinimaList;
1895   //dispose of Edges ...
1896   for I := 0 to FEdgeList.Count -1 do
1897     FreeMem(PEdgeArray(fEdgeList[I]));
1898   FEdgeList.Clear;
1899 
1900   FUse64BitRange := False;
1901   FHasOpenPaths := False;
1902 end;
1903 //------------------------------------------------------------------------------
1904 
1905 procedure TClipperBase.InsertScanbeam(const Y: cInt);
1906 var
1907   newSb, sb: PScanbeam;
1908 begin
1909   //single-linked list: sorted descending, ignoring dups.
1910   new(newSb);
1911   newSb.Y := Y;
1912   if not Assigned(fScanbeam) then
1913   begin
1914     FScanbeam := newSb;
1915     newSb.Next := nil;
1916   end else if Y > FScanbeam.Y then
1917   begin
1918     newSb.Next := FScanbeam;
1919     FScanbeam := newSb;
1920   end else
1921   begin
1922     sb := FScanbeam;
1923     while Assigned(sb.Next) and (Y <= sb.Next.Y) do sb := sb.Next;
1924     if Y <> sb.Y then
1925     begin
1926       newSb.Next := sb.Next;
1927       sb.Next := newSb;
1928     end
1929     else dispose(newSb);
1930   end;
1931 end;
1932 //------------------------------------------------------------------------------
1933 
PopScanbeamnull1934 function TClipperBase.PopScanbeam(out Y: cInt): Boolean;
1935 var
1936   Sb: PScanbeam;
1937 begin
1938   Result := assigned(FScanbeam);
1939   if not result then exit;
1940   Y := FScanbeam.Y;
1941   Sb := FScanbeam;
1942   FScanbeam := FScanbeam.Next;
1943   dispose(Sb);
1944 end;
1945 //------------------------------------------------------------------------------
1946 
TClipperBase.LocalMinimaPendingnull1947 function TClipperBase.LocalMinimaPending: Boolean;
1948 begin
1949   Result := FCurrentLocMinIdx < FLocMinList.Count;
1950 end;
1951 //------------------------------------------------------------------------------
1952 
TClipperBase.PopLocalMinimanull1953 function TClipperBase.PopLocalMinima(Y: cInt;
1954   out LocalMinima: PLocalMinimum): Boolean;
1955 begin
1956   Result := false;
1957   if (FCurrentLocMinIdx = FLocMinList.Count) then Exit;
1958   LocalMinima := PLocalMinimum(FLocMinList[FCurrentLocMinIdx]);
1959   if (LocalMinima.Y = Y) then
1960   begin
1961     inc(FCurrentLocMinIdx);
1962     Result := true;
1963   end;
1964 end;
1965 //------------------------------------------------------------------------------
1966 
1967 procedure TClipperBase.DisposeScanbeamList;
1968 var
1969   SB: PScanbeam;
1970 begin
1971   while Assigned(fScanbeam) do
1972   begin
1973     SB := FScanbeam.Next;
1974     Dispose(fScanbeam);
1975     FScanbeam := SB;
1976   end;
1977 end;
1978 //------------------------------------------------------------------------------
1979 
1980 {$IFNDEF USEGENERICS}
LocMinListSortnull1981 function LocMinListSort(item1, item2:Pointer): Integer;
1982 var
1983   y: cInt;
1984 begin
1985   y := PLocalMinimum(item2).Y - PLocalMinimum(item1).Y;
1986   if y < 0 then result := -1
1987   else if y > 0 then result := 1
1988   else result := 0;
1989 end;
1990 {$ENDIF}
1991 
1992 //------------------------------------------------------------------------------
1993 
1994 procedure TClipperBase.Reset;
1995 var
1996   i: Integer;
1997   Lm: PLocalMinimum;
1998 begin
1999   //Reset() allows various clipping operations to be executed
2000   //multiple times on the same polygon sets.
2001 {$IFDEF USEGENERICS}
2002     FLocMinList.Sort(TComparer<PLocalMinimum>.Construct(
2003       function (const Item1, Item2 : PLocalMinimum) : integer
2004       var
2005         y: cInt;
2006       begin
2007         y := PLocalMinimum(item2).Y - PLocalMinimum(item1).Y;
2008         if y < 0 then result := -1
2009         else if y > 0 then result := 1
2010         else result := 0;
2011       end
2012     ));
2013 {$ELSE}
2014   FLocMinList.Sort(LocMinListSort);
2015 {$ENDIF}
2016   for i := 0 to FLocMinList.Count -1 do
2017   begin
2018     Lm := PLocalMinimum(FLocMinList[i]);
2019     InsertScanbeam(Lm.Y);
2020     //resets just the two (L & R) edges attached to each Local Minima ...
2021     if assigned(Lm.LeftBound) then
2022       with Lm.LeftBound^ do
2023       begin
2024         Curr := Bot;
2025         OutIdx := Unassigned;
2026       end;
2027     if assigned(Lm.RightBound) then
2028       with Lm.RightBound^ do
2029       begin
2030         Curr := Bot;
2031         OutIdx := Unassigned;
2032       end;
2033   end;
2034   FCurrentLocMinIdx := 0;
2035   FActiveEdges := nil;
2036 end;
2037 //------------------------------------------------------------------------------
2038 
2039 procedure TClipperBase.DisposePolyPts(PP: POutPt);
2040 var
2041   TmpPp: POutPt;
2042 begin
2043   PP.Prev.Next := nil;
2044   while Assigned(PP) do
2045   begin
2046     TmpPp := PP;
2047     PP := PP.Next;
2048     dispose(TmpPp);
2049   end;
2050 end;
2051 //------------------------------------------------------------------------------
2052 
2053 procedure TClipperBase.DisposeLocalMinimaList;
2054 var
2055   i: Integer;
2056 begin
2057   for i := 0 to FLocMinList.Count -1 do
2058     Dispose(PLocalMinimum(FLocMinList[i]));
2059   FLocMinList.Clear;
2060   FCurrentLocMinIdx := 0;
2061 end;
2062 //------------------------------------------------------------------------------
2063 
CreateOutRecnull2064 function TClipperBase.CreateOutRec: POutRec;
2065 begin
2066   new(Result);
2067   Result.IsHole := False;
2068   Result.IsOpen := False;
2069   Result.FirstLeft := nil;
2070   Result.Pts := nil;
2071   Result.BottomPt := nil;
2072   Result.PolyNode := nil;
2073   Result.Idx := FPolyOutList.Add(Result);
2074 end;
2075 //------------------------------------------------------------------------------
2076 
2077 procedure TClipperBase.DisposeOutRec(Index: Integer);
2078 var
2079   OutRec: POutRec;
2080 begin
2081   OutRec := FPolyOutList[Index];
2082   if Assigned(OutRec.Pts) then DisposePolyPts(OutRec.Pts);
2083   Dispose(OutRec);
2084   FPolyOutList[Index] := nil;
2085 end;
2086 //------------------------------------------------------------------------------
2087 
2088 procedure TClipperBase.DisposeAllOutRecs;
2089 var
2090   I: Integer;
2091 begin
2092   for I := 0 to FPolyOutList.Count -1 do DisposeOutRec(I);
2093   FPolyOutList.Clear;
2094 end;
2095 //------------------------------------------------------------------------------
2096 
2097 procedure TClipperBase.UpdateEdgeIntoAEL(var E: PEdge);
2098 var
2099   AelPrev, AelNext: PEdge;
2100 begin
2101   //return true when AddOutPt() call needed too
2102   if not Assigned(E.NextInLML) then
2103     raise exception.Create(rsUpdateEdgeIntoAEL);
2104 
2105   E.NextInLML.OutIdx := E.OutIdx;
2106 
2107   AelPrev := E.PrevInAEL;
2108   AelNext := E.NextInAEL;
2109   if Assigned(AelPrev) then
2110     AelPrev.NextInAEL := E.NextInLML else
2111     FActiveEdges := E.NextInLML;
2112   if Assigned(AelNext) then
2113     AelNext.PrevInAEL := E.NextInLML;
2114   E.NextInLML.Side := E.Side;
2115   E.NextInLML.WindDelta := E.WindDelta;
2116   E.NextInLML.WindCnt := E.WindCnt;
2117   E.NextInLML.WindCnt2 := E.WindCnt2;
2118   E := E.NextInLML; ////
2119   E.Curr := E.Bot;
2120   E.PrevInAEL := AelPrev;
2121   E.NextInAEL := AelNext;
2122   if E.Dx <> Horizontal then
2123     InsertScanbeam(E.Top.Y);
2124 end;
2125 //------------------------------------------------------------------------------
2126 
2127 procedure TClipperBase.SwapPositionsInAEL(E1, E2: PEdge);
2128 var
2129   Prev,Next: PEdge;
2130 begin
2131   //check that one or other edge hasn't already been removed from AEL ...
2132   if (E1.NextInAEL = E1.PrevInAEL) or (E2.NextInAEL = E2.PrevInAEL) then
2133     Exit;
2134 
2135   if E1.NextInAEL = E2 then
2136   begin
2137     Next := E2.NextInAEL;
2138     if Assigned(Next) then Next.PrevInAEL := E1;
2139     Prev := E1.PrevInAEL;
2140     if Assigned(Prev) then Prev.NextInAEL := E2;
2141     E2.PrevInAEL := Prev;
2142     E2.NextInAEL := E1;
2143     E1.PrevInAEL := E2;
2144     E1.NextInAEL := Next;
2145   end
2146   else if E2.NextInAEL = E1 then
2147   begin
2148     Next := E1.NextInAEL;
2149     if Assigned(Next) then Next.PrevInAEL := E2;
2150     Prev := E2.PrevInAEL;
2151     if Assigned(Prev) then Prev.NextInAEL := E1;
2152     E1.PrevInAEL := Prev;
2153     E1.NextInAEL := E2;
2154     E2.PrevInAEL := E1;
2155     E2.NextInAEL := Next;
2156   end else
2157   begin
2158     Next := E1.NextInAEL;
2159     Prev := E1.PrevInAEL;
2160     E1.NextInAEL := E2.NextInAEL;
2161     if Assigned(E1.NextInAEL) then E1.NextInAEL.PrevInAEL := E1;
2162     E1.PrevInAEL := E2.PrevInAEL;
2163     if Assigned(E1.PrevInAEL) then E1.PrevInAEL.NextInAEL := E1;
2164     E2.NextInAEL := Next;
2165     if Assigned(E2.NextInAEL) then E2.NextInAEL.PrevInAEL := E2;
2166     E2.PrevInAEL := Prev;
2167     if Assigned(E2.PrevInAEL) then E2.PrevInAEL.NextInAEL := E2;
2168   end;
2169   if not Assigned(E1.PrevInAEL) then FActiveEdges := E1
2170   else if not Assigned(E2.PrevInAEL) then FActiveEdges := E2;
2171 end;
2172 //------------------------------------------------------------------------------
2173 
2174 procedure TClipperBase.DeleteFromAEL(E: PEdge);
2175 var
2176   AelPrev, AelNext: PEdge;
2177 begin
2178   AelPrev := E.PrevInAEL;
2179   AelNext := E.NextInAEL;
2180   if not Assigned(AelPrev) and not Assigned(AelNext) and
2181     (E <> FActiveEdges) then Exit; //already deleted
2182   if Assigned(AelPrev) then AelPrev.NextInAEL := AelNext
2183   else FActiveEdges := AelNext;
2184   if Assigned(AelNext) then AelNext.PrevInAEL := AelPrev;
2185   E.NextInAEL := nil;
2186   E.PrevInAEL := nil;
2187 end;
2188 
2189 //------------------------------------------------------------------------------
2190 // TClipper methods ...
2191 //------------------------------------------------------------------------------
2192 
2193 constructor TClipper.Create(InitOptions: TInitOptions = []);
2194 begin
2195   inherited Create;
2196   FJoinList := TJoinList.Create;
2197   FGhostJoinList := TJoinList.Create;
2198   FIntersectList := TIntersecList.Create;
2199   if ioReverseSolution in InitOptions then
2200     FReverseOutput := true;
2201   if ioStrictlySimple in InitOptions then
2202     FStrictSimple := true;
2203   if ioPreserveCollinear in InitOptions then
2204     FPreserveCollinear := true;
2205 end;
2206 //------------------------------------------------------------------------------
2207 
2208 destructor TClipper.Destroy;
2209 begin
2210   inherited; //this must be first since inherited Destroy calls Clear.
2211   FJoinList.Free;
2212   FGhostJoinList.Free;
2213   FIntersectList.Free;
2214 end;
2215 //------------------------------------------------------------------------------
2216 
Executenull2217 function TClipper.Execute(clipType: TClipType;
2218   out solution: TPaths;
2219   FillType: TPolyFillType = pftEvenOdd): Boolean;
2220 begin
2221   Result := Execute(clipType, solution, FillType, FillType);
2222 end;
2223 //------------------------------------------------------------------------------
2224 
Executenull2225 function TClipper.Execute(clipType: TClipType;
2226   out solution: TPaths;
2227   subjFillType: TPolyFillType; clipFillType: TPolyFillType): Boolean;
2228 begin
2229   Result := False;
2230   solution := nil;
2231   if FExecuteLocked then Exit;
2232   //nb: Open paths can only be returned via the PolyTree structure ...
2233   if HasOpenPaths then raise Exception.Create(rsOpenPath3);
2234   try try
2235     FExecuteLocked := True;
2236     FSubjFillType := subjFillType;
2237     FClipFillType := clipFillType;
2238     FClipType := clipType;
2239     FUsingPolyTree := False;
2240     Result := ExecuteInternal;
2241     if Result then
2242       solution := BuildResult;
2243   except
2244     solution := nil;
2245     Result := False;
2246   end;
2247   finally
2248     DisposeAllOutRecs;
2249     FExecuteLocked := False;
2250   end;
2251 end;
2252 //------------------------------------------------------------------------------
2253 
Executenull2254 function TClipper.Execute(clipType: TClipType;
2255   out PolyTree: TPolyTree;
2256   FillType: TPolyFillType = pftEvenOdd): Boolean;
2257 begin
2258   Result := Execute(clipType, PolyTree, FillType, FillType);
2259 end;
2260 //------------------------------------------------------------------------------
2261 
Executenull2262 function TClipper.Execute(clipType: TClipType;
2263   out PolyTree: TPolyTree;
2264   subjFillType: TPolyFillType;
2265   clipFillType: TPolyFillType): Boolean;
2266 begin
2267   Result := False;
2268   if FExecuteLocked or not Assigned(PolyTree) then Exit;
2269   try try
2270     FExecuteLocked := True;
2271     FSubjFillType := subjFillType;
2272     FClipFillType := clipFillType;
2273     FClipType := clipType;
2274     FUsingPolyTree := True;
2275     Result := ExecuteInternal and BuildResult2(PolyTree);
2276   except
2277     Result := False;
2278   end;
2279   finally
2280     DisposeAllOutRecs;
2281     FExecuteLocked := False;
2282   end;
2283 end;
2284 //------------------------------------------------------------------------------
2285 
2286 procedure TClipper.FixHoleLinkage(OutRec: POutRec);
2287 var
2288   orfl: POutRec;
2289 begin
2290   //skip if it's an outermost polygon or if FirstLeft
2291   //already points to the outer/owner polygon ...
2292   if not Assigned(OutRec.FirstLeft) or
2293     ((OutRec.IsHole <> OutRec.FirstLeft.IsHole) and
2294       Assigned(OutRec.FirstLeft.Pts)) then Exit;
2295   orfl := OutRec.FirstLeft;
2296   while Assigned(orfl) and
2297     ((orfl.IsHole = OutRec.IsHole) or not Assigned(orfl.Pts)) do
2298       orfl := orfl.FirstLeft;
2299   OutRec.FirstLeft := orfl;
2300 end;
2301 //------------------------------------------------------------------------------
2302 
TClipper.ExecuteInternalnull2303 function TClipper.ExecuteInternal: Boolean;
2304 var
2305   I: Integer;
2306   OutRec: POutRec;
2307   BotY, TopY: cInt;
2308 begin
2309   try
2310     Reset;
2311     FSortedEdges := nil;
2312     Result := false;
2313     if not PopScanbeam(BotY) then Exit;
2314     InsertLocalMinimaIntoAEL(BotY);
2315     while PopScanbeam(TopY) or LocalMinimaPending do
2316     begin
2317       ProcessHorizontals;
2318       ClearGhostJoins;
2319       if not ProcessIntersections(TopY) then Exit;
2320       ProcessEdgesAtTopOfScanbeam(TopY);
2321       BotY := TopY;
2322       InsertLocalMinimaIntoAEL(BotY);
2323     end;
2324 
2325     //fix orientations ...
2326     for I := 0 to FPolyOutList.Count -1 do
2327     begin
2328       OutRec := FPolyOutList[I];
2329       if Assigned(OutRec.Pts) and not OutRec.IsOpen and
2330         ((OutRec.IsHole xor FReverseOutput) = (Area(OutRec) > 0)) then
2331           ReversePolyPtLinks(OutRec.Pts);
2332     end;
2333 
2334     if FJoinList.count > 0 then JoinCommonEdges;
2335 
2336     //unfortunately FixupOutPolygon() must be done after JoinCommonEdges ...
2337     for I := 0 to FPolyOutList.Count -1 do
2338     begin
2339       OutRec := FPolyOutList[I];
2340       if not Assigned(OutRec.Pts) then continue;
2341       if OutRec.IsOpen then
2342         FixupOutPolyline(OutRec)
2343       else
2344         FixupOutPolygon(OutRec);
2345     end;
2346 
2347     if FStrictSimple then DoSimplePolygons;
2348     Result := True;
2349   finally
2350     ClearJoins;
2351     ClearGhostJoins;
2352   end;
2353 end;
2354 //------------------------------------------------------------------------------
2355 
2356 procedure TClipper.InsertMaxima(const X: cInt);
2357 var
2358   newMax, m: PMaxima;
2359 begin
2360   //double-linked list: sorted ascending, ignoring dups.
2361   new(newMax);
2362   newMax.X := X;
2363   if not Assigned(FMaxima) then
2364   begin
2365     FMaxima := newMax;
2366     newMax.Next := nil;
2367     newMax.Prev := nil;
2368   end else if X < FMaxima.X then
2369   begin
2370     newMax.Next := FMaxima;
2371     newMax.Prev := nil;
2372     FMaxima.Prev := newMax;
2373     FMaxima := newMax;
2374   end else
2375   begin
2376     m := FMaxima;
2377     while Assigned(m.Next) and (X >= m.Next.X) do m := m.Next;
2378     if X <> m.X then
2379     begin
2380       //insert m1 between m2 and m2.Next ...
2381       newMax.Next := m.Next;
2382       newMax.Prev := m;
2383       if assigned(m.Next) then m.Next.Prev := newMax;
2384       m.Next := newMax;
2385     end
2386     else dispose(newMax);
2387   end;
2388 end;
2389 //------------------------------------------------------------------------------
2390 
2391 procedure TClipper.DisposeMaximaList;
2392 var
2393   m: PMaxima;
2394 begin
2395   while Assigned(FMaxima) do
2396   begin
2397     m := FMaxima.Next;
2398     Dispose(FMaxima);
2399     FMaxima := m;
2400   end;
2401 end;
2402 //------------------------------------------------------------------------------
2403 
2404 procedure TClipper.SetWindingCount(Edge: PEdge);
2405 var
2406   E, E2: PEdge;
2407   Inside: Boolean;
2408   pft: TPolyFillType;
2409 begin
2410   E := Edge.PrevInAEL;
2411   //find the Edge of the same PolyType that immediately preceeds 'Edge' in AEL
2412   while Assigned(E) and ((E.PolyType <> Edge.PolyType) or (E.WindDelta = 0)) do
2413     E := E.PrevInAEL;
2414   if not Assigned(E) then
2415   begin
2416     if Edge.WindDelta = 0 then
2417     begin
2418       if edge.PolyType = ptSubject then
2419         pft := FSubjFillType else
2420         pft :=  FClipFillType;
2421       if pft = pftNegative then
2422         Edge.WindCnt := -1 else
2423         Edge.WindCnt := 1;
2424     end else Edge.WindCnt := Edge.WindDelta;
2425     Edge.WindCnt2 := 0;
2426     E := FActiveEdges; //ie get ready to calc WindCnt2
2427   end
2428   else if (Edge.WindDelta = 0) and (FClipType <> ctUnion) then
2429   begin
2430     Edge.WindCnt := 1;
2431     Edge.WindCnt2 := E.WindCnt2;
2432     E := E.NextInAEL; //ie get ready to calc WindCnt2
2433   end
2434   else if IsEvenOddFillType(Edge) then
2435   begin
2436     //even-odd filling ...
2437     if (Edge.WindDelta = 0) then  //if edge is part of a line
2438     begin
2439       //are we inside a subj polygon ...
2440       Inside := true;
2441       E2 := E.PrevInAEL;
2442       while assigned(E2) do
2443       begin
2444         if (E2.PolyType = E.PolyType) and (E2.WindDelta <> 0) then
2445           Inside := not Inside;
2446         E2 := E2.PrevInAEL;
2447       end;
2448       if Inside then Edge.WindCnt := 0
2449       else Edge.WindCnt := 1;
2450     end
2451     else //else a polygon
2452     begin
2453       Edge.WindCnt := Edge.WindDelta;
2454     end;
2455     Edge.WindCnt2 := E.WindCnt2;
2456     E := E.NextInAEL; //ie get ready to calc WindCnt2
2457   end else
2458   begin
2459     //NonZero, Positive, or Negative filling ...
2460     if (E.WindCnt * E.WindDelta < 0) then
2461     begin
2462       //prev edge is 'decreasing' WindCount (WC) toward zero
2463       //so we're outside the previous polygon ...
2464       if (Abs(E.WindCnt) > 1) then
2465       begin
2466         //outside prev poly but still inside another.
2467         //when reversing direction of prev poly use the same WC
2468         if (E.WindDelta * Edge.WindDelta < 0) then
2469           Edge.WindCnt := E.WindCnt
2470         //otherwise continue to 'decrease' WC ...
2471         else Edge.WindCnt := E.WindCnt + Edge.WindDelta;
2472       end
2473       else
2474         //now outside all polys of same polytype so set own WC ...
2475         if Edge.WindDelta = 0 then Edge.WindCnt := 1
2476         else Edge.WindCnt := Edge.WindDelta;
2477     end else
2478     begin
2479       //prev edge is 'increasing' WindCount (WC) away from zero
2480       //so we're inside the previous polygon ...
2481       if (Edge.WindDelta = 0) then
2482       begin
2483         if (E.WindCnt < 0) then Edge.WindCnt := E.WindCnt -1
2484         else Edge.WindCnt := E.WindCnt +1;
2485       end
2486       //if wind direction is reversing prev then use same WC
2487       else if (E.WindDelta * Edge.WindDelta < 0) then
2488         Edge.WindCnt := E.WindCnt
2489       //otherwise add to WC ...
2490       else Edge.WindCnt := E.WindCnt + Edge.WindDelta;
2491     end;
2492     Edge.WindCnt2 := E.WindCnt2;
2493     E := E.NextInAEL; //ie get ready to calc WindCnt2
2494   end;
2495 
2496   //update WindCnt2 ...
2497   if IsEvenOddAltFillType(Edge) then
2498   begin
2499     //even-odd filling ...
2500     while (E <> Edge) do
2501     begin
2502       if E.WindDelta = 0 then //do nothing (ie ignore lines)
2503       else if Edge.WindCnt2 = 0 then Edge.WindCnt2 := 1
2504       else Edge.WindCnt2 := 0;
2505       E := E.NextInAEL;
2506     end;
2507   end else
2508   begin
2509     //NonZero, Positive, or Negative filling ...
2510     while (E <> Edge) do
2511     begin
2512       Inc(Edge.WindCnt2, E.WindDelta);
2513       E := E.NextInAEL;
2514     end;
2515   end;
2516 end;
2517 //------------------------------------------------------------------------------
2518 
TClipper.IsEvenOddFillTypenull2519 function TClipper.IsEvenOddFillType(Edge: PEdge): Boolean;
2520 begin
2521   if Edge.PolyType = ptSubject then
2522     Result := FSubjFillType = pftEvenOdd else
2523     Result := FClipFillType = pftEvenOdd;
2524 end;
2525 //------------------------------------------------------------------------------
2526 
IsEvenOddAltFillTypenull2527 function TClipper.IsEvenOddAltFillType(Edge: PEdge): Boolean;
2528 begin
2529   if Edge.PolyType = ptSubject then
2530     Result := FClipFillType = pftEvenOdd else
2531     Result := FSubjFillType = pftEvenOdd;
2532 end;
2533 //------------------------------------------------------------------------------
2534 
TClipper.IsContributingnull2535 function TClipper.IsContributing(Edge: PEdge): Boolean;
2536 var
2537   Pft, Pft2: TPolyFillType;
2538 begin
2539   if Edge.PolyType = ptSubject then
2540   begin
2541     Pft := FSubjFillType;
2542     Pft2 := FClipFillType;
2543   end else
2544   begin
2545     Pft := FClipFillType;
2546     Pft2 := FSubjFillType
2547   end;
2548 
2549   case Pft of
2550     pftEvenOdd: Result := (Edge.WindDelta <> 0) or (Edge.WindCnt = 1);
2551     pftNonZero: Result := abs(Edge.WindCnt) = 1;
2552     pftPositive: Result := (Edge.WindCnt = 1);
2553     else Result := (Edge.WindCnt = -1);
2554   end;
2555   if not Result then Exit;
2556 
2557   case FClipType of
2558     ctIntersection:
2559       case Pft2 of
2560         pftEvenOdd, pftNonZero: Result := (Edge.WindCnt2 <> 0);
2561         pftPositive: Result := (Edge.WindCnt2 > 0);
2562         pftNegative: Result := (Edge.WindCnt2 < 0);
2563       end;
2564     ctUnion:
2565       case Pft2 of
2566         pftEvenOdd, pftNonZero: Result := (Edge.WindCnt2 = 0);
2567         pftPositive: Result := (Edge.WindCnt2 <= 0);
2568         pftNegative: Result := (Edge.WindCnt2 >= 0);
2569       end;
2570     ctDifference:
2571       if Edge.PolyType = ptSubject then
2572         case Pft2 of
2573           pftEvenOdd, pftNonZero: Result := (Edge.WindCnt2 = 0);
2574           pftPositive: Result := (Edge.WindCnt2 <= 0);
2575           pftNegative: Result := (Edge.WindCnt2 >= 0);
2576         end
2577       else
2578         case Pft2 of
2579           pftEvenOdd, pftNonZero: Result := (Edge.WindCnt2 <> 0);
2580           pftPositive: Result := (Edge.WindCnt2 > 0);
2581           pftNegative: Result := (Edge.WindCnt2 < 0);
2582         end;
2583       ctXor:
2584         if Edge.WindDelta = 0 then //XOr always contributing unless open
2585           case Pft2 of
2586             pftEvenOdd, pftNonZero: Result := (Edge.WindCnt2 = 0);
2587             pftPositive: Result := (Edge.WindCnt2 <= 0);
2588             pftNegative: Result := (Edge.WindCnt2 >= 0);
2589           end;
2590   end;
2591 end;
2592 //------------------------------------------------------------------------------
2593 
AddLocalMinPolynull2594 function TClipper.AddLocalMinPoly(E1, E2: PEdge; const Pt: TIntPoint): POutPt;
2595 var
2596   E, prevE: PEdge;
2597   OutPt: POutPt;
2598   X1, X2: cInt;
2599 begin
2600   if (E2.Dx = Horizontal) or (E1.Dx > E2.Dx) then
2601   begin
2602     Result := AddOutPt(E1, Pt);
2603     E2.OutIdx := E1.OutIdx;
2604     E1.Side := esLeft;
2605     E2.Side := esRight;
2606     E := E1;
2607     if E.PrevInAEL = E2 then
2608       prevE := E2.PrevInAEL
2609     else
2610       prevE := E.PrevInAEL;
2611   end else
2612   begin
2613     Result := AddOutPt(E2, Pt);
2614     E1.OutIdx := E2.OutIdx;
2615     E1.Side := esRight;
2616     E2.Side := esLeft;
2617 
2618     E := E2;
2619     if E.PrevInAEL = E1 then
2620       prevE := E1.PrevInAEL
2621     else
2622       prevE := E.PrevInAEL;
2623   end;
2624 
2625   if Assigned(prevE) and (prevE.OutIdx >= 0) and
2626     (prevE.Top.Y < Pt.Y) and (E.Top.Y < Pt.Y) then
2627   begin
2628     X1 := TopX(prevE, Pt.Y);
2629     X2 := TopX(E, Pt.Y);
2630     if (X1 = X2) and
2631       SlopesEqual(IntPoint(X1, Pt.Y), prevE.Top, IntPoint(X2, Pt.Y), E.Top,
2632         FUse64BitRange) and (E.WindDelta <> 0) and (prevE.WindDelta <> 0) then
2633     begin
2634       OutPt := AddOutPt(prevE, Pt);
2635       AddJoin(Result, OutPt, E.Top);
2636     end;
2637   end;
2638 end;
2639 //------------------------------------------------------------------------------
2640 
2641 procedure TClipper.AddLocalMaxPoly(E1, E2: PEdge; const Pt: TIntPoint);
2642 begin
2643   AddOutPt(E1, Pt);
2644   if E2.WindDelta = 0 then AddOutPt(E2, Pt);
2645   if (E1.OutIdx = E2.OutIdx) then
2646   begin
2647     E1.OutIdx := Unassigned;
2648     E2.OutIdx := Unassigned;
2649   end
2650   else if E1.OutIdx < E2.OutIdx then
2651     AppendPolygon(E1, E2)
2652   else
2653     AppendPolygon(E2, E1);
2654   end;
2655 //------------------------------------------------------------------------------
2656 
2657 procedure TClipper.AddEdgeToSEL(Edge: PEdge);
2658 begin
2659   //SEL pointers in PEdge are use to build transient lists of horizontal edges.
2660   //However, since we don't need to worry about processing order, all additions
2661   //are made to the front of the list ...
2662   if not Assigned(FSortedEdges) then
2663   begin
2664     FSortedEdges := Edge;
2665     Edge.PrevInSEL := nil;
2666     Edge.NextInSEL := nil;
2667   end else
2668   begin
2669     Edge.NextInSEL := FSortedEdges;
2670     Edge.PrevInSEL := nil;
2671     FSortedEdges.PrevInSEL := Edge;
2672     FSortedEdges := Edge;
2673   end;
2674 end;
2675 //------------------------------------------------------------------------------
2676 
PopEdgeFromSELnull2677 function TClipper.PopEdgeFromSEL(out E: PEdge): Boolean;
2678 begin
2679   //Pop edge from front of SEL (ie SEL is a FILO list)
2680   E := FSortedEdges;
2681   Result := assigned(E);
2682   if not Result then Exit;
2683   FSortedEdges := E.NextInSEL;
2684   if Assigned(FSortedEdges) then FSortedEdges.PrevInSEL := nil;
2685   E.NextInSEL := nil;
2686   E.PrevInSEL := nil;
2687 end;
2688 //------------------------------------------------------------------------------
2689 
2690 procedure TClipper.CopyAELToSEL;
2691 var
2692   E: PEdge;
2693 begin
2694   E := FActiveEdges;
2695   FSortedEdges := E;
2696   while Assigned(E) do
2697   begin
2698     E.PrevInSEL := E.PrevInAEL;
2699     E.NextInSEL := E.NextInAEL;
2700     E := E.NextInAEL;
2701   end;
2702 end;
2703 //------------------------------------------------------------------------------
2704 
AddOutPtnull2705 function TClipper.AddOutPt(E: PEdge; const Pt: TIntPoint): POutPt;
2706 var
2707   OutRec: POutRec;
2708   PrevOp, Op: POutPt;
2709   ToFront: Boolean;
2710 begin
2711   if E.OutIdx < 0 then
2712   begin
2713     OutRec := CreateOutRec;
2714     OutRec.IsOpen := (E.WindDelta = 0);
2715     new(Result);
2716     OutRec.Pts := Result;
2717     Result.Pt := Pt;
2718     Result.Next := Result;
2719     Result.Prev := Result;
2720     Result.Idx := OutRec.Idx;
2721     if not OutRec.IsOpen then
2722       SetHoleState(E, OutRec);
2723     E.OutIdx := OutRec.Idx;
2724   end else
2725   begin
2726     ToFront := E.Side = esLeft;
2727     OutRec := FPolyOutList[E.OutIdx];
2728     //OutRec.Pts is the 'left-most' point & OutRec.Pts.Prev is the 'right-most'
2729     Op := OutRec.Pts;
2730     if ToFront then PrevOp := Op else PrevOp := Op.Prev;
2731     if PointsEqual(Pt, PrevOp.Pt) then
2732     begin
2733       Result := PrevOp;
2734       Exit;
2735     end;
2736     new(Result);
2737     Result.Pt := Pt;
2738     Result.Idx := OutRec.Idx;
2739     Result.Next := Op;
2740     Result.Prev := Op.Prev;
2741     Op.Prev.Next := Result;
2742     Op.Prev := Result;
2743     if ToFront then OutRec.Pts := Result;
2744   end;
2745 end;
2746 //------------------------------------------------------------------------------
2747 
2748 procedure TClipper.AddJoin(Op1, Op2: POutPt; const OffPt: TIntPoint);
2749 var
2750   Jr: PJoin;
2751 begin
2752   new(Jr);
2753   Jr.OutPt1 := Op1;
2754   Jr.OutPt2 := Op2;
2755   Jr.OffPt := OffPt;
2756   FJoinList.add(Jr);
2757 end;
2758 //------------------------------------------------------------------------------
2759 
2760 procedure TClipper.ClearJoins;
2761 var
2762   I: Integer;
2763 begin
2764   for I := 0 to FJoinList.count -1 do
2765     Dispose(PJoin(fJoinList[I]));
2766   FJoinList.Clear;
2767 end;
2768 //------------------------------------------------------------------------------
2769 
2770 procedure TClipper.AddGhostJoin(OutPt: POutPt; const OffPt: TIntPoint);
2771 var
2772   Jr: PJoin;
2773 begin
2774   //Ghost joins are used to find horizontal edges at the top of one scanbeam
2775   //that coincide with horizontal edges at the bottom of the next. Ghost joins
2776   //are converted to real joins when match ups occur.
2777   new(Jr);
2778   Jr.OutPt1 := OutPt;
2779   Jr.OffPt := OffPt;
2780   FGhostJoinList.Add(Jr);
2781 end;
2782 //------------------------------------------------------------------------------
2783 
2784 procedure TClipper.ClearGhostJoins;
2785 var
2786   I: Integer;
2787 begin
2788   for I := 0 to FGhostJoinList.Count -1 do
2789     Dispose(PJoin(FGhostJoinList[I]));
2790   FGhostJoinList.Clear;
2791 end;
2792 //------------------------------------------------------------------------------
2793 
2794 procedure SwapPoints(var Pt1, Pt2: TIntPoint);
2795 var
2796   Tmp: TIntPoint;
2797 begin
2798   Tmp := Pt1;
2799   Pt1 := Pt2;
2800   Pt2 := Tmp;
2801 end;
2802 //------------------------------------------------------------------------------
2803 
2804 function HorzSegmentsOverlap(seg1a, seg1b, seg2a, seg2b: cInt): Boolean;
2805 begin
2806   if (seg1a > seg1b) then Swap(seg1a, seg1b);
2807   if (seg2a > seg2b) then Swap(seg2a, seg2b);
2808   Result := (seg1a < seg2b) and (seg2a < seg1b);
2809 end;
2810 //------------------------------------------------------------------------------
2811 
2812 function E2InsertsBeforeE1(E1, E2: PEdge): Boolean;
2813   {$IFDEF INLINING} inline; {$ENDIF}
2814 begin
2815   if E2.Curr.X = E1.Curr.X then
2816   begin
2817     //nb: E1.Top.Y == E2.Bot.Y only occurs when an earlier Rb is horizontal
2818     if E2.Top.Y > E1.Top.Y then
2819       Result := E2.Top.X < TopX(E1, E2.Top.Y) else
2820       Result := E1.Top.X > TopX(E2, E1.Top.Y);
2821   end else
2822     Result := E2.Curr.X < E1.Curr.X;
2823 end;
2824 //----------------------------------------------------------------------
2825 
2826 procedure TClipper.InsertLocalMinimaIntoAEL(const BotY: cInt);
2827 
2828   procedure InsertEdgeIntoAEL(Edge, StartEdge: PEdge);
2829   begin
2830     if not Assigned(FActiveEdges) then
2831     begin
2832       Edge.PrevInAEL := nil;
2833       Edge.NextInAEL := nil;
2834       FActiveEdges := Edge;
2835     end
2836     else if not Assigned(StartEdge) and
2837       E2InsertsBeforeE1(FActiveEdges, Edge) then
2838     begin
2839       Edge.PrevInAEL := nil;
2840       Edge.NextInAEL := FActiveEdges;
2841       FActiveEdges.PrevInAEL := Edge;
2842       FActiveEdges := Edge;
2843     end else
2844     begin
2845       if not Assigned(StartEdge) then StartEdge := FActiveEdges;
2846       while Assigned(StartEdge.NextInAEL) and
2847         not E2InsertsBeforeE1(StartEdge.NextInAEL, Edge) do
2848           StartEdge := StartEdge.NextInAEL;
2849       Edge.NextInAEL := StartEdge.NextInAEL;
2850       if Assigned(StartEdge.NextInAEL) then
2851         StartEdge.NextInAEL.PrevInAEL := Edge;
2852       Edge.PrevInAEL := StartEdge;
2853       StartEdge.NextInAEL := Edge;
2854     end;
2855   end;
2856   //----------------------------------------------------------------------
2857 
2858 var
2859   I: Integer;
2860   E: PEdge;
2861   Lb, Rb: PEdge;
2862   Jr: PJoin;
2863   Op1, Op2: POutPt;
2864   LocMin: PLocalMinimum;
2865 begin
2866   //Add any local minima at BotY ...
2867   while PopLocalMinima(BotY, LocMin) do
2868   begin
2869     Lb := LocMin.LeftBound;
2870     Rb := LocMin.RightBound;
2871     Op1 := nil;
2872     if not assigned(Lb) then
2873     begin
2874       InsertEdgeIntoAEL(Rb, nil);
2875       SetWindingCount(Rb);
2876       if IsContributing(Rb) then
2877         Op1 := AddOutPt(Rb, Rb.Bot);
2878     end
2879     else if not assigned(Rb) then
2880     begin
2881       InsertEdgeIntoAEL(Lb, nil);
2882       SetWindingCount(Lb);
2883       if IsContributing(Lb) then
2884         Op1 := AddOutPt(Lb, Lb.Bot);
2885       InsertScanbeam(Lb.Top.Y);
2886     end else
2887     begin
2888       InsertEdgeIntoAEL(Lb, nil);
2889       InsertEdgeIntoAEL(Rb, Lb);
2890       SetWindingCount(Lb);
2891       Rb.WindCnt := Lb.WindCnt;
2892       Rb.WindCnt2 := Lb.WindCnt2;
2893       if IsContributing(Lb) then
2894         Op1 := AddLocalMinPoly(Lb, Rb, Lb.Bot);
2895       InsertScanbeam(Lb.Top.Y);
2896     end;
2897 
2898     if Assigned(Rb) then
2899     begin
2900       if (Rb.Dx = Horizontal) then
2901       begin
2902         AddEdgeToSEL(Rb);
2903         if assigned(Rb.NextInLML) then
2904           InsertScanbeam(Rb.NextInLML.Top.Y);
2905       end else
2906         InsertScanbeam(Rb.Top.Y);
2907     end;
2908 
2909     if not assigned(Lb) or not assigned(Rb) then Continue;
2910 
2911     //if output polygons share an Edge with rb, they'll need joining later ...
2912     if assigned(Op1) and (Rb.Dx = Horizontal) and
2913       (FGhostJoinList.Count > 0) and (Rb.WindDelta <> 0) then
2914     begin
2915       for I := 0 to FGhostJoinList.Count -1 do
2916       begin
2917         //if the horizontal Rb and a 'ghost' horizontal overlap, then convert
2918         //the 'ghost' join to a real join ready for later ...
2919         Jr := PJoin(FGhostJoinList[I]);
2920         if HorzSegmentsOverlap(Jr.OutPt1.Pt.X, Jr.OffPt.X,
2921           Rb.Bot.X, Rb.Top.X) then
2922             AddJoin(Jr.OutPt1, Op1, Jr.OffPt);
2923       end;
2924     end;
2925 
2926     if (Lb.OutIdx >= 0) and assigned(Lb.PrevInAEL) and
2927       (Lb.PrevInAEL.Curr.X = Lb.Bot.X) and
2928       (Lb.PrevInAEL.OutIdx >= 0) and
2929       SlopesEqual(Lb.PrevInAEL.Curr, Lb.PrevInAEL.Top,
2930       Lb.Curr, Lb.Top, FUse64BitRange) and
2931       (Lb.WindDelta <> 0) and (Lb.PrevInAEL.WindDelta <> 0) then
2932     begin
2933         Op2 := AddOutPt(Lb.PrevInAEL, Lb.Bot);
2934         AddJoin(Op1, Op2, Lb.Top);
2935     end;
2936 
2937     if (Lb.NextInAEL <> Rb) then
2938     begin
2939       if (Rb.OutIdx >= 0) and (Rb.PrevInAEL.OutIdx >= 0) and
2940         SlopesEqual(Rb.PrevInAEL.Curr, Rb.PrevInAEL.Top,
2941         Rb.Curr, Rb.Top, FUse64BitRange) and
2942         (Rb.WindDelta <> 0) and (Rb.PrevInAEL.WindDelta <> 0) then
2943       begin
2944           Op2 := AddOutPt(Rb.PrevInAEL, Rb.Bot);
2945           AddJoin(Op1, Op2, Rb.Top);
2946       end;
2947 
2948       E := Lb.NextInAEL;
2949       if Assigned(E) then
2950         while (E <> Rb) do
2951         begin
2952           //nb: For calculating winding counts etc, IntersectEdges() assumes
2953           //that param1 will be to the right of param2 ABOVE the intersection ...
2954           IntersectEdges(Rb, E, Lb.Curr);
2955           E := E.NextInAEL;
2956         end;
2957     end;
2958   end;
2959 end;
2960 //------------------------------------------------------------------------------
2961 
2962 procedure TClipper.IntersectEdges(E1,E2: PEdge; Pt: TIntPoint);
2963 var
2964   E1Contributing, E2contributing: Boolean;
2965   E1FillType, E2FillType, E1FillType2, E2FillType2: TPolyFillType;
2966   E1Wc, E2Wc, E1Wc2, E2Wc2: Integer;
2967 begin
2968   {IntersectEdges}
2969   //E1 will be to the left of E2 BELOW the intersection. Therefore E1 is before
2970   //E2 in AEL except when E1 is being inserted at the intersection point ...
2971 
2972   E1Contributing := (E1.OutIdx >= 0);
2973   E2contributing := (E2.OutIdx >= 0);
2974 
2975 {$IFDEF use_xyz}
2976         SetZ(Pt, E1, E2, FZFillCallback);
2977 {$ENDIF}
2978 
2979 {$IFDEF use_lines}
2980   //if either edge is on an OPEN path ...
2981   if (E1.WindDelta = 0) or (E2.WindDelta = 0) then
2982   begin
2983     //ignore subject-subject open path intersections ...
2984     if (E1.WindDelta = 0) AND (E2.WindDelta = 0) then Exit
2985     //if intersecting a subj line with a subj poly ...
2986     else if (E1.PolyType = E2.PolyType) and
2987       (E1.WindDelta <> E2.WindDelta) and (FClipType = ctUnion) then
2988     begin
2989       if (E1.WindDelta = 0) then
2990       begin
2991         if (E2Contributing) then
2992         begin
2993           AddOutPt(E1, pt);
2994           if (E1Contributing) then E1.OutIdx := Unassigned;
2995         end;
2996       end else
2997       begin
2998         if (E1Contributing) then
2999         begin
3000           AddOutPt(E2, pt);
3001           if (E2Contributing) then E2.OutIdx := Unassigned;
3002         end;
3003       end;
3004     end
3005     else if (E1.PolyType <> E2.PolyType) then
3006     begin
3007       //toggle subj open path OutIdx on/off when Abs(clip.WndCnt) = 1 ...
3008       if (E1.WindDelta = 0) and (Abs(E2.WindCnt) = 1) and
3009        ((FClipType <> ctUnion) or (E2.WindCnt2 = 0)) then
3010       begin
3011         AddOutPt(E1, Pt);
3012         if E1Contributing then E1.OutIdx := Unassigned;
3013       end
3014       else if (E2.WindDelta = 0) and (Abs(E1.WindCnt) = 1) and
3015        ((FClipType <> ctUnion) or (E1.WindCnt2 = 0)) then
3016       begin
3017         AddOutPt(E2, Pt);
3018         if E2Contributing then E2.OutIdx := Unassigned;
3019       end
3020     end;
3021     Exit;
3022   end;
3023 {$ENDIF}
3024 
3025   //update winding counts...
3026   //assumes that E1 will be to the right of E2 ABOVE the intersection
3027   if E1.PolyType = E2.PolyType then
3028   begin
3029     if IsEvenOddFillType(E1) then
3030     begin
3031       E1Wc := E1.WindCnt;
3032       E1.WindCnt := E2.WindCnt;
3033       E2.WindCnt := E1Wc;
3034     end else
3035     begin
3036       if E1.WindCnt + E2.WindDelta = 0 then
3037         E1.WindCnt := -E1.WindCnt else
3038         Inc(E1.WindCnt, E2.WindDelta);
3039       if E2.WindCnt - E1.WindDelta = 0 then
3040         E2.WindCnt := -E2.WindCnt else
3041         Dec(E2.WindCnt, E1.WindDelta);
3042     end;
3043   end else
3044   begin
3045     if not IsEvenOddFillType(E2) then Inc(E1.WindCnt2, E2.WindDelta)
3046     else if E1.WindCnt2 = 0 then E1.WindCnt2 := 1
3047     else E1.WindCnt2 := 0;
3048 
3049     if not IsEvenOddFillType(E1) then Dec(E2.WindCnt2, E1.WindDelta)
3050     else if E2.WindCnt2 = 0 then E2.WindCnt2 := 1
3051     else E2.WindCnt2 := 0;
3052   end;
3053 
3054   if E1.PolyType = ptSubject then
3055   begin
3056     E1FillType := FSubjFillType;
3057     E1FillType2 := FClipFillType;
3058   end else
3059   begin
3060     E1FillType := FClipFillType;
3061     E1FillType2 := FSubjFillType;
3062   end;
3063   if E2.PolyType = ptSubject then
3064   begin
3065     E2FillType := FSubjFillType;
3066     E2FillType2 := FClipFillType;
3067   end else
3068   begin
3069     E2FillType := FClipFillType;
3070     E2FillType2 := FSubjFillType;
3071   end;
3072 
3073   case E1FillType of
3074     pftPositive: E1Wc := E1.WindCnt;
3075     pftNegative : E1Wc := -E1.WindCnt;
3076     else E1Wc := abs(E1.WindCnt);
3077   end;
3078   case E2FillType of
3079     pftPositive: E2Wc := E2.WindCnt;
3080     pftNegative : E2Wc := -E2.WindCnt;
3081     else E2Wc := abs(E2.WindCnt);
3082   end;
3083 
3084   if E1Contributing and E2contributing then
3085   begin
3086     if not (E1Wc in [0,1]) or not (E2Wc in [0,1]) or
3087       ((E1.PolyType <> E2.PolyType) and (fClipType <> ctXor)) then
3088     begin
3089         AddLocalMaxPoly(E1, E2, Pt);
3090     end else
3091     begin
3092       AddOutPt(E1, Pt);
3093       AddOutPt(E2, Pt);
3094       SwapSides(E1, E2);
3095       SwapPolyIndexes(E1, E2);
3096     end;
3097   end else if E1Contributing then
3098   begin
3099     if (E2Wc = 0) or (E2Wc = 1) then
3100     begin
3101       AddOutPt(E1, Pt);
3102       SwapSides(E1, E2);
3103       SwapPolyIndexes(E1, E2);
3104     end;
3105   end
3106   else if E2contributing then
3107   begin
3108     if (E1Wc = 0) or (E1Wc = 1) then
3109     begin
3110       AddOutPt(E2, Pt);
3111       SwapSides(E1, E2);
3112       SwapPolyIndexes(E1, E2);
3113     end;
3114   end
3115   else if  ((E1Wc = 0) or (E1Wc = 1)) and ((E2Wc = 0) or (E2Wc = 1)) then
3116   begin
3117     //neither Edge is currently contributing ...
3118 
3119     case E1FillType2 of
3120       pftPositive: E1Wc2 := E1.WindCnt2;
3121       pftNegative : E1Wc2 := -E1.WindCnt2;
3122       else E1Wc2 := abs(E1.WindCnt2);
3123     end;
3124     case E2FillType2 of
3125       pftPositive: E2Wc2 := E2.WindCnt2;
3126       pftNegative : E2Wc2 := -E2.WindCnt2;
3127       else E2Wc2 := abs(E2.WindCnt2);
3128     end;
3129 
3130     if (E1.PolyType <> E2.PolyType) then
3131     begin
3132       AddLocalMinPoly(E1, E2, Pt);
3133     end
3134     else if (E1Wc = 1) and (E2Wc = 1) then
3135       case FClipType of
3136         ctIntersection:
3137           if (E1Wc2 > 0) and (E2Wc2 > 0) then
3138             AddLocalMinPoly(E1, E2, Pt);
3139         ctUnion:
3140           if (E1Wc2 <= 0) and (E2Wc2 <= 0) then
3141             AddLocalMinPoly(E1, E2, Pt);
3142         ctDifference:
3143           if ((E1.PolyType = ptClip) and (E1Wc2 > 0) and (E2Wc2 > 0)) or
3144             ((E1.PolyType = ptSubject) and (E1Wc2 <= 0) and (E2Wc2 <= 0)) then
3145               AddLocalMinPoly(E1, E2, Pt);
3146         ctXor:
3147           AddLocalMinPoly(E1, E2, Pt);
3148       end
3149     else
3150       swapsides(E1,E2);
3151   end;
3152 end;
3153 //------------------------------------------------------------------------------
3154 
FirstParamIsBottomPtnull3155 function FirstParamIsBottomPt(btmPt1, btmPt2: POutPt): Boolean;
3156 var
3157   Dx1n, Dx1p, Dx2n, Dx2p: Double;
3158   P: POutPt;
3159 begin
3160   //Precondition: bottom-points share the same vertex.
3161   //Use inverse slopes of adjacent edges (ie dx/dy) to determine the outer
3162   //polygon and hence the 'real' bottompoint.
3163   //nb: Slope is vertical when dx == 0. If the greater abs(dx) of param1
3164   //is greater than or equal both abs(dx) in param2 then param1 is outer.
3165   P := btmPt1.Prev;
3166   while PointsEqual(P.Pt, btmPt1.Pt) and (P <> btmPt1) do P := P.Prev;
3167   Dx1p := abs(GetDx(btmPt1.Pt, P.Pt));
3168   P := btmPt1.Next;
3169   while PointsEqual(P.Pt, btmPt1.Pt) and (P <> btmPt1) do P := P.Next;
3170   Dx1n := abs(GetDx(btmPt1.Pt, P.Pt));
3171 
3172   P := btmPt2.Prev;
3173   while PointsEqual(P.Pt, btmPt2.Pt) and (P <> btmPt2) do P := P.Prev;
3174   Dx2p := abs(GetDx(btmPt2.Pt, P.Pt));
3175   P := btmPt2.Next;
3176   while PointsEqual(P.Pt, btmPt2.Pt) and (P <> btmPt2) do P := P.Next;
3177   Dx2n := abs(GetDx(btmPt2.Pt, P.Pt));
3178   if (Max(Dx1p, Dx1n) = Max(Dx2p, Dx2n)) and
3179     (Min(Dx1p, Dx1n) = Min(Dx2p, Dx2n)) then
3180       Result := Area(btmPt1) > 0 //if otherwise identical use orientation
3181   else
3182     Result := ((Dx1p >= Dx2p) and (Dx1p >= Dx2n)) or
3183       ((Dx1n >= Dx2p) and (Dx1n >= Dx2n));
3184 end;
3185 //------------------------------------------------------------------------------
3186 
GetBottomPtnull3187 function GetBottomPt(PP: POutPt): POutPt;
3188 var
3189   P, Dups: POutPt;
3190 begin
3191   Dups := nil;
3192   P := PP.Next;
3193   while P <> PP do
3194   begin
3195     if P.Pt.Y > PP.Pt.Y then
3196     begin
3197       PP := P;
3198       Dups := nil;
3199     end
3200     else if (P.Pt.Y = PP.Pt.Y) and (P.Pt.X <= PP.Pt.X) then
3201     begin
3202       if (P.Pt.X < PP.Pt.X) then
3203       begin
3204         Dups := nil;
3205         PP := P;
3206       end else
3207       begin
3208         if (P.Next <> PP) and (P.Prev <> PP) then Dups := P;
3209       end;
3210     end;
3211     P := P.Next;
3212   end;
3213   if Assigned(Dups) then
3214   begin
3215     //there appears to be at least 2 vertices at bottom-most point so ...
3216     while Dups <> P do
3217     begin
3218       if not FirstParamIsBottomPt(P, Dups) then PP := Dups;
3219       Dups := Dups.Next;
3220       while not PointsEqual(Dups.Pt, PP.Pt) do Dups := Dups.Next;
3221     end;
3222   end;
3223   Result := PP;
3224 end;
3225 //------------------------------------------------------------------------------
3226 
3227 procedure TClipper.SetHoleState(E: PEdge; OutRec: POutRec);
3228 var
3229   E2, eTmp: PEdge;
3230 begin
3231   //E.FirstLeft is the parent/container OutRec of E if any, and is the first
3232   //unpaired OutRec to the left in AEL. (Paired OutRecs will either be a
3233   //sibling of E or a sibling of one of its 'parents'.)
3234   eTmp := nil;
3235   E2 := E.PrevInAEL;
3236   while Assigned(E2) do
3237   begin
3238     if (E2.OutIdx >= 0) and (E2.WindDelta <> 0) then
3239     begin
3240       if not assigned(eTmp) then
3241         eTmp := E2
3242       else if (eTmp.OutIdx = E2.OutIdx) then
3243         eTmp := nil; //paired
3244     end;
3245     E2 := E2.PrevInAEL;
3246   end;
3247   if assigned(eTmp) then
3248   begin
3249     OutRec.FirstLeft := POutRec(fPolyOutList[eTmp.OutIdx]);
3250     OutRec.IsHole := not OutRec.FirstLeft.IsHole;
3251   end else
3252   begin
3253     OutRec.FirstLeft := nil;
3254     OutRec.IsHole := False;
3255   end;
3256 end;
3257 //------------------------------------------------------------------------------
3258 
GetLowermostRecnull3259 function GetLowermostRec(OutRec1, OutRec2: POutRec): POutRec;
3260 var
3261   OutPt1, OutPt2: POutPt;
3262 begin
3263   if not assigned(OutRec1.BottomPt) then
3264     OutRec1.BottomPt := GetBottomPt(OutRec1.Pts);
3265   if not assigned(OutRec2.BottomPt) then
3266     OutRec2.BottomPt := GetBottomPt(OutRec2.Pts);
3267   OutPt1 := OutRec1.BottomPt;
3268   OutPt2 := OutRec2.BottomPt;
3269   if (OutPt1.Pt.Y > OutPt2.Pt.Y) then Result := OutRec1
3270   else if (OutPt1.Pt.Y < OutPt2.Pt.Y) then Result := OutRec2
3271   else if (OutPt1.Pt.X < OutPt2.Pt.X) then Result := OutRec1
3272   else if (OutPt1.Pt.X > OutPt2.Pt.X) then Result := OutRec2
3273   else if (OutPt1.Next = OutPt1) then Result := OutRec2
3274   else if (OutPt2.Next = OutPt2) then Result := OutRec1
3275   else if FirstParamIsBottomPt(OutPt1, OutPt2) then Result := OutRec1
3276   else Result := OutRec2;
3277 end;
3278 //------------------------------------------------------------------------------
3279 
OutRec1RightOfOutRec2null3280 function OutRec1RightOfOutRec2(OutRec1, OutRec2: POutRec): Boolean;
3281 begin
3282   Result := True;
3283   repeat
3284     OutRec1 := OutRec1.FirstLeft;
3285     if OutRec1 = OutRec2 then Exit;
3286   until not Assigned(OutRec1);
3287   Result := False;
3288 end;
3289 //------------------------------------------------------------------------------
3290 
GetOutRecnull3291 function TClipper.GetOutRec(Idx: integer): POutRec;
3292 begin
3293   Result := FPolyOutList[Idx];
3294   while Result <> FPolyOutList[Result.Idx] do
3295     Result := FPolyOutList[Result.Idx];
3296 end;
3297 //------------------------------------------------------------------------------
3298 
3299 procedure TClipper.AppendPolygon(E1, E2: PEdge);
3300 var
3301   HoleStateRec, OutRec1, OutRec2: POutRec;
3302   P1_lft, P1_rt, P2_lft, P2_rt: POutPt;
3303   OKIdx, ObsoleteIdx: Integer;
3304   E: PEdge;
3305 begin
3306   OutRec1 := FPolyOutList[E1.OutIdx];
3307   OutRec2 := FPolyOutList[E2.OutIdx];
3308 
3309   //First work out which polygon fragment has the correct hole state.
3310   //Since we're working from the bottom upward and left to right, the left most
3311   //and lowermost polygon is outermost and must have the correct hole state ...
3312   if OutRec1RightOfOutRec2(OutRec1, OutRec2) then HoleStateRec := OutRec2
3313   else if OutRec1RightOfOutRec2(OutRec2, OutRec1) then HoleStateRec := OutRec1
3314   else HoleStateRec := GetLowermostRec(OutRec1, OutRec2);
3315 
3316   //get the start and ends of both output polygons and
3317   //join E2 poly onto E1 poly and delete pointers to E2 ...
3318 
3319   P1_lft := OutRec1.Pts;
3320   P2_lft := OutRec2.Pts;
3321   P1_rt := P1_lft.Prev;
3322   P2_rt := P2_lft.Prev;
3323 
3324   if E1.Side = esLeft then
3325   begin
3326     if E2.Side = esLeft then
3327     begin
3328       //z y x a b c
3329       ReversePolyPtLinks(P2_lft);
3330       P2_lft.Next := P1_lft;
3331       P1_lft.Prev := P2_lft;
3332       P1_rt.Next := P2_rt;
3333       P2_rt.Prev := P1_rt;
3334       OutRec1.Pts := P2_rt;
3335     end else
3336     begin
3337       //x y z a b c
3338       P2_rt.Next := P1_lft;
3339       P1_lft.Prev := P2_rt;
3340       P2_lft.Prev := P1_rt;
3341       P1_rt.Next := P2_lft;
3342       OutRec1.Pts := P2_lft;
3343     end;
3344   end else
3345   begin
3346     if E2.Side = esRight then
3347     begin
3348       //a b c z y x
3349       ReversePolyPtLinks(P2_lft);
3350       P1_rt.Next := P2_rt;
3351       P2_rt.Prev := P1_rt;
3352       P2_lft.Next := P1_lft;
3353       P1_lft.Prev := P2_lft;
3354     end else
3355     begin
3356       //a b c x y z
3357       P1_rt.Next := P2_lft;
3358       P2_lft.Prev := P1_rt;
3359       P1_lft.Prev := P2_rt;
3360       P2_rt.Next := P1_lft;
3361     end;
3362   end;
3363 
3364   OutRec1.BottomPt := nil;
3365   if HoleStateRec = OutRec2 then
3366   begin
3367     if OutRec2.FirstLeft <> OutRec1 then
3368       OutRec1.FirstLeft := OutRec2.FirstLeft;
3369     OutRec1.IsHole := OutRec2.IsHole;
3370   end;
3371 
3372   OutRec2.Pts := nil;
3373   OutRec2.BottomPt := nil;
3374   OutRec2.FirstLeft := OutRec1;
3375 
3376   OKIdx := OutRec1.Idx;
3377   ObsoleteIdx := OutRec2.Idx;
3378 
3379   E1.OutIdx := Unassigned; //safe because we only get here via AddLocalMaxPoly
3380   E2.OutIdx := Unassigned;
3381 
3382   E := FActiveEdges;
3383   while Assigned(E) do
3384   begin
3385     if (E.OutIdx = ObsoleteIdx) then
3386     begin
3387       E.OutIdx := OKIdx;
3388       E.Side := E1.Side;
3389       Break;
3390     end;
3391     E := E.NextInAEL;
3392   end;
3393 
3394   OutRec2.Idx := OutRec1.Idx;
3395 end;
3396 //------------------------------------------------------------------------------
3397 
GetLastOutPtnull3398 function TClipper.GetLastOutPt(E: PEdge): POutPt;
3399 var
3400   OutRec: POutRec;
3401 begin
3402   OutRec := FPolyOutList[E.OutIdx];
3403   if E.Side = esLeft then
3404     Result := OutRec.Pts else
3405     Result := OutRec.Pts.Prev;
3406 end;
3407 //------------------------------------------------------------------------------
3408 
3409 procedure TClipper.ProcessHorizontals;
3410 var
3411   E: PEdge;
3412 begin
3413   while PopEdgeFromSEL(E) do
3414     ProcessHorizontal(E);
3415 end;
3416 //------------------------------------------------------------------------------
3417 
3418 function IsMinima(E: PEdge): Boolean; {$IFDEF INLINING} inline; {$ENDIF}
3419 begin
3420   Result := Assigned(E) and (E.Prev.NextInLML <> E) and (E.Next.NextInLML <> E);
3421 end;
3422 //------------------------------------------------------------------------------
3423 
3424 function IsMaxima(E: PEdge; const Y: cInt): Boolean;
3425 {$IFDEF INLINING} inline; {$ENDIF}
3426 begin
3427   Result := Assigned(E) and (E.Top.Y = Y) and not Assigned(E.NextInLML);
3428 end;
3429 //------------------------------------------------------------------------------
3430 
3431 function IsIntermediate(E: PEdge; const Y: cInt): Boolean;
3432 {$IFDEF INLINING} inline; {$ENDIF}
3433 begin
3434   Result := (E.Top.Y = Y) and Assigned(E.NextInLML);
3435 end;
3436 //------------------------------------------------------------------------------
3437 
3438 function GetMaximaPair(E: PEdge): PEdge;
3439 begin
3440   if PointsEqual(E.Next.Top, E.Top) and not assigned(E.Next.NextInLML) then
3441     Result := E.Next
3442   else if PointsEqual(E.Prev.Top, E.Top) and not assigned(E.Prev.NextInLML) then
3443     Result := E.Prev
3444   else
3445     Result := nil;
3446 end;
3447 //------------------------------------------------------------------------------
3448 
3449 function GetMaximaPairEx(E: PEdge): PEdge;
3450 begin
3451   //as above but returns nil if MaxPair isn't in AEL (unless it's horizontal)
3452   Result := GetMaximaPair(E);
3453   if not assigned(Result) or (Result.OutIdx = Skip) or
3454     ((Result.NextInAEL = Result.PrevInAEL) and not IsHorizontal(Result)) then
3455       Result := nil;
3456 end;
3457 //------------------------------------------------------------------------------
3458 
3459 procedure TClipper.SwapPositionsInSEL(E1, E2: PEdge);
3460 var
3461   Prev,Next: PEdge;
3462 begin
3463   if E1.NextInSEL = E2 then
3464   begin
3465     Next    := E2.NextInSEL;
3466     if Assigned(Next) then Next.PrevInSEL := E1;
3467     Prev    := E1.PrevInSEL;
3468     if Assigned(Prev) then Prev.NextInSEL := E2;
3469     E2.PrevInSEL := Prev;
3470     E2.NextInSEL := E1;
3471     E1.PrevInSEL := E2;
3472     E1.NextInSEL := Next;
3473   end
3474   else if E2.NextInSEL = E1 then
3475   begin
3476     Next    := E1.NextInSEL;
3477     if Assigned(Next) then Next.PrevInSEL := E2;
3478     Prev    := E2.PrevInSEL;
3479     if Assigned(Prev) then Prev.NextInSEL := E1;
3480     E1.PrevInSEL := Prev;
3481     E1.NextInSEL := E2;
3482     E2.PrevInSEL := E1;
3483     E2.NextInSEL := Next;
3484   end else
3485   begin
3486     Next    := E1.NextInSEL;
3487     Prev    := E1.PrevInSEL;
3488     E1.NextInSEL := E2.NextInSEL;
3489     if Assigned(E1.NextInSEL) then E1.NextInSEL.PrevInSEL := E1;
3490     E1.PrevInSEL := E2.PrevInSEL;
3491     if Assigned(E1.PrevInSEL) then E1.PrevInSEL.NextInSEL := E1;
3492     E2.NextInSEL := Next;
3493     if Assigned(E2.NextInSEL) then E2.NextInSEL.PrevInSEL := E2;
3494     E2.PrevInSEL := Prev;
3495     if Assigned(E2.PrevInSEL) then E2.PrevInSEL.NextInSEL := E2;
3496   end;
3497   if not Assigned(E1.PrevInSEL) then FSortedEdges := E1
3498   else if not Assigned(E2.PrevInSEL) then FSortedEdges := E2;
3499 end;
3500 //------------------------------------------------------------------------------
3501 
3502 function GetNextInAEL(E: PEdge; Direction: TDirection): PEdge;
3503   {$IFDEF INLINING} inline; {$ENDIF}
3504 begin
3505   if Direction = dLeftToRight then
3506     Result := E.NextInAEL else
3507     Result := E.PrevInAEL;
3508 end;
3509 //------------------------------------------------------------------------
3510 
3511 procedure GetHorzDirection(HorzEdge: PEdge; out Dir: TDirection;
3512   out Left, Right: cInt); {$IFDEF INLINING} inline; {$ENDIF}
3513 begin
3514   if HorzEdge.Bot.X < HorzEdge.Top.X then
3515   begin
3516     Left := HorzEdge.Bot.X;
3517     Right := HorzEdge.Top.X;
3518     Dir := dLeftToRight;
3519   end else
3520   begin
3521     Left := HorzEdge.Top.X;
3522     Right := HorzEdge.Bot.X;
3523     Dir := dRightToLeft;
3524   end;
3525 end;
3526 //------------------------------------------------------------------------
3527 
3528 procedure TClipper.ProcessHorizontal(HorzEdge: PEdge);
3529 var
3530   E, eNext, eNextHorz, ePrev, eMaxPair, eLastHorz: PEdge;
3531   HorzLeft, HorzRight: cInt;
3532   Direction: TDirection;
3533   Pt: TIntPoint;
3534   Op1, Op2: POutPt;
3535   IsLastHorz, IsOpen: Boolean;
3536   currMax: PMaxima;
3537 begin
3538 (*******************************************************************************
3539 * Notes: Horizontal edges (HEs) at scanline intersections (ie at the top or    *
3540 * bottom of a scanbeam) are processed as if layered. The order in which HEs    *
3541 * are processed doesn't matter. HEs intersect with other HE Bot.Xs only [#]    *
3542 * (or they could intersect with Top.Xs only, ie EITHER Bot.Xs OR Top.Xs),      *
3543 * and with other non-horizontal edges [*]. Once these intersections are        *
3544 * processed, intermediate HEs then 'promote' the Edge above (NextInLML) into   *
3545 * the AEL. These 'promoted' edges may in turn intersect [%] with other HEs.    *
3546 *******************************************************************************)
3547 
3548 (*******************************************************************************
3549 *           \   nb: HE processing order doesn't matter         /          /    *
3550 *            \                                                /          /     *
3551 * { --------  \  -------------------  /  \  - (3) o==========%==========o  - } *
3552 * {            o==========o (2)      /    \       .          .               } *
3553 * {                       .         /      \      .          .               } *
3554 * { ----  o===============#========*========*=====#==========o  (1)  ------- } *
3555 *        /                 \      /          \   /                             *
3556 *******************************************************************************)
3557 
3558   GetHorzDirection(HorzEdge, Direction, HorzLeft, HorzRight);
3559   IsOpen := (HorzEdge.WindDelta = 0);
3560 
3561   eLastHorz := HorzEdge;
3562   while Assigned(eLastHorz.NextInLML) and
3563     (eLastHorz.NextInLML.Dx = Horizontal) do
3564       eLastHorz := eLastHorz.NextInLML;
3565   if Assigned(eLastHorz.NextInLML) then
3566     eMaxPair := nil else
3567     eMaxPair := GetMaximaPair(eLastHorz);
3568 
3569   Op1 := nil;
3570   currMax := FMaxima;
3571   //nb: FMaxima will only be assigned when the Simplify property is set true.
3572 
3573   if assigned(currMax) then
3574   begin
3575     //get the first useful Maxima ...
3576     if (Direction = dLeftToRight) then
3577     begin
3578       while Assigned(currMax) and (currMax.X <= HorzEdge.Bot.X) do
3579         currMax := currMax.Next;
3580       if Assigned(currMax) and (currMax.X >= eLastHorz.Top.X) then
3581         currMax := nil;
3582     end else
3583     begin
3584       while Assigned(currMax.Next) and (currMax.Next.X < HorzEdge.Bot.X) do
3585         currMax := currMax.Next;
3586       if (currMax.X <= eLastHorz.Top.X) then currMax := nil;
3587     end;
3588   end;
3589 
3590   while true do //loops through consec. horizontal edges
3591   begin
3592     IsLastHorz := (HorzEdge = eLastHorz);
3593     E := GetNextInAEL(HorzEdge, Direction);
3594     while Assigned(E) do
3595     begin
3596 
3597       //this code block inserts extra coords into horizontal edges (in output
3598       //polygons) whereever maxima touch these horizontal edges. This helps
3599       //'simplifying' polygons (ie if the Simplify property is set).
3600       if assigned(currMax) then
3601       begin
3602         if (Direction = dLeftToRight) then
3603         begin
3604           while assigned(currMax) and (currMax.X < E.Curr.X) do
3605           begin
3606             if (HorzEdge.OutIdx >= 0) and not IsOpen then
3607               AddOutPt(HorzEdge, IntPoint(currMax.X, HorzEdge.Bot.Y));
3608             currMax := currMax.Next;
3609           end;
3610         end else
3611         begin
3612           while assigned(currMax) and (currMax.X > E.Curr.X) do
3613           begin
3614             if (HorzEdge.OutIdx >= 0) and not IsOpen then
3615               AddOutPt(HorzEdge, IntPoint(currMax.X, HorzEdge.Bot.Y));
3616             currMax := currMax.Prev;
3617           end;
3618         end;
3619       end;
3620 
3621       if ((Direction = dLeftToRight) and (E.Curr.X > HorzRight)) or
3622         ((Direction = dRightToLeft) and (E.Curr.X < HorzLeft)) then
3623           Break;
3624 
3625       //also break if we've got to the end of an intermediate horizontal edge
3626       //nb: Smaller Dx's are to the right of larger Dx's ABOVE the horizontal.
3627       if (E.Curr.X = HorzEdge.Top.X) and
3628         Assigned(HorzEdge.NextInLML) and (E.Dx < HorzEdge.NextInLML.Dx) then
3629           Break;
3630 
3631       if (HorzEdge.OutIdx >= 0) and not IsOpen then //may be done multiple times
3632       begin
3633 {$IFDEF use_xyz}
3634       if (Direction = dLeftToRight) then SetZ(E.Curr, HorzEdge, E, FZFillCallback)
3635       else SetZ(E.Curr, E, HorzEdge, FZFillCallback);
3636 {$ENDIF}
3637         Op1 := AddOutPt(HorzEdge, E.Curr);
3638         eNextHorz := FSortedEdges;
3639         while Assigned(eNextHorz) do
3640         begin
3641           if (eNextHorz.OutIdx >= 0) and
3642             HorzSegmentsOverlap(HorzEdge.Bot.X,
3643             HorzEdge.Top.X, eNextHorz.Bot.X, eNextHorz.Top.X) then
3644           begin
3645             Op2 := GetLastOutPt(eNextHorz);
3646             AddJoin(Op2, Op1, eNextHorz.Top);
3647           end;
3648           eNextHorz := eNextHorz.NextInSEL;
3649         end;
3650         AddGhostJoin(Op1, HorzEdge.Bot);
3651       end;
3652 
3653       //OK, so far we're still in range of the horizontal Edge  but make sure
3654       //we're at the last of consec. horizontals when matching with eMaxPair
3655       if (E = eMaxPair) and IsLastHorz then
3656       begin
3657         if HorzEdge.OutIdx >= 0 then
3658           AddLocalMaxPoly(HorzEdge, eMaxPair, HorzEdge.Top);
3659         deleteFromAEL(HorzEdge);
3660         deleteFromAEL(eMaxPair);
3661         Exit;
3662       end;
3663 
3664       if (Direction = dLeftToRight) then
3665       begin
3666         Pt := IntPoint(E.Curr.X, HorzEdge.Curr.Y);
3667         IntersectEdges(HorzEdge, E, Pt);
3668       end else
3669       begin
3670         Pt := IntPoint(E.Curr.X, HorzEdge.Curr.Y);
3671         IntersectEdges(E, HorzEdge, Pt);
3672       end;
3673       eNext := GetNextInAEL(E, Direction);
3674       SwapPositionsInAEL(HorzEdge, E);
3675       E := eNext;
3676     end;
3677 
3678     //Break out of loop if HorzEdge.NextInLML is not also horizontal ...
3679     if not Assigned(HorzEdge.NextInLML) or
3680       (HorzEdge.NextInLML.Dx <> Horizontal) then Break;
3681 
3682     UpdateEdgeIntoAEL(HorzEdge);
3683     if (HorzEdge.OutIdx >= 0) then AddOutPt(HorzEdge, HorzEdge.Bot);
3684     GetHorzDirection(HorzEdge, Direction, HorzLeft, HorzRight);
3685   end;
3686 
3687   if (HorzEdge.OutIdx >= 0) and not Assigned(Op1) then
3688   begin
3689     Op1 := GetLastOutPt(HorzEdge);
3690     eNextHorz := FSortedEdges;
3691     while Assigned(eNextHorz) do
3692     begin
3693       if (eNextHorz.OutIdx >= 0) and
3694         HorzSegmentsOverlap(HorzEdge.Bot.X,
3695         HorzEdge.Top.X, eNextHorz.Bot.X, eNextHorz.Top.X) then
3696       begin
3697         Op2 := GetLastOutPt(eNextHorz);
3698         AddJoin(Op2, Op1, eNextHorz.Top);
3699       end;
3700       eNextHorz := eNextHorz.NextInSEL;
3701     end;
3702     AddGhostJoin(Op1, HorzEdge.Top);
3703   end;
3704 
3705   if Assigned(HorzEdge.NextInLML) then
3706   begin
3707     if (HorzEdge.OutIdx >= 0) then
3708     begin
3709       Op1 := AddOutPt(HorzEdge, HorzEdge.Top);
3710 
3711       UpdateEdgeIntoAEL(HorzEdge);
3712       if (HorzEdge.WindDelta = 0) then Exit;
3713       //nb: HorzEdge is no longer horizontal here
3714       ePrev := HorzEdge.PrevInAEL;
3715       eNext := HorzEdge.NextInAEL;
3716       if Assigned(ePrev) and (ePrev.Curr.X = HorzEdge.Bot.X) and
3717         (ePrev.Curr.Y = HorzEdge.Bot.Y) and (ePrev.WindDelta <> 0) and
3718         (ePrev.OutIdx >= 0) and (ePrev.Curr.Y > ePrev.Top.Y) and
3719         SlopesEqual(HorzEdge, ePrev, FUse64BitRange) then
3720       begin
3721         Op2 := AddOutPt(ePrev, HorzEdge.Bot);
3722         AddJoin(Op1, Op2, HorzEdge.Top);
3723       end
3724       else if Assigned(eNext) and (eNext.Curr.X = HorzEdge.Bot.X) and
3725         (eNext.Curr.Y = HorzEdge.Bot.Y) and (eNext.WindDelta <> 0) and
3726           (eNext.OutIdx >= 0) and (eNext.Curr.Y > eNext.Top.Y) and
3727         SlopesEqual(HorzEdge, eNext, FUse64BitRange) then
3728       begin
3729         Op2 := AddOutPt(eNext, HorzEdge.Bot);
3730         AddJoin(Op1, Op2, HorzEdge.Top);
3731       end;
3732     end else
3733       UpdateEdgeIntoAEL(HorzEdge);
3734   end else
3735   begin
3736     if (HorzEdge.OutIdx >= 0) then AddOutPt(HorzEdge, HorzEdge.Top);
3737     DeleteFromAEL(HorzEdge);
3738   end;
3739 end;
3740 //------------------------------------------------------------------------------
3741 
ProcessIntersectionsnull3742 function TClipper.ProcessIntersections(const TopY: cInt): Boolean;
3743 begin
3744   Result := True;
3745   try
3746     BuildIntersectList(TopY);
3747     if (FIntersectList.Count = 0) then
3748       Exit
3749     else if FixupIntersectionOrder then
3750       ProcessIntersectList()
3751     else
3752       Result := False;
3753   finally
3754     DisposeIntersectNodes; //clean up if there's been an error
3755     FSortedEdges := nil;
3756   end;
3757 end;
3758 //------------------------------------------------------------------------------
3759 
3760 procedure TClipper.DisposeIntersectNodes;
3761 var
3762   I: Integer;
3763 begin
3764   for I := 0 to FIntersectList.Count - 1 do
3765     Dispose(PIntersectNode(FIntersectList[I]));
3766   FIntersectList.Clear;
3767 end;
3768 //------------------------------------------------------------------------------
3769 
3770 procedure TClipper.BuildIntersectList(const TopY: cInt);
3771 var
3772   E, eNext: PEdge;
3773   Pt: TIntPoint;
3774   IsModified: Boolean;
3775   NewNode: PIntersectNode;
3776 begin
3777   if not Assigned(fActiveEdges) then Exit;
3778 
3779   //prepare for sorting ...
3780   E := FActiveEdges;
3781   FSortedEdges := E;
3782   while Assigned(E) do
3783   begin
3784     E.PrevInSEL := E.PrevInAEL;
3785     E.NextInSEL := E.NextInAEL;
3786     E.Curr.X := TopX(E, TopY);
3787     E := E.NextInAEL;
3788   end;
3789 
3790   //bubblesort (because adjacent swaps are required) ...
3791   repeat
3792     IsModified := False;
3793     E := FSortedEdges;
3794     while Assigned(E.NextInSEL) do
3795     begin
3796       eNext := E.NextInSEL;
3797       if (E.Curr.X > eNext.Curr.X) then
3798       begin
3799         IntersectPointEx(E, eNext, Pt);
3800         if Pt.Y < TopY then
3801           Pt := IntPoint(TopX(E, TopY), TopY);
3802         new(NewNode);
3803         NewNode.Edge1 := E;
3804         NewNode.Edge2 := eNext;
3805         NewNode.Pt := Pt;
3806         FIntersectList.Add(NewNode);
3807 
3808         SwapPositionsInSEL(E, eNext);
3809         IsModified := True;
3810       end else
3811         E := eNext;
3812     end;
3813     if Assigned(E.PrevInSEL) then
3814       E.PrevInSEL.NextInSEL := nil
3815     else Break;
3816   until not IsModified;
3817 end;
3818 //------------------------------------------------------------------------------
3819 
3820 procedure TClipper.ProcessIntersectList;
3821 var
3822   I: Integer;
3823 begin
3824   for I := 0 to FIntersectList.Count - 1 do
3825   begin
3826     with PIntersectNode(FIntersectList[I])^ do
3827     begin
3828       IntersectEdges(Edge1, Edge2, Pt);
3829       SwapPositionsInAEL(Edge1, Edge2);
3830     end;
3831     dispose(PIntersectNode(FIntersectList[I]));
3832   end;
3833   FIntersectList.Clear;
3834 end;
3835 //------------------------------------------------------------------------------
3836 
3837 procedure TClipper.DoMaxima(E: PEdge);
3838 var
3839   ENext, EMaxPair: PEdge;
3840 begin
3841   EMaxPair := GetMaximaPairEx(E);
3842   if not assigned(EMaxPair) then
3843   begin
3844     if E.OutIdx >= 0 then
3845       AddOutPt(E, E.Top);
3846     DeleteFromAEL(E);
3847     Exit;
3848   end;
3849 
3850   ENext := E.NextInAEL;
3851   //rarely, with overlapping collinear edges (in open paths) ENext can be nil
3852   while Assigned(ENext) and (ENext <> EMaxPair) do
3853   begin
3854     IntersectEdges(E, ENext, E.Top);
3855     SwapPositionsInAEL(E, ENext);
3856     ENext := E.NextInAEL;
3857   end;
3858 
3859   if (E.OutIdx = Unassigned) and (EMaxPair.OutIdx = Unassigned) then
3860   begin
3861     DeleteFromAEL(E);
3862     DeleteFromAEL(EMaxPair);
3863   end
3864   else if (E.OutIdx >= 0) and (EMaxPair.OutIdx >= 0) then
3865   begin
3866     if E.OutIdx >= 0 then
3867       AddLocalMaxPoly(E, EMaxPair, E.Top);
3868     deleteFromAEL(E);
3869     deleteFromAEL(eMaxPair);
3870   end
3871 {$IFDEF use_lines}
3872   else if E.WindDelta = 0 then
3873   begin
3874     if (E.OutIdx >= 0) then
3875     begin
3876       AddOutPt(E, E.Top);
3877       E.OutIdx := Unassigned;
3878     end;
3879     DeleteFromAEL(E);
3880 
3881     if (EMaxPair.OutIdx >= 0) then
3882     begin
3883       AddOutPt(EMaxPair, E.Top);
3884       EMaxPair.OutIdx := Unassigned;
3885     end;
3886     DeleteFromAEL(EMaxPair);
3887   end
3888 {$ENDIF}
3889   else
3890     raise exception.Create(rsDoMaxima);
3891 end;
3892 //------------------------------------------------------------------------------
3893 
3894 procedure TClipper.ProcessEdgesAtTopOfScanbeam(const TopY: cInt);
3895 var
3896   E, EMaxPair, ePrev, eNext: PEdge;
3897   Op, Op2: POutPt;
3898   IsMaximaEdge: Boolean;
3899   Pt: TIntPoint;
3900 begin
3901 (*******************************************************************************
3902 * Notes: Processing edges at scanline intersections (ie at the top or bottom   *
3903 * of a scanbeam) needs to be done in multiple stages and in the correct order. *
3904 * Firstly, edges forming a 'maxima' need to be processed and then removed.     *
3905 * Next, 'intermediate' and 'maxima' horizontal edges are processed. Then edges *
3906 * that intersect exactly at the top of the scanbeam are processed [%].         *
3907 * Finally, new minima are added and any intersects they create are processed.  *
3908 *******************************************************************************)
3909 
3910 (*******************************************************************************
3911 *     \                          /    /          \   /                         *
3912 *      \   Horizontal minima    /    /            \ /                          *
3913 * { --  o======================#====o   --------   .     ------------------- } *
3914 * {       Horizontal maxima    .                   %  scanline intersect     } *
3915 * { -- o=======================#===================#========o     ---------- } *
3916 *      |                      /                   / \        \                 *
3917 *      + maxima intersect    /                   /   \        \                *
3918 *     /|\                   /                   /     \        \               *
3919 *    / | \                 /                   /       \        \              *
3920 *******************************************************************************)
3921 
3922   E := FActiveEdges;
3923   while Assigned(E) do
3924   begin
3925     //1. process maxima, treating them as if they're 'bent' horizontal edges,
3926     //   but exclude maxima with Horizontal edges. nb: E can't be a Horizontal.
3927     IsMaximaEdge := IsMaxima(E, TopY);
3928     if IsMaximaEdge then
3929     begin
3930       EMaxPair := GetMaximaPairEx(E);
3931       IsMaximaEdge := not assigned(EMaxPair) or (EMaxPair.Dx <> Horizontal);
3932     end;
3933 
3934     if IsMaximaEdge then
3935     begin
3936       if FStrictSimple then
3937         InsertMaxima(E.Top.X);
3938       //'E' might be removed from AEL, as may any following edges so ...
3939       ePrev := E.PrevInAEL;
3940       DoMaxima(E);
3941       if not Assigned(ePrev) then
3942         E := FActiveEdges else
3943         E := ePrev.NextInAEL;
3944     end else
3945     begin
3946       //2. promote horizontal edges, otherwise update Curr.X and Curr.Y ...
3947       if IsIntermediate(E, TopY) and (E.NextInLML.Dx = Horizontal) then
3948       begin
3949         UpdateEdgeIntoAEL(E);
3950         if (E.OutIdx >= 0) then
3951           AddOutPt(E, E.Bot);
3952         AddEdgeToSEL(E);
3953       end else
3954       begin
3955         E.Curr.X := TopX(E, TopY);
3956         E.Curr.Y := TopY;
3957 {$IFDEF use_xyz}
3958         if E.Top.Y = TopY then e.Curr.Z := e.Top.Z
3959         else if (E.Bot.Y = TopY) then e.Curr.Z := E.Bot.Z else
3960         e.Curr.Z := 0;
3961 {$ENDIF}
3962       end;
3963 
3964       //When StrictlySimple and 'e' is being touched by another edge, then
3965       //make sure both edges have a vertex here ...
3966       if FStrictSimple then
3967       begin
3968         ePrev := E.PrevInAEL;
3969         if (E.OutIdx >= 0) and (E.WindDelta <> 0) and
3970           Assigned(ePrev) and (ePrev.Curr.X = E.Curr.X) and
3971           (ePrev.OutIdx >= 0) and (ePrev.WindDelta <> 0) then
3972         begin
3973           Pt := E.Curr;
3974 {$IFDEF use_xyz}
3975           SetZ(Pt, ePrev, E, FZFillCallback);
3976 {$ENDIF}
3977           Op := AddOutPt(ePrev, Pt);
3978           Op2 := AddOutPt(E, Pt);
3979           AddJoin(Op, Op2, Pt); //strictly-simple (type-3) 'join'
3980         end;
3981       end;
3982 
3983       E := E.NextInAEL;
3984     end;
3985   end;
3986 
3987   //3. Process horizontals at the top of the scanbeam ...
3988   ProcessHorizontals;
3989   if FStrictSimple then DisposeMaximaList;
3990 
3991   //4. Promote intermediate vertices ...
3992   E := FActiveEdges;
3993   while Assigned(E) do
3994   begin
3995     if IsIntermediate(E, TopY) then
3996     begin
3997       if (E.OutIdx >= 0) then
3998         Op := AddOutPt(E, E.Top) else
3999         Op := nil;
4000       UpdateEdgeIntoAEL(E);
4001 
4002       //if output polygons share an Edge, they'll need joining later ...
4003       ePrev := E.PrevInAEL;
4004       eNext  := E.NextInAEL;
4005       if Assigned(ePrev) and (ePrev.Curr.X = E.Bot.X) and
4006         (ePrev.Curr.Y = E.Bot.Y) and assigned(Op) and
4007         (ePrev.OutIdx >= 0) and (ePrev.Curr.Y > ePrev.Top.Y) and
4008         SlopesEqual(E.Curr, E.Top, ePrev.Curr, ePrev.Top, FUse64BitRange) and
4009         (E.WindDelta <> 0) and (ePrev.WindDelta <> 0) then
4010       begin
4011         Op2 := AddOutPt(ePrev, E.Bot);
4012         AddJoin(Op, Op2, E.Top);
4013       end
4014       else if Assigned(eNext) and (eNext.Curr.X = E.Bot.X) and
4015         (eNext.Curr.Y = E.Bot.Y) and assigned(Op) and
4016           (eNext.OutIdx >= 0) and (eNext.Curr.Y > eNext.Top.Y) and
4017         SlopesEqual(E.Curr, E.Top, eNext.Curr, eNext.Top, FUse64BitRange) and
4018         (E.WindDelta <> 0) and (eNext.WindDelta <> 0) then
4019       begin
4020         Op2 := AddOutPt(eNext, E.Bot);
4021         AddJoin(Op, Op2, E.Top);
4022       end;
4023     end;
4024     E := E.NextInAEL;
4025   end;
4026 end;
4027 //------------------------------------------------------------------------------
4028 
BuildResultnull4029 function TClipper.BuildResult: TPaths;
4030 var
4031   I, J, K, Cnt: Integer;
4032   OutRec: POutRec;
4033   Op: POutPt;
4034 begin
4035   J := 0;
4036   SetLength(Result, FPolyOutList.Count);
4037   for I := 0 to FPolyOutList.Count -1 do
4038     if Assigned(fPolyOutList[I]) then
4039     begin
4040       OutRec := FPolyOutList[I];
4041       if not assigned(OutRec.Pts) then Continue;
4042 
4043       Op := OutRec.Pts.Prev;
4044       Cnt := PointCount(Op);
4045       if (Cnt < 2) then Continue;
4046       SetLength(Result[J], Cnt);
4047       for K := 0 to Cnt -1 do
4048       begin
4049         Result[J][K] := Op.Pt;
4050         Op := Op.Prev;
4051       end;
4052       Inc(J);
4053     end;
4054   SetLength(Result, J);
4055 end;
4056 //------------------------------------------------------------------------------
4057 
BuildResult2null4058 function TClipper.BuildResult2(PolyTree: TPolyTree): Boolean;
4059 var
4060   I, J, Cnt, CntAll: Integer;
4061   Op: POutPt;
4062   OutRec: POutRec;
4063   PolyNode: TPolyNode;
4064 begin
4065   try
4066     PolyTree.Clear;
4067     SetLength(PolyTree.FAllNodes, FPolyOutList.Count);
4068 
4069     //add PolyTree ...
4070     CntAll := 0;
4071     for I := 0 to FPolyOutList.Count -1 do
4072     begin
4073       OutRec := fPolyOutList[I];
4074       Cnt := PointCount(OutRec.Pts);
4075       if (OutRec.IsOpen and (cnt < 2)) or
4076         (not outRec.IsOpen and (cnt < 3)) then Continue;
4077       FixHoleLinkage(OutRec);
4078 
4079       PolyNode := TPolyNode.Create;
4080       PolyTree.FAllNodes[CntAll] := PolyNode;
4081       OutRec.PolyNode := PolyNode;
4082       Inc(CntAll);
4083       SetLength(PolyNode.FPath, Cnt);
4084       Op := OutRec.Pts.Prev;
4085       for J := 0 to Cnt -1 do
4086       begin
4087         PolyNode.FPath[J] := Op.Pt;
4088         Op := Op.Prev;
4089       end;
4090     end;
4091 
4092     //fix Poly links ...
4093     SetLength(PolyTree.FAllNodes, CntAll);
4094     SetLength(PolyTree.FChilds, CntAll);
4095     for I := 0 to FPolyOutList.Count -1 do
4096     begin
4097       OutRec := fPolyOutList[I];
4098       if Assigned(OutRec.PolyNode) then
4099       begin
4100         if OutRec.IsOpen then
4101         begin
4102           OutRec.PolyNode.FIsOpen := true;
4103           PolyTree.AddChild(OutRec.PolyNode);
4104         end
4105         else if Assigned(OutRec.FirstLeft) and
4106           assigned(OutRec.FirstLeft.PolyNode)then
4107           OutRec.FirstLeft.PolyNode.AddChild(OutRec.PolyNode)
4108         else
4109           PolyTree.AddChild(OutRec.PolyNode);
4110       end;
4111     end;
4112     SetLength(PolyTree.FChilds, PolyTree.FCount);
4113     Result := True;
4114   except
4115     Result := False;
4116   end;
4117 end;
4118 //------------------------------------------------------------------------------
4119 
4120 procedure TClipper.FixupOutPolyline(OutRec: POutRec);
4121 var
4122   PP, LastPP, TmpPP: POutPt;
4123 begin
4124   //remove duplicate points ...
4125   PP := OutRec.Pts;
4126   LastPP := PP.Prev;
4127   while (PP <> LastPP) do
4128   begin
4129     PP := PP.Next;
4130     //strip duplicate points ...
4131     if PointsEqual(PP.Pt, PP.Prev.Pt) then
4132     begin
4133       if PP = LastPP then LastPP := PP.Prev;
4134       TmpPP := PP.Prev;
4135       TmpPP.Next := PP.Next;
4136       PP.Next.Prev := TmpPP;
4137       dispose(PP);
4138       PP := TmpPP;
4139     end;
4140   end;
4141 
4142   if (PP = PP.Prev) then
4143   begin
4144     Dispose(PP);
4145     OutRec.Pts := nil;
4146     Exit;
4147   end;
4148 
4149 end;
4150 //------------------------------------------------------------------------------
4151 
4152 procedure TClipper.FixupOutPolygon(OutRec: POutRec);
4153 var
4154   PP, Tmp, LastOK: POutPt;
4155   PreserveCol: Boolean;
4156 begin
4157   //remove duplicate points and collinear edges
4158   LastOK := nil;
4159   OutRec.BottomPt := nil; //flag as stale
4160   PP := OutRec.Pts;
4161   PreserveCol := FPreserveCollinear or FStrictSimple;
4162   while True do
4163   begin
4164     if (PP = PP.Prev) or (PP.Next = PP.Prev) then
4165     begin
4166       DisposePolyPts(PP);
4167       OutRec.Pts := nil;
4168       Exit;
4169     end;
4170 
4171     //test for duplicate points and collinear edges ...
4172     if PointsEqual(PP.Pt, PP.Next.Pt) or PointsEqual(PP.Pt, PP.Prev.Pt) or
4173       (SlopesEqual(PP.Prev.Pt, PP.Pt, PP.Next.Pt, FUse64BitRange) and
4174       (not PreserveCol or
4175       not Pt2IsBetweenPt1AndPt3(PP.Prev.Pt, PP.Pt, PP.Next.Pt))) then
4176     begin
4177       //OK, we need to delete a point ...
4178       LastOK := nil;
4179       Tmp := PP;
4180       PP.Prev.Next := PP.Next;
4181       PP.Next.Prev := PP.Prev;
4182       PP := PP.Prev;
4183       dispose(Tmp);
4184     end
4185     else if PP = LastOK then Break
4186     else
4187     begin
4188       if not Assigned(LastOK) then LastOK := PP;
4189       PP := PP.Next;
4190     end;
4191   end;
4192   OutRec.Pts := PP;
4193 end;
4194 //------------------------------------------------------------------------------
4195 
4196 function EdgesAdjacent(Inode: PIntersectNode): Boolean; {$IFDEF INLINING} inline; {$ENDIF}
4197 begin
4198   Result := (Inode.Edge1.NextInSEL = Inode.Edge2) or
4199     (Inode.Edge1.PrevInSEL = Inode.Edge2);
4200 end;
4201 //------------------------------------------------------------------------------
4202 
4203 function IntersectListSort(Node1, Node2: Pointer): Integer;
4204 var
4205   i: cInt;
4206 begin
4207   i := PIntersectNode(Node2).Pt.Y - PIntersectNode(Node1).Pt.Y;
4208   if i < 0 then Result := -1
4209   else if i > 0 then Result := 1
4210   else Result := 0;
4211 end;
4212 //------------------------------------------------------------------------------
4213 
FixupIntersectionOrdernull4214 function TClipper.FixupIntersectionOrder: Boolean;
4215 var
4216   I, J, Cnt: Integer;
4217   Node: PIntersectNode;
4218 begin
4219   //pre-condition: intersections are sorted bottom-most first.
4220   //Now it's crucial that intersections are made only between adjacent edges,
4221   //and to ensure this the order of intersections may need adjusting ...
4222   Result := True;
4223   Cnt := FIntersectList.Count;
4224   if Cnt < 2 then exit;
4225 
4226   CopyAELToSEL;
4227   {$IFDEF USEGENERICS}
4228   FIntersectList.Sort(TComparer<PIntersectNode>.Construct(
4229     function (const Node1, Node2 : PIntersectNode) : integer
4230     var
4231       i: cInt;
4232     begin
4233       i := PIntersectNode(Node2).Pt.Y - PIntersectNode(Node1).Pt.Y;
4234       if i < 0 then Result := -1
4235       else if i > 0 then Result := 1
4236       else Result := 0;
4237     end
4238     ));
4239   {$ELSE}
4240   FIntersectList.Sort(IntersectListSort);
4241   {$ENDIF}
4242   for I := 0 to Cnt - 1 do
4243   begin
4244     if not EdgesAdjacent(FIntersectList[I]) then
4245     begin
4246       J := I + 1;
4247       while (J < Cnt) and not EdgesAdjacent(FIntersectList[J]) do inc(J);
4248       if J = Cnt then
4249       begin
4250         Result := False;
4251         Exit; //error!!
4252       end;
4253       //Swap IntersectNodes ...
4254       Node := FIntersectList[I];
4255       FIntersectList[I] := FIntersectList[J];
4256       FIntersectList[J] := Node;
4257     end;
4258     with PIntersectNode(FIntersectList[I])^ do
4259       SwapPositionsInSEL(Edge1, Edge2);
4260   end;
4261 end;
4262 //------------------------------------------------------------------------------
4263 
DupOutPtnull4264 function DupOutPt(OutPt: POutPt; InsertAfter: Boolean = true): POutPt;
4265 begin
4266   new(Result);
4267   Result.Pt := OutPt.Pt;
4268   Result.Idx := OutPt.Idx;
4269   if InsertAfter then
4270   begin
4271     Result.Next := OutPt.Next;
4272     Result.Prev := OutPt;
4273     OutPt.Next.Prev := Result;
4274     OutPt.Next := Result;
4275   end else
4276   begin
4277     Result.Prev := OutPt.Prev;
4278     Result.Next := OutPt;
4279     OutPt.Prev.Next := Result;
4280     OutPt.Prev := Result;
4281   end;
4282 end;
4283 //------------------------------------------------------------------------------
4284 
JoinHorznull4285 function JoinHorz(Op1, Op1b, Op2, Op2b: POutPt;
4286   const Pt: TIntPoint; DiscardLeft: Boolean): Boolean;
4287 var
4288   Dir1, Dir2: TDirection;
4289 begin
4290   if Op1.Pt.X > Op1b.Pt.X then Dir1 := dRightToLeft else Dir1 := dLeftToRight;
4291   if Op2.Pt.X > Op2b.Pt.X then Dir2 := dRightToLeft else Dir2 := dLeftToRight;
4292   Result := Dir1 <> Dir2;
4293   if not Result then Exit;
4294 
4295   //When DiscardLeft, we want Op1b to be on the left of Op1, otherwise we
4296   //want Op1b to be on the right. (And likewise with Op2 and Op2b.)
4297   //To facilitate this while inserting Op1b & Op2b when DiscardLeft == true,
4298   //make sure we're either AT or RIGHT OF Pt before adding Op1b, otherwise
4299   //make sure we're AT or LEFT OF Pt. (Likewise with Op2b.)
4300   if Dir1 = dLeftToRight then
4301   begin
4302     while (Op1.Next.Pt.X <= Pt.X) and
4303       (Op1.Next.Pt.X >= Op1.Pt.X) and (Op1.Next.Pt.Y = Pt.Y) do
4304       Op1 := Op1.Next;
4305     if DiscardLeft and (Op1.Pt.X <> Pt.X) then Op1 := Op1.Next;
4306     Op1b := DupOutPt(Op1, not DiscardLeft);
4307     if not PointsEqual(Op1b.Pt, Pt) then
4308     begin
4309       Op1 := Op1b;
4310       Op1.Pt := Pt;
4311       Op1b := DupOutPt(Op1, not DiscardLeft);
4312     end;
4313   end else
4314   begin
4315     while (Op1.Next.Pt.X >= Pt.X) and
4316       (Op1.Next.Pt.X <= Op1.Pt.X) and (Op1.Next.Pt.Y = Pt.Y) do
4317       Op1 := Op1.Next;
4318     if not DiscardLeft and (Op1.Pt.X <> Pt.X) then Op1 := Op1.Next;
4319     Op1b := DupOutPt(Op1, DiscardLeft);
4320     if not PointsEqual(Op1b.Pt, Pt) then
4321     begin
4322       Op1 := Op1b;
4323       Op1.Pt := Pt;
4324       Op1b := DupOutPt(Op1, DiscardLeft);
4325     end;
4326   end;
4327 
4328   if Dir2 = dLeftToRight then
4329   begin
4330     while (Op2.Next.Pt.X <= Pt.X) and
4331       (Op2.Next.Pt.X >= Op2.Pt.X) and (Op2.Next.Pt.Y = Pt.Y) do
4332         Op2 := Op2.Next;
4333     if DiscardLeft and (Op2.Pt.X <> Pt.X) then Op2 := Op2.Next;
4334     Op2b := DupOutPt(Op2, not DiscardLeft);
4335     if not PointsEqual(Op2b.Pt, Pt) then
4336     begin
4337       Op2 := Op2b;
4338       Op2.Pt := Pt;
4339       Op2b := DupOutPt(Op2, not DiscardLeft);
4340     end;
4341   end else
4342   begin
4343     while (Op2.Next.Pt.X >= Pt.X) and
4344       (Op2.Next.Pt.X <= Op2.Pt.X) and (Op2.Next.Pt.Y = Pt.Y) do
4345       Op2 := Op2.Next;
4346     if not DiscardLeft and (Op2.Pt.X <> Pt.X) then Op2 := Op2.Next;
4347     Op2b := DupOutPt(Op2, DiscardLeft);
4348     if not PointsEqual(Op2b.Pt, Pt) then
4349     begin
4350       Op2 := Op2b;
4351       Op2.Pt := Pt;
4352       Op2b := DupOutPt(Op2, DiscardLeft);
4353     end;
4354   end;
4355 
4356   if (Dir1 = dLeftToRight) = DiscardLeft then
4357   begin
4358     Op1.Prev := Op2;
4359     Op2.Next := Op1;
4360     Op1b.Next := Op2b;
4361     Op2b.Prev := Op1b;
4362   end
4363   else
4364   begin
4365     Op1.Next := Op2;
4366     Op2.Prev := Op1;
4367     Op1b.Prev := Op2b;
4368     Op2b.Next := Op1b;
4369   end;
4370 end;
4371 //------------------------------------------------------------------------------
4372 
TClipper.JoinPointsnull4373 function TClipper.JoinPoints(Jr: PJoin; OutRec1, OutRec2: POutRec): Boolean;
4374 var
4375   Op1, Op1b, Op2, Op2b: POutPt;
4376   Pt: TIntPoint;
4377   Reverse1, Reverse2, DiscardLeftSide: Boolean;
4378   IsHorizontal: Boolean;
4379   Left, Right: cInt;
4380 begin
4381   Result := False;
4382   Op1 := Jr.OutPt1;
4383   Op2 := Jr.OutPt2;
4384 
4385   //There are 3 kinds of joins for output polygons ...
4386   //1. Horizontal joins where Join.OutPt1 & Join.OutPt2 are vertices anywhere
4387   //along (horizontal) collinear edges (& Join.OffPt is on the same horizontal).
4388   //2. Non-horizontal joins where Join.OutPt1 & Join.OutPt2 are at the same
4389   //location at the bottom of the overlapping segment (& Join.OffPt is above).
4390   //3. StrictlySimple joins where edges touch but are not collinear and where
4391   //Join.OutPt1, Join.OutPt2 & Join.OffPt all share the same point.
4392   IsHorizontal := (Jr.OutPt1.Pt.Y = Jr.OffPt.Y);
4393 
4394   if IsHorizontal and PointsEqual(Jr.OffPt, Jr.OutPt1.Pt) and
4395   PointsEqual(Jr.OffPt, Jr.OutPt2.Pt) then
4396   begin
4397     //Strictly Simple join ...
4398     if (OutRec1 <> OutRec2) then
4399       Exit;
4400 
4401     Op1b := Jr.OutPt1.Next;
4402     while (Op1b <> Op1) and
4403       PointsEqual(Op1b.Pt, Jr.OffPt) do Op1b := Op1b.Next;
4404     Reverse1 := (Op1b.Pt.Y > Jr.OffPt.Y);
4405     Op2b := Jr.OutPt2.Next;
4406     while (Op2b <> Op2) and
4407       PointsEqual(Op2b.Pt, Jr.OffPt) do Op2b := Op2b.Next;
4408     Reverse2 := (Op2b.Pt.Y > Jr.OffPt.Y);
4409     if (Reverse1 = Reverse2) then Exit;
4410 
4411     if Reverse1 then
4412     begin
4413       Op1b := DupOutPt(Op1, False);
4414       Op2b := DupOutPt(Op2, True);
4415       Op1.Prev := Op2;
4416       Op2.Next := Op1;
4417       Op1b.Next := Op2b;
4418       Op2b.Prev := Op1b;
4419       Jr.OutPt1 := Op1;
4420       Jr.OutPt2 := Op1b;
4421       Result := True;
4422     end else
4423     begin
4424       Op1b := DupOutPt(Op1, True);
4425       Op2b := DupOutPt(Op2, False);
4426       Op1.Next := Op2;
4427       Op2.Prev := Op1;
4428       Op1b.Prev := Op2b;
4429       Op2b.Next := Op1b;
4430       Jr.OutPt1 := Op1;
4431       Jr.OutPt2 := Op1b;
4432       Result := True;
4433     end;
4434   end
4435   else if IsHorizontal then
4436   begin
4437     op1b := op1;
4438     while (op1.Prev.Pt.Y = op1.Pt.Y) and
4439       (op1.Prev <> Op1b) and (op1.Prev <> op2) do
4440         op1 := op1.Prev;
4441     while (op1b.Next.Pt.Y = op1b.Pt.Y) and
4442       (op1b.Next <> Op1) and (op1b.Next <> op2) do
4443         op1b := op1b.Next;
4444     if (op1b.Next = Op1) or (op1b.Next = op2) then Exit; //a flat 'polygon'
4445 
4446     op2b := op2;
4447     while (op2.Prev.Pt.Y = op2.Pt.Y) and
4448       (op2.Prev <> Op2b) and (op2.Prev <> op1b) do
4449         op2 := op2.Prev;
4450     while (op2b.Next.Pt.Y = op2b.Pt.Y) and
4451       (op2b.Next <> Op2) and (op2b.Next <> op1) do
4452         op2b := op2b.Next;
4453     if (op2b.Next = Op2) or (op2b.Next = op1) then Exit; //a flat 'polygon'
4454 
4455     //Op1 --> Op1b & Op2 --> Op2b are the extremites of the horizontal edges
4456     if not GetOverlap(Op1.Pt.X, Op1b.Pt.X, Op2.Pt.X, Op2b.Pt.X, Left, Right) then
4457       Exit;
4458 
4459     //DiscardLeftSide: when joining overlapping edges, a spike will be created
4460     //which needs to be cleaned up. However, we don't want Op1 or Op2 caught up
4461     //on the discard side as either may still be needed for other joins ...
4462     if (Op1.Pt.X >= Left) and (Op1.Pt.X <= Right) then
4463     begin
4464       Pt := Op1.Pt; DiscardLeftSide := Op1.Pt.X > Op1b.Pt.X;
4465     end else if (Op2.Pt.X >= Left) and (Op2.Pt.X <= Right) then
4466     begin
4467       Pt := Op2.Pt; DiscardLeftSide := Op2.Pt.X > Op2b.Pt.X;
4468     end else if (Op1b.Pt.X >= Left) and (Op1b.Pt.X <= Right) then
4469     begin
4470       Pt := Op1b.Pt; DiscardLeftSide := Op1b.Pt.X > Op1.Pt.X;
4471     end else
4472     begin
4473       Pt := Op2b.Pt; DiscardLeftSide := Op2b.Pt.X > Op2.Pt.X;
4474     end;
4475 
4476     Result := JoinHorz(Op1, Op1b, Op2, Op2b, Pt, DiscardLeftSide);
4477     if not Result then Exit;
4478     Jr.OutPt1 := Op1;
4479     Jr.OutPt2 := Op2;
4480   end else
4481   begin
4482     //make sure the polygons are correctly oriented ...
4483     Op1b := Op1.Next;
4484     while PointsEqual(Op1b.Pt, Op1.Pt) and (Op1b <> Op1) do Op1b := Op1b.Next;
4485     Reverse1 := (Op1b.Pt.Y > Op1.Pt.Y) or
4486       not SlopesEqual(Op1.Pt, Op1b.Pt, Jr.OffPt, FUse64BitRange);
4487     if Reverse1 then
4488     begin
4489       Op1b := Op1.Prev;
4490       while PointsEqual(Op1b.Pt, Op1.Pt) and (Op1b <> Op1) do Op1b := Op1b.Prev;
4491       if (Op1b.Pt.Y > Op1.Pt.Y) or
4492         not SlopesEqual(Op1.Pt, Op1b.Pt, Jr.OffPt, FUse64BitRange) then Exit;
4493     end;
4494     Op2b := Op2.Next;
4495     while PointsEqual(Op2b.Pt, Op2.Pt) and (Op2b <> Op2) do Op2b := Op2b.Next;
4496     Reverse2 := (Op2b.Pt.Y > Op2.Pt.Y) or
4497       not SlopesEqual(Op2.Pt, Op2b.Pt, Jr.OffPt, FUse64BitRange);
4498     if Reverse2 then
4499     begin
4500       Op2b := Op2.Prev;
4501       while PointsEqual(Op2b.Pt, Op2.Pt) and (Op2b <> Op2) do Op2b := Op2b.Prev;
4502       if (Op2b.Pt.Y > Op2.Pt.Y) or
4503         not SlopesEqual(Op2.Pt, Op2b.Pt, Jr.OffPt, FUse64BitRange) then Exit;
4504     end;
4505 
4506     if (Op1b = Op1) or (Op2b = Op2) or (Op1b = Op2b) or
4507       ((OutRec1 = OutRec2) and (Reverse1 = Reverse2)) then Exit;
4508 
4509     if Reverse1 then
4510     begin
4511       Op1b := DupOutPt(Op1, False);
4512       Op2b := DupOutPt(Op2, True);
4513       Op1.Prev := Op2;
4514       Op2.Next := Op1;
4515       Op1b.Next := Op2b;
4516       Op2b.Prev := Op1b;
4517       Jr.OutPt1 := Op1;
4518       Jr.OutPt2 := Op1b;
4519       Result := True;
4520     end else
4521     begin
4522       Op1b := DupOutPt(Op1, True);
4523       Op2b := DupOutPt(Op2, False);
4524       Op1.Next := Op2;
4525       Op2.Prev := Op1;
4526       Op1b.Prev := Op2b;
4527       Op2b.Next := Op1b;
4528       Jr.OutPt1 := Op1;
4529       Jr.OutPt2 := Op1b;
4530       Result := True;
4531     end;
4532   end;
4533 end;
4534 //------------------------------------------------------------------------------
4535 
4536 function ParseFirstLeft(FirstLeft: POutRec): POutRec;
4537 begin
4538   while Assigned(FirstLeft) and not Assigned(FirstLeft.Pts) do
4539     FirstLeft := FirstLeft.FirstLeft;
4540   Result := FirstLeft;
4541 end;
4542 //------------------------------------------------------------------------------
4543 
4544 procedure TClipper.FixupFirstLefts1(OldOutRec, NewOutRec: POutRec);
4545 var
4546   I: Integer;
4547   outRec: POutRec;
4548   firstLeft: POutRec;
4549 begin
4550   //tests if NewOutRec contains the polygon before reassigning FirstLeft
4551   for I := 0 to FPolyOutList.Count -1 do
4552   begin
4553     outRec := fPolyOutList[I];
4554     firstLeft := ParseFirstLeft(outRec.FirstLeft);
4555     if Assigned(outRec.Pts) and (firstLeft = OldOutRec) then
4556     begin
4557       if Poly2ContainsPoly1(outRec.Pts, NewOutRec.Pts) then
4558         outRec.FirstLeft := NewOutRec;
4559     end;
4560   end;
4561 end;
4562 //------------------------------------------------------------------------------
4563 
4564 procedure TClipper.FixupFirstLefts2(InnerOutRec, OuterOutRec: POutRec);
4565 var
4566   I: Integer;
4567   orfl, orec: POutRec;
4568   firstLeft: POutRec;
4569 begin
4570   //A polygon has split into two such that one is now the inner of the other.
4571   //It's possible that these polygons now wrap around other polygons, so check
4572   //every polygon that's also contained by OuterOutRec's FirstLeft container
4573   //(including nil) to see if they've become inner to the new inner polygon ...
4574   orfl := OuterOutRec.FirstLeft;
4575   for I := 0 to FPolyOutList.Count -1 do
4576   begin
4577     orec := POutRec(fPolyOutList[I]);
4578     if not Assigned(orec.Pts) or
4579       (orec = OuterOutRec) or (orec = InnerOutRec) then continue;
4580     firstLeft := ParseFirstLeft(orec.FirstLeft);
4581     if (firstLeft <> orfl) and (firstLeft <> InnerOutRec) and
4582       (firstLeft <> OuterOutRec) then Continue;
4583     if Poly2ContainsPoly1(orec.Pts, InnerOutRec.Pts) then
4584       orec.FirstLeft := InnerOutRec
4585     else if Poly2ContainsPoly1(orec.Pts, OuterOutRec.Pts) then
4586       orec.FirstLeft := OuterOutRec
4587     else if (orec.FirstLeft = InnerOutRec) or
4588       (orec.FirstLeft = OuterOutRec) then
4589         orec.FirstLeft := orfl;
4590   end;
4591 end;
4592 //------------------------------------------------------------------------------
4593 
4594 procedure TClipper.FixupFirstLefts3(OldOutRec, NewOutRec: POutRec);
4595 var
4596   I: Integer;
4597   outRec: POutRec;
4598   firstLeft: POutRec;
4599 begin
4600   //same as FixupFirstLefts1 but doesn't call Poly2ContainsPoly1()
4601   for I := 0 to FPolyOutList.Count -1 do
4602   begin
4603     outRec := fPolyOutList[I];
4604     firstLeft := ParseFirstLeft(outRec.FirstLeft);
4605     if Assigned(outRec.Pts) and (firstLeft = OldOutRec) then
4606        outRec.FirstLeft := NewOutRec;
4607   end;
4608 end;
4609 //------------------------------------------------------------------------------
4610 
4611 procedure TClipper.JoinCommonEdges;
4612 var
4613   I: Integer;
4614   Jr: PJoin;
4615   OutRec1, OutRec2, HoleStateRec: POutRec;
4616 begin
4617   for I := 0 to FJoinList.count -1 do
4618   begin
4619     Jr := FJoinList[I];
4620 
4621     OutRec1 := GetOutRec(Jr.OutPt1.Idx);
4622     OutRec2 := GetOutRec(Jr.OutPt2.Idx);
4623 
4624     if not Assigned(OutRec1.Pts) or not Assigned(OutRec2.Pts) then Continue;
4625     if OutRec1.IsOpen or OutRec2.IsOpen then Continue;
4626 
4627     //get the polygon fragment with the correct hole state (FirstLeft)
4628     //before calling JoinPoints() ...
4629     if OutRec1 = OutRec2 then HoleStateRec := OutRec1
4630     else if OutRec1RightOfOutRec2(OutRec1, OutRec2) then HoleStateRec := OutRec2
4631     else if OutRec1RightOfOutRec2(OutRec2, OutRec1) then HoleStateRec := OutRec1
4632     else HoleStateRec := GetLowermostRec(OutRec1, OutRec2);
4633 
4634     if not JoinPoints(Jr, OutRec1, OutRec2) then Continue;
4635 
4636     if (OutRec1 = OutRec2) then
4637     begin
4638       //instead of joining two polygons, we've just created a new one by
4639       //splitting one polygon into two.
4640       OutRec1.Pts := Jr.OutPt1;
4641       OutRec1.BottomPt := nil;
4642       OutRec2 := CreateOutRec;
4643       OutRec2.Pts := Jr.OutPt2;
4644 
4645       //update all OutRec2.Pts idx's ...
4646       UpdateOutPtIdxs(OutRec2);
4647 
4648       //sort out the hole states of both polygon ...
4649       if Poly2ContainsPoly1(OutRec2.Pts, OutRec1.Pts) then
4650       begin
4651         //OutRec1 contains OutRec2 ...
4652         OutRec2.IsHole := not OutRec1.IsHole;
4653         OutRec2.FirstLeft := OutRec1;
4654 
4655         if FUsingPolyTree then
4656           FixupFirstLefts2(OutRec2, OutRec1);
4657 
4658         if (OutRec2.IsHole xor FReverseOutput) = (Area(OutRec2) > 0) then
4659             ReversePolyPtLinks(OutRec2.Pts);
4660       end else if Poly2ContainsPoly1(OutRec1.Pts, OutRec2.Pts) then
4661       begin
4662         //OutRec2 contains OutRec1 ...
4663         OutRec2.IsHole := OutRec1.IsHole;
4664         OutRec1.IsHole := not OutRec2.IsHole;
4665         OutRec2.FirstLeft := OutRec1.FirstLeft;
4666         OutRec1.FirstLeft := OutRec2;
4667         if FUsingPolyTree then
4668           FixupFirstLefts2(OutRec1, OutRec2);
4669 
4670         if (OutRec1.IsHole xor FReverseOutput) = (Area(OutRec1) > 0) then
4671           ReversePolyPtLinks(OutRec1.Pts);
4672       end else
4673       begin
4674         //the 2 polygons are completely separate ...
4675         OutRec2.IsHole := OutRec1.IsHole;
4676         OutRec2.FirstLeft := OutRec1.FirstLeft;
4677 
4678         //fixup FirstLeft pointers that may need reassigning to OutRec2
4679         if FUsingPolyTree then FixupFirstLefts1(OutRec1, OutRec2);
4680       end;
4681     end else
4682     begin
4683       //joined 2 polygons together ...
4684 
4685       //delete the obsolete pointer ...
4686       OutRec2.Pts := nil;
4687       OutRec2.BottomPt := nil;
4688       OutRec2.Idx := OutRec1.Idx;
4689 
4690       OutRec1.IsHole := HoleStateRec.IsHole;
4691       if HoleStateRec = OutRec2 then
4692         OutRec1.FirstLeft := OutRec2.FirstLeft;
4693       OutRec2.FirstLeft := OutRec1;
4694 
4695       if FUsingPolyTree then
4696         FixupFirstLefts3(OutRec2, OutRec1);
4697     end;
4698   end;
4699 end;
4700 //------------------------------------------------------------------------------
4701 
4702 procedure TClipper.DoSimplePolygons;
4703 var
4704   I: Integer;
4705   OutRec1, OutRec2: POutRec;
4706   Op, Op2, Op3, Op4: POutPt;
4707 begin
4708   I := 0;
4709   while I < FPolyOutList.Count do
4710   begin
4711     OutRec1 := POutRec(fPolyOutList[I]);
4712     inc(I);
4713     Op := OutRec1.Pts;
4714     if not assigned(Op) or OutRec1.IsOpen then Continue;
4715     repeat //for each Pt in Path until duplicate found do ...
4716       Op2 := Op.Next;
4717       while (Op2 <> OutRec1.Pts) do
4718       begin
4719         if (PointsEqual(Op.Pt, Op2.Pt) and
4720           (Op2.Next <> Op) and (Op2.Prev <> Op)) then
4721         begin
4722           //split the polygon into two ...
4723           Op3 := Op.Prev;
4724           Op4 := Op2.Prev;
4725           Op.Prev := Op4;
4726           Op4.Next := Op;
4727           Op2.Prev := Op3;
4728           Op3.Next := Op2;
4729 
4730           OutRec1.Pts := Op;
4731 
4732           OutRec2 := CreateOutRec;
4733           OutRec2.Pts := Op2;
4734           UpdateOutPtIdxs(OutRec2);
4735           if Poly2ContainsPoly1(OutRec2.Pts, OutRec1.Pts) then
4736           begin
4737             //OutRec2 is contained by OutRec1 ...
4738             OutRec2.IsHole := not OutRec1.IsHole;
4739             OutRec2.FirstLeft := OutRec1;
4740             if FUsingPolyTree then FixupFirstLefts2(OutRec2, OutRec1);
4741           end
4742           else
4743           if Poly2ContainsPoly1(OutRec1.Pts, OutRec2.Pts) then
4744           begin
4745             //OutRec1 is contained by OutRec2 ...
4746             OutRec2.IsHole := OutRec1.IsHole;
4747             OutRec1.IsHole := not OutRec2.IsHole;
4748             OutRec2.FirstLeft := OutRec1.FirstLeft;
4749             OutRec1.FirstLeft := OutRec2;
4750             if FUsingPolyTree then FixupFirstLefts2(OutRec1, OutRec2);
4751           end else
4752           begin
4753             //the 2 polygons are separate ...
4754             OutRec2.IsHole := OutRec1.IsHole;
4755             OutRec2.FirstLeft := OutRec1.FirstLeft;
4756             if FUsingPolyTree then FixupFirstLefts1(OutRec1, OutRec2);
4757           end;
4758           Op2 := Op; //ie get ready for the next iteration
4759         end;
4760         Op2 := Op2.Next;
4761       end;
4762       Op := Op.Next;
4763     until (Op = OutRec1.Pts);
4764   end;
4765 end;
4766 
4767 //------------------------------------------------------------------------------
4768 // TClipperOffset methods
4769 //------------------------------------------------------------------------------
4770 
4771 constructor TClipperOffset.Create(
4772   MiterLimit: Double = 2;
4773   ArcTolerance: Double = def_arc_tolerance);
4774 begin
4775   inherited Create;
4776   FPolyNodes := TPolyNode.Create;
4777   FLowest.X := -1;
4778   FMiterLimit := MiterLimit;
4779   FArcTolerance := ArcTolerance;
4780 end;
4781 //------------------------------------------------------------------------------
4782 
4783 destructor TClipperOffset.Destroy;
4784 begin
4785   Clear;
4786   FPolyNodes.Free;
4787   inherited;
4788 end;
4789 //------------------------------------------------------------------------------
4790 
4791 procedure TClipperOffset.Clear;
4792 var
4793   I: Integer;
4794   PolyNode: TPolyNode;
4795 begin
4796   for I := 0 to FPolyNodes.ChildCount -1 do
4797     begin
4798       PolyNode:= FPolyNodes.Childs[I];
4799       PolyNode.Free;
4800     end;
4801   FPolyNodes.FCount := 0;
4802   FPolyNodes.FBuffLen := 16;
4803   SetLength(FPolyNodes.FChilds, 16);
4804   FLowest.X := -1;
4805 end;
4806 //------------------------------------------------------------------------------
4807 
4808 procedure TClipperOffset.AddPath(const Path: TPath;
4809   JoinType: TJoinType; EndType: TEndType);
4810 var
4811   I, J, K, HighI: Integer;
4812   NewNode: TPolyNode;
4813   ip: TIntPoint;
4814 begin
4815   HighI := High(Path);
4816   if HighI < 0 then Exit;
4817   NewNode := TPolyNode.Create;
4818   NewNode.FJoinType := JoinType;
4819   NewNode.FEndType := EndType;
4820 
4821   //strip duplicate points from path and also get index to the lowest point ...
4822   if EndType in [etClosedLine, etClosedPolygon] then
4823     while (HighI > 0) and PointsEqual(Path[0], Path[HighI]) do dec(HighI);
4824   SetLength(NewNode.FPath, HighI +1);
4825   NewNode.FPath[0] := Path[0];
4826   J := 0; K := 0;
4827   for I := 1 to HighI do
4828     if not PointsEqual(NewNode.FPath[J], Path[I]) then
4829     begin
4830       inc(J);
4831       NewNode.FPath[J] := Path[I];
4832       if (NewNode.FPath[K].Y < Path[I].Y) or
4833         ((NewNode.FPath[K].Y = Path[I].Y) and
4834         (NewNode.FPath[K].X > Path[I].X)) then
4835           K := J;
4836     end;
4837   inc(J);
4838   if J < HighI +1 then
4839     SetLength(NewNode.FPath, J);
4840   if (EndType = etClosedPolygon) and (J < 3) then
4841   begin
4842     NewNode.free;
4843     Exit;
4844   end;
4845   FPolyNodes.AddChild(NewNode);
4846 
4847   if EndType <> etClosedPolygon then Exit;
4848   //if this path's lowest pt is lower than all the others then update FLowest
4849   if (FLowest.X < 0) then
4850   begin
4851     FLowest := IntPoint(FPolyNodes.ChildCount -1, K);
4852   end else
4853   begin
4854     ip := FPolyNodes.Childs[FLowest.X].FPath[FLowest.Y];
4855     if (NewNode.FPath[K].Y > ip.Y) or
4856       ((NewNode.FPath[K].Y = ip.Y) and
4857       (NewNode.FPath[K].X < ip.X)) then
4858         FLowest := IntPoint(FPolyNodes.ChildCount -1, K);
4859   end;
4860 end;
4861 //------------------------------------------------------------------------------
4862 
4863 procedure TClipperOffset.AddPaths(const Paths: TPaths;
4864   JoinType: TJoinType; EndType: TEndType);
4865 var
4866   I: Integer;
4867 begin
4868   for I := 0 to High(Paths) do AddPath(Paths[I], JoinType, EndType);
4869 end;
4870 //------------------------------------------------------------------------------
4871 
4872 procedure TClipperOffset.FixOrientations;
4873 var
4874   I: Integer;
4875 begin
4876   //fixup orientations of all closed paths if the orientation of the
4877   //closed path with the lowermost vertex is wrong ...
4878   if (FLowest.X >= 0) and
4879     not Orientation(FPolyNodes.Childs[FLowest.X].FPath) then
4880   begin
4881     for I := 0 to FPolyNodes.ChildCount -1 do
4882       if FPolyNodes.Childs[I].FEndType = etClosedPolygon then
4883         FPolyNodes.Childs[I].FPath := ReversePath(FPolyNodes.Childs[I].FPath)
4884       else if (FPolyNodes.Childs[I].FEndType = etClosedLine) and
4885         Orientation(FPolyNodes.Childs[I].FPath) then
4886           FPolyNodes.Childs[I].FPath := ReversePath(FPolyNodes.Childs[I].FPath);
4887   end else
4888   begin
4889     for I := 0 to FPolyNodes.ChildCount -1 do
4890       if (FPolyNodes.Childs[I].FEndType = etClosedLine) and
4891         not Orientation(FPolyNodes.Childs[I].FPath) then
4892           FPolyNodes.Childs[I].FPath := ReversePath(FPolyNodes.Childs[I].FPath);
4893   end;
4894 end;
4895 //------------------------------------------------------------------------------
4896 
4897 procedure TClipperOffset.DoOffset(Delta: Double);
4898 var
4899   I, J, K, Len, solCount: Integer;
4900   X, X2, Y, Steps, AbsDelta: Double;
4901   Node: TPolyNode;
4902   N: TDoublePoint;
4903 begin
4904   FSolution := nil;
4905   FDelta := Delta;
4906   AbsDelta := Abs(Delta);
4907 
4908   //if Zero offset, just copy any CLOSED polygons to FSolution and return ...
4909   if AbsDelta < Tolerance then
4910   begin
4911     solCount := 0;
4912     SetLength(FSolution, FPolyNodes.ChildCount);
4913     for I := 0 to FPolyNodes.ChildCount -1 do
4914       if FPolyNodes.Childs[I].FEndType = etClosedPolygon then
4915       begin
4916         FSolution[solCount] := FPolyNodes.Childs[I].FPath;
4917         inc(solCount);
4918       end;
4919     SetLength(FSolution, solCount);
4920     Exit;
4921   end;
4922 
4923   //FMiterLimit: see offset_triginometry3.svg in the documentation folder ...
4924   if FMiterLimit > 2 then FMiterLim := 2/(sqr(FMiterLimit))
4925   else FMiterLim := 0.5;
4926 
4927   if (FArcTolerance <= 0) then Y := def_arc_tolerance
4928   else if FArcTolerance > AbsDelta * def_arc_tolerance then
4929     Y := AbsDelta * def_arc_tolerance
4930   else Y := FArcTolerance;
4931 
4932   //see offset_triginometry2.svg in the documentation folder ...
4933   Steps := PI / ArcCos(1 - Y / AbsDelta);  //steps per 360 degrees
4934   if (Steps > AbsDelta * Pi) then
4935     Steps := AbsDelta * Pi;                //ie excessive precision check
4936 
4937   Math.SinCos(Two_Pi / Steps, FSin, FCos); //sin & cos per step
4938   if Delta < 0 then FSin := -FSin;
4939   FStepsPerRad := Steps / Two_Pi;
4940 
4941   SetLength(FSolution, FPolyNodes.ChildCount * 2);
4942   solCount := 0;
4943   for I := 0 to FPolyNodes.ChildCount -1 do
4944   begin
4945     Node := FPolyNodes.Childs[I];
4946     FInP := Node.FPath;
4947     Len := length(FInP);
4948 
4949     if (Len = 0) or
4950       ((Delta <= 0) and ((Len < 3) or (Node.FEndType <> etClosedPolygon))) then
4951         Continue;
4952 
4953     FOutPos := 0;
4954     FOutP := nil;
4955 
4956     //if a single vertex then build circle or a square ...
4957     if (Len = 1) then
4958     begin
4959       if Node.FJoinType = jtRound then
4960       begin
4961         X := 1; Y := 0;
4962         for J := 1 to Round(Steps) do
4963         begin
4964           AddPoint(IntPoint(
4965             Round(FInP[0].X + X * FDelta),
4966             Round(FInP[0].Y + Y * FDelta)));
4967           X2 := X;
4968           X := X * FCos - FSin * Y;
4969           Y := X2 * FSin + Y * FCos;
4970         end
4971       end else
4972       begin
4973         X := -1; Y := -1;
4974         for J := 1 to 4 do
4975         begin
4976           AddPoint(IntPoint( Round(FInP[0].X + X * FDelta),
4977             Round(FInP[0].Y + Y * FDelta)));
4978           if X < 0 then X := 1
4979           else if Y < 0 then Y := 1
4980           else X := -1;
4981         end;
4982       end;
4983       SetLength(FOutP, FOutPos);
4984       FSolution[solCount] := FOutP;
4985       Inc(solCount);
4986       Continue;
4987     end;
4988 
4989     //build Normals ...
4990     SetLength(FNorms, Len);
4991     for J := 0 to Len-2 do
4992       FNorms[J] := GetUnitNormal(FInP[J], FInP[J+1]);
4993     if not (Node.FEndType in [etClosedLine, etClosedPolygon]) then
4994       FNorms[Len-1] := FNorms[Len-2] else
4995       FNorms[Len-1] := GetUnitNormal(FInP[Len-1], FInP[0]);
4996 
4997     if Node.FEndType = etClosedPolygon then
4998     begin
4999       K := Len -1;
5000       for J := 0 to Len-1 do
5001         OffsetPoint(J, K, Node.FJoinType);
5002       SetLength(FOutP, FOutPos);
5003       FSolution[solCount] := FOutP;
5004       Inc(solCount);
5005     end
5006     else if (Node.FEndType = etClosedLine) then
5007     begin
5008       K := Len -1;
5009       for J := 0 to Len-1 do
5010         OffsetPoint(J, K, Node.FJoinType);
5011       SetLength(FOutP, FOutPos);
5012       FSolution[solCount] := FOutP;
5013       Inc(solCount);
5014 
5015       FOutPos := 0;
5016       FOutP := nil;
5017 
5018       //re-build Normals ...
5019       N := FNorms[Len - 1];
5020       for J := Len-1 downto 1 do
5021       begin
5022         FNorms[J].X := -FNorms[J-1].X;
5023         FNorms[J].Y := -FNorms[J-1].Y;
5024       end;
5025       FNorms[0].X := -N.X;
5026       FNorms[0].Y := -N.Y;
5027 
5028       K := 0;
5029       for J := Len-1 downto 0 do
5030         OffsetPoint(J, K, Node.FJoinType);
5031       SetLength(FOutP, FOutPos);
5032 
5033       FSolution[solCount] := FOutP;
5034       Inc(solCount);
5035     end else
5036     begin
5037       //offset the polyline going forward ...
5038       K := 0;
5039       for J := 1 to Len-2 do
5040         OffsetPoint(J, K, Node.FJoinType);
5041 
5042       //handle the end (butt, round or square) ...
5043       if Node.FEndType = etOpenButt then
5044       begin
5045         J := Len - 1;
5046         AddPoint(IntPoint(round(FInP[J].X + FNorms[J].X *FDelta),
5047           round(FInP[J].Y + FNorms[J].Y * FDelta)));
5048         AddPoint(IntPoint(round(FInP[J].X - FNorms[J].X *FDelta),
5049           round(FInP[J].Y - FNorms[J].Y * FDelta)));
5050       end else
5051       begin
5052         J := Len - 1;
5053         K := Len - 2;
5054         FNorms[J].X := -FNorms[J].X;
5055         FNorms[J].Y := -FNorms[J].Y;
5056         FSinA := 0;
5057         if Node.FEndType = etOpenSquare then
5058           DoSquare(J, K) else
5059           DoRound(J, K);
5060       end;
5061 
5062       //re-build Normals ...
5063       for J := Len-1 downto 1 do
5064       begin
5065         FNorms[J].X := -FNorms[J-1].X;
5066         FNorms[J].Y := -FNorms[J-1].Y;
5067       end;
5068       FNorms[0].X := -FNorms[1].X;
5069       FNorms[0].Y := -FNorms[1].Y;
5070 
5071       //offset the polyline going backward ...
5072       K := Len -1;
5073       for J := Len -2 downto 1 do
5074         OffsetPoint(J, K, Node.FJoinType);
5075 
5076       //finally handle the start (butt, round or square) ...
5077       if Node.FEndType = etOpenButt then
5078       begin
5079         AddPoint(IntPoint(round(FInP[0].X - FNorms[0].X *FDelta),
5080           round(FInP[0].Y - FNorms[0].Y * FDelta)));
5081         AddPoint(IntPoint(round(FInP[0].X + FNorms[0].X *FDelta),
5082           round(FInP[0].Y + FNorms[0].Y * FDelta)));
5083       end else
5084       begin
5085         FSinA := 0;
5086         if Node.FEndType = etOpenSquare then
5087           DoSquare(0, 1) else
5088           DoRound(0, 1);
5089       end;
5090       SetLength(FOutP, FOutPos);
5091       FSolution[solCount] := FOutP;
5092       Inc(solCount);
5093     end;
5094   end;
5095   SetLength(FSolution, solCount);
5096 end;
5097 //------------------------------------------------------------------------------
5098 
5099 procedure TClipperOffset.Execute(out solution: TPaths; Delta: Double);
5100 var
5101   I, Len: Integer;
5102   Outer: TPath;
5103   Bounds: TIntRect;
5104 begin
5105   FixOrientations;
5106   DoOffset(Delta);
5107   //now clean up 'corners' ...
5108   with TClipper.Create do
5109   try
5110     AddPaths(FSolution, ptSubject, True);
5111     if Delta > 0 then
5112     begin
5113       Execute(ctUnion, solution, pftPositive, pftPositive);
5114     end else
5115     begin
5116       Bounds := GetBounds(FSolution);
5117       SetLength(Outer, 4);
5118       Outer[0] := IntPoint(Bounds.left-10, Bounds.bottom+10);
5119       Outer[1] := IntPoint(Bounds.right+10, Bounds.bottom+10);
5120       Outer[2] := IntPoint(Bounds.right+10, Bounds.top-10);
5121       Outer[3] := IntPoint(Bounds.left-10, Bounds.top-10);
5122       AddPath(Outer, ptSubject, True);
5123       ReverseSolution := True;
5124       Execute(ctUnion, solution, pftNegative, pftNegative);
5125       //delete the outer rectangle ...
5126       Len := length(solution);
5127       for I := 1 to Len -1 do solution[I-1] := solution[I];
5128       if Len > 0 then SetLength(solution, Len -1);
5129     end;
5130   finally
5131     free;
5132   end;
5133 end;
5134 //------------------------------------------------------------------------------
5135 
5136 procedure TClipperOffset.Execute(out solution: TPolyTree; Delta: Double);
5137 var
5138   I: Integer;
5139   Outer: TPath;
5140   Bounds: TIntRect;
5141   OuterNode: TPolyNode;
5142 begin
5143   if not assigned(solution) then
5144     raise exception.Create(rsClipperOffset);
5145   solution.Clear;
5146 
5147   FixOrientations;
5148   DoOffset(Delta);
5149 
5150   //now clean up 'corners' ...
5151   with TClipper.Create do
5152   try
5153     AddPaths(FSolution, ptSubject, True);
5154     if Delta > 0 then
5155     begin
5156       Execute(ctUnion, solution, pftPositive, pftPositive);
5157     end else
5158     begin
5159       Bounds := GetBounds(FSolution);
5160       SetLength(Outer, 4);
5161       Outer[0] := IntPoint(Bounds.left-10, Bounds.bottom+10);
5162       Outer[1] := IntPoint(Bounds.right+10, Bounds.bottom+10);
5163       Outer[2] := IntPoint(Bounds.right+10, Bounds.top-10);
5164       Outer[3] := IntPoint(Bounds.left-10, Bounds.top-10);
5165       AddPath(Outer, ptSubject, True);
5166       ReverseSolution := True;
5167       Execute(ctUnion, solution, pftNegative, pftNegative);
5168       //remove the outer PolyNode rectangle ...
5169       if (solution.ChildCount = 1) and (solution.Childs[0].ChildCount > 0) then
5170       begin
5171         OuterNode := solution.Childs[0];
5172         SetLength(solution.FChilds, OuterNode.ChildCount);
5173         solution.FChilds[0] := OuterNode.Childs[0];
5174         solution.FChilds[0].FParent := solution;
5175         for I := 1 to OuterNode.ChildCount -1 do
5176           solution.AddChild(OuterNode.Childs[I]);
5177       end else
5178         solution.Clear;
5179     end;
5180   finally
5181     free;
5182   end;
5183 end;
5184 //------------------------------------------------------------------------------
5185 
5186 procedure TClipperOffset.AddPoint(const Pt: TIntPoint);
5187 const
5188   BuffLength = 32;
5189 begin
5190   if FOutPos = length(FOutP) then
5191     SetLength(FOutP, FOutPos + BuffLength);
5192   FOutP[FOutPos] := Pt;
5193   Inc(FOutPos);
5194 end;
5195 //------------------------------------------------------------------------------
5196 
5197 procedure TClipperOffset.DoSquare(J, K: Integer);
5198 begin
5199   AddPoint(IntPoint(
5200     round(FInP[J].X + FDelta * (FNorms[K].X - FNorms[K].Y)),
5201     round(FInP[J].Y + FDelta * (FNorms[K].Y + FNorms[K].X))));
5202   AddPoint(IntPoint(
5203     round(FInP[J].X + FDelta * (FNorms[J].X + FNorms[J].Y)),
5204     round(FInP[J].Y + FDelta * (FNorms[J].Y - FNorms[J].X))));
5205 end;
5206 //------------------------------------------------------------------------------
5207 
5208 procedure TClipperOffset.DoMiter(J, K: Integer; R: Double);
5209 var
5210   Q: Double;
5211 begin
5212   Q := FDelta / R;
5213   AddPoint(IntPoint(round(FInP[J].X + (FNorms[K].X + FNorms[J].X)*Q),
5214     round(FInP[J].Y + (FNorms[K].Y + FNorms[J].Y)*Q)));
5215 end;
5216 //------------------------------------------------------------------------------
5217 
5218 procedure TClipperOffset.DoRound(J, K: Integer);
5219 var
5220   I, Steps: Integer;
5221   A, X, X2, Y: Double;
5222 begin
5223   A := ArcTan2(FSinA, FNorms[K].X * FNorms[J].X + FNorms[K].Y * FNorms[J].Y);
5224   Steps := Max(Round(FStepsPerRad * Abs(A)), 1);
5225 
5226   X := FNorms[K].X;
5227   Y := FNorms[K].Y;
5228   for I := 1 to Steps do
5229   begin
5230     AddPoint(IntPoint(
5231       round(FInP[J].X + X * FDelta),
5232       round(FInP[J].Y + Y * FDelta)));
5233     X2 := X;
5234     X := X * FCos - FSin * Y;
5235     Y := X2 * FSin + Y * FCos;
5236   end;
5237   AddPoint(IntPoint(
5238     round(FInP[J].X + FNorms[J].X * FDelta),
5239     round(FInP[J].Y + FNorms[J].Y * FDelta)));
5240 end;
5241 //------------------------------------------------------------------------------
5242 
5243 procedure TClipperOffset.OffsetPoint(J: Integer;
5244   var K: Integer; JoinType: TJoinType);
5245 var
5246   R, cosA: Double;
5247 begin
5248   //cross product ...
5249   FSinA := (FNorms[K].X * FNorms[J].Y - FNorms[J].X * FNorms[K].Y);
5250   if (Abs(FSinA * FDelta) < 1.0) then
5251   begin
5252     //very nearly collinear edges can occasionally cause tiny self-intersections
5253     //due to rounding so offset with a single vertex here. (nb: The two offset
5254     //vertices that would otherwise have been used would be < 1 unit apart.)
5255     //dot product ...
5256     cosA := (FNorms[K].X * FNorms[J].X + FNorms[J].Y * FNorms[K].Y );
5257     if (cosA > 0) then // angle => 0 deg.
5258     begin
5259       AddPoint(IntPoint(round(FInP[J].X + FNorms[K].X * FDelta),
5260         round(FInP[J].Y + FNorms[K].Y * FDelta)));
5261       Exit;
5262     end
5263     //else angle => 180 deg.
5264   end
5265   else if (FSinA > 1.0) then FSinA := 1.0
5266   else if (FSinA < -1.0) then FSinA := -1.0;
5267 
5268   if FSinA * FDelta < 0 then
5269   begin
5270     AddPoint(IntPoint(round(FInP[J].X + FNorms[K].X * FDelta),
5271       round(FInP[J].Y + FNorms[K].Y * FDelta)));
5272     AddPoint(FInP[J]);
5273     AddPoint(IntPoint(round(FInP[J].X + FNorms[J].X * FDelta),
5274       round(FInP[J].Y + FNorms[J].Y * FDelta)));
5275   end
5276   else
5277     case JoinType of
5278       jtMiter:
5279       begin
5280         R := 1 + (FNorms[J].X * FNorms[K].X + FNorms[J].Y * FNorms[K].Y);
5281         if (R >= FMiterLim) then DoMiter(J, K, R)
5282         else DoSquare(J, K);
5283       end;
5284       jtSquare: DoSquare(J, K);
5285       jtRound: DoRound(J, K);
5286     end;
5287   K := J;
5288 end;
5289 //------------------------------------------------------------------------------
5290 
5291 function SimplifyPolygon(const Poly: TPath; FillType: TPolyFillType = pftEvenOdd): TPaths;
5292 begin
5293   with TClipper.Create do
5294   try
5295     StrictlySimple := True;
5296     AddPath(Poly, ptSubject, True);
5297     Execute(ctUnion, Result, FillType, FillType);
5298   finally
5299     free;
5300   end;
5301 end;
5302 //------------------------------------------------------------------------------
5303 
5304 function SimplifyPolygons(const Polys: TPaths; FillType: TPolyFillType = pftEvenOdd): TPaths;
5305 begin
5306   with TClipper.Create do
5307   try
5308     StrictlySimple := True;
5309     AddPaths(Polys, ptSubject, True);
5310     Execute(ctUnion, Result, FillType, FillType);
5311   finally
5312     free;
5313   end;
5314 end;
5315 //------------------------------------------------------------------------------
5316 
5317 function DistanceSqrd(const Pt1, Pt2: TIntPoint): Double;
5318 {$IFDEF INLINING} inline; {$ENDIF}
5319 var
5320   dx, dy: Double;
5321 begin
5322   dx := (Pt1.X - Pt2.X);
5323   dy := (Pt1.Y - Pt2.Y);
5324   result := (dx*dx + dy*dy);
5325 end;
5326 //------------------------------------------------------------------------------
5327 
5328 function DistanceFromLineSqrd(const pt, ln1, ln2: TIntPoint): double;
5329 var
5330   A, B, C: double;
5331 begin
5332   //The equation of a line in general form (Ax + By + C = 0)
5333   //given 2 points (x�,y�) & (x�,y�) is ...
5334   //(y� - y�)x + (x� - x�)y + (y� - y�)x� - (x� - x�)y� = 0
5335   //A = (y� - y�); B = (x� - x�); C = (y� - y�)x� - (x� - x�)y�
5336   //perpendicular distance of point (x�,y�) = (Ax� + By� + C)/Sqrt(A� + B�)
5337   //see http://en.wikipedia.org/wiki/Perpendicular_distance
5338   A := ln1.Y - ln2.Y;
5339   B := ln2.X - ln1.X;
5340   C := A * ln1.X  + B * ln1.Y;
5341   C := A * pt.X + B * pt.Y - C;
5342   Result := (C * C) / (A * A + B * B);
5343 end;
5344 //---------------------------------------------------------------------------
5345 
5346 function SlopesNearCollinear(const Pt1, Pt2, Pt3: TIntPoint;
5347   DistSqrd: Double): Boolean;
5348 begin
5349   //this function is more accurate when the point that's geometrically
5350   //between the other 2 points is the one that's tested for distance.
5351   //ie makes it more likely to pick up 'spikes' ...
5352   if Abs(Pt1.X - Pt2.X) > Abs(Pt1.Y - Pt2.Y) then
5353   begin
5354     if (Pt1.X > Pt2.X) = (Pt1.X < Pt3.X) then
5355       result := DistanceFromLineSqrd(Pt1, Pt2, Pt3) < DistSqrd
5356     else if (Pt2.X > Pt1.X) = (Pt2.X < Pt3.X) then
5357       result := DistanceFromLineSqrd(Pt2, Pt1, Pt3) < DistSqrd
5358     else
5359       result := DistanceFromLineSqrd(Pt3, Pt1, Pt2) < DistSqrd;
5360   end else
5361   begin
5362     if (Pt1.Y > Pt2.Y) = (Pt1.Y < Pt3.Y) then
5363       result := DistanceFromLineSqrd(Pt1, Pt2, Pt3) < DistSqrd
5364     else if (Pt2.Y > Pt1.Y) = (Pt2.Y < Pt3.Y) then
5365       result := DistanceFromLineSqrd(Pt2, Pt1, Pt3) < DistSqrd
5366     else
5367       result := DistanceFromLineSqrd(Pt3, Pt1, Pt2) < DistSqrd;
5368   end;
5369 end;
5370 //------------------------------------------------------------------------------
5371 
5372 function PointsAreClose(const Pt1, Pt2: TIntPoint;
5373   DistSqrd: Double): Boolean;
5374 begin
5375   result := DistanceSqrd(Pt1, Pt2) <= DistSqrd;
5376 end;
5377 //------------------------------------------------------------------------------
5378 
5379 function CleanPolygon(const Poly: TPath; Distance: Double = 1.415): TPath;
5380 var
5381   I, Len: Integer;
5382   DistSqrd: double;
5383   OutPts: array of TOutPt;
5384   op: POutPt;
5385 
5386   function ExcludeOp(op: POutPt): POutPt;
5387   begin
5388     Result := op.Prev;
5389     Result.Next := op.Next;
5390     op.Next.Prev := Result;
5391     Result.Idx := 0;
5392   end;
5393 
5394 begin
5395   //Distance = proximity in units/pixels below which vertices
5396   //will be stripped. Default ~= sqrt(2) so when adjacent
5397   //vertices have both x & y coords within 1 unit, then
5398   //the second vertex will be stripped.
5399   DistSqrd := Round(Distance * Distance);
5400   Result := nil;
5401   Len := Length(Poly);
5402   if Len = 0 then Exit;
5403 
5404   SetLength(OutPts, Len);
5405   for I := 0 to Len -1 do
5406   begin
5407     OutPts[I].Pt := Poly[I];
5408     OutPts[I].Next := @OutPts[(I + 1) mod Len];
5409     OutPts[I].Next.Prev := @OutPts[I];
5410     OutPts[I].Idx := 0;
5411   end;
5412 
5413   op := @OutPts[0];
5414   while (op.Idx = 0) and (op.Next <> op.Prev) do
5415   begin
5416     if PointsAreClose(op.Pt, op.Prev.Pt, DistSqrd) then
5417     begin
5418       op := ExcludeOp(op);
5419       Dec(Len);
5420     end else if PointsAreClose(op.Prev.Pt, op.Next.Pt, DistSqrd) then
5421     begin
5422       ExcludeOp(op.Next);
5423       op := ExcludeOp(op);
5424       Dec(Len, 2);
5425     end
5426     else if SlopesNearCollinear(op.Prev.Pt, op.Pt, op.Next.Pt, DistSqrd) then
5427     begin
5428       op := ExcludeOp(op);
5429       Dec(Len);
5430     end
5431     else
5432     begin
5433       op.Idx := 1;
5434       op := op.Next;
5435     end;
5436   end;
5437 
5438   if Len < 3 then Len := 0;
5439   SetLength(Result, Len);
5440   for I := 0 to Len -1 do
5441   begin
5442     Result[I] := op.Pt;
5443     op := op.Next;
5444   end;
5445 end;
5446 //------------------------------------------------------------------------------
5447 
5448 function CleanPolygons(const Polys: TPaths; Distance: double = 1.415): TPaths;
5449 var
5450   I, Len: Integer;
5451 begin
5452   Len := Length(Polys);
5453   SetLength(Result, Len);
5454   for I := 0 to Len - 1 do
5455     Result[I] := CleanPolygon(Polys[I], Distance);
5456 end;
5457 //------------------------------------------------------------------------------
5458 
5459 function Minkowski(const Base, Path: TPath;
5460   IsSum: Boolean; IsClosed: Boolean): TPaths;
5461 var
5462   i, j, delta, baseLen, pathLen: integer;
5463   quad: TPath;
5464   tmp: TPaths;
5465 begin
5466   if IsClosed then delta := 1 else delta := 0;
5467 
5468   baseLen := Length(Base);
5469   pathLen := Length(Path);
5470   setLength(tmp, pathLen);
5471   if IsSum then
5472     for i := 0 to pathLen -1 do
5473     begin
5474       setLength(tmp[i], baseLen);
5475       for j := 0 to baseLen -1 do
5476       begin
5477         tmp[i][j].X := Path[i].X + Base[j].X;
5478         tmp[i][j].Y := Path[i].Y + Base[j].Y;
5479       end;
5480     end
5481   else
5482     for i := 0 to pathLen -1 do
5483     begin
5484       setLength(tmp[i], baseLen);
5485       for j := 0 to baseLen -1 do
5486       begin
5487         tmp[i][j].X := Path[i].X - Base[j].X;
5488         tmp[i][j].Y := Path[i].Y - Base[j].Y;
5489       end;
5490     end;
5491 
5492   SetLength(quad, 4);
5493   SetLength(Result, (pathLen + delta) * (baseLen + 1));
5494   for i := 0 to pathLen - 2 + delta do
5495   begin
5496     for j := 0 to baseLen - 1 do
5497     begin
5498       quad[0] := tmp[i mod pathLen][j mod baseLen];
5499       quad[1] := tmp[(i+1) mod pathLen][j mod baseLen];
5500       quad[2] := tmp[(i+1) mod pathLen][(j+1) mod baseLen];
5501       quad[3] := tmp[i mod pathLen][(j+1) mod baseLen];
5502       if not Orientation(quad) then quad := ReversePath(quad);
5503       Result[i*baseLen + j] := copy(quad, 0, 4);
5504     end;
5505   end;
5506 end;
5507 //------------------------------------------------------------------------------
5508 
5509 function TranslatePath(const Path: TPath; const Delta: TIntPoint): TPath;
5510 var
5511   i, len: Integer;
5512 begin
5513   len := Length(Path);
5514   SetLength(Result, len);
5515   for i := 0 to High(Path) do
5516   begin
5517     Result[i].X := Path[i].X + Delta.X;
5518     Result[i].Y := Path[i].Y + Delta.Y;
5519   end;
5520 end;
5521 //------------------------------------------------------------------------------
5522 
5523 function MinkowskiSum(const Pattern, Path: TPath; PathIsClosed: Boolean): TPaths;
5524 begin
5525   Result := Minkowski(Pattern, Path, true, PathIsClosed);
5526   with TClipper.Create() do
5527   try
5528     AddPaths(Result, ptSubject, True);
5529     Execute(ctUnion, Result, pftNonZero);
5530   finally
5531     Free;
5532   end;
5533 end;
5534 //------------------------------------------------------------------------------
5535 
5536 function MinkowskiSum(const Pattern: TPath; const Paths: TPaths;
5537   PathFillType: TPolyFillType; PathIsClosed: Boolean): TPaths;
5538 var
5539   I, Cnt: Integer;
5540   Paths2: TPaths;
5541   Path: TPath;
5542 begin
5543   Result := nil;
5544   if Length(Pattern) = 0 then Exit;
5545   Cnt := Length(Paths);
5546   with TClipper.Create() do
5547   try
5548     for I := 0 to Cnt -1 do
5549     begin
5550       Paths2 := Minkowski(Pattern, Paths[I], true, PathIsClosed);
5551       AddPaths( Paths2, ptSubject, true);
5552       if PathIsClosed then
5553       begin
5554         Path := TranslatePath(Paths[I], Pattern[0]);
5555         AddPath(Path, ptClip, true);
5556       end;
5557     end;
5558     Execute(ctUnion, Result, PathFillType, PathFillType);
5559   finally
5560     Free;
5561   end;
5562 end;
5563 //------------------------------------------------------------------------------
5564 
5565 function MinkowskiDiff(const Poly1, Poly2: TPath): TPaths;
5566 begin
5567   Result := Minkowski(Poly1, Poly2, false, true);
5568   with TClipper.Create() do
5569   try
5570     AddPaths(Result, ptSubject, True);
5571     Execute(ctUnion, Result, pftNonZero);
5572   finally
5573     Free;
5574   end;
5575 end;
5576 //------------------------------------------------------------------------------
5577 
5578 type
5579   TNodeType = (ntAny, ntOpen, ntClosed);
5580 
5581 procedure AddPolyNodeToPaths(PolyNode: TPolyNode;
5582   NodeType: TNodeType; var Paths: TPaths);
5583 var
5584   I: Integer;
5585   Match: Boolean;
5586 begin
5587   case NodeType of
5588     ntAny: Match := True;
5589     ntClosed: Match := not PolyNode.IsOpen;
5590     else Exit;
5591   end;
5592 
5593   if (Length(PolyNode.Contour) > 0) and Match then
5594   begin
5595     I := Length(Paths);
5596     SetLength(Paths, I +1);
5597     Paths[I] := PolyNode.Contour;
5598   end;
5599   for I := 0 to PolyNode.ChildCount - 1 do
5600     AddPolyNodeToPaths(PolyNode.Childs[I], NodeType, Paths);
5601 end;
5602 //------------------------------------------------------------------------------
5603 
5604 function PolyTreeToPaths(PolyTree: TPolyTree): TPaths;
5605 begin
5606   Result := nil;
5607   AddPolyNodeToPaths(PolyTree, ntAny, Result);
5608 end;
5609 //------------------------------------------------------------------------------
5610 
5611 function ClosedPathsFromPolyTree(PolyTree: TPolyTree): TPaths;
5612 begin
5613   Result := nil;
5614   AddPolyNodeToPaths(PolyTree, ntClosed, Result);
5615 end;
5616 //------------------------------------------------------------------------------
5617 
5618 function OpenPathsFromPolyTree(PolyTree: TPolyTree): TPaths;
5619 var
5620   I, J: Integer;
5621 begin
5622   Result := nil;
5623   //Open polys are top level only, so ...
5624   for I := 0 to PolyTree.ChildCount - 1 do
5625     if PolyTree.Childs[I].IsOpen then
5626     begin
5627       J := Length(Result);
5628       SetLength(Result, J +1);
5629       Result[J] := PolyTree.Childs[I].Contour;
5630     end;
5631 end;
5632 
5633 //------------------------------------------------------------------------------
5634 //------------------------------------------------------------------------------
5635 
5636 end.
5637