1 //
2 //
3 // Author    :  Angus Johnson                                                   *
4 // Version   :  4.8.4                                                           *
5 // Date      :  1 June 2012                                                     *
6 // Website   :  http://www.angusj.com                                           *
7 // Copyright :  Angus Johnson 2010-2012                                         *
8 //                                                                              *
9 // License:                                                                     *
10 // Use, modification & distribution is subject to Boost Software License Ver 1. *
11 // http://www.boost.org/LICENSE_1_0.txt                                         *
12 //                                                                              *
13 // Attributions:                                                                *
14 // The code in this library is an extension of Bala Vatti's clipping algorithm: *
15 // "A generic solution to polygon clipping"                                     *
16 // Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63.             *
17 // http://portal.acm.org/citation.cfm?id=129906                                 *
18 //                                                                              *
19 // Computer graphics and geometric modeling: implementation and algorithms      *
20 // By Max K. Agoston                                                            *
21 // Springer; 1 edition (January 4, 2005)                                        *
22 // http://books.google.com/books?q=vatti+clipping+agoston                       *
23 //                                                                              *
24 // See also:                                                                    *
25 // "Polygon Offsetting by Computing Winding Numbers"                            *
26 // Paper no. DETC2005-85513 pp. 565-575                                         *
27 // ASME 2005 International Design Engineering Technical Conferences             *
28 // and Computers and Information in Engineering Conference (IDETC/CIE2005)      *
29 // September 24–28, 2005 , Long Beach, California, USA                          *
30 // http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf              *
31 //                                                                              *
32 //
33 
34 //
35 // This is a translation of the Delphi Clipper library and the naming style
36 // used has retained a Delphi flavour.
37 //
38 
39 //
40 // This Clipper Library code was imported into Pinta on June 6th, 2012 by
41 // Andrew Davis of GSoC 2012 for the MagicWandTool selection modes
42 // implementation. It calculates "boolean clipping operations" (union,
43 // difference (exclude), xor, and intersect) on any type, number, and/or
44 // combination of polygons. It is independent C# code that strictly serves
45 // this purpose. More information can be found here:
46 //
47 // http://www.angusj.com/delphi/clipper.php
48 // http://sourceforge.net/projects/polyclipping/
49 //
50 // - Andrew Davis
51 //
52 
53 using System;
54 using System.Collections.Generic;
55 
56 namespace ClipperLibrary
57 {
58 
59     using Polygon = List<IntPoint>;
60     using Polygons = List<List<IntPoint>>;
61     using ExPolygons = List<ExPolygon>;
62 
63     //------------------------------------------------------------------------------
64     // Int128 struct (enables safe math on signed 64bit integers)
65     // eg Int128 val1((Int64)9223372036854775807); //ie 2^63 -1
66     //    Int128 val2((Int64)9223372036854775807);
67     //    Int128 val3 = val1 * val2;
68     //    val3.ToString => "85070591730234615847396907784232501249" (8.5e+37)
69     //------------------------------------------------------------------------------
70 
71     internal struct Int128
72     {
73         private Int64 hi;
74         private Int64 lo;
75 
Int128ClipperLibrary.Int12876         public Int128(Int64 lo)
77         {
78             this.lo = lo;
79             if (lo < 0)
80                 this.hi = -1;
81             else
82                 this.hi = 0;
83         }
84 
Int128ClipperLibrary.Int12885 		public Int128(Int64 lo, Int64 hi)
86 		{
87 			this.lo = lo;
88 			this.hi = hi;
89 		}
90 
Int128ClipperLibrary.Int12891         public Int128(Int128 val)
92         {
93             hi = val.hi;
94             lo = val.lo;
95         }
96 
IsNegativeClipperLibrary.Int12897         public bool IsNegative()
98         {
99             return hi < 0;
100         }
101 
operator ==ClipperLibrary.Int128102         public static bool operator ==(Int128 val1, Int128 val2)
103         {
104             if ((object)val1 == (object)val2) return true;
105             else if ((object)val1 == null || (object)val2 == null) return false;
106             return (val1.hi == val2.hi && val1.lo == val2.lo);
107         }
108 
operator !=ClipperLibrary.Int128109         public static bool operator!= (Int128 val1, Int128 val2)
110         {
111             return !(val1 == val2);
112         }
113 
EqualsClipperLibrary.Int128114         public override bool Equals(System.Object obj)
115         {
116             if (obj == null || !(obj is Int128))
117 	            return false;
118             Int128 i128 = (Int128)obj;
119             return (i128.hi == hi && i128.lo == lo);
120         }
121 
GetHashCodeClipperLibrary.Int128122         public override int GetHashCode()
123         {
124             return hi.GetHashCode() ^ lo.GetHashCode();
125         }
126 
operator >ClipperLibrary.Int128127         public static bool operator> (Int128 val1, Int128 val2)
128         {
129             if (val1.hi != val2.hi)
130                 return val1.hi > val2.hi;
131             else
132                 return val1.lo > val2.lo;
133         }
134 
operator <ClipperLibrary.Int128135         public static bool operator< (Int128 val1, Int128 val2)
136         {
137             if (val1.hi != val2.hi)
138                 return val1.hi < val2.hi;
139             else
140                 return val1.lo < val2.lo;
141         }
142 
operator +ClipperLibrary.Int128143         public static Int128 operator+ (Int128 lhs, Int128 rhs)
144         {
145             lhs.hi += rhs.hi;
146             lhs.lo += rhs.lo;
147             if ( (UInt64)lhs.lo < (UInt64)rhs.lo) lhs.hi++;
148             return lhs;
149         }
150 
operator -ClipperLibrary.Int128151         public static Int128 operator- (Int128 lhs, Int128 rhs)
152         {
153             return lhs + -rhs;
154         }
155 
operator -ClipperLibrary.Int128156 		public static Int128 operator -(Int128 val)
157 		{
158 			if (val.lo == 0) {
159                 if (val.hi == 0) return val;
160                 return new Int128(0, -val.hi);
161 			}
162             else return new Int128(-val.lo, ~val.hi);
163 		}
164 
165         //nb: Constructing two new Int128 objects every time we want to multiply longs
166         //is slow. So, although calling the Int128Mul method doesn't look as clean, the
167         //code runs significantly faster than if we'd used the * operator.
168 
Int128MulClipperLibrary.Int128169         public static Int128 Int128Mul(Int64 lhs, Int64 rhs)
170         {
171             bool negate = (lhs < 0) != (rhs < 0);
172             if (lhs < 0) lhs = -lhs;
173             if (rhs < 0) rhs = -rhs;
174             UInt64 int1Hi = (UInt64)lhs >> 32;
175             UInt64 int1Lo = (UInt64)lhs & 0xFFFFFFFF;
176             UInt64 int2Hi = (UInt64)rhs >> 32;
177             UInt64 int2Lo = (UInt64)rhs & 0xFFFFFFFF;
178 
179             //nb: see comments in clipper.pas
180             UInt64 a = int1Hi * int2Hi;
181             UInt64 b = int1Lo * int2Lo;
182             UInt64 c = int1Hi * int2Lo + int1Lo * int2Hi;
183 
184             Int64 lo, hi;
185             hi = (Int64)(a + (c >> 32));
186 
187             lo = (Int64)(c << 32);
188             lo += (Int64)b;
189             if ((UInt64)lo < b) hi++;
190             var result = new Int128(lo, hi);
191             return negate ? -result : result;
192         }
193 
operator /ClipperLibrary.Int128194         public static Int128 operator /(Int128 lhs, Int128 rhs)
195         {
196             if (rhs.lo == 0 && rhs.hi == 0)
197                 throw new ClipperException("Int128: divide by zero");
198             bool negate = (rhs.hi < 0) != (lhs.hi < 0);
199             Int128 result = new Int128(lhs), denom = new Int128(rhs);
200             if (result.hi < 0) result = -result;
201             if (denom.hi < 0) denom = -denom;
202             if (denom > result) return new Int128(0); //result is only a fraction of 1
203             denom = -denom;
204 
205             Int128 p = new Int128(0);
206             for (int i = 0; i < 128; ++i)
207             {
208                 p.hi = p.hi << 1;
209                 if (p.lo < 0) p.hi++;
210                 p.lo = (Int64)p.lo << 1;
211                 if (result.hi < 0) p.lo++;
212                 result.hi = result.hi << 1;
213                 if (result.lo < 0) result.hi++;
214                 result.lo = (Int64)result.lo << 1;
215                 if (p.hi >= 0)
216                 {
217                     p += denom;
218                     result.lo++;
219                 }
220             }
221             return negate ? -result : result;
222         }
223 
ToDoubleClipperLibrary.Int128224         public double ToDouble()
225         {
226             const double shift64 = 18446744073709551616.0; //2^64
227             const double bit64 = 9223372036854775808.0;
228             if (hi < 0)
229             {
230                 Int128 tmp = new Int128(this);
231                 tmp = -tmp;
232                 if (tmp.lo < 0)
233                     return (double)tmp.lo - bit64 - tmp.hi * shift64;
234                 else
235                     return -(double)tmp.lo - tmp.hi * shift64;
236             }
237             else if (lo < 0)
238                 return -(double)lo + bit64 + hi * shift64;
239             else
240                 return (double)lo + (double)hi * shift64;
241         }
242 
243         ////for bug testing ...
244         //public override string ToString()
245         //{
246         //    int r = 0;
247         //    Int128 tmp = new Int128(0), val = new Int128(this);
248         //    if (hi < 0) Negate(val);
249         //    StringBuilder builder = new StringBuilder(50);
250         //    while (val.hi != 0 || val.lo != 0)
251         //    {
252         //        Div10(val, ref tmp, ref r);
253         //        builder.Insert(0, (char)('0' + r));
254         //        val = tmp;
255         //    }
256         //    if (hi < 0) return '-' + builder.ToString();
257         //    if (builder.Length == 0) return "0";
258         //    return builder.ToString();
259         //}
260 
261         ////debugging only ...
262         //private void Div10(Int128 val, ref Int128 result, ref int remainder)
263         //{
264         //    remainder = 0;
265         //    result = new Int128(0);
266         //    for (int i = 63; i >= 0; --i)
267         //    {
268         //        if ((val.hi & ((Int64)1 << i)) != 0)
269         //            remainder = (remainder * 2) + 1;
270         //        else
271         //            remainder *= 2;
272         //        if (remainder >= 10)
273         //        {
274         //            result.hi += ((Int64)1 << i);
275         //            remainder -= 10;
276         //        }
277         //    }
278         //    for (int i = 63; i >= 0; --i)
279         //    {
280         //        if ((val.lo & ((Int64)1 << i)) != 0)
281         //            remainder = (remainder * 2) + 1;
282         //        else
283         //            remainder *= 2;
284         //        if (remainder >= 10)
285         //        {
286         //            result.lo += ((Int64)1 << i);
287         //            remainder -= 10;
288         //        }
289         //    }
290         //}
291     };
292 
293     //------------------------------------------------------------------------------
294     //------------------------------------------------------------------------------
295 
296     public struct IntPoint
297     {
298         public Int64 X;
299         public Int64 Y;
300 
IntPointClipperLibrary.IntPoint301         public IntPoint(Int64 X, Int64 Y)
302         {
303             this.X = X; this.Y = Y;
304         }
305 
IntPointClipperLibrary.IntPoint306         public IntPoint(IntPoint pt)
307         {
308             this.X = pt.X; this.Y = pt.Y;
309         }
310     }
311 
312     public struct IntRect
313     {
314         public Int64 left;
315         public Int64 top;
316         public Int64 right;
317         public Int64 bottom;
318 
IntRectClipperLibrary.IntRect319         public IntRect(Int64 l, Int64 t, Int64 r, Int64 b)
320         {
321             this.left = l; this.top = t;
322             this.right = r; this.bottom = b;
323         }
324     }
325 
326     public struct ExPolygon
327     {
328         public Polygon outer;
329         public Polygons holes;
330     }
331 
332     public enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor };
333     public enum PolyType { ptSubject, ptClip };
334     //By far the most widely used winding rules for polygon filling are
335     //EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32)
336     //Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL)
337     //see http://glprogramming.com/red/chapter11.html
338     public enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative };
339     public enum JoinType { jtSquare, jtRound, jtMiter };
340 
341 
342     [Flags]
343     internal enum EdgeSide { esNeither = 0, esLeft = 1, esRight = 2, esBoth = 3 };
344     [Flags]
345     internal enum Protects { ipNone = 0, ipLeft = 1, ipRight = 2, ipBoth = 3 };
346     internal enum Direction { dRightToLeft, dLeftToRight };
347 
348     internal class TEdge {
349         public Int64 xbot;
350         public Int64 ybot;
351         public Int64 xcurr;
352         public Int64 ycurr;
353         public Int64 xtop;
354         public Int64 ytop;
355         public double dx;
356         public Int64 tmpX;
357         public PolyType polyType;
358         public EdgeSide side;
359         public int windDelta; //1 or -1 depending on winding direction
360         public int windCnt;
361         public int windCnt2; //winding count of the opposite polytype
362         public int outIdx;
363         public TEdge next;
364         public TEdge prev;
365         public TEdge nextInLML;
366         public TEdge nextInAEL;
367         public TEdge prevInAEL;
368         public TEdge nextInSEL;
369         public TEdge prevInSEL;
370     };
371 
372     internal class IntersectNode
373     {
374         public TEdge edge1;
375         public TEdge edge2;
376         public IntPoint pt;
377         public IntersectNode next;
378     };
379 
380     internal class LocalMinima
381     {
382         public Int64 Y;
383         public TEdge leftBound;
384         public TEdge rightBound;
385         public LocalMinima next;
386     };
387 
388     internal class Scanbeam
389     {
390         public Int64 Y;
391         public Scanbeam next;
392     };
393 
394     internal class OutRec
395     {
396         public int idx;
397         public bool isHole;
398         public OutRec FirstLeft;
399         public OutRec AppendLink;
400         public OutPt pts;
401         public OutPt bottomPt;
402         public OutPt bottomFlag;
403         public EdgeSide sides;
404     };
405 
406     internal class OutPt
407     {
408         public int idx;
409         public IntPoint pt;
410         public OutPt next;
411         public OutPt prev;
412     };
413 
414     internal class JoinRec
415     {
416         public IntPoint pt1a;
417         public IntPoint pt1b;
418         public int poly1Idx;
419         public IntPoint pt2a;
420         public IntPoint pt2b;
421         public int poly2Idx;
422     };
423 
424     internal class HorzJoinRec
425     {
426         public TEdge edge;
427         public int savedIdx;
428     };
429 
430     public class ClipperBase
431     {
432         protected const double horizontal = -3.4E+38;
433         internal const Int64 loRange = 1518500249;           //sqrt(2^63 -1)/2
434         internal const Int64 hiRange = 6521908912666391106L; //sqrt(2^127 -1)/2
435 
436         internal LocalMinima m_MinimaList;
437         internal LocalMinima m_CurrentLM;
438         internal List<List<TEdge>> m_edges = new List<List<TEdge>>();
439         internal bool m_UseFullRange;
440 
441         //------------------------------------------------------------------------------
442 
PointsEqual(IntPoint pt1, IntPoint pt2)443         protected static bool PointsEqual(IntPoint pt1, IntPoint pt2)
444         {
445           return ( pt1.X == pt2.X && pt1.Y == pt2.Y );
446         }
447         //------------------------------------------------------------------------------
448 
PointIsVertex(IntPoint pt, OutPt pp)449         internal bool PointIsVertex(IntPoint pt, OutPt pp)
450         {
451           OutPt pp2 = pp;
452           do
453           {
454             if (PointsEqual(pp2.pt, pt)) return true;
455             pp2 = pp2.next;
456           }
457           while (pp2 != pp);
458           return false;
459         }
460         //------------------------------------------------------------------------------
461 
PointInPolygon(IntPoint pt, OutPt pp, bool UseFulllongRange)462         internal bool PointInPolygon(IntPoint pt, OutPt pp, bool UseFulllongRange)
463         {
464           OutPt pp2 = pp;
465           bool result = false;
466           if (UseFulllongRange)
467           {
468               do
469               {
470                   if ((((pp2.pt.Y <= pt.Y) && (pt.Y < pp2.prev.pt.Y)) ||
471                       ((pp2.prev.pt.Y <= pt.Y) && (pt.Y < pp2.pt.Y))) &&
472                       new Int128(pt.X - pp2.pt.X) <
473                       Int128.Int128Mul(pp2.prev.pt.X - pp2.pt.X,  pt.Y - pp2.pt.Y) /
474                       new Int128(pp2.prev.pt.Y - pp2.pt.Y))
475                         result = !result;
476                   pp2 = pp2.next;
477               }
478               while (pp2 != pp);
479           }
480           else
481           {
482               do
483               {
484                   if ((((pp2.pt.Y <= pt.Y) && (pt.Y < pp2.prev.pt.Y)) ||
485                     ((pp2.prev.pt.Y <= pt.Y) && (pt.Y < pp2.pt.Y))) &&
486                     (pt.X - pp2.pt.X < (pp2.prev.pt.X - pp2.pt.X) * (pt.Y - pp2.pt.Y) /
487                     (pp2.prev.pt.Y - pp2.pt.Y))) result = !result;
488                   pp2 = pp2.next;
489               }
490               while (pp2 != pp);
491           }
492           return result;
493         }
494         //------------------------------------------------------------------------------
495 
SlopesEqual(TEdge e1, TEdge e2, bool UseFullRange)496         internal bool SlopesEqual(TEdge e1, TEdge e2, bool UseFullRange)
497         {
498             if (UseFullRange)
499               return Int128.Int128Mul(e1.ytop - e1.ybot, e2.xtop - e2.xbot) ==
500                   Int128.Int128Mul(e1.xtop - e1.xbot, e2.ytop - e2.ybot);
501             else return (Int64)(e1.ytop - e1.ybot) * (e2.xtop - e2.xbot) -
502               (Int64)(e1.xtop - e1.xbot)*(e2.ytop - e2.ybot) == 0;
503         }
504         //------------------------------------------------------------------------------
505 
SlopesEqual(IntPoint pt1, IntPoint pt2, IntPoint pt3, bool UseFullRange)506         protected bool SlopesEqual(IntPoint pt1, IntPoint pt2,
507             IntPoint pt3, bool UseFullRange)
508         {
509             if (UseFullRange)
510                 return Int128.Int128Mul(pt1.Y - pt2.Y, pt2.X - pt3.X) ==
511                   Int128.Int128Mul(pt1.X - pt2.X, pt2.Y - pt3.Y);
512             else return
513               (Int64)(pt1.Y - pt2.Y) * (pt2.X - pt3.X) - (Int64)(pt1.X - pt2.X) * (pt2.Y - pt3.Y) == 0;
514         }
515         //------------------------------------------------------------------------------
516 
SlopesEqual(IntPoint pt1, IntPoint pt2, IntPoint pt3, IntPoint pt4, bool UseFullRange)517         protected bool SlopesEqual(IntPoint pt1, IntPoint pt2,
518             IntPoint pt3, IntPoint pt4, bool UseFullRange)
519         {
520             if (UseFullRange)
521                 return Int128.Int128Mul(pt1.Y - pt2.Y, pt3.X - pt4.X) ==
522                   Int128.Int128Mul(pt1.X - pt2.X, pt3.Y - pt4.Y);
523             else return
524               (Int64)(pt1.Y - pt2.Y) * (pt3.X - pt4.X) - (Int64)(pt1.X - pt2.X) * (pt3.Y - pt4.Y) == 0;
525         }
526         //------------------------------------------------------------------------------
527 
ClipperBase()528         internal ClipperBase() //constructor (nb: no external instantiation)
529         {
530             m_MinimaList = null;
531             m_CurrentLM = null;
532             m_UseFullRange = false;
533         }
534         //------------------------------------------------------------------------------
535 
536         //destructor - commented out since I gather this impedes the GC
537         //~ClipperBase()
538         //{
539         //    Clear();
540         //}
541         //------------------------------------------------------------------------------
542 
Clear()543         public virtual void Clear()
544         {
545             DisposeLocalMinimaList();
546             for (int i = 0; i < m_edges.Count; ++i)
547             {
548                 for (int j = 0; j < m_edges[i].Count; ++j) m_edges[i][j] = null;
549                 m_edges[i].Clear();
550             }
551             m_edges.Clear();
552             m_UseFullRange = false;
553         }
554         //------------------------------------------------------------------------------
555 
DisposeLocalMinimaList()556         private void DisposeLocalMinimaList()
557         {
558             while( m_MinimaList != null )
559             {
560                 LocalMinima tmpLm = m_MinimaList.next;
561                 m_MinimaList = null;
562                 m_MinimaList = tmpLm;
563             }
564             m_CurrentLM = null;
565         }
566         //------------------------------------------------------------------------------
567 
AddPolygons(Polygons ppg, PolyType polyType)568         public bool AddPolygons(Polygons ppg, PolyType polyType)
569         {
570             bool result = false;
571             for (int i = 0; i < ppg.Count; ++i)
572                 if (AddPolygon(ppg[i], polyType)) result = true;
573             return result;
574         }
575         //------------------------------------------------------------------------------
576 
AddPolygon(Polygon pg, PolyType polyType)577         public bool AddPolygon(Polygon pg, PolyType polyType)
578         {
579             int len = pg.Count;
580             if (len < 3) return false;
581             Polygon p = new Polygon(len);
582             p.Add(new IntPoint(pg[0].X, pg[0].Y));
583             int j = 0;
584             for (int i = 1; i < len; ++i)
585             {
586 
587                 Int64 maxVal;
588                 if (m_UseFullRange) maxVal = hiRange; else maxVal = loRange;
589                 if (Math.Abs(pg[i].X) > maxVal || Math.Abs(pg[i].Y) > maxVal)
590                 {
591                     if (Math.Abs(pg[i].X) > hiRange || Math.Abs(pg[i].Y) > hiRange)
592                     throw new ClipperException("Coordinate exceeds range bounds");
593                   maxVal = hiRange;
594                   m_UseFullRange = true;
595                 }
596 
597                 if (PointsEqual(p[j], pg[i])) continue;
598                 else if (j > 0 && SlopesEqual(p[j-1], p[j], pg[i], m_UseFullRange))
599                 {
600                     if (PointsEqual(p[j-1], pg[i])) j--;
601                 } else j++;
602                 if (j < p.Count)
603                     p[j] = pg[i]; else
604                     p.Add(new IntPoint(pg[i].X, pg[i].Y));
605             }
606             if (j < 2) return false;
607 
608             len = j+1;
609             while (len > 2)
610             {
611                 //nb: test for point equality before testing slopes ...
612                 if (PointsEqual(p[j], p[0])) j--;
613                 else if (PointsEqual(p[0], p[1]) || SlopesEqual(p[j], p[0], p[1], m_UseFullRange))
614                     p[0] = p[j--];
615                 else if (SlopesEqual(p[j - 1], p[j], p[0], m_UseFullRange)) j--;
616                 else if (SlopesEqual(p[0], p[1], p[2], m_UseFullRange))
617                 {
618                     for (int i = 2; i <= j; ++i) p[i - 1] = p[i];
619                     j--;
620                 }
621                 else break;
622                 len--;
623             }
624             if (len < 3) return false;
625 
626             //create a new edge array ...
627             List<TEdge> edges = new List<TEdge>(len);
628             for (int i = 0; i < len; i++) edges.Add(new TEdge());
629             m_edges.Add(edges);
630 
631             //convert vertices to a double-linked-list of edges and initialize ...
632             edges[0].xcurr = p[0].X;
633             edges[0].ycurr = p[0].Y;
634             InitEdge(edges[len-1], edges[0], edges[len-2], p[len-1], polyType);
635             for (int i = len-2; i > 0; --i)
636             InitEdge(edges[i], edges[i+1], edges[i-1], p[i], polyType);
637             InitEdge(edges[0], edges[1], edges[len-1], p[0], polyType);
638 
639             //reset xcurr & ycurr and find 'eHighest' (given the Y axis coordinates
640             //increase downward so the 'highest' edge will have the smallest ytop) ...
641             TEdge e = edges[0];
642             TEdge eHighest = e;
643             do
644             {
645             e.xcurr = e.xbot;
646             e.ycurr = e.ybot;
647             if (e.ytop < eHighest.ytop) eHighest = e;
648             e = e.next;
649             }
650             while ( e != edges[0]);
651 
652             //make sure eHighest is positioned so the following loop works safely ...
653             if (eHighest.windDelta > 0) eHighest = eHighest.next;
654             if (eHighest.dx == horizontal) eHighest = eHighest.next;
655 
656             //finally insert each local minima ...
657             e = eHighest;
658             do {
659             e = AddBoundsToLML(e);
660             }
661             while( e != eHighest );
662             return true;
663         }
664         //------------------------------------------------------------------------------
665 
InitEdge(TEdge e, TEdge eNext, TEdge ePrev, IntPoint pt, PolyType polyType)666         private void InitEdge(TEdge e, TEdge eNext,
667           TEdge ePrev, IntPoint pt, PolyType polyType)
668         {
669           e.next = eNext;
670           e.prev = ePrev;
671           e.xcurr = pt.X;
672           e.ycurr = pt.Y;
673           if (e.ycurr >= e.next.ycurr)
674           {
675             e.xbot = e.xcurr;
676             e.ybot = e.ycurr;
677             e.xtop = e.next.xcurr;
678             e.ytop = e.next.ycurr;
679             e.windDelta = 1;
680           } else
681           {
682             e.xtop = e.xcurr;
683             e.ytop = e.ycurr;
684             e.xbot = e.next.xcurr;
685             e.ybot = e.next.ycurr;
686             e.windDelta = -1;
687           }
688           SetDx(e);
689           e.polyType = polyType;
690           e.outIdx = -1;
691         }
692         //------------------------------------------------------------------------------
693 
SetDx(TEdge e)694         private void SetDx(TEdge e)
695         {
696           if (e.ybot == e.ytop) e.dx = horizontal;
697           else e.dx = (double)(e.xtop - e.xbot)/(e.ytop - e.ybot);
698         }
699         //---------------------------------------------------------------------------
700 
AddBoundsToLML(TEdge e)701         TEdge AddBoundsToLML(TEdge e)
702         {
703           //Starting at the top of one bound we progress to the bottom where there's
704           //a local minima. We then go to the top of the next bound. These two bounds
705           //form the left and right (or right and left) bounds of the local minima.
706           e.nextInLML = null;
707           e = e.next;
708           for (;;)
709           {
710             if ( e.dx == horizontal )
711             {
712               //nb: proceed through horizontals when approaching from their right,
713               //    but break on horizontal minima if approaching from their left.
714               //    This ensures 'local minima' are always on the left of horizontals.
715               if (e.next.ytop < e.ytop && e.next.xbot > e.prev.xbot) break;
716               if (e.xtop != e.prev.xbot) SwapX(e);
717               e.nextInLML = e.prev;
718             }
719             else if (e.ycurr == e.prev.ycurr) break;
720             else e.nextInLML = e.prev;
721             e = e.next;
722           }
723 
724           //e and e.prev are now at a local minima ...
725           LocalMinima newLm = new LocalMinima();
726           newLm.next = null;
727           newLm.Y = e.prev.ybot;
728 
729           if ( e.dx == horizontal ) //horizontal edges never start a left bound
730           {
731             if (e.xbot != e.prev.xbot) SwapX(e);
732             newLm.leftBound = e.prev;
733             newLm.rightBound = e;
734           } else if (e.dx < e.prev.dx)
735           {
736             newLm.leftBound = e.prev;
737             newLm.rightBound = e;
738           } else
739           {
740             newLm.leftBound = e;
741             newLm.rightBound = e.prev;
742           }
743           newLm.leftBound.side = EdgeSide.esLeft;
744           newLm.rightBound.side = EdgeSide.esRight;
745           InsertLocalMinima( newLm );
746 
747           for (;;)
748           {
749             if ( e.next.ytop == e.ytop && e.next.dx != horizontal ) break;
750             e.nextInLML = e.next;
751             e = e.next;
752             if ( e.dx == horizontal && e.xbot != e.prev.xtop) SwapX(e);
753           }
754           return e.next;
755         }
756         //------------------------------------------------------------------------------
757 
InsertLocalMinima(LocalMinima newLm)758         private void InsertLocalMinima(LocalMinima newLm)
759         {
760           if( m_MinimaList == null )
761           {
762             m_MinimaList = newLm;
763           }
764           else if( newLm.Y >= m_MinimaList.Y )
765           {
766             newLm.next = m_MinimaList;
767             m_MinimaList = newLm;
768           } else
769           {
770             LocalMinima tmpLm = m_MinimaList;
771             while( tmpLm.next != null  && ( newLm.Y < tmpLm.next.Y ) )
772               tmpLm = tmpLm.next;
773             newLm.next = tmpLm.next;
774             tmpLm.next = newLm;
775           }
776         }
777         //------------------------------------------------------------------------------
778 
PopLocalMinima()779         protected void PopLocalMinima()
780         {
781             if (m_CurrentLM == null) return;
782             m_CurrentLM = m_CurrentLM.next;
783         }
784         //------------------------------------------------------------------------------
785 
SwapX(TEdge e)786         private void SwapX(TEdge e)
787         {
788           //swap horizontal edges' top and bottom x's so they follow the natural
789           //progression of the bounds - ie so their xbots will align with the
790           //adjoining lower edge. [Helpful in the ProcessHorizontal() method.]
791           e.xcurr = e.xtop;
792           e.xtop = e.xbot;
793           e.xbot = e.xcurr;
794         }
795         //------------------------------------------------------------------------------
796 
Reset()797         protected virtual void Reset()
798         {
799             m_CurrentLM = m_MinimaList;
800 
801             //reset all edges ...
802             LocalMinima lm = m_MinimaList;
803             while (lm != null)
804             {
805                 TEdge e = lm.leftBound;
806                 while (e != null)
807                 {
808                     e.xcurr = e.xbot;
809                     e.ycurr = e.ybot;
810                     e.side = EdgeSide.esLeft;
811                     e.outIdx = -1;
812                     e = e.nextInLML;
813                 }
814                 e = lm.rightBound;
815                 while (e != null)
816                 {
817                     e.xcurr = e.xbot;
818                     e.ycurr = e.ybot;
819                     e.side = EdgeSide.esRight;
820                     e.outIdx = -1;
821                     e = e.nextInLML;
822                 }
823                 lm = lm.next;
824             }
825             return;
826         }
827         //------------------------------------------------------------------------------
828 
GetBounds()829         public IntRect GetBounds()
830         {
831             IntRect result = new IntRect();
832             LocalMinima lm = m_MinimaList;
833             if (lm == null) return result;
834             result.left = lm.leftBound.xbot;
835             result.top = lm.leftBound.ybot;
836             result.right = lm.leftBound.xbot;
837             result.bottom = lm.leftBound.ybot;
838             while (lm != null)
839             {
840                 if (lm.leftBound.ybot > result.bottom)
841                     result.bottom = lm.leftBound.ybot;
842                 TEdge e = lm.leftBound;
843                 for (; ; )
844                 {
845                     TEdge bottomE = e;
846                     while (e.nextInLML != null)
847                     {
848                         if (e.xbot < result.left) result.left = e.xbot;
849                         if (e.xbot > result.right) result.right = e.xbot;
850                         e = e.nextInLML;
851                     }
852                     if (e.xbot < result.left) result.left = e.xbot;
853                     if (e.xbot > result.right) result.right = e.xbot;
854                     if (e.xtop < result.left) result.left = e.xtop;
855                     if (e.xtop > result.right) result.right = e.xtop;
856                     if (e.ytop < result.top) result.top = e.ytop;
857 
858                     if (bottomE == lm.leftBound) e = lm.rightBound;
859                     else break;
860                 }
861                 lm = lm.next;
862             }
863             return result;
864         }
865 
866     } //ClipperBase
867 
868     public class Clipper : ClipperBase
869     {
870         private List<OutRec> m_PolyOuts;
871         private ClipType m_ClipType;
872         private Scanbeam m_Scanbeam;
873         private TEdge m_ActiveEdges;
874         private TEdge m_SortedEdges;
875         private IntersectNode m_IntersectNodes;
876         private bool m_ExecuteLocked;
877         private PolyFillType m_ClipFillType;
878         private PolyFillType m_SubjFillType;
879         private List<JoinRec> m_Joins;
880         private List<HorzJoinRec> m_HorizJoins;
881         private bool m_ReverseOutput;
882 
Clipper()883         public Clipper()
884         {
885             m_Scanbeam = null;
886             m_ActiveEdges = null;
887             m_SortedEdges = null;
888             m_IntersectNodes = null;
889             m_ExecuteLocked = false;
890             m_PolyOuts = new List<OutRec>();
891             m_Joins = new List<JoinRec>();
892             m_HorizJoins = new List<HorzJoinRec>();
893             m_ReverseOutput = false;
894         }
895         //------------------------------------------------------------------------------
896 
897         //destructor - commented out since I gather this impedes the GC
898         //~Clipper() //destructor
899         //{
900         //    Clear();
901         //    DisposeScanbeamList();
902         //}
903         //------------------------------------------------------------------------------
904 
Clear()905         public override void Clear()
906         {
907             if (m_edges.Count == 0) return; //avoids problems with ClipperBase destructor
908             DisposeAllPolyPts();
909             base.Clear();
910         }
911         //------------------------------------------------------------------------------
912 
DisposeScanbeamList()913         void DisposeScanbeamList()
914         {
915           while ( m_Scanbeam != null ) {
916           Scanbeam sb2 = m_Scanbeam.next;
917           m_Scanbeam = null;
918           m_Scanbeam = sb2;
919           }
920         }
921         //------------------------------------------------------------------------------
922 
Reset()923         protected override void Reset()
924         {
925           base.Reset();
926           m_Scanbeam = null;
927           m_ActiveEdges = null;
928           m_SortedEdges = null;
929           DisposeAllPolyPts();
930           LocalMinima lm = m_MinimaList;
931           while (lm != null)
932           {
933             InsertScanbeam(lm.Y);
934             InsertScanbeam(lm.leftBound.ytop);
935             lm = lm.next;
936           }
937         }
938         //------------------------------------------------------------------------------
939 
940         public bool ReverseSolution
941         {
942             get { return m_ReverseOutput; }
943             set { m_ReverseOutput = value; }
944         }
945         //------------------------------------------------------------------------------
946 
InsertScanbeam(Int64 Y)947         private void InsertScanbeam(Int64 Y)
948         {
949           if( m_Scanbeam == null )
950           {
951             m_Scanbeam = new Scanbeam();
952             m_Scanbeam.next = null;
953             m_Scanbeam.Y = Y;
954           }
955           else if(  Y > m_Scanbeam.Y )
956           {
957             Scanbeam newSb = new Scanbeam();
958             newSb.Y = Y;
959             newSb.next = m_Scanbeam;
960             m_Scanbeam = newSb;
961           } else
962           {
963             Scanbeam sb2 = m_Scanbeam;
964             while( sb2.next != null  && ( Y <= sb2.next.Y ) ) sb2 = sb2.next;
965             if(  Y == sb2.Y ) return; //ie ignores duplicates
966             Scanbeam newSb = new Scanbeam();
967             newSb.Y = Y;
968             newSb.next = sb2.next;
969             sb2.next = newSb;
970           }
971         }
972         //------------------------------------------------------------------------------
973 
Execute(ClipType clipType, Polygons solution, PolyFillType subjFillType, PolyFillType clipFillType)974         public bool Execute(ClipType clipType, Polygons solution,
975             PolyFillType subjFillType, PolyFillType clipFillType)
976         {
977             if (m_ExecuteLocked) return false;
978             m_ExecuteLocked = true;
979             solution.Clear();
980             m_SubjFillType = subjFillType;
981             m_ClipFillType = clipFillType;
982             m_ClipType = clipType;
983             bool succeeded = ExecuteInternal(false);
984             //build the return polygons ...
985             if (succeeded) BuildResult(solution);
986             m_ExecuteLocked = false;
987             return succeeded;
988         }
989         //------------------------------------------------------------------------------
990 
Execute(ClipType clipType, ExPolygons solution, PolyFillType subjFillType, PolyFillType clipFillType)991         public bool Execute(ClipType clipType, ExPolygons solution,
992             PolyFillType subjFillType, PolyFillType clipFillType)
993         {
994             if (m_ExecuteLocked) return false;
995             m_ExecuteLocked = true;
996             solution.Clear();
997             m_SubjFillType = subjFillType;
998             m_ClipFillType = clipFillType;
999             m_ClipType = clipType;
1000             bool succeeded = ExecuteInternal(true);
1001             //build the return polygons ...
1002             if (succeeded) BuildResultEx(solution);
1003             m_ExecuteLocked = false;
1004             return succeeded;
1005         }
1006         //------------------------------------------------------------------------------
1007 
Execute(ClipType clipType, Polygons solution)1008         public bool Execute(ClipType clipType, Polygons solution)
1009         {
1010             return Execute(clipType, solution,
1011                 PolyFillType.pftEvenOdd, PolyFillType.pftEvenOdd);
1012         }
1013         //------------------------------------------------------------------------------
1014 
Execute(ClipType clipType, ExPolygons solution)1015         public bool Execute(ClipType clipType, ExPolygons solution)
1016         {
1017             return Execute(clipType, solution,
1018                 PolyFillType.pftEvenOdd, PolyFillType.pftEvenOdd);
1019         }
1020         //------------------------------------------------------------------------------
1021 
PolySort(OutRec or1, OutRec or2)1022         internal int PolySort(OutRec or1, OutRec or2)
1023         {
1024           if (or1 == or2) return 0;
1025           else if (or1.pts == null || or2.pts == null)
1026           {
1027             if ((or1.pts == null) != (or2.pts == null))
1028             {
1029                 return or1.pts == null ? 1 : -1;
1030             }
1031             else return 0;
1032           }
1033           int i1, i2;
1034           if (or1.isHole)
1035             i1 = or1.FirstLeft.idx; else
1036             i1 = or1.idx;
1037           if (or2.isHole)
1038             i2 = or2.FirstLeft.idx; else
1039             i2 = or2.idx;
1040           int result = i1 - i2;
1041           if (result == 0 && (or1.isHole != or2.isHole))
1042           {
1043               return or1.isHole ? 1 : -1;
1044           }
1045           return result;
1046         }
1047         //------------------------------------------------------------------------------
1048 
FindAppendLinkEnd(OutRec outRec)1049         internal OutRec FindAppendLinkEnd(OutRec outRec)
1050         {
1051           while (outRec.AppendLink != null) outRec = outRec.AppendLink;
1052           return outRec;
1053         }
1054         //------------------------------------------------------------------------------
1055 
FixHoleLinkage(OutRec outRec)1056         internal void FixHoleLinkage(OutRec outRec)
1057         {
1058             OutRec tmp;
1059             if (outRec.bottomPt != null)
1060                 tmp = m_PolyOuts[outRec.bottomPt.idx].FirstLeft;
1061             else
1062                 tmp = outRec.FirstLeft;
1063             if (outRec == tmp) throw new ClipperException("HoleLinkage error");
1064 
1065             if (tmp != null)
1066             {
1067                 if (tmp.AppendLink != null) tmp = FindAppendLinkEnd(tmp);
1068 
1069                 if (tmp == outRec) tmp = null;
1070                 else if (tmp.isHole)
1071                 {
1072                     FixHoleLinkage(tmp);
1073                     tmp = tmp.FirstLeft;
1074                 }
1075             }
1076             outRec.FirstLeft = tmp;
1077             if (tmp == null) outRec.isHole = false;
1078             outRec.AppendLink = null;
1079         }
1080         //------------------------------------------------------------------------------
1081 
ExecuteInternal(bool fixHoleLinkages)1082         private bool ExecuteInternal(bool fixHoleLinkages)
1083         {
1084             bool succeeded;
1085             try
1086             {
1087                 Reset();
1088                 if (m_CurrentLM == null) return true;
1089                 Int64 botY = PopScanbeam();
1090                 do
1091                 {
1092                     InsertLocalMinimaIntoAEL(botY);
1093                     m_HorizJoins.Clear();
1094                     ProcessHorizontals();
1095                     Int64 topY = PopScanbeam();
1096                     succeeded = ProcessIntersections(botY, topY);
1097                     if (!succeeded) break;
1098                     ProcessEdgesAtTopOfScanbeam(topY);
1099                     botY = topY;
1100                 } while (m_Scanbeam != null);
1101             }
1102             catch { succeeded = false; }
1103 
1104             if (succeeded)
1105             {
1106                 //tidy up output polygons and fix orientations where necessary ...
1107                 foreach (OutRec outRec in m_PolyOuts)
1108                 {
1109                   if (outRec.pts == null) continue;
1110                   FixupOutPolygon(outRec);
1111                   if (outRec.pts == null) continue;
1112                   if (outRec.isHole && fixHoleLinkages) FixHoleLinkage(outRec);
1113 
1114                   if (outRec.bottomPt == outRec.bottomFlag &&
1115                     (Orientation(outRec, m_UseFullRange) != (Area(outRec, m_UseFullRange) > 0)))
1116                   {
1117                       DisposeBottomPt(outRec);
1118                       FixupOutPolygon(outRec);
1119                   }
1120 
1121                   if (outRec.isHole == (m_ReverseOutput ^ Orientation(outRec, m_UseFullRange)))
1122                     ReversePolyPtLinks(outRec.pts);
1123                 }
1124 
1125                 JoinCommonEdges(fixHoleLinkages);
1126                 if (fixHoleLinkages) m_PolyOuts.Sort(new Comparison<OutRec>(PolySort));
1127             }
1128             m_Joins.Clear();
1129             m_HorizJoins.Clear();
1130             return succeeded;
1131         }
1132         //------------------------------------------------------------------------------
1133 
PopScanbeam()1134         private Int64 PopScanbeam()
1135         {
1136           Int64 Y = m_Scanbeam.Y;
1137           m_Scanbeam = m_Scanbeam.next;
1138           return Y;
1139         }
1140         //------------------------------------------------------------------------------
1141 
DisposeAllPolyPts()1142         private void DisposeAllPolyPts(){
1143           for (int i = 0; i < m_PolyOuts.Count; ++i) DisposeOutRec(i);
1144           m_PolyOuts.Clear();
1145         }
1146         //------------------------------------------------------------------------------
1147 
DisposeBottomPt(OutRec outRec)1148         void DisposeBottomPt(OutRec outRec)
1149         {
1150           OutPt next = outRec.bottomPt.next;
1151           OutPt prev = outRec.bottomPt.prev;
1152           if (outRec.pts == outRec.bottomPt) outRec.pts = next;
1153           outRec.bottomPt = null;
1154           next.prev = prev;
1155           prev.next = next;
1156           outRec.bottomPt = next;
1157         }
1158         //------------------------------------------------------------------------------
1159 
DisposeOutRec(int index)1160         void DisposeOutRec(int index)
1161         {
1162           OutRec outRec = m_PolyOuts[index];
1163           if (outRec.pts != null) DisposeOutPts(outRec.pts);
1164           outRec = null;
1165           m_PolyOuts[index] = null;
1166         }
1167         //------------------------------------------------------------------------------
1168 
DisposeOutPts(OutPt pp)1169         private void DisposeOutPts(OutPt pp)
1170         {
1171             if (pp == null) return;
1172             pp.prev.next = null;
1173             while (pp != null)
1174             {
1175                 pp = pp.next;
1176             }
1177         }
1178         //------------------------------------------------------------------------------
1179 
AddJoin(TEdge e1, TEdge e2, int e1OutIdx, int e2OutIdx)1180         private void AddJoin(TEdge e1, TEdge e2, int e1OutIdx, int e2OutIdx)
1181         {
1182             JoinRec jr = new JoinRec();
1183             if (e1OutIdx >= 0)
1184                 jr.poly1Idx = e1OutIdx; else
1185             jr.poly1Idx = e1.outIdx;
1186             jr.pt1a = new IntPoint(e1.xcurr, e1.ycurr);
1187             jr.pt1b = new IntPoint(e1.xtop, e1.ytop);
1188             if (e2OutIdx >= 0)
1189                 jr.poly2Idx = e2OutIdx; else
1190                 jr.poly2Idx = e2.outIdx;
1191             jr.pt2a = new IntPoint(e2.xcurr, e2.ycurr);
1192             jr.pt2b = new IntPoint(e2.xtop, e2.ytop);
1193             m_Joins.Add(jr);
1194         }
1195         //------------------------------------------------------------------------------
1196 
AddHorzJoin(TEdge e, int idx)1197         private void AddHorzJoin(TEdge e, int idx)
1198         {
1199             HorzJoinRec hj = new HorzJoinRec();
1200             hj.edge = e;
1201             hj.savedIdx = idx;
1202             m_HorizJoins.Add(hj);
1203         }
1204         //------------------------------------------------------------------------------
1205 
InsertLocalMinimaIntoAEL(Int64 botY)1206         private void InsertLocalMinimaIntoAEL(Int64 botY)
1207         {
1208           while(  m_CurrentLM != null  && ( m_CurrentLM.Y == botY ) )
1209           {
1210             TEdge lb = m_CurrentLM.leftBound;
1211             TEdge rb = m_CurrentLM.rightBound;
1212 
1213             InsertEdgeIntoAEL( lb );
1214             InsertScanbeam( lb.ytop );
1215             InsertEdgeIntoAEL( rb );
1216 
1217             if (IsEvenOddFillType(lb))
1218             {
1219                 lb.windDelta = 1;
1220                 rb.windDelta = 1;
1221             }
1222             else
1223             {
1224                 rb.windDelta = -lb.windDelta;
1225             }
1226             SetWindingCount(lb);
1227             rb.windCnt = lb.windCnt;
1228             rb.windCnt2 = lb.windCnt2;
1229 
1230             if(  rb.dx == horizontal )
1231             {
1232               //nb: only rightbounds can have a horizontal bottom edge
1233               AddEdgeToSEL( rb );
1234               InsertScanbeam( rb.nextInLML.ytop );
1235             }
1236             else
1237               InsertScanbeam( rb.ytop );
1238 
1239             if( IsContributing(lb) )
1240                 AddLocalMinPoly(lb, rb, new IntPoint(lb.xcurr, m_CurrentLM.Y));
1241 
1242             //if any output polygons share an edge, they'll need joining later ...
1243             if (rb.outIdx >= 0)
1244             {
1245                 if (rb.dx == horizontal)
1246                 {
1247                     for (int i = 0; i < m_HorizJoins.Count; i++)
1248                     {
1249                         IntPoint pt = new IntPoint(), pt2 = new IntPoint(); //used as dummy params.
1250                         HorzJoinRec hj = m_HorizJoins[i];
1251                         //if horizontals rb and hj.edge overlap, flag for joining later ...
1252                         if (GetOverlapSegment(new IntPoint(hj.edge.xbot, hj.edge.ybot),
1253                             new IntPoint(hj.edge.xtop, hj.edge.ytop),
1254                             new IntPoint(rb.xbot, rb.ybot),
1255                             new IntPoint(rb.xtop, rb.ytop),
1256                             ref pt, ref pt2))
1257                             AddJoin(hj.edge, rb, hj.savedIdx, -1);
1258                     }
1259                 }
1260             }
1261 
1262 
1263             if( lb.nextInAEL != rb )
1264             {
1265                 if (rb.outIdx >= 0 && rb.prevInAEL.outIdx >= 0 &&
1266                     SlopesEqual(rb.prevInAEL, rb, m_UseFullRange))
1267                     AddJoin(rb, rb.prevInAEL, -1, -1);
1268 
1269               TEdge e = lb.nextInAEL;
1270               IntPoint pt = new IntPoint(lb.xcurr, lb.ycurr);
1271               while( e != rb )
1272               {
1273                 if(e == null)
1274                     throw new ClipperException("InsertLocalMinimaIntoAEL: missing rightbound!");
1275                 //nb: For calculating winding counts etc, IntersectEdges() assumes
1276                 //that param1 will be to the right of param2 ABOVE the intersection ...
1277                 IntersectEdges( rb , e , pt , Protects.ipNone); //order important here
1278                 e = e.nextInAEL;
1279               }
1280             }
1281             PopLocalMinima();
1282           }
1283         }
1284         //------------------------------------------------------------------------------
1285 
InsertEdgeIntoAEL(TEdge edge)1286         private void InsertEdgeIntoAEL(TEdge edge)
1287         {
1288           edge.prevInAEL = null;
1289           edge.nextInAEL = null;
1290           if (m_ActiveEdges == null)
1291           {
1292             m_ActiveEdges = edge;
1293           }
1294           else if( E2InsertsBeforeE1(m_ActiveEdges, edge) )
1295           {
1296             edge.nextInAEL = m_ActiveEdges;
1297             m_ActiveEdges.prevInAEL = edge;
1298             m_ActiveEdges = edge;
1299           } else
1300           {
1301             TEdge e = m_ActiveEdges;
1302             while (e.nextInAEL != null && !E2InsertsBeforeE1(e.nextInAEL, edge))
1303               e = e.nextInAEL;
1304             edge.nextInAEL = e.nextInAEL;
1305             if (e.nextInAEL != null) e.nextInAEL.prevInAEL = edge;
1306             edge.prevInAEL = e;
1307             e.nextInAEL = edge;
1308           }
1309         }
1310         //----------------------------------------------------------------------
1311 
E2InsertsBeforeE1(TEdge e1, TEdge e2)1312         private bool E2InsertsBeforeE1(TEdge e1, TEdge e2)
1313         {
1314           return e2.xcurr == e1.xcurr? e2.dx > e1.dx : e2.xcurr < e1.xcurr;
1315         }
1316         //------------------------------------------------------------------------------
1317 
IsEvenOddFillType(TEdge edge)1318         private bool IsEvenOddFillType(TEdge edge)
1319         {
1320           if (edge.polyType == PolyType.ptSubject)
1321               return m_SubjFillType == PolyFillType.pftEvenOdd;
1322           else
1323               return m_ClipFillType == PolyFillType.pftEvenOdd;
1324         }
1325         //------------------------------------------------------------------------------
1326 
IsEvenOddAltFillType(TEdge edge)1327         private bool IsEvenOddAltFillType(TEdge edge)
1328         {
1329           if (edge.polyType == PolyType.ptSubject)
1330               return m_ClipFillType == PolyFillType.pftEvenOdd;
1331           else
1332               return m_SubjFillType == PolyFillType.pftEvenOdd;
1333         }
1334         //------------------------------------------------------------------------------
1335 
IsContributing(TEdge edge)1336         private bool IsContributing(TEdge edge)
1337         {
1338             PolyFillType pft, pft2;
1339             if (edge.polyType == PolyType.ptSubject)
1340             {
1341                 pft = m_SubjFillType;
1342                 pft2 = m_ClipFillType;
1343             }
1344             else
1345             {
1346                 pft = m_ClipFillType;
1347                 pft2 = m_SubjFillType;
1348             }
1349 
1350             switch (pft)
1351             {
1352                 case PolyFillType.pftEvenOdd:
1353                 case PolyFillType.pftNonZero:
1354                     if (Math.Abs(edge.windCnt) != 1) return false;
1355                     break;
1356                 case PolyFillType.pftPositive:
1357                     if (edge.windCnt != 1) return false;
1358                     break;
1359                 default: //PolyFillType.pftNegative
1360                     if (edge.windCnt != -1) return false;
1361                     break;
1362             }
1363 
1364             switch (m_ClipType)
1365             {
1366                 case ClipType.ctIntersection:
1367                     switch (pft2)
1368                     {
1369                         case PolyFillType.pftEvenOdd:
1370                         case PolyFillType.pftNonZero:
1371                             return (edge.windCnt2 != 0);
1372                         case PolyFillType.pftPositive:
1373                             return (edge.windCnt2 > 0);
1374                         default:
1375                             return (edge.windCnt2 < 0);
1376                     }
1377                 case ClipType.ctUnion:
1378                     switch (pft2)
1379                     {
1380                         case PolyFillType.pftEvenOdd:
1381                         case PolyFillType.pftNonZero:
1382                             return (edge.windCnt2 == 0);
1383                         case PolyFillType.pftPositive:
1384                             return (edge.windCnt2 <= 0);
1385                         default:
1386                             return (edge.windCnt2 >= 0);
1387                     }
1388                 case ClipType.ctDifference:
1389                     if (edge.polyType == PolyType.ptSubject)
1390                         switch (pft2)
1391                         {
1392                             case PolyFillType.pftEvenOdd:
1393                             case PolyFillType.pftNonZero:
1394                                 return (edge.windCnt2 == 0);
1395                             case PolyFillType.pftPositive:
1396                                 return (edge.windCnt2 <= 0);
1397                             default:
1398                                 return (edge.windCnt2 >= 0);
1399                         }
1400                     else
1401                         switch (pft2)
1402                         {
1403                             case PolyFillType.pftEvenOdd:
1404                             case PolyFillType.pftNonZero:
1405                                 return (edge.windCnt2 != 0);
1406                             case PolyFillType.pftPositive:
1407                                 return (edge.windCnt2 > 0);
1408                             default:
1409                                 return (edge.windCnt2 < 0);
1410                         }
1411             }
1412             return true;
1413         }
1414         //------------------------------------------------------------------------------
1415 
SetWindingCount(TEdge edge)1416         private void SetWindingCount(TEdge edge)
1417         {
1418             TEdge e = edge.prevInAEL;
1419             //find the edge of the same polytype that immediately preceeds 'edge' in AEL
1420             while (e != null && e.polyType != edge.polyType)
1421                 e = e.prevInAEL;
1422             if (e == null)
1423             {
1424                 edge.windCnt = edge.windDelta;
1425                 edge.windCnt2 = 0;
1426                 e = m_ActiveEdges; //ie get ready to calc windCnt2
1427             }
1428             else if (IsEvenOddFillType(edge))
1429             {
1430                 //even-odd filling ...
1431                 edge.windCnt = 1;
1432                 edge.windCnt2 = e.windCnt2;
1433                 e = e.nextInAEL; //ie get ready to calc windCnt2
1434             }
1435             else
1436             {
1437                 //nonZero filling ...
1438                 if (e.windCnt * e.windDelta < 0)
1439                 {
1440                     if (Math.Abs(e.windCnt) > 1)
1441                     {
1442                         if (e.windDelta * edge.windDelta < 0)
1443                             edge.windCnt = e.windCnt;
1444                         else
1445                             edge.windCnt = e.windCnt + edge.windDelta;
1446                     }
1447                     else
1448                         edge.windCnt = e.windCnt + e.windDelta + edge.windDelta;
1449                 }
1450                 else
1451                 {
1452                     if (Math.Abs(e.windCnt) > 1 && e.windDelta * edge.windDelta < 0)
1453                         edge.windCnt = e.windCnt;
1454                     else if (e.windCnt + edge.windDelta == 0)
1455                         edge.windCnt = e.windCnt;
1456                     else
1457                         edge.windCnt = e.windCnt + edge.windDelta;
1458                 }
1459                 edge.windCnt2 = e.windCnt2;
1460                 e = e.nextInAEL; //ie get ready to calc windCnt2
1461             }
1462 
1463             //update windCnt2 ...
1464             if (IsEvenOddAltFillType(edge))
1465             {
1466                 //even-odd filling ...
1467                 while (e != edge)
1468                 {
1469                     edge.windCnt2 = (edge.windCnt2 == 0) ? 1 : 0;
1470                     e = e.nextInAEL;
1471                 }
1472             }
1473             else
1474             {
1475                 //nonZero filling ...
1476                 while (e != edge)
1477                 {
1478                     edge.windCnt2 += e.windDelta;
1479                     e = e.nextInAEL;
1480                 }
1481             }
1482         }
1483         //------------------------------------------------------------------------------
1484 
AddEdgeToSEL(TEdge edge)1485         private void AddEdgeToSEL(TEdge edge)
1486         {
1487             //SEL pointers in PEdge are reused to build a list of horizontal edges.
1488             //However, we don't need to worry about order with horizontal edge processing.
1489             if (m_SortedEdges == null)
1490             {
1491                 m_SortedEdges = edge;
1492                 edge.prevInSEL = null;
1493                 edge.nextInSEL = null;
1494             }
1495             else
1496             {
1497                 edge.nextInSEL = m_SortedEdges;
1498                 edge.prevInSEL = null;
1499                 m_SortedEdges.prevInSEL = edge;
1500                 m_SortedEdges = edge;
1501             }
1502         }
1503         //------------------------------------------------------------------------------
1504 
CopyAELToSEL()1505         private void CopyAELToSEL()
1506         {
1507             TEdge e = m_ActiveEdges;
1508             m_SortedEdges = e;
1509             if (m_ActiveEdges == null)
1510                 return;
1511             m_SortedEdges.prevInSEL = null;
1512             e = e.nextInAEL;
1513             while (e != null)
1514             {
1515                 e.prevInSEL = e.prevInAEL;
1516                 e.prevInSEL.nextInSEL = e;
1517                 e.nextInSEL = null;
1518                 e = e.nextInAEL;
1519             }
1520         }
1521         //------------------------------------------------------------------------------
1522 
SwapPositionsInAEL(TEdge edge1, TEdge edge2)1523         private void SwapPositionsInAEL(TEdge edge1, TEdge edge2)
1524         {
1525             if (edge1.nextInAEL == null && edge1.prevInAEL == null)
1526                 return;
1527             if (edge2.nextInAEL == null && edge2.prevInAEL == null)
1528                 return;
1529 
1530             if (edge1.nextInAEL == edge2)
1531             {
1532                 TEdge next = edge2.nextInAEL;
1533                 if (next != null)
1534                     next.prevInAEL = edge1;
1535                 TEdge prev = edge1.prevInAEL;
1536                 if (prev != null)
1537                     prev.nextInAEL = edge2;
1538                 edge2.prevInAEL = prev;
1539                 edge2.nextInAEL = edge1;
1540                 edge1.prevInAEL = edge2;
1541                 edge1.nextInAEL = next;
1542             }
1543             else if (edge2.nextInAEL == edge1)
1544             {
1545                 TEdge next = edge1.nextInAEL;
1546                 if (next != null)
1547                     next.prevInAEL = edge2;
1548                 TEdge prev = edge2.prevInAEL;
1549                 if (prev != null)
1550                     prev.nextInAEL = edge1;
1551                 edge1.prevInAEL = prev;
1552                 edge1.nextInAEL = edge2;
1553                 edge2.prevInAEL = edge1;
1554                 edge2.nextInAEL = next;
1555             }
1556             else
1557             {
1558                 TEdge next = edge1.nextInAEL;
1559                 TEdge prev = edge1.prevInAEL;
1560                 edge1.nextInAEL = edge2.nextInAEL;
1561                 if (edge1.nextInAEL != null)
1562                     edge1.nextInAEL.prevInAEL = edge1;
1563                 edge1.prevInAEL = edge2.prevInAEL;
1564                 if (edge1.prevInAEL != null)
1565                     edge1.prevInAEL.nextInAEL = edge1;
1566                 edge2.nextInAEL = next;
1567                 if (edge2.nextInAEL != null)
1568                     edge2.nextInAEL.prevInAEL = edge2;
1569                 edge2.prevInAEL = prev;
1570                 if (edge2.prevInAEL != null)
1571                     edge2.prevInAEL.nextInAEL = edge2;
1572             }
1573 
1574             if (edge1.prevInAEL == null)
1575                 m_ActiveEdges = edge1;
1576             else if (edge2.prevInAEL == null)
1577                 m_ActiveEdges = edge2;
1578         }
1579         //------------------------------------------------------------------------------
1580 
SwapPositionsInSEL(TEdge edge1, TEdge edge2)1581         private void SwapPositionsInSEL(TEdge edge1, TEdge edge2)
1582         {
1583             if (edge1.nextInSEL == null && edge1.prevInSEL == null)
1584                 return;
1585             if (edge2.nextInSEL == null && edge2.prevInSEL == null)
1586                 return;
1587 
1588             if (edge1.nextInSEL == edge2)
1589             {
1590                 TEdge next = edge2.nextInSEL;
1591                 if (next != null)
1592                     next.prevInSEL = edge1;
1593                 TEdge prev = edge1.prevInSEL;
1594                 if (prev != null)
1595                     prev.nextInSEL = edge2;
1596                 edge2.prevInSEL = prev;
1597                 edge2.nextInSEL = edge1;
1598                 edge1.prevInSEL = edge2;
1599                 edge1.nextInSEL = next;
1600             }
1601             else if (edge2.nextInSEL == edge1)
1602             {
1603                 TEdge next = edge1.nextInSEL;
1604                 if (next != null)
1605                     next.prevInSEL = edge2;
1606                 TEdge prev = edge2.prevInSEL;
1607                 if (prev != null)
1608                     prev.nextInSEL = edge1;
1609                 edge1.prevInSEL = prev;
1610                 edge1.nextInSEL = edge2;
1611                 edge2.prevInSEL = edge1;
1612                 edge2.nextInSEL = next;
1613             }
1614             else
1615             {
1616                 TEdge next = edge1.nextInSEL;
1617                 TEdge prev = edge1.prevInSEL;
1618                 edge1.nextInSEL = edge2.nextInSEL;
1619                 if (edge1.nextInSEL != null)
1620                     edge1.nextInSEL.prevInSEL = edge1;
1621                 edge1.prevInSEL = edge2.prevInSEL;
1622                 if (edge1.prevInSEL != null)
1623                     edge1.prevInSEL.nextInSEL = edge1;
1624                 edge2.nextInSEL = next;
1625                 if (edge2.nextInSEL != null)
1626                     edge2.nextInSEL.prevInSEL = edge2;
1627                 edge2.prevInSEL = prev;
1628                 if (edge2.prevInSEL != null)
1629                     edge2.prevInSEL.nextInSEL = edge2;
1630             }
1631 
1632             if (edge1.prevInSEL == null)
1633                 m_SortedEdges = edge1;
1634             else if (edge2.prevInSEL == null)
1635                 m_SortedEdges = edge2;
1636         }
1637         //------------------------------------------------------------------------------
1638 
1639 
AddLocalMaxPoly(TEdge e1, TEdge e2, IntPoint pt)1640         private void AddLocalMaxPoly(TEdge e1, TEdge e2, IntPoint pt)
1641         {
1642             AddOutPt(e1, pt);
1643             if (e1.outIdx == e2.outIdx)
1644             {
1645                 e1.outIdx = -1;
1646                 e2.outIdx = -1;
1647             }
1648             else if (e1.outIdx < e2.outIdx)
1649                 AppendPolygon(e1, e2);
1650             else
1651                 AppendPolygon(e2, e1);
1652         }
1653         //------------------------------------------------------------------------------
1654 
AddLocalMinPoly(TEdge e1, TEdge e2, IntPoint pt)1655         private void AddLocalMinPoly(TEdge e1, TEdge e2, IntPoint pt)
1656         {
1657             TEdge e, prevE;
1658             if (e2.dx == horizontal || (e1.dx > e2.dx))
1659             {
1660                 AddOutPt(e1, pt);
1661                 e2.outIdx = e1.outIdx;
1662                 e1.side = EdgeSide.esLeft;
1663                 e2.side = EdgeSide.esRight;
1664                 e = e1;
1665                 if (e.prevInAEL == e2)
1666                   prevE = e2.prevInAEL;
1667                 else
1668                   prevE = e.prevInAEL;
1669             }
1670             else
1671             {
1672                 AddOutPt(e2, pt);
1673                 e1.outIdx = e2.outIdx;
1674                 e1.side = EdgeSide.esRight;
1675                 e2.side = EdgeSide.esLeft;
1676                 e = e2;
1677                 if (e.prevInAEL == e1)
1678                     prevE = e1.prevInAEL;
1679                 else
1680                     prevE = e.prevInAEL;
1681             }
1682 
1683             if (prevE != null && prevE.outIdx >= 0 &&
1684                 (TopX(prevE, pt.Y) == TopX(e, pt.Y)) &&
1685                  SlopesEqual(e, prevE, m_UseFullRange))
1686                    AddJoin(e, prevE, -1, -1);
1687 
1688         }
1689         //------------------------------------------------------------------------------
1690 
CreateOutRec()1691         private OutRec CreateOutRec()
1692         {
1693           OutRec result = new OutRec();
1694           result.idx = -1;
1695           result.isHole = false;
1696           result.FirstLeft = null;
1697           result.AppendLink = null;
1698           result.pts = null;
1699           result.bottomPt = null;
1700           result.bottomFlag = null;
1701           result.sides = EdgeSide.esNeither;
1702           return result;
1703         }
1704         //------------------------------------------------------------------------------
1705 
AddOutPt(TEdge e, IntPoint pt)1706         private void AddOutPt(TEdge e, IntPoint pt)
1707         {
1708           bool ToFront = (e.side == EdgeSide.esLeft);
1709           if(  e.outIdx < 0 )
1710           {
1711               OutRec outRec = CreateOutRec();
1712               m_PolyOuts.Add(outRec);
1713               outRec.idx = m_PolyOuts.Count -1;
1714               e.outIdx = outRec.idx;
1715               OutPt op = new OutPt();
1716               outRec.pts = op;
1717               outRec.bottomPt = op;
1718               op.pt = pt;
1719               op.idx = outRec.idx;
1720               op.next = op;
1721               op.prev = op;
1722               SetHoleState(e, outRec);
1723           } else
1724           {
1725               OutRec outRec = m_PolyOuts[e.outIdx];
1726               OutPt op = outRec.pts, op2, opBot;
1727               if (ToFront && PointsEqual(pt, op.pt) ||
1728                   (!ToFront && PointsEqual(pt, op.prev.pt))) return;
1729 
1730               if ((e.side | outRec.sides) != outRec.sides)
1731               {
1732                   //check for 'rounding' artefacts ...
1733                   if (outRec.sides == EdgeSide.esNeither && pt.Y == op.pt.Y)
1734                       if (ToFront)
1735                       {
1736                           if (pt.X == op.pt.X + 1) return;    //ie wrong side of bottomPt
1737                       }
1738                       else if (pt.X == op.pt.X - 1) return; //ie wrong side of bottomPt
1739 
1740                   outRec.sides = (EdgeSide)(outRec.sides | e.side);
1741                   if (outRec.sides == EdgeSide.esBoth)
1742                   {
1743                     //A vertex from each side has now been added.
1744                     //Vertices of one side of an output polygon are quite commonly close to
1745                     //or even 'touching' edges of the other side of the output polygon.
1746                     //Very occasionally vertices from one side can 'cross' an edge on the
1747                     //the other side. The distance 'crossed' is always less that a unit
1748                     //and is purely an artefact of coordinate rounding. Nevertheless, this
1749                     //results in very tiny self-intersections. Because of the way
1750                     //orientation is calculated, even tiny self-intersections can cause
1751                     //the Orientation function to return the wrong result. Therefore, it's
1752                     //important to ensure that any self-intersections close to BottomPt are
1753                     //detected and removed before orientation is assigned.
1754 
1755                     if (ToFront)
1756                     {
1757                       opBot = outRec.pts;
1758                       op2 = opBot.next; //op2 == right side
1759                       if (opBot.pt.Y != op2.pt.Y && opBot.pt.Y != pt.Y &&
1760                         ((opBot.pt.X - pt.X) / (opBot.pt.Y - pt.Y) <
1761                         (opBot.pt.X - op2.pt.X) / (opBot.pt.Y - op2.pt.Y)))
1762                           outRec.bottomFlag = opBot;
1763                     }
1764                     else
1765                     {
1766                       opBot = outRec.pts.prev;
1767                       op2 = opBot.next; //op2 == left side
1768                       if (opBot.pt.Y != op2.pt.Y && opBot.pt.Y != pt.Y &&
1769                         ((opBot.pt.X - pt.X) / (opBot.pt.Y - pt.Y) >
1770                         (opBot.pt.X - op2.pt.X) / (opBot.pt.Y - op2.pt.Y)))
1771                           outRec.bottomFlag = opBot;
1772                     }
1773                   }
1774               }
1775 
1776               op2 = new OutPt();
1777               op2.pt = pt;
1778               op2.idx = outRec.idx;
1779               if (op2.pt.Y == outRec.bottomPt.pt.Y &&
1780                 op2.pt.X < outRec.bottomPt.pt.X)
1781                   outRec.bottomPt = op2;
1782               op2.next = op;
1783               op2.prev = op.prev;
1784               op2.prev.next = op2;
1785               op.prev = op2;
1786               if (ToFront) outRec.pts = op2;
1787           }
1788         }
1789         //------------------------------------------------------------------------------
1790 
SwapPoints(ref IntPoint pt1, ref IntPoint pt2)1791         internal void SwapPoints(ref IntPoint pt1, ref IntPoint pt2)
1792         {
1793             IntPoint tmp = pt1;
1794             pt1 = pt2;
1795             pt2 = tmp;
1796         }
1797         //------------------------------------------------------------------------------
1798 
GetOverlapSegment(IntPoint pt1a, IntPoint pt1b, IntPoint pt2a, IntPoint pt2b, ref IntPoint pt1, ref IntPoint pt2)1799         private bool GetOverlapSegment(IntPoint pt1a, IntPoint pt1b, IntPoint pt2a,
1800             IntPoint pt2b, ref IntPoint pt1, ref IntPoint pt2)
1801         {
1802             //precondition: segments are colinear.
1803             if ( pt1a.Y == pt1b.Y || Math.Abs((pt1a.X - pt1b.X)/(pt1a.Y - pt1b.Y)) > 1 )
1804             {
1805             if (pt1a.X > pt1b.X) SwapPoints(ref pt1a, ref pt1b);
1806             if (pt2a.X > pt2b.X) SwapPoints(ref pt2a, ref pt2b);
1807             if (pt1a.X > pt2a.X) pt1 = pt1a; else pt1 = pt2a;
1808             if (pt1b.X < pt2b.X) pt2 = pt1b; else pt2 = pt2b;
1809             return pt1.X < pt2.X;
1810             } else
1811             {
1812             if (pt1a.Y < pt1b.Y) SwapPoints(ref pt1a, ref pt1b);
1813             if (pt2a.Y < pt2b.Y) SwapPoints(ref pt2a, ref pt2b);
1814             if (pt1a.Y < pt2a.Y) pt1 = pt1a; else pt1 = pt2a;
1815             if (pt1b.Y > pt2b.Y) pt2 = pt1b; else pt2 = pt2b;
1816             return pt1.Y > pt2.Y;
1817             }
1818         }
1819         //------------------------------------------------------------------------------
1820 
FindSegment(ref OutPt pp, ref IntPoint pt1, ref IntPoint pt2)1821         private bool FindSegment(ref OutPt pp, ref IntPoint pt1, ref IntPoint pt2)
1822         {
1823             if (pp == null) return false;
1824             OutPt pp2 = pp;
1825             IntPoint pt1a = new IntPoint(pt1);
1826             IntPoint pt2a = new IntPoint(pt2);
1827             do
1828             {
1829                 if (SlopesEqual(pt1a, pt2a, pp.pt, pp.prev.pt, true) &&
1830                     SlopesEqual(pt1a, pt2a, pp.pt, true) &&
1831                     GetOverlapSegment(pt1a, pt2a, pp.pt, pp.prev.pt, ref pt1, ref pt2))
1832                         return true;
1833             pp = pp.next;
1834             }
1835             while (pp != pp2);
1836             return false;
1837         }
1838         //------------------------------------------------------------------------------
1839 
Pt3IsBetweenPt1AndPt2(IntPoint pt1, IntPoint pt2, IntPoint pt3)1840         internal bool Pt3IsBetweenPt1AndPt2(IntPoint pt1, IntPoint pt2, IntPoint pt3)
1841         {
1842             if (PointsEqual(pt1, pt3) || PointsEqual(pt2, pt3)) return true;
1843             else if (pt1.X != pt2.X) return (pt1.X < pt3.X) == (pt3.X < pt2.X);
1844             else return (pt1.Y < pt3.Y) == (pt3.Y < pt2.Y);
1845         }
1846         //------------------------------------------------------------------------------
1847 
InsertPolyPtBetween(OutPt p1, OutPt p2, IntPoint pt)1848         private OutPt InsertPolyPtBetween(OutPt p1, OutPt p2, IntPoint pt)
1849         {
1850             OutPt result = new OutPt();
1851             result.pt = pt;
1852             if (p2 == p1.next)
1853             {
1854                 p1.next = result;
1855                 p2.prev = result;
1856                 result.next = p2;
1857                 result.prev = p1;
1858             } else
1859             {
1860                 p2.next = result;
1861                 p1.prev = result;
1862                 result.next = p1;
1863                 result.prev = p2;
1864             }
1865             return result;
1866         }
1867         //------------------------------------------------------------------------------
1868 
SetHoleState(TEdge e, OutRec outRec)1869         private void SetHoleState(TEdge e, OutRec outRec)
1870         {
1871             bool isHole = false;
1872             TEdge e2 = e.prevInAEL;
1873             while (e2 != null)
1874             {
1875                 if (e2.outIdx >= 0)
1876                 {
1877                     isHole = !isHole;
1878                     if (outRec.FirstLeft == null)
1879                         outRec.FirstLeft = m_PolyOuts[e2.outIdx];
1880                 }
1881                 e2 = e2.prevInAEL;
1882             }
1883             if (isHole) outRec.isHole = true;
1884         }
1885         //------------------------------------------------------------------------------
1886 
GetDx(IntPoint pt1, IntPoint pt2)1887         private double GetDx(IntPoint pt1, IntPoint pt2)
1888         {
1889             if (pt1.Y == pt2.Y) return horizontal;
1890             else return (double)(pt2.X - pt1.X) / (double)(pt2.Y - pt1.Y);
1891         }
1892         //---------------------------------------------------------------------------
1893 
FirstIsBottomPt(OutPt btmPt1, OutPt btmPt2)1894         private bool FirstIsBottomPt(OutPt btmPt1, OutPt btmPt2)
1895         {
1896           OutPt p = btmPt1.prev;
1897           while (PointsEqual(p.pt, btmPt1.pt) && (p != btmPt1)) p = p.prev;
1898           double dx1p = Math.Abs(GetDx(btmPt1.pt, p.pt));
1899           p = btmPt1.next;
1900           while (PointsEqual(p.pt, btmPt1.pt) && (p != btmPt1)) p = p.next;
1901           double dx1n = Math.Abs(GetDx(btmPt1.pt, p.pt));
1902 
1903           p = btmPt2.prev;
1904           while (PointsEqual(p.pt, btmPt2.pt) && (p != btmPt2)) p = p.prev;
1905           double dx2p = Math.Abs(GetDx(btmPt2.pt, p.pt));
1906           p = btmPt2.next;
1907           while (PointsEqual(p.pt, btmPt2.pt) && (p != btmPt2)) p = p.next;
1908           double dx2n = Math.Abs(GetDx(btmPt2.pt, p.pt));
1909           return (dx1p >= dx2p && dx1p >= dx2n) || (dx1n >= dx2p && dx1n >= dx2n);
1910         }
1911         //------------------------------------------------------------------------------
1912 
GetBottomPt(OutPt pp)1913         private OutPt GetBottomPt(OutPt pp)
1914         {
1915           OutPt dups = null;
1916           OutPt p = pp.next;
1917           while (p != pp)
1918           {
1919             if (p.pt.Y > pp.pt.Y)
1920             {
1921               pp = p;
1922               dups = null;
1923             }
1924             else if (p.pt.Y == pp.pt.Y && p.pt.X <= pp.pt.X)
1925             {
1926               if (p.pt.X < pp.pt.X)
1927               {
1928                   dups = null;
1929                   pp = p;
1930               } else
1931               {
1932                 if (p.next != pp && p.prev != pp) dups = p;
1933               }
1934             }
1935             p = p.next;
1936           }
1937           if (dups != null)
1938           {
1939             //there appears to be at least 2 vertices at bottomPt so ...
1940             while (dups != p)
1941             {
1942               if (!FirstIsBottomPt(p, dups)) pp = dups;
1943               dups = dups.next;
1944               while (!PointsEqual(dups.pt, pp.pt)) dups = dups.next;
1945             }
1946           }
1947           return pp;
1948         }
1949         //------------------------------------------------------------------------------
1950 
GetLowermostRec(OutRec outRec1, OutRec outRec2)1951         private OutRec GetLowermostRec(OutRec outRec1, OutRec outRec2)
1952         {
1953             //work out which polygon fragment has the correct hole state ...
1954             OutPt bPt1 = outRec1.bottomPt;
1955             OutPt bPt2 = outRec2.bottomPt;
1956             if (bPt1.pt.Y > bPt2.pt.Y) return outRec1;
1957             else if (bPt1.pt.Y < bPt2.pt.Y) return outRec2;
1958             else if (bPt1.pt.X < bPt2.pt.X) return outRec1;
1959             else if (bPt1.pt.X > bPt2.pt.X) return outRec2;
1960             else if (bPt1.next == bPt1) return outRec2;
1961             else if (bPt2.next == bPt2) return outRec1;
1962             else if (FirstIsBottomPt(bPt1, bPt2)) return outRec1;
1963             else return outRec2;
1964         }
1965         //------------------------------------------------------------------------------
1966 
AppendPolygon(TEdge e1, TEdge e2)1967         private void AppendPolygon(TEdge e1, TEdge e2)
1968         {
1969           //get the start and ends of both output polygons ...
1970           OutRec outRec1 = m_PolyOuts[e1.outIdx];
1971           OutRec outRec2 = m_PolyOuts[e2.outIdx];
1972 
1973           OutRec holeStateRec;
1974           if (outRec1.FirstLeft == outRec2) holeStateRec = outRec2;
1975           else if (outRec2.FirstLeft == outRec1) holeStateRec = outRec1;
1976           else holeStateRec = GetLowermostRec(outRec1, outRec2);
1977 
1978           OutPt p1_lft = outRec1.pts;
1979           OutPt p1_rt = p1_lft.prev;
1980           OutPt p2_lft = outRec2.pts;
1981           OutPt p2_rt = p2_lft.prev;
1982 
1983           EdgeSide side;
1984           //join e2 poly onto e1 poly and delete pointers to e2 ...
1985           if(  e1.side == EdgeSide.esLeft )
1986           {
1987             if (e2.side == EdgeSide.esLeft)
1988             {
1989               //z y x a b c
1990               ReversePolyPtLinks(p2_lft);
1991               p2_lft.next = p1_lft;
1992               p1_lft.prev = p2_lft;
1993               p1_rt.next = p2_rt;
1994               p2_rt.prev = p1_rt;
1995               outRec1.pts = p2_rt;
1996             } else
1997             {
1998               //x y z a b c
1999               p2_rt.next = p1_lft;
2000               p1_lft.prev = p2_rt;
2001               p2_lft.prev = p1_rt;
2002               p1_rt.next = p2_lft;
2003               outRec1.pts = p2_lft;
2004             }
2005             side = EdgeSide.esLeft;
2006           } else
2007           {
2008             if (e2.side == EdgeSide.esRight)
2009             {
2010               //a b c z y x
2011               ReversePolyPtLinks( p2_lft );
2012               p1_rt.next = p2_rt;
2013               p2_rt.prev = p1_rt;
2014               p2_lft.next = p1_lft;
2015               p1_lft.prev = p2_lft;
2016             } else
2017             {
2018               //a b c x y z
2019               p1_rt.next = p2_lft;
2020               p2_lft.prev = p1_rt;
2021               p1_lft.prev = p2_rt;
2022               p2_rt.next = p1_lft;
2023             }
2024             side = EdgeSide.esRight;
2025           }
2026 
2027           if (holeStateRec == outRec2)
2028           {
2029               outRec1.bottomPt = outRec2.bottomPt;
2030               outRec1.bottomPt.idx = outRec1.idx;
2031               if (outRec2.FirstLeft != outRec1)
2032                   outRec1.FirstLeft = outRec2.FirstLeft;
2033               outRec1.isHole = outRec2.isHole;
2034           }
2035           outRec2.pts = null;
2036           outRec2.bottomPt = null;
2037           outRec2.AppendLink = outRec1;
2038           int OKIdx = e1.outIdx;
2039           int ObsoleteIdx = e2.outIdx;
2040 
2041           e1.outIdx = -1; //nb: safe because we only get here via AddLocalMaxPoly
2042           e2.outIdx = -1;
2043 
2044           TEdge e = m_ActiveEdges;
2045           while( e != null )
2046           {
2047             if( e.outIdx == ObsoleteIdx )
2048             {
2049               e.outIdx = OKIdx;
2050               e.side = side;
2051               break;
2052             }
2053             e = e.nextInAEL;
2054           }
2055 
2056 
2057           for (int i = 0; i < m_Joins.Count; ++i)
2058           {
2059               if (m_Joins[i].poly1Idx == ObsoleteIdx) m_Joins[i].poly1Idx = OKIdx;
2060               if (m_Joins[i].poly2Idx == ObsoleteIdx) m_Joins[i].poly2Idx = OKIdx;
2061           }
2062 
2063           for (int i = 0; i < m_HorizJoins.Count; ++i)
2064           {
2065               if (m_HorizJoins[i].savedIdx == ObsoleteIdx)
2066                 m_HorizJoins[i].savedIdx = OKIdx;
2067           }
2068 
2069         }
2070         //------------------------------------------------------------------------------
2071 
ReversePolyPtLinks(OutPt pp)2072         private void ReversePolyPtLinks(OutPt pp)
2073         {
2074             OutPt pp1;
2075             OutPt pp2;
2076             pp1 = pp;
2077             do
2078             {
2079                 pp2 = pp1.next;
2080                 pp1.next = pp1.prev;
2081                 pp1.prev = pp2;
2082                 pp1 = pp2;
2083             } while (pp1 != pp);
2084         }
2085         //------------------------------------------------------------------------------
2086 
SwapSides(TEdge edge1, TEdge edge2)2087         private static void SwapSides(TEdge edge1, TEdge edge2)
2088         {
2089             EdgeSide side = edge1.side;
2090             edge1.side = edge2.side;
2091             edge2.side = side;
2092         }
2093         //------------------------------------------------------------------------------
2094 
SwapPolyIndexes(TEdge edge1, TEdge edge2)2095         private static void SwapPolyIndexes(TEdge edge1, TEdge edge2)
2096         {
2097             int outIdx = edge1.outIdx;
2098             edge1.outIdx = edge2.outIdx;
2099             edge2.outIdx = outIdx;
2100         }
2101         //------------------------------------------------------------------------------
2102 
DoEdge1(TEdge edge1, TEdge edge2, IntPoint pt)2103         private void DoEdge1(TEdge edge1, TEdge edge2, IntPoint pt)
2104         {
2105             AddOutPt(edge1, pt);
2106             SwapSides(edge1, edge2);
2107             SwapPolyIndexes(edge1, edge2);
2108         }
2109         //------------------------------------------------------------------------------
2110 
DoEdge2(TEdge edge1, TEdge edge2, IntPoint pt)2111         private void DoEdge2(TEdge edge1, TEdge edge2, IntPoint pt)
2112         {
2113             AddOutPt(edge2, pt);
2114             SwapSides(edge1, edge2);
2115             SwapPolyIndexes(edge1, edge2);
2116         }
2117         //------------------------------------------------------------------------------
2118 
DoBothEdges(TEdge edge1, TEdge edge2, IntPoint pt)2119         private void DoBothEdges(TEdge edge1, TEdge edge2, IntPoint pt)
2120         {
2121             AddOutPt(edge1, pt);
2122             AddOutPt(edge2, pt);
2123             SwapSides(edge1, edge2);
2124             SwapPolyIndexes(edge1, edge2);
2125         }
2126         //------------------------------------------------------------------------------
2127 
IntersectEdges(TEdge e1, TEdge e2, IntPoint pt, Protects protects)2128         private void IntersectEdges(TEdge e1, TEdge e2, IntPoint pt, Protects protects)
2129         {
2130             //e1 will be to the left of e2 BELOW the intersection. Therefore e1 is before
2131             //e2 in AEL except when e1 is being inserted at the intersection point ...
2132 
2133             bool e1stops = (Protects.ipLeft & protects) == 0 && e1.nextInLML == null &&
2134               e1.xtop == pt.X && e1.ytop == pt.Y;
2135             bool e2stops = (Protects.ipRight & protects) == 0 && e2.nextInLML == null &&
2136               e2.xtop == pt.X && e2.ytop == pt.Y;
2137             bool e1Contributing = (e1.outIdx >= 0);
2138             bool e2contributing = (e2.outIdx >= 0);
2139 
2140             //update winding counts...
2141             //assumes that e1 will be to the right of e2 ABOVE the intersection
2142             if (e1.polyType == e2.polyType)
2143             {
2144                 if (IsEvenOddFillType(e1))
2145                 {
2146                     int oldE1WindCnt = e1.windCnt;
2147                     e1.windCnt = e2.windCnt;
2148                     e2.windCnt = oldE1WindCnt;
2149                 }
2150                 else
2151                 {
2152                     if (e1.windCnt + e2.windDelta == 0) e1.windCnt = -e1.windCnt;
2153                     else e1.windCnt += e2.windDelta;
2154                     if (e2.windCnt - e1.windDelta == 0) e2.windCnt = -e2.windCnt;
2155                     else e2.windCnt -= e1.windDelta;
2156                 }
2157             }
2158             else
2159             {
2160                 if (!IsEvenOddFillType(e2)) e1.windCnt2 += e2.windDelta;
2161                 else e1.windCnt2 = (e1.windCnt2 == 0) ? 1 : 0;
2162                 if (!IsEvenOddFillType(e1)) e2.windCnt2 -= e1.windDelta;
2163                 else e2.windCnt2 = (e2.windCnt2 == 0) ? 1 : 0;
2164             }
2165 
2166             PolyFillType e1FillType, e2FillType, e1FillType2, e2FillType2;
2167             if (e1.polyType == PolyType.ptSubject)
2168             {
2169                 e1FillType = m_SubjFillType;
2170                 e1FillType2 = m_ClipFillType;
2171             }
2172             else
2173             {
2174                 e1FillType = m_ClipFillType;
2175                 e1FillType2 = m_SubjFillType;
2176             }
2177             if (e2.polyType == PolyType.ptSubject)
2178             {
2179                 e2FillType = m_SubjFillType;
2180                 e2FillType2 = m_ClipFillType;
2181             }
2182             else
2183             {
2184                 e2FillType = m_ClipFillType;
2185                 e2FillType2 = m_SubjFillType;
2186             }
2187 
2188             int e1Wc, e2Wc;
2189             switch (e1FillType)
2190             {
2191                 case PolyFillType.pftPositive: e1Wc = e1.windCnt; break;
2192                 case PolyFillType.pftNegative: e1Wc = -e1.windCnt; break;
2193                 default: e1Wc = Math.Abs(e1.windCnt); break;
2194             }
2195             switch (e2FillType)
2196             {
2197                 case PolyFillType.pftPositive: e2Wc = e2.windCnt; break;
2198                 case PolyFillType.pftNegative: e2Wc = -e2.windCnt; break;
2199                 default: e2Wc = Math.Abs(e2.windCnt); break;
2200             }
2201 
2202 
2203             if (e1Contributing && e2contributing)
2204             {
2205                 if ( e1stops || e2stops ||
2206                   (e1Wc != 0 && e1Wc != 1) || (e2Wc != 0 && e2Wc != 1) ||
2207                   (e1.polyType != e2.polyType && m_ClipType != ClipType.ctXor))
2208                     AddLocalMaxPoly(e1, e2, pt);
2209                 else
2210                     DoBothEdges(e1, e2, pt);
2211             }
2212             else if (e1Contributing)
2213             {
2214                 if ((e2Wc == 0 || e2Wc == 1) &&
2215                   (m_ClipType != ClipType.ctIntersection ||
2216                     e2.polyType == PolyType.ptSubject || (e2.windCnt2 != 0)))
2217                         DoEdge1(e1, e2, pt);
2218             }
2219             else if (e2contributing)
2220             {
2221                 if ((e1Wc == 0 || e1Wc == 1) &&
2222                   (m_ClipType != ClipType.ctIntersection ||
2223                                 e1.polyType == PolyType.ptSubject || (e1.windCnt2 != 0)))
2224                         DoEdge2(e1, e2, pt);
2225             }
2226             else if ( (e1Wc == 0 || e1Wc == 1) &&
2227                 (e2Wc == 0 || e2Wc == 1) && !e1stops && !e2stops )
2228             {
2229                 //neither edge is currently contributing ...
2230                 Int64 e1Wc2, e2Wc2;
2231                 switch (e1FillType2)
2232                 {
2233                     case PolyFillType.pftPositive: e1Wc2 = e1.windCnt2; break;
2234                     case PolyFillType.pftNegative: e1Wc2 = -e1.windCnt2; break;
2235                     default: e1Wc2 = Math.Abs(e1.windCnt2); break;
2236                 }
2237                 switch (e2FillType2)
2238                 {
2239                     case PolyFillType.pftPositive: e2Wc2 = e2.windCnt2; break;
2240                     case PolyFillType.pftNegative: e2Wc2 = -e2.windCnt2; break;
2241                     default: e2Wc2 = Math.Abs(e2.windCnt2); break;
2242                 }
2243 
2244                 if (e1.polyType != e2.polyType)
2245                     AddLocalMinPoly(e1, e2, pt);
2246                 else if (e1Wc == 1 && e2Wc == 1)
2247                     switch (m_ClipType)
2248                     {
2249                         case ClipType.ctIntersection:
2250                             {
2251                                 if (e1Wc2 > 0 && e2Wc2 > 0)
2252                                     AddLocalMinPoly(e1, e2, pt);
2253                                 break;
2254                             }
2255                         case ClipType.ctUnion:
2256                             {
2257                                 if (e1Wc2 <= 0 && e2Wc2 <= 0)
2258                                     AddLocalMinPoly(e1, e2, pt);
2259                                 break;
2260                             }
2261                         case ClipType.ctDifference:
2262                             {
2263                                 if (((e1.polyType == PolyType.ptClip) && (e1Wc2 > 0) && (e2Wc2 > 0)) ||
2264                                    ((e1.polyType == PolyType.ptSubject) && (e1Wc2 <= 0) && (e2Wc2 <= 0)))
2265                                         AddLocalMinPoly(e1, e2, pt);
2266                                 break;
2267                             }
2268                         case ClipType.ctXor:
2269                             {
2270                                 AddLocalMinPoly(e1, e2, pt);
2271                                 break;
2272                             }
2273                     }
2274                 else
2275                     SwapSides(e1, e2);
2276             }
2277 
2278             if ((e1stops != e2stops) &&
2279               ((e1stops && (e1.outIdx >= 0)) || (e2stops && (e2.outIdx >= 0))))
2280             {
2281                 SwapSides(e1, e2);
2282                 SwapPolyIndexes(e1, e2);
2283             }
2284 
2285             //finally, delete any non-contributing maxima edges  ...
2286             if (e1stops) DeleteFromAEL(e1);
2287             if (e2stops) DeleteFromAEL(e2);
2288         }
2289         //------------------------------------------------------------------------------
2290 
DeleteFromAEL(TEdge e)2291         private void DeleteFromAEL(TEdge e)
2292         {
2293             TEdge AelPrev = e.prevInAEL;
2294             TEdge AelNext = e.nextInAEL;
2295             if (AelPrev == null && AelNext == null && (e != m_ActiveEdges))
2296                 return; //already deleted
2297             if (AelPrev != null)
2298                 AelPrev.nextInAEL = AelNext;
2299             else m_ActiveEdges = AelNext;
2300             if (AelNext != null)
2301                 AelNext.prevInAEL = AelPrev;
2302             e.nextInAEL = null;
2303             e.prevInAEL = null;
2304         }
2305         //------------------------------------------------------------------------------
2306 
DeleteFromSEL(TEdge e)2307         private void DeleteFromSEL(TEdge e)
2308         {
2309             TEdge SelPrev = e.prevInSEL;
2310             TEdge SelNext = e.nextInSEL;
2311             if (SelPrev == null && SelNext == null && (e != m_SortedEdges))
2312                 return; //already deleted
2313             if (SelPrev != null)
2314                 SelPrev.nextInSEL = SelNext;
2315             else m_SortedEdges = SelNext;
2316             if (SelNext != null)
2317                 SelNext.prevInSEL = SelPrev;
2318             e.nextInSEL = null;
2319             e.prevInSEL = null;
2320         }
2321         //------------------------------------------------------------------------------
2322 
UpdateEdgeIntoAEL(ref TEdge e)2323         private void UpdateEdgeIntoAEL(ref TEdge e)
2324         {
2325             if (e.nextInLML == null)
2326                 throw new ClipperException("UpdateEdgeIntoAEL: invalid call");
2327             TEdge AelPrev = e.prevInAEL;
2328             TEdge AelNext = e.nextInAEL;
2329             e.nextInLML.outIdx = e.outIdx;
2330             if (AelPrev != null)
2331                 AelPrev.nextInAEL = e.nextInLML;
2332             else m_ActiveEdges = e.nextInLML;
2333             if (AelNext != null)
2334                 AelNext.prevInAEL = e.nextInLML;
2335             e.nextInLML.side = e.side;
2336             e.nextInLML.windDelta = e.windDelta;
2337             e.nextInLML.windCnt = e.windCnt;
2338             e.nextInLML.windCnt2 = e.windCnt2;
2339             e = e.nextInLML;
2340             e.prevInAEL = AelPrev;
2341             e.nextInAEL = AelNext;
2342             if (e.dx != horizontal) InsertScanbeam(e.ytop);
2343         }
2344         //------------------------------------------------------------------------------
2345 
ProcessHorizontals()2346         private void ProcessHorizontals()
2347         {
2348             TEdge horzEdge = m_SortedEdges;
2349             while (horzEdge != null)
2350             {
2351                 DeleteFromSEL(horzEdge);
2352                 ProcessHorizontal(horzEdge);
2353                 horzEdge = m_SortedEdges;
2354             }
2355         }
2356         //------------------------------------------------------------------------------
2357 
ProcessHorizontal(TEdge horzEdge)2358         private void ProcessHorizontal(TEdge horzEdge)
2359         {
2360             Direction Direction;
2361             Int64 horzLeft, horzRight;
2362 
2363             if (horzEdge.xcurr < horzEdge.xtop)
2364             {
2365                 horzLeft = horzEdge.xcurr;
2366                 horzRight = horzEdge.xtop;
2367                 Direction = Direction.dLeftToRight;
2368             }
2369             else
2370             {
2371                 horzLeft = horzEdge.xtop;
2372                 horzRight = horzEdge.xcurr;
2373                 Direction = Direction.dRightToLeft;
2374             }
2375 
2376             TEdge eMaxPair;
2377             if (horzEdge.nextInLML != null)
2378                 eMaxPair = null;
2379             else
2380                 eMaxPair = GetMaximaPair(horzEdge);
2381 
2382             TEdge e = GetNextInAEL(horzEdge, Direction);
2383             while (e != null)
2384             {
2385                 TEdge eNext = GetNextInAEL(e, Direction);
2386                 if (eMaxPair != null ||
2387                   ((Direction == Direction.dLeftToRight) && (e.xcurr <= horzRight)) ||
2388                   ((Direction == Direction.dRightToLeft) && (e.xcurr >= horzLeft)))
2389                 {
2390                     //ok, so far it looks like we're still in range of the horizontal edge
2391                     if (e.xcurr == horzEdge.xtop && eMaxPair == null)
2392                     {
2393                         if (SlopesEqual(e, horzEdge.nextInLML, m_UseFullRange))
2394                         {
2395                             //if output polygons share an edge, they'll need joining later ...
2396                             if (horzEdge.outIdx >= 0 && e.outIdx >= 0)
2397                                 AddJoin(horzEdge.nextInLML, e, horzEdge.outIdx, -1);
2398                             break; //we've reached the end of the horizontal line
2399                         }
2400                         else if (e.dx < horzEdge.nextInLML.dx)
2401                             //we really have got to the end of the intermediate horz edge so quit.
2402                             //nb: More -ve slopes follow more +ve slopes ABOVE the horizontal.
2403                             break;
2404                     }
2405 
2406                     if (e == eMaxPair)
2407                     {
2408                         //horzEdge is evidently a maxima horizontal and we've arrived at its end.
2409                         if (Direction == Direction.dLeftToRight)
2410                             IntersectEdges(horzEdge, e, new IntPoint(e.xcurr, horzEdge.ycurr), 0);
2411                         else
2412                             IntersectEdges(e, horzEdge, new IntPoint(e.xcurr, horzEdge.ycurr), 0);
2413                         if (eMaxPair.outIdx >= 0) throw new ClipperException("ProcessHorizontal error");
2414                         return;
2415                     }
2416                     else if (e.dx == horizontal && !IsMinima(e) && !(e.xcurr > e.xtop))
2417                     {
2418                         if (Direction == Direction.dLeftToRight)
2419                             IntersectEdges(horzEdge, e, new IntPoint(e.xcurr, horzEdge.ycurr),
2420                               (IsTopHorz(horzEdge, e.xcurr)) ? Protects.ipLeft : Protects.ipBoth);
2421                         else
2422                             IntersectEdges(e, horzEdge, new IntPoint(e.xcurr, horzEdge.ycurr),
2423                               (IsTopHorz(horzEdge, e.xcurr)) ? Protects.ipRight : Protects.ipBoth);
2424                     }
2425                     else if (Direction == Direction.dLeftToRight)
2426                     {
2427                         IntersectEdges(horzEdge, e, new IntPoint(e.xcurr, horzEdge.ycurr),
2428                           (IsTopHorz(horzEdge, e.xcurr)) ? Protects.ipLeft : Protects.ipBoth);
2429                     }
2430                     else
2431                     {
2432                         IntersectEdges(e, horzEdge, new IntPoint(e.xcurr, horzEdge.ycurr),
2433                           (IsTopHorz(horzEdge, e.xcurr)) ? Protects.ipRight : Protects.ipBoth);
2434                     }
2435                     SwapPositionsInAEL(horzEdge, e);
2436                 }
2437                 else if ( (Direction == Direction.dLeftToRight &&
2438                     e.xcurr > horzRight && horzEdge.nextInSEL == null) ||
2439                     (Direction == Direction.dRightToLeft &&
2440                     e.xcurr < horzLeft && horzEdge.nextInSEL == null) ) break;
2441                 e = eNext;
2442             } //end while ( e )
2443 
2444             if (horzEdge.nextInLML != null)
2445             {
2446                 if (horzEdge.outIdx >= 0)
2447                     AddOutPt(horzEdge, new IntPoint(horzEdge.xtop, horzEdge.ytop));
2448                 UpdateEdgeIntoAEL(ref horzEdge);
2449             }
2450             else
2451             {
2452                 if (horzEdge.outIdx >= 0)
2453                     IntersectEdges(horzEdge, eMaxPair,
2454                         new IntPoint(horzEdge.xtop, horzEdge.ycurr), Protects.ipBoth);
2455                 DeleteFromAEL(eMaxPair);
2456                 DeleteFromAEL(horzEdge);
2457             }
2458         }
2459         //------------------------------------------------------------------------------
2460 
IsTopHorz(TEdge horzEdge, double XPos)2461         private bool IsTopHorz(TEdge horzEdge, double XPos)
2462         {
2463             TEdge e = m_SortedEdges;
2464             while (e != null)
2465             {
2466                 if ((XPos >= Math.Min(e.xcurr, e.xtop)) && (XPos <= Math.Max(e.xcurr, e.xtop)))
2467                     return false;
2468                 e = e.nextInSEL;
2469             }
2470             return true;
2471         }
2472         //------------------------------------------------------------------------------
2473 
GetNextInAEL(TEdge e, Direction Direction)2474         private TEdge GetNextInAEL(TEdge e, Direction Direction)
2475         {
2476             return Direction == Direction.dLeftToRight ? e.nextInAEL: e.prevInAEL;
2477         }
2478         //------------------------------------------------------------------------------
2479 
IsMinima(TEdge e)2480         private bool IsMinima(TEdge e)
2481         {
2482             return e != null && (e.prev.nextInLML != e) && (e.next.nextInLML != e);
2483         }
2484         //------------------------------------------------------------------------------
2485 
IsMaxima(TEdge e, double Y)2486         private bool IsMaxima(TEdge e, double Y)
2487         {
2488             return (e != null && e.ytop == Y && e.nextInLML == null);
2489         }
2490         //------------------------------------------------------------------------------
2491 
IsIntermediate(TEdge e, double Y)2492         private bool IsIntermediate(TEdge e, double Y)
2493         {
2494             return (e.ytop == Y && e.nextInLML != null);
2495         }
2496         //------------------------------------------------------------------------------
2497 
GetMaximaPair(TEdge e)2498         private TEdge GetMaximaPair(TEdge e)
2499         {
2500             if (!IsMaxima(e.next, e.ytop) || (e.next.xtop != e.xtop))
2501                 return e.prev; else
2502                 return e.next;
2503         }
2504         //------------------------------------------------------------------------------
2505 
ProcessIntersections(Int64 botY, Int64 topY)2506         private bool ProcessIntersections(Int64 botY, Int64 topY)
2507         {
2508           if( m_ActiveEdges == null ) return true;
2509           try {
2510             BuildIntersectList(botY, topY);
2511             if ( m_IntersectNodes == null) return true;
2512             if ( FixupIntersections() ) ProcessIntersectList();
2513             else return false;
2514           }
2515           catch {
2516             m_SortedEdges = null;
2517             DisposeIntersectNodes();
2518             throw new ClipperException("ProcessIntersections error");
2519           }
2520           return true;
2521         }
2522         //------------------------------------------------------------------------------
2523 
BuildIntersectList(Int64 botY, Int64 topY)2524         private void BuildIntersectList(Int64 botY, Int64 topY)
2525         {
2526           if ( m_ActiveEdges == null ) return;
2527 
2528           //prepare for sorting ...
2529           TEdge e = m_ActiveEdges;
2530           e.tmpX = TopX( e, topY );
2531           m_SortedEdges = e;
2532           m_SortedEdges.prevInSEL = null;
2533           e = e.nextInAEL;
2534           while( e != null )
2535           {
2536             e.prevInSEL = e.prevInAEL;
2537             e.prevInSEL.nextInSEL = e;
2538             e.nextInSEL = null;
2539             e.tmpX = TopX( e, topY );
2540             e = e.nextInAEL;
2541           }
2542 
2543           //bubblesort ...
2544           bool isModified = true;
2545           while( isModified && m_SortedEdges != null )
2546           {
2547             isModified = false;
2548             e = m_SortedEdges;
2549             while( e.nextInSEL != null )
2550             {
2551               TEdge eNext = e.nextInSEL;
2552               IntPoint pt = new IntPoint();
2553               if(e.tmpX > eNext.tmpX && IntersectPoint(e, eNext, ref pt))
2554               {
2555                   if (pt.Y > botY)
2556                   {
2557                       pt.Y = botY;
2558                       pt.X = TopX(e, pt.Y);
2559                   }
2560                   AddIntersectNode(e, eNext, pt);
2561                   SwapPositionsInSEL(e, eNext);
2562                   isModified = true;
2563               }
2564               else
2565                 e = eNext;
2566             }
2567             if( e.prevInSEL != null ) e.prevInSEL.nextInSEL = null;
2568             else break;
2569           }
2570           m_SortedEdges = null;
2571         }
2572         //------------------------------------------------------------------------------
2573 
FixupIntersections()2574         private bool FixupIntersections()
2575         {
2576           if ( m_IntersectNodes.next == null ) return true;
2577 
2578           CopyAELToSEL();
2579           IntersectNode int1 = m_IntersectNodes;
2580           IntersectNode int2 = m_IntersectNodes.next;
2581           while (int2 != null)
2582           {
2583             TEdge e1 = int1.edge1;
2584             TEdge e2;
2585             if (e1.prevInSEL == int1.edge2) e2 = e1.prevInSEL;
2586             else if (e1.nextInSEL == int1.edge2) e2 = e1.nextInSEL;
2587             else
2588             {
2589               //The current intersection is out of order, so try and swap it with
2590               //a subsequent intersection ...
2591               while (int2 != null)
2592               {
2593                 if (int2.edge1.nextInSEL == int2.edge2 ||
2594                   int2.edge1.prevInSEL == int2.edge2) break;
2595                 else int2 = int2.next;
2596               }
2597               if (int2 == null) return false; //oops!!!
2598 
2599               //found an intersect node that can be swapped ...
2600               SwapIntersectNodes(int1, int2);
2601               e1 = int1.edge1;
2602               e2 = int1.edge2;
2603             }
2604             SwapPositionsInSEL(e1, e2);
2605             int1 = int1.next;
2606             int2 = int1.next;
2607           }
2608 
2609           m_SortedEdges = null;
2610 
2611           //finally, check the last intersection too ...
2612           return (int1.edge1.prevInSEL == int1.edge2 || int1.edge1.nextInSEL == int1.edge2);
2613         }
2614         //------------------------------------------------------------------------------
2615 
ProcessIntersectList()2616         private void ProcessIntersectList()
2617         {
2618           while( m_IntersectNodes != null )
2619           {
2620             IntersectNode iNode = m_IntersectNodes.next;
2621             {
2622               IntersectEdges( m_IntersectNodes.edge1 ,
2623                 m_IntersectNodes.edge2 , m_IntersectNodes.pt, Protects.ipBoth );
2624               SwapPositionsInAEL( m_IntersectNodes.edge1 , m_IntersectNodes.edge2 );
2625             }
2626             m_IntersectNodes = null;
2627             m_IntersectNodes = iNode;
2628           }
2629         }
2630         //------------------------------------------------------------------------------
2631 
Round(double value)2632         private static Int64 Round(double value)
2633         {
2634             return value < 0 ? (Int64)(value - 0.5) : (Int64)(value + 0.5);
2635         }
2636         //------------------------------------------------------------------------------
2637 
TopX(TEdge edge, Int64 currentY)2638         private static Int64 TopX(TEdge edge, Int64 currentY)
2639         {
2640             if (currentY == edge.ytop)
2641                 return edge.xtop;
2642             return edge.xbot + Round(edge.dx *(currentY - edge.ybot));
2643         }
2644         //------------------------------------------------------------------------------
2645 
TopX(IntPoint pt1, IntPoint pt2, Int64 currentY)2646         private Int64 TopX(IntPoint pt1, IntPoint pt2, Int64 currentY)
2647         {
2648           //preconditions: pt1.Y <> pt2.Y and pt1.Y > pt2.Y
2649           if (currentY >= pt1.Y) return pt1.X;
2650           else if (currentY == pt2.Y) return pt2.X;
2651           else if (pt1.X == pt2.X) return pt1.X;
2652           else
2653           {
2654             double q = (pt1.X-pt2.X)/(pt1.Y-pt2.Y);
2655             return (Int64)Round(pt1.X + (currentY - pt1.Y) * q);
2656           }
2657         }
2658         //------------------------------------------------------------------------------
2659 
AddIntersectNode(TEdge e1, TEdge e2, IntPoint pt)2660         private void AddIntersectNode(TEdge e1, TEdge e2, IntPoint pt)
2661         {
2662           IntersectNode newNode = new IntersectNode();
2663           newNode.edge1 = e1;
2664           newNode.edge2 = e2;
2665           newNode.pt = pt;
2666           newNode.next = null;
2667           if (m_IntersectNodes == null) m_IntersectNodes = newNode;
2668           else if( Process1Before2(newNode, m_IntersectNodes) )
2669           {
2670             newNode.next = m_IntersectNodes;
2671             m_IntersectNodes = newNode;
2672           }
2673           else
2674           {
2675             IntersectNode iNode = m_IntersectNodes;
2676             while( iNode.next != null  && Process1Before2(iNode.next, newNode) )
2677                 iNode = iNode.next;
2678             newNode.next = iNode.next;
2679             iNode.next = newNode;
2680           }
2681         }
2682         //------------------------------------------------------------------------------
2683 
Process1Before2(IntersectNode node1, IntersectNode node2)2684         private bool Process1Before2(IntersectNode node1, IntersectNode node2)
2685         {
2686           bool result;
2687           if (node1.pt.Y == node2.pt.Y)
2688           {
2689             if (node1.edge1 == node2.edge1 || node1.edge2 == node2.edge1)
2690             {
2691               result = node2.pt.X > node1.pt.X;
2692                 return node2.edge1.dx > 0 ? !result : result;
2693             }
2694             else if (node1.edge1 == node2.edge2 || node1.edge2 == node2.edge2)
2695             {
2696               result = node2.pt.X > node1.pt.X;
2697                 return node2.edge2.dx > 0 ? !result : result;
2698             }
2699             else return node2.pt.X > node1.pt.X;
2700           }
2701           else return node1.pt.Y > node2.pt.Y;
2702         }
2703         //------------------------------------------------------------------------------
2704 
SwapIntersectNodes(IntersectNode int1, IntersectNode int2)2705         private void SwapIntersectNodes(IntersectNode int1, IntersectNode int2)
2706         {
2707           TEdge e1 = int1.edge1;
2708           TEdge e2 = int1.edge2;
2709           IntPoint p = int1.pt;
2710           int1.edge1 = int2.edge1;
2711           int1.edge2 = int2.edge2;
2712           int1.pt = int2.pt;
2713           int2.edge1 = e1;
2714           int2.edge2 = e2;
2715           int2.pt = p;
2716         }
2717         //------------------------------------------------------------------------------
2718 
IntersectPoint(TEdge edge1, TEdge edge2, ref IntPoint ip)2719         private bool IntersectPoint(TEdge edge1, TEdge edge2, ref IntPoint ip)
2720         {
2721           double b1, b2;
2722           if (SlopesEqual(edge1, edge2, m_UseFullRange)) return false;
2723           else if (edge1.dx == 0)
2724           {
2725             ip.X = edge1.xbot;
2726             if (edge2.dx == horizontal)
2727             {
2728               ip.Y = edge2.ybot;
2729             } else
2730             {
2731               b2 = edge2.ybot - (edge2.xbot/edge2.dx);
2732               ip.Y = Round(ip.X/edge2.dx + b2);
2733             }
2734           }
2735           else if (edge2.dx == 0)
2736           {
2737             ip.X = edge2.xbot;
2738             if (edge1.dx == horizontal)
2739             {
2740               ip.Y = edge1.ybot;
2741             } else
2742             {
2743               b1 = edge1.ybot - (edge1.xbot/edge1.dx);
2744               ip.Y = Round(ip.X/edge1.dx + b1);
2745             }
2746           } else
2747           {
2748             b1 = edge1.xbot - edge1.ybot * edge1.dx;
2749             b2 = edge2.xbot - edge2.ybot * edge2.dx;
2750             b2 = (b2-b1)/(edge1.dx - edge2.dx);
2751             ip.Y = Round(b2);
2752             ip.X = Round(edge1.dx * b2 + b1);
2753           }
2754 
2755           return
2756             //can be *so close* to the top of one edge that the rounded Y equals one ytop ...
2757             (ip.Y == edge1.ytop && ip.Y >= edge2.ytop && edge1.tmpX > edge2.tmpX) ||
2758             (ip.Y == edge2.ytop && ip.Y >= edge1.ytop && edge1.tmpX > edge2.tmpX) ||
2759             (ip.Y > edge1.ytop && ip.Y > edge2.ytop);
2760         }
2761         //------------------------------------------------------------------------------
2762 
DisposeIntersectNodes()2763         private void DisposeIntersectNodes()
2764         {
2765           while ( m_IntersectNodes != null )
2766           {
2767             IntersectNode iNode = m_IntersectNodes.next;
2768             m_IntersectNodes = null;
2769             m_IntersectNodes = iNode;
2770           }
2771         }
2772         //------------------------------------------------------------------------------
2773 
ProcessEdgesAtTopOfScanbeam(Int64 topY)2774         private void ProcessEdgesAtTopOfScanbeam(Int64 topY)
2775         {
2776           TEdge e = m_ActiveEdges;
2777           while( e != null )
2778           {
2779             //1. process maxima, treating them as if they're 'bent' horizontal edges,
2780             //   but exclude maxima with horizontal edges. nb: e can't be a horizontal.
2781             if( IsMaxima(e, topY) && GetMaximaPair(e).dx != horizontal )
2782             {
2783               //'e' might be removed from AEL, as may any following edges so ...
2784               TEdge ePrior = e.prevInAEL;
2785               DoMaxima(e, topY);
2786               if( ePrior == null ) e = m_ActiveEdges;
2787               else e = ePrior.nextInAEL;
2788             }
2789             else
2790             {
2791               //2. promote horizontal edges, otherwise update xcurr and ycurr ...
2792               if(  IsIntermediate(e, topY) && e.nextInLML.dx == horizontal )
2793               {
2794                 if (e.outIdx >= 0)
2795                 {
2796                     AddOutPt(e, new IntPoint(e.xtop, e.ytop));
2797 
2798                     for (int i = 0; i < m_HorizJoins.Count; ++i)
2799                     {
2800                         IntPoint pt = new IntPoint(), pt2 = new IntPoint();
2801                         HorzJoinRec hj = m_HorizJoins[i];
2802                         if (GetOverlapSegment(new IntPoint(hj.edge.xbot, hj.edge.ybot),
2803                             new IntPoint(hj.edge.xtop, hj.edge.ytop),
2804                             new IntPoint(e.nextInLML.xbot, e.nextInLML.ybot),
2805                             new IntPoint(e.nextInLML.xtop, e.nextInLML.ytop), ref pt, ref pt2))
2806                                 AddJoin(hj.edge, e.nextInLML, hj.savedIdx, e.outIdx);
2807                     }
2808 
2809                     AddHorzJoin(e.nextInLML, e.outIdx);
2810                 }
2811                 UpdateEdgeIntoAEL(ref e);
2812                 AddEdgeToSEL(e);
2813               }
2814               else
2815               {
2816                 //this just simplifies horizontal processing ...
2817                 e.xcurr = TopX( e, topY );
2818                 e.ycurr = topY;
2819               }
2820               e = e.nextInAEL;
2821             }
2822           }
2823 
2824           //3. Process horizontals at the top of the scanbeam ...
2825           ProcessHorizontals();
2826 
2827           //4. Promote intermediate vertices ...
2828           e = m_ActiveEdges;
2829           while( e != null )
2830           {
2831             if( IsIntermediate( e, topY ) )
2832             {
2833                 if (e.outIdx >= 0) AddOutPt(e, new IntPoint(e.xtop, e.ytop));
2834               UpdateEdgeIntoAEL(ref e);
2835 
2836               //if output polygons share an edge, they'll need joining later ...
2837               if (e.outIdx >= 0 && e.prevInAEL != null && e.prevInAEL.outIdx >= 0 &&
2838                 e.prevInAEL.xcurr == e.xbot && e.prevInAEL.ycurr == e.ybot &&
2839                 SlopesEqual(new IntPoint(e.xbot, e.ybot), new IntPoint(e.xtop, e.ytop),
2840                   new IntPoint(e.xbot, e.ybot),
2841                   new IntPoint(e.prevInAEL.xtop, e.prevInAEL.ytop), m_UseFullRange))
2842               {
2843                   AddOutPt(e.prevInAEL, new IntPoint(e.xbot, e.ybot));
2844                   AddJoin(e, e.prevInAEL, -1, -1);
2845               }
2846               else if (e.outIdx >= 0 && e.nextInAEL != null && e.nextInAEL.outIdx >= 0 &&
2847                 e.nextInAEL.ycurr > e.nextInAEL.ytop &&
2848                 e.nextInAEL.ycurr <= e.nextInAEL.ybot &&
2849                 e.nextInAEL.xcurr == e.xbot && e.nextInAEL.ycurr == e.ybot &&
2850                 SlopesEqual(new IntPoint(e.xbot, e.ybot), new IntPoint(e.xtop, e.ytop),
2851                   new IntPoint(e.xbot, e.ybot),
2852                   new IntPoint(e.nextInAEL.xtop, e.nextInAEL.ytop), m_UseFullRange))
2853               {
2854                   AddOutPt(e.nextInAEL, new IntPoint(e.xbot, e.ybot));
2855                   AddJoin(e, e.nextInAEL, -1, -1);
2856               }
2857 
2858             }
2859             e = e.nextInAEL;
2860           }
2861         }
2862         //------------------------------------------------------------------------------
2863 
DoMaxima(TEdge e, Int64 topY)2864         private void DoMaxima(TEdge e, Int64 topY)
2865         {
2866           TEdge eMaxPair = GetMaximaPair(e);
2867           Int64 X = e.xtop;
2868           TEdge eNext = e.nextInAEL;
2869           while( eNext != eMaxPair )
2870           {
2871             if (eNext == null) throw new ClipperException("DoMaxima error");
2872             IntersectEdges( e, eNext, new IntPoint(X, topY), Protects.ipBoth );
2873             eNext = eNext.nextInAEL;
2874           }
2875           if( e.outIdx < 0 && eMaxPair.outIdx < 0 )
2876           {
2877             DeleteFromAEL( e );
2878             DeleteFromAEL( eMaxPair );
2879           }
2880           else if( e.outIdx >= 0 && eMaxPair.outIdx >= 0 )
2881           {
2882               IntersectEdges(e, eMaxPair, new IntPoint(X, topY), Protects.ipNone);
2883           }
2884           else throw new ClipperException("DoMaxima error");
2885         }
2886         //------------------------------------------------------------------------------
2887 
ReversePolygons(Polygons polys)2888         public static void ReversePolygons(Polygons polys)
2889         {
2890             foreach (var poly in polys) poly.Reverse();
2891         }
2892         //------------------------------------------------------------------------------
2893 
Orientation(Polygon poly)2894         public static bool Orientation(Polygon poly)
2895         {
2896             int highI = poly.Count -1;
2897             if (highI < 2) return false;
2898             int j = 0, jplus, jminus;
2899             for (int i = 0; i <= highI; ++i)
2900             {
2901                 if (poly[i].Y < poly[j].Y) continue;
2902                 if ((poly[i].Y > poly[j].Y || poly[i].X < poly[j].X)) j = i;
2903             };
2904             if (j == highI) jplus = 0;
2905             else jplus = j +1;
2906             if (j == 0) jminus = highI;
2907             else jminus = j -1;
2908 
2909             //get cross product of vectors of the edges adjacent to highest point ...
2910             IntPoint vec1 = new IntPoint(poly[j].X - poly[jminus].X, poly[j].Y - poly[jminus].Y);
2911             IntPoint vec2 = new IntPoint(poly[jplus].X - poly[j].X, poly[jplus].Y - poly[j].Y);
2912             if (Math.Abs(vec1.X) > loRange || Math.Abs(vec1.Y) > loRange ||
2913                 Math.Abs(vec2.X) > loRange || Math.Abs(vec2.Y) > loRange)
2914             {
2915                 if (Math.Abs(vec1.X) > hiRange || Math.Abs(vec1.Y) > hiRange ||
2916                     Math.Abs(vec2.X) > hiRange || Math.Abs(vec2.Y) > hiRange)
2917                     throw new ClipperException("Coordinate exceeds range bounds.");
2918                 Int128 cross = Int128.Int128Mul(vec1.X, vec2.Y) - Int128.Int128Mul(vec2.X, vec1.Y);
2919                 return !cross.IsNegative();
2920             }
2921             else
2922                 return (vec1.X * vec2.Y - vec2.X * vec1.Y) > 0;
2923         }
2924         //------------------------------------------------------------------------------
2925 
FullRangeNeeded(Polygon pts)2926         private static bool FullRangeNeeded(Polygon pts)
2927         {
2928             bool result = false;
2929             for (int i = 0; i <  pts.Count; i++)
2930             {
2931                 if (Math.Abs(pts[i].X) > hiRange || Math.Abs(pts[i].Y) > hiRange)
2932                     throw new ClipperException("Coordinate exceeds range bounds.");
2933                 else if (Math.Abs(pts[i].X) > loRange || Math.Abs(pts[i].Y) > loRange)
2934                   result = true;
2935             }
2936             return result;
2937         }
2938         //------------------------------------------------------------------------------
2939 
Orientation(OutRec outRec, bool UseFull64BitRange)2940         private bool Orientation(OutRec outRec, bool UseFull64BitRange)
2941         {
2942             //first make sure bottomPt is correctly assigned ...
2943             OutPt opBottom = outRec.pts, op = outRec.pts.next;
2944             while (op != outRec.pts)
2945             {
2946 	            if (op.pt.Y >= opBottom.pt.Y)
2947 	            {
2948 		            if (op.pt.Y > opBottom.pt.Y || op.pt.X < opBottom.pt.X)
2949 		            opBottom = op;
2950 	            }
2951 	            op = op.next;
2952             }
2953             outRec.bottomPt = opBottom;
2954             opBottom.idx = outRec.idx;
2955 
2956             op = opBottom;
2957             //find vertices either side of bottomPt (skipping duplicate points) ....
2958             OutPt opPrev = op.prev;
2959             OutPt opNext = op.next;
2960             while (op != opPrev && PointsEqual(op.pt, opPrev.pt))
2961               opPrev = opPrev.prev;
2962             while (op != opNext && PointsEqual(op.pt, opNext.pt))
2963               opNext = opNext.next;
2964 
2965             IntPoint vec1 = new IntPoint(op.pt.X - opPrev.pt.X, op.pt.Y - opPrev.pt.Y);
2966             IntPoint vec2 = new IntPoint(opNext.pt.X - op.pt.X, opNext.pt.Y - op.pt.Y);
2967 
2968             if (UseFull64BitRange)
2969             {
2970                 Int128 cross = Int128.Int128Mul(vec1.X, vec2.Y) - Int128.Int128Mul(vec2.X, vec1.Y);
2971                 return !cross.IsNegative();
2972             }
2973             else
2974                 return (vec1.X * vec2.Y - vec2.X * vec1.Y) > 0;
2975 
2976         }
2977         //------------------------------------------------------------------------------
2978 
PointCount(OutPt pts)2979         private int PointCount(OutPt pts)
2980         {
2981             if (pts == null) return 0;
2982             int result = 0;
2983             OutPt p = pts;
2984             do
2985             {
2986                 result++;
2987                 p = p.next;
2988             }
2989             while (p != pts);
2990             return result;
2991         }
2992         //------------------------------------------------------------------------------
2993 
BuildResult(Polygons polyg)2994         private void BuildResult(Polygons polyg)
2995         {
2996             polyg.Clear();
2997             polyg.Capacity = m_PolyOuts.Count;
2998             foreach (OutRec outRec in m_PolyOuts)
2999             {
3000                 if (outRec.pts == null) continue;
3001                 OutPt p = outRec.pts;
3002                 int cnt = PointCount(p);
3003                 if (cnt < 3) continue;
3004                 Polygon pg = new Polygon(cnt);
3005                 for (int j = 0; j < cnt; j++)
3006                 {
3007                     pg.Add(p.pt);
3008                     p = p.next;
3009                 }
3010                 polyg.Add(pg);
3011             }
3012         }
3013         //------------------------------------------------------------------------------
3014 
BuildResultEx(ExPolygons polyg)3015         private void BuildResultEx(ExPolygons polyg)
3016         {
3017             polyg.Clear();
3018             polyg.Capacity = m_PolyOuts.Count;
3019             int i = 0;
3020             while (i < m_PolyOuts.Count)
3021             {
3022                 OutRec outRec = m_PolyOuts[i++];
3023                 if (outRec.pts == null) break; //nb: already sorted here
3024                 OutPt p = outRec.pts;
3025                 int cnt = PointCount(p);
3026                 if (cnt < 3) continue;
3027                 ExPolygon epg = new ExPolygon();
3028                 epg.outer = new Polygon(cnt);
3029                 epg.holes = new Polygons();
3030                 for (int j = 0; j < cnt; j++)
3031                 {
3032                     epg.outer.Add(p.pt);
3033                     p = p.next;
3034                 }
3035                 while (i < m_PolyOuts.Count)
3036                 {
3037                     outRec = m_PolyOuts[i];
3038                     if (outRec.pts == null || !outRec.isHole) break;
3039                     Polygon pg = new Polygon();
3040                     p = outRec.pts;
3041                     do
3042                     {
3043                         pg.Add(p.pt);
3044                         p = p.next;
3045                     } while (p != outRec.pts);
3046                     epg.holes.Add(pg);
3047                     i++;
3048                 }
3049                 polyg.Add(epg);
3050             }
3051         }
3052         //------------------------------------------------------------------------------
3053 
FixupOutPolygon(OutRec outRec)3054         private void FixupOutPolygon(OutRec outRec)
3055         {
3056             //FixupOutPolygon() - removes duplicate points and simplifies consecutive
3057             //parallel edges by removing the middle vertex.
3058             OutPt lastOK = null;
3059             outRec.pts = outRec.bottomPt;
3060             OutPt pp = outRec.bottomPt;
3061             for (;;)
3062             {
3063                 if (pp.prev == pp || pp.prev == pp.next)
3064                 {
3065                     DisposeOutPts(pp);
3066                     outRec.pts = null;
3067                     outRec.bottomPt = null;
3068                     return;
3069                 }
3070                 //test for duplicate points and for same slope (cross-product) ...
3071                 if (PointsEqual(pp.pt, pp.next.pt) ||
3072                   SlopesEqual(pp.prev.pt, pp.pt, pp.next.pt, m_UseFullRange))
3073                 {
3074                     lastOK = null;
3075                     if (pp == outRec.bottomPt)
3076                          outRec.bottomPt = null; //flags need for updating
3077                     pp.prev.next = pp.next;
3078                     pp.next.prev = pp.prev;
3079                     pp = pp.prev;
3080                 }
3081                 else if (pp == lastOK) break;
3082                 else
3083                 {
3084                     if (lastOK == null) lastOK = pp;
3085                     pp = pp.next;
3086                 }
3087             }
3088           if (outRec.bottomPt == null)
3089           {
3090             outRec.bottomPt = GetBottomPt(pp);
3091             outRec.bottomPt.idx = outRec.idx;
3092             outRec.pts = outRec.bottomPt;
3093           }
3094         }
3095         //------------------------------------------------------------------------------
3096 
CheckHoleLinkages1(OutRec outRec1, OutRec outRec2)3097         private void CheckHoleLinkages1(OutRec outRec1, OutRec outRec2)
3098         {
3099           //when a polygon is split into 2 polygons, make sure any holes the original
3100           //polygon contained link to the correct polygon ...
3101           for (int i = 0; i < m_PolyOuts.Count; ++i)
3102           {
3103             if (m_PolyOuts[i].isHole && m_PolyOuts[i].bottomPt != null &&
3104                 m_PolyOuts[i].FirstLeft == outRec1 &&
3105                 !PointInPolygon(m_PolyOuts[i].bottomPt.pt,
3106                 outRec1.pts, m_UseFullRange))
3107                     m_PolyOuts[i].FirstLeft = outRec2;
3108           }
3109         }
3110         //----------------------------------------------------------------------
3111 
CheckHoleLinkages2(OutRec outRec1, OutRec outRec2)3112         private void CheckHoleLinkages2(OutRec outRec1, OutRec outRec2)
3113         {
3114           //if a hole is owned by outRec2 then make it owned by outRec1 ...
3115           for (int i = 0; i < m_PolyOuts.Count; ++i)
3116             if (m_PolyOuts[i].isHole && m_PolyOuts[i].bottomPt != null &&
3117               m_PolyOuts[i].FirstLeft == outRec2)
3118                 m_PolyOuts[i].FirstLeft = outRec1;
3119         }
3120         //----------------------------------------------------------------------
3121 
JoinCommonEdges(bool fixHoleLinkages)3122         private void JoinCommonEdges(bool fixHoleLinkages)
3123         {
3124           for (int i = 0; i < m_Joins.Count; i++)
3125           {
3126             JoinRec j = m_Joins[i];
3127             OutRec outRec1 = m_PolyOuts[j.poly1Idx];
3128             OutPt pp1a = outRec1.pts;
3129             OutRec outRec2 = m_PolyOuts[j.poly2Idx];
3130             OutPt pp2a = outRec2.pts;
3131             IntPoint pt1 = new IntPoint(j.pt2a);
3132             IntPoint pt2 = new IntPoint(j.pt2b);
3133             IntPoint pt3 = new IntPoint(j.pt1a);
3134             IntPoint pt4 = new IntPoint(j.pt1b);
3135             if (!FindSegment(ref pp1a, ref pt1, ref pt2)) continue;
3136             if (j.poly1Idx == j.poly2Idx)
3137             {
3138                 //we're searching the same polygon for overlapping segments so
3139                 //segment 2 mustn't be the same as segment 1 ...
3140                 pp2a = pp1a.next;
3141                 if (!FindSegment(ref pp2a, ref pt3, ref pt4) || (pp2a == pp1a)) continue;
3142             }
3143             else if (!FindSegment(ref pp2a, ref pt3, ref pt4)) continue;
3144 
3145             if (!GetOverlapSegment(pt1, pt2, pt3, pt4, ref pt1, ref pt2)) continue;
3146 
3147             OutPt p1, p2, p3, p4;
3148             OutPt prev = pp1a.prev;
3149             //get p1 & p2 polypts - the overlap start & endpoints on poly1
3150 
3151             if (PointsEqual(pp1a.pt, pt1)) p1 = pp1a;
3152             else if (PointsEqual(prev.pt, pt1)) p1 = prev;
3153             else p1 = InsertPolyPtBetween(pp1a, prev, pt1);
3154 
3155             if (PointsEqual(pp1a.pt, pt2)) p2 = pp1a;
3156             else if (PointsEqual(prev.pt, pt2)) p2 = prev;
3157             else if ((p1 == pp1a) || (p1 == prev))
3158                 p2 = InsertPolyPtBetween(pp1a, prev, pt2);
3159             else if (Pt3IsBetweenPt1AndPt2(pp1a.pt, p1.pt, pt2))
3160                 p2 = InsertPolyPtBetween(pp1a, p1, pt2);
3161             else
3162                 p2 = InsertPolyPtBetween(p1, prev, pt2);
3163 
3164             //get p3 & p4 polypts - the overlap start & endpoints on poly2
3165             prev = pp2a.prev;
3166             if (PointsEqual(pp2a.pt, pt1)) p3 = pp2a;
3167             else if (PointsEqual(prev.pt, pt1)) p3 = prev;
3168             else p3 = InsertPolyPtBetween(pp2a, prev, pt1);
3169 
3170             if (PointsEqual(pp2a.pt, pt2)) p4 = pp2a;
3171             else if (PointsEqual(prev.pt, pt2)) p4 = prev;
3172             else if ((p3 == pp2a) || (p3 == prev))
3173                 p4 = InsertPolyPtBetween(pp2a, prev, pt2);
3174             else if (Pt3IsBetweenPt1AndPt2(pp2a.pt, p3.pt, pt2))
3175                 p4 = InsertPolyPtBetween(pp2a, p3, pt2);
3176             else
3177                 p4 = InsertPolyPtBetween(p3, prev, pt2);
3178 
3179             //p1.pt should equal p3.pt and p2.pt should equal p4.pt here, so ...
3180             //join p1 to p3 and p2 to p4 ...
3181             if (p1.next == p2 && p3.prev == p4)
3182             {
3183                 p1.next = p3;
3184                 p3.prev = p1;
3185                 p2.prev = p4;
3186                 p4.next = p2;
3187             }
3188             else if (p1.prev == p2 && p3.next == p4)
3189             {
3190                 p1.prev = p3;
3191                 p3.next = p1;
3192                 p2.next = p4;
3193                 p4.prev = p2;
3194             }
3195             else
3196                 continue; //an orientation is probably wrong
3197 
3198             if (j.poly2Idx == j.poly1Idx)
3199             {
3200                 //instead of joining two polygons, we've just created a new one by
3201                 //splitting one polygon into two.
3202                 outRec1.pts = GetBottomPt(p1);
3203                 outRec1.bottomPt = outRec1.pts;
3204                 outRec1.bottomPt.idx = outRec1.idx;
3205                 outRec2 = CreateOutRec();
3206                 m_PolyOuts.Add(outRec2);
3207                 outRec2.idx = m_PolyOuts.Count - 1;
3208                 j.poly2Idx = outRec2.idx;
3209                 outRec2.pts = GetBottomPt(p2);
3210                 outRec2.bottomPt = outRec2.pts;
3211                 outRec2.bottomPt.idx = outRec2.idx;
3212 
3213                 if (PointInPolygon(outRec2.pts.pt, outRec1.pts, m_UseFullRange))
3214                 {
3215                     //outRec1 is contained by outRec2 ...
3216                     outRec2.isHole = !outRec1.isHole;
3217                     outRec2.FirstLeft = outRec1;
3218                     if (outRec2.isHole == Orientation(outRec2, m_UseFullRange))
3219                       ReversePolyPtLinks(outRec2.pts);
3220                 }
3221                 else if (PointInPolygon(outRec1.pts.pt, outRec2.pts, m_UseFullRange))
3222                 {
3223                     //outRec2 is contained by outRec1 ...
3224                     outRec2.isHole = outRec1.isHole;
3225                     outRec1.isHole = !outRec2.isHole;
3226                     outRec2.FirstLeft = outRec1.FirstLeft;
3227                     outRec1.FirstLeft = outRec2;
3228                     if (outRec1.isHole == Orientation(outRec1, m_UseFullRange))
3229                       ReversePolyPtLinks(outRec1.pts);
3230                     //make sure any contained holes now link to the correct polygon ...
3231                     if (fixHoleLinkages) CheckHoleLinkages1(outRec1, outRec2);
3232                 }
3233                 else
3234                 {
3235                     outRec2.isHole = outRec1.isHole;
3236                     outRec2.FirstLeft = outRec1.FirstLeft;
3237                     //make sure any contained holes now link to the correct polygon ...
3238                     if (fixHoleLinkages) CheckHoleLinkages1(outRec1, outRec2);
3239                 }
3240 
3241                 //now fixup any subsequent m_Joins that match this polygon
3242                 for (int k = i + 1; k < m_Joins.Count; k++)
3243                 {
3244                     JoinRec j2 = m_Joins[k];
3245                     if (j2.poly1Idx == j.poly1Idx && PointIsVertex(j2.pt1a, p2))
3246                         j2.poly1Idx = j.poly2Idx;
3247                     if (j2.poly2Idx == j.poly1Idx && PointIsVertex(j2.pt2a, p2))
3248                         j2.poly2Idx = j.poly2Idx;
3249                 }
3250 
3251                 //now cleanup redundant edges too ...
3252                 FixupOutPolygon(outRec1);
3253                 FixupOutPolygon(outRec2);
3254             }
3255             else
3256             {
3257                 //joined 2 polygons together ...
3258 
3259                 //make sure any holes contained by outRec2 now link to outRec1 ...
3260                 if (fixHoleLinkages) CheckHoleLinkages2(outRec1, outRec2);
3261 
3262                 //now cleanup redundant edges too ...
3263                 FixupOutPolygon(outRec1);
3264 
3265                 if (outRec1.pts != null)
3266                 {
3267                     outRec1.isHole = !Orientation(outRec1, m_UseFullRange);
3268                     if (outRec1.isHole &&  outRec1.FirstLeft == null)
3269                       outRec1.FirstLeft = outRec2.FirstLeft;
3270                 }
3271 
3272                 //delete the obsolete pointer ...
3273                 int OKIdx = outRec1.idx;
3274                 int ObsoleteIdx = outRec2.idx;
3275                 outRec2.pts = null;
3276                 outRec2.bottomPt = null;
3277                 outRec2.AppendLink = outRec1;
3278 
3279                 //now fixup any subsequent joins that match this polygon
3280                 for (int k = i + 1; k < m_Joins.Count; k++)
3281                 {
3282                     JoinRec j2 = m_Joins[k];
3283                     if (j2.poly1Idx == ObsoleteIdx) j2.poly1Idx = OKIdx;
3284                     if (j2.poly2Idx == ObsoleteIdx) j2.poly2Idx = OKIdx;
3285                 }
3286             }
3287           }
3288         }
3289         //------------------------------------------------------------------------------
3290 
Area(Polygon poly)3291         public static double Area(Polygon poly)
3292         {
3293             int highI = poly.Count - 1;
3294             if (highI < 2) return 0;
3295             if (FullRangeNeeded(poly))
3296             {
3297                 Int128 a = new Int128();
3298                 a = Int128.Int128Mul(poly[highI].X, poly[0].Y) -
3299                     Int128.Int128Mul(poly[0].X, poly[highI].Y);
3300                 for (int i = 0; i < highI; ++i)
3301                     a += Int128.Int128Mul(poly[i].X, poly[i + 1].Y) -
3302                     Int128.Int128Mul(poly[i + 1].X, poly[i].Y);
3303                 return a.ToDouble() / 2;
3304             }
3305             else
3306             {
3307                 double area = (double)poly[highI].X * (double)poly[0].Y -
3308                     (double)poly[0].X * (double)poly[highI].Y;
3309                 for (int i = 0; i < highI; ++i)
3310                     area += (double)poly[i].X * (double)poly[i + 1].Y -
3311                         (double)poly[i + 1].X * (double)poly[i].Y;
3312                 return area / 2;
3313             }
3314         }
3315         //------------------------------------------------------------------------------
3316 
Area(OutRec outRec, bool UseFull64BitRange)3317         double Area(OutRec outRec, bool UseFull64BitRange)
3318         {
3319           OutPt op = outRec.pts;
3320           if (UseFull64BitRange)
3321           {
3322             Int128 a = new Int128(0);
3323             do
3324             {
3325                 a += Int128.Int128Mul(op.prev.pt.X, op.pt.Y) -
3326                     Int128.Int128Mul(op.pt.X, op.prev.pt.Y);
3327                 op = op.next;
3328             } while (op != outRec.pts);
3329             return a.ToDouble() / 2;
3330           }
3331           else
3332           {
3333             double a = 0;
3334             do {
3335               a += (op.prev.pt.X * op.pt.Y) - (op.pt.X * op.prev.pt.Y);
3336               op = op.next;
3337             } while (op != outRec.pts);
3338             return a/2;
3339           }
3340         }
3341 
3342         //------------------------------------------------------------------------------
3343         // OffsetPolygon functions ...
3344         //------------------------------------------------------------------------------
3345 
BuildArc(IntPoint pt, double a1, double a2, double r)3346         internal static Polygon BuildArc(IntPoint pt, double a1, double a2, double r)
3347         {
3348             int steps = Math.Max(6, (int)(Math.Sqrt(Math.Abs(r)) * Math.Abs(a2 - a1)));
3349             Polygon result = new Polygon(steps);
3350             int n = steps - 1;
3351             double da = (a2 - a1) / n;
3352             double a = a1;
3353             for (int i = 0; i < steps; ++i)
3354             {
3355                 result.Add(new IntPoint(pt.X + Round(Math.Cos(a) * r), pt.Y + Round(Math.Sin(a) * r)));
3356                 a += da;
3357             }
3358             return result;
3359         }
3360         //------------------------------------------------------------------------------
3361 
GetUnitNormal(IntPoint pt1, IntPoint pt2)3362         internal static DoublePoint GetUnitNormal(IntPoint pt1, IntPoint pt2)
3363         {
3364             double dx = (pt2.X - pt1.X);
3365             double dy = (pt2.Y - pt1.Y);
3366             if ((dx == 0) && (dy == 0)) return new DoublePoint();
3367 
3368             double f = 1 * 1.0 / Math.Sqrt(dx * dx + dy * dy);
3369             dx *= f;
3370             dy *= f;
3371 
3372             return new DoublePoint(dy, -dx);
3373         }
3374         //------------------------------------------------------------------------------
3375 
3376         internal class DoublePoint
3377         {
3378             public double X { get; set; }
3379             public double Y { get; set; }
DoublePoint(double x = 0, double y = 0)3380             public DoublePoint(double x = 0, double y = 0)
3381             {
3382                 this.X = x; this.Y = y;
3383             }
3384         };
3385         //------------------------------------------------------------------------------
3386 
3387         private class PolyOffsetBuilder
3388         {
3389             private Polygons pts;
3390             private Polygon currentPoly;
3391             private List<DoublePoint> normals;
3392             private double delta, m_R;
3393             private int m_i, m_j, m_k;
3394             private const int buffLength = 128;
3395 
PolyOffsetBuilder(Polygons pts, Polygons solution, double delta, JoinType jointype, double MiterLimit = 2)3396             public PolyOffsetBuilder(Polygons pts, Polygons solution, double delta, JoinType jointype, double MiterLimit = 2)
3397             {
3398                 //precondtion: solution != pts
3399 
3400                 if (delta == 0)
3401                 {
3402                     solution = pts;
3403                     return;
3404                 }
3405 
3406                 this.pts = pts;
3407                 this.delta = delta;
3408                 if (MiterLimit <= 1) MiterLimit = 1;
3409                 double RMin = 2/(MiterLimit*MiterLimit);
3410 
3411                 normals = new List<DoublePoint>();
3412 
3413                 solution.Clear();
3414                 solution.Capacity = pts.Count;
3415                 for (m_i = 0; m_i < pts.Count; m_i++)
3416                 {
3417                     int len = pts[m_i].Count;
3418                     if (len > 1 && pts[m_i][0].X == pts[m_i][len - 1].X &&
3419                         pts[m_i][0].Y == pts[m_i][len - 1].Y) len--;
3420 
3421                     if (len == 0 || (len < 3 && delta <= 0))
3422                         continue;
3423                     else if (len == 1)
3424                     {
3425                         Polygon arc;
3426                         arc = BuildArc(pts[m_i][len - 1], 0, 2 * Math.PI, delta);
3427                         solution.Add(arc);
3428                         continue;
3429                     }
3430 
3431                     //build normals ...
3432                     normals.Clear();
3433                     normals.Capacity = len;
3434                     for (int j = 0; j < len -1; ++j)
3435                         normals.Add(GetUnitNormal(pts[m_i][j], pts[m_i][j+1]));
3436                     normals.Add(GetUnitNormal(pts[m_i][len - 1], pts[m_i][0]));
3437 
3438                     currentPoly = new Polygon();
3439                     m_k = len - 1;
3440                     for (m_j = 0; m_j < len; ++m_j)
3441                     {
3442                         switch (jointype)
3443                         {
3444                             case JoinType.jtMiter:
3445                             {
3446                                 m_R = 1 + (normals[m_j].X*normals[m_k].X +
3447                                     normals[m_j].Y*normals[m_k].Y);
3448                                 if (m_R >= RMin) DoMiter(); else DoSquare(MiterLimit);
3449                                 break;
3450                             }
3451                             case JoinType.jtRound:
3452                                 DoRound();
3453                                 break;
3454                             case JoinType.jtSquare:
3455                                 DoSquare(1);
3456                                 break;
3457                         }
3458                         m_k = m_j;
3459                     }
3460                     solution.Add(currentPoly);
3461                 }
3462 
3463                 //finally, clean up untidy corners ...
3464                 Clipper clpr = new Clipper();
3465                 clpr.AddPolygons(solution, PolyType.ptSubject);
3466                 if (delta > 0)
3467                 {
3468                     clpr.Execute(ClipType.ctUnion, solution, PolyFillType.pftPositive, PolyFillType.pftPositive);
3469                 }
3470                 else
3471                 {
3472                     IntRect r = clpr.GetBounds();
3473                     Polygon outer = new Polygon(4);
3474 
3475                     outer.Add(new IntPoint(r.left - 10, r.bottom + 10));
3476                     outer.Add(new IntPoint(r.right + 10, r.bottom + 10));
3477                     outer.Add(new IntPoint(r.right + 10, r.top - 10));
3478                     outer.Add(new IntPoint(r.left - 10, r.top - 10));
3479 
3480                     clpr.AddPolygon(outer, PolyType.ptSubject);
3481                     clpr.Execute(ClipType.ctUnion, solution, PolyFillType.pftNegative, PolyFillType.pftNegative);
3482                     if (solution.Count > 0)
3483                     {
3484                         solution.RemoveAt(0);
3485                         for (int i = 0; i < solution.Count; i++)
3486                             solution[i].Reverse();
3487                     }
3488                 }
3489             }
3490             //------------------------------------------------------------------------------
3491 
AddPoint(IntPoint pt)3492             internal void AddPoint(IntPoint pt)
3493             {
3494                 int len = currentPoly.Count;
3495                 if (len == currentPoly.Capacity)
3496                     currentPoly.Capacity = len + buffLength;
3497                 currentPoly.Add(pt);
3498             }
3499             //------------------------------------------------------------------------------
3500 
DoSquare(double mul)3501             internal void DoSquare(double mul)
3502             {
3503                 IntPoint pt1 = new IntPoint((Int64)Round(pts[m_i][m_j].X + normals[m_k].X * delta),
3504                     (Int64)Round(pts[m_i][m_j].Y + normals[m_k].Y * delta));
3505                 IntPoint pt2 = new IntPoint((Int64)Round(pts[m_i][m_j].X + normals[m_j].X * delta),
3506                     (Int64)Round(pts[m_i][m_j].Y + normals[m_j].Y * delta));
3507                 if ((normals[m_k].X * normals[m_j].Y - normals[m_j].X * normals[m_k].Y) * delta >= 0)
3508                 {
3509                     double a1 = Math.Atan2(normals[m_k].Y, normals[m_k].X);
3510                     double a2 = Math.Atan2(-normals[m_j].Y, -normals[m_j].X);
3511                     a1 = Math.Abs(a2 - a1);
3512                     if (a1 > Math.PI) a1 = Math.PI * 2 - a1;
3513                     double dx = Math.Tan((Math.PI - a1) / 4) * Math.Abs(delta * mul);
3514                     pt1 = new IntPoint((Int64)(pt1.X - normals[m_k].Y * dx),
3515                         (Int64)(pt1.Y + normals[m_k].X * dx));
3516                     AddPoint(pt1);
3517                     pt2 = new IntPoint((Int64)(pt2.X + normals[m_j].Y * dx),
3518                         (Int64)(pt2.Y - normals[m_j].X * dx));
3519                     AddPoint(pt2);
3520                 }
3521                 else
3522                 {
3523                     AddPoint(pt1);
3524                     AddPoint(pts[m_i][m_j]);
3525                     AddPoint(pt2);
3526                 }
3527             }
3528             //------------------------------------------------------------------------------
3529 
DoMiter()3530             internal void DoMiter()
3531             {
3532                 if ((normals[m_k].X * normals[m_j].Y - normals[m_j].X * normals[m_k].Y) * delta >= 0)
3533                 {
3534                     double q = delta / m_R;
3535                     AddPoint(new IntPoint((Int64)Round(pts[m_i][m_j].X +
3536                         (normals[m_k].X + normals[m_j].X) * q),
3537                         (Int64)Round(pts[m_i][m_j].Y + (normals[m_k].Y + normals[m_j].Y) * q)));
3538                 }
3539                 else
3540                 {
3541                     IntPoint pt1 = new IntPoint((Int64)Round(pts[m_i][m_j].X + normals[m_k].X * delta),
3542                         (Int64)Round(pts[m_i][m_j].Y + normals[m_k].Y * delta));
3543                     IntPoint pt2 = new IntPoint((Int64)Round(pts[m_i][m_j].X + normals[m_j].X * delta),
3544                         (Int64)Round(pts[m_i][m_j].Y + normals[m_j].Y * delta));
3545                     AddPoint(pt1);
3546                     AddPoint(pts[m_i][m_j]);
3547                     AddPoint(pt2);
3548                 }
3549             }
3550             //------------------------------------------------------------------------------
3551 
DoRound()3552             internal void DoRound()
3553             {
3554                 IntPoint pt1 = new IntPoint(Round(pts[m_i][m_j].X + normals[m_k].X * delta),
3555                     Round(pts[m_i][m_j].Y + normals[m_k].Y * delta));
3556                 IntPoint pt2 = new IntPoint(Round(pts[m_i][m_j].X + normals[m_j].X * delta),
3557                     Round(pts[m_i][m_j].Y + normals[m_j].Y * delta));
3558                 AddPoint(pt1);
3559                 //round off reflex angles (ie > 180 deg) unless almost flat (ie < 10deg).
3560                 //cross product normals < 0 . angle > 180 deg.
3561                 //dot product normals == 1 . no angle
3562                 if ((normals[m_k].X * normals[m_j].Y - normals[m_j].X * normals[m_k].Y) * delta >= 0)
3563                 {
3564                     if ((normals[m_j].X * normals[m_k].X + normals[m_j].Y * normals[m_k].Y) < 0.985)
3565                     {
3566                         double a1 = Math.Atan2(normals[m_k].Y, normals[m_k].X);
3567                         double a2 = Math.Atan2(normals[m_j].Y, normals[m_j].X);
3568                         if (delta > 0 && a2 < a1) a2 += Math.PI * 2;
3569                         else if (delta < 0 && a2 > a1) a2 -= Math.PI * 2;
3570                         Polygon arc = BuildArc(pts[m_i][m_j], a1, a2, delta);
3571                         for (int m = 0; m < arc.Count; m++)
3572                             AddPoint(arc[m]);
3573                     }
3574                 }
3575                 else
3576                     AddPoint(pts[m_i][m_j]);
3577                 AddPoint(pt2);
3578             }
3579             //------------------------------------------------------------------------------
3580 
3581         } //end PolyOffsetBuilder
3582         //------------------------------------------------------------------------------
3583 
OffsetPolygons(Polygons poly, double delta, JoinType jointype, double MiterLimit)3584         public static Polygons OffsetPolygons(Polygons poly, double delta,
3585             JoinType jointype, double MiterLimit)
3586         {
3587             Polygons result = new Polygons(poly.Count);
3588             new PolyOffsetBuilder(poly, result, delta, jointype, MiterLimit);
3589             return result;
3590         }
3591         //------------------------------------------------------------------------------
3592 
OffsetPolygons(Polygons poly, double delta, JoinType jointype)3593         public static Polygons OffsetPolygons(Polygons poly, double delta, JoinType jointype)
3594         {
3595             Polygons result = new Polygons(poly.Count);
3596             new PolyOffsetBuilder(poly, result, delta, jointype, 2.0);
3597             return result;
3598         }
3599         //------------------------------------------------------------------------------
3600 
OffsetPolygons(Polygons poly, double delta)3601         public static Polygons OffsetPolygons(Polygons poly, double delta)
3602         {
3603             Polygons result = new Polygons(poly.Count);
3604             new PolyOffsetBuilder(poly, result, delta, JoinType.jtSquare, 2.0);
3605             return result;
3606         }
3607 
3608         //------------------------------------------------------------------------------
3609         // SimplifyPolygon functions ...
3610         // Convert self-intersecting polygons into simple polygons
3611         //------------------------------------------------------------------------------
3612 
SimplifyPolygon(Polygon poly)3613         public static Polygons SimplifyPolygon(Polygon poly)
3614         {
3615             Polygons result = new Polygons();
3616             Clipper c = new Clipper();
3617             c.AddPolygon(poly, PolyType.ptSubject);
3618             c.Execute(ClipType.ctUnion, result);
3619             return result;
3620         }
3621         //------------------------------------------------------------------------------
3622 
SimplifyPolygons(Polygons polys)3623         public static Polygons SimplifyPolygons(Polygons polys)
3624         {
3625             Polygons result = new Polygons();
3626             Clipper c = new Clipper();
3627             c.AddPolygons(polys, PolyType.ptSubject);
3628             c.Execute(ClipType.ctUnion, result);
3629             return result;
3630         }
3631         //------------------------------------------------------------------------------
3632 
3633     } //end ClipperLib namespace
3634 
3635     class ClipperException : Exception
3636     {
ClipperException(string description)3637         public ClipperException(string description) : base(description){}
3638     }
3639     //------------------------------------------------------------------------------
3640 }
3641