Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/clipper2/src/clipper.rectclip.cpp
9902 views
1
/*******************************************************************************
2
* Author : Angus Johnson *
3
* Date : 5 July 2024 *
4
* Website : https://www.angusj.com *
5
* Copyright : Angus Johnson 2010-2024 *
6
* Purpose : FAST rectangular clipping *
7
* License : https://www.boost.org/LICENSE_1_0.txt *
8
*******************************************************************************/
9
10
#include "clipper2/clipper.h"
11
#include "clipper2/clipper.rectclip.h"
12
13
namespace Clipper2Lib {
14
15
//------------------------------------------------------------------------------
16
// Miscellaneous methods
17
//------------------------------------------------------------------------------
18
19
inline bool Path1ContainsPath2(const Path64& path1, const Path64& path2)
20
{
21
int io_count = 0;
22
// precondition: no (significant) overlap
23
for (const Point64& pt : path2)
24
{
25
PointInPolygonResult pip = PointInPolygon(pt, path1);
26
switch (pip)
27
{
28
case PointInPolygonResult::IsOutside: ++io_count; break;
29
case PointInPolygonResult::IsInside: --io_count; break;
30
default: continue;
31
}
32
if (std::abs(io_count) > 1) break;
33
}
34
return io_count <= 0;
35
}
36
37
inline bool GetLocation(const Rect64& rec,
38
const Point64& pt, Location& loc)
39
{
40
if (pt.x == rec.left && pt.y >= rec.top && pt.y <= rec.bottom)
41
{
42
loc = Location::Left;
43
return false;
44
}
45
else if (pt.x == rec.right && pt.y >= rec.top && pt.y <= rec.bottom)
46
{
47
loc = Location::Right;
48
return false;
49
}
50
else if (pt.y == rec.top && pt.x >= rec.left && pt.x <= rec.right)
51
{
52
loc = Location::Top;
53
return false;
54
}
55
else if (pt.y == rec.bottom && pt.x >= rec.left && pt.x <= rec.right)
56
{
57
loc = Location::Bottom;
58
return false;
59
}
60
else if (pt.x < rec.left) loc = Location::Left;
61
else if (pt.x > rec.right) loc = Location::Right;
62
else if (pt.y < rec.top) loc = Location::Top;
63
else if (pt.y > rec.bottom) loc = Location::Bottom;
64
else loc = Location::Inside;
65
return true;
66
}
67
68
inline bool IsHorizontal(const Point64& pt1, const Point64& pt2)
69
{
70
return pt1.y == pt2.y;
71
}
72
73
bool GetSegmentIntersection(const Point64& p1,
74
const Point64& p2, const Point64& p3, const Point64& p4, Point64& ip)
75
{
76
double res1 = CrossProduct(p1, p3, p4);
77
double res2 = CrossProduct(p2, p3, p4);
78
if (res1 == 0)
79
{
80
ip = p1;
81
if (res2 == 0) return false; // segments are collinear
82
else if (p1 == p3 || p1 == p4) return true;
83
//else if (p2 == p3 || p2 == p4) { ip = p2; return true; }
84
else if (IsHorizontal(p3, p4)) return ((p1.x > p3.x) == (p1.x < p4.x));
85
else return ((p1.y > p3.y) == (p1.y < p4.y));
86
}
87
else if (res2 == 0)
88
{
89
ip = p2;
90
if (p2 == p3 || p2 == p4) return true;
91
else if (IsHorizontal(p3, p4)) return ((p2.x > p3.x) == (p2.x < p4.x));
92
else return ((p2.y > p3.y) == (p2.y < p4.y));
93
}
94
if ((res1 > 0) == (res2 > 0)) return false;
95
96
double res3 = CrossProduct(p3, p1, p2);
97
double res4 = CrossProduct(p4, p1, p2);
98
if (res3 == 0)
99
{
100
ip = p3;
101
if (p3 == p1 || p3 == p2) return true;
102
else if (IsHorizontal(p1, p2)) return ((p3.x > p1.x) == (p3.x < p2.x));
103
else return ((p3.y > p1.y) == (p3.y < p2.y));
104
}
105
else if (res4 == 0)
106
{
107
ip = p4;
108
if (p4 == p1 || p4 == p2) return true;
109
else if (IsHorizontal(p1, p2)) return ((p4.x > p1.x) == (p4.x < p2.x));
110
else return ((p4.y > p1.y) == (p4.y < p2.y));
111
}
112
if ((res3 > 0) == (res4 > 0)) return false;
113
114
// segments must intersect to get here
115
return GetSegmentIntersectPt(p1, p2, p3, p4, ip);
116
}
117
118
inline bool GetIntersection(const Path64& rectPath,
119
const Point64& p, const Point64& p2, Location& loc, Point64& ip)
120
{
121
// gets the intersection closest to 'p'
122
// when Result = false, loc will remain unchanged
123
switch (loc)
124
{
125
case Location::Left:
126
if (GetSegmentIntersection(p, p2, rectPath[0], rectPath[3], ip)) return true;
127
else if ((p.y < rectPath[0].y) && GetSegmentIntersection(p, p2, rectPath[0], rectPath[1], ip))
128
{
129
loc = Location::Top;
130
return true;
131
}
132
else if (GetSegmentIntersection(p, p2, rectPath[2], rectPath[3], ip))
133
{
134
loc = Location::Bottom;
135
return true;
136
}
137
else return false;
138
139
case Location::Top:
140
if (GetSegmentIntersection(p, p2, rectPath[0], rectPath[1], ip)) return true;
141
else if ((p.x < rectPath[0].x) && GetSegmentIntersection(p, p2, rectPath[0], rectPath[3], ip))
142
{
143
loc = Location::Left;
144
return true;
145
}
146
else if (GetSegmentIntersection(p, p2, rectPath[1], rectPath[2], ip))
147
{
148
loc = Location::Right;
149
return true;
150
}
151
else return false;
152
153
case Location::Right:
154
if (GetSegmentIntersection(p, p2, rectPath[1], rectPath[2], ip)) return true;
155
else if ((p.y < rectPath[1].y) && GetSegmentIntersection(p, p2, rectPath[0], rectPath[1], ip))
156
{
157
loc = Location::Top;
158
return true;
159
}
160
else if (GetSegmentIntersection(p, p2, rectPath[2], rectPath[3], ip))
161
{
162
loc = Location::Bottom;
163
return true;
164
}
165
else return false;
166
167
case Location::Bottom:
168
if (GetSegmentIntersection(p, p2, rectPath[2], rectPath[3], ip)) return true;
169
else if ((p.x < rectPath[3].x) && GetSegmentIntersection(p, p2, rectPath[0], rectPath[3], ip))
170
{
171
loc = Location::Left;
172
return true;
173
}
174
else if (GetSegmentIntersection(p, p2, rectPath[1], rectPath[2], ip))
175
{
176
loc = Location::Right;
177
return true;
178
}
179
else return false;
180
181
default: // loc == rInside
182
if (GetSegmentIntersection(p, p2, rectPath[0], rectPath[3], ip))
183
{
184
loc = Location::Left;
185
return true;
186
}
187
else if (GetSegmentIntersection(p, p2, rectPath[0], rectPath[1], ip))
188
{
189
loc = Location::Top;
190
return true;
191
}
192
else if (GetSegmentIntersection(p, p2, rectPath[1], rectPath[2], ip))
193
{
194
loc = Location::Right;
195
return true;
196
}
197
else if (GetSegmentIntersection(p, p2, rectPath[2], rectPath[3], ip))
198
{
199
loc = Location::Bottom;
200
return true;
201
}
202
else return false;
203
}
204
}
205
206
inline Location GetAdjacentLocation(Location loc, bool isClockwise)
207
{
208
int delta = (isClockwise) ? 1 : 3;
209
return static_cast<Location>((static_cast<int>(loc) + delta) % 4);
210
}
211
212
inline bool HeadingClockwise(Location prev, Location curr)
213
{
214
return (static_cast<int>(prev) + 1) % 4 == static_cast<int>(curr);
215
}
216
217
inline bool AreOpposites(Location prev, Location curr)
218
{
219
return abs(static_cast<int>(prev) - static_cast<int>(curr)) == 2;
220
}
221
222
inline bool IsClockwise(Location prev, Location curr,
223
const Point64& prev_pt, const Point64& curr_pt, const Point64& rect_mp)
224
{
225
if (AreOpposites(prev, curr))
226
return CrossProduct(prev_pt, rect_mp, curr_pt) < 0;
227
else
228
return HeadingClockwise(prev, curr);
229
}
230
231
inline OutPt2* UnlinkOp(OutPt2* op)
232
{
233
if (op->next == op) return nullptr;
234
op->prev->next = op->next;
235
op->next->prev = op->prev;
236
return op->next;
237
}
238
239
inline OutPt2* UnlinkOpBack(OutPt2* op)
240
{
241
if (op->next == op) return nullptr;
242
op->prev->next = op->next;
243
op->next->prev = op->prev;
244
return op->prev;
245
}
246
247
inline uint32_t GetEdgesForPt(const Point64& pt, const Rect64& rec)
248
{
249
uint32_t result = 0;
250
if (pt.x == rec.left) result = 1;
251
else if (pt.x == rec.right) result = 4;
252
if (pt.y == rec.top) result += 2;
253
else if (pt.y == rec.bottom) result += 8;
254
return result;
255
}
256
257
inline bool IsHeadingClockwise(const Point64& pt1, const Point64& pt2, int edgeIdx)
258
{
259
switch (edgeIdx)
260
{
261
case 0: return pt2.y < pt1.y;
262
case 1: return pt2.x > pt1.x;
263
case 2: return pt2.y > pt1.y;
264
default: return pt2.x < pt1.x;
265
}
266
}
267
268
inline bool HasHorzOverlap(const Point64& left1, const Point64& right1,
269
const Point64& left2, const Point64& right2)
270
{
271
return (left1.x < right2.x) && (right1.x > left2.x);
272
}
273
274
inline bool HasVertOverlap(const Point64& top1, const Point64& bottom1,
275
const Point64& top2, const Point64& bottom2)
276
{
277
return (top1.y < bottom2.y) && (bottom1.y > top2.y);
278
}
279
280
inline void AddToEdge(OutPt2List& edge, OutPt2* op)
281
{
282
if (op->edge) return;
283
op->edge = &edge;
284
edge.emplace_back(op);
285
}
286
287
inline void UncoupleEdge(OutPt2* op)
288
{
289
if (!op->edge) return;
290
for (size_t i = 0; i < op->edge->size(); ++i)
291
{
292
OutPt2* op2 = (*op->edge)[i];
293
if (op2 == op)
294
{
295
(*op->edge)[i] = nullptr;
296
break;
297
}
298
}
299
op->edge = nullptr;
300
}
301
302
inline void SetNewOwner(OutPt2* op, size_t new_idx)
303
{
304
op->owner_idx = new_idx;
305
OutPt2* op2 = op->next;
306
while (op2 != op)
307
{
308
op2->owner_idx = new_idx;
309
op2 = op2->next;
310
}
311
}
312
313
//----------------------------------------------------------------------------
314
// RectClip64
315
//----------------------------------------------------------------------------
316
317
OutPt2* RectClip64::Add(Point64 pt, bool start_new)
318
{
319
// this method is only called by InternalExecute.
320
// Later splitting & rejoining won't create additional op's,
321
// though they will change the (non-storage) results_ count.
322
size_t curr_idx = results_.size();
323
OutPt2* result;
324
if (curr_idx == 0 || start_new)
325
{
326
result = &op_container_.emplace_back(OutPt2());
327
result->pt = pt;
328
result->next = result;
329
result->prev = result;
330
results_.emplace_back(result);
331
}
332
else
333
{
334
--curr_idx;
335
OutPt2* prevOp = results_[curr_idx];
336
if (prevOp->pt == pt) return prevOp;
337
result = &op_container_.emplace_back(OutPt2());
338
result->owner_idx = curr_idx;
339
result->pt = pt;
340
result->next = prevOp->next;
341
prevOp->next->prev = result;
342
prevOp->next = result;
343
result->prev = prevOp;
344
results_[curr_idx] = result;
345
}
346
return result;
347
}
348
349
void RectClip64::AddCorner(Location prev, Location curr)
350
{
351
if (HeadingClockwise(prev, curr))
352
Add(rect_as_path_[static_cast<size_t>(prev)]);
353
else
354
Add(rect_as_path_[static_cast<size_t>(curr)]);
355
}
356
357
void RectClip64::AddCorner(Location& loc, bool isClockwise)
358
{
359
if (isClockwise)
360
{
361
Add(rect_as_path_[static_cast<size_t>(loc)]);
362
loc = GetAdjacentLocation(loc, true);
363
}
364
else
365
{
366
loc = GetAdjacentLocation(loc, false);
367
Add(rect_as_path_[static_cast<size_t>(loc)]);
368
}
369
}
370
371
void RectClip64::GetNextLocation(const Path64& path,
372
Location& loc, size_t& i, size_t highI)
373
{
374
switch (loc)
375
{
376
case Location::Left:
377
while (i <= highI && path[i].x <= rect_.left) ++i;
378
if (i > highI) break;
379
else if (path[i].x >= rect_.right) loc = Location::Right;
380
else if (path[i].y <= rect_.top) loc = Location::Top;
381
else if (path[i].y >= rect_.bottom) loc = Location::Bottom;
382
else loc = Location::Inside;
383
break;
384
385
case Location::Top:
386
while (i <= highI && path[i].y <= rect_.top) ++i;
387
if (i > highI) break;
388
else if (path[i].y >= rect_.bottom) loc = Location::Bottom;
389
else if (path[i].x <= rect_.left) loc = Location::Left;
390
else if (path[i].x >= rect_.right) loc = Location::Right;
391
else loc = Location::Inside;
392
break;
393
394
case Location::Right:
395
while (i <= highI && path[i].x >= rect_.right) ++i;
396
if (i > highI) break;
397
else if (path[i].x <= rect_.left) loc = Location::Left;
398
else if (path[i].y <= rect_.top) loc = Location::Top;
399
else if (path[i].y >= rect_.bottom) loc = Location::Bottom;
400
else loc = Location::Inside;
401
break;
402
403
case Location::Bottom:
404
while (i <= highI && path[i].y >= rect_.bottom) ++i;
405
if (i > highI) break;
406
else if (path[i].y <= rect_.top) loc = Location::Top;
407
else if (path[i].x <= rect_.left) loc = Location::Left;
408
else if (path[i].x >= rect_.right) loc = Location::Right;
409
else loc = Location::Inside;
410
break;
411
412
case Location::Inside:
413
while (i <= highI)
414
{
415
if (path[i].x < rect_.left) loc = Location::Left;
416
else if (path[i].x > rect_.right) loc = Location::Right;
417
else if (path[i].y > rect_.bottom) loc = Location::Bottom;
418
else if (path[i].y < rect_.top) loc = Location::Top;
419
else { Add(path[i]); ++i; continue; }
420
break; //inner loop
421
}
422
break;
423
} //switch
424
}
425
426
bool StartLocsAreClockwise(const std::vector<Location>& startlocs)
427
{
428
int result = 0;
429
for (size_t i = 1; i < startlocs.size(); ++i)
430
{
431
int d = static_cast<int>(startlocs[i]) - static_cast<int>(startlocs[i - 1]);
432
switch (d)
433
{
434
case -1: result -= 1; break;
435
case 1: result += 1; break;
436
case -3: result += 1; break;
437
case 3: result -= 1; break;
438
}
439
}
440
return result > 0;
441
}
442
443
void RectClip64::ExecuteInternal(const Path64& path)
444
{
445
if (path.size() < 1)
446
return;
447
448
size_t highI = path.size() - 1;
449
Location prev = Location::Inside, loc;
450
Location crossing_loc = Location::Inside;
451
Location first_cross_ = Location::Inside;
452
if (!GetLocation(rect_, path[highI], loc))
453
{
454
size_t i = highI;
455
while (i > 0 && !GetLocation(rect_, path[i - 1], prev))
456
--i;
457
if (i == 0)
458
{
459
// all of path must be inside fRect
460
for (const auto& pt : path) Add(pt);
461
return;
462
}
463
if (prev == Location::Inside) loc = Location::Inside;
464
}
465
Location starting_loc = loc;
466
467
///////////////////////////////////////////////////
468
size_t i = 0;
469
while (i <= highI)
470
{
471
prev = loc;
472
Location crossing_prev = crossing_loc;
473
474
GetNextLocation(path, loc, i, highI);
475
476
if (i > highI) break;
477
Point64 ip, ip2;
478
Point64 prev_pt = (i) ?
479
path[static_cast<size_t>(i - 1)] :
480
path[highI];
481
482
crossing_loc = loc;
483
if (!GetIntersection(rect_as_path_,
484
path[i], prev_pt, crossing_loc, ip))
485
{
486
// ie remaining outside
487
if (crossing_prev == Location::Inside)
488
{
489
bool isClockw = IsClockwise(prev, loc, prev_pt, path[i], rect_mp_);
490
do {
491
start_locs_.emplace_back(prev);
492
prev = GetAdjacentLocation(prev, isClockw);
493
} while (prev != loc);
494
crossing_loc = crossing_prev; // still not crossed
495
}
496
else if (prev != Location::Inside && prev != loc)
497
{
498
bool isClockw = IsClockwise(prev, loc, prev_pt, path[i], rect_mp_);
499
do {
500
AddCorner(prev, isClockw);
501
} while (prev != loc);
502
}
503
++i;
504
continue;
505
}
506
507
////////////////////////////////////////////////////
508
// we must be crossing the rect boundary to get here
509
////////////////////////////////////////////////////
510
511
if (loc == Location::Inside) // path must be entering rect
512
{
513
if (first_cross_ == Location::Inside)
514
{
515
first_cross_ = crossing_loc;
516
start_locs_.emplace_back(prev);
517
}
518
else if (prev != crossing_loc)
519
{
520
bool isClockw = IsClockwise(prev, crossing_loc, prev_pt, path[i], rect_mp_);
521
do {
522
AddCorner(prev, isClockw);
523
} while (prev != crossing_loc);
524
}
525
}
526
else if (prev != Location::Inside)
527
{
528
// passing right through rect. 'ip' here will be the second
529
// intersect pt but we'll also need the first intersect pt (ip2)
530
loc = prev;
531
GetIntersection(rect_as_path_, prev_pt, path[i], loc, ip2);
532
if (crossing_prev != Location::Inside && crossing_prev != loc) //579
533
AddCorner(crossing_prev, loc);
534
535
if (first_cross_ == Location::Inside)
536
{
537
first_cross_ = loc;
538
start_locs_.emplace_back(prev);
539
}
540
541
loc = crossing_loc;
542
Add(ip2);
543
if (ip == ip2)
544
{
545
// it's very likely that path[i] is on rect
546
GetLocation(rect_, path[i], loc);
547
AddCorner(crossing_loc, loc);
548
crossing_loc = loc;
549
continue;
550
}
551
}
552
else // path must be exiting rect
553
{
554
loc = crossing_loc;
555
if (first_cross_ == Location::Inside)
556
first_cross_ = crossing_loc;
557
}
558
559
Add(ip);
560
561
} //while i <= highI
562
///////////////////////////////////////////////////
563
564
if (first_cross_ == Location::Inside)
565
{
566
// path never intersects
567
if (starting_loc != Location::Inside)
568
{
569
// path is outside rect
570
// but being outside, it still may not contain rect
571
if (path_bounds_.Contains(rect_) &&
572
Path1ContainsPath2(path, rect_as_path_))
573
{
574
// yep, the path does fully contain rect
575
// so add rect to the solution
576
bool is_clockwise_path = StartLocsAreClockwise(start_locs_);
577
for (size_t j = 0; j < 4; ++j)
578
{
579
size_t k = is_clockwise_path ? j : 3 - j; // reverses result path
580
Add(rect_as_path_[k]);
581
// we may well need to do some splitting later, so
582
AddToEdge(edges_[k * 2], results_[0]);
583
}
584
}
585
}
586
}
587
else if (loc != Location::Inside &&
588
(loc != first_cross_ || start_locs_.size() > 2))
589
{
590
if (start_locs_.size() > 0)
591
{
592
prev = loc;
593
for (auto loc2 : start_locs_)
594
{
595
if (prev == loc2) continue;
596
AddCorner(prev, HeadingClockwise(prev, loc2));
597
prev = loc2;
598
}
599
loc = prev;
600
}
601
if (loc != first_cross_)
602
AddCorner(loc, HeadingClockwise(loc, first_cross_));
603
}
604
}
605
606
void RectClip64::CheckEdges()
607
{
608
for (size_t i = 0; i < results_.size(); ++i)
609
{
610
OutPt2* op = results_[i];
611
if (!op) continue;
612
OutPt2* op2 = op;
613
do
614
{
615
if (IsCollinear(op2->prev->pt, op2->pt, op2->next->pt))
616
{
617
if (op2 == op)
618
{
619
op2 = UnlinkOpBack(op2);
620
if (!op2) break;
621
op = op2->prev;
622
}
623
else
624
{
625
op2 = UnlinkOpBack(op2);
626
if (!op2) break;
627
}
628
}
629
else
630
op2 = op2->next;
631
} while (op2 != op);
632
633
if (!op2)
634
{
635
results_[i] = nullptr;
636
continue;
637
}
638
results_[i] = op; // safety first
639
640
uint32_t edgeSet1 = GetEdgesForPt(op->prev->pt, rect_);
641
op2 = op;
642
do
643
{
644
uint32_t edgeSet2 = GetEdgesForPt(op2->pt, rect_);
645
if (edgeSet2 && !op2->edge)
646
{
647
uint32_t combinedSet = (edgeSet1 & edgeSet2);
648
for (int j = 0; j < 4; ++j)
649
{
650
if (combinedSet & (1 << j))
651
{
652
if (IsHeadingClockwise(op2->prev->pt, op2->pt, j))
653
AddToEdge(edges_[j * 2], op2);
654
else
655
AddToEdge(edges_[j * 2 + 1], op2);
656
}
657
}
658
}
659
edgeSet1 = edgeSet2;
660
op2 = op2->next;
661
} while (op2 != op);
662
}
663
}
664
665
void RectClip64::TidyEdges(size_t idx, OutPt2List& cw, OutPt2List& ccw)
666
{
667
if (ccw.empty()) return;
668
bool isHorz = ((idx == 1) || (idx == 3));
669
bool cwIsTowardLarger = ((idx == 1) || (idx == 2));
670
size_t i = 0, j = 0;
671
OutPt2* p1, * p2, * p1a, * p2a, * op, * op2;
672
673
while (i < cw.size())
674
{
675
p1 = cw[i];
676
if (!p1 || p1->next == p1->prev)
677
{
678
cw[i++] = nullptr;
679
j = 0;
680
continue;
681
}
682
683
size_t jLim = ccw.size();
684
while (j < jLim &&
685
(!ccw[j] || ccw[j]->next == ccw[j]->prev)) ++j;
686
687
if (j == jLim)
688
{
689
++i;
690
j = 0;
691
continue;
692
}
693
694
if (cwIsTowardLarger)
695
{
696
// p1 >>>> p1a;
697
// p2 <<<< p2a;
698
p1 = cw[i]->prev;
699
p1a = cw[i];
700
p2 = ccw[j];
701
p2a = ccw[j]->prev;
702
}
703
else
704
{
705
// p1 <<<< p1a;
706
// p2 >>>> p2a;
707
p1 = cw[i];
708
p1a = cw[i]->prev;
709
p2 = ccw[j]->prev;
710
p2a = ccw[j];
711
}
712
713
if ((isHorz && !HasHorzOverlap(p1->pt, p1a->pt, p2->pt, p2a->pt)) ||
714
(!isHorz && !HasVertOverlap(p1->pt, p1a->pt, p2->pt, p2a->pt)))
715
{
716
++j;
717
continue;
718
}
719
720
// to get here we're either splitting or rejoining
721
bool isRejoining = cw[i]->owner_idx != ccw[j]->owner_idx;
722
723
if (isRejoining)
724
{
725
results_[p2->owner_idx] = nullptr;
726
SetNewOwner(p2, p1->owner_idx);
727
}
728
729
// do the split or re-join
730
if (cwIsTowardLarger)
731
{
732
// p1 >> | >> p1a;
733
// p2 << | << p2a;
734
p1->next = p2;
735
p2->prev = p1;
736
p1a->prev = p2a;
737
p2a->next = p1a;
738
}
739
else
740
{
741
// p1 << | << p1a;
742
// p2 >> | >> p2a;
743
p1->prev = p2;
744
p2->next = p1;
745
p1a->next = p2a;
746
p2a->prev = p1a;
747
}
748
749
if (!isRejoining)
750
{
751
size_t new_idx = results_.size();
752
results_.emplace_back(p1a);
753
SetNewOwner(p1a, new_idx);
754
}
755
756
if (cwIsTowardLarger)
757
{
758
op = p2;
759
op2 = p1a;
760
}
761
else
762
{
763
op = p1;
764
op2 = p2a;
765
}
766
results_[op->owner_idx] = op;
767
results_[op2->owner_idx] = op2;
768
769
// and now lots of work to get ready for the next loop
770
771
bool opIsLarger, op2IsLarger;
772
if (isHorz) // X
773
{
774
opIsLarger = op->pt.x > op->prev->pt.x;
775
op2IsLarger = op2->pt.x > op2->prev->pt.x;
776
}
777
else // Y
778
{
779
opIsLarger = op->pt.y > op->prev->pt.y;
780
op2IsLarger = op2->pt.y > op2->prev->pt.y;
781
}
782
783
if ((op->next == op->prev) ||
784
(op->pt == op->prev->pt))
785
{
786
if (op2IsLarger == cwIsTowardLarger)
787
{
788
cw[i] = op2;
789
ccw[j++] = nullptr;
790
}
791
else
792
{
793
ccw[j] = op2;
794
cw[i++] = nullptr;
795
}
796
}
797
else if ((op2->next == op2->prev) ||
798
(op2->pt == op2->prev->pt))
799
{
800
if (opIsLarger == cwIsTowardLarger)
801
{
802
cw[i] = op;
803
ccw[j++] = nullptr;
804
}
805
else
806
{
807
ccw[j] = op;
808
cw[i++] = nullptr;
809
}
810
}
811
else if (opIsLarger == op2IsLarger)
812
{
813
if (opIsLarger == cwIsTowardLarger)
814
{
815
cw[i] = op;
816
UncoupleEdge(op2);
817
AddToEdge(cw, op2);
818
ccw[j++] = nullptr;
819
}
820
else
821
{
822
cw[i++] = nullptr;
823
ccw[j] = op2;
824
UncoupleEdge(op);
825
AddToEdge(ccw, op);
826
j = 0;
827
}
828
}
829
else
830
{
831
if (opIsLarger == cwIsTowardLarger)
832
cw[i] = op;
833
else
834
ccw[j] = op;
835
if (op2IsLarger == cwIsTowardLarger)
836
cw[i] = op2;
837
else
838
ccw[j] = op2;
839
}
840
}
841
}
842
843
Path64 RectClip64::GetPath(OutPt2*& op)
844
{
845
if (!op || op->next == op->prev) return Path64();
846
847
OutPt2* op2 = op->next;
848
while (op2 && op2 != op)
849
{
850
if (IsCollinear(op2->prev->pt,
851
op2->pt, op2->next->pt))
852
{
853
op = op2->prev;
854
op2 = UnlinkOp(op2);
855
}
856
else
857
op2 = op2->next;
858
}
859
op = op2; // needed for op cleanup
860
if (!op2) return Path64();
861
862
Path64 result;
863
result.emplace_back(op->pt);
864
op2 = op->next;
865
while (op2 != op)
866
{
867
result.emplace_back(op2->pt);
868
op2 = op2->next;
869
}
870
return result;
871
}
872
873
Paths64 RectClip64::Execute(const Paths64& paths)
874
{
875
Paths64 result;
876
if (rect_.IsEmpty()) return result;
877
878
for (const Path64& path : paths)
879
{
880
if (path.size() < 3) continue;
881
path_bounds_ = GetBounds(path);
882
if (!rect_.Intersects(path_bounds_))
883
continue; // the path must be completely outside rect_
884
else if (rect_.Contains(path_bounds_))
885
{
886
// the path must be completely inside rect_
887
result.emplace_back(path);
888
continue;
889
}
890
891
ExecuteInternal(path);
892
CheckEdges();
893
for (size_t i = 0; i < 4; ++i)
894
TidyEdges(i, edges_[i * 2], edges_[i * 2 + 1]);
895
896
for (OutPt2*& op : results_)
897
{
898
Path64 tmp = GetPath(op);
899
if (!tmp.empty())
900
result.emplace_back(std::move(tmp));
901
}
902
903
//clean up after every loop
904
op_container_ = std::deque<OutPt2>();
905
results_.clear();
906
for (OutPt2List &edge : edges_) edge.clear();
907
start_locs_.clear();
908
}
909
return result;
910
}
911
912
//------------------------------------------------------------------------------
913
// RectClipLines64
914
//------------------------------------------------------------------------------
915
916
Paths64 RectClipLines64::Execute(const Paths64& paths)
917
{
918
Paths64 result;
919
if (rect_.IsEmpty()) return result;
920
921
for (const auto& path : paths)
922
{
923
Rect64 pathrec = GetBounds(path);
924
if (!rect_.Intersects(pathrec)) continue;
925
926
ExecuteInternal(path);
927
928
for (OutPt2*& op : results_)
929
{
930
Path64 tmp = GetPath(op);
931
if (!tmp.empty())
932
result.emplace_back(std::move(tmp));
933
}
934
results_.clear();
935
936
op_container_ = std::deque<OutPt2>();
937
start_locs_.clear();
938
}
939
return result;
940
}
941
942
void RectClipLines64::ExecuteInternal(const Path64& path)
943
{
944
if (rect_.IsEmpty() || path.size() < 2) return;
945
946
results_.clear();
947
op_container_ = std::deque<OutPt2>();
948
start_locs_.clear();
949
950
size_t i = 1, highI = path.size() - 1;
951
952
Location prev = Location::Inside, loc;
953
Location crossing_loc;
954
if (!GetLocation(rect_, path[0], loc))
955
{
956
while (i <= highI && !GetLocation(rect_, path[i], prev)) ++i;
957
if (i > highI)
958
{
959
// all of path must be inside fRect
960
for (const auto& pt : path) Add(pt);
961
return;
962
}
963
if (prev == Location::Inside) loc = Location::Inside;
964
i = 1;
965
}
966
if (loc == Location::Inside) Add(path[0]);
967
968
///////////////////////////////////////////////////
969
while (i <= highI)
970
{
971
prev = loc;
972
GetNextLocation(path, loc, i, highI);
973
if (i > highI) break;
974
Point64 ip, ip2;
975
Point64 prev_pt = path[static_cast<size_t>(i - 1)];
976
977
crossing_loc = loc;
978
if (!GetIntersection(rect_as_path_,
979
path[i], prev_pt, crossing_loc, ip))
980
{
981
// ie remaining outside
982
++i;
983
continue;
984
}
985
986
////////////////////////////////////////////////////
987
// we must be crossing the rect boundary to get here
988
////////////////////////////////////////////////////
989
990
if (loc == Location::Inside) // path must be entering rect
991
{
992
Add(ip, true);
993
}
994
else if (prev != Location::Inside)
995
{
996
// passing right through rect. 'ip' here will be the second
997
// intersect pt but we'll also need the first intersect pt (ip2)
998
crossing_loc = prev;
999
GetIntersection(rect_as_path_,
1000
prev_pt, path[i], crossing_loc, ip2);
1001
Add(ip2, true);
1002
Add(ip);
1003
}
1004
else // path must be exiting rect
1005
{
1006
Add(ip);
1007
}
1008
} //while i <= highI
1009
///////////////////////////////////////////////////
1010
}
1011
1012
Path64 RectClipLines64::GetPath(OutPt2*& op)
1013
{
1014
Path64 result;
1015
if (!op || op == op->next) return result;
1016
op = op->next; // starting at path beginning
1017
result.emplace_back(op->pt);
1018
OutPt2 *op2 = op->next;
1019
while (op2 != op)
1020
{
1021
result.emplace_back(op2->pt);
1022
op2 = op2->next;
1023
}
1024
return result;
1025
}
1026
1027
} // namespace
1028
1029