1   
2 /** D port (betterC) of the earcut polygon triangulation library.
3     
4     Ported from: https://github.com/mapbox/earcut.hpp
5 
6 Copyright:
7  Copyright (c) 2020, Ferhat Kurtulmuş.
8  License:
9    $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
10 */
11 
12 module earcutd;
13 
14 version(LDC){
15     version(D_BetterC){
16         pragma(LDC_no_moduleinfo);
17     }
18 }
19 
20 import dvector;
21 
22 private {
23     import core.stdc.stdlib;
24     import std.traits;
25     import std.traits: isArray;
26     import std.range.primitives: isRandomAccessRange;
27     import std.math;
28     import std.stdint;
29     import std.algorithm.comparison: min, max;
30 }
31 
32 struct Earcut(N, Polygon) {
33 
34     static if (isArray!Polygon){
35         alias VecPoint = typeof(Polygon.init[0]);
36     } else static if (isRandomAccessRange!Polygon || __traits(compiles, Polygon.init[0])){
37         alias ASeq = TemplateArgsOf!Polygon;
38         alias VecPoint = ASeq[0];
39     } else
40         static assert(0, typeof(Polygon).stringof ~ " type is not supported.");
41     
42     static if (isArray!VecPoint){
43         alias Point = typeof(VecPoint.init[0]);
44     } else static if (isRandomAccessRange!VecPoint){
45         alias ASeq2 = TemplateArgsOf!VecPoint;
46         alias Point = ASeq2[0];
47     } else
48         static assert(0, typeof(VecPoint).stringof ~ " type is not supported. You must use a slice or a RandomAccessRange");
49 
50     Dvector!N indices;
51     size_t vertices = 0;
52 
53     struct Node {
54         
55         N i;
56         double x;
57         double y;
58         
59     @nogc nothrow:
60         int opCmp(Node rhs) {
61             if (x < rhs.x) return -1;
62             if (rhs.x < x) return 1;
63             return 0;
64         }
65 
66         // previous and next vertice nodes in a polygon ring
67         Node* prev;
68         Node* next;
69 
70         // z-order curve value
71         int32_t z;
72 
73         // previous and next nodes in z-order
74         Node* prevZ;
75         Node* nextZ;
76 
77         // indicates whether this is a steiner point
78         bool steiner = false;
79 
80         this(N index, double x_, double y_){
81             i = index;
82             x = x_;
83             y = y_;
84         }
85     }
86 
87     bool hashing;
88     double minX, maxX;
89     double minY, maxY;
90     double inv_size = 0.0;
91 
92     struct ObjectPool(T) {
93         @nogc nothrow:
94         public:
95         this(size_t blockSize_) {
96             reset(blockSize_);
97         }
98         ~this() {
99             clear();
100         }
101 
102         T* construct(N)(N i, double x, double y) {
103             if (currentIndex >= blockSize) {
104                 currentBlock = cast(T*)malloc(blockSize*T.sizeof);
105                 allocations.pushBack(currentBlock);
106                 currentIndex = 0;
107             }
108             T* object = &currentBlock[currentIndex++];
109             *object = T(i, x, y);
110             return object;
111         }
112         void reset(size_t newBlockSize) {
113             foreach (allocation; allocations) {
114                 core.stdc.stdlib.free(allocation);
115             }
116             allocations.free();
117             blockSize = max(1, newBlockSize);
118             currentBlock = null;
119             currentIndex = blockSize;
120         }
121         void clear() { reset(blockSize); }
122         private:
123         T* currentBlock = null;
124         size_t currentIndex = 1;
125         size_t blockSize = 1;
126         Dvector!(T*) allocations;
127     }
128 
129     ObjectPool!Node nodes;
130     
131     @nogc nothrow:
132 
133     void run(ref Polygon points){
134         // reset
135         indices.free;
136         vertices = 0;
137 
138         if (!points.length) return;
139 
140         double x;
141         double y;
142         int threshold = 80;
143         size_t len = 0;
144 
145         for (size_t i = 0; threshold >= 0 && i < points.length; i++) {
146             threshold -= cast(int)(points[i].length);
147             len += points[i].length;
148         }
149 
150         //estimate size of nodes and indices
151         nodes.reset(len * 3 / 2);
152         indices.reserve(len + points[0].length);
153 
154         Node* outerNode = linkedList(points[0], true);
155         if (!outerNode || outerNode.prev == outerNode.next) return;
156 
157         if (points.length > 1) outerNode = eliminateHoles(points, outerNode);
158 
159         // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox
160         hashing = threshold < 0;
161         if (hashing) {
162             Node* p = outerNode.next;
163             minX = maxX = outerNode.x;
164             minY = maxY = outerNode.y;
165             do {
166                 x = p.x;
167                 y = p.y;
168                 minX = min(minX, x);
169                 minY = min(minY, y);
170                 maxX = max(maxX, x);
171                 maxY = max(maxY, y);
172                 p = p.next;
173             } while (p != outerNode);
174 
175             // minX, minY and size are later used to transform coords into integers for z-order calculation
176             inv_size = max(maxX - minX, maxY - minY);
177             inv_size = inv_size != .0 ? (1. / inv_size) : .0;
178         }
179 
180         earcutLinked(outerNode);
181 
182         nodes.clear();        
183     }
184 
185     Node* linkedList(Ring)(ref Ring points, const bool clockwise) {
186         //using Point = typename Ring::value_type;
187         double sum = 0;
188         const size_t len = points.length;
189         size_t i, j;
190         Node* last = null;
191 
192         // calculate original winding order of a polygon ring
193         for (i = 0, j = len > 0 ? len - 1 : 0; i < len; j = i++) {
194             const p1 = points[i];
195             const p2 = points[j];
196             const double p20 = p2[0];
197             const double p10 = p1[0];
198             const double p11 = p1[1];
199             const double p21 = p2[1];
200             
201             sum += (p20 - p10) * (p11 + p21);
202         }
203 
204         // link points into circular doubly-linked list in the specified winding order
205         if (clockwise == (sum > 0)) {
206             for (i = 0; i < len; i++) last = insertNode(vertices + i, points[i], last);
207         } else {
208             for (i = len; i-- > 0;) last = insertNode(vertices + i, points[i], last);
209         }
210 
211         if (last && equals(last, last.next)) {
212             removeNode(last);
213             last = last.next;
214         }
215 
216         vertices += len;
217 
218         return last;
219     }
220 
221     Node* filterPoints(Node* start, Node* end = null){
222         if (!end) end = start;
223 
224         Node* p = start;
225         bool again;
226         do {
227             again = false;
228 
229             if (!p.steiner && (equals(p, p.next) || area(p.prev, p, p.next) == 0)) {
230                 removeNode(p);
231                 p = end = p.prev;
232 
233                 if (p == p.next) break;
234                 again = true;
235 
236             } else {
237                 p = p.next;
238             }
239         } while (again || p != end);
240 
241         return end;
242     }
243 
244     void earcutLinked(Node* ear, int pass = 0){
245         if (!ear) return;
246 
247         // interlink polygon nodes in z-order
248         if (!pass && hashing) indexCurve(ear);
249 
250         Node* stop = ear;
251         Node* prev;
252         Node* next;
253 
254         int iterations = 0;
255 
256         // iterate through ears, slicing them one by one
257         while (ear.prev != ear.next) {
258             iterations++;
259             prev = ear.prev;
260             next = ear.next;
261 
262             if (hashing ? isEarHashed(ear) : isEar(ear)) {
263                 // cut off the triangle
264                 indices.pushBack(prev.i);
265                 indices.pushBack(ear.i);
266                 indices.pushBack(next.i);
267 
268                 removeNode(ear);
269 
270                 // skipping the next vertice leads to less sliver triangles
271                 ear = next.next;
272                 stop = next.next;
273 
274                 continue;
275             }
276 
277             ear = next;
278 
279             // if we looped through the whole remaining polygon and can't find any more ears
280             if (ear == stop) {
281                 // try filtering points and slicing again
282                 if (!pass) earcutLinked(filterPoints(ear), 1);
283 
284                 // if this didn't work, try curing all small self-intersections locally
285                 else if (pass == 1) {
286                     ear = cureLocalIntersections(filterPoints(ear));
287                     earcutLinked(ear, 2);
288 
289                 // as a last resort, try splitting the remaining polygon into two
290                 } else if (pass == 2) splitEarcut(ear);
291 
292                 break;
293             }
294         }
295 
296     }
297 
298     bool isEar(Node* ear) {
299         Node* a = ear.prev;
300         Node* b = ear;
301         Node* c = ear.next;
302 
303         if (area(a, b, c) >= 0) return false; // reflex, can't be an ear
304 
305         // now make sure we don't have other points inside the potential ear
306         Node* p = ear.next.next;
307 
308         while (p != ear.prev) {
309             if (pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) &&
310                 area(p.prev, p, p.next) >= 0) return false;
311             p = p.next;
312         }
313 
314         return true;
315     }
316 
317     bool isEarHashed(Node* ear) {
318         Node* a = ear.prev;
319         Node* b = ear;
320         Node* c = ear.next;
321 
322         if (area(a, b, c) >= 0) return false; // reflex, can't be an ear
323 
324         // triangle bbox; min & max are calculated like this for speed
325         const double minTX = min(a.x, min(b.x, c.x));
326         const double minTY = min(a.y, min(b.y, c.y));
327         const double maxTX = max(a.x, max(b.x, c.x));
328         const double maxTY = max(a.y, max(b.y, c.y));
329 
330         // z-order range for the current triangle bbox;
331         const int32_t minZ = zOrder(minTX, minTY);
332         const int32_t maxZ = zOrder(maxTX, maxTY);
333 
334         // first look for points inside the triangle in increasing z-order
335         Node* p = ear.nextZ;
336 
337         while (p && p.z <= maxZ) {
338             if (p != ear.prev && p != ear.next &&
339                 pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) &&
340                 area(p.prev, p, p.next) >= 0) return false;
341             p = p.nextZ;
342         }
343 
344         // then look for points in decreasing z-order
345         p = ear.prevZ;
346 
347         while (p && p.z >= minZ) {
348             if (p != ear.prev && p != ear.next &&
349                 pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) &&
350                 area(p.prev, p, p.next) >= 0) return false;
351             p = p.prevZ;
352         }
353 
354         return true;
355     }
356 
357     Node* cureLocalIntersections(Node* start) {
358         Node* p = start;
359         do {
360             Node* a = p.prev;
361             Node* b = p.next.next;
362 
363             // a self-intersection where edge (v[i-1],v[i]) intersects (v[i+1],v[i+2])
364             if (!equals(a, b) && intersects(a, p, p.next, b) && locallyInside(a, b) && locallyInside(b, a)) {
365                 indices.pushBack(a.i);
366                 indices.pushBack(p.i);
367                 indices.pushBack(b.i);
368 
369                 // remove two nodes involved
370                 removeNode(p);
371                 removeNode(p.next);
372 
373                 p = start = b;
374             }
375             p = p.next;
376         } while (p != start);
377 
378         return filterPoints(p);
379     }
380 
381     void splitEarcut(Node* start) {
382         // look for a valid diagonal that divides the polygon into two
383         Node* a = start;
384         do {
385             Node* b = a.next.next;
386             while (b != a.prev) {
387                 if (a.i != b.i && isValidDiagonal(a, b)) {
388                     // split the polygon in two by the diagonal
389                     Node* c = splitPolygon(a, b);
390 
391                     // filter colinear points around the cuts
392                     a = filterPoints(a, a.next);
393                     c = filterPoints(c, c.next);
394 
395                     // run earcut on each half
396                     earcutLinked(a);
397                     earcutLinked(c);
398                     return;
399                 }
400                 b = b.next;
401             }
402             a = a.next;
403         } while (a != start);
404     }
405 
406     Node* eliminateHoles(Polygon)(ref Polygon points, Node* outerNode){
407         const size_t len = points.length;
408 
409         Dvector!(Node*) queue;
410         for (size_t i = 1; i < len; i++) {
411             Node* list = linkedList(points[i], false);
412             if (list) {
413                 if (list == list.next) list.steiner = true;
414                 queue.pushBack(getLeftmost(list));
415             }
416         }
417         
418         import std.algorithm.sorting;
419         
420         auto _q = queue.slice.sort!("a < b");
421 
422         // process holes from left to right
423         for (size_t i = 0; i < queue.length; i++) {
424             eliminateHole(_q[i], outerNode);
425             outerNode = filterPoints(outerNode, outerNode.next);
426         }
427 
428         queue.free;
429         return outerNode;
430     }
431 
432     void eliminateHole(Node* hole, Node* outerNode) {
433         outerNode = findHoleBridge(hole, outerNode);
434         if (outerNode) {
435             Node* b = splitPolygon(outerNode, hole);
436 
437             // filter out colinear points around cuts
438             filterPoints(outerNode, outerNode.next);
439             filterPoints(b, b.next);
440         }
441     }
442 
443 // David Eberly's algorithm for finding a bridge between hole and outer polygon
444     Node* findHoleBridge(Node* hole, Node* outerNode) {
445         Node* p = outerNode;
446         double hx = hole.x;
447         double hy = hole.y;
448         double qx = -double.max;
449         Node* m = null;
450 
451         // find a segment intersected by a ray from the hole's leftmost Vertex to the left;
452         // segment's endpoint with lesser x will be potential connection Vertex
453         do {
454             if (hy <= p.y && hy >= p.next.y && p.next.y != p.y) {
455             double x = p.x + (hy - p.y) * (p.next.x - p.x) / (p.next.y - p.y);
456             if (x <= hx && x > qx) {
457                 qx = x;
458                 if (x == hx) {
459                     if (hy == p.y) return p;
460                     if (hy == p.next.y) return p.next;
461                 }
462                 m = p.x < p.next.x ? p : p.next;
463             }
464             }
465             p = p.next;
466         } while (p != outerNode);
467 
468         if (!m) return null;
469 
470         if (hx == qx) return m; // hole touches outer segment; pick leftmost endpoint
471 
472         // look for points inside the triangle of hole Vertex, segment intersection and endpoint;
473         // if there are no points found, we have a valid connection;
474         // otherwise choose the Vertex of the minimum angle with the ray as connection Vertex
475 
476         Node* stop = m;
477         double tanMin = double.max;
478         double tanCur = 0;
479 
480         p = m;
481         double mx = m.x;
482         double my = m.y;
483 
484         do {
485             if (hx >= p.x && p.x >= mx && hx != p.x &&
486                 pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y)) {
487 
488                 tanCur = fabs(hy - p.y) / (hx - p.x); // tangential
489 
490                 if (locallyInside(p, hole) &&
491                     (tanCur < tanMin || (tanCur == tanMin && (p.x > m.x || sectorContainsSector(m, p))))) {
492                     m = p;
493                     tanMin = tanCur;
494                 }
495             }
496 
497             p = p.next;
498         } while (p != stop);
499 
500         return m;
501     }
502 
503     bool sectorContainsSector(Node* m, Node* p) {
504         return area(m.prev, m, p.prev) < 0 && area(p.next, m, m.next) < 0;
505     }
506 
507     void indexCurve(Node* start) {
508         assert(start);
509         Node* p = start;
510 
511         do {
512             p.z = p.z ? p.z : zOrder(p.x, p.y);
513             p.prevZ = p.prev;
514             p.nextZ = p.next;
515             p = p.next;
516         } while (p != start);
517 
518         p.prevZ.nextZ = null;
519         p.prevZ = null;
520 
521         sortLinked(p);
522     }
523 
524     Node* sortLinked(Node* list) {
525         assert(list);
526         Node* p;
527         Node* q;
528         Node* e;
529         Node* tail;
530         int i, numMerges, pSize, qSize;
531         int inSize = 1;
532 
533         for (;;) {
534             p = list;
535             list = null;
536             tail = null;
537             numMerges = 0;
538 
539             while (p) {
540                 numMerges++;
541                 q = p;
542                 pSize = 0;
543                 for (i = 0; i < inSize; i++) {
544                     pSize++;
545                     q = q.nextZ;
546                     if (!q) break;
547                 }
548 
549                 qSize = inSize;
550 
551                 while (pSize > 0 || (qSize > 0 && q)) {
552 
553                     if (pSize == 0) {
554                         e = q;
555                         q = q.nextZ;
556                         qSize--;
557                     } else if (qSize == 0 || !q) {
558                         e = p;
559                         p = p.nextZ;
560                         pSize--;
561                     } else if (p.z <= q.z) {
562                         e = p;
563                         p = p.nextZ;
564                         pSize--;
565                     } else {
566                         e = q;
567                         q = q.nextZ;
568                         qSize--;
569                     }
570 
571                     if (tail) tail.nextZ = e;
572                     else list = e;
573 
574                     e.prevZ = tail;
575                     tail = e;
576                 }
577 
578                 p = q;
579             }
580 
581             tail.nextZ = null;
582 
583             if (numMerges <= 1) return list;
584 
585             inSize *= 2;
586         }
587     }
588 
589     int32_t zOrder(const double x_, const double y_) {
590         // coords are transformed into non-negative 15-bit integer range
591         int32_t x = cast(int32_t)(32767.0 * (x_ - minX) * inv_size);
592         int32_t y = cast(int32_t)(32767.0 * (y_ - minY) * inv_size);
593 
594         x = (x | (x << 8)) & 0x00FF00FF;
595         x = (x | (x << 4)) & 0x0F0F0F0F;
596         x = (x | (x << 2)) & 0x33333333;
597         x = (x | (x << 1)) & 0x55555555;
598 
599         y = (y | (y << 8)) & 0x00FF00FF;
600         y = (y | (y << 4)) & 0x0F0F0F0F;
601         y = (y | (y << 2)) & 0x33333333;
602         y = (y | (y << 1)) & 0x55555555;
603 
604         return x | (y << 1);
605     }
606     
607     Node* getLeftmost(Node* start) {
608         Node* p = start;
609         Node* leftmost = start;
610         do {
611             if (p.x < leftmost.x || (p.x == leftmost.x && p.y < leftmost.y))
612                 leftmost = p;
613             p = p.next;
614         } while (p != start);
615 
616         return leftmost;
617     }
618     
619     bool pointInTriangle(double ax, double ay, double bx, double by, double cx, double cy, double px, double py) const {
620         return (cx - px) * (ay - py) - (ax - px) * (cy - py) >= 0 &&
621             (ax - px) * (by - py) - (bx - px) * (ay - py) >= 0 &&
622             (bx - px) * (cy - py) - (cx - px) * (by - py) >= 0;
623     }
624 
625     bool isValidDiagonal(Node* a, Node* b) {
626         return a.next.i != b.i && a.prev.i != b.i && !intersectsPolygon(a, b) && // dones't intersect other edges
627             ((locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && // locally visible
628                 (area(a.prev, a, b.prev) != 0.0 || area(a, b.prev, b) != 0.0)) || // does not create opposite-facing sectors
629                 (equals(a, b) && area(a.prev, a, a.next) > 0 && area(b.prev, b, b.next) > 0)); // special zero-length case
630     }
631 
632     double area(Node* p, Node* q, Node* r) const {
633         return (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
634     }
635 
636     bool equals(Node* p1, Node* p2) {
637         return p1.x == p2.x && p1.y == p2.y;
638     }
639 
640     bool intersects(Node* p1, Node* q1, Node* p2, Node* q2) {
641         int o1 = sign(area(p1, q1, p2));
642         int o2 = sign(area(p1, q1, q2));
643         int o3 = sign(area(p2, q2, p1));
644         int o4 = sign(area(p2, q2, q1));
645 
646         if (o1 != o2 && o3 != o4) return true; // general case
647 
648         if (o1 == 0 && onSegment(p1, p2, q1)) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1
649         if (o2 == 0 && onSegment(p1, q2, q1)) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1
650         if (o3 == 0 && onSegment(p2, p1, q2)) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2
651         if (o4 == 0 && onSegment(p2, q1, q2)) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2
652 
653         return false;
654     }
655 
656     bool onSegment(Node* p, Node* q, Node* r) {
657         return q.x <= max(p.x, r.x) &&
658             q.x >= min(p.x, r.x) &&
659             q.y <= max(p.y, r.y) &&
660             q.y >= min(p.y, r.y);
661     }
662 
663     int sign(double val) {
664         return (0.0 < val) - (val < 0.0);
665     }
666 
667     bool intersectsPolygon(Node* a, Node* b) {
668         Node* p = a;
669         do {
670             if (p.i != a.i && p.next.i != a.i && p.i != b.i && p.next.i != b.i &&
671                     intersects(p, p.next, a, b)) return true;
672             p = p.next;
673         } while (p != a);
674 
675         return false;
676     }
677 
678     bool locallyInside(Node* a, Node* b) {
679         return area(a.prev, a, a.next) < 0 ?
680             area(a, b, a.next) >= 0 && area(a, a.prev, b) >= 0 :
681             area(a, b, a.prev) < 0 || area(a, a.next, b) < 0;
682     }
683 
684     bool middleInside(Node* a, Node* b) {
685         Node* p = a;
686         bool inside = false;
687         double px = (a.x + b.x) / 2;
688         double py = (a.y + b.y) / 2;
689         do {
690             if (((p.y > py) != (p.next.y > py)) && p.next.y != p.y &&
691                     (px < (p.next.x - p.x) * (py - p.y) / (p.next.y - p.y) + p.x))
692                 inside = !inside;
693             p = p.next;
694         } while (p != a);
695 
696         return inside;
697     }
698     
699     Node* splitPolygon(Node* a, Node* b) {
700         Node* a2 = nodes.construct(a.i, a.x, a.y);
701         Node* b2 = nodes.construct(b.i, b.x, b.y);
702         Node* an = a.next;
703         Node* bp = b.prev;
704 
705         a.next = b;
706         b.prev = a;
707 
708         a2.next = an;
709         an.prev = a2;
710 
711         b2.next = a2;
712         a2.prev = b2;
713 
714         bp.next = b2;
715         b2.prev = bp;
716 
717         return b2;
718     }
719 
720     Node* insertNode(size_t i, ref Point pt, Node* last) {
721         Node* p = nodes.construct(cast(N)i, pt[0], pt[1]);
722 
723         if (!last) {
724             p.prev = p;
725             p.next = p;
726 
727         } else {
728             assert(last);
729             p.next = last.next;
730             p.prev = last;
731             last.next.prev = p;
732             last.next = p;
733         }
734         return p;
735     }
736 
737     void removeNode(Node* p) {
738         p.next.prev = p.prev;
739         p.prev.next = p.next;
740 
741         if (p.prevZ) p.prevZ.nextZ = p.nextZ;
742         if (p.nextZ) p.nextZ.prevZ = p.prevZ;
743     }
744 
745 }
746 
747 // those are optional. You can just use Tuple!(int, int)
748 struct Pair(T){
749     T x;
750     T y;
751     @nogc nothrow:
752     inout(T) opIndex(size_t index) inout {
753         T[2] tmp = [x, y];
754         return tmp[index];
755     }
756 
757     void opIndexAssign(T)(T value, size_t index){
758         T*[2] tmp = [&x, &y];
759         *tmp[i] = value;
760     }
761 }
762 
763 struct Pair2(T){
764     T[2] coord;
765 
766     alias coord this;
767 
768     this(T x, T y,){
769         coord[0] = x;
770         coord[1] = y;
771     }
772 }