ruby_clipper 5.0.3

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