Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/clipper2/src/clipper.engine.cpp
9903 views
1
/*******************************************************************************
2
* Author : Angus Johnson *
3
* Date : 30 May 2025 *
4
* Website : https://www.angusj.com *
5
* Copyright : Angus Johnson 2010-2025 *
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* vertices = new Vertex[total_vertex_count], * v = vertices;
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(vertices);
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
GetSegmentIntersectPt(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 (SegmentsIntersect(op2->prev->pt,
1657
op2->pt, op2->next->next->pt, op2->next->next->next->pt))
1658
{
1659
// adjacent intersections (ie a micro self-intersections)
1660
op2 = DuplicateOp(op2, false);
1661
op2->pt = op2->next->next->next->pt;
1662
op2 = op2->next;
1663
}
1664
else
1665
{
1666
if (op2 == outrec->pts || op2->next == outrec->pts)
1667
outrec->pts = outrec->pts->prev;
1668
DoSplitOp(outrec, op2);
1669
if (!outrec->pts) break;
1670
op2 = outrec->pts;
1671
if (op2->prev == op2->next->next)
1672
break; // again, because triangles can't self-intersect
1673
continue;
1674
}
1675
}
1676
else
1677
op2 = op2->next;
1678
1679
if (op2 == outrec->pts) break;
1680
}
1681
}
1682
1683
1684
inline void UpdateOutrecOwner(OutRec* outrec)
1685
{
1686
OutPt* opCurr = outrec->pts;
1687
for (; ; )
1688
{
1689
opCurr->outrec = outrec;
1690
opCurr = opCurr->next;
1691
if (opCurr == outrec->pts) return;
1692
}
1693
}
1694
1695
1696
OutPt* ClipperBase::StartOpenPath(Active& e, const Point64& pt)
1697
{
1698
OutRec* outrec = NewOutRec();
1699
outrec->is_open = true;
1700
1701
if (e.wind_dx > 0)
1702
{
1703
outrec->front_edge = &e;
1704
outrec->back_edge = nullptr;
1705
}
1706
else
1707
{
1708
outrec->front_edge = nullptr;
1709
outrec->back_edge = &e;
1710
}
1711
1712
e.outrec = outrec;
1713
1714
OutPt* op = new OutPt(pt, outrec);
1715
outrec->pts = op;
1716
return op;
1717
}
1718
1719
inline void TrimHorz(Active& horzEdge, bool preserveCollinear)
1720
{
1721
bool wasTrimmed = false;
1722
Point64 pt = NextVertex(horzEdge)->pt;
1723
while (pt.y == horzEdge.top.y)
1724
{
1725
//always trim 180 deg. spikes (in closed paths)
1726
//but otherwise break if preserveCollinear = true
1727
if (preserveCollinear &&
1728
((pt.x < horzEdge.top.x) != (horzEdge.bot.x < horzEdge.top.x)))
1729
break;
1730
1731
horzEdge.vertex_top = NextVertex(horzEdge);
1732
horzEdge.top = pt;
1733
wasTrimmed = true;
1734
if (IsMaxima(horzEdge)) break;
1735
pt = NextVertex(horzEdge)->pt;
1736
}
1737
1738
if (wasTrimmed) SetDx(horzEdge); // +/-infinity
1739
}
1740
1741
1742
inline void ClipperBase::UpdateEdgeIntoAEL(Active* e)
1743
{
1744
e->bot = e->top;
1745
e->vertex_top = NextVertex(*e);
1746
e->top = e->vertex_top->pt;
1747
e->curr_x = e->bot.x;
1748
SetDx(*e);
1749
1750
if (IsJoined(*e)) Split(*e, e->bot);
1751
1752
if (IsHorizontal(*e))
1753
{
1754
if (!IsOpen(*e)) TrimHorz(*e, preserve_collinear_);
1755
return;
1756
}
1757
1758
InsertScanline(e->top.y);
1759
CheckJoinLeft(*e, e->bot);
1760
CheckJoinRight(*e, e->bot, true); // (#500)
1761
}
1762
1763
Active* FindEdgeWithMatchingLocMin(Active* e)
1764
{
1765
Active* result = e->next_in_ael;
1766
while (result)
1767
{
1768
if (result->local_min == e->local_min) return result;
1769
else if (!IsHorizontal(*result) && e->bot != result->bot) result = nullptr;
1770
else result = result->next_in_ael;
1771
}
1772
result = e->prev_in_ael;
1773
while (result)
1774
{
1775
if (result->local_min == e->local_min) return result;
1776
else if (!IsHorizontal(*result) && e->bot != result->bot) return nullptr;
1777
else result = result->prev_in_ael;
1778
}
1779
return result;
1780
}
1781
1782
1783
void ClipperBase::IntersectEdges(Active& e1, Active& e2, const Point64& pt)
1784
{
1785
//MANAGE OPEN PATH INTERSECTIONS SEPARATELY ...
1786
if (has_open_paths_ && (IsOpen(e1) || IsOpen(e2)))
1787
{
1788
if (IsOpen(e1) && IsOpen(e2)) return;
1789
Active* edge_o, * edge_c;
1790
if (IsOpen(e1))
1791
{
1792
edge_o = &e1;
1793
edge_c = &e2;
1794
}
1795
else
1796
{
1797
edge_o = &e2;
1798
edge_c = &e1;
1799
}
1800
if (IsJoined(*edge_c)) Split(*edge_c, pt); // needed for safety
1801
1802
if (abs(edge_c->wind_cnt) != 1) return;
1803
switch (cliptype_)
1804
{
1805
case ClipType::Union:
1806
if (!IsHotEdge(*edge_c)) return;
1807
break;
1808
default:
1809
if (edge_c->local_min->polytype == PathType::Subject)
1810
return;
1811
}
1812
1813
switch (fillrule_)
1814
{
1815
case FillRule::Positive:
1816
if (edge_c->wind_cnt != 1) return;
1817
break;
1818
case FillRule::Negative:
1819
if (edge_c->wind_cnt != -1) return;
1820
break;
1821
default:
1822
if (std::abs(edge_c->wind_cnt) != 1) return;
1823
}
1824
1825
#ifdef USINGZ
1826
OutPt* resultOp;
1827
#endif
1828
//toggle contribution ...
1829
if (IsHotEdge(*edge_o))
1830
{
1831
#ifdef USINGZ
1832
resultOp = AddOutPt(*edge_o, pt);
1833
#else
1834
AddOutPt(*edge_o, pt);
1835
#endif
1836
if (IsFront(*edge_o)) edge_o->outrec->front_edge = nullptr;
1837
else edge_o->outrec->back_edge = nullptr;
1838
edge_o->outrec = nullptr;
1839
}
1840
1841
//horizontal edges can pass under open paths at a LocMins
1842
else if (pt == edge_o->local_min->vertex->pt &&
1843
!IsOpenEnd(*edge_o->local_min->vertex))
1844
{
1845
//find the other side of the LocMin and
1846
//if it's 'hot' join up with it ...
1847
Active* e3 = FindEdgeWithMatchingLocMin(edge_o);
1848
if (e3 && IsHotEdge(*e3))
1849
{
1850
edge_o->outrec = e3->outrec;
1851
if (edge_o->wind_dx > 0)
1852
SetSides(*e3->outrec, *edge_o, *e3);
1853
else
1854
SetSides(*e3->outrec, *e3, *edge_o);
1855
return;
1856
}
1857
else
1858
#ifdef USINGZ
1859
resultOp = StartOpenPath(*edge_o, pt);
1860
#else
1861
StartOpenPath(*edge_o, pt);
1862
#endif
1863
}
1864
else
1865
#ifdef USINGZ
1866
resultOp = StartOpenPath(*edge_o, pt);
1867
#else
1868
StartOpenPath(*edge_o, pt);
1869
#endif
1870
1871
#ifdef USINGZ
1872
if (zCallback_) SetZ(*edge_o, *edge_c, resultOp->pt);
1873
#endif
1874
return;
1875
} // end of an open path intersection
1876
1877
//MANAGING CLOSED PATHS FROM HERE ON
1878
1879
if (IsJoined(e1)) Split(e1, pt);
1880
if (IsJoined(e2)) Split(e2, pt);
1881
1882
//UPDATE WINDING COUNTS...
1883
1884
int old_e1_windcnt, old_e2_windcnt;
1885
if (e1.local_min->polytype == e2.local_min->polytype)
1886
{
1887
if (fillrule_ == FillRule::EvenOdd)
1888
{
1889
old_e1_windcnt = e1.wind_cnt;
1890
e1.wind_cnt = e2.wind_cnt;
1891
e2.wind_cnt = old_e1_windcnt;
1892
}
1893
else
1894
{
1895
if (e1.wind_cnt + e2.wind_dx == 0)
1896
e1.wind_cnt = -e1.wind_cnt;
1897
else
1898
e1.wind_cnt += e2.wind_dx;
1899
if (e2.wind_cnt - e1.wind_dx == 0)
1900
e2.wind_cnt = -e2.wind_cnt;
1901
else
1902
e2.wind_cnt -= e1.wind_dx;
1903
}
1904
}
1905
else
1906
{
1907
if (fillrule_ != FillRule::EvenOdd)
1908
{
1909
e1.wind_cnt2 += e2.wind_dx;
1910
e2.wind_cnt2 -= e1.wind_dx;
1911
}
1912
else
1913
{
1914
e1.wind_cnt2 = (e1.wind_cnt2 == 0 ? 1 : 0);
1915
e2.wind_cnt2 = (e2.wind_cnt2 == 0 ? 1 : 0);
1916
}
1917
}
1918
1919
switch (fillrule_)
1920
{
1921
case FillRule::EvenOdd:
1922
case FillRule::NonZero:
1923
old_e1_windcnt = abs(e1.wind_cnt);
1924
old_e2_windcnt = abs(e2.wind_cnt);
1925
break;
1926
default:
1927
if (fillrule_ == fillpos)
1928
{
1929
old_e1_windcnt = e1.wind_cnt;
1930
old_e2_windcnt = e2.wind_cnt;
1931
}
1932
else
1933
{
1934
old_e1_windcnt = -e1.wind_cnt;
1935
old_e2_windcnt = -e2.wind_cnt;
1936
}
1937
break;
1938
}
1939
1940
const bool e1_windcnt_in_01 = old_e1_windcnt == 0 || old_e1_windcnt == 1;
1941
const bool e2_windcnt_in_01 = old_e2_windcnt == 0 || old_e2_windcnt == 1;
1942
1943
if ((!IsHotEdge(e1) && !e1_windcnt_in_01) ||
1944
(!IsHotEdge(e2) && !e2_windcnt_in_01))
1945
return;
1946
1947
//NOW PROCESS THE INTERSECTION ...
1948
#ifdef USINGZ
1949
OutPt* resultOp = nullptr;
1950
#endif
1951
//if both edges are 'hot' ...
1952
if (IsHotEdge(e1) && IsHotEdge(e2))
1953
{
1954
if ((old_e1_windcnt != 0 && old_e1_windcnt != 1) || (old_e2_windcnt != 0 && old_e2_windcnt != 1) ||
1955
(e1.local_min->polytype != e2.local_min->polytype && cliptype_ != ClipType::Xor))
1956
{
1957
#ifdef USINGZ
1958
resultOp = AddLocalMaxPoly(e1, e2, pt);
1959
if (zCallback_ && resultOp) SetZ(e1, e2, resultOp->pt);
1960
#else
1961
AddLocalMaxPoly(e1, e2, pt);
1962
#endif
1963
}
1964
else if (IsFront(e1) || (e1.outrec == e2.outrec))
1965
{
1966
//this 'else if' condition isn't strictly needed but
1967
//it's sensible to split polygons that only touch at
1968
//a common vertex (not at common edges).
1969
1970
#ifdef USINGZ
1971
resultOp = AddLocalMaxPoly(e1, e2, pt);
1972
OutPt* op2 = AddLocalMinPoly(e1, e2, pt);
1973
if (zCallback_ && resultOp) SetZ(e1, e2, resultOp->pt);
1974
if (zCallback_) SetZ(e1, e2, op2->pt);
1975
#else
1976
AddLocalMaxPoly(e1, e2, pt);
1977
AddLocalMinPoly(e1, e2, pt);
1978
#endif
1979
}
1980
else
1981
{
1982
#ifdef USINGZ
1983
resultOp = AddOutPt(e1, pt);
1984
OutPt* op2 = AddOutPt(e2, pt);
1985
if (zCallback_)
1986
{
1987
SetZ(e1, e2, resultOp->pt);
1988
SetZ(e1, e2, op2->pt);
1989
}
1990
#else
1991
AddOutPt(e1, pt);
1992
AddOutPt(e2, pt);
1993
#endif
1994
SwapOutrecs(e1, e2);
1995
}
1996
}
1997
else if (IsHotEdge(e1))
1998
{
1999
#ifdef USINGZ
2000
resultOp = AddOutPt(e1, pt);
2001
if (zCallback_) SetZ(e1, e2, resultOp->pt);
2002
#else
2003
AddOutPt(e1, pt);
2004
#endif
2005
SwapOutrecs(e1, e2);
2006
}
2007
else if (IsHotEdge(e2))
2008
{
2009
#ifdef USINGZ
2010
resultOp = AddOutPt(e2, pt);
2011
if (zCallback_) SetZ(e1, e2, resultOp->pt);
2012
#else
2013
AddOutPt(e2, pt);
2014
#endif
2015
SwapOutrecs(e1, e2);
2016
}
2017
else
2018
{
2019
int64_t e1Wc2, e2Wc2;
2020
switch (fillrule_)
2021
{
2022
case FillRule::EvenOdd:
2023
case FillRule::NonZero:
2024
e1Wc2 = abs(e1.wind_cnt2);
2025
e2Wc2 = abs(e2.wind_cnt2);
2026
break;
2027
default:
2028
if (fillrule_ == fillpos)
2029
{
2030
e1Wc2 = e1.wind_cnt2;
2031
e2Wc2 = e2.wind_cnt2;
2032
}
2033
else
2034
{
2035
e1Wc2 = -e1.wind_cnt2;
2036
e2Wc2 = -e2.wind_cnt2;
2037
}
2038
break;
2039
}
2040
2041
if (!IsSamePolyType(e1, e2))
2042
{
2043
#ifdef USINGZ
2044
resultOp = AddLocalMinPoly(e1, e2, pt, false);
2045
if (zCallback_) SetZ(e1, e2, resultOp->pt);
2046
#else
2047
AddLocalMinPoly(e1, e2, pt, false);
2048
#endif
2049
}
2050
else if (old_e1_windcnt == 1 && old_e2_windcnt == 1)
2051
{
2052
#ifdef USINGZ
2053
resultOp = nullptr;
2054
#endif
2055
switch (cliptype_)
2056
{
2057
case ClipType::Union:
2058
if (e1Wc2 <= 0 && e2Wc2 <= 0)
2059
#ifdef USINGZ
2060
resultOp = AddLocalMinPoly(e1, e2, pt, false);
2061
#else
2062
AddLocalMinPoly(e1, e2, pt, false);
2063
#endif
2064
break;
2065
case ClipType::Difference:
2066
if (((GetPolyType(e1) == PathType::Clip) && (e1Wc2 > 0) && (e2Wc2 > 0)) ||
2067
((GetPolyType(e1) == PathType::Subject) && (e1Wc2 <= 0) && (e2Wc2 <= 0)))
2068
{
2069
#ifdef USINGZ
2070
resultOp = AddLocalMinPoly(e1, e2, pt, false);
2071
#else
2072
AddLocalMinPoly(e1, e2, pt, false);
2073
#endif
2074
}
2075
break;
2076
case ClipType::Xor:
2077
#ifdef USINGZ
2078
resultOp = AddLocalMinPoly(e1, e2, pt, false);
2079
#else
2080
AddLocalMinPoly(e1, e2, pt, false);
2081
#endif
2082
break;
2083
default:
2084
if (e1Wc2 > 0 && e2Wc2 > 0)
2085
#ifdef USINGZ
2086
resultOp = AddLocalMinPoly(e1, e2, pt, false);
2087
#else
2088
AddLocalMinPoly(e1, e2, pt, false);
2089
#endif
2090
break;
2091
}
2092
#ifdef USINGZ
2093
if (resultOp && zCallback_) SetZ(e1, e2, resultOp->pt);
2094
#endif
2095
}
2096
}
2097
}
2098
2099
inline void ClipperBase::DeleteFromAEL(Active& e)
2100
{
2101
Active* prev = e.prev_in_ael;
2102
Active* next = e.next_in_ael;
2103
if (!prev && !next && (&e != actives_)) return; // already deleted
2104
if (prev)
2105
prev->next_in_ael = next;
2106
else
2107
actives_ = next;
2108
if (next) next->prev_in_ael = prev;
2109
delete& e;
2110
}
2111
2112
2113
inline void ClipperBase::AdjustCurrXAndCopyToSEL(const int64_t top_y)
2114
{
2115
Active* e = actives_;
2116
sel_ = e;
2117
while (e)
2118
{
2119
e->prev_in_sel = e->prev_in_ael;
2120
e->next_in_sel = e->next_in_ael;
2121
e->jump = e->next_in_sel;
2122
// it is safe to ignore 'joined' edges here because
2123
// if necessary they will be split in IntersectEdges()
2124
e->curr_x = TopX(*e, top_y);
2125
e = e->next_in_ael;
2126
}
2127
}
2128
2129
bool ClipperBase::ExecuteInternal(ClipType ct, FillRule fillrule, bool use_polytrees)
2130
{
2131
cliptype_ = ct;
2132
fillrule_ = fillrule;
2133
using_polytree_ = use_polytrees;
2134
Reset();
2135
int64_t y;
2136
if (ct == ClipType::NoClip || !PopScanline(y)) return true;
2137
2138
while (succeeded_)
2139
{
2140
InsertLocalMinimaIntoAEL(y);
2141
Active* e;
2142
while (PopHorz(e)) DoHorizontal(*e);
2143
if (horz_seg_list_.size() > 0)
2144
{
2145
ConvertHorzSegsToJoins();
2146
horz_seg_list_.clear();
2147
}
2148
bot_y_ = y; // bot_y_ == bottom of scanbeam
2149
if (!PopScanline(y)) break; // y new top of scanbeam
2150
DoIntersections(y);
2151
DoTopOfScanbeam(y);
2152
while (PopHorz(e)) DoHorizontal(*e);
2153
}
2154
if (succeeded_) ProcessHorzJoins();
2155
return succeeded_;
2156
}
2157
2158
inline void FixOutRecPts(OutRec* outrec)
2159
{
2160
OutPt* op = outrec->pts;
2161
do {
2162
op->outrec = outrec;
2163
op = op->next;
2164
} while (op != outrec->pts);
2165
}
2166
2167
inline bool SetHorzSegHeadingForward(HorzSegment& hs, OutPt* opP, OutPt* opN)
2168
{
2169
if (opP->pt.x == opN->pt.x) return false;
2170
if (opP->pt.x < opN->pt.x)
2171
{
2172
hs.left_op = opP;
2173
hs.right_op = opN;
2174
hs.left_to_right = true;
2175
}
2176
else
2177
{
2178
hs.left_op = opN;
2179
hs.right_op = opP;
2180
hs.left_to_right = false;
2181
}
2182
return true;
2183
}
2184
2185
inline bool UpdateHorzSegment(HorzSegment& hs)
2186
{
2187
OutPt* op = hs.left_op;
2188
OutRec* outrec = GetRealOutRec(op->outrec);
2189
bool outrecHasEdges = outrec->front_edge;
2190
int64_t curr_y = op->pt.y;
2191
OutPt* opP = op, * opN = op;
2192
if (outrecHasEdges)
2193
{
2194
OutPt* opA = outrec->pts, * opZ = opA->next;
2195
while (opP != opZ && opP->prev->pt.y == curr_y)
2196
opP = opP->prev;
2197
while (opN != opA && opN->next->pt.y == curr_y)
2198
opN = opN->next;
2199
}
2200
else
2201
{
2202
while (opP->prev != opN && opP->prev->pt.y == curr_y)
2203
opP = opP->prev;
2204
while (opN->next != opP && opN->next->pt.y == curr_y)
2205
opN = opN->next;
2206
}
2207
bool result =
2208
SetHorzSegHeadingForward(hs, opP, opN) &&
2209
!hs.left_op->horz;
2210
2211
if (result)
2212
hs.left_op->horz = &hs;
2213
else
2214
hs.right_op = nullptr; // (for sorting)
2215
return result;
2216
}
2217
2218
void ClipperBase::ConvertHorzSegsToJoins()
2219
{
2220
auto j = std::count_if(horz_seg_list_.begin(),
2221
horz_seg_list_.end(),
2222
[](HorzSegment& hs) { return UpdateHorzSegment(hs); });
2223
if (j < 2) return;
2224
2225
std::stable_sort(horz_seg_list_.begin(), horz_seg_list_.end(), HorzSegSorter());
2226
2227
HorzSegmentList::iterator hs1 = horz_seg_list_.begin(), hs2;
2228
HorzSegmentList::iterator hs_end = hs1 +j;
2229
HorzSegmentList::iterator hs_end1 = hs_end - 1;
2230
2231
for (; hs1 != hs_end1; ++hs1)
2232
{
2233
for (hs2 = hs1 + 1; hs2 != hs_end; ++hs2)
2234
{
2235
if ((hs2->left_op->pt.x >= hs1->right_op->pt.x) ||
2236
(hs2->left_to_right == hs1->left_to_right) ||
2237
(hs2->right_op->pt.x <= hs1->left_op->pt.x)) continue;
2238
int64_t curr_y = hs1->left_op->pt.y;
2239
if (hs1->left_to_right)
2240
{
2241
while (hs1->left_op->next->pt.y == curr_y &&
2242
hs1->left_op->next->pt.x <= hs2->left_op->pt.x)
2243
hs1->left_op = hs1->left_op->next;
2244
while (hs2->left_op->prev->pt.y == curr_y &&
2245
hs2->left_op->prev->pt.x <= hs1->left_op->pt.x)
2246
hs2->left_op = hs2->left_op->prev;
2247
HorzJoin join = HorzJoin(
2248
DuplicateOp(hs1->left_op, true),
2249
DuplicateOp(hs2->left_op, false));
2250
horz_join_list_.emplace_back(join);
2251
}
2252
else
2253
{
2254
while (hs1->left_op->prev->pt.y == curr_y &&
2255
hs1->left_op->prev->pt.x <= hs2->left_op->pt.x)
2256
hs1->left_op = hs1->left_op->prev;
2257
while (hs2->left_op->next->pt.y == curr_y &&
2258
hs2->left_op->next->pt.x <= hs1->left_op->pt.x)
2259
hs2->left_op = hs2->left_op->next;
2260
HorzJoin join = HorzJoin(
2261
DuplicateOp(hs2->left_op, true),
2262
DuplicateOp(hs1->left_op, false));
2263
horz_join_list_.emplace_back(join);
2264
}
2265
}
2266
}
2267
}
2268
2269
void MoveSplits(OutRec* fromOr, OutRec* toOr)
2270
{
2271
if (!toOr->splits) toOr->splits = new OutRecList();
2272
OutRecList::iterator orIter = fromOr->splits->begin();
2273
for (; orIter != fromOr->splits->end(); ++orIter)
2274
toOr->splits->emplace_back(*orIter);
2275
fromOr->splits->clear();
2276
}
2277
2278
void ClipperBase::ProcessHorzJoins()
2279
{
2280
for (const HorzJoin& j : horz_join_list_)
2281
{
2282
OutRec* or1 = GetRealOutRec(j.op1->outrec);
2283
OutRec* or2 = GetRealOutRec(j.op2->outrec);
2284
2285
OutPt* op1b = j.op1->next;
2286
OutPt* op2b = j.op2->prev;
2287
j.op1->next = j.op2;
2288
j.op2->prev = j.op1;
2289
op1b->prev = op2b;
2290
op2b->next = op1b;
2291
2292
if (or1 == or2) // 'join' is really a split
2293
{
2294
or2 = NewOutRec();
2295
or2->pts = op1b;
2296
FixOutRecPts(or2);
2297
2298
//if or1->pts has moved to or2 then update or1->pts!!
2299
if (or1->pts->outrec == or2)
2300
{
2301
or1->pts = j.op1;
2302
or1->pts->outrec = or1;
2303
}
2304
2305
if (using_polytree_) //#498, #520, #584, D#576, #618
2306
{
2307
if (Path2ContainsPath1(or1->pts, or2->pts))
2308
{
2309
//swap or1's & or2's pts
2310
OutPt* tmp = or1->pts;
2311
or1->pts = or2->pts;
2312
or2->pts = tmp;
2313
FixOutRecPts(or1);
2314
FixOutRecPts(or2);
2315
//or2 is now inside or1
2316
or2->owner = or1;
2317
}
2318
else if (Path2ContainsPath1(or2->pts, or1->pts))
2319
{
2320
or2->owner = or1;
2321
}
2322
else
2323
or2->owner = or1->owner;
2324
2325
if (!or1->splits) or1->splits = new OutRecList();
2326
or1->splits->emplace_back(or2);
2327
}
2328
else
2329
or2->owner = or1;
2330
}
2331
else // joining, not splitting
2332
{
2333
or2->pts = nullptr;
2334
if (using_polytree_)
2335
{
2336
SetOwner(or2, or1);
2337
if (or2->splits)
2338
MoveSplits(or2, or1); //#618
2339
}
2340
else
2341
or2->owner = or1;
2342
}
2343
}
2344
}
2345
2346
void ClipperBase::DoIntersections(const int64_t top_y)
2347
{
2348
if (BuildIntersectList(top_y))
2349
{
2350
ProcessIntersectList();
2351
intersect_nodes_.clear();
2352
}
2353
}
2354
2355
void ClipperBase::AddNewIntersectNode(Active& e1, Active& e2, int64_t top_y)
2356
{
2357
Point64 ip;
2358
if (!GetSegmentIntersectPt(e1.bot, e1.top, e2.bot, e2.top, ip))
2359
ip = Point64(e1.curr_x, top_y); //parallel edges
2360
2361
//rounding errors can occasionally place the calculated intersection
2362
//point either below or above the scanbeam, so check and correct ...
2363
if (ip.y > bot_y_ || ip.y < top_y)
2364
{
2365
double abs_dx1 = std::fabs(e1.dx);
2366
double abs_dx2 = std::fabs(e2.dx);
2367
if (abs_dx1 > 100 && abs_dx2 > 100)
2368
{
2369
if (abs_dx1 > abs_dx2)
2370
ip = GetClosestPointOnSegment(ip, e1.bot, e1.top);
2371
else
2372
ip = GetClosestPointOnSegment(ip, e2.bot, e2.top);
2373
}
2374
else if (abs_dx1 > 100)
2375
ip = GetClosestPointOnSegment(ip, e1.bot, e1.top);
2376
else if (abs_dx2 > 100)
2377
ip = GetClosestPointOnSegment(ip, e2.bot, e2.top);
2378
else
2379
{
2380
if (ip.y < top_y) ip.y = top_y;
2381
else ip.y = bot_y_;
2382
if (abs_dx1 < abs_dx2) ip.x = TopX(e1, ip.y);
2383
else ip.x = TopX(e2, ip.y);
2384
}
2385
}
2386
intersect_nodes_.emplace_back(&e1, &e2, ip);
2387
}
2388
2389
bool ClipperBase::BuildIntersectList(const int64_t top_y)
2390
{
2391
if (!actives_ || !actives_->next_in_ael) return false;
2392
2393
//Calculate edge positions at the top of the current scanbeam, and from this
2394
//we will determine the intersections required to reach these new positions.
2395
AdjustCurrXAndCopyToSEL(top_y);
2396
//Find all edge intersections in the current scanbeam using a stable merge
2397
//sort that ensures only adjacent edges are intersecting. Intersect info is
2398
//stored in FIntersectList ready to be processed in ProcessIntersectList.
2399
//Re merge sorts see https://stackoverflow.com/a/46319131/359538
2400
2401
Active* left = sel_, * right, * l_end, * r_end, * curr_base, * tmp;
2402
2403
while (left && left->jump)
2404
{
2405
Active* prev_base = nullptr;
2406
while (left && left->jump)
2407
{
2408
curr_base = left;
2409
right = left->jump;
2410
l_end = right;
2411
r_end = right->jump;
2412
left->jump = r_end;
2413
while (left != l_end && right != r_end)
2414
{
2415
if (right->curr_x < left->curr_x)
2416
{
2417
tmp = right->prev_in_sel;
2418
for (; ; )
2419
{
2420
AddNewIntersectNode(*tmp, *right, top_y);
2421
if (tmp == left) break;
2422
tmp = tmp->prev_in_sel;
2423
}
2424
2425
tmp = right;
2426
right = ExtractFromSEL(tmp);
2427
l_end = right;
2428
Insert1Before2InSEL(tmp, left);
2429
if (left == curr_base)
2430
{
2431
curr_base = tmp;
2432
curr_base->jump = r_end;
2433
if (!prev_base) sel_ = curr_base;
2434
else prev_base->jump = curr_base;
2435
}
2436
}
2437
else left = left->next_in_sel;
2438
}
2439
prev_base = curr_base;
2440
left = r_end;
2441
}
2442
left = sel_;
2443
}
2444
return intersect_nodes_.size() > 0;
2445
}
2446
2447
void ClipperBase::ProcessIntersectList()
2448
{
2449
//We now have a list of intersections required so that edges will be
2450
//correctly positioned at the top of the scanbeam. However, it's important
2451
//that edge intersections are processed from the bottom up, but it's also
2452
//crucial that intersections only occur between adjacent edges.
2453
2454
//First we do a quicksort so intersections proceed in a bottom up order ...
2455
std::sort(intersect_nodes_.begin(), intersect_nodes_.end(), IntersectListSort);
2456
//Now as we process these intersections, we must sometimes adjust the order
2457
//to ensure that intersecting edges are always adjacent ...
2458
2459
IntersectNodeList::iterator node_iter, node_iter2;
2460
for (node_iter = intersect_nodes_.begin();
2461
node_iter != intersect_nodes_.end(); ++node_iter)
2462
{
2463
if (!EdgesAdjacentInAEL(*node_iter))
2464
{
2465
node_iter2 = node_iter + 1;
2466
while (!EdgesAdjacentInAEL(*node_iter2)) ++node_iter2;
2467
std::swap(*node_iter, *node_iter2);
2468
}
2469
2470
IntersectNode& node = *node_iter;
2471
IntersectEdges(*node.edge1, *node.edge2, node.pt);
2472
SwapPositionsInAEL(*node.edge1, *node.edge2);
2473
2474
node.edge1->curr_x = node.pt.x;
2475
node.edge2->curr_x = node.pt.x;
2476
CheckJoinLeft(*node.edge2, node.pt, true);
2477
CheckJoinRight(*node.edge1, node.pt, true);
2478
}
2479
}
2480
2481
void ClipperBase::SwapPositionsInAEL(Active& e1, Active& e2)
2482
{
2483
//preconditon: e1 must be immediately to the left of e2
2484
Active* next = e2.next_in_ael;
2485
if (next) next->prev_in_ael = &e1;
2486
Active* prev = e1.prev_in_ael;
2487
if (prev) prev->next_in_ael = &e2;
2488
e2.prev_in_ael = prev;
2489
e2.next_in_ael = &e1;
2490
e1.prev_in_ael = &e2;
2491
e1.next_in_ael = next;
2492
if (!e2.prev_in_ael) actives_ = &e2;
2493
}
2494
2495
inline OutPt* GetLastOp(const Active& hot_edge)
2496
{
2497
OutRec* outrec = hot_edge.outrec;
2498
OutPt* result = outrec->pts;
2499
if (&hot_edge != outrec->front_edge)
2500
result = result->next;
2501
return result;
2502
}
2503
2504
void ClipperBase::AddTrialHorzJoin(OutPt* op)
2505
{
2506
if (op->outrec->is_open) return;
2507
horz_seg_list_.emplace_back(op);
2508
}
2509
2510
bool ClipperBase::ResetHorzDirection(const Active& horz,
2511
const Vertex* max_vertex, int64_t& horz_left, int64_t& horz_right)
2512
{
2513
if (horz.bot.x == horz.top.x)
2514
{
2515
//the horizontal edge is going nowhere ...
2516
horz_left = horz.curr_x;
2517
horz_right = horz.curr_x;
2518
Active* e = horz.next_in_ael;
2519
while (e && e->vertex_top != max_vertex) e = e->next_in_ael;
2520
return e != nullptr;
2521
}
2522
else if (horz.curr_x < horz.top.x)
2523
{
2524
horz_left = horz.curr_x;
2525
horz_right = horz.top.x;
2526
return true;
2527
}
2528
else
2529
{
2530
horz_left = horz.top.x;
2531
horz_right = horz.curr_x;
2532
return false; // right to left
2533
}
2534
}
2535
2536
void ClipperBase::DoHorizontal(Active& horz)
2537
/*******************************************************************************
2538
* Notes: Horizontal edges (HEs) at scanline intersections (ie at the top or *
2539
* bottom of a scanbeam) are processed as if layered.The order in which HEs *
2540
* are processed doesn't matter. HEs intersect with the bottom vertices of *
2541
* other HEs[#] and with non-horizontal edges [*]. Once these intersections *
2542
* are completed, intermediate HEs are 'promoted' to the next edge in their *
2543
* bounds, and they in turn may be intersected[%] by other HEs. *
2544
* *
2545
* eg: 3 horizontals at a scanline: / | / / *
2546
* | / | (HE3)o ========%========== o *
2547
* o ======= o(HE2) / | / / *
2548
* o ============#=========*======*========#=========o (HE1) *
2549
* / | / | / *
2550
*******************************************************************************/
2551
{
2552
Point64 pt;
2553
bool horzIsOpen = IsOpen(horz);
2554
int64_t y = horz.bot.y;
2555
Vertex* vertex_max;
2556
if (horzIsOpen)
2557
vertex_max = GetCurrYMaximaVertex_Open(horz);
2558
else
2559
vertex_max = GetCurrYMaximaVertex(horz);
2560
2561
//// remove 180 deg.spikes and also simplify
2562
//// consecutive horizontals when PreserveCollinear = true
2563
//if (!horzIsOpen && vertex_max != horz.vertex_top)
2564
// TrimHorz(horz, PreserveCollinear);
2565
2566
int64_t horz_left, horz_right;
2567
bool is_left_to_right =
2568
ResetHorzDirection(horz, vertex_max, horz_left, horz_right);
2569
2570
if (IsHotEdge(horz))
2571
{
2572
#ifdef USINGZ
2573
OutPt* op = AddOutPt(horz, Point64(horz.curr_x, y, horz.bot.z));
2574
#else
2575
OutPt* op = AddOutPt(horz, Point64(horz.curr_x, y));
2576
#endif
2577
AddTrialHorzJoin(op);
2578
}
2579
2580
while (true) // loop through consec. horizontal edges
2581
{
2582
Active* e;
2583
if (is_left_to_right) e = horz.next_in_ael;
2584
else e = horz.prev_in_ael;
2585
2586
while (e)
2587
{
2588
if (e->vertex_top == vertex_max)
2589
{
2590
if (IsHotEdge(horz) && IsJoined(*e))
2591
Split(*e, e->top);
2592
2593
//if (IsHotEdge(horz) != IsHotEdge(*e))
2594
// DoError(undefined_error_i);
2595
2596
if (IsHotEdge(horz))
2597
{
2598
while (horz.vertex_top != vertex_max)
2599
{
2600
AddOutPt(horz, horz.top);
2601
UpdateEdgeIntoAEL(&horz);
2602
}
2603
if (is_left_to_right)
2604
AddLocalMaxPoly(horz, *e, horz.top);
2605
else
2606
AddLocalMaxPoly(*e, horz, horz.top);
2607
}
2608
DeleteFromAEL(*e);
2609
DeleteFromAEL(horz);
2610
return;
2611
}
2612
2613
//if horzEdge is a maxima, keep going until we reach
2614
//its maxima pair, otherwise check for break conditions
2615
if (vertex_max != horz.vertex_top || IsOpenEnd(horz))
2616
{
2617
//otherwise stop when 'ae' is beyond the end of the horizontal line
2618
if ((is_left_to_right && e->curr_x > horz_right) ||
2619
(!is_left_to_right && e->curr_x < horz_left)) break;
2620
2621
if (e->curr_x == horz.top.x && !IsHorizontal(*e))
2622
{
2623
pt = NextVertex(horz)->pt;
2624
if (is_left_to_right)
2625
{
2626
//with open paths we'll only break once past horz's end
2627
if (IsOpen(*e) && !IsSamePolyType(*e, horz) && !IsHotEdge(*e))
2628
{
2629
if (TopX(*e, pt.y) > pt.x) break;
2630
}
2631
//otherwise we'll only break when horz's outslope is greater than e's
2632
else if (TopX(*e, pt.y) >= pt.x) break;
2633
}
2634
else
2635
{
2636
if (IsOpen(*e) && !IsSamePolyType(*e, horz) && !IsHotEdge(*e))
2637
{
2638
if (TopX(*e, pt.y) < pt.x) break;
2639
}
2640
else if (TopX(*e, pt.y) <= pt.x) break;
2641
}
2642
}
2643
}
2644
2645
pt = Point64(e->curr_x, horz.bot.y);
2646
if (is_left_to_right)
2647
{
2648
IntersectEdges(horz, *e, pt);
2649
SwapPositionsInAEL(horz, *e);
2650
CheckJoinLeft(*e, pt);
2651
horz.curr_x = e->curr_x;
2652
e = horz.next_in_ael;
2653
}
2654
else
2655
{
2656
IntersectEdges(*e, horz, pt);
2657
SwapPositionsInAEL(*e, horz);
2658
CheckJoinRight(*e, pt);
2659
horz.curr_x = e->curr_x;
2660
e = horz.prev_in_ael;
2661
}
2662
2663
if (horz.outrec)
2664
{
2665
//nb: The outrec containing the op returned by IntersectEdges
2666
//above may no longer be associated with horzEdge.
2667
AddTrialHorzJoin(GetLastOp(horz));
2668
}
2669
}
2670
2671
//check if we've finished with (consecutive) horizontals ...
2672
if (horzIsOpen && IsOpenEnd(horz)) // ie open at top
2673
{
2674
if (IsHotEdge(horz))
2675
{
2676
AddOutPt(horz, horz.top);
2677
if (IsFront(horz))
2678
horz.outrec->front_edge = nullptr;
2679
else
2680
horz.outrec->back_edge = nullptr;
2681
horz.outrec = nullptr;
2682
}
2683
DeleteFromAEL(horz);
2684
return;
2685
}
2686
else if (NextVertex(horz)->pt.y != horz.top.y)
2687
break;
2688
2689
//still more horizontals in bound to process ...
2690
if (IsHotEdge(horz))
2691
AddOutPt(horz, horz.top);
2692
UpdateEdgeIntoAEL(&horz);
2693
2694
is_left_to_right =
2695
ResetHorzDirection(horz, vertex_max, horz_left, horz_right);
2696
}
2697
2698
if (IsHotEdge(horz))
2699
{
2700
OutPt* op = AddOutPt(horz, horz.top);
2701
AddTrialHorzJoin(op);
2702
}
2703
2704
UpdateEdgeIntoAEL(&horz); // end of an intermediate horiz.
2705
}
2706
2707
void ClipperBase::DoTopOfScanbeam(const int64_t y)
2708
{
2709
sel_ = nullptr; // sel_ is reused to flag horizontals (see PushHorz below)
2710
Active* e = actives_;
2711
while (e)
2712
{
2713
//nb: 'e' will never be horizontal here
2714
if (e->top.y == y)
2715
{
2716
e->curr_x = e->top.x;
2717
if (IsMaxima(*e))
2718
{
2719
e = DoMaxima(*e); // TOP OF BOUND (MAXIMA)
2720
continue;
2721
}
2722
else
2723
{
2724
//INTERMEDIATE VERTEX ...
2725
if (IsHotEdge(*e)) AddOutPt(*e, e->top);
2726
UpdateEdgeIntoAEL(e);
2727
if (IsHorizontal(*e))
2728
PushHorz(*e); // horizontals are processed later
2729
}
2730
}
2731
else // i.e. not the top of the edge
2732
e->curr_x = TopX(*e, y);
2733
2734
e = e->next_in_ael;
2735
}
2736
}
2737
2738
2739
Active* ClipperBase::DoMaxima(Active& e)
2740
{
2741
Active* next_e, * prev_e, * max_pair;
2742
prev_e = e.prev_in_ael;
2743
next_e = e.next_in_ael;
2744
if (IsOpenEnd(e))
2745
{
2746
if (IsHotEdge(e)) AddOutPt(e, e.top);
2747
if (!IsHorizontal(e))
2748
{
2749
if (IsHotEdge(e))
2750
{
2751
if (IsFront(e))
2752
e.outrec->front_edge = nullptr;
2753
else
2754
e.outrec->back_edge = nullptr;
2755
e.outrec = nullptr;
2756
}
2757
DeleteFromAEL(e);
2758
}
2759
return next_e;
2760
}
2761
2762
max_pair = GetMaximaPair(e);
2763
if (!max_pair) return next_e; // eMaxPair is horizontal
2764
2765
if (IsJoined(e)) Split(e, e.top);
2766
if (IsJoined(*max_pair)) Split(*max_pair, max_pair->top);
2767
2768
//only non-horizontal maxima here.
2769
//process any edges between maxima pair ...
2770
while (next_e != max_pair)
2771
{
2772
IntersectEdges(e, *next_e, e.top);
2773
SwapPositionsInAEL(e, *next_e);
2774
next_e = e.next_in_ael;
2775
}
2776
2777
if (IsOpen(e))
2778
{
2779
if (IsHotEdge(e))
2780
AddLocalMaxPoly(e, *max_pair, e.top);
2781
DeleteFromAEL(*max_pair);
2782
DeleteFromAEL(e);
2783
return (prev_e ? prev_e->next_in_ael : actives_);
2784
}
2785
2786
// e.next_in_ael== max_pair ...
2787
if (IsHotEdge(e))
2788
AddLocalMaxPoly(e, *max_pair, e.top);
2789
2790
DeleteFromAEL(e);
2791
DeleteFromAEL(*max_pair);
2792
return (prev_e ? prev_e->next_in_ael : actives_);
2793
}
2794
2795
void ClipperBase::Split(Active& e, const Point64& pt)
2796
{
2797
if (e.join_with == JoinWith::Right)
2798
{
2799
e.join_with = JoinWith::NoJoin;
2800
e.next_in_ael->join_with = JoinWith::NoJoin;
2801
AddLocalMinPoly(e, *e.next_in_ael, pt, true);
2802
}
2803
else
2804
{
2805
e.join_with = JoinWith::NoJoin;
2806
e.prev_in_ael->join_with = JoinWith::NoJoin;
2807
AddLocalMinPoly(*e.prev_in_ael, e, pt, true);
2808
}
2809
}
2810
2811
void ClipperBase::CheckJoinLeft(Active& e,
2812
const Point64& pt, bool check_curr_x)
2813
{
2814
Active* prev = e.prev_in_ael;
2815
if (!prev ||
2816
!IsHotEdge(e) || !IsHotEdge(*prev) ||
2817
IsHorizontal(e) || IsHorizontal(*prev) ||
2818
IsOpen(e) || IsOpen(*prev) ) return;
2819
if ((pt.y < e.top.y + 2 || pt.y < prev->top.y + 2) &&
2820
((e.bot.y > pt.y) || (prev->bot.y > pt.y))) return; // avoid trivial joins
2821
2822
if (check_curr_x)
2823
{
2824
if (PerpendicDistFromLineSqrd(pt, prev->bot, prev->top) > 0.25) return;
2825
}
2826
else if (e.curr_x != prev->curr_x) return;
2827
if (!IsCollinear(e.top, pt, prev->top)) return;
2828
2829
if (e.outrec->idx == prev->outrec->idx)
2830
AddLocalMaxPoly(*prev, e, pt);
2831
else if (e.outrec->idx < prev->outrec->idx)
2832
JoinOutrecPaths(e, *prev);
2833
else
2834
JoinOutrecPaths(*prev, e);
2835
prev->join_with = JoinWith::Right;
2836
e.join_with = JoinWith::Left;
2837
}
2838
2839
void ClipperBase::CheckJoinRight(Active& e,
2840
const Point64& pt, bool check_curr_x)
2841
{
2842
Active* next = e.next_in_ael;
2843
if (!next ||
2844
!IsHotEdge(e) || !IsHotEdge(*next) ||
2845
IsHorizontal(e) || IsHorizontal(*next) ||
2846
IsOpen(e) || IsOpen(*next)) return;
2847
if ((pt.y < e.top.y +2 || pt.y < next->top.y +2) &&
2848
((e.bot.y > pt.y) || (next->bot.y > pt.y))) return; // avoid trivial joins
2849
2850
if (check_curr_x)
2851
{
2852
if (PerpendicDistFromLineSqrd(pt, next->bot, next->top) > 0.35) return;
2853
}
2854
else if (e.curr_x != next->curr_x) return;
2855
if (!IsCollinear(e.top, pt, next->top)) return;
2856
2857
if (e.outrec->idx == next->outrec->idx)
2858
AddLocalMaxPoly(e, *next, pt);
2859
else if (e.outrec->idx < next->outrec->idx)
2860
JoinOutrecPaths(e, *next);
2861
else
2862
JoinOutrecPaths(*next, e);
2863
2864
e.join_with = JoinWith::Right;
2865
next->join_with = JoinWith::Left;
2866
}
2867
2868
inline bool GetHorzExtendedHorzSeg(OutPt*& op, OutPt*& op2)
2869
{
2870
OutRec* outrec = GetRealOutRec(op->outrec);
2871
op2 = op;
2872
if (outrec->front_edge)
2873
{
2874
while (op->prev != outrec->pts &&
2875
op->prev->pt.y == op->pt.y) op = op->prev;
2876
while (op2 != outrec->pts &&
2877
op2->next->pt.y == op2->pt.y) op2 = op2->next;
2878
return op2 != op;
2879
}
2880
else
2881
{
2882
while (op->prev != op2 && op->prev->pt.y == op->pt.y)
2883
op = op->prev;
2884
while (op2->next != op && op2->next->pt.y == op2->pt.y)
2885
op2 = op2->next;
2886
return op2 != op && op2->next != op;
2887
}
2888
}
2889
2890
bool BuildPath64(OutPt* op, bool reverse, bool isOpen, Path64& path)
2891
{
2892
if (!op || op->next == op || (!isOpen && op->next == op->prev))
2893
return false;
2894
2895
path.resize(0);
2896
Point64 lastPt;
2897
OutPt* op2;
2898
if (reverse)
2899
{
2900
lastPt = op->pt;
2901
op2 = op->prev;
2902
}
2903
else
2904
{
2905
op = op->next;
2906
lastPt = op->pt;
2907
op2 = op->next;
2908
}
2909
path.emplace_back(lastPt);
2910
2911
while (op2 != op)
2912
{
2913
if (op2->pt != lastPt)
2914
{
2915
lastPt = op2->pt;
2916
path.emplace_back(lastPt);
2917
}
2918
if (reverse)
2919
op2 = op2->prev;
2920
else
2921
op2 = op2->next;
2922
}
2923
2924
if (!isOpen && path.size() == 3 && IsVerySmallTriangle(*op2)) return false;
2925
else return true;
2926
}
2927
2928
bool ClipperBase::CheckBounds(OutRec* outrec)
2929
{
2930
if (!outrec->pts) return false;
2931
if (!outrec->bounds.IsEmpty()) return true;
2932
CleanCollinear(outrec);
2933
if (!outrec->pts ||
2934
!BuildPath64(outrec->pts, reverse_solution_, false, outrec->path)){
2935
return false;}
2936
outrec->bounds = GetBounds(outrec->path);
2937
return true;
2938
}
2939
2940
bool ClipperBase::CheckSplitOwner(OutRec* outrec, OutRecList* splits)
2941
{
2942
for (auto split : *splits)
2943
{
2944
if (!split->pts && split->splits &&
2945
CheckSplitOwner(outrec, split->splits)) return true; //#942
2946
split = GetRealOutRec(split);
2947
if (!split || split == outrec || split->recursive_split == outrec) continue;
2948
split->recursive_split = outrec; // prevent infinite loops
2949
2950
if (split->splits && CheckSplitOwner(outrec, split->splits))
2951
return true;
2952
2953
if (!CheckBounds(split) || !split->bounds.Contains(outrec->bounds) ||
2954
!Path2ContainsPath1(outrec->pts, split->pts)) continue;
2955
2956
if (!IsValidOwner(outrec, split)) // split is owned by outrec! (#957)
2957
split->owner = outrec->owner;
2958
2959
outrec->owner = split;
2960
return true;
2961
2962
}
2963
return false;
2964
}
2965
2966
void ClipperBase::RecursiveCheckOwners(OutRec* outrec, PolyPath* polypath)
2967
{
2968
// pre-condition: outrec will have valid bounds
2969
// post-condition: if a valid path, outrec will have a polypath
2970
2971
if (outrec->polypath || outrec->bounds.IsEmpty()) return;
2972
2973
while (outrec->owner)
2974
{
2975
if (outrec->owner->splits && CheckSplitOwner(outrec, outrec->owner->splits)) break;
2976
if (outrec->owner->pts && CheckBounds(outrec->owner) &&
2977
outrec->owner->bounds.Contains(outrec->bounds) &&
2978
Path2ContainsPath1(outrec->pts, outrec->owner->pts)) break;
2979
outrec->owner = outrec->owner->owner;
2980
}
2981
2982
if (outrec->owner)
2983
{
2984
if (!outrec->owner->polypath)
2985
RecursiveCheckOwners(outrec->owner, polypath);
2986
outrec->polypath = outrec->owner->polypath->AddChild(outrec->path);
2987
}
2988
else
2989
outrec->polypath = polypath->AddChild(outrec->path);
2990
}
2991
2992
void Clipper64::BuildPaths64(Paths64& solutionClosed, Paths64* solutionOpen)
2993
{
2994
solutionClosed.resize(0);
2995
solutionClosed.reserve(outrec_list_.size());
2996
if (solutionOpen)
2997
{
2998
solutionOpen->resize(0);
2999
solutionOpen->reserve(outrec_list_.size());
3000
}
3001
3002
// nb: outrec_list_.size() may change in the following
3003
// while loop because polygons may be split during
3004
// calls to CleanCollinear which calls FixSelfIntersects
3005
for (size_t i = 0; i < outrec_list_.size(); ++i)
3006
{
3007
OutRec* outrec = outrec_list_[i];
3008
if (outrec->pts == nullptr) continue;
3009
3010
Path64 path;
3011
if (solutionOpen && outrec->is_open)
3012
{
3013
if (BuildPath64(outrec->pts, reverse_solution_, true, path))
3014
solutionOpen->emplace_back(std::move(path));
3015
}
3016
else
3017
{
3018
// nb: CleanCollinear can add to outrec_list_
3019
CleanCollinear(outrec);
3020
//closed paths should always return a Positive orientation
3021
if (BuildPath64(outrec->pts, reverse_solution_, false, path))
3022
solutionClosed.emplace_back(std::move(path));
3023
}
3024
}
3025
}
3026
3027
void Clipper64::BuildTree64(PolyPath64& polytree, Paths64& open_paths)
3028
{
3029
polytree.Clear();
3030
open_paths.resize(0);
3031
if (has_open_paths_)
3032
open_paths.reserve(outrec_list_.size());
3033
3034
// outrec_list_.size() is not static here because
3035
// CheckBounds below can indirectly add additional
3036
// OutRec (via FixOutRecPts & CleanCollinear)
3037
for (size_t i = 0; i < outrec_list_.size(); ++i)
3038
{
3039
OutRec* outrec = outrec_list_[i];
3040
if (!outrec || !outrec->pts) continue;
3041
3042
if (outrec->is_open)
3043
{
3044
Path64 path;
3045
if (BuildPath64(outrec->pts, reverse_solution_, true, path))
3046
open_paths.emplace_back(std::move(path));
3047
continue;
3048
}
3049
3050
if (CheckBounds(outrec))
3051
RecursiveCheckOwners(outrec, &polytree);
3052
}
3053
}
3054
3055
bool BuildPathD(OutPt* op, bool reverse, bool isOpen, PathD& path, double inv_scale)
3056
{
3057
if (!op || op->next == op || (!isOpen && op->next == op->prev))
3058
return false;
3059
3060
path.resize(0);
3061
Point64 lastPt;
3062
OutPt* op2;
3063
if (reverse)
3064
{
3065
lastPt = op->pt;
3066
op2 = op->prev;
3067
}
3068
else
3069
{
3070
op = op->next;
3071
lastPt = op->pt;
3072
op2 = op->next;
3073
}
3074
#ifdef USINGZ
3075
path.emplace_back(lastPt.x * inv_scale, lastPt.y * inv_scale, lastPt.z);
3076
#else
3077
path.emplace_back(lastPt.x * inv_scale, lastPt.y * inv_scale);
3078
#endif
3079
3080
while (op2 != op)
3081
{
3082
if (op2->pt != lastPt)
3083
{
3084
lastPt = op2->pt;
3085
#ifdef USINGZ
3086
path.emplace_back(lastPt.x * inv_scale, lastPt.y * inv_scale, lastPt.z);
3087
#else
3088
path.emplace_back(lastPt.x * inv_scale, lastPt.y * inv_scale);
3089
#endif
3090
3091
}
3092
if (reverse)
3093
op2 = op2->prev;
3094
else
3095
op2 = op2->next;
3096
}
3097
if (path.size() == 3 && IsVerySmallTriangle(*op2)) return false;
3098
return true;
3099
}
3100
3101
void ClipperD::BuildPathsD(PathsD& solutionClosed, PathsD* solutionOpen)
3102
{
3103
solutionClosed.resize(0);
3104
solutionClosed.reserve(outrec_list_.size());
3105
if (solutionOpen)
3106
{
3107
solutionOpen->resize(0);
3108
solutionOpen->reserve(outrec_list_.size());
3109
}
3110
3111
// outrec_list_.size() is not static here because
3112
// CleanCollinear below can indirectly add additional
3113
// OutRec (via FixOutRecPts)
3114
for (std::size_t i = 0; i < outrec_list_.size(); ++i)
3115
{
3116
OutRec* outrec = outrec_list_[i];
3117
if (outrec->pts == nullptr) continue;
3118
3119
PathD path;
3120
if (solutionOpen && outrec->is_open)
3121
{
3122
if (BuildPathD(outrec->pts, reverse_solution_, true, path, invScale_))
3123
solutionOpen->emplace_back(std::move(path));
3124
}
3125
else
3126
{
3127
CleanCollinear(outrec);
3128
//closed paths should always return a Positive orientation
3129
if (BuildPathD(outrec->pts, reverse_solution_, false, path, invScale_))
3130
solutionClosed.emplace_back(std::move(path));
3131
}
3132
}
3133
}
3134
3135
void ClipperD::BuildTreeD(PolyPathD& polytree, PathsD& open_paths)
3136
{
3137
polytree.Clear();
3138
open_paths.resize(0);
3139
if (has_open_paths_)
3140
open_paths.reserve(outrec_list_.size());
3141
3142
// outrec_list_.size() is not static here because
3143
// BuildPathD below can indirectly add additional OutRec //#607
3144
for (size_t i = 0; i < outrec_list_.size(); ++i)
3145
{
3146
OutRec* outrec = outrec_list_[i];
3147
if (!outrec || !outrec->pts) continue;
3148
if (outrec->is_open)
3149
{
3150
PathD path;
3151
if (BuildPathD(outrec->pts, reverse_solution_, true, path, invScale_))
3152
open_paths.emplace_back(std::move(path));
3153
continue;
3154
}
3155
3156
if (CheckBounds(outrec))
3157
RecursiveCheckOwners(outrec, &polytree);
3158
}
3159
}
3160
3161
} // namespace clipper2lib
3162
3163