clipper2 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +165 -0
  4. data/examples/larb_transform.rb +17 -0
  5. data/examples/rugl_mesh.rb +33 -0
  6. data/examples/svg_boolean.rb +28 -0
  7. data/ext/clipper2/CMakeLists.txt +27 -0
  8. data/ext/clipper2/clipper2_native.cpp +2 -0
  9. data/ext/clipper2/extconf.rb +27 -0
  10. data/ext/clipper2/generate_bindings.rb +73 -0
  11. data/ext/clipper2/memory_check.cpp +48 -0
  12. data/ext/clipper2/vendor/LICENSE +23 -0
  13. data/ext/clipper2/vendor/UPSTREAM.md +11 -0
  14. data/ext/clipper2/vendor/include/clipper2/clipper.core.h +1159 -0
  15. data/ext/clipper2/vendor/include/clipper2/clipper.engine.h +635 -0
  16. data/ext/clipper2/vendor/include/clipper2/clipper.export.h +851 -0
  17. data/ext/clipper2/vendor/include/clipper2/clipper.h +796 -0
  18. data/ext/clipper2/vendor/include/clipper2/clipper.minkowski.h +117 -0
  19. data/ext/clipper2/vendor/include/clipper2/clipper.offset.h +125 -0
  20. data/ext/clipper2/vendor/include/clipper2/clipper.rectclip.h +80 -0
  21. data/ext/clipper2/vendor/include/clipper2/clipper.triangulation.h +27 -0
  22. data/ext/clipper2/vendor/include/clipper2/clipper.version.h +6 -0
  23. data/ext/clipper2/vendor/src/clipper.engine.cpp +3152 -0
  24. data/ext/clipper2/vendor/src/clipper.offset.cpp +661 -0
  25. data/ext/clipper2/vendor/src/clipper.rectclip.cpp +1027 -0
  26. data/ext/clipper2/vendor/src/clipper.triangulation.cpp +1221 -0
  27. data/lib/clipper2/api.rb +45 -0
  28. data/lib/clipper2/c_paths64.rb +80 -0
  29. data/lib/clipper2/earcut/engine.rb +191 -0
  30. data/lib/clipper2/earcut/geometry.rb +100 -0
  31. data/lib/clipper2/earcut/node.rb +18 -0
  32. data/lib/clipper2/earcut/ring.rb +94 -0
  33. data/lib/clipper2/earcut.rb +18 -0
  34. data/lib/clipper2/errors.rb +19 -0
  35. data/lib/clipper2/generated.rb +62 -0
  36. data/lib/clipper2/native.rb +38 -0
  37. data/lib/clipper2/native_memory.rb +64 -0
  38. data/lib/clipper2/operations.rb +206 -0
  39. data/lib/clipper2/path.rb +171 -0
  40. data/lib/clipper2/path_simplifier.rb +61 -0
  41. data/lib/clipper2/paths.rb +130 -0
  42. data/lib/clipper2/quantizer.rb +48 -0
  43. data/lib/clipper2/triangulation.rb +103 -0
  44. data/lib/clipper2/version.rb +5 -0
  45. data/lib/clipper2.rb +34 -0
  46. data/licenses/CLIPPER2-BSL-1.0.txt +23 -0
  47. data/licenses/EARCUT-ISC.txt +15 -0
  48. metadata +113 -0
@@ -0,0 +1,1221 @@
1
+ /*******************************************************************************
2
+ * Author : Angus Johnson *
3
+ * Date : 21 February 2026 *
4
+ * Release : BETA RELEASE *
5
+ * Website : https://www.angusj.com *
6
+ * Copyright : Angus Johnson 2010-2026 *
7
+ * Purpose : Constrained Delaunay Triangulation *
8
+ * License : https://www.boost.org/LICENSE_1_0.txt *
9
+ *******************************************************************************/
10
+
11
+ #include "clipper2/clipper.h"
12
+ #include "clipper2/clipper.triangulation.h"
13
+
14
+ namespace Clipper2Lib
15
+ {
16
+
17
+ enum class EdgeKind { loose, ascend, descend }; // ascend & descend are 'fixed' edges
18
+ enum class IntersectKind { none, collinear, intersect };
19
+ enum class EdgeContainsResult { neither, left, right };
20
+
21
+ //forward definitions
22
+ class Vertex2;
23
+ class Edge;
24
+ class Triangle;
25
+
26
+ typedef std::vector<Vertex2*> VertexList;
27
+ typedef std::vector<Edge*> EdgeList;
28
+
29
+ class Vertex2 {
30
+ public:
31
+ Point64 pt;
32
+ EdgeList edges;
33
+ bool innerLM = false;
34
+ Vertex2(const Point64& p64) : pt(p64) { edges.reserve(2); };
35
+ };
36
+
37
+ class Edge {
38
+ public:
39
+ Vertex2* vL = nullptr;
40
+ Vertex2* vR = nullptr;
41
+ Vertex2* vB = nullptr;
42
+ Vertex2* vT = nullptr;
43
+ EdgeKind kind = EdgeKind::loose;
44
+ Triangle* triA = nullptr;
45
+ Triangle* triB = nullptr;
46
+ bool isActive = false;
47
+ Edge* nextE = nullptr;
48
+ Edge* prevE = nullptr;
49
+ };
50
+
51
+ class Triangle {
52
+ public:
53
+ Edge* edges[3];
54
+ Triangle(Edge* e1, Edge* e2, Edge* e3)
55
+ {
56
+ edges[0] = e1;
57
+ edges[1] = e2;
58
+ edges[2] = e3;
59
+ }
60
+ };
61
+
62
+ /////////////////////////////////////////////////////////////////////////////
63
+ // Delaunay class declaration
64
+ /////////////////////////////////////////////////////////////////////////////
65
+
66
+ class Delaunay {
67
+ private:
68
+ VertexList allVertices;
69
+ EdgeList allEdges;
70
+ std::vector<Triangle*> allTriangles;
71
+ std::stack<Edge*> pendingDelaunayStack;
72
+ std::stack<Edge*> horzEdgeStack;
73
+ std::stack<Vertex2*> locMinStack;
74
+ bool useDelaunay = true;
75
+ Vertex2* lowermostVertex = nullptr;
76
+ Edge* firstActive = nullptr;
77
+ void AddPath(const Path64& path);
78
+ bool AddPaths(const Paths64& paths);
79
+ void CleanUp();
80
+ bool FixupEdgeIntersects();
81
+ void MergeDupOrCollinearVertices();
82
+ void SplitEdge(Edge* longE, Edge* shortE);
83
+ bool RemoveIntersection(Edge* e1, Edge* e2);
84
+ Edge* CreateEdge(Vertex2* v1, Vertex2* v2, EdgeKind k);
85
+ Triangle* CreateTriangle(Edge* e1, Edge* e2, Edge* e3);
86
+ Edge* CreateInnerLocMinLooseEdge(Vertex2* vAbove);
87
+ Edge* HorizontalBetween(Vertex2* v1, Vertex2* v2);
88
+ void DoTriangulateLeft(Edge* edge, Vertex2* pivot, int64_t minY);
89
+ void DoTriangulateRight(Edge* edge, Vertex2* pivot, int64_t minY);
90
+ void AddEdgeToActives(Edge* edge);
91
+ void RemoveEdgeFromActives(Edge* edge);
92
+ void ForceLegal(Edge* edge);
93
+ public:
94
+ explicit Delaunay(bool delaunay = true) : useDelaunay(delaunay) {};
95
+ ~Delaunay() { CleanUp(); };
96
+ Paths64 Execute(const Paths64& paths, TriangulateResult& triResult);
97
+ };
98
+
99
+ /////////////////////////////////////////////////////////////////////////////
100
+ // Miscellaneous functions
101
+ /////////////////////////////////////////////////////////////////////////////
102
+
103
+ static bool VertexListSort(const Vertex2* a, const Vertex2* b)
104
+ {
105
+ return (a->pt.y == b->pt.y) ? (a->pt.x < b->pt.x) : (a->pt.y > b->pt.y);
106
+ }
107
+
108
+ static bool EdgeListSort(const Edge* a, const Edge* b)
109
+ {
110
+ return (a->vL->pt.x < b->vL->pt.x);
111
+ }
112
+
113
+ static bool IsLooseEdge(const Edge& e)
114
+ {
115
+ return e.kind == EdgeKind::loose;
116
+ }
117
+
118
+ static bool IsLeftEdge(const Edge& e)
119
+ {
120
+ // left edge (bound) of a fill region
121
+ // ie fills on the **right** side of the edge
122
+ // precondition - e is never a 'loose' edge
123
+ return e.kind == EdgeKind::ascend;
124
+ }
125
+
126
+ static bool IsRightEdge(const Edge& e)
127
+ {
128
+ // right edge (bound) of a fill region
129
+ // but still fills on the **right** side of the edge
130
+ // precondition - e is never a 'loose' edge
131
+ return e.kind == EdgeKind::descend;
132
+ }
133
+
134
+ static bool IsHorizontal(const Edge& e)
135
+ {
136
+ return e.vB->pt.y == e.vT->pt.y;
137
+ }
138
+
139
+ static bool LeftTurning(const Point64& p1, const Point64& p2, const Point64& p3)
140
+ {
141
+ return CrossProductSign(p1, p2, p3) < 0;
142
+ }
143
+
144
+ static bool RightTurning(const Point64& p1, const Point64& p2, const Point64& p3)
145
+ {
146
+ return CrossProductSign(p1, p2, p3) > 0;
147
+ }
148
+
149
+ static bool EdgeCompleted(Edge* edge)
150
+ {
151
+ if (!edge->triA) return false;
152
+ if (edge->triB) return true;
153
+ return edge->kind != EdgeKind::loose;
154
+ }
155
+
156
+ static EdgeContainsResult EdgeContains(const Edge* edge, const Vertex2* v)
157
+ {
158
+ if (edge->vL == v) return EdgeContainsResult::left;
159
+ else if (edge->vR == v) return EdgeContainsResult::right;
160
+ else return EdgeContainsResult::neither;
161
+ }
162
+
163
+ static double GetAngle(const Point64& a, const Point64& b, const Point64& c)
164
+ {
165
+ //https://stackoverflow.com/a/3487062/359538
166
+ double abx = static_cast<double>(b.x - a.x);
167
+ double aby = static_cast<double>(b.y - a.y);
168
+ double bcx = static_cast<double>(b.x - c.x);
169
+ double bcy = static_cast<double>(b.y - c.y);
170
+ double dp = (abx * bcx + aby * bcy);
171
+ double cp = (abx * bcy - aby * bcx);
172
+ return std::atan2(cp, dp); //range between -Pi and Pi
173
+ }
174
+
175
+ static double GetLocMinAngle(Vertex2* v)
176
+ {
177
+ // todo - recheck the result's sign compared to left vs right turning
178
+ // (currently assumes left turning => positive values)
179
+ // precondition - this function is called before processing locMin.
180
+ //Assert(v->edges.size() == 2);
181
+ int asc, des;
182
+ if (v->edges[0]->kind == EdgeKind::ascend)
183
+ {
184
+ asc = 0;
185
+ des = 1;
186
+ }
187
+ else
188
+ {
189
+ des = 0;
190
+ asc = 1;
191
+ }
192
+ // winding direction - descending to ascending
193
+ return GetAngle(v->edges[des]->vT->pt, v->pt, v->edges[asc]->vT->pt);
194
+ }
195
+
196
+ static void RemoveEdgeFromVertex(Vertex2* vert, Edge* edge)
197
+ {
198
+ auto it = std::find(vert->edges.begin(), vert->edges.end(), edge);
199
+ if (it == vert->edges.end()) DoError(undefined_error_i);
200
+ vert->edges.erase(it);
201
+ }
202
+
203
+ static bool FindLocMinIdx(const Path64& path, size_t len, size_t& idx)
204
+ {
205
+ if (len < 3) return false;
206
+ size_t i0 = idx, n = (idx + 1) % len;
207
+ while (path[n].y <= path[idx].y)
208
+ {
209
+ idx = n; n = (n + 1) % len;
210
+ if (idx == i0) return false; // fails if the path is completely horizontal
211
+ }
212
+ while (path[n].y >= path[idx].y)
213
+ {
214
+ idx = n; n = (n + 1) % len;
215
+ }
216
+ return true;
217
+ }
218
+
219
+ static size_t Prev(size_t& idx, size_t len)
220
+ {
221
+ if (idx == 0) return len - 1; else return idx - 1;
222
+ }
223
+
224
+ static size_t Next(size_t& idx, size_t len)
225
+ {
226
+ return (idx + 1) % len;
227
+ }
228
+
229
+ static Edge* FindLinkingEdge(const Vertex2* vert1, const Vertex2* vert2, bool preferAscending)
230
+ {
231
+ Edge* res = nullptr;
232
+ for (auto e : vert1->edges)
233
+ {
234
+ if (e->vL == vert2 || e->vR == vert2)
235
+ {
236
+ if (e->kind == EdgeKind::loose ||
237
+ ((e->kind == EdgeKind::ascend) == preferAscending)) return e;
238
+ res = e;
239
+ }
240
+ }
241
+ return res;
242
+ }
243
+
244
+ static Path64 PathFromTriangle(Triangle tri)
245
+ {
246
+ Path64 res;
247
+ res.reserve(3);
248
+ res.push_back(tri.edges[0]->vL->pt);
249
+ res.push_back(tri.edges[0]->vR->pt);
250
+ const Edge& e = *tri.edges[1];
251
+ if (e.vL->pt == res[0] || e.vL->pt == res[1])
252
+ res.push_back(e.vR->pt);
253
+ else
254
+ res.push_back(e.vL->pt);
255
+ return res;
256
+ }
257
+
258
+ static double InCircleTest(const Point64& ptA, const Point64& ptB,
259
+ const Point64& ptC, const Point64& ptD)
260
+ {
261
+ // Return the determinant value of 3 x 3 matrix ...
262
+ // | ax-dx ay-dy Sqr(ax-dx)+Sqr(ay-dy) |
263
+ // | bx-dx by-dy Sqr(bx-dx)+Sqr(by-dy) |
264
+ // | cx-dx cy-dy Sqr(cx-dx)+Sqr(cy-dy) |
265
+
266
+ // The *sign* of the return value is determined by
267
+ // the orientation (CW vs CCW) of ptA, ptB & ptC.
268
+
269
+ // precondition - ptA, ptB & ptC make a *non-empty* triangle
270
+ double m00 = static_cast<double>(ptA.x - ptD.x);
271
+ double m01 = static_cast<double>(ptA.y - ptD.y);
272
+ double m02 = (Sqr(m00) + Sqr(m01));
273
+ double m10 = static_cast<double>(ptB.x - ptD.x);
274
+ double m11 = static_cast<double>(ptB.y - ptD.y);
275
+ double m12 = (Sqr(m10) + Sqr(m11));
276
+ double m20 = static_cast<double>(ptC.x - ptD.x);
277
+ double m21 = static_cast<double>(ptC.y - ptD.y);
278
+ double m22 = (Sqr(m20) + Sqr(m21));
279
+ return m00 * (m11 * m22 - m21 * m12) -
280
+ m10 * (m01 * m22 - m21 * m02) +
281
+ m20 * (m01 * m12 - m11 * m02);
282
+ }
283
+
284
+ static double ShortestDistFromSegment(const Point64& pt, const Point64& segPt1, const Point64& segPt2)
285
+ {
286
+ // precondition: segPt1 <> segPt2
287
+ double dx = static_cast<double>(segPt2.x - segPt1.x);
288
+ double dy = static_cast<double>(segPt2.y - segPt1.y);
289
+ //Assert((dx < > 0) or (dy < > 0)); // ie segPt1 <> segPt2
290
+
291
+ double ax = static_cast<double>(pt.x - segPt1.x);
292
+ double ay = static_cast<double>(pt.y - segPt1.y);
293
+ //q = (ax * dx + ay * dy) / (dx * dx + dy * dy);
294
+ double qNum = ax * dx + ay * dy;
295
+ if (qNum < 0) // pt is closest to seq1
296
+ return DistanceSqr(pt, segPt1);
297
+ else if (qNum > (Sqr(dx) + Sqr(dy))) // pt is closest to seq2
298
+ return DistanceSqr(pt, segPt2);
299
+ else
300
+ return Sqr(ax * dy - dx * ay) / (dx * dx + dy * dy);
301
+ }
302
+
303
+ static IntersectKind SegsIntersect(const Point64 s1a, const Point64 s1b,
304
+ const Point64 s2a, const Point64 s2b)
305
+ {
306
+ //ignore segments sharing an end-point
307
+ if (s1a == s2a || s1b == s2a || s1b == s2b) return IntersectKind::none;
308
+
309
+ double dy1 = static_cast<double>(s1b.y - s1a.y);
310
+ double dx1 = static_cast<double>(s1b.x - s1a.x);
311
+ double dy2 = static_cast<double>(s2b.y - s2a.y);
312
+ double dx2 = static_cast<double>(s2b.x - s2a.x);
313
+ double cp = dy1 * dx2 - dy2 * dx1;
314
+ if (cp == 0) return IntersectKind::collinear;
315
+
316
+ double t = (static_cast<double>(s1a.x - s2a.x) * dy2 -
317
+ static_cast<double>(s1a.y - s2a.y) * dx2);
318
+
319
+ // nb: testing for t == 0 is unreliable due to float imprecision
320
+ if (t >= 0)
321
+ {
322
+ if (cp < 0 || t >= cp) return IntersectKind::none;
323
+ }
324
+ else
325
+ {
326
+ if (cp > 0 || t <= cp) return IntersectKind::none;
327
+ }
328
+
329
+ // so far, the *segment* 's1' intersects the *line* through 's2',
330
+ // but now make sure it also intersects the *segment* 's2'
331
+ t = ((s1a.x - s2a.x) * dy1 - (s1a.y - s2a.y) * dx1);
332
+ if (t >= 0)
333
+ {
334
+ if (cp > 0 && t < cp) return IntersectKind::intersect;
335
+ }
336
+ else
337
+ {
338
+ if (cp < 0 && t > cp) return IntersectKind::intersect;
339
+ }
340
+ return IntersectKind::none;
341
+ }
342
+
343
+ /////////////////////////////////////////////////////////////////////////////
344
+ // Delaunay class definitions
345
+ /////////////////////////////////////////////////////////////////////////////
346
+
347
+ void Delaunay::CleanUp()
348
+ {
349
+ for (auto v : allVertices) delete v;
350
+ allVertices.resize(0);
351
+ for (auto e : allEdges) delete e;
352
+ allEdges.resize(0);
353
+ for (auto t : allTriangles) delete t;
354
+ allTriangles.resize(0);
355
+
356
+ firstActive = nullptr;
357
+ lowermostVertex = nullptr;
358
+ }
359
+
360
+ void Delaunay::ForceLegal(Edge* edge)
361
+ {
362
+ // don't try to make empty triangles legal
363
+ if (!edge->triA || !edge->triB) return;
364
+
365
+ // vertA will be assigned the vertex in edge's triangleA
366
+ // that is NOT an end vertex of edge
367
+ // Likewise, vertB will be assigned the vertex in edge's
368
+ // triangleB that is NOT an end vertex of edge
369
+ // If edge is rotated, vertA & vertB will become its end vertices.
370
+ Vertex2* vertA = nullptr;
371
+ Vertex2* vertB = nullptr;
372
+
373
+ // Excluding 'edge', edgesA will contain two edges (one from
374
+ // triangleA and one from triangleB) that touch edge.vL.
375
+ // And edgesB will contain the two edges that touch edge.vR.
376
+
377
+ Edge* edgesA[3] = { nullptr, nullptr, nullptr };
378
+ Edge* edgesB[3] = { nullptr, nullptr, nullptr };
379
+ for (int i = 0; i < 3; ++i)
380
+ {
381
+ if (edge->triA->edges[i] == edge) continue;
382
+ switch (EdgeContains(edge->triA->edges[i], edge->vL))
383
+ {
384
+ case EdgeContainsResult::left:
385
+ edgesA[1] = edge->triA->edges[i];
386
+ vertA = edge->triA->edges[i]->vR;
387
+ break;
388
+ case EdgeContainsResult::right:
389
+ edgesA[1] = edge->triA->edges[i];
390
+ vertA = edge->triA->edges[i]->vL;
391
+ break;
392
+ default:
393
+ edgesB[1] = edge->triA->edges[i];
394
+ }
395
+ }
396
+
397
+ for (int i = 0; i < 3; ++i)
398
+ {
399
+ if (edge->triB->edges[i] == edge) continue;
400
+ switch (EdgeContains(edge->triB->edges[i], edge->vL))
401
+ {
402
+ case EdgeContainsResult::left:
403
+ edgesA[2] = edge->triB->edges[i];
404
+ vertB = edge->triB->edges[i]->vR;
405
+ break;
406
+ case EdgeContainsResult::right:
407
+ edgesA[2] = edge->triB->edges[i];
408
+ vertB = edge->triB->edges[i]->vL;
409
+ break;
410
+ default:
411
+ edgesB[2] = edge->triB->edges[i];
412
+ }
413
+ }
414
+
415
+ // InCircleTest reqires edge.triangleA to be a valid triangle
416
+ // if IsEmptyTriangle(edge.triangleA) then Exit; // slower
417
+ if (CrossProductSign(vertA->pt, edge->vL->pt, edge->vR->pt) == 0) return;
418
+
419
+ // ictResult - result sign is dependant on triangleA's orientation
420
+ double ictResult = InCircleTest(vertA->pt, edge->vL->pt, edge->vR->pt, vertB->pt);
421
+ if (ictResult == 0 || // if on or out of circle then exit
422
+ (RightTurning(vertA->pt, edge->vL->pt, edge->vR->pt) == (ictResult < 0))) return;
423
+
424
+ // TRIANGLES HERE ARE **NOT** DELAUNAY COMPLIANT, SO MAKE THEM SO.
425
+
426
+ // NOTE: ONCE WE BEGIN DELAUNAY COMPLIANCE, vL & vR WILL
427
+ // NO LONGER REPRESENT LEFT AND RIGHT VERTEX ORIENTATION.
428
+ // THIS IS MINOR PERFORMANCE EFFICIENCY IS SAFE AS LONG AS
429
+ // THE TRIANGULATE() METHOD IS CALLED ONCE ONLY ON A GIVEN
430
+ // SET OF PATHS
431
+
432
+ edge->vL = vertA;
433
+ edge->vR = vertB;
434
+
435
+ edge->triA->edges[0] = edge;
436
+ for (int i = 1; i < 3; ++i)
437
+ {
438
+ edge->triA->edges[i] = edgesA[i];
439
+ if (!edgesA[i]) DoError(undefined_error_i);
440
+ if (IsLooseEdge(*edgesA[i]))
441
+ pendingDelaunayStack.push(edgesA[i]);
442
+ // since each edge has its own triangleA and triangleB, we have to be careful
443
+ // to update the correct one ...
444
+ if (edgesA[i]->triA == edge->triA || edgesA[i]->triB == edge->triA) continue;
445
+
446
+ if (edgesA[i]->triA == edge->triB)
447
+ edgesA[i]->triA = edge->triA;
448
+ else if (edgesA[i]->triB == edge->triB)
449
+ edgesA[i]->triB = edge->triA;
450
+ else DoError(undefined_error_i);
451
+ }
452
+
453
+ edge->triB->edges[0] = edge;
454
+ for (int i = 1; i < 3; ++i)
455
+ {
456
+ edge->triB->edges[i] = edgesB[i];
457
+ if (!edgesB[i]) DoError(undefined_error_i);
458
+ if (IsLooseEdge(*edgesB[i]))
459
+ pendingDelaunayStack.push(edgesB[i]);
460
+ // since each edge has its own triangleA and triangleB, we have to be careful
461
+ // to update the correct one ...
462
+ if (edgesB[i]->triA == edge->triB || edgesB[i]->triB == edge->triB) continue;
463
+
464
+ if (edgesB[i]->triA == edge->triA)
465
+ edgesB[i]->triA = edge->triB;
466
+ else if (edgesB[i]->triB == edge->triA)
467
+ edgesB[i]->triB = edge->triB;
468
+ else DoError(undefined_error_i);
469
+ }
470
+
471
+ }
472
+
473
+ Edge* Delaunay::CreateEdge(Vertex2* v1, Vertex2* v2, EdgeKind k)
474
+ {
475
+ Edge* res = allEdges.emplace_back(new Edge());
476
+ if (v1->pt.y == v2->pt.y)
477
+ {
478
+ res->vB = v1; res->vT = v2;
479
+ }
480
+ else if (v1->pt.y < v2->pt.y)
481
+ {
482
+ res->vB = v2; res->vT = v1;
483
+ }
484
+ else
485
+ {
486
+ res->vB = v1; res->vT = v2;
487
+ }
488
+
489
+ if (v1->pt.x <= v2->pt.x)
490
+ {
491
+ res->vL = v1; res->vR = v2;
492
+ }
493
+ else
494
+ {
495
+ res->vL = v2; res->vR = v1;
496
+ }
497
+ res->kind = k;
498
+ v1->edges.push_back(res);
499
+ v2->edges.push_back(res);
500
+
501
+ if (k == EdgeKind::loose)
502
+ {
503
+ pendingDelaunayStack.push(res);
504
+ AddEdgeToActives(res);
505
+ }
506
+ return res;
507
+ }
508
+
509
+ Triangle* Delaunay::CreateTriangle(Edge* e1, Edge* e2, Edge* e3)
510
+ {
511
+ Triangle* res = allTriangles.emplace_back(new Triangle(e1, e2, e3));
512
+ // nb: only expire loose edges when both sides of these edges have triangles.
513
+ for (int i = 0; i < 3; ++i)
514
+ if (res->edges[i]->triA)
515
+ {
516
+ res->edges[i]->triB = res;
517
+ // this is the edge's second triangle hence no longer active
518
+ RemoveEdgeFromActives(res->edges[i]);
519
+ }
520
+ else
521
+ {
522
+ res->edges[i]->triA = res;
523
+ // this is the edge's first triangle, so only remove
524
+ // this edge from actives if it's a fixed edge.
525
+ if (!IsLooseEdge(*res->edges[i]))
526
+ RemoveEdgeFromActives(res->edges[i]);
527
+ }
528
+ return res;
529
+ }
530
+
531
+ bool Delaunay::RemoveIntersection(Edge* e1, Edge* e2)
532
+ {
533
+ // find which vertex is closest to the other segment
534
+ // (ie not the vertex closest to the intersection point) ...
535
+
536
+ Vertex2* v = e1->vL;
537
+ Edge* tmpE = e2;
538
+ double d = ShortestDistFromSegment(e1->vL->pt, e2->vL->pt, e2->vR->pt);
539
+ double d2 = ShortestDistFromSegment(e1->vR->pt, e2->vL->pt, e2->vR->pt);
540
+ if (d2 < d) { d = d2; v = e1->vR; }
541
+ d2 = ShortestDistFromSegment(e2->vL->pt, e1->vL->pt, e1->vR->pt);
542
+ if (d2 < d) { d = d2; tmpE = e1; v = e2->vL; }
543
+ d2 = ShortestDistFromSegment(e2->vR->pt, e1->vL->pt, e1->vR->pt);
544
+ if (d2 < d) { d = d2; tmpE = e1; v = e2->vR; }
545
+ if (d > 1.000)
546
+ return false; // Oops - this is not just a simple 'rounding' intersection
547
+
548
+ // split 'tmpE' into 2 edges at 'v'
549
+ Vertex2* v2 = tmpE->vT;
550
+ RemoveEdgeFromVertex(v2, tmpE);
551
+ // replace v2 in tmpE with v
552
+ if (tmpE->vL == v2) tmpE->vL = v;
553
+ else tmpE->vR = v;
554
+ tmpE->vT = v;
555
+ v->edges.push_back(tmpE);
556
+ v->innerLM = false; // #47
557
+ // left turning is angle positive
558
+ if (tmpE->vB->innerLM && GetLocMinAngle(tmpE->vB) <= 0)
559
+ tmpE->vB->innerLM = false; // #44, 52
560
+ // finally create a new edge between v and v2 ...
561
+ CreateEdge(v, v2, tmpE->kind);
562
+ return true;
563
+ }
564
+
565
+ bool Delaunay::FixupEdgeIntersects()
566
+ {
567
+ // precondition - edgeList must be sorted - ascending on edge.vL.pt.x
568
+
569
+ for (size_t i1 = 0; i1 < allEdges.size(); ++i1)
570
+ {
571
+ Edge* e1 = allEdges[i1];
572
+ // nb: we can safely ignore edges newly created inside this for loop
573
+ for (size_t i2 = i1 + 1; i2 < allEdges.size(); ++i2)
574
+ {
575
+ Edge* e2 = allEdges[i2];
576
+ if (e2->vL->pt.x >= e1->vR->pt.x)
577
+ break; // all 'e' from now on are too far right
578
+
579
+ // 'e2' is inside e1's horizontal region. If 'e2' is also inside
580
+ // e1's vertical region, only then check for an intersection ...
581
+ if (e2->vT->pt.y < e1->vB->pt.y && e2->vB->pt.y > e1->vT->pt.y &&
582
+ (SegsIntersect(e2->vL->pt, e2->vR->pt,
583
+ e1->vL->pt, e1->vR->pt) == IntersectKind::intersect))
584
+ {
585
+ if (!RemoveIntersection(e2, e1))
586
+ return false; // oops!!
587
+ }
588
+ // nb: collinear edges are managed in MergeDupOrCollinearVertices below
589
+ }
590
+ }
591
+ return true;
592
+ }
593
+
594
+ void Delaunay::SplitEdge(Edge* longE, Edge* shortE)
595
+ {
596
+ auto oldT = longE->vT, newT = shortE->vT;
597
+ // remove longEdge from longEdge.vT.edges
598
+ RemoveEdgeFromVertex(oldT, longE);
599
+ // shorten longEdge
600
+ longE->vT = newT;
601
+ if (longE->vL == oldT) longE->vL = newT;
602
+ else longE->vR = newT;
603
+ // add shortened longEdge to newT.edges
604
+ newT->edges.push_back(longE);
605
+ // and create a new edge betweem newV, oldT
606
+ CreateEdge(newT, oldT, longE->kind);
607
+ }
608
+
609
+ void Delaunay::MergeDupOrCollinearVertices()
610
+ {
611
+ // note: this procedure may add new edges and change the
612
+ // number of edges connected with a given vertex, but it
613
+ // won't add or delete vertices (so it's safe to use iterators)
614
+ auto vIter1 = allVertices.begin();
615
+ for (auto vIter2 = allVertices.begin() + 1; vIter2 != allVertices.end(); ++vIter2)
616
+ {
617
+ if ((*vIter1)->pt != (*vIter2)->pt)
618
+ {
619
+ vIter1 = vIter2;
620
+ continue;
621
+ }
622
+
623
+ // merge v1 & v2
624
+ Vertex2* v1 = *vIter1, * v2 = *vIter2;
625
+ if (!v1->innerLM || !v2->innerLM)
626
+ v1->innerLM = false;
627
+
628
+ // in all of v2's edges, replace links to v2 with links to v1
629
+ for (auto e : v2->edges)
630
+ {
631
+ if (e->vB == v2) e->vB = v1;
632
+ else e->vT = v1;
633
+ if (e->vL == v2) e->vL = v1;
634
+ else e->vR = v1;
635
+ }
636
+ // move all of v2's edges to v1
637
+ std::copy(v2->edges.begin(), v2->edges.end(), back_inserter(v1->edges));
638
+ v2->edges.resize(0);
639
+
640
+ // excluding horizontals, if pv.edges contains two edges
641
+ // that are *collinear* and share the same bottom coords
642
+ // but have different lengths, split the longer edge at
643
+ // the top of the shorter edge ...
644
+ for (auto itE = v1->edges.begin(); itE != v1->edges.end(); ++itE)
645
+ {
646
+ if (IsHorizontal(*(*itE)) || (*itE)->vB != v1) continue;
647
+ for (auto itE2 = itE + 1; itE2 != v1->edges.end(); ++itE2)
648
+ {
649
+ auto e1 = *itE, e2 = *itE2;
650
+ if (e2->vB != v1 || e1->vT->pt.y == e2->vT->pt.y ||
651
+ (CrossProductSign(e1->vT->pt, v1->pt, e2->vT->pt) != 0)) continue;
652
+ // we have parallel edges, both heading up from v1.pt.
653
+ // split the longer edge at the top of the shorter edge.
654
+ if (e1->vT->pt.y < e2->vT->pt.y) SplitEdge(e1, e2);
655
+ else SplitEdge(e2, e1);
656
+ break; // because only two edges can be collinear
657
+ }
658
+ }
659
+ }
660
+ }
661
+
662
+ Edge* Delaunay::CreateInnerLocMinLooseEdge(Vertex2* vAbove)
663
+ {
664
+ if (!firstActive) return nullptr; // oops!!
665
+
666
+ int64_t xAbove = vAbove->pt.x;
667
+ int64_t yAbove = vAbove->pt.y;
668
+
669
+ // find the closest 'active' edge with a vertex that's not above vAbove
670
+ Edge* e = firstActive, *eBelow = nullptr;
671
+ double bestD = -1.0;
672
+ while (e)
673
+ {
674
+ if (e->vL->pt.x <= xAbove && e->vR->pt.x >= xAbove &&
675
+ e->vB->pt.y >= yAbove && e->vB != vAbove && e->vT != vAbove &&
676
+ !LeftTurning(e->vL->pt, vAbove->pt, e->vR->pt))
677
+ {
678
+ double d = ShortestDistFromSegment(vAbove->pt, e->vL->pt, e->vR->pt);
679
+ if (!eBelow || d < bestD) // compare e with eBelow
680
+ {
681
+ eBelow = e;
682
+ bestD = d;
683
+ }
684
+ }
685
+ e = e->nextE;
686
+ }
687
+ if (!eBelow) return nullptr; // oops!!
688
+
689
+ // get the best vertex from 'eBelow'
690
+ Vertex2* vBest = (eBelow->vT->pt.y <= yAbove) ? eBelow->vB : eBelow->vT;
691
+ int64_t xBest = vBest->pt.x;
692
+ int64_t yBest = vBest->pt.y;
693
+
694
+ // make sure no edges intersect 'vAbove' and 'vBest' ...
695
+ // todo: fActives is currently *unsorted* but consider making it
696
+ // a tree structure based on each edge's left and right bounds
697
+ e = firstActive;
698
+ if (xBest < xAbove)
699
+ {
700
+ while (e)
701
+ {
702
+ if (e->vR->pt.x > xBest && e->vL->pt.x < xAbove &&
703
+ e->vB->pt.y > yAbove && e->vT->pt.y < yBest &&
704
+ (SegsIntersect(e->vB->pt, e->vT->pt,
705
+ vBest->pt, vAbove->pt) == IntersectKind::intersect))
706
+ {
707
+ vBest = (e->vT->pt.y > yAbove) ? e->vT : e->vB;
708
+ xBest = vBest->pt.x;
709
+ yBest = vBest->pt.y;
710
+ }
711
+ e = e->nextE;
712
+ }
713
+ }
714
+ else
715
+ {
716
+ while (e)
717
+ {
718
+ if (e->vR->pt.x < xBest && e->vL->pt.x > xAbove &&
719
+ e->vB->pt.y > yAbove && e->vT->pt.y < yBest &&
720
+ (SegsIntersect(e->vB->pt, e->vT->pt,
721
+ vBest->pt, vAbove->pt) == IntersectKind::intersect))
722
+ {
723
+ vBest = e->vT->pt.y > yAbove ? e->vT : e->vB;
724
+ xBest = vBest->pt.x;
725
+ yBest = vBest->pt.y;
726
+ }
727
+ e = e->nextE;
728
+ }
729
+ }
730
+ return CreateEdge(vBest, vAbove, EdgeKind::loose);
731
+ }
732
+
733
+ Edge* Delaunay::HorizontalBetween(Vertex2* v1, Vertex2* v2)
734
+ {
735
+ int64_t y = v1->pt.y, l, r;
736
+ if (v1->pt.x > v2->pt.x)
737
+ {
738
+ l = v2->pt.x;
739
+ r = v1->pt.x;
740
+ }
741
+ else
742
+ {
743
+ l = v1->pt.x;
744
+ r = v2->pt.x;
745
+ }
746
+
747
+ Edge* res = firstActive;
748
+ while (res)
749
+ {
750
+ if (res->vL->pt.y == y && res->vR->pt.y == y &&
751
+ res->vL->pt.x >= l && res->vR->pt.x <= r &&
752
+ (res->vL->pt.x != l || res->vL->pt.x != r)) break;
753
+ res = res->nextE;
754
+ }
755
+ return res;
756
+ }
757
+
758
+ void Delaunay::DoTriangulateLeft(Edge* edge, Vertex2* pivot, int64_t minY)
759
+ {
760
+ // precondition - pivot must be one end of edge (Usually .vB)
761
+ //Assert(!EdgeCompleted(edge));
762
+ Vertex2* vAlt = nullptr;
763
+ Edge* eAlt = nullptr;
764
+ Vertex2* v = (edge->vB == pivot) ? edge->vT : edge->vB;
765
+
766
+ for (auto e : pivot->edges)
767
+ {
768
+ if (e == edge || !e->isActive) continue;
769
+ Vertex2* vX = e->vT == pivot ? e->vB : e->vT;
770
+ if (vX == v) continue;
771
+
772
+ int cps = CrossProductSign(v->pt, pivot->pt, vX->pt);
773
+ if (cps == 0) //collinear paths
774
+ {
775
+ // if pivot is between v and vX then continue;
776
+ // nb: this is important for both horiz and non-horiz collinear
777
+ if ((v->pt.x > pivot->pt.x) == (pivot->pt.x > vX->pt.x)) continue;
778
+ }
779
+ // else if right-turning or not the best edge, then continue
780
+ else if (cps > 0 || (vAlt && !LeftTurning(vX->pt, pivot->pt, vAlt->pt)))
781
+ continue;
782
+ vAlt = vX;
783
+ eAlt = e;
784
+ }
785
+
786
+ if (!vAlt || vAlt->pt.y < minY) return;
787
+
788
+ // Don't triangulate **across** fixed edges
789
+ if (vAlt->pt.y < pivot->pt.y)
790
+ {
791
+ if (IsLeftEdge(*eAlt)) return;
792
+ }
793
+ else if (vAlt->pt.y > pivot->pt.y)
794
+ {
795
+ if (IsRightEdge(*eAlt)) return;
796
+ }
797
+
798
+ Edge* eX = FindLinkingEdge(vAlt, v, (vAlt->pt.y < v->pt.y));
799
+ if (!eX)
800
+ {
801
+ // be very careful creating loose horizontals at minY
802
+ if (vAlt->pt.y == v->pt.y && v->pt.y == minY &&
803
+ HorizontalBetween(vAlt, v)) return;
804
+ eX = CreateEdge(vAlt, v, EdgeKind::loose);
805
+ }
806
+
807
+ CreateTriangle(edge, eAlt, eX);
808
+ if (!EdgeCompleted(eX))
809
+ DoTriangulateLeft(eX, vAlt, minY);
810
+
811
+ }
812
+
813
+ void Delaunay::DoTriangulateRight(Edge* edge, Vertex2* pivot, int64_t minY)
814
+ {
815
+ // precondition - pivot must be one end of edge (Usually .vB)
816
+ //Assert(!EdgeCompleted(edge));
817
+ Vertex2* vAlt = nullptr;
818
+ Edge* eAlt = nullptr;
819
+ Vertex2* v = (edge->vB == pivot) ? edge->vT : edge->vB;
820
+
821
+ for (auto e : pivot->edges)
822
+ {
823
+ if (e == edge || !e->isActive) continue;
824
+ Vertex2* vX = e->vT == pivot ? e->vB : e->vT;
825
+ if (vX == v) continue;
826
+
827
+ int cps = CrossProductSign(v->pt, pivot->pt, vX->pt);
828
+ if (cps == 0) //collinear paths
829
+ {
830
+ // if pivot is between v and vX then continue;
831
+ // nb: this is important for both horiz and non-horiz collinear
832
+ if ((v->pt.x > pivot->pt.x) == (pivot->pt.x > vX->pt.x)) continue;
833
+ }
834
+ // else if right-turning or not the best edge, then continue
835
+ else if (cps < 0 || (vAlt && !RightTurning(vX->pt, pivot->pt, vAlt->pt)))
836
+ continue;
837
+ vAlt = vX;
838
+ eAlt = e;
839
+ }
840
+
841
+ if (!vAlt || vAlt->pt.y < minY) return;
842
+
843
+ // Don't triangulate **across** fixed edges
844
+ if (vAlt->pt.y < pivot->pt.y)
845
+ {
846
+ if (IsRightEdge(*eAlt)) return;
847
+ }
848
+ else if (vAlt->pt.y > pivot->pt.y)
849
+ {
850
+ if (IsLeftEdge(*eAlt)) return;
851
+ }
852
+
853
+ Edge* eX = FindLinkingEdge(vAlt, v, (vAlt->pt.y > v->pt.y));
854
+ if (!eX)
855
+ {
856
+ // be very careful creating loose horizontals at minY
857
+ if (vAlt->pt.y == v->pt.y && v->pt.y == minY &&
858
+ HorizontalBetween(vAlt, v)) return;
859
+ eX = CreateEdge(vAlt, v, EdgeKind::loose);
860
+ }
861
+
862
+ CreateTriangle(edge, eX, eAlt);
863
+ if (!EdgeCompleted(eX))
864
+ DoTriangulateRight(eX, vAlt, minY);
865
+
866
+
867
+ }
868
+
869
+ void Delaunay::AddEdgeToActives(Edge* edge)
870
+ {
871
+ // nb: on occassions this method can get called twice for a given edge
872
+ // This is because, in the Triangulate() method where vertex 'edges'
873
+ // arrays are being parsed, edges can can be removed from the array
874
+ // which changes the index of following edges.
875
+ if (edge->isActive) return;
876
+
877
+ edge->prevE = nullptr;
878
+ edge->nextE = firstActive;
879
+ edge->isActive = true;
880
+ if (firstActive)
881
+ firstActive->prevE = edge;
882
+ firstActive = edge;
883
+ }
884
+
885
+
886
+ void Delaunay::RemoveEdgeFromActives(Edge* edge)
887
+ {
888
+ // first, remove the edge from its vertices
889
+ RemoveEdgeFromVertex(edge->vB, edge);
890
+ RemoveEdgeFromVertex(edge->vT, edge);
891
+
892
+ // now remove the edge from double linked list (AEL)
893
+ Edge* prev = edge->prevE;
894
+ Edge* next = edge->nextE;
895
+ if (next) next->prevE = prev;
896
+ if (prev) prev->nextE = next;
897
+ edge->isActive = false;
898
+ if (firstActive == edge) firstActive = next;
899
+ }
900
+
901
+ Paths64 Delaunay::Execute(const Paths64& paths, TriangulateResult& triResult)
902
+ {
903
+ if (!AddPaths(paths))
904
+ {
905
+ triResult = TriangulateResult::no_polygons;
906
+ return Paths64(); // oops!
907
+ }
908
+
909
+ // if necessary fix path orientation because the algorithm
910
+ // expects clockwise outer paths and counter-clockwise inner paths
911
+ if (lowermostVertex->innerLM)
912
+ {
913
+ // the orientation of added paths must be wrong, so
914
+ // 1. reverse innerLM flags ...
915
+ Vertex2* lm;
916
+ while (!locMinStack.empty())
917
+ {
918
+ lm = locMinStack.top();
919
+ lm->innerLM = !lm->innerLM;
920
+ locMinStack.pop();
921
+ }
922
+ // 2. swap edge kinds
923
+ for (Edge* e : allEdges)
924
+ if (e->kind == EdgeKind::ascend)
925
+ e->kind = EdgeKind::descend;
926
+ else
927
+ e->kind = EdgeKind::ascend;
928
+ }
929
+ else
930
+ {
931
+ // path orientation is fine so ...
932
+ while (!locMinStack.empty())
933
+ locMinStack.pop();
934
+ }
935
+
936
+ std::sort(allEdges.begin(), allEdges.end(), EdgeListSort);
937
+ if (!FixupEdgeIntersects())
938
+ {
939
+ CleanUp();
940
+ triResult = TriangulateResult::paths_intersect;
941
+ return Paths64(); // oops!
942
+ }
943
+
944
+ std::sort(allVertices.begin(), allVertices.end(), VertexListSort);
945
+ MergeDupOrCollinearVertices();
946
+
947
+ int64_t currY = allVertices[0]->pt.y;
948
+ for (auto vIt = allVertices.begin(); vIt != allVertices.end(); ++vIt)
949
+ {
950
+ Vertex2* v = *vIt;
951
+ if (v->edges.empty()) continue;
952
+ if (v->pt.y != currY)
953
+ {
954
+ // JOIN AN INNER LOCMIN WITH A SUITABLE EDGE BELOW
955
+ while (!locMinStack.empty())
956
+ {
957
+ Vertex2* lm = locMinStack.top();
958
+ locMinStack.pop();
959
+ Edge* e = CreateInnerLocMinLooseEdge(lm);
960
+ if (!e)
961
+ {
962
+ CleanUp();
963
+ triResult = TriangulateResult::fail;
964
+ return Paths64(); // oops!
965
+ }
966
+
967
+ if (IsHorizontal(*e))
968
+ {
969
+ if (e->vL == e->vB)
970
+ DoTriangulateLeft(e, e->vB, currY); else
971
+ DoTriangulateRight(e, e->vB, currY);
972
+ }
973
+ else
974
+ {
975
+ DoTriangulateLeft(e, e->vB, currY);
976
+ if (!EdgeCompleted(e))
977
+ DoTriangulateRight(e, e->vB, currY);
978
+ }
979
+
980
+ // and because adding locMin edges to Actives was delayed ..
981
+ AddEdgeToActives(lm->edges[0]);
982
+ AddEdgeToActives(lm->edges[1]);
983
+ }
984
+
985
+ while (!horzEdgeStack.empty())
986
+ {
987
+ Edge* e = horzEdgeStack.top();
988
+ horzEdgeStack.pop();
989
+ if (EdgeCompleted(e)) continue;
990
+ if (e->vB == e->vL) // #45
991
+ {
992
+ if (IsLeftEdge(*e))
993
+ DoTriangulateLeft(e, e->vB, currY);
994
+ }
995
+ else
996
+ if (IsRightEdge(*e))
997
+ DoTriangulateRight(e, e->vB, currY);
998
+ }
999
+ currY = v->pt.y;
1000
+ }
1001
+
1002
+ for (int i = static_cast<int>(v->edges.size()) - 1; i >= 0; --i)
1003
+ {
1004
+ // the following line may look superfluous, but within this loop
1005
+ // v->edges may be altered with additions and or deletions.
1006
+ // So this line *is* necessary (and why we can't use an iterator).
1007
+ // Also, we need to use a *descending* index which is safe because
1008
+ // any additions will be loose edges which are ignored here.
1009
+ if (i >= static_cast<int>(v->edges.size())) continue;
1010
+
1011
+ Edge* e = v->edges[i];
1012
+ if (EdgeCompleted(e) || IsLooseEdge(*e)) continue;
1013
+
1014
+ if (v == e->vB)
1015
+ {
1016
+ if (IsHorizontal(*e))
1017
+ horzEdgeStack.push(e);
1018
+ // delay adding locMin edges to actives
1019
+ if (!v->innerLM)
1020
+ AddEdgeToActives(e);
1021
+ }
1022
+ else
1023
+ {
1024
+ if (IsHorizontal(*e))
1025
+ horzEdgeStack.push(e);
1026
+ else if (IsLeftEdge(*e))
1027
+ DoTriangulateLeft(e, e->vB, v->pt.y);
1028
+ else
1029
+ DoTriangulateRight(e, e->vB, v->pt.y);
1030
+ }
1031
+ } // for v->edges loop
1032
+
1033
+ if (v->innerLM) locMinStack.push(v);
1034
+
1035
+ } // for allVertices loop
1036
+
1037
+
1038
+ while (!horzEdgeStack.empty())
1039
+ {
1040
+ Edge* e = horzEdgeStack.top();
1041
+ horzEdgeStack.pop();
1042
+ if (!EdgeCompleted(e) && e->vB == e->vL)
1043
+ DoTriangulateLeft(e, e->vB, currY);
1044
+ }
1045
+
1046
+ if (useDelaunay)
1047
+ {
1048
+ // Convert triangles to Delaunay conforming
1049
+ while (!pendingDelaunayStack.empty())
1050
+ {
1051
+ Edge* e = pendingDelaunayStack.top();
1052
+ pendingDelaunayStack.pop();
1053
+ ForceLegal(e);
1054
+ }
1055
+ }
1056
+
1057
+ Paths64 res;
1058
+ res.reserve(allTriangles.size());
1059
+ for (auto tri : allTriangles)
1060
+ {
1061
+ Path64 p = PathFromTriangle(*tri);
1062
+ int cps = CrossProductSign(p[0], p[1], p[2]);
1063
+ if (cps == 0) continue; // skip any empty triangles
1064
+ if (cps < 0) // ccw
1065
+ std::reverse(p.begin(), p.end());
1066
+ res.push_back(p);
1067
+ }
1068
+
1069
+ CleanUp();
1070
+ triResult = TriangulateResult::success;
1071
+ return res;
1072
+ }
1073
+
1074
+ static double DistSqr(const Point64& pt1, const Point64& pt2)
1075
+ {
1076
+ return Sqr(pt1.x - pt2.x) + Sqr(pt1.y - pt2.y);
1077
+ }
1078
+
1079
+ void Delaunay::AddPath(const Path64& path)
1080
+ {
1081
+ size_t len = path.size(), i0 = 0, iPrev, iNext;
1082
+ // find the first locMin for the current path
1083
+ if (!FindLocMinIdx(path, len, i0)) return;
1084
+ iPrev = Prev(i0, len);
1085
+ while (path[iPrev] == path[i0]) iPrev = Prev(iPrev, len);
1086
+ iNext = Next(i0, len);
1087
+
1088
+ // it is possible for a locMin here to simply be a
1089
+ // collinear spike that should be ignored, so ...
1090
+ size_t i = i0;
1091
+ while (CrossProductSign(path[iPrev], path[i], path[iNext]) == 0)
1092
+ {
1093
+ FindLocMinIdx(path, len, i);
1094
+ if (i == i0) return; // this is an entirely collinear path
1095
+ iPrev = Prev(i, len);
1096
+ while (path[iPrev] == path[i]) iPrev = Prev(iPrev, len);
1097
+ iNext = Next(i, len);
1098
+ }
1099
+
1100
+ size_t vert_cnt = allVertices.size();
1101
+
1102
+ // we are now at the first legitimate locMin
1103
+ Vertex2* v0 = allVertices.emplace_back(new Vertex2(path[i]));
1104
+
1105
+ if (LeftTurning(path[iPrev], path[i], path[iNext]))
1106
+ v0->innerLM = true;
1107
+ Vertex2* vPrev = v0;
1108
+ i = iNext;
1109
+
1110
+ for (;;)
1111
+ {
1112
+ // vPrev is a locMin here
1113
+ locMinStack.push(vPrev);
1114
+ // ? update lowermostVertex ...
1115
+ if (!lowermostVertex ||
1116
+ vPrev->pt.y > lowermostVertex->pt.y ||
1117
+ (vPrev->pt.y == lowermostVertex->pt.y &&
1118
+ vPrev->pt.x < lowermostVertex->pt.x))
1119
+ lowermostVertex = vPrev;
1120
+
1121
+ iNext = Next(i, len);
1122
+ if (CrossProductSign(vPrev->pt, path[i], path[iNext]) == 0)
1123
+ {
1124
+ i = iNext;
1125
+ continue;
1126
+ }
1127
+
1128
+ // ascend up next bound to LocMax
1129
+ while (path[i].y <= vPrev->pt.y)
1130
+ {
1131
+ Vertex2* v = allVertices.emplace_back(new Vertex2(path[i]));
1132
+ CreateEdge(vPrev, v, EdgeKind::ascend);
1133
+ vPrev = v;
1134
+ i = iNext;
1135
+ iNext = Next(i, len);
1136
+
1137
+ while (CrossProductSign(vPrev->pt, path[i], path[iNext]) == 0)
1138
+ {
1139
+ i = iNext;
1140
+ iNext = Next(i, len);
1141
+ }
1142
+ }
1143
+
1144
+ // Now at a locMax, so descend to next locMin
1145
+ Vertex2* vPrevPrev = vPrev;
1146
+ while (i != i0 && path[i].y >= vPrev->pt.y)
1147
+ {
1148
+ Vertex2* v = allVertices.emplace_back(new Vertex2(path[i]));
1149
+ CreateEdge(v, vPrev, EdgeKind::descend);
1150
+ vPrevPrev = vPrev;
1151
+ vPrev = v;
1152
+ i = iNext;
1153
+ iNext = Next(i, len);
1154
+
1155
+ while (CrossProductSign(vPrev->pt, path[i], path[iNext]) == 0)
1156
+ {
1157
+ i = iNext;
1158
+ iNext = Next(i, len);
1159
+ }
1160
+ }
1161
+
1162
+ // now at the next locMin
1163
+ if (i == i0) break; // break for(;;) loop
1164
+ if (LeftTurning(vPrevPrev->pt, vPrev->pt, path[i]))
1165
+ vPrev->innerLM = true;
1166
+ }
1167
+ CreateEdge(v0, vPrev, EdgeKind::descend);
1168
+
1169
+ // finally, ignore this path if is not a polygon or too small
1170
+ len = allVertices.size() - vert_cnt;
1171
+ i = vert_cnt;
1172
+ if (len < 3 || (len == 3 && // or just a very tiny triangle
1173
+ ((DistSqr(allVertices[i]->pt, allVertices[i + 1]->pt) <= 1) ||
1174
+ (DistSqr(allVertices[i + 1]->pt, allVertices[i + 2]->pt) <= 1) ||
1175
+ (DistSqr(allVertices[i + 2]->pt, allVertices[i]->pt) <= 1))))
1176
+ {
1177
+ for (size_t j = vert_cnt; j < allVertices.size(); ++j)
1178
+ allVertices[j]->edges.clear(); // flag to ignore
1179
+ }
1180
+ }
1181
+
1182
+ bool Delaunay::AddPaths(const Paths64& paths)
1183
+ {
1184
+ const auto total_vertex_count =
1185
+ std::accumulate(paths.begin(), paths.end(), size_t(0),
1186
+ [](const auto& a, const Path64& path)
1187
+ {return a + path.size(); });
1188
+ if (total_vertex_count == 0) return false;
1189
+ allVertices.reserve(allVertices.capacity() + total_vertex_count);
1190
+ allEdges.reserve(allEdges.capacity() + total_vertex_count);
1191
+
1192
+ for (const Path64& path : paths)
1193
+ AddPath(path);
1194
+ return (allVertices.size() > 2);
1195
+ }
1196
+
1197
+ TriangulateResult Triangulate(const Paths64& pp, Paths64& solution, bool useDelaunay)
1198
+ {
1199
+ TriangulateResult result;
1200
+ Delaunay d(useDelaunay);
1201
+ solution = d.Execute(pp, result);
1202
+ return result;
1203
+ }
1204
+
1205
+ TriangulateResult Triangulate(const PathsD& pp, int decPlaces, PathsD& solution, bool useDelaunay)
1206
+ {
1207
+ int ec = 0;
1208
+ double scale;
1209
+ TriangulateResult result;
1210
+ if (decPlaces <= 0) scale = 1;
1211
+ else if (decPlaces > 8) scale = std::pow(10, 8);
1212
+ else scale = std::pow(10, decPlaces);
1213
+ Paths64 pp64 = ScalePaths<int64_t, double>(pp, scale, ec);
1214
+
1215
+ Delaunay d(useDelaunay);
1216
+ Paths64 sol64 = d.Execute(pp64, result);
1217
+ solution = ScalePaths<double, int64_t>(sol64, 1 / scale, ec);
1218
+ return result;
1219
+ }
1220
+
1221
+ } // Clipper2Lib namespace