clipper2 0.1.0

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.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +165 -0
  4. data/examples/larb_transform.rb +17 -0
  5. data/examples/rugl_mesh.rb +33 -0
  6. data/examples/svg_boolean.rb +28 -0
  7. data/ext/clipper2/CMakeLists.txt +27 -0
  8. data/ext/clipper2/clipper2_native.cpp +2 -0
  9. data/ext/clipper2/extconf.rb +27 -0
  10. data/ext/clipper2/generate_bindings.rb +73 -0
  11. data/ext/clipper2/memory_check.cpp +48 -0
  12. data/ext/clipper2/vendor/LICENSE +23 -0
  13. data/ext/clipper2/vendor/UPSTREAM.md +11 -0
  14. data/ext/clipper2/vendor/include/clipper2/clipper.core.h +1159 -0
  15. data/ext/clipper2/vendor/include/clipper2/clipper.engine.h +635 -0
  16. data/ext/clipper2/vendor/include/clipper2/clipper.export.h +851 -0
  17. data/ext/clipper2/vendor/include/clipper2/clipper.h +796 -0
  18. data/ext/clipper2/vendor/include/clipper2/clipper.minkowski.h +117 -0
  19. data/ext/clipper2/vendor/include/clipper2/clipper.offset.h +125 -0
  20. data/ext/clipper2/vendor/include/clipper2/clipper.rectclip.h +80 -0
  21. data/ext/clipper2/vendor/include/clipper2/clipper.triangulation.h +27 -0
  22. data/ext/clipper2/vendor/include/clipper2/clipper.version.h +6 -0
  23. data/ext/clipper2/vendor/src/clipper.engine.cpp +3152 -0
  24. data/ext/clipper2/vendor/src/clipper.offset.cpp +661 -0
  25. data/ext/clipper2/vendor/src/clipper.rectclip.cpp +1027 -0
  26. data/ext/clipper2/vendor/src/clipper.triangulation.cpp +1221 -0
  27. data/lib/clipper2/api.rb +45 -0
  28. data/lib/clipper2/c_paths64.rb +80 -0
  29. data/lib/clipper2/earcut/engine.rb +191 -0
  30. data/lib/clipper2/earcut/geometry.rb +100 -0
  31. data/lib/clipper2/earcut/node.rb +18 -0
  32. data/lib/clipper2/earcut/ring.rb +94 -0
  33. data/lib/clipper2/earcut.rb +18 -0
  34. data/lib/clipper2/errors.rb +19 -0
  35. data/lib/clipper2/generated.rb +62 -0
  36. data/lib/clipper2/native.rb +38 -0
  37. data/lib/clipper2/native_memory.rb +64 -0
  38. data/lib/clipper2/operations.rb +206 -0
  39. data/lib/clipper2/path.rb +171 -0
  40. data/lib/clipper2/path_simplifier.rb +61 -0
  41. data/lib/clipper2/paths.rb +130 -0
  42. data/lib/clipper2/quantizer.rb +48 -0
  43. data/lib/clipper2/triangulation.rb +103 -0
  44. data/lib/clipper2/version.rb +5 -0
  45. data/lib/clipper2.rb +34 -0
  46. data/licenses/CLIPPER2-BSL-1.0.txt +23 -0
  47. data/licenses/EARCUT-ISC.txt +15 -0
  48. metadata +113 -0
@@ -0,0 +1,3152 @@
1
+ /*******************************************************************************
2
+ * Author : Angus Johnson *
3
+ * Date : 21 February 2026 *
4
+ * Website : https://www.angusj.com *
5
+ * Copyright : Angus Johnson 2010-2026 *
6
+ * Purpose : This is the main polygon clipping module *
7
+ * License : https://www.boost.org/LICENSE_1_0.txt *
8
+ *******************************************************************************/
9
+
10
+ #include "clipper2/clipper.engine.h"
11
+ #include "clipper2/clipper.h"
12
+ #include <stdexcept>
13
+
14
+ // https://github.com/AngusJohnson/Clipper2/discussions/334
15
+ // #discussioncomment-4248602
16
+ #if defined(_MSC_VER) && ( defined(_M_AMD64) || defined(_M_X64) )
17
+ #include <xmmintrin.h>
18
+ #include <emmintrin.h>
19
+ #define fmin(a,b) _mm_cvtsd_f64(_mm_min_sd(_mm_set_sd(a),_mm_set_sd(b)))
20
+ #define fmax(a,b) _mm_cvtsd_f64(_mm_max_sd(_mm_set_sd(a),_mm_set_sd(b)))
21
+ #define nearbyint(a) _mm_cvtsd_si64(_mm_set_sd(a)) /* Note: expression type is (int64_t) */
22
+ #endif
23
+
24
+ namespace Clipper2Lib {
25
+
26
+ static const Rect64 invalid_rect = Rect64(false);
27
+
28
+ // Every closed path (ie polygon) is made up of a series of vertices forming edge
29
+ // 'bounds' that alternate between ascending bounds (containing edges going up
30
+ // relative to the Y-axis) and descending bounds. 'Local Minima' refers to
31
+ // vertices where ascending and descending bounds join at the bottom, and
32
+ // 'Local Maxima' are where ascending and descending bounds join at the top.
33
+
34
+ struct Scanline {
35
+ int64_t y = 0;
36
+ Scanline* next = nullptr;
37
+
38
+ explicit Scanline(int64_t y_) : y(y_) {}
39
+ };
40
+
41
+ struct HorzSegSorter {
42
+ inline bool operator()(const HorzSegment& hs1, const HorzSegment& hs2)
43
+ {
44
+ if (!hs1.right_op || !hs2.right_op) return (hs1.right_op);
45
+ return hs2.left_op->pt.x > hs1.left_op->pt.x;
46
+ }
47
+ };
48
+
49
+ struct LocMinSorter {
50
+ inline bool operator()(const LocalMinima_ptr& locMin1,
51
+ const LocalMinima_ptr& locMin2)
52
+ {
53
+ if (locMin2->vertex->pt.y != locMin1->vertex->pt.y)
54
+ return locMin2->vertex->pt.y < locMin1->vertex->pt.y;
55
+ else
56
+ return locMin2->vertex->pt.x > locMin1->vertex->pt.x;
57
+ }
58
+ };
59
+
60
+
61
+ inline bool IsOdd(int val)
62
+ {
63
+ return (val & 1) ? true : false;
64
+ }
65
+
66
+
67
+ inline bool IsHotEdge(const Active& e)
68
+ {
69
+ return (e.outrec);
70
+ }
71
+
72
+
73
+ inline bool IsOpen(const Active& e)
74
+ {
75
+ return (e.local_min->is_open);
76
+ }
77
+
78
+
79
+ inline bool IsOpenEnd(const Vertex& v)
80
+ {
81
+ return (v.flags & (VertexFlags::OpenStart | VertexFlags::OpenEnd)) !=
82
+ VertexFlags::Empty;
83
+ }
84
+
85
+
86
+ inline bool IsOpenEnd(const Active& ae)
87
+ {
88
+ return IsOpenEnd(*ae.vertex_top);
89
+ }
90
+
91
+
92
+ inline Active* GetPrevHotEdge(const Active& e)
93
+ {
94
+ Active* prev = e.prev_in_ael;
95
+ while (prev && (IsOpen(*prev) || !IsHotEdge(*prev)))
96
+ prev = prev->prev_in_ael;
97
+ return prev;
98
+ }
99
+
100
+ inline bool IsFront(const Active& e)
101
+ {
102
+ return (&e == e.outrec->front_edge);
103
+ }
104
+
105
+ inline bool IsInvalidPath(OutPt* op)
106
+ {
107
+ return (!op || op->next == op);
108
+ }
109
+
110
+ /*******************************************************************************
111
+ * Dx: 0(90deg) *
112
+ * | *
113
+ * +inf (180deg) <--- o ---> -inf (0deg) *
114
+ *******************************************************************************/
115
+
116
+ inline double GetDx(const Point64& pt1, const Point64& pt2)
117
+ {
118
+ double dy = double(pt2.y - pt1.y);
119
+ if (dy != 0)
120
+ return double(pt2.x - pt1.x) / dy;
121
+ else if (pt2.x > pt1.x)
122
+ return -std::numeric_limits<double>::max();
123
+ else
124
+ return std::numeric_limits<double>::max();
125
+ }
126
+
127
+ inline int64_t TopX(const Active& ae, const int64_t currentY)
128
+ {
129
+ if ((currentY == ae.top.y) || (ae.top.x == ae.bot.x)) return ae.top.x;
130
+ else if (currentY == ae.bot.y) return ae.bot.x;
131
+ else return ae.bot.x + static_cast<int64_t>(nearbyint(ae.dx * (currentY - ae.bot.y)));
132
+ // nb: std::nearbyint (or std::round) substantially *improves* performance here
133
+ // as it greatly improves the likelihood of edge adjacency in ProcessIntersectList().
134
+ }
135
+
136
+
137
+ inline bool IsHorizontal(const Active& e)
138
+ {
139
+ return (e.top.y == e.bot.y);
140
+ }
141
+
142
+
143
+ inline bool IsHeadingRightHorz(const Active& e)
144
+ {
145
+ return e.dx == -std::numeric_limits<double>::max();
146
+ }
147
+
148
+
149
+ inline bool IsHeadingLeftHorz(const Active& e)
150
+ {
151
+ return e.dx == std::numeric_limits<double>::max();
152
+ }
153
+
154
+
155
+ inline void SwapActives(Active*& e1, Active*& e2)
156
+ {
157
+ Active* e = e1;
158
+ e1 = e2;
159
+ e2 = e;
160
+ }
161
+
162
+ inline PathType GetPolyType(const Active& e)
163
+ {
164
+ return e.local_min->polytype;
165
+ }
166
+
167
+ inline bool IsSamePolyType(const Active& e1, const Active& e2)
168
+ {
169
+ return e1.local_min->polytype == e2.local_min->polytype;
170
+ }
171
+
172
+ inline void SetDx(Active& e)
173
+ {
174
+ e.dx = GetDx(e.bot, e.top);
175
+ }
176
+
177
+ inline Vertex* NextVertex(const Active& e)
178
+ {
179
+ if (e.wind_dx > 0)
180
+ return e.vertex_top->next;
181
+ else
182
+ return e.vertex_top->prev;
183
+ }
184
+
185
+ //PrevPrevVertex: useful to get the (inverted Y-axis) top of the
186
+ //alternate edge (ie left or right bound) during edge insertion.
187
+ inline Vertex* PrevPrevVertex(const Active& ae)
188
+ {
189
+ if (ae.wind_dx > 0)
190
+ return ae.vertex_top->prev->prev;
191
+ else
192
+ return ae.vertex_top->next->next;
193
+ }
194
+
195
+
196
+ inline Active* ExtractFromSEL(Active* ae)
197
+ {
198
+ Active* res = ae->next_in_sel;
199
+ if (res)
200
+ res->prev_in_sel = ae->prev_in_sel;
201
+ ae->prev_in_sel->next_in_sel = res;
202
+ return res;
203
+ }
204
+
205
+
206
+ inline void Insert1Before2InSEL(Active* ae1, Active* ae2)
207
+ {
208
+ ae1->prev_in_sel = ae2->prev_in_sel;
209
+ if (ae1->prev_in_sel)
210
+ ae1->prev_in_sel->next_in_sel = ae1;
211
+ ae1->next_in_sel = ae2;
212
+ ae2->prev_in_sel = ae1;
213
+ }
214
+
215
+ inline bool IsMaxima(const Vertex& v)
216
+ {
217
+ return ((v.flags & VertexFlags::LocalMax) != VertexFlags::Empty);
218
+ }
219
+
220
+
221
+ inline bool IsMaxima(const Active& e)
222
+ {
223
+ return IsMaxima(*e.vertex_top);
224
+ }
225
+
226
+ inline Vertex* GetCurrYMaximaVertex_Open(const Active& e)
227
+ {
228
+ Vertex* result = e.vertex_top;
229
+ if (e.wind_dx > 0)
230
+ while ((result->next->pt.y == result->pt.y) &&
231
+ ((result->flags & (VertexFlags::OpenEnd |
232
+ VertexFlags::LocalMax)) == VertexFlags::Empty))
233
+ result = result->next;
234
+ else
235
+ while (result->prev->pt.y == result->pt.y &&
236
+ ((result->flags & (VertexFlags::OpenEnd |
237
+ VertexFlags::LocalMax)) == VertexFlags::Empty))
238
+ result = result->prev;
239
+ if (!IsMaxima(*result)) result = nullptr; // not a maxima
240
+ return result;
241
+ }
242
+
243
+ inline Vertex* GetCurrYMaximaVertex(const Active& e)
244
+ {
245
+ Vertex* result = e.vertex_top;
246
+ if (e.wind_dx > 0)
247
+ while (result->next->pt.y == result->pt.y) result = result->next;
248
+ else
249
+ while (result->prev->pt.y == result->pt.y) result = result->prev;
250
+ if (!IsMaxima(*result)) result = nullptr; // not a maxima
251
+ return result;
252
+ }
253
+
254
+ Active* GetMaximaPair(const Active& e)
255
+ {
256
+ Active* e2;
257
+ e2 = e.next_in_ael;
258
+ while (e2)
259
+ {
260
+ if (e2->vertex_top == e.vertex_top) return e2; // Found!
261
+ e2 = e2->next_in_ael;
262
+ }
263
+ return nullptr;
264
+ }
265
+
266
+ inline int PointCount(OutPt* op)
267
+ {
268
+ OutPt* op2 = op;
269
+ int cnt = 0;
270
+ do
271
+ {
272
+ op2 = op2->next;
273
+ ++cnt;
274
+ } while (op2 != op);
275
+ return cnt;
276
+ }
277
+
278
+ inline OutPt* DuplicateOp(OutPt* op, bool insert_after)
279
+ {
280
+ OutPt* result = new OutPt(op->pt, op->outrec);
281
+ if (insert_after)
282
+ {
283
+ result->next = op->next;
284
+ result->next->prev = result;
285
+ result->prev = op;
286
+ op->next = result;
287
+ }
288
+ else
289
+ {
290
+ result->prev = op->prev;
291
+ result->prev->next = result;
292
+ result->next = op;
293
+ op->prev = result;
294
+ }
295
+ return result;
296
+ }
297
+
298
+ inline OutPt* DisposeOutPt(OutPt* op)
299
+ {
300
+ OutPt* result = op->next;
301
+ op->prev->next = op->next;
302
+ op->next->prev = op->prev;
303
+ delete op;
304
+ return result;
305
+ }
306
+
307
+
308
+ inline void DisposeOutPts(OutRec* outrec)
309
+ {
310
+ OutPt* op = outrec->pts;
311
+ op->prev->next = nullptr;
312
+ while (op)
313
+ {
314
+ OutPt* tmp = op;
315
+ op = op->next;
316
+ delete tmp;
317
+ };
318
+ outrec->pts = nullptr;
319
+ }
320
+
321
+
322
+ bool IntersectListSort(const IntersectNode& a, const IntersectNode& b)
323
+ {
324
+ //note different inequality tests ...
325
+ return (a.pt.y == b.pt.y) ? (a.pt.x < b.pt.x) : (a.pt.y > b.pt.y);
326
+ }
327
+
328
+
329
+ inline void SetSides(OutRec& outrec, Active& start_edge, Active& end_edge)
330
+ {
331
+ outrec.front_edge = &start_edge;
332
+ outrec.back_edge = &end_edge;
333
+ }
334
+
335
+
336
+ void SwapOutrecs(Active& e1, Active& e2)
337
+ {
338
+ OutRec* or1 = e1.outrec;
339
+ OutRec* or2 = e2.outrec;
340
+ if (or1 == or2)
341
+ {
342
+ Active* e = or1->front_edge;
343
+ or1->front_edge = or1->back_edge;
344
+ or1->back_edge = e;
345
+ return;
346
+ }
347
+ if (or1)
348
+ {
349
+ if (&e1 == or1->front_edge)
350
+ or1->front_edge = &e2;
351
+ else
352
+ or1->back_edge = &e2;
353
+ }
354
+ if (or2)
355
+ {
356
+ if (&e2 == or2->front_edge)
357
+ or2->front_edge = &e1;
358
+ else
359
+ or2->back_edge = &e1;
360
+ }
361
+ e1.outrec = or2;
362
+ e2.outrec = or1;
363
+ }
364
+
365
+
366
+ double Area(OutPt* op)
367
+ {
368
+ //https://en.wikipedia.org/wiki/Shoelace_formula
369
+ double result = 0.0;
370
+ OutPt* op2 = op;
371
+ do
372
+ {
373
+ result += static_cast<double>(op2->prev->pt.y + op2->pt.y) *
374
+ static_cast<double>(op2->prev->pt.x - op2->pt.x);
375
+ op2 = op2->next;
376
+ } while (op2 != op);
377
+ return result * 0.5;
378
+ }
379
+
380
+ inline double AreaTriangle(const Point64& pt1,
381
+ const Point64& pt2, const Point64& pt3)
382
+ {
383
+ return (static_cast<double>(pt3.y + pt1.y) * static_cast<double>(pt3.x - pt1.x) +
384
+ static_cast<double>(pt1.y + pt2.y) * static_cast<double>(pt1.x - pt2.x) +
385
+ static_cast<double>(pt2.y + pt3.y) * static_cast<double>(pt2.x - pt3.x));
386
+ }
387
+
388
+ void ReverseOutPts(OutPt* op)
389
+ {
390
+ if (!op) return;
391
+
392
+ OutPt* op1 = op;
393
+ OutPt* op2;
394
+
395
+ do
396
+ {
397
+ op2 = op1->next;
398
+ op1->next = op1->prev;
399
+ op1->prev = op2;
400
+ op1 = op2;
401
+ } while (op1 != op);
402
+ }
403
+
404
+ inline void SwapSides(OutRec& outrec)
405
+ {
406
+ Active* e2 = outrec.front_edge;
407
+ outrec.front_edge = outrec.back_edge;
408
+ outrec.back_edge = e2;
409
+ outrec.pts = outrec.pts->next;
410
+ }
411
+
412
+ inline OutRec* GetRealOutRec(OutRec* outrec)
413
+ {
414
+ while (outrec && !outrec->pts) outrec = outrec->owner;
415
+ return outrec;
416
+ }
417
+
418
+ inline bool IsValidOwner(OutRec* outrec, OutRec* testOwner)
419
+ {
420
+ // prevent outrec owning itself either directly or indirectly
421
+ while (testOwner && testOwner != outrec) testOwner = testOwner->owner;
422
+ return !testOwner;
423
+ }
424
+
425
+ inline void UncoupleOutRec(Active ae)
426
+ {
427
+ OutRec* outrec = ae.outrec;
428
+ if (!outrec) return;
429
+ outrec->front_edge->outrec = nullptr;
430
+ outrec->back_edge->outrec = nullptr;
431
+ outrec->front_edge = nullptr;
432
+ outrec->back_edge = nullptr;
433
+ }
434
+
435
+
436
+ inline bool PtsReallyClose(const Point64& pt1, const Point64& pt2)
437
+ {
438
+ return (std::llabs(pt1.x - pt2.x) < 2) && (std::llabs(pt1.y - pt2.y) < 2);
439
+ }
440
+
441
+ inline bool IsVerySmallTriangle(const OutPt& op)
442
+ {
443
+ return op.next->next == op.prev &&
444
+ (PtsReallyClose(op.prev->pt, op.next->pt) ||
445
+ PtsReallyClose(op.pt, op.next->pt) ||
446
+ PtsReallyClose(op.pt, op.prev->pt));
447
+ }
448
+
449
+ inline bool IsValidClosedPath(const OutPt* op)
450
+ {
451
+ return op && (op->next != op) && (op->next != op->prev) &&
452
+ !IsVerySmallTriangle(*op);
453
+ }
454
+
455
+ inline bool OutrecIsAscending(const Active* hotEdge)
456
+ {
457
+ return (hotEdge == hotEdge->outrec->front_edge);
458
+ }
459
+
460
+ inline void SwapFrontBackSides(OutRec& outrec)
461
+ {
462
+ Active* tmp = outrec.front_edge;
463
+ outrec.front_edge = outrec.back_edge;
464
+ outrec.back_edge = tmp;
465
+ outrec.pts = outrec.pts->next;
466
+ }
467
+
468
+ inline bool EdgesAdjacentInAEL(const IntersectNode& inode)
469
+ {
470
+ return (inode.edge1->next_in_ael == inode.edge2) || (inode.edge1->prev_in_ael == inode.edge2);
471
+ }
472
+
473
+ inline bool IsJoined(const Active& e)
474
+ {
475
+ return e.join_with != JoinWith::NoJoin;
476
+ }
477
+
478
+ inline void SetOwner(OutRec* outrec, OutRec* new_owner)
479
+ {
480
+ //precondition1: new_owner is never null
481
+ new_owner->owner = GetRealOutRec(new_owner->owner);
482
+ OutRec* tmp = new_owner;
483
+ while (tmp && tmp != outrec) tmp = tmp->owner;
484
+ if (tmp) new_owner->owner = outrec->owner;
485
+ outrec->owner = new_owner;
486
+ }
487
+
488
+ static PointInPolygonResult PointInOpPolygon(const Point64& pt, OutPt* op)
489
+ {
490
+ if (op == op->next || op->prev == op->next)
491
+ return PointInPolygonResult::IsOutside;
492
+
493
+ OutPt* op2 = op;
494
+ do
495
+ {
496
+ if (op->pt.y != pt.y) break;
497
+ op = op->next;
498
+ } while (op != op2);
499
+ if (op->pt.y == pt.y) // not a proper polygon
500
+ return PointInPolygonResult::IsOutside;
501
+
502
+ bool is_above = op->pt.y < pt.y, starting_above = is_above;
503
+ int val = 0;
504
+ op2 = op->next;
505
+ while (op2 != op)
506
+ {
507
+ if (is_above)
508
+ while (op2 != op && op2->pt.y < pt.y) op2 = op2->next;
509
+ else
510
+ while (op2 != op && op2->pt.y > pt.y) op2 = op2->next;
511
+ if (op2 == op) break;
512
+
513
+ // must have touched or crossed the pt.Y horizontal
514
+ // and this must happen an even number of times
515
+
516
+ if (op2->pt.y == pt.y) // touching the horizontal
517
+ {
518
+ if (op2->pt.x == pt.x || (op2->pt.y == op2->prev->pt.y &&
519
+ (pt.x < op2->prev->pt.x) != (pt.x < op2->pt.x)))
520
+ return PointInPolygonResult::IsOn;
521
+
522
+ op2 = op2->next;
523
+ if (op2 == op) break;
524
+ continue;
525
+ }
526
+
527
+ if (pt.x < op2->pt.x && pt.x < op2->prev->pt.x);
528
+ // do nothing because
529
+ // we're only interested in edges crossing on the left
530
+ else if ((pt.x > op2->prev->pt.x && pt.x > op2->pt.x))
531
+ val = 1 - val; // toggle val
532
+ else
533
+ {
534
+ int i = CrossProductSign(op2->prev->pt, op2->pt, pt);
535
+ if (i == 0) return PointInPolygonResult::IsOn;
536
+ if ((i < 0) == is_above) val = 1 - val;
537
+ }
538
+ is_above = !is_above;
539
+ op2 = op2->next;
540
+ }
541
+
542
+ if (is_above != starting_above)
543
+ {
544
+ int i = CrossProductSign(op2->prev->pt, op2->pt, pt);
545
+ if (i == 0) return PointInPolygonResult::IsOn;
546
+ if ((i < 0) == is_above) val = 1 - val;
547
+ }
548
+
549
+ if (val == 0) return PointInPolygonResult::IsOutside;
550
+ else return PointInPolygonResult::IsInside;
551
+ }
552
+
553
+ inline Path64 GetCleanPath(OutPt* op)
554
+ {
555
+ Path64 result;
556
+ OutPt* op2 = op;
557
+ while (op2->next != op &&
558
+ ((op2->pt.x == op2->next->pt.x && op2->pt.x == op2->prev->pt.x) ||
559
+ (op2->pt.y == op2->next->pt.y && op2->pt.y == op2->prev->pt.y))) op2 = op2->next;
560
+ result.emplace_back(op2->pt);
561
+ OutPt* prevOp = op2;
562
+ op2 = op2->next;
563
+ while (op2 != op)
564
+ {
565
+ if ((op2->pt.x != op2->next->pt.x || op2->pt.x != prevOp->pt.x) &&
566
+ (op2->pt.y != op2->next->pt.y || op2->pt.y != prevOp->pt.y))
567
+ {
568
+ result.emplace_back(op2->pt);
569
+ prevOp = op2;
570
+ }
571
+ op2 = op2->next;
572
+ }
573
+ return result;
574
+ }
575
+
576
+ inline bool Path2ContainsPath1(OutPt* op1, OutPt* op2)
577
+ {
578
+ // this function accommodates rounding errors that
579
+ // can cause path micro intersections
580
+ PointInPolygonResult pip = PointInPolygonResult::IsOn;
581
+ OutPt* op = op1;
582
+ do {
583
+ switch (PointInOpPolygon(op->pt, op2))
584
+ {
585
+ case PointInPolygonResult::IsOutside:
586
+ if (pip == PointInPolygonResult::IsOutside) return false;
587
+ pip = PointInPolygonResult::IsOutside;
588
+ break;
589
+ case PointInPolygonResult::IsInside:
590
+ if (pip == PointInPolygonResult::IsInside) return true;
591
+ pip = PointInPolygonResult::IsInside;
592
+ break;
593
+ default: break;
594
+ }
595
+ op = op->next;
596
+ } while (op != op1);
597
+ // result unclear, so try again using cleaned paths
598
+ return Path2ContainsPath1(GetCleanPath(op1), GetCleanPath(op2)); // (#973)
599
+ }
600
+
601
+ void AddLocMin(LocalMinimaList& list,
602
+ Vertex& vert, PathType polytype, bool is_open)
603
+ {
604
+ //make sure the vertex is added only once ...
605
+ if ((VertexFlags::LocalMin & vert.flags) != VertexFlags::Empty) return;
606
+
607
+ vert.flags = (vert.flags | VertexFlags::LocalMin);
608
+ list.emplace_back(std::make_unique <LocalMinima>(&vert, polytype, is_open));
609
+ }
610
+
611
+ void AddPaths_(const Paths64& paths, PathType polytype, bool is_open,
612
+ std::vector<Vertex*>& vertexLists, LocalMinimaList& locMinList)
613
+ {
614
+ const auto total_vertex_count =
615
+ std::accumulate(paths.begin(), paths.end(), size_t(0),
616
+ [](const auto& a, const Path64& path)
617
+ {return a + path.size(); });
618
+ if (total_vertex_count == 0) return;
619
+
620
+ Vertex* allVertices = new Vertex[total_vertex_count], * v = allVertices;
621
+ for (const Path64& path : paths)
622
+ {
623
+ //for each path create a circular double linked list of vertices
624
+ Vertex* v0 = v, * curr_v = v, * prev_v = nullptr;
625
+
626
+ if (path.empty())
627
+ continue;
628
+
629
+ v->prev = nullptr;
630
+ int cnt = 0;
631
+ for (const Point64& pt : path)
632
+ {
633
+ if (prev_v)
634
+ {
635
+ if (prev_v->pt == pt) continue; // ie skips duplicates
636
+ prev_v->next = curr_v;
637
+ }
638
+ curr_v->prev = prev_v;
639
+ curr_v->pt = pt;
640
+ curr_v->flags = VertexFlags::Empty;
641
+ prev_v = curr_v++;
642
+ cnt++;
643
+ }
644
+ if (!prev_v || !prev_v->prev) continue;
645
+ if (!is_open && prev_v->pt == v0->pt)
646
+ prev_v = prev_v->prev;
647
+ prev_v->next = v0;
648
+ v0->prev = prev_v;
649
+ v = curr_v; // ie get ready for next path
650
+ if (cnt < 2 || (cnt == 2 && !is_open)) continue;
651
+
652
+ //now find and assign local minima
653
+ bool going_up, going_up0;
654
+ if (is_open)
655
+ {
656
+ curr_v = v0->next;
657
+ while (curr_v != v0 && curr_v->pt.y == v0->pt.y)
658
+ curr_v = curr_v->next;
659
+ going_up = curr_v->pt.y <= v0->pt.y;
660
+ if (going_up)
661
+ {
662
+ v0->flags = VertexFlags::OpenStart;
663
+ AddLocMin(locMinList , *v0, polytype, true);
664
+ }
665
+ else
666
+ v0->flags = VertexFlags::OpenStart | VertexFlags::LocalMax;
667
+ }
668
+ else // closed path
669
+ {
670
+ prev_v = v0->prev;
671
+ while (prev_v != v0 && prev_v->pt.y == v0->pt.y)
672
+ prev_v = prev_v->prev;
673
+ if (prev_v == v0)
674
+ continue; // only open paths can be completely flat
675
+ going_up = prev_v->pt.y > v0->pt.y;
676
+ }
677
+
678
+ going_up0 = going_up;
679
+ prev_v = v0;
680
+ curr_v = v0->next;
681
+ while (curr_v != v0)
682
+ {
683
+ if (curr_v->pt.y > prev_v->pt.y && going_up)
684
+ {
685
+ prev_v->flags = (prev_v->flags | VertexFlags::LocalMax);
686
+ going_up = false;
687
+ }
688
+ else if (curr_v->pt.y < prev_v->pt.y && !going_up)
689
+ {
690
+ going_up = true;
691
+ AddLocMin(locMinList, *prev_v, polytype, is_open);
692
+ }
693
+ prev_v = curr_v;
694
+ curr_v = curr_v->next;
695
+ }
696
+
697
+ if (is_open)
698
+ {
699
+ prev_v->flags = prev_v->flags | VertexFlags::OpenEnd;
700
+ if (going_up)
701
+ prev_v->flags = prev_v->flags | VertexFlags::LocalMax;
702
+ else
703
+ AddLocMin(locMinList, *prev_v, polytype, is_open);
704
+ }
705
+ else if (going_up != going_up0)
706
+ {
707
+ if (going_up0) AddLocMin(locMinList, *prev_v, polytype, false);
708
+ else prev_v->flags = prev_v->flags | VertexFlags::LocalMax;
709
+ }
710
+ } // end processing current path
711
+
712
+ vertexLists.emplace_back(allVertices);
713
+ }
714
+
715
+ //------------------------------------------------------------------------------
716
+ // ReuseableDataContainer64 methods ...
717
+ //------------------------------------------------------------------------------
718
+
719
+ void ReuseableDataContainer64::AddLocMin(Vertex& vert, PathType polytype, bool is_open)
720
+ {
721
+ //make sure the vertex is added only once ...
722
+ if ((VertexFlags::LocalMin & vert.flags) != VertexFlags::Empty) return;
723
+
724
+ vert.flags = (vert.flags | VertexFlags::LocalMin);
725
+ minima_list_.emplace_back(std::make_unique <LocalMinima>(&vert, polytype, is_open));
726
+ }
727
+
728
+ void ReuseableDataContainer64::AddPaths(const Paths64& paths,
729
+ PathType polytype, bool is_open)
730
+ {
731
+ AddPaths_(paths, polytype, is_open, vertex_lists_, minima_list_);
732
+ }
733
+
734
+ ReuseableDataContainer64::~ReuseableDataContainer64()
735
+ {
736
+ Clear();
737
+ }
738
+
739
+ void ReuseableDataContainer64::Clear()
740
+ {
741
+ minima_list_.clear();
742
+ for (auto v : vertex_lists_) delete[] v;
743
+ vertex_lists_.clear();
744
+ }
745
+
746
+ //------------------------------------------------------------------------------
747
+ // ClipperBase methods ...
748
+ //------------------------------------------------------------------------------
749
+
750
+ ClipperBase::~ClipperBase()
751
+ {
752
+ Clear();
753
+ }
754
+
755
+ void ClipperBase::DeleteEdges(Active*& e)
756
+ {
757
+ while (e)
758
+ {
759
+ Active* e2 = e;
760
+ e = e->next_in_ael;
761
+ delete e2;
762
+ }
763
+ }
764
+
765
+ void ClipperBase::CleanUp()
766
+ {
767
+ DeleteEdges(actives_);
768
+ scanline_list_ = std::priority_queue<int64_t>();
769
+ intersect_nodes_.clear();
770
+ DisposeAllOutRecs();
771
+ horz_seg_list_.clear();
772
+ horz_join_list_.clear();
773
+ }
774
+
775
+
776
+ void ClipperBase::Clear()
777
+ {
778
+ CleanUp();
779
+ DisposeVerticesAndLocalMinima();
780
+ current_locmin_iter_ = minima_list_.begin();
781
+ minima_list_sorted_ = false;
782
+ has_open_paths_ = false;
783
+ }
784
+
785
+
786
+ void ClipperBase::Reset()
787
+ {
788
+ if (!minima_list_sorted_)
789
+ {
790
+ std::stable_sort(minima_list_.begin(), minima_list_.end(), LocMinSorter()); //#594
791
+ minima_list_sorted_ = true;
792
+ }
793
+ LocalMinimaList::const_reverse_iterator i;
794
+ for (i = minima_list_.rbegin(); i != minima_list_.rend(); ++i)
795
+ InsertScanline((*i)->vertex->pt.y);
796
+
797
+ current_locmin_iter_ = minima_list_.begin();
798
+ actives_ = nullptr;
799
+ sel_ = nullptr;
800
+ succeeded_ = true;
801
+ }
802
+
803
+
804
+ #ifdef USINGZ
805
+ void ClipperBase::SetZ(const Active& e1, const Active& e2, Point64& ip)
806
+ {
807
+ if (!zCallback_) return;
808
+ // prioritize subject over clip vertices by passing
809
+ // subject vertices before clip vertices in the callback
810
+ if (GetPolyType(e1) == PathType::Subject)
811
+ {
812
+ if (ip == e1.bot) ip.z = e1.bot.z;
813
+ else if (ip == e1.top) ip.z = e1.top.z;
814
+ else if (ip == e2.bot) ip.z = e2.bot.z;
815
+ else if (ip == e2.top) ip.z = e2.top.z;
816
+ else ip.z = DefaultZ;
817
+ zCallback_(e1.bot, e1.top, e2.bot, e2.top, ip);
818
+ }
819
+ else
820
+ {
821
+ if (ip == e2.bot) ip.z = e2.bot.z;
822
+ else if (ip == e2.top) ip.z = e2.top.z;
823
+ else if (ip == e1.bot) ip.z = e1.bot.z;
824
+ else if (ip == e1.top) ip.z = e1.top.z;
825
+ else ip.z = DefaultZ;
826
+ zCallback_(e2.bot, e2.top, e1.bot, e1.top, ip);
827
+ }
828
+ }
829
+ #endif
830
+
831
+ void ClipperBase::AddPath(const Path64& path, PathType polytype, bool is_open)
832
+ {
833
+ AddPaths(Paths64(1, path), polytype, is_open);
834
+ }
835
+
836
+ void ClipperBase::AddPaths(const Paths64& paths, PathType polytype, bool is_open)
837
+ {
838
+ if (is_open) has_open_paths_ = true;
839
+ minima_list_sorted_ = false;
840
+ AddPaths_(paths, polytype, is_open, vertex_lists_, minima_list_);
841
+ }
842
+
843
+ void ClipperBase::AddReuseableData(const ReuseableDataContainer64& reuseable_data)
844
+ {
845
+ // nb: reuseable_data will continue to own the vertices
846
+ // and remains responsible for their clean up.
847
+ succeeded_ = false;
848
+ minima_list_sorted_ = false;
849
+ LocalMinimaList::const_iterator i;
850
+ for (i = reuseable_data.minima_list_.cbegin(); i != reuseable_data.minima_list_.cend(); ++i)
851
+ {
852
+ minima_list_.emplace_back(std::make_unique <LocalMinima>((*i)->vertex, (*i)->polytype, (*i)->is_open));
853
+ if ((*i)->is_open) has_open_paths_ = true;
854
+ }
855
+ }
856
+
857
+ void ClipperBase::InsertScanline(int64_t y)
858
+ {
859
+ scanline_list_.push(y);
860
+ }
861
+
862
+
863
+ bool ClipperBase::PopScanline(int64_t& y)
864
+ {
865
+ if (scanline_list_.empty()) return false;
866
+ y = scanline_list_.top();
867
+ scanline_list_.pop();
868
+ while (!scanline_list_.empty() && y == scanline_list_.top())
869
+ scanline_list_.pop(); // Pop duplicates.
870
+ return true;
871
+ }
872
+
873
+
874
+ bool ClipperBase::PopLocalMinima(int64_t y, LocalMinima*& local_minima)
875
+ {
876
+ if (current_locmin_iter_ == minima_list_.end() || (*current_locmin_iter_)->vertex->pt.y != y) return false;
877
+ local_minima = (current_locmin_iter_++)->get();
878
+ return true;
879
+ }
880
+
881
+ void ClipperBase::DisposeAllOutRecs()
882
+ {
883
+ for (auto outrec : outrec_list_)
884
+ {
885
+ if (outrec->pts) DisposeOutPts(outrec);
886
+ delete outrec;
887
+ }
888
+ outrec_list_.resize(0);
889
+ }
890
+
891
+ void ClipperBase::DisposeVerticesAndLocalMinima()
892
+ {
893
+ minima_list_.clear();
894
+ for (auto v : vertex_lists_) delete[] v;
895
+ vertex_lists_.clear();
896
+ }
897
+
898
+
899
+ void ClipperBase::AddLocMin(Vertex& vert, PathType polytype, bool is_open)
900
+ {
901
+ //make sure the vertex is added only once ...
902
+ if ((VertexFlags::LocalMin & vert.flags) != VertexFlags::Empty) return;
903
+
904
+ vert.flags = (vert.flags | VertexFlags::LocalMin);
905
+ minima_list_.emplace_back(std::make_unique <LocalMinima>(&vert, polytype, is_open));
906
+ }
907
+
908
+ bool ClipperBase::IsContributingClosed(const Active& e) const
909
+ {
910
+ switch (fillrule_)
911
+ {
912
+ case FillRule::EvenOdd:
913
+ break;
914
+ case FillRule::NonZero:
915
+ if (abs(e.wind_cnt) != 1) return false;
916
+ break;
917
+ case FillRule::Positive:
918
+ if (e.wind_cnt != 1) return false;
919
+ break;
920
+ case FillRule::Negative:
921
+ if (e.wind_cnt != -1) return false;
922
+ break;
923
+ // Should never happen, but adding this to stop a compiler warning
924
+ default:
925
+ break;
926
+ }
927
+
928
+ switch (cliptype_)
929
+ {
930
+ case ClipType::NoClip:
931
+ return false;
932
+ case ClipType::Intersection:
933
+ switch (fillrule_)
934
+ {
935
+ case FillRule::Positive:
936
+ return (e.wind_cnt2 > 0);
937
+ case FillRule::Negative:
938
+ return (e.wind_cnt2 < 0);
939
+ default:
940
+ return (e.wind_cnt2 != 0);
941
+ }
942
+ break;
943
+
944
+ case ClipType::Union:
945
+ switch (fillrule_)
946
+ {
947
+ case FillRule::Positive:
948
+ return (e.wind_cnt2 <= 0);
949
+ case FillRule::Negative:
950
+ return (e.wind_cnt2 >= 0);
951
+ default:
952
+ return (e.wind_cnt2 == 0);
953
+ }
954
+ break;
955
+
956
+ case ClipType::Difference:
957
+ bool result;
958
+ switch (fillrule_)
959
+ {
960
+ case FillRule::Positive:
961
+ result = (e.wind_cnt2 <= 0);
962
+ break;
963
+ case FillRule::Negative:
964
+ result = (e.wind_cnt2 >= 0);
965
+ break;
966
+ default:
967
+ result = (e.wind_cnt2 == 0);
968
+ }
969
+ if (GetPolyType(e) == PathType::Subject)
970
+ return result;
971
+ else
972
+ return !result;
973
+ break;
974
+
975
+ case ClipType::Xor: return true; break;
976
+ // Should never happen, but adding this to stop a compiler warning
977
+ default:
978
+ break;
979
+ }
980
+ return false; // we should never get here
981
+ }
982
+
983
+
984
+ inline bool ClipperBase::IsContributingOpen(const Active& e) const
985
+ {
986
+ bool is_in_clip, is_in_subj;
987
+ switch (fillrule_)
988
+ {
989
+ case FillRule::Positive:
990
+ is_in_clip = e.wind_cnt2 > 0;
991
+ is_in_subj = e.wind_cnt > 0;
992
+ break;
993
+ case FillRule::Negative:
994
+ is_in_clip = e.wind_cnt2 < 0;
995
+ is_in_subj = e.wind_cnt < 0;
996
+ break;
997
+ default:
998
+ is_in_clip = e.wind_cnt2 != 0;
999
+ is_in_subj = e.wind_cnt != 0;
1000
+ }
1001
+
1002
+ switch (cliptype_)
1003
+ {
1004
+ case ClipType::Intersection: return is_in_clip;
1005
+ case ClipType::Union: return (!is_in_subj && !is_in_clip);
1006
+ default: return !is_in_clip;
1007
+ }
1008
+ }
1009
+
1010
+
1011
+ void ClipperBase::SetWindCountForClosedPathEdge(Active& e)
1012
+ {
1013
+ //Wind counts refer to polygon regions not edges, so here an edge's WindCnt
1014
+ //indicates the higher of the wind counts for the two regions touching the
1015
+ //edge. (NB Adjacent regions can only ever have their wind counts differ by
1016
+ //one. Also, open paths have no meaningful wind directions or counts.)
1017
+
1018
+ Active* e2 = e.prev_in_ael;
1019
+ //find the nearest closed path edge of the same PolyType in AEL (heading left)
1020
+ PathType pt = GetPolyType(e);
1021
+ while (e2 && (GetPolyType(*e2) != pt || IsOpen(*e2))) e2 = e2->prev_in_ael;
1022
+
1023
+ if (!e2)
1024
+ {
1025
+ e.wind_cnt = e.wind_dx;
1026
+ e2 = actives_;
1027
+ }
1028
+ else if (fillrule_ == FillRule::EvenOdd)
1029
+ {
1030
+ e.wind_cnt = e.wind_dx;
1031
+ e.wind_cnt2 = e2->wind_cnt2;
1032
+ e2 = e2->next_in_ael;
1033
+ }
1034
+ else
1035
+ {
1036
+ //NonZero, positive, or negative filling here ...
1037
+ //if e's WindCnt is in the SAME direction as its WindDx, then polygon
1038
+ //filling will be on the right of 'e'.
1039
+ //NB neither e2.WindCnt nor e2.WindDx should ever be 0.
1040
+ if (e2->wind_cnt * e2->wind_dx < 0)
1041
+ {
1042
+ //opposite directions so 'e' is outside 'e2' ...
1043
+ if (abs(e2->wind_cnt) > 1)
1044
+ {
1045
+ //outside prev poly but still inside another.
1046
+ if (e2->wind_dx * e.wind_dx < 0)
1047
+ //reversing direction so use the same WC
1048
+ e.wind_cnt = e2->wind_cnt;
1049
+ else
1050
+ //otherwise keep 'reducing' the WC by 1 (ie towards 0) ...
1051
+ e.wind_cnt = e2->wind_cnt + e.wind_dx;
1052
+ }
1053
+ else
1054
+ //now outside all polys of same polytype so set own WC ...
1055
+ e.wind_cnt = (IsOpen(e) ? 1 : e.wind_dx);
1056
+ }
1057
+ else
1058
+ {
1059
+ //'e' must be inside 'e2'
1060
+ if (e2->wind_dx * e.wind_dx < 0)
1061
+ //reversing direction so use the same WC
1062
+ e.wind_cnt = e2->wind_cnt;
1063
+ else
1064
+ //otherwise keep 'increasing' the WC by 1 (ie away from 0) ...
1065
+ e.wind_cnt = e2->wind_cnt + e.wind_dx;
1066
+ }
1067
+ e.wind_cnt2 = e2->wind_cnt2;
1068
+ e2 = e2->next_in_ael; // ie get ready to calc WindCnt2
1069
+ }
1070
+
1071
+ //update wind_cnt2 ...
1072
+ if (fillrule_ == FillRule::EvenOdd)
1073
+ while (e2 != &e)
1074
+ {
1075
+ if (GetPolyType(*e2) != pt && !IsOpen(*e2))
1076
+ e.wind_cnt2 = (e.wind_cnt2 == 0 ? 1 : 0);
1077
+ e2 = e2->next_in_ael;
1078
+ }
1079
+ else
1080
+ while (e2 != &e)
1081
+ {
1082
+ if (GetPolyType(*e2) != pt && !IsOpen(*e2))
1083
+ e.wind_cnt2 += e2->wind_dx;
1084
+ e2 = e2->next_in_ael;
1085
+ }
1086
+ }
1087
+
1088
+
1089
+ void ClipperBase::SetWindCountForOpenPathEdge(Active& e)
1090
+ {
1091
+ Active* e2 = actives_;
1092
+ if (fillrule_ == FillRule::EvenOdd)
1093
+ {
1094
+ int cnt1 = 0, cnt2 = 0;
1095
+ while (e2 != &e)
1096
+ {
1097
+ if (GetPolyType(*e2) == PathType::Clip)
1098
+ cnt2++;
1099
+ else if (!IsOpen(*e2))
1100
+ cnt1++;
1101
+ e2 = e2->next_in_ael;
1102
+ }
1103
+ e.wind_cnt = (IsOdd(cnt1) ? 1 : 0);
1104
+ e.wind_cnt2 = (IsOdd(cnt2) ? 1 : 0);
1105
+ }
1106
+ else
1107
+ {
1108
+ while (e2 != &e)
1109
+ {
1110
+ if (GetPolyType(*e2) == PathType::Clip)
1111
+ e.wind_cnt2 += e2->wind_dx;
1112
+ else if (!IsOpen(*e2))
1113
+ e.wind_cnt += e2->wind_dx;
1114
+ e2 = e2->next_in_ael;
1115
+ }
1116
+ }
1117
+ }
1118
+
1119
+ bool IsValidAelOrder(const Active& resident, const Active& newcomer)
1120
+ {
1121
+ if (newcomer.curr_x != resident.curr_x)
1122
+ return newcomer.curr_x > resident.curr_x;
1123
+
1124
+ //get the turning direction a1.top, a2.bot, a2.top
1125
+ int i = CrossProductSign(resident.top, newcomer.bot, newcomer.top);
1126
+ if (i != 0) return i < 0;
1127
+
1128
+ //edges must be collinear to get here
1129
+ //for starting open paths, place them according to
1130
+ //the direction they're about to turn
1131
+ if (!IsMaxima(resident) && (resident.top.y > newcomer.top.y))
1132
+ {
1133
+ return (CrossProductSign(newcomer.bot, resident.top, NextVertex(resident)->pt) <= 0);
1134
+ }
1135
+ else if (!IsMaxima(newcomer) && (newcomer.top.y > resident.top.y))
1136
+ {
1137
+ return (CrossProductSign(newcomer.bot, newcomer.top, NextVertex(newcomer)->pt) >= 0);
1138
+ }
1139
+
1140
+ int64_t y = newcomer.bot.y;
1141
+ bool newcomerIsLeft = newcomer.is_left_bound;
1142
+
1143
+ if (resident.bot.y != y || resident.local_min->vertex->pt.y != y)
1144
+ return newcomer.is_left_bound;
1145
+ //resident must also have just been inserted
1146
+ else if (resident.is_left_bound != newcomerIsLeft)
1147
+ return newcomerIsLeft;
1148
+ else if (IsCollinear(PrevPrevVertex(resident)->pt,
1149
+ resident.bot, resident.top)) return true;
1150
+ else
1151
+ //compare turning direction of the alternate bound
1152
+ return (CrossProductSign(PrevPrevVertex(resident)->pt,
1153
+ newcomer.bot, PrevPrevVertex(newcomer)->pt) > 0) == newcomerIsLeft;
1154
+ }
1155
+
1156
+
1157
+ void ClipperBase::InsertLeftEdge(Active& e)
1158
+ {
1159
+ Active* e2;
1160
+ if (!actives_)
1161
+ {
1162
+ e.prev_in_ael = nullptr;
1163
+ e.next_in_ael = nullptr;
1164
+ actives_ = &e;
1165
+ }
1166
+ else if (!IsValidAelOrder(*actives_, e))
1167
+ {
1168
+ e.prev_in_ael = nullptr;
1169
+ e.next_in_ael = actives_;
1170
+ actives_->prev_in_ael = &e;
1171
+ actives_ = &e;
1172
+ }
1173
+ else
1174
+ {
1175
+ e2 = actives_;
1176
+ while (e2->next_in_ael && IsValidAelOrder(*e2->next_in_ael, e))
1177
+ e2 = e2->next_in_ael;
1178
+ if (e2->join_with == JoinWith::Right)
1179
+ e2 = e2->next_in_ael;
1180
+ if (!e2) return; // should never happen and stops compiler warning :)
1181
+ e.next_in_ael = e2->next_in_ael;
1182
+ if (e2->next_in_ael) e2->next_in_ael->prev_in_ael = &e;
1183
+ e.prev_in_ael = e2;
1184
+ e2->next_in_ael = &e;
1185
+ }
1186
+ }
1187
+
1188
+
1189
+ void InsertRightEdge(Active& e, Active& e2)
1190
+ {
1191
+ e2.next_in_ael = e.next_in_ael;
1192
+ if (e.next_in_ael) e.next_in_ael->prev_in_ael = &e2;
1193
+ e2.prev_in_ael = &e;
1194
+ e.next_in_ael = &e2;
1195
+ }
1196
+
1197
+
1198
+ void ClipperBase::InsertLocalMinimaIntoAEL(int64_t bot_y)
1199
+ {
1200
+ LocalMinima* local_minima;
1201
+ Active* left_bound, * right_bound;
1202
+ //Add any local minima (if any) at BotY ...
1203
+ //nb: horizontal local minima edges should contain locMin.vertex.prev
1204
+
1205
+ while (PopLocalMinima(bot_y, local_minima))
1206
+ {
1207
+ if ((local_minima->vertex->flags & VertexFlags::OpenStart) != VertexFlags::Empty)
1208
+ {
1209
+ left_bound = nullptr;
1210
+ }
1211
+ else
1212
+ {
1213
+ left_bound = new Active();
1214
+ left_bound->bot = local_minima->vertex->pt;
1215
+ left_bound->curr_x = left_bound->bot.x;
1216
+ left_bound->wind_dx = -1;
1217
+ left_bound->vertex_top = local_minima->vertex->prev; // ie descending
1218
+ left_bound->top = left_bound->vertex_top->pt;
1219
+ left_bound->local_min = local_minima;
1220
+ SetDx(*left_bound);
1221
+ }
1222
+
1223
+ if ((local_minima->vertex->flags & VertexFlags::OpenEnd) != VertexFlags::Empty)
1224
+ {
1225
+ right_bound = nullptr;
1226
+ }
1227
+ else
1228
+ {
1229
+ right_bound = new Active();
1230
+ right_bound->bot = local_minima->vertex->pt;
1231
+ right_bound->curr_x = right_bound->bot.x;
1232
+ right_bound->wind_dx = 1;
1233
+ right_bound->vertex_top = local_minima->vertex->next; // ie ascending
1234
+ right_bound->top = right_bound->vertex_top->pt;
1235
+ right_bound->local_min = local_minima;
1236
+ SetDx(*right_bound);
1237
+ }
1238
+
1239
+ //Currently LeftB is just the descending bound and RightB is the ascending.
1240
+ //Now if the LeftB isn't on the left of RightB then we need swap them.
1241
+ if (left_bound && right_bound)
1242
+ {
1243
+ if (IsHorizontal(*left_bound))
1244
+ {
1245
+ if (IsHeadingRightHorz(*left_bound)) SwapActives(left_bound, right_bound);
1246
+ }
1247
+ else if (IsHorizontal(*right_bound))
1248
+ {
1249
+ if (IsHeadingLeftHorz(*right_bound)) SwapActives(left_bound, right_bound);
1250
+ }
1251
+ else if (left_bound->dx < right_bound->dx)
1252
+ SwapActives(left_bound, right_bound);
1253
+ }
1254
+ else if (!left_bound)
1255
+ {
1256
+ left_bound = right_bound;
1257
+ right_bound = nullptr;
1258
+ }
1259
+
1260
+ bool contributing;
1261
+ left_bound->is_left_bound = true;
1262
+ InsertLeftEdge(*left_bound);
1263
+
1264
+ if (IsOpen(*left_bound))
1265
+ {
1266
+ SetWindCountForOpenPathEdge(*left_bound);
1267
+ contributing = IsContributingOpen(*left_bound);
1268
+ }
1269
+ else
1270
+ {
1271
+ SetWindCountForClosedPathEdge(*left_bound);
1272
+ contributing = IsContributingClosed(*left_bound);
1273
+ }
1274
+
1275
+ if (right_bound)
1276
+ {
1277
+ right_bound->is_left_bound = false;
1278
+ right_bound->wind_cnt = left_bound->wind_cnt;
1279
+ right_bound->wind_cnt2 = left_bound->wind_cnt2;
1280
+ InsertRightEdge(*left_bound, *right_bound); ///////
1281
+ if (contributing)
1282
+ {
1283
+ AddLocalMinPoly(*left_bound, *right_bound, left_bound->bot, true);
1284
+ if (!IsHorizontal(*left_bound))
1285
+ CheckJoinLeft(*left_bound, left_bound->bot);
1286
+ }
1287
+
1288
+ while (right_bound->next_in_ael &&
1289
+ IsValidAelOrder(*right_bound->next_in_ael, *right_bound))
1290
+ {
1291
+ IntersectEdges(*right_bound, *right_bound->next_in_ael, right_bound->bot);
1292
+ SwapPositionsInAEL(*right_bound, *right_bound->next_in_ael);
1293
+ }
1294
+
1295
+ if (IsHorizontal(*right_bound))
1296
+ PushHorz(*right_bound);
1297
+ else
1298
+ {
1299
+ CheckJoinRight(*right_bound, right_bound->bot);
1300
+ InsertScanline(right_bound->top.y);
1301
+ }
1302
+ }
1303
+ else if (contributing)
1304
+ {
1305
+ StartOpenPath(*left_bound, left_bound->bot);
1306
+ }
1307
+
1308
+ if (IsHorizontal(*left_bound))
1309
+ PushHorz(*left_bound);
1310
+ else
1311
+ InsertScanline(left_bound->top.y);
1312
+ } // while (PopLocalMinima())
1313
+ }
1314
+
1315
+
1316
+ inline void ClipperBase::PushHorz(Active& e)
1317
+ {
1318
+ e.next_in_sel = (sel_ ? sel_ : nullptr);
1319
+ sel_ = &e;
1320
+ }
1321
+
1322
+
1323
+ inline bool ClipperBase::PopHorz(Active*& e)
1324
+ {
1325
+ e = sel_;
1326
+ if (!e) return false;
1327
+ sel_ = sel_->next_in_sel;
1328
+ return true;
1329
+ }
1330
+
1331
+
1332
+ OutPt* ClipperBase::AddLocalMinPoly(Active& e1, Active& e2,
1333
+ const Point64& pt, bool is_new)
1334
+ {
1335
+ OutRec* outrec = NewOutRec();
1336
+ e1.outrec = outrec;
1337
+ e2.outrec = outrec;
1338
+
1339
+ if (IsOpen(e1))
1340
+ {
1341
+ outrec->owner = nullptr;
1342
+ outrec->is_open = true;
1343
+ if (e1.wind_dx > 0)
1344
+ SetSides(*outrec, e1, e2);
1345
+ else
1346
+ SetSides(*outrec, e2, e1);
1347
+ }
1348
+ else
1349
+ {
1350
+ Active* prevHotEdge = GetPrevHotEdge(e1);
1351
+ //e.windDx is the winding direction of the **input** paths
1352
+ //and unrelated to the winding direction of output polygons.
1353
+ //Output orientation is determined by e.outrec.frontE which is
1354
+ //the ascending edge (see AddLocalMinPoly).
1355
+ if (prevHotEdge)
1356
+ {
1357
+ if (using_polytree_)
1358
+ SetOwner(outrec, prevHotEdge->outrec);
1359
+ if (OutrecIsAscending(prevHotEdge) == is_new)
1360
+ SetSides(*outrec, e2, e1);
1361
+ else
1362
+ SetSides(*outrec, e1, e2);
1363
+ }
1364
+ else
1365
+ {
1366
+ outrec->owner = nullptr;
1367
+ if (is_new)
1368
+ SetSides(*outrec, e1, e2);
1369
+ else
1370
+ SetSides(*outrec, e2, e1);
1371
+ }
1372
+ }
1373
+
1374
+ OutPt* op = new OutPt(pt, outrec);
1375
+ outrec->pts = op;
1376
+ return op;
1377
+ }
1378
+
1379
+
1380
+ OutPt* ClipperBase::AddLocalMaxPoly(Active& e1, Active& e2, const Point64& pt)
1381
+ {
1382
+ if (IsJoined(e1)) Split(e1, pt);
1383
+ if (IsJoined(e2)) Split(e2, pt);
1384
+
1385
+ if (IsFront(e1) == IsFront(e2))
1386
+ {
1387
+ if (IsOpenEnd(e1))
1388
+ SwapFrontBackSides(*e1.outrec);
1389
+ else if (IsOpenEnd(e2))
1390
+ SwapFrontBackSides(*e2.outrec);
1391
+ else
1392
+ {
1393
+ succeeded_ = false;
1394
+ return nullptr;
1395
+ }
1396
+ }
1397
+
1398
+ OutPt* result = AddOutPt(e1, pt);
1399
+ if (e1.outrec == e2.outrec)
1400
+ {
1401
+ OutRec& outrec = *e1.outrec;
1402
+ outrec.pts = result;
1403
+
1404
+ if (using_polytree_)
1405
+ {
1406
+ Active* e = GetPrevHotEdge(e1);
1407
+ if (!e)
1408
+ outrec.owner = nullptr;
1409
+ else
1410
+ SetOwner(&outrec, e->outrec);
1411
+ // nb: outRec.owner here is likely NOT the real
1412
+ // owner but this will be checked in RecursiveCheckOwners()
1413
+ }
1414
+
1415
+ UncoupleOutRec(e1);
1416
+ result = outrec.pts;
1417
+ if (outrec.owner && !outrec.owner->front_edge)
1418
+ outrec.owner = GetRealOutRec(outrec.owner);
1419
+ }
1420
+ //and to preserve the winding orientation of outrec ...
1421
+ else if (IsOpen(e1))
1422
+ {
1423
+ if (e1.wind_dx < 0)
1424
+ JoinOutrecPaths(e1, e2);
1425
+ else
1426
+ JoinOutrecPaths(e2, e1);
1427
+ }
1428
+ else if (e1.outrec->idx < e2.outrec->idx)
1429
+ JoinOutrecPaths(e1, e2);
1430
+ else
1431
+ JoinOutrecPaths(e2, e1);
1432
+ return result;
1433
+ }
1434
+
1435
+ void ClipperBase::JoinOutrecPaths(Active& e1, Active& e2)
1436
+ {
1437
+ //join e2 outrec path onto e1 outrec path and then delete e2 outrec path
1438
+ //pointers. (NB Only very rarely do the joining ends share the same coords.)
1439
+ OutPt* p1_st = e1.outrec->pts;
1440
+ OutPt* p2_st = e2.outrec->pts;
1441
+ OutPt* p1_end = p1_st->next;
1442
+ OutPt* p2_end = p2_st->next;
1443
+ if (IsFront(e1))
1444
+ {
1445
+ p2_end->prev = p1_st;
1446
+ p1_st->next = p2_end;
1447
+ p2_st->next = p1_end;
1448
+ p1_end->prev = p2_st;
1449
+ e1.outrec->pts = p2_st;
1450
+ e1.outrec->front_edge = e2.outrec->front_edge;
1451
+ if (e1.outrec->front_edge)
1452
+ e1.outrec->front_edge->outrec = e1.outrec;
1453
+ }
1454
+ else
1455
+ {
1456
+ p1_end->prev = p2_st;
1457
+ p2_st->next = p1_end;
1458
+ p1_st->next = p2_end;
1459
+ p2_end->prev = p1_st;
1460
+ e1.outrec->back_edge = e2.outrec->back_edge;
1461
+ if (e1.outrec->back_edge)
1462
+ e1.outrec->back_edge->outrec = e1.outrec;
1463
+ }
1464
+
1465
+ //after joining, the e2.OutRec must contains no vertices ...
1466
+ e2.outrec->front_edge = nullptr;
1467
+ e2.outrec->back_edge = nullptr;
1468
+ e2.outrec->pts = nullptr;
1469
+
1470
+ if (IsOpenEnd(e1))
1471
+ {
1472
+ e2.outrec->pts = e1.outrec->pts;
1473
+ e1.outrec->pts = nullptr;
1474
+ }
1475
+ else
1476
+ SetOwner(e2.outrec, e1.outrec);
1477
+
1478
+ //and e1 and e2 are maxima and are about to be dropped from the Actives list.
1479
+ e1.outrec = nullptr;
1480
+ e2.outrec = nullptr;
1481
+ }
1482
+
1483
+ OutRec* ClipperBase::NewOutRec()
1484
+ {
1485
+ OutRec* result = new OutRec();
1486
+ result->idx = outrec_list_.size();
1487
+ outrec_list_.emplace_back(result);
1488
+ result->pts = nullptr;
1489
+ result->owner = nullptr;
1490
+ result->polypath = nullptr;
1491
+ result->is_open = false;
1492
+ result->splits = nullptr;
1493
+ return result;
1494
+ }
1495
+
1496
+
1497
+ OutPt* ClipperBase::AddOutPt(const Active& e, const Point64& pt)
1498
+ {
1499
+ OutPt* new_op = nullptr;
1500
+
1501
+ //Outrec.OutPts: a circular doubly-linked-list of POutPt where ...
1502
+ //op_front[.Prev]* ~~~> op_back & op_back == op_front.Next
1503
+ OutRec* outrec = e.outrec;
1504
+ bool to_front = IsFront(e);
1505
+ OutPt* op_front = outrec->pts;
1506
+ OutPt* op_back = op_front->next;
1507
+
1508
+ if (to_front)
1509
+ {
1510
+ if (pt == op_front->pt)
1511
+ return op_front;
1512
+ }
1513
+ else if (pt == op_back->pt)
1514
+ return op_back;
1515
+
1516
+ new_op = new OutPt(pt, outrec);
1517
+ op_back->prev = new_op;
1518
+ new_op->prev = op_front;
1519
+ new_op->next = op_back;
1520
+ op_front->next = new_op;
1521
+ if (to_front) outrec->pts = new_op;
1522
+ return new_op;
1523
+ }
1524
+
1525
+ void ClipperBase::CleanCollinear(OutRec* outrec)
1526
+ {
1527
+ outrec = GetRealOutRec(outrec);
1528
+ if (!outrec || outrec->is_open) return;
1529
+ if (!IsValidClosedPath(outrec->pts))
1530
+ {
1531
+ DisposeOutPts(outrec);
1532
+ return;
1533
+ }
1534
+
1535
+ OutPt* startOp = outrec->pts, * op2 = startOp;
1536
+ for (; ; )
1537
+ {
1538
+ //NB if preserveCollinear == true, then only remove 180 deg. spikes
1539
+ if (IsCollinear(op2->prev->pt, op2->pt, op2->next->pt) &&
1540
+ (op2->pt == op2->prev->pt ||
1541
+ op2->pt == op2->next->pt || !preserve_collinear_ ||
1542
+ DotProduct(op2->prev->pt, op2->pt, op2->next->pt) < 0))
1543
+ {
1544
+
1545
+ if (op2 == outrec->pts) outrec->pts = op2->prev;
1546
+
1547
+ op2 = DisposeOutPt(op2);
1548
+ if (!IsValidClosedPath(op2))
1549
+ {
1550
+ DisposeOutPts(outrec);
1551
+ return;
1552
+ }
1553
+ startOp = op2;
1554
+ continue;
1555
+ }
1556
+ op2 = op2->next;
1557
+ if (op2 == startOp) break;
1558
+ }
1559
+ FixSelfIntersects(outrec);
1560
+ }
1561
+
1562
+ void ClipperBase::DoSplitOp (OutRec* outrec, OutPt* splitOp)
1563
+ {
1564
+ // splitOp.prev -> splitOp &&
1565
+ // splitOp.next -> splitOp.next.next are intersecting
1566
+ OutPt* prevOp = splitOp->prev;
1567
+ OutPt* nextNextOp = splitOp->next->next;
1568
+ outrec->pts = prevOp;
1569
+
1570
+ Point64 ip;
1571
+ GetLineIntersectPt(prevOp->pt, splitOp->pt,
1572
+ splitOp->next->pt, nextNextOp->pt, ip);
1573
+
1574
+ #ifdef USINGZ
1575
+ if (zCallback_) zCallback_(prevOp->pt, splitOp->pt,
1576
+ splitOp->next->pt, nextNextOp->pt, ip);
1577
+ #endif
1578
+ double area1 = Area(outrec->pts);
1579
+ double absArea1 = std::fabs(area1);
1580
+ if (absArea1 < 2)
1581
+ {
1582
+ DisposeOutPts(outrec);
1583
+ return;
1584
+ }
1585
+
1586
+ double area2 = AreaTriangle(ip, splitOp->pt, splitOp->next->pt);
1587
+ double absArea2 = std::fabs(area2);
1588
+
1589
+ // de-link splitOp and splitOp.next from the path
1590
+ // while inserting the intersection point
1591
+ if (ip == prevOp->pt || ip == nextNextOp->pt)
1592
+ {
1593
+ nextNextOp->prev = prevOp;
1594
+ prevOp->next = nextNextOp;
1595
+ }
1596
+ else
1597
+ {
1598
+ OutPt* newOp2 = new OutPt(ip, prevOp->outrec);
1599
+ newOp2->prev = prevOp;
1600
+ newOp2->next = nextNextOp;
1601
+ nextNextOp->prev = newOp2;
1602
+ prevOp->next = newOp2;
1603
+ }
1604
+
1605
+ // area1 is the path's area *before* splitting, whereas area2 is
1606
+ // the area of the triangle containing splitOp & splitOp.next.
1607
+ // So the only way for these areas to have the same sign is if
1608
+ // the split triangle is larger than the path containing prevOp or
1609
+ // if there's more than one self-intersection.
1610
+ if (absArea2 >= 1 &&
1611
+ (absArea2 > absArea1 || (area2 > 0) == (area1 > 0)))
1612
+ {
1613
+ OutRec* newOr = NewOutRec();
1614
+ newOr->owner = outrec->owner;
1615
+
1616
+ splitOp->outrec = newOr;
1617
+ splitOp->next->outrec = newOr;
1618
+ OutPt* newOp = new OutPt(ip, newOr);
1619
+ newOp->prev = splitOp->next;
1620
+ newOp->next = splitOp;
1621
+ newOr->pts = newOp;
1622
+ splitOp->prev = newOp;
1623
+ splitOp->next->next = newOp;
1624
+
1625
+ if (using_polytree_)
1626
+ {
1627
+ if (Path2ContainsPath1(prevOp, newOp))
1628
+ {
1629
+ newOr->splits = new OutRecList();
1630
+ newOr->splits->emplace_back(outrec);
1631
+ }
1632
+ else
1633
+ {
1634
+ if (!outrec->splits) outrec->splits = new OutRecList();
1635
+ outrec->splits->emplace_back(newOr);
1636
+ }
1637
+ }
1638
+ }
1639
+ else
1640
+ {
1641
+ delete splitOp->next;
1642
+ delete splitOp;
1643
+ }
1644
+ }
1645
+
1646
+ void ClipperBase::FixSelfIntersects(OutRec* outrec)
1647
+ {
1648
+ OutPt* op2 = outrec->pts;
1649
+ if (op2->prev == op2->next->next)
1650
+ return; // because triangles can't self-intersect
1651
+ for (; ; )
1652
+ {
1653
+ if (SegmentsIntersect(op2->prev->pt,
1654
+ op2->pt, op2->next->pt, op2->next->next->pt))
1655
+ {
1656
+ if (op2 == outrec->pts || op2->next == outrec->pts)
1657
+ outrec->pts = outrec->pts->prev;
1658
+ DoSplitOp(outrec, op2);
1659
+ if (!outrec->pts) break;
1660
+ op2 = outrec->pts;
1661
+ if (op2->prev == op2->next->next)
1662
+ break; // again, because triangles can't self-intersect
1663
+ continue;
1664
+ }
1665
+ else
1666
+ op2 = op2->next;
1667
+
1668
+ if (op2 == outrec->pts) break;
1669
+ }
1670
+ }
1671
+
1672
+
1673
+ inline void UpdateOutrecOwner(OutRec* outrec)
1674
+ {
1675
+ OutPt* opCurr = outrec->pts;
1676
+ for (; ; )
1677
+ {
1678
+ opCurr->outrec = outrec;
1679
+ opCurr = opCurr->next;
1680
+ if (opCurr == outrec->pts) return;
1681
+ }
1682
+ }
1683
+
1684
+
1685
+ OutPt* ClipperBase::StartOpenPath(Active& e, const Point64& pt)
1686
+ {
1687
+ OutRec* outrec = NewOutRec();
1688
+ outrec->is_open = true;
1689
+
1690
+ if (e.wind_dx > 0)
1691
+ {
1692
+ outrec->front_edge = &e;
1693
+ outrec->back_edge = nullptr;
1694
+ }
1695
+ else
1696
+ {
1697
+ outrec->front_edge = nullptr;
1698
+ outrec->back_edge = &e;
1699
+ }
1700
+
1701
+ e.outrec = outrec;
1702
+
1703
+ OutPt* op = new OutPt(pt, outrec);
1704
+ outrec->pts = op;
1705
+ return op;
1706
+ }
1707
+
1708
+ inline void TrimHorz(Active& horzEdge, bool preserveCollinear)
1709
+ {
1710
+ bool wasTrimmed = false;
1711
+ Point64 pt = NextVertex(horzEdge)->pt;
1712
+ while (pt.y == horzEdge.top.y)
1713
+ {
1714
+ //always trim 180 deg. spikes (in closed paths)
1715
+ //but otherwise break if preserveCollinear = true
1716
+ if (preserveCollinear &&
1717
+ ((pt.x < horzEdge.top.x) != (horzEdge.bot.x < horzEdge.top.x)))
1718
+ break;
1719
+
1720
+ horzEdge.vertex_top = NextVertex(horzEdge);
1721
+ horzEdge.top = pt;
1722
+ wasTrimmed = true;
1723
+ if (IsMaxima(horzEdge)) break;
1724
+ pt = NextVertex(horzEdge)->pt;
1725
+ }
1726
+
1727
+ if (wasTrimmed) SetDx(horzEdge); // +/-infinity
1728
+ }
1729
+
1730
+
1731
+ inline void ClipperBase::UpdateEdgeIntoAEL(Active* e)
1732
+ {
1733
+ e->bot = e->top;
1734
+ e->vertex_top = NextVertex(*e);
1735
+ e->top = e->vertex_top->pt;
1736
+ e->curr_x = e->bot.x;
1737
+ SetDx(*e);
1738
+
1739
+ if (IsJoined(*e)) Split(*e, e->bot);
1740
+
1741
+ if (IsHorizontal(*e))
1742
+ {
1743
+ if (!IsOpen(*e)) TrimHorz(*e, preserve_collinear_);
1744
+ return;
1745
+ }
1746
+
1747
+ InsertScanline(e->top.y);
1748
+ CheckJoinLeft(*e, e->bot);
1749
+ CheckJoinRight(*e, e->bot, true); // (#500)
1750
+ }
1751
+
1752
+ Active* FindEdgeWithMatchingLocMin(Active* e)
1753
+ {
1754
+ Active* result = e->next_in_ael;
1755
+ while (result)
1756
+ {
1757
+ if (result->local_min == e->local_min) return result;
1758
+ else if (!IsHorizontal(*result) && e->bot != result->bot) result = nullptr;
1759
+ else result = result->next_in_ael;
1760
+ }
1761
+ result = e->prev_in_ael;
1762
+ while (result)
1763
+ {
1764
+ if (result->local_min == e->local_min) return result;
1765
+ else if (!IsHorizontal(*result) && e->bot != result->bot) return nullptr;
1766
+ else result = result->prev_in_ael;
1767
+ }
1768
+ return result;
1769
+ }
1770
+
1771
+
1772
+ void ClipperBase::IntersectEdges(Active& e1, Active& e2, const Point64& pt)
1773
+ {
1774
+ //MANAGE OPEN PATH INTERSECTIONS SEPARATELY ...
1775
+ if (has_open_paths_ && (IsOpen(e1) || IsOpen(e2)))
1776
+ {
1777
+ if (IsOpen(e1) && IsOpen(e2)) return;
1778
+ Active* edge_o, * edge_c;
1779
+ if (IsOpen(e1))
1780
+ {
1781
+ edge_o = &e1;
1782
+ edge_c = &e2;
1783
+ }
1784
+ else
1785
+ {
1786
+ edge_o = &e2;
1787
+ edge_c = &e1;
1788
+ }
1789
+ if (IsJoined(*edge_c)) Split(*edge_c, pt); // needed for safety
1790
+
1791
+ if (abs(edge_c->wind_cnt) != 1) return;
1792
+ switch (cliptype_)
1793
+ {
1794
+ case ClipType::Union:
1795
+ if (!IsHotEdge(*edge_c)) return;
1796
+ break;
1797
+ default:
1798
+ if (edge_c->local_min->polytype == PathType::Subject)
1799
+ return;
1800
+ }
1801
+
1802
+ switch (fillrule_)
1803
+ {
1804
+ case FillRule::Positive:
1805
+ if (edge_c->wind_cnt != 1) return;
1806
+ break;
1807
+ case FillRule::Negative:
1808
+ if (edge_c->wind_cnt != -1) return;
1809
+ break;
1810
+ default:
1811
+ if (std::abs(edge_c->wind_cnt) != 1) return;
1812
+ }
1813
+
1814
+ #ifdef USINGZ
1815
+ OutPt* resultOp;
1816
+ #endif
1817
+ //toggle contribution ...
1818
+ if (IsHotEdge(*edge_o))
1819
+ {
1820
+ #ifdef USINGZ
1821
+ resultOp = AddOutPt(*edge_o, pt);
1822
+ #else
1823
+ AddOutPt(*edge_o, pt);
1824
+ #endif
1825
+ if (IsFront(*edge_o)) edge_o->outrec->front_edge = nullptr;
1826
+ else edge_o->outrec->back_edge = nullptr;
1827
+ edge_o->outrec = nullptr;
1828
+ }
1829
+
1830
+ //horizontal edges can pass under open paths at a LocMins
1831
+ else if (pt == edge_o->local_min->vertex->pt &&
1832
+ !IsOpenEnd(*edge_o->local_min->vertex))
1833
+ {
1834
+ //find the other side of the LocMin and
1835
+ //if it's 'hot' join up with it ...
1836
+ Active* e3 = FindEdgeWithMatchingLocMin(edge_o);
1837
+ if (e3 && IsHotEdge(*e3))
1838
+ {
1839
+ edge_o->outrec = e3->outrec;
1840
+ if (edge_o->wind_dx > 0)
1841
+ SetSides(*e3->outrec, *edge_o, *e3);
1842
+ else
1843
+ SetSides(*e3->outrec, *e3, *edge_o);
1844
+ return;
1845
+ }
1846
+ else
1847
+ #ifdef USINGZ
1848
+ resultOp = StartOpenPath(*edge_o, pt);
1849
+ #else
1850
+ StartOpenPath(*edge_o, pt);
1851
+ #endif
1852
+ }
1853
+ else
1854
+ #ifdef USINGZ
1855
+ resultOp = StartOpenPath(*edge_o, pt);
1856
+ #else
1857
+ StartOpenPath(*edge_o, pt);
1858
+ #endif
1859
+
1860
+ #ifdef USINGZ
1861
+ if (zCallback_) SetZ(*edge_o, *edge_c, resultOp->pt);
1862
+ #endif
1863
+ return;
1864
+ } // end of an open path intersection
1865
+
1866
+ //MANAGING CLOSED PATHS FROM HERE ON
1867
+
1868
+ if (IsJoined(e1)) Split(e1, pt);
1869
+ if (IsJoined(e2)) Split(e2, pt);
1870
+
1871
+ //UPDATE WINDING COUNTS...
1872
+
1873
+ int old_e1_windcnt, old_e2_windcnt;
1874
+ if (e1.local_min->polytype == e2.local_min->polytype)
1875
+ {
1876
+ if (fillrule_ == FillRule::EvenOdd)
1877
+ {
1878
+ old_e1_windcnt = e1.wind_cnt;
1879
+ e1.wind_cnt = e2.wind_cnt;
1880
+ e2.wind_cnt = old_e1_windcnt;
1881
+ }
1882
+ else
1883
+ {
1884
+ if (e1.wind_cnt + e2.wind_dx == 0)
1885
+ e1.wind_cnt = -e1.wind_cnt;
1886
+ else
1887
+ e1.wind_cnt += e2.wind_dx;
1888
+ if (e2.wind_cnt - e1.wind_dx == 0)
1889
+ e2.wind_cnt = -e2.wind_cnt;
1890
+ else
1891
+ e2.wind_cnt -= e1.wind_dx;
1892
+ }
1893
+ }
1894
+ else
1895
+ {
1896
+ if (fillrule_ != FillRule::EvenOdd)
1897
+ {
1898
+ e1.wind_cnt2 += e2.wind_dx;
1899
+ e2.wind_cnt2 -= e1.wind_dx;
1900
+ }
1901
+ else
1902
+ {
1903
+ e1.wind_cnt2 = (e1.wind_cnt2 == 0 ? 1 : 0);
1904
+ e2.wind_cnt2 = (e2.wind_cnt2 == 0 ? 1 : 0);
1905
+ }
1906
+ }
1907
+
1908
+ switch (fillrule_)
1909
+ {
1910
+ case FillRule::EvenOdd:
1911
+ case FillRule::NonZero:
1912
+ old_e1_windcnt = abs(e1.wind_cnt);
1913
+ old_e2_windcnt = abs(e2.wind_cnt);
1914
+ break;
1915
+ default:
1916
+ if (fillrule_ == fillpos)
1917
+ {
1918
+ old_e1_windcnt = e1.wind_cnt;
1919
+ old_e2_windcnt = e2.wind_cnt;
1920
+ }
1921
+ else
1922
+ {
1923
+ old_e1_windcnt = -e1.wind_cnt;
1924
+ old_e2_windcnt = -e2.wind_cnt;
1925
+ }
1926
+ break;
1927
+ }
1928
+
1929
+ const bool e1_windcnt_in_01 = old_e1_windcnt == 0 || old_e1_windcnt == 1;
1930
+ const bool e2_windcnt_in_01 = old_e2_windcnt == 0 || old_e2_windcnt == 1;
1931
+
1932
+ if ((!IsHotEdge(e1) && !e1_windcnt_in_01) ||
1933
+ (!IsHotEdge(e2) && !e2_windcnt_in_01))
1934
+ return;
1935
+
1936
+ //NOW PROCESS THE INTERSECTION ...
1937
+ #ifdef USINGZ
1938
+ OutPt* resultOp = nullptr;
1939
+ #endif
1940
+ //if both edges are 'hot' ...
1941
+ if (IsHotEdge(e1) && IsHotEdge(e2))
1942
+ {
1943
+ if ((old_e1_windcnt != 0 && old_e1_windcnt != 1) || (old_e2_windcnt != 0 && old_e2_windcnt != 1) ||
1944
+ (e1.local_min->polytype != e2.local_min->polytype && cliptype_ != ClipType::Xor))
1945
+ {
1946
+ #ifdef USINGZ
1947
+ resultOp = AddLocalMaxPoly(e1, e2, pt);
1948
+ if (zCallback_ && resultOp) SetZ(e1, e2, resultOp->pt);
1949
+ #else
1950
+ AddLocalMaxPoly(e1, e2, pt);
1951
+ #endif
1952
+ }
1953
+ else if (IsFront(e1) || (e1.outrec == e2.outrec))
1954
+ {
1955
+ //this 'else if' condition isn't strictly needed but
1956
+ //it's sensible to split polygons that only touch at
1957
+ //a common vertex (not at common edges).
1958
+
1959
+ #ifdef USINGZ
1960
+ resultOp = AddLocalMaxPoly(e1, e2, pt);
1961
+ OutPt* op2 = AddLocalMinPoly(e1, e2, pt);
1962
+ if (zCallback_ && resultOp) SetZ(e1, e2, resultOp->pt);
1963
+ if (zCallback_) SetZ(e1, e2, op2->pt);
1964
+ #else
1965
+ AddLocalMaxPoly(e1, e2, pt);
1966
+ AddLocalMinPoly(e1, e2, pt);
1967
+ #endif
1968
+ }
1969
+ else
1970
+ {
1971
+ #ifdef USINGZ
1972
+ resultOp = AddOutPt(e1, pt);
1973
+ OutPt* op2 = AddOutPt(e2, pt);
1974
+ if (zCallback_)
1975
+ {
1976
+ SetZ(e1, e2, resultOp->pt);
1977
+ SetZ(e1, e2, op2->pt);
1978
+ }
1979
+ #else
1980
+ AddOutPt(e1, pt);
1981
+ AddOutPt(e2, pt);
1982
+ #endif
1983
+ SwapOutrecs(e1, e2);
1984
+ }
1985
+ }
1986
+ else if (IsHotEdge(e1))
1987
+ {
1988
+ #ifdef USINGZ
1989
+ resultOp = AddOutPt(e1, pt);
1990
+ if (zCallback_) SetZ(e1, e2, resultOp->pt);
1991
+ #else
1992
+ AddOutPt(e1, pt);
1993
+ #endif
1994
+ SwapOutrecs(e1, e2);
1995
+ }
1996
+ else if (IsHotEdge(e2))
1997
+ {
1998
+ #ifdef USINGZ
1999
+ resultOp = AddOutPt(e2, pt);
2000
+ if (zCallback_) SetZ(e1, e2, resultOp->pt);
2001
+ #else
2002
+ AddOutPt(e2, pt);
2003
+ #endif
2004
+ SwapOutrecs(e1, e2);
2005
+ }
2006
+ else
2007
+ {
2008
+ int64_t e1Wc2, e2Wc2;
2009
+ switch (fillrule_)
2010
+ {
2011
+ case FillRule::EvenOdd:
2012
+ case FillRule::NonZero:
2013
+ e1Wc2 = abs(e1.wind_cnt2);
2014
+ e2Wc2 = abs(e2.wind_cnt2);
2015
+ break;
2016
+ default:
2017
+ if (fillrule_ == fillpos)
2018
+ {
2019
+ e1Wc2 = e1.wind_cnt2;
2020
+ e2Wc2 = e2.wind_cnt2;
2021
+ }
2022
+ else
2023
+ {
2024
+ e1Wc2 = -e1.wind_cnt2;
2025
+ e2Wc2 = -e2.wind_cnt2;
2026
+ }
2027
+ break;
2028
+ }
2029
+
2030
+ if (!IsSamePolyType(e1, e2))
2031
+ {
2032
+ #ifdef USINGZ
2033
+ resultOp = AddLocalMinPoly(e1, e2, pt, false);
2034
+ if (zCallback_) SetZ(e1, e2, resultOp->pt);
2035
+ #else
2036
+ AddLocalMinPoly(e1, e2, pt, false);
2037
+ #endif
2038
+ }
2039
+ else if (old_e1_windcnt == 1 && old_e2_windcnt == 1)
2040
+ {
2041
+ #ifdef USINGZ
2042
+ resultOp = nullptr;
2043
+ #endif
2044
+ switch (cliptype_)
2045
+ {
2046
+ case ClipType::Union:
2047
+ if (e1Wc2 <= 0 && e2Wc2 <= 0)
2048
+ #ifdef USINGZ
2049
+ resultOp = AddLocalMinPoly(e1, e2, pt, false);
2050
+ #else
2051
+ AddLocalMinPoly(e1, e2, pt, false);
2052
+ #endif
2053
+ break;
2054
+ case ClipType::Difference:
2055
+ if (((GetPolyType(e1) == PathType::Clip) && (e1Wc2 > 0) && (e2Wc2 > 0)) ||
2056
+ ((GetPolyType(e1) == PathType::Subject) && (e1Wc2 <= 0) && (e2Wc2 <= 0)))
2057
+ {
2058
+ #ifdef USINGZ
2059
+ resultOp = AddLocalMinPoly(e1, e2, pt, false);
2060
+ #else
2061
+ AddLocalMinPoly(e1, e2, pt, false);
2062
+ #endif
2063
+ }
2064
+ break;
2065
+ case ClipType::Xor:
2066
+ #ifdef USINGZ
2067
+ resultOp = AddLocalMinPoly(e1, e2, pt, false);
2068
+ #else
2069
+ AddLocalMinPoly(e1, e2, pt, false);
2070
+ #endif
2071
+ break;
2072
+ default:
2073
+ if (e1Wc2 > 0 && e2Wc2 > 0)
2074
+ #ifdef USINGZ
2075
+ resultOp = AddLocalMinPoly(e1, e2, pt, false);
2076
+ #else
2077
+ AddLocalMinPoly(e1, e2, pt, false);
2078
+ #endif
2079
+ break;
2080
+ }
2081
+ #ifdef USINGZ
2082
+ if (resultOp && zCallback_) SetZ(e1, e2, resultOp->pt);
2083
+ #endif
2084
+ }
2085
+ }
2086
+ }
2087
+
2088
+ inline void ClipperBase::DeleteFromAEL(Active& e)
2089
+ {
2090
+ Active* prev = e.prev_in_ael;
2091
+ Active* next = e.next_in_ael;
2092
+ if (!prev && !next && (&e != actives_)) return; // already deleted
2093
+ if (prev)
2094
+ prev->next_in_ael = next;
2095
+ else
2096
+ actives_ = next;
2097
+ if (next) next->prev_in_ael = prev;
2098
+ delete& e;
2099
+ }
2100
+
2101
+
2102
+ inline void ClipperBase::AdjustCurrXAndCopyToSEL(const int64_t top_y)
2103
+ {
2104
+ Active* e = actives_;
2105
+ sel_ = e;
2106
+ while (e)
2107
+ {
2108
+ e->prev_in_sel = e->prev_in_ael;
2109
+ e->next_in_sel = e->next_in_ael;
2110
+ e->jump = e->next_in_sel;
2111
+ // it is safe to ignore 'joined' edges here because
2112
+ // if necessary they will be split in IntersectEdges()
2113
+ e->curr_x = TopX(*e, top_y);
2114
+ e = e->next_in_ael;
2115
+ }
2116
+ }
2117
+
2118
+ bool ClipperBase::ExecuteInternal(ClipType ct, FillRule fillrule, bool use_polytrees)
2119
+ {
2120
+ cliptype_ = ct;
2121
+ fillrule_ = fillrule;
2122
+ using_polytree_ = use_polytrees;
2123
+ Reset();
2124
+ int64_t y;
2125
+ if (ct == ClipType::NoClip || !PopScanline(y)) return true;
2126
+
2127
+ while (succeeded_)
2128
+ {
2129
+ InsertLocalMinimaIntoAEL(y);
2130
+ Active* e;
2131
+ while (PopHorz(e)) DoHorizontal(*e);
2132
+ if (horz_seg_list_.size() > 0)
2133
+ {
2134
+ ConvertHorzSegsToJoins();
2135
+ horz_seg_list_.clear();
2136
+ }
2137
+ bot_y_ = y; // bot_y_ == bottom of scanbeam
2138
+ if (!PopScanline(y)) break; // y new top of scanbeam
2139
+ DoIntersections(y);
2140
+ DoTopOfScanbeam(y);
2141
+ while (PopHorz(e)) DoHorizontal(*e);
2142
+ }
2143
+ if (succeeded_) ProcessHorzJoins();
2144
+ return succeeded_;
2145
+ }
2146
+
2147
+ inline void FixOutRecPts(OutRec* outrec)
2148
+ {
2149
+ OutPt* op = outrec->pts;
2150
+ do {
2151
+ op->outrec = outrec;
2152
+ op = op->next;
2153
+ } while (op != outrec->pts);
2154
+ }
2155
+
2156
+ inline bool SetHorzSegHeadingForward(HorzSegment& hs, OutPt* opP, OutPt* opN)
2157
+ {
2158
+ if (opP->pt.x == opN->pt.x) return false;
2159
+ if (opP->pt.x < opN->pt.x)
2160
+ {
2161
+ hs.left_op = opP;
2162
+ hs.right_op = opN;
2163
+ hs.left_to_right = true;
2164
+ }
2165
+ else
2166
+ {
2167
+ hs.left_op = opN;
2168
+ hs.right_op = opP;
2169
+ hs.left_to_right = false;
2170
+ }
2171
+ return true;
2172
+ }
2173
+
2174
+ inline bool UpdateHorzSegment(HorzSegment& hs)
2175
+ {
2176
+ OutPt* op = hs.left_op;
2177
+ OutRec* outrec = GetRealOutRec(op->outrec);
2178
+ bool outrecHasEdges = outrec->front_edge;
2179
+ int64_t curr_y = op->pt.y;
2180
+ OutPt* opP = op, * opN = op;
2181
+ if (outrecHasEdges)
2182
+ {
2183
+ OutPt* opA = outrec->pts, * opZ = opA->next;
2184
+ while (opP != opZ && opP->prev->pt.y == curr_y)
2185
+ opP = opP->prev;
2186
+ while (opN != opA && opN->next->pt.y == curr_y)
2187
+ opN = opN->next;
2188
+ }
2189
+ else
2190
+ {
2191
+ while (opP->prev != opN && opP->prev->pt.y == curr_y)
2192
+ opP = opP->prev;
2193
+ while (opN->next != opP && opN->next->pt.y == curr_y)
2194
+ opN = opN->next;
2195
+ }
2196
+ bool result =
2197
+ SetHorzSegHeadingForward(hs, opP, opN) &&
2198
+ !hs.left_op->horz;
2199
+
2200
+ if (result)
2201
+ hs.left_op->horz = &hs;
2202
+ else
2203
+ hs.right_op = nullptr; // (for sorting)
2204
+ return result;
2205
+ }
2206
+
2207
+ void ClipperBase::ConvertHorzSegsToJoins()
2208
+ {
2209
+ auto j = std::count_if(horz_seg_list_.begin(),
2210
+ horz_seg_list_.end(),
2211
+ [](HorzSegment& hs) { return UpdateHorzSegment(hs); });
2212
+ if (j < 2) return;
2213
+
2214
+ std::stable_sort(horz_seg_list_.begin(), horz_seg_list_.end(), HorzSegSorter());
2215
+
2216
+ HorzSegmentList::iterator hs1 = horz_seg_list_.begin(), hs2;
2217
+ HorzSegmentList::iterator hs_end = hs1 +j;
2218
+ HorzSegmentList::iterator hs_end1 = hs_end - 1;
2219
+
2220
+ for (; hs1 != hs_end1; ++hs1)
2221
+ {
2222
+ for (hs2 = hs1 + 1; hs2 != hs_end; ++hs2)
2223
+ {
2224
+ if ((hs2->left_op->pt.x >= hs1->right_op->pt.x) ||
2225
+ (hs2->left_to_right == hs1->left_to_right) ||
2226
+ (hs2->right_op->pt.x <= hs1->left_op->pt.x)) continue;
2227
+ int64_t curr_y = hs1->left_op->pt.y;
2228
+ if (hs1->left_to_right)
2229
+ {
2230
+ while (hs1->left_op->next->pt.y == curr_y &&
2231
+ hs1->left_op->next->pt.x <= hs2->left_op->pt.x)
2232
+ hs1->left_op = hs1->left_op->next;
2233
+ while (hs2->left_op->prev->pt.y == curr_y &&
2234
+ hs2->left_op->prev->pt.x <= hs1->left_op->pt.x)
2235
+ hs2->left_op = hs2->left_op->prev;
2236
+ HorzJoin join = HorzJoin(
2237
+ DuplicateOp(hs1->left_op, true),
2238
+ DuplicateOp(hs2->left_op, false));
2239
+ horz_join_list_.emplace_back(join);
2240
+ }
2241
+ else
2242
+ {
2243
+ while (hs1->left_op->prev->pt.y == curr_y &&
2244
+ hs1->left_op->prev->pt.x <= hs2->left_op->pt.x)
2245
+ hs1->left_op = hs1->left_op->prev;
2246
+ while (hs2->left_op->next->pt.y == curr_y &&
2247
+ hs2->left_op->next->pt.x <= hs1->left_op->pt.x)
2248
+ hs2->left_op = hs2->left_op->next;
2249
+ HorzJoin join = HorzJoin(
2250
+ DuplicateOp(hs2->left_op, true),
2251
+ DuplicateOp(hs1->left_op, false));
2252
+ horz_join_list_.emplace_back(join);
2253
+ }
2254
+ }
2255
+ }
2256
+ }
2257
+
2258
+ void MoveSplits(OutRec* fromOr, OutRec* toOr)
2259
+ {
2260
+ if (!toOr->splits) toOr->splits = new OutRecList();
2261
+ OutRecList::iterator orIter = fromOr->splits->begin();
2262
+ for (; orIter != fromOr->splits->end(); ++orIter)
2263
+ if (toOr != *orIter) // #987
2264
+ toOr->splits->emplace_back(*orIter);
2265
+ fromOr->splits->clear();
2266
+ }
2267
+
2268
+ void ClipperBase::ProcessHorzJoins()
2269
+ {
2270
+ for (const HorzJoin& j : horz_join_list_)
2271
+ {
2272
+ OutRec* or1 = GetRealOutRec(j.op1->outrec);
2273
+ OutRec* or2 = GetRealOutRec(j.op2->outrec);
2274
+
2275
+ OutPt* op1b = j.op1->next;
2276
+ OutPt* op2b = j.op2->prev;
2277
+ j.op1->next = j.op2;
2278
+ j.op2->prev = j.op1;
2279
+ op1b->prev = op2b;
2280
+ op2b->next = op1b;
2281
+
2282
+ if (or1 == or2) // 'join' is really a split
2283
+ {
2284
+ or2 = NewOutRec();
2285
+ or2->pts = op1b;
2286
+ FixOutRecPts(or2);
2287
+
2288
+ //if or1->pts has moved to or2 then update or1->pts!!
2289
+ if (or1->pts->outrec == or2)
2290
+ {
2291
+ or1->pts = j.op1;
2292
+ or1->pts->outrec = or1;
2293
+ }
2294
+
2295
+ if (using_polytree_) //#498, #520, #584, D#576, #618
2296
+ {
2297
+ if (Path2ContainsPath1(or1->pts, or2->pts))
2298
+ {
2299
+ //swap or1's & or2's pts
2300
+ OutPt* tmp = or1->pts;
2301
+ or1->pts = or2->pts;
2302
+ or2->pts = tmp;
2303
+ FixOutRecPts(or1);
2304
+ FixOutRecPts(or2);
2305
+ //or2 is now inside or1
2306
+ or2->owner = or1;
2307
+ }
2308
+ else if (Path2ContainsPath1(or2->pts, or1->pts))
2309
+ {
2310
+ or2->owner = or1;
2311
+ }
2312
+ else
2313
+ or2->owner = or1->owner;
2314
+
2315
+ if (!or1->splits) or1->splits = new OutRecList();
2316
+ or1->splits->emplace_back(or2);
2317
+ }
2318
+ else
2319
+ or2->owner = or1;
2320
+ }
2321
+ else // joining, not splitting
2322
+ {
2323
+ or2->pts = nullptr;
2324
+ if (using_polytree_)
2325
+ {
2326
+ SetOwner(or2, or1);
2327
+ if (or2->splits)
2328
+ MoveSplits(or2, or1); //#618
2329
+ }
2330
+ else
2331
+ or2->owner = or1;
2332
+ }
2333
+ }
2334
+ }
2335
+
2336
+ void ClipperBase::DoIntersections(const int64_t top_y)
2337
+ {
2338
+ if (BuildIntersectList(top_y))
2339
+ {
2340
+ ProcessIntersectList();
2341
+ intersect_nodes_.clear();
2342
+ }
2343
+ }
2344
+
2345
+ void ClipperBase::AddNewIntersectNode(Active& e1, Active& e2, int64_t top_y)
2346
+ {
2347
+ Point64 ip;
2348
+ if (!GetLineIntersectPt(e1.bot, e1.top, e2.bot, e2.top, ip))
2349
+ ip = Point64(e1.curr_x, top_y); //parallel edges
2350
+
2351
+ //rounding errors can occasionally place the calculated intersection
2352
+ //point either below or above the scanbeam, so check and correct ...
2353
+ if (ip.y > bot_y_ || ip.y < top_y)
2354
+ {
2355
+ double abs_dx1 = std::fabs(e1.dx);
2356
+ double abs_dx2 = std::fabs(e2.dx);
2357
+ if (abs_dx1 > 100 && abs_dx2 > 100)
2358
+ {
2359
+ if (abs_dx1 > abs_dx2)
2360
+ ip = GetClosestPointOnSegment(ip, e1.bot, e1.top);
2361
+ else
2362
+ ip = GetClosestPointOnSegment(ip, e2.bot, e2.top);
2363
+ }
2364
+ else if (abs_dx1 > 100)
2365
+ ip = GetClosestPointOnSegment(ip, e1.bot, e1.top);
2366
+ else if (abs_dx2 > 100)
2367
+ ip = GetClosestPointOnSegment(ip, e2.bot, e2.top);
2368
+ else
2369
+ {
2370
+ if (ip.y < top_y) ip.y = top_y;
2371
+ else ip.y = bot_y_;
2372
+ if (abs_dx1 < abs_dx2) ip.x = TopX(e1, ip.y);
2373
+ else ip.x = TopX(e2, ip.y);
2374
+ }
2375
+ }
2376
+ intersect_nodes_.emplace_back(&e1, &e2, ip);
2377
+ }
2378
+
2379
+ bool ClipperBase::BuildIntersectList(const int64_t top_y)
2380
+ {
2381
+ if (!actives_ || !actives_->next_in_ael) return false;
2382
+
2383
+ //Calculate edge positions at the top of the current scanbeam, and from this
2384
+ //we will determine the intersections required to reach these new positions.
2385
+ AdjustCurrXAndCopyToSEL(top_y);
2386
+ //Find all edge intersections in the current scanbeam using a stable merge
2387
+ //sort that ensures only adjacent edges are intersecting. Intersect info is
2388
+ //stored in FIntersectList ready to be processed in ProcessIntersectList.
2389
+ //Re merge sorts see https://stackoverflow.com/a/46319131/359538
2390
+
2391
+ Active* left = sel_, * right, * l_end, * r_end, * curr_base, * tmp;
2392
+
2393
+ while (left && left->jump)
2394
+ {
2395
+ Active* prev_base = nullptr;
2396
+ while (left && left->jump)
2397
+ {
2398
+ curr_base = left;
2399
+ right = left->jump;
2400
+ l_end = right;
2401
+ r_end = right->jump;
2402
+ left->jump = r_end;
2403
+ while (left != l_end && right != r_end)
2404
+ {
2405
+ if (right->curr_x < left->curr_x)
2406
+ {
2407
+ tmp = right->prev_in_sel;
2408
+ for (; ; )
2409
+ {
2410
+ AddNewIntersectNode(*tmp, *right, top_y);
2411
+ if (tmp == left) break;
2412
+ tmp = tmp->prev_in_sel;
2413
+ }
2414
+
2415
+ tmp = right;
2416
+ right = ExtractFromSEL(tmp);
2417
+ l_end = right;
2418
+ Insert1Before2InSEL(tmp, left);
2419
+ if (left == curr_base)
2420
+ {
2421
+ curr_base = tmp;
2422
+ curr_base->jump = r_end;
2423
+ if (!prev_base) sel_ = curr_base;
2424
+ else prev_base->jump = curr_base;
2425
+ }
2426
+ }
2427
+ else left = left->next_in_sel;
2428
+ }
2429
+ prev_base = curr_base;
2430
+ left = r_end;
2431
+ }
2432
+ left = sel_;
2433
+ }
2434
+ return intersect_nodes_.size() > 0;
2435
+ }
2436
+
2437
+ void ClipperBase::ProcessIntersectList()
2438
+ {
2439
+ //We now have a list of intersections required so that edges will be
2440
+ //correctly positioned at the top of the scanbeam. However, it's important
2441
+ //that edge intersections are processed from the bottom up, but it's also
2442
+ //crucial that intersections only occur between adjacent edges.
2443
+
2444
+ //First we do a quicksort so intersections proceed in a bottom up order ...
2445
+ std::sort(intersect_nodes_.begin(), intersect_nodes_.end(), IntersectListSort);
2446
+ //Now as we process these intersections, we must sometimes adjust the order
2447
+ //to ensure that intersecting edges are always adjacent ...
2448
+
2449
+ IntersectNodeList::iterator node_iter, node_iter2;
2450
+ for (node_iter = intersect_nodes_.begin();
2451
+ node_iter != intersect_nodes_.end(); ++node_iter)
2452
+ {
2453
+ if (!EdgesAdjacentInAEL(*node_iter))
2454
+ {
2455
+ node_iter2 = node_iter + 1;
2456
+ while (!EdgesAdjacentInAEL(*node_iter2)) ++node_iter2;
2457
+ std::swap(*node_iter, *node_iter2);
2458
+ }
2459
+
2460
+ IntersectNode& node = *node_iter;
2461
+ IntersectEdges(*node.edge1, *node.edge2, node.pt);
2462
+ SwapPositionsInAEL(*node.edge1, *node.edge2);
2463
+
2464
+ node.edge1->curr_x = node.pt.x;
2465
+ node.edge2->curr_x = node.pt.x;
2466
+ CheckJoinLeft(*node.edge2, node.pt, true);
2467
+ CheckJoinRight(*node.edge1, node.pt, true);
2468
+ }
2469
+ }
2470
+
2471
+ void ClipperBase::SwapPositionsInAEL(Active& e1, Active& e2)
2472
+ {
2473
+ //preconditon: e1 must be immediately to the left of e2
2474
+ Active* next = e2.next_in_ael;
2475
+ if (next) next->prev_in_ael = &e1;
2476
+ Active* prev = e1.prev_in_ael;
2477
+ if (prev) prev->next_in_ael = &e2;
2478
+ e2.prev_in_ael = prev;
2479
+ e2.next_in_ael = &e1;
2480
+ e1.prev_in_ael = &e2;
2481
+ e1.next_in_ael = next;
2482
+ if (!e2.prev_in_ael) actives_ = &e2;
2483
+ }
2484
+
2485
+ inline OutPt* GetLastOp(const Active& hot_edge)
2486
+ {
2487
+ OutRec* outrec = hot_edge.outrec;
2488
+ OutPt* result = outrec->pts;
2489
+ if (&hot_edge != outrec->front_edge)
2490
+ result = result->next;
2491
+ return result;
2492
+ }
2493
+
2494
+ void ClipperBase::AddTrialHorzJoin(OutPt* op)
2495
+ {
2496
+ if (op->outrec->is_open) return;
2497
+ horz_seg_list_.emplace_back(op);
2498
+ }
2499
+
2500
+ bool ClipperBase::ResetHorzDirection(const Active& horz,
2501
+ const Vertex* max_vertex, int64_t& horz_left, int64_t& horz_right)
2502
+ {
2503
+ if (horz.bot.x == horz.top.x)
2504
+ {
2505
+ //the horizontal edge is going nowhere ...
2506
+ horz_left = horz.curr_x;
2507
+ horz_right = horz.curr_x;
2508
+ Active* e = horz.next_in_ael;
2509
+ while (e && e->vertex_top != max_vertex) e = e->next_in_ael;
2510
+ return e != nullptr;
2511
+ }
2512
+ else if (horz.curr_x < horz.top.x)
2513
+ {
2514
+ horz_left = horz.curr_x;
2515
+ horz_right = horz.top.x;
2516
+ return true;
2517
+ }
2518
+ else
2519
+ {
2520
+ horz_left = horz.top.x;
2521
+ horz_right = horz.curr_x;
2522
+ return false; // right to left
2523
+ }
2524
+ }
2525
+
2526
+ void ClipperBase::DoHorizontal(Active& horz)
2527
+ /*******************************************************************************
2528
+ * Notes: Horizontal edges (HEs) at scanline intersections (ie at the top or *
2529
+ * bottom of a scanbeam) are processed as if layered.The order in which HEs *
2530
+ * are processed doesn't matter. HEs intersect with the bottom vertices of *
2531
+ * other HEs[#] and with non-horizontal edges [*]. Once these intersections *
2532
+ * are completed, intermediate HEs are 'promoted' to the next edge in their *
2533
+ * bounds, and they in turn may be intersected[%] by other HEs. *
2534
+ * *
2535
+ * eg: 3 horizontals at a scanline: / | / / *
2536
+ * | / | (HE3)o ========%========== o *
2537
+ * o ======= o(HE2) / | / / *
2538
+ * o ============#=========*======*========#=========o (HE1) *
2539
+ * / | / | / *
2540
+ *******************************************************************************/
2541
+ {
2542
+ Point64 pt;
2543
+ bool horzIsOpen = IsOpen(horz);
2544
+ int64_t y = horz.bot.y;
2545
+ Vertex* vertex_max;
2546
+ if (horzIsOpen)
2547
+ vertex_max = GetCurrYMaximaVertex_Open(horz);
2548
+ else
2549
+ vertex_max = GetCurrYMaximaVertex(horz);
2550
+
2551
+ //// remove 180 deg.spikes and also simplify
2552
+ //// consecutive horizontals when PreserveCollinear = true
2553
+ //if (!horzIsOpen && vertex_max != horz.vertex_top)
2554
+ // TrimHorz(horz, PreserveCollinear);
2555
+
2556
+ int64_t horz_left, horz_right;
2557
+ bool is_left_to_right =
2558
+ ResetHorzDirection(horz, vertex_max, horz_left, horz_right);
2559
+
2560
+ if (IsHotEdge(horz))
2561
+ {
2562
+ #ifdef USINGZ
2563
+ OutPt* op = AddOutPt(horz, Point64(horz.curr_x, y, horz.bot.z));
2564
+ #else
2565
+ OutPt* op = AddOutPt(horz, Point64(horz.curr_x, y));
2566
+ #endif
2567
+ AddTrialHorzJoin(op);
2568
+ }
2569
+
2570
+ while (true) // loop through consec. horizontal edges
2571
+ {
2572
+ Active* e;
2573
+ if (is_left_to_right) e = horz.next_in_ael;
2574
+ else e = horz.prev_in_ael;
2575
+
2576
+ while (e)
2577
+ {
2578
+ if (e->vertex_top == vertex_max)
2579
+ {
2580
+ if (IsHotEdge(horz) && IsJoined(*e))
2581
+ Split(*e, e->top);
2582
+
2583
+ //if (IsHotEdge(horz) != IsHotEdge(*e))
2584
+ // DoError(undefined_error_i);
2585
+
2586
+ if (IsHotEdge(horz))
2587
+ {
2588
+ while (horz.vertex_top != vertex_max)
2589
+ {
2590
+ AddOutPt(horz, horz.top);
2591
+ UpdateEdgeIntoAEL(&horz);
2592
+ }
2593
+ if (is_left_to_right)
2594
+ AddLocalMaxPoly(horz, *e, horz.top);
2595
+ else
2596
+ AddLocalMaxPoly(*e, horz, horz.top);
2597
+ }
2598
+ DeleteFromAEL(*e);
2599
+ DeleteFromAEL(horz);
2600
+ return;
2601
+ }
2602
+
2603
+ //if horzEdge is a maxima, keep going until we reach
2604
+ //its maxima pair, otherwise check for break conditions
2605
+ if (vertex_max != horz.vertex_top || IsOpenEnd(horz))
2606
+ {
2607
+ //otherwise stop when 'ae' is beyond the end of the horizontal line
2608
+ if ((is_left_to_right && e->curr_x > horz_right) ||
2609
+ (!is_left_to_right && e->curr_x < horz_left)) break;
2610
+
2611
+ if (e->curr_x == horz.top.x && !IsHorizontal(*e))
2612
+ {
2613
+ pt = NextVertex(horz)->pt;
2614
+ if (is_left_to_right)
2615
+ {
2616
+ //with open paths we'll only break once past horz's end
2617
+ if (IsOpen(*e) && !IsSamePolyType(*e, horz) && !IsHotEdge(*e))
2618
+ {
2619
+ if (TopX(*e, pt.y) > pt.x) break;
2620
+ }
2621
+ //otherwise we'll only break when horz's outslope is greater than e's
2622
+ else if (TopX(*e, pt.y) >= pt.x) break;
2623
+ }
2624
+ else
2625
+ {
2626
+ if (IsOpen(*e) && !IsSamePolyType(*e, horz) && !IsHotEdge(*e))
2627
+ {
2628
+ if (TopX(*e, pt.y) < pt.x) break;
2629
+ }
2630
+ else if (TopX(*e, pt.y) <= pt.x) break;
2631
+ }
2632
+ }
2633
+ }
2634
+
2635
+ pt = Point64(e->curr_x, horz.bot.y);
2636
+ if (is_left_to_right)
2637
+ {
2638
+ IntersectEdges(horz, *e, pt);
2639
+ SwapPositionsInAEL(horz, *e);
2640
+ CheckJoinLeft(*e, pt);
2641
+ horz.curr_x = e->curr_x;
2642
+ e = horz.next_in_ael;
2643
+ }
2644
+ else
2645
+ {
2646
+ IntersectEdges(*e, horz, pt);
2647
+ SwapPositionsInAEL(*e, horz);
2648
+ CheckJoinRight(*e, pt);
2649
+ horz.curr_x = e->curr_x;
2650
+ e = horz.prev_in_ael;
2651
+ }
2652
+
2653
+ if (horz.outrec)
2654
+ {
2655
+ //nb: The outrec containing the op returned by IntersectEdges
2656
+ //above may no longer be associated with horzEdge.
2657
+ AddTrialHorzJoin(GetLastOp(horz));
2658
+ }
2659
+ }
2660
+
2661
+ //check if we've finished with (consecutive) horizontals ...
2662
+ if (horzIsOpen && IsOpenEnd(horz)) // ie open at top
2663
+ {
2664
+ if (IsHotEdge(horz))
2665
+ {
2666
+ AddOutPt(horz, horz.top);
2667
+ if (IsFront(horz))
2668
+ horz.outrec->front_edge = nullptr;
2669
+ else
2670
+ horz.outrec->back_edge = nullptr;
2671
+ horz.outrec = nullptr;
2672
+ }
2673
+ DeleteFromAEL(horz);
2674
+ return;
2675
+ }
2676
+ else if (NextVertex(horz)->pt.y != horz.top.y)
2677
+ break;
2678
+
2679
+ //still more horizontals in bound to process ...
2680
+ if (IsHotEdge(horz))
2681
+ AddOutPt(horz, horz.top);
2682
+ UpdateEdgeIntoAEL(&horz);
2683
+
2684
+ is_left_to_right =
2685
+ ResetHorzDirection(horz, vertex_max, horz_left, horz_right);
2686
+ }
2687
+
2688
+ if (IsHotEdge(horz))
2689
+ {
2690
+ OutPt* op = AddOutPt(horz, horz.top);
2691
+ AddTrialHorzJoin(op);
2692
+ }
2693
+
2694
+ UpdateEdgeIntoAEL(&horz); // end of an intermediate horiz.
2695
+ }
2696
+
2697
+ void ClipperBase::DoTopOfScanbeam(const int64_t y)
2698
+ {
2699
+ sel_ = nullptr; // sel_ is reused to flag horizontals (see PushHorz below)
2700
+ Active* e = actives_;
2701
+ while (e)
2702
+ {
2703
+ //nb: 'e' will never be horizontal here
2704
+ if (e->top.y == y)
2705
+ {
2706
+ e->curr_x = e->top.x;
2707
+ if (IsMaxima(*e))
2708
+ {
2709
+ e = DoMaxima(*e); // TOP OF BOUND (MAXIMA)
2710
+ continue;
2711
+ }
2712
+ else
2713
+ {
2714
+ //INTERMEDIATE VERTEX ...
2715
+ if (IsHotEdge(*e)) AddOutPt(*e, e->top);
2716
+ UpdateEdgeIntoAEL(e);
2717
+ if (IsHorizontal(*e))
2718
+ PushHorz(*e); // horizontals are processed later
2719
+ }
2720
+ }
2721
+ else // i.e. not the top of the edge
2722
+ e->curr_x = TopX(*e, y);
2723
+
2724
+ e = e->next_in_ael;
2725
+ }
2726
+ }
2727
+
2728
+
2729
+ Active* ClipperBase::DoMaxima(Active& e)
2730
+ {
2731
+ Active* next_e, * prev_e, * max_pair;
2732
+ prev_e = e.prev_in_ael;
2733
+ next_e = e.next_in_ael;
2734
+ if (IsOpenEnd(e))
2735
+ {
2736
+ if (IsHotEdge(e)) AddOutPt(e, e.top);
2737
+ if (!IsHorizontal(e))
2738
+ {
2739
+ if (IsHotEdge(e))
2740
+ {
2741
+ if (IsFront(e))
2742
+ e.outrec->front_edge = nullptr;
2743
+ else
2744
+ e.outrec->back_edge = nullptr;
2745
+ e.outrec = nullptr;
2746
+ }
2747
+ DeleteFromAEL(e);
2748
+ }
2749
+ return next_e;
2750
+ }
2751
+
2752
+ max_pair = GetMaximaPair(e);
2753
+ if (!max_pair) return next_e; // eMaxPair is horizontal
2754
+
2755
+ if (IsJoined(e)) Split(e, e.top);
2756
+ if (IsJoined(*max_pair)) Split(*max_pair, max_pair->top);
2757
+
2758
+ //only non-horizontal maxima here.
2759
+ //process any edges between maxima pair ...
2760
+ while (next_e != max_pair)
2761
+ {
2762
+ IntersectEdges(e, *next_e, e.top);
2763
+ SwapPositionsInAEL(e, *next_e);
2764
+ next_e = e.next_in_ael;
2765
+ }
2766
+
2767
+ if (IsOpen(e))
2768
+ {
2769
+ if (IsHotEdge(e))
2770
+ AddLocalMaxPoly(e, *max_pair, e.top);
2771
+ DeleteFromAEL(*max_pair);
2772
+ DeleteFromAEL(e);
2773
+ return (prev_e ? prev_e->next_in_ael : actives_);
2774
+ }
2775
+
2776
+ // e.next_in_ael== max_pair ...
2777
+ if (IsHotEdge(e))
2778
+ AddLocalMaxPoly(e, *max_pair, e.top);
2779
+
2780
+ DeleteFromAEL(e);
2781
+ DeleteFromAEL(*max_pair);
2782
+ return (prev_e ? prev_e->next_in_ael : actives_);
2783
+ }
2784
+
2785
+ void ClipperBase::Split(Active& e, const Point64& pt)
2786
+ {
2787
+ if (e.join_with == JoinWith::Right)
2788
+ {
2789
+ e.join_with = JoinWith::NoJoin;
2790
+ e.next_in_ael->join_with = JoinWith::NoJoin;
2791
+ AddLocalMinPoly(e, *e.next_in_ael, pt, true);
2792
+ }
2793
+ else
2794
+ {
2795
+ e.join_with = JoinWith::NoJoin;
2796
+ e.prev_in_ael->join_with = JoinWith::NoJoin;
2797
+ AddLocalMinPoly(*e.prev_in_ael, e, pt, true);
2798
+ }
2799
+ }
2800
+
2801
+ void ClipperBase::CheckJoinLeft(Active& e,
2802
+ const Point64& pt, bool check_curr_x)
2803
+ {
2804
+ Active* prev = e.prev_in_ael;
2805
+ if (!prev ||
2806
+ !IsHotEdge(e) || !IsHotEdge(*prev) ||
2807
+ IsHorizontal(e) || IsHorizontal(*prev) ||
2808
+ IsOpen(e) || IsOpen(*prev) ) return;
2809
+ if ((pt.y < e.top.y + 2 || pt.y < prev->top.y + 2) &&
2810
+ ((e.bot.y > pt.y) || (prev->bot.y > pt.y))) return; // avoid trivial joins
2811
+
2812
+ if (check_curr_x)
2813
+ {
2814
+ if (PerpendicDistFromLineSqrd(pt, prev->bot, prev->top) > 0.25) return;
2815
+ }
2816
+ else if (e.curr_x != prev->curr_x) return;
2817
+ if (!IsCollinear(e.top, pt, prev->top)) return;
2818
+
2819
+ if (e.outrec->idx == prev->outrec->idx)
2820
+ AddLocalMaxPoly(*prev, e, pt);
2821
+ else if (e.outrec->idx < prev->outrec->idx)
2822
+ JoinOutrecPaths(e, *prev);
2823
+ else
2824
+ JoinOutrecPaths(*prev, e);
2825
+ prev->join_with = JoinWith::Right;
2826
+ e.join_with = JoinWith::Left;
2827
+ }
2828
+
2829
+ void ClipperBase::CheckJoinRight(Active& e,
2830
+ const Point64& pt, bool check_curr_x)
2831
+ {
2832
+ Active* next = e.next_in_ael;
2833
+ if (!next ||
2834
+ !IsHotEdge(e) || !IsHotEdge(*next) ||
2835
+ IsHorizontal(e) || IsHorizontal(*next) ||
2836
+ IsOpen(e) || IsOpen(*next)) return;
2837
+ if ((pt.y < e.top.y +2 || pt.y < next->top.y +2) &&
2838
+ ((e.bot.y > pt.y) || (next->bot.y > pt.y))) return; // avoid trivial joins
2839
+
2840
+ if (check_curr_x)
2841
+ {
2842
+ if (PerpendicDistFromLineSqrd(pt, next->bot, next->top) > 0.35) return;
2843
+ }
2844
+ else if (e.curr_x != next->curr_x) return;
2845
+ if (!IsCollinear(e.top, pt, next->top)) return;
2846
+
2847
+ if (e.outrec->idx == next->outrec->idx)
2848
+ AddLocalMaxPoly(e, *next, pt);
2849
+ else if (e.outrec->idx < next->outrec->idx)
2850
+ JoinOutrecPaths(e, *next);
2851
+ else
2852
+ JoinOutrecPaths(*next, e);
2853
+
2854
+ e.join_with = JoinWith::Right;
2855
+ next->join_with = JoinWith::Left;
2856
+ }
2857
+
2858
+ inline bool GetHorzExtendedHorzSeg(OutPt*& op, OutPt*& op2)
2859
+ {
2860
+ OutRec* outrec = GetRealOutRec(op->outrec);
2861
+ op2 = op;
2862
+ if (outrec->front_edge)
2863
+ {
2864
+ while (op->prev != outrec->pts &&
2865
+ op->prev->pt.y == op->pt.y) op = op->prev;
2866
+ while (op2 != outrec->pts &&
2867
+ op2->next->pt.y == op2->pt.y) op2 = op2->next;
2868
+ return op2 != op;
2869
+ }
2870
+ else
2871
+ {
2872
+ while (op->prev != op2 && op->prev->pt.y == op->pt.y)
2873
+ op = op->prev;
2874
+ while (op2->next != op && op2->next->pt.y == op2->pt.y)
2875
+ op2 = op2->next;
2876
+ return op2 != op && op2->next != op;
2877
+ }
2878
+ }
2879
+
2880
+ bool BuildPath64(OutPt* op, bool reverse, bool isOpen, Path64& path)
2881
+ {
2882
+ if (!op || op->next == op || (!isOpen && op->next == op->prev))
2883
+ return false;
2884
+
2885
+ path.resize(0);
2886
+ Point64 lastPt;
2887
+ OutPt* op2;
2888
+ if (reverse)
2889
+ {
2890
+ lastPt = op->pt;
2891
+ op2 = op->prev;
2892
+ }
2893
+ else
2894
+ {
2895
+ op = op->next;
2896
+ lastPt = op->pt;
2897
+ op2 = op->next;
2898
+ }
2899
+ path.emplace_back(lastPt);
2900
+
2901
+ while (op2 != op)
2902
+ {
2903
+ if (op2->pt != lastPt)
2904
+ {
2905
+ lastPt = op2->pt;
2906
+ path.emplace_back(lastPt);
2907
+ }
2908
+ if (reverse)
2909
+ op2 = op2->prev;
2910
+ else
2911
+ op2 = op2->next;
2912
+ }
2913
+
2914
+ if (!isOpen && path.size() == 3 && IsVerySmallTriangle(*op2)) return false;
2915
+ else return true;
2916
+ }
2917
+
2918
+ bool ClipperBase::CheckBounds(OutRec* outrec)
2919
+ {
2920
+ if (!outrec->pts) return false;
2921
+ if (!outrec->bounds.IsEmpty()) return true;
2922
+ CleanCollinear(outrec);
2923
+ if (!outrec->pts ||
2924
+ !BuildPath64(outrec->pts, reverse_solution_, false, outrec->path)){
2925
+ return false;}
2926
+ outrec->bounds = GetBounds(outrec->path);
2927
+ return true;
2928
+ }
2929
+
2930
+ bool ClipperBase::CheckSplitOwner(OutRec* outrec, OutRecList* splits)
2931
+ {
2932
+ // nb: use indexing (not an iterator) in case 'splits' is modified inside this loop (#1029)
2933
+ for (size_t idx = 0; idx < splits->size(); ++idx)
2934
+ {
2935
+ OutRec* split = (*splits)[idx];
2936
+ if (!split->pts && split->splits &&
2937
+ CheckSplitOwner(outrec, split->splits)) return true; //#942
2938
+ split = GetRealOutRec(split);
2939
+ if (!split || split == outrec || split->recursive_split == outrec) continue;
2940
+ split->recursive_split = outrec; // prevent infinite loops
2941
+
2942
+ if (split->splits && CheckSplitOwner(outrec, split->splits))
2943
+ return true;
2944
+
2945
+ if (!CheckBounds(split) || !split->bounds.Contains(outrec->bounds) ||
2946
+ !Path2ContainsPath1(outrec->pts, split->pts)) continue;
2947
+
2948
+ if (!IsValidOwner(outrec, split)) // split is owned by outrec! (#957)
2949
+ split->owner = outrec->owner;
2950
+
2951
+ outrec->owner = split;
2952
+ return true;
2953
+
2954
+ }
2955
+ return false;
2956
+ }
2957
+
2958
+ void ClipperBase::RecursiveCheckOwners(OutRec* outrec, PolyPath* polypath)
2959
+ {
2960
+ // pre-condition: outrec will have valid bounds
2961
+ // post-condition: if a valid path, outrec will have a polypath
2962
+
2963
+ if (outrec->polypath || outrec->bounds.IsEmpty()) return;
2964
+ while (outrec->owner)
2965
+ {
2966
+ if (outrec->owner->splits && CheckSplitOwner(outrec, outrec->owner->splits)) break;
2967
+ if (outrec->owner->pts && CheckBounds(outrec->owner) &&
2968
+ outrec->owner->bounds.Contains(outrec->bounds) &&
2969
+ Path2ContainsPath1(outrec->pts, outrec->owner->pts)) break;
2970
+ outrec->owner = outrec->owner->owner;
2971
+ }
2972
+
2973
+ if (outrec->owner)
2974
+ {
2975
+ if (!outrec->owner->polypath)
2976
+ RecursiveCheckOwners(outrec->owner, polypath);
2977
+ outrec->polypath = outrec->owner->polypath->AddChild(outrec->path);
2978
+ }
2979
+ else
2980
+ outrec->polypath = polypath->AddChild(outrec->path);
2981
+ }
2982
+
2983
+ void Clipper64::BuildPaths64(Paths64& solutionClosed, Paths64* solutionOpen)
2984
+ {
2985
+ solutionClosed.resize(0);
2986
+ solutionClosed.reserve(outrec_list_.size());
2987
+ if (solutionOpen)
2988
+ {
2989
+ solutionOpen->resize(0);
2990
+ solutionOpen->reserve(outrec_list_.size());
2991
+ }
2992
+
2993
+ // nb: outrec_list_.size() may change in the following
2994
+ // while loop because polygons may be split during
2995
+ // calls to CleanCollinear which calls FixSelfIntersects
2996
+ for (size_t i = 0; i < outrec_list_.size(); ++i)
2997
+ {
2998
+ OutRec* outrec = outrec_list_[i];
2999
+ if (outrec->pts == nullptr) continue;
3000
+
3001
+ Path64 path;
3002
+ if (solutionOpen && outrec->is_open)
3003
+ {
3004
+ if (BuildPath64(outrec->pts, reverse_solution_, true, path))
3005
+ solutionOpen->emplace_back(std::move(path));
3006
+ }
3007
+ else
3008
+ {
3009
+ // nb: CleanCollinear can add to outrec_list_
3010
+ CleanCollinear(outrec);
3011
+ //closed paths should always return a Positive orientation
3012
+ if (BuildPath64(outrec->pts, reverse_solution_, false, path))
3013
+ solutionClosed.emplace_back(std::move(path));
3014
+ }
3015
+ }
3016
+ }
3017
+
3018
+ void Clipper64::BuildTree64(PolyPath64& polytree, Paths64& open_paths)
3019
+ {
3020
+ polytree.Clear();
3021
+ open_paths.resize(0);
3022
+ if (has_open_paths_)
3023
+ open_paths.reserve(outrec_list_.size());
3024
+
3025
+ // outrec_list_.size() is not static here because
3026
+ // CheckBounds below can indirectly add additional
3027
+ // OutRec (via FixOutRecPts & CleanCollinear)
3028
+ for (size_t i = 0; i < outrec_list_.size(); ++i)
3029
+ {
3030
+ OutRec* outrec = outrec_list_[i];
3031
+ if (!outrec || !outrec->pts) continue;
3032
+
3033
+ if (outrec->is_open)
3034
+ {
3035
+ Path64 path;
3036
+ if (BuildPath64(outrec->pts, reverse_solution_, true, path))
3037
+ open_paths.emplace_back(std::move(path));
3038
+ continue;
3039
+ }
3040
+
3041
+ if (CheckBounds(outrec))
3042
+ RecursiveCheckOwners(outrec, &polytree);
3043
+ }
3044
+ }
3045
+
3046
+ bool BuildPathD(OutPt* op, bool reverse, bool isOpen, PathD& path, double inv_scale)
3047
+ {
3048
+ if (!op || op->next == op || (!isOpen && op->next == op->prev))
3049
+ return false;
3050
+
3051
+ path.resize(0);
3052
+ Point64 lastPt;
3053
+ OutPt* op2;
3054
+ if (reverse)
3055
+ {
3056
+ lastPt = op->pt;
3057
+ op2 = op->prev;
3058
+ }
3059
+ else
3060
+ {
3061
+ op = op->next;
3062
+ lastPt = op->pt;
3063
+ op2 = op->next;
3064
+ }
3065
+ #ifdef USINGZ
3066
+ path.emplace_back(lastPt.x * inv_scale, lastPt.y * inv_scale, lastPt.z);
3067
+ #else
3068
+ path.emplace_back(lastPt.x * inv_scale, lastPt.y * inv_scale);
3069
+ #endif
3070
+
3071
+ while (op2 != op)
3072
+ {
3073
+ if (op2->pt != lastPt)
3074
+ {
3075
+ lastPt = op2->pt;
3076
+ #ifdef USINGZ
3077
+ path.emplace_back(lastPt.x * inv_scale, lastPt.y * inv_scale, lastPt.z);
3078
+ #else
3079
+ path.emplace_back(lastPt.x * inv_scale, lastPt.y * inv_scale);
3080
+ #endif
3081
+
3082
+ }
3083
+ if (reverse)
3084
+ op2 = op2->prev;
3085
+ else
3086
+ op2 = op2->next;
3087
+ }
3088
+ if (path.size() == 3 && IsVerySmallTriangle(*op2)) return false;
3089
+ return true;
3090
+ }
3091
+
3092
+ void ClipperD::BuildPathsD(PathsD& solutionClosed, PathsD* solutionOpen)
3093
+ {
3094
+ solutionClosed.resize(0);
3095
+ solutionClosed.reserve(outrec_list_.size());
3096
+ if (solutionOpen)
3097
+ {
3098
+ solutionOpen->resize(0);
3099
+ solutionOpen->reserve(outrec_list_.size());
3100
+ }
3101
+
3102
+ // outrec_list_.size() is not static here because
3103
+ // CleanCollinear below can indirectly add additional
3104
+ // OutRec (via FixOutRecPts)
3105
+ for (std::size_t i = 0; i < outrec_list_.size(); ++i)
3106
+ {
3107
+ OutRec* outrec = outrec_list_[i];
3108
+ if (outrec->pts == nullptr) continue;
3109
+
3110
+ PathD path;
3111
+ if (solutionOpen && outrec->is_open)
3112
+ {
3113
+ if (BuildPathD(outrec->pts, reverse_solution_, true, path, invScale_))
3114
+ solutionOpen->emplace_back(std::move(path));
3115
+ }
3116
+ else
3117
+ {
3118
+ CleanCollinear(outrec);
3119
+ //closed paths should always return a Positive orientation
3120
+ if (BuildPathD(outrec->pts, reverse_solution_, false, path, invScale_))
3121
+ solutionClosed.emplace_back(std::move(path));
3122
+ }
3123
+ }
3124
+ }
3125
+
3126
+ void ClipperD::BuildTreeD(PolyPathD& polytree, PathsD& open_paths)
3127
+ {
3128
+ polytree.Clear();
3129
+ open_paths.resize(0);
3130
+ if (has_open_paths_)
3131
+ open_paths.reserve(outrec_list_.size());
3132
+
3133
+ // outrec_list_.size() is not static here because
3134
+ // BuildPathD below can indirectly add additional OutRec //#607
3135
+ for (size_t i = 0; i < outrec_list_.size(); ++i)
3136
+ {
3137
+ OutRec* outrec = outrec_list_[i];
3138
+ if (!outrec || !outrec->pts) continue;
3139
+ if (outrec->is_open)
3140
+ {
3141
+ PathD path;
3142
+ if (BuildPathD(outrec->pts, reverse_solution_, true, path, invScale_))
3143
+ open_paths.emplace_back(std::move(path));
3144
+ continue;
3145
+ }
3146
+
3147
+ if (CheckBounds(outrec))
3148
+ RecursiveCheckOwners(outrec, &polytree);
3149
+ }
3150
+ }
3151
+
3152
+ } // namespace clipper2lib