clipper2 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +165 -0
- data/examples/larb_transform.rb +17 -0
- data/examples/rugl_mesh.rb +33 -0
- data/examples/svg_boolean.rb +28 -0
- data/ext/clipper2/CMakeLists.txt +27 -0
- data/ext/clipper2/clipper2_native.cpp +2 -0
- data/ext/clipper2/extconf.rb +27 -0
- data/ext/clipper2/generate_bindings.rb +73 -0
- data/ext/clipper2/memory_check.cpp +48 -0
- data/ext/clipper2/vendor/LICENSE +23 -0
- data/ext/clipper2/vendor/UPSTREAM.md +11 -0
- data/ext/clipper2/vendor/include/clipper2/clipper.core.h +1159 -0
- data/ext/clipper2/vendor/include/clipper2/clipper.engine.h +635 -0
- data/ext/clipper2/vendor/include/clipper2/clipper.export.h +851 -0
- data/ext/clipper2/vendor/include/clipper2/clipper.h +796 -0
- data/ext/clipper2/vendor/include/clipper2/clipper.minkowski.h +117 -0
- data/ext/clipper2/vendor/include/clipper2/clipper.offset.h +125 -0
- data/ext/clipper2/vendor/include/clipper2/clipper.rectclip.h +80 -0
- data/ext/clipper2/vendor/include/clipper2/clipper.triangulation.h +27 -0
- data/ext/clipper2/vendor/include/clipper2/clipper.version.h +6 -0
- data/ext/clipper2/vendor/src/clipper.engine.cpp +3152 -0
- data/ext/clipper2/vendor/src/clipper.offset.cpp +661 -0
- data/ext/clipper2/vendor/src/clipper.rectclip.cpp +1027 -0
- data/ext/clipper2/vendor/src/clipper.triangulation.cpp +1221 -0
- data/lib/clipper2/api.rb +45 -0
- data/lib/clipper2/c_paths64.rb +80 -0
- data/lib/clipper2/earcut/engine.rb +191 -0
- data/lib/clipper2/earcut/geometry.rb +100 -0
- data/lib/clipper2/earcut/node.rb +18 -0
- data/lib/clipper2/earcut/ring.rb +94 -0
- data/lib/clipper2/earcut.rb +18 -0
- data/lib/clipper2/errors.rb +19 -0
- data/lib/clipper2/generated.rb +62 -0
- data/lib/clipper2/native.rb +38 -0
- data/lib/clipper2/native_memory.rb +64 -0
- data/lib/clipper2/operations.rb +206 -0
- data/lib/clipper2/path.rb +171 -0
- data/lib/clipper2/path_simplifier.rb +61 -0
- data/lib/clipper2/paths.rb +130 -0
- data/lib/clipper2/quantizer.rb +48 -0
- data/lib/clipper2/triangulation.rb +103 -0
- data/lib/clipper2/version.rb +5 -0
- data/lib/clipper2.rb +34 -0
- data/licenses/CLIPPER2-BSL-1.0.txt +23 -0
- data/licenses/EARCUT-ISC.txt +15 -0
- metadata +113 -0
|
@@ -0,0 +1,1027 @@
|
|
|
1
|
+
/*******************************************************************************
|
|
2
|
+
* Author : Angus Johnson *
|
|
3
|
+
* Date : 11 October 2025 *
|
|
4
|
+
* Website : https://www.angusj.com *
|
|
5
|
+
* Copyright : Angus Johnson 2010-2025 *
|
|
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
|
+
int res1 = CrossProductSign(p1, p3, p4);
|
|
77
|
+
int res2 = CrossProductSign(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
|
+
int res3 = CrossProductSign(p3, p1, p2);
|
|
97
|
+
int res4 = CrossProductSign(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 GetLineIntersectPt(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 CrossProductSign(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
|