1 /*
2     Copyright © 2022, Inochi2D Project
3     Distributed under the 2-Clause BSD License, see LICENSE file.
4 
5     Authors:
6     - Luna Nielsen
7     - Asahi Lina
8 
9     in_circle() from poly2tri, licensed under BSD-3:
10         Copyright (c) 2009-2018, Poly2Tri Contributors
11         https://github.com/jhasse/poly2tri
12 */
13 module creator.viewport.common.mesh;
14 import creator.viewport;
15 import inochi2d;
16 import inochi2d.core.dbg;
17 import bindbc.opengl;
18 import std.algorithm.mutation;
19 
20 struct MeshVertex {
21     vec2 position;
22     MeshVertex*[] connections;
23 }
24 
25 void connect(MeshVertex* self, MeshVertex* other) {
26     if (isConnectedTo(self, other)) return;
27 
28     self.connections ~= other;
29     other.connections ~= self;
30 }
31  
32 void disconnect(MeshVertex* self, MeshVertex* other) {
33     import std.algorithm.searching : countUntil;
34     import std.algorithm.mutation : remove;
35     
36     auto idx = other.connections.countUntil(self);
37     if (idx != -1) other.connections = remove(other.connections, idx);
38 
39     idx = self.connections.countUntil(other);
40     if (idx != -1) self.connections = remove(self.connections, idx);
41 }
42 
43 void disconnectAll(MeshVertex* self) {
44     while(self.connections.length > 0) {
45         self.disconnect(self.connections[0]);
46     }
47 }
48 
49 bool isConnectedTo(MeshVertex* self, MeshVertex* other) {
50     if (other == null) return false;
51 
52     foreach(conn; other.connections) {
53         if (conn == self) return true;
54     }
55     return false;
56 }
57 
58 class IncMesh {
59 private:
60     MeshData* data;
61 
62     void mImport(ref MeshData data) {
63         // Reset vertex length
64         vertices.length = 0;
65 
66         // Iterate over flat mesh and extract it in to
67         // vertices and "connections"
68         MeshVertex*[] iVertices;
69 
70         iVertices.length = data.vertices.length;
71         foreach(idx, vertex; data.vertices) {
72             iVertices[idx] = new MeshVertex(vertex+data.origin, []);
73         }
74 
75         foreach(i; 0..data.indices.length/3) {
76             auto index = data.indices[i*3];
77             auto nindex = data.indices[(i*3)+1];
78             auto nnindex = data.indices[(i*3)+2];
79 
80             if (!iVertices[index].isConnectedTo(iVertices[nindex])) iVertices[index].connect(iVertices[nindex]);
81             if (!iVertices[nindex].isConnectedTo(iVertices[nnindex])) iVertices[nindex].connect(iVertices[nnindex]);
82             if (!iVertices[nnindex].isConnectedTo(iVertices[index])) iVertices[nnindex].connect(iVertices[index]);
83         }
84         
85         void printConnections(MeshVertex* v) {
86             import std.stdio;
87             ushort[] conns;
88             vec2[] coords;
89             foreach(conn; v.connections) {
90                 foreach(key, value; iVertices) {
91                     if (value == conn) {
92                         conns ~= cast(ushort)key;
93                         coords ~= value.position;
94                         break;
95                     }
96                 }
97             }
98         }
99 
100         foreach(i, vertex; iVertices) {
101             printConnections(vertex);
102             vertices ~= vertex;
103         }
104 
105         refresh();
106     }
107 
108     MeshData mExport() {
109         import std.algorithm.searching : canFind;
110         MeshData* newData = new MeshData;
111 
112         ushort[MeshVertex*] indices;
113         ushort indiceIdx = 0;
114         foreach(vertex; vertices) {
115             newData.vertices ~= vertex.position;
116             newData.uvs ~= vertex.position;
117             indices[vertex] = indiceIdx++;
118         }
119 
120         bool goesBackToRoot(MeshVertex* root, MeshVertex* vert) {
121             foreach(MeshVertex* conn; vert.connections) {
122                 if (conn == root) return true;
123             }
124             return false;
125         }
126 
127         bool hasIndiceSeq(ushort a, ushort b, ushort c) {
128             foreach(i; 0..newData.indices.length/3) {
129                 int score = 0;
130 
131                 if (newData.indices[(i*3)+0] == a || newData.indices[(i*3)+0] == b || newData.indices[(i*3)+0] == c) score++;
132                 if (newData.indices[(i*3)+1] == a || newData.indices[(i*3)+1] == b || newData.indices[(i*3)+1] == c) score++;
133                 if (newData.indices[(i*3)+2] == a || newData.indices[(i*3)+2] == b || newData.indices[(i*3)+2] == c) score++;
134 
135                 if (score == 3) return true;
136             }
137             return false;
138         }
139 
140         bool isAnyEdgeIntersecting(vec2[3] t1, vec2[3] t2) {
141             vec2 t1p1, t1p2, t2p1, t2p2;
142             static foreach(i; 0..3) {
143                 static foreach(j; 0..3) {
144                     t1p1 = t1[i];
145                     t1p2 = t1[(i+1)%3];
146                     t2p1 = t2[j];
147                     t2p2 = t2[(j+1)%3];
148 
149                     if (areLineSegmentsIntersecting(t1p1, t1p2, t2p1, t2p2)) return true;
150                 }
151             }
152             return false;
153         }
154 
155         bool isIntersectingWithTris(vec2[3] t1) {
156             foreach(i; 0..newData.indices.length/3) {
157                 vec2[3] verts = [
158                     newData.vertices[newData.indices[(i*3)+0]],
159                     newData.vertices[newData.indices[(i*3)+0]],
160                     newData.vertices[newData.indices[(i*3)+0]]
161                 ];
162                 if (isAnyEdgeIntersecting(t1, verts)) return true;
163             }
164             return false;
165         }
166 
167         MeshVertex*[] visited;
168         void mExportVisit(MeshVertex* v) {
169             visited ~= v;
170 
171             MeshVertex* findFreeIndice() {
172                 foreach (key; indices.keys) {
173                     if (indices[key] != newData.indices[$-1] && 
174                         indices[key] != newData.indices[$-2] && 
175                         indices[key] != newData.indices[$-3] && 
176                         !visited.canFind(key)) return cast(MeshVertex*)key;
177                 }
178                 return null;
179             }
180 
181             // Second vertex
182             foreach(MeshVertex* conn; v.connections) {
183                 if (conn == v) continue;
184 
185                 // Third vertex
186                 foreach(MeshVertex* conn2; conn.connections) {
187                     if (goesBackToRoot(v, conn2)) {
188 
189                         // Skip repeat sequences
190                         if (hasIndiceSeq(indices[v], indices[conn], indices[conn2])) continue;
191                         if (isIntersectingWithTris([v.position, conn.position, conn2.position])) continue;
192                         
193 
194                         // Add new indices
195                         newData.indices ~= [
196                             indices[v],
197                             indices[conn],
198                             indices[conn2]
199                         ];
200                         break;
201                     }
202                 }
203             }
204 
205             foreach(MeshVertex* conn; v.connections) {
206                 if (!visited.canFind(conn)) mExportVisit(conn);
207             }
208         }
209 
210         // Run the export
211         foreach(ref vert; vertices) {
212             if (!visited.canFind(vert)) {
213                 mExportVisit(vert);
214             }
215         }
216 
217         // Save the data as the new data and refresh
218         data = newData;
219         reset();
220         return *newData;
221     }
222 
223     vec3[] points;
224     vec3[] lines;
225     vec3[] wlines;
226     void regen() {
227         points.length = 0;
228         
229         // Updates all point positions
230         foreach(i, vert; vertices) {
231             points ~= vec3(vert.position, 0);
232         }
233     }
234 
235     void regenConnections() {
236         import std.algorithm.searching : canFind;
237 
238         // setup
239         lines.length = 0;
240         wlines.length = 0;
241         MeshVertex*[] visited;
242         
243         // our crazy recursive func
244         void recurseLines(MeshVertex* cur) {
245             visited ~= cur;
246 
247             // First add the lines
248             foreach(conn; cur.connections) {
249 
250                 // Skip already scanned connections
251                 if (!visited.canFind(conn)) {
252                     lines ~= [vec3(cur.position, 0), vec3(conn.position, 0)];
253                 }
254             }
255             // Then scan the next unvisited point
256             foreach(conn; cur.connections) {
257 
258                 // Skip already scanned connections
259                 if (!visited.canFind(conn)) {
260                     recurseLines(conn);
261                 }
262             }
263         }
264 
265         foreach(ref vert; vertices) {
266             if (!visited.canFind(vert)) {
267                 recurseLines(vert);
268             }
269         }
270     }
271 
272 public:
273     float selectRadius = 16f;
274     MeshVertex*[] vertices;
275     bool changed;
276 
277     /**
278         Constructs a new IncMesh
279     */
280     this(ref MeshData mesh) {
281         import_(mesh);
282     }
283 
284     final
285     void import_(ref MeshData mesh) {
286         data = &mesh;
287         mImport(mesh);
288     }
289     
290     /**
291         Exports the working mesh to a MeshData object.
292     */
293     final
294     MeshData export_() {
295         return mExport();
296     }
297 
298     /**
299         Resets mesh to prior state
300     */
301     void reset() {
302         mImport(*data);
303         refresh();
304         changed = true;
305     }
306 
307     /**
308         Clears the mesh of everything
309     */
310     void clear() {
311         vertices.length = 0;
312         refresh();
313         changed = true;
314     }
315 
316     /**
317         Refreshes graphical portion of the mesh
318     */
319     void refresh() {
320         regen();
321         regenConnections();
322     }
323 
324     /**
325         Draws the mesh
326     */
327     void drawLines(mat4 trans = mat4.identity, vec4 color = vec4(0.7, 0.7, 0.7, 1)) {
328         if (lines.length > 0) {
329             inDbgSetBuffer(lines);
330             inDbgDrawLines(color, trans);
331         }
332 
333         if (wlines.length > 0) {
334             inDbgSetBuffer(wlines);
335             inDbgDrawLines(vec4(0.7, 0.2, 0.2, 1), trans);
336         }
337     }
338 
339     void drawPoints(mat4 trans = mat4.identity) {
340         if (points.length > 0) {
341             inDbgSetBuffer(points);
342             inDbgPointsSize(10);
343             inDbgDrawPoints(vec4(0, 0, 0, 1), trans);
344             inDbgPointsSize(6);
345             inDbgDrawPoints(vec4(1, 1, 1, 1), trans);
346         }
347     }
348 
349     void drawPointSubset(MeshVertex*[] subset, vec4 color, mat4 trans = mat4.identity, float size=6) {
350         vec3[] subPoints;
351 
352         if (subset.length == 0) return;
353 
354         // Updates all point positions
355         foreach(vtx; subset) {
356             subPoints ~= vec3(vtx.position, 0);
357         }
358         inDbgSetBuffer(subPoints);
359         inDbgPointsSize(size);
360         inDbgDrawPoints(color, trans);
361     }
362 
363     void drawPoint(vec2 point, vec4 color, mat4 trans = mat4.identity, float size=6) {
364         inDbgSetBuffer([vec3(point, 0)]);
365         inDbgPointsSize(size);
366         inDbgDrawPoints(color, trans);
367     }
368 
369     void draw(mat4 trans = mat4.identity) {
370         drawLines(trans);
371         drawPoints(trans);
372     }
373 
374     bool isPointOverVertex(vec2 point) {
375         foreach(vert; vertices) {
376             if (abs(vert.position.distance(point)) < selectRadius/incViewportZoom) return true;
377         }
378         return false;
379     }
380 
381     void removeVertexAt(vec2 point) {
382         foreach(i; 0..vertices.length) {
383             if (abs(vertices[i].position.distance(point)) < selectRadius/incViewportZoom) {
384                 this.remove(vertices[i]);
385                 return;
386             }
387         }
388     }
389 
390     MeshVertex* getVertexFromPoint(vec2 point) {
391         foreach(ref vert; vertices) {
392             if (abs(vert.position.distance(point)) < selectRadius/incViewportZoom) return vert;
393         }
394         return null;
395     }
396 
397     void remove(MeshVertex* vert) {
398         import std.algorithm.searching : countUntil;
399         import std.algorithm.mutation : remove;
400         
401         auto idx = vertices.countUntil(vert);
402         if (idx != -1) {
403             disconnectAll(vert);
404             vertices = vertices.remove(idx);
405         }
406         changed = true;
407     }
408 
409     vec2[] getOffsets() {
410         vec2[] offsets;
411 
412         offsets.length = vertices.length;
413         foreach(idx, vertex; vertices) {
414             offsets[idx] = vertex.position - data.vertices[idx];
415         }
416         return offsets;
417     }
418 
419     void applyOffsets(vec2[] offsets) {
420         foreach(idx, vertex; vertices) {
421             vertex.position += offsets[idx];
422         }
423         regen();
424         regenConnections();
425         changed = true;
426     }
427 
428     /**
429         Flips all vertices horizontally
430     */
431     void flipHorz() {
432         foreach(ref vert; vertices) {
433             vert.position.x *= -1;
434         }
435         refresh();
436         changed = true;
437     }
438 
439     /**
440         Flips all vertices vertically
441     */
442     void flipVert() {
443         foreach(ref vert; vertices) {
444             vert.position.y *= -1;
445         }
446         refresh();
447         changed = true;
448     }
449 
450     void getBounds(out vec2 min, out vec2 max) {
451         min = vec2(float.infinity, float.infinity);
452         max = vec2(-float.infinity, -float.infinity);
453 
454         foreach(idx, vertex; vertices) {
455             if (min.x > vertex.position.x) min.x = vertex.position.x;
456             if (min.y > vertex.position.y) min.y = vertex.position.y;
457             if (max.x < vertex.position.x) max.x = vertex.position.x;
458             if (max.y < vertex.position.y) max.y = vertex.position.y;
459         }
460     }
461 
462     MeshVertex*[] getInRect(vec2 min, vec2 max) {
463         if (min.x > max.x) swap(min.x, max.x);
464         if (min.y > max.y) swap(min.y, max.y);
465 
466         MeshVertex*[] matching;
467         foreach(idx, vertex; vertices) {
468             if (min.x > vertex.position.x) continue;
469             if (min.y > vertex.position.y) continue;
470             if (max.x < vertex.position.x) continue;
471             if (max.y < vertex.position.y) continue;
472             matching ~= vertex;
473         }
474 
475         return matching;
476     }
477 
478     IncMesh autoTriangulate() {
479         import std.stdio;
480         debug(delaunay) writeln("==== autoTriangulate ====");
481         if (vertices.length < 3) return new IncMesh(*data);
482 
483         IncMesh newMesh = new IncMesh(*data);
484         newMesh.changed = true;
485 
486         vec2 min, max;
487         getBounds(min, max);
488 
489         // Pad (fudge factors are a hack to work around contains() instability, TODO: fix)
490         vec2 range = max - min;
491         min -= range + vec2(range.y, range.x) + vec2(0.123, 0.125);
492         max += range + vec2(range.y, range.x) + vec2(0.127, 0.129);
493 
494         vec3u[] tris;
495         vec3u[] tri2edge;
496         vec2u[] edge2tri;
497 
498         vec2[] vtx;
499         vtx.length = 4;
500 
501         // Define initial state (two tris)
502         vtx[0] = vec2(min.x, min.y);
503         vtx[1] = vec2(min.x, max.y);
504         vtx[2] = vec2(max.x, max.y);
505         vtx[3] = vec2(max.x, min.y);
506         tris ~= vec3u(0, 1, 3);
507         tris ~= vec3u(1, 2, 3);
508         tri2edge ~= vec3u(0, 1, 2);
509         tri2edge ~= vec3u(3, 4, 1);
510         edge2tri ~= vec2u(0, 0);
511         edge2tri ~= vec2u(0, 1);
512         edge2tri ~= vec2u(0, 0);
513         edge2tri ~= vec2u(1, 1);
514         edge2tri ~= vec2u(1, 1);
515 
516         // Helpers
517         float sign(vec2 p1, vec2 p2, vec2 p3) {
518             return (p1.x - p3.x) * (p2.y - p3.y) - (p2.x - p3.x) * (p1.y - p3.y);
519         }
520 
521         bool contains(vec3u tri, vec2 pt) {
522             float d1, d2, d3;
523             bool hasNeg, hasPos;
524 
525             d1 = sign(pt, vtx[tri.x], vtx[tri.y]);
526             d2 = sign(pt, vtx[tri.y], vtx[tri.z]);
527             d3 = sign(pt, vtx[tri.z], vtx[tri.x]);
528 
529             hasNeg = (d1 < 0) || (d2 < 0) || (d3 < 0);
530             hasPos = (d1 > 0) || (d2 > 0) || (d3 > 0);
531 
532             return !(hasNeg && hasPos);
533         }
534 
535         void replaceE2T(ref vec2u e2t, uint from, uint to) {
536             if (e2t.x == from) {
537                 e2t.x = to;
538                 if (e2t.y == from) e2t.y = to;
539             } else if (e2t.y == from) {
540                 e2t.y = to;
541             } else assert(false, "edge mismatch");
542         }
543 
544         void orientTri(uint tri, uint edge) {
545             vec3u t2e = tri2edge[tri];
546             vec3u pt = tris[tri];
547             if (t2e.x == edge) {
548                 return;
549             } else if (t2e.y == edge) {
550                 tri2edge[tri] = vec3u(t2e.y, t2e.z, t2e.x);
551                 tris[tri] = vec3u(pt.y, pt.z, pt.x);
552             } else if (t2e.z == edge) {
553                 tri2edge[tri] = vec3u(t2e.z, t2e.x, t2e.y);
554                 tris[tri] = vec3u(pt.z, pt.x, pt.y);
555             } else {
556                 assert(false, "triangle does not own edge");
557             }
558         }
559 
560         void splitEdges() {
561             uint edgeCnt = cast(uint)edge2tri.length;
562             for(uint e = 0; e < edgeCnt; e++) {
563                 vec2u tr = edge2tri[e];
564 
565                 if (tr.x != tr.y) continue; // Only handle outer edges
566 
567                 orientTri(tr.x, e);
568 
569                 uint t1 = tr.x;
570                 uint t2 = cast(uint)tris.length;
571                 uint l = tris[t1].x;
572                 uint r = tris[t1].y;
573                 uint z = tris[t1].z;
574                 uint m = cast(uint)vtx.length;
575                 vtx ~= (vtx[l] + vtx[r]) / 2;
576 
577                 uint xe = cast(uint)edge2tri.length;
578                 uint me = xe + 1;
579                 uint re = tri2edge[t1].y;
580 
581                 tris[t1].y = m;
582                 tri2edge[t1].y = me;
583                 tris ~= vec3u(m, r, z);
584                 tri2edge ~= vec3u(xe, re, me);
585                 edge2tri ~= vec2u(t2, t2);
586                 edge2tri ~= vec2u(t1, t2);
587                 replaceE2T(edge2tri[re], t1, t2);
588             }
589         }
590 
591         bool inCircle(vec2 pa, vec2 pb, vec2 pc, vec2 pd) {
592             debug(delaunay) writefln("in_circle(%s, %s, %s, %s)", pa, pb, pc, pd);
593             float adx = pa.x - pd.x;
594             float ady = pa.y - pd.y;
595             float bdx = pb.x - pd.x;
596             float bdy = pb.y - pd.y;
597 
598             float adxbdy = adx * bdy;
599             float bdxady = bdx * ady;
600             float oabd = adxbdy - bdxady;
601 
602             if (oabd <= 0) return false;
603 
604             float cdx = pc.x - pd.x;
605             float cdy = pc.y - pd.y;
606 
607             float cdxady = cdx * ady;
608             float adxcdy = adx * cdy;
609             float ocad = cdxady - adxcdy;
610 
611             if (ocad <= 0) return false;
612 
613             float bdxcdy = bdx * cdy;
614             float cdxbdy = cdx * bdy;
615 
616             float alift = adx * adx + ady * ady;
617             float blift = bdx * bdx + bdy * bdy;
618             float clift = cdx * cdx + cdy * cdy;
619 
620             float det = alift * (bdxcdy - cdxbdy) + blift * ocad + clift * oabd;
621 
622             debug(delaunay) writefln("det=%s", det);
623             return det > 0;
624         }
625 
626         splitEdges();
627         splitEdges();
628         splitEdges();
629         splitEdges();
630 
631         uint dropVertices = cast(uint)vtx.length;
632 
633         // Add vertices, preserving Delaunay condition
634         foreach(orig_i, vertex; vertices) {
635             uint i = cast(uint)orig_i + dropVertices;
636             debug(delaunay) writefln("Add @%d: %s", i, vertex.position);
637             vtx ~= vertex.position;
638             bool found = false;
639 
640             uint[] affectedEdges;
641 
642             foreach(a_, tri; tris) {
643                 if (!contains(tri, vertex.position)) continue;
644 
645                 /*
646                            x
647                   Y-----------------X
648                    \`,            '/    XYZ = original vertices
649                     \ `q   a   p' /     a = original triangle
650                      \  `,    '  /      bc = new triangles
651                       \   `i'   /       xyz = original edges
652                      y \ b | c / z      pqr = new edges
653                         \  r  /
654                          \ | /
655                           \|/
656                            Z
657                 */
658 
659                 // Subdivide containing triangle
660                 // New triangles
661                 uint a = cast(uint)a_;
662                 uint b = cast(uint)tris.length;
663                 uint c = b + 1;
664                 tris[a] = vec3u(tri.x, tri.y, i);
665                 tris ~= vec3u(tri.y, tri.z, i); // b
666                 tris ~= vec3u(tri.z, tri.x, i); // c
667 
668                 debug(delaunay) writefln("*** Tri %d: %s Edges: %s", a, tris[a], tri2edge[a]);
669 
670                 // New inner edges
671                 uint p = cast(uint)edge2tri.length;
672                 uint q = p + 1;
673                 uint r = q + 1;
674 
675                 // Get outer edges
676                 uint x = tri2edge[a].x;
677                 uint y = tri2edge[a].y;
678                 uint z = tri2edge[a].z;
679 
680                 // Update triangle to edge mappings
681                 tri2edge[a] = vec3u(x, q, p);
682                 tri2edge ~= vec3u(y, r, q);
683                 tri2edge ~= vec3u(z, p, r);
684 
685                 debug(delaunay) writefln("  * Tri a %d: %s Edges: %s", a, tris[a], tri2edge[a]);
686                 debug(delaunay) writefln("  + Tri b %d: %s Edges: %s", b, tris[b], tri2edge[b]);
687                 debug(delaunay) writefln("  + Tri c %d: %s Edges: %s", c, tris[c], tri2edge[c]);
688 
689                 // Save new edges
690                 edge2tri ~= vec2u(c, a);
691                 edge2tri ~= vec2u(a, b);
692                 edge2tri ~= vec2u(b, c);
693                 debug(delaunay) writefln("  + Edg p %d: Tris %s", p, edge2tri[p]);
694                 debug(delaunay) writefln("  + Edg q %d: Tris %s", q, edge2tri[q]);
695                 debug(delaunay) writefln("  + Edg r %d: Tris %s", r, edge2tri[r]);
696 
697                 // Update two outer edges
698                 debug(delaunay) writefln("  - Edg y %d: Tris %s", y, edge2tri[y]);
699                 replaceE2T(edge2tri[y], a, b);
700                 debug(delaunay) writefln("  + Edg y %d: Tris %s", y, edge2tri[y]);
701                 debug(delaunay) writefln("  - Edg z %d: Tris %s", y, edge2tri[z]);
702                 replaceE2T(edge2tri[z], a, c);
703                 debug(delaunay) writefln("  + Edg z %d: Tris %s", z, edge2tri[z]);
704 
705                 // Keep track of what edges we have to look at
706                 affectedEdges ~= [x, y, z, p, q, r];
707 
708                 found = true;
709                 break;
710             }
711             if (!found) {
712                 debug(delaunay) writeln("FAILED!");
713                 break;
714             }
715 
716             bool[] checked;
717             checked.length = edge2tri.length;
718 
719             for (uint j = 0; j < affectedEdges.length; j++) {
720                 uint e = affectedEdges[j];
721                 vec2u t = edge2tri[e];
722 
723                 debug(delaunay) writefln(" ## Edge %d: T %s: %s %s", e, t, tris[t.x], tris[t.y]);
724 
725                 if (t.x == t.y) {
726                     debug(delaunay) writefln("  + Outer edge");
727                     continue; // Outer edge
728                 }
729 
730                 // Orient triangles so 1st edge is shared
731                 orientTri(t.x, e);
732                 orientTri(t.y, e);
733 
734                 assert(tris[t.x].x == tris[t.y].y, "triangles do not share edge");
735                 assert(tris[t.y].x == tris[t.x].y, "triangles do not share edge");
736 
737                 uint a = tris[t.x].x;
738                 uint c = tris[t.x].y;
739                 uint d = tris[t.x].z;
740                 uint b = tris[t.y].z;
741 
742                 // Delaunay check
743                 if (!inCircle(vtx[b], vtx[a], vtx[c], vtx[d])) {
744                     // We're good
745                     debug(delaunay) writefln("  + Meets condition");
746                     continue;
747                 }
748 
749                 debug(delaunay) writefln("  - Flip!");
750 
751                 // Flip edge
752                 /*
753                    c          c
754                   /|\      r / \ q
755                  / | \      / x \
756                 d x|y b -> d-----b
757                  \ | /      \ y /
758                   \|/      s \ / p
759                    a          a
760                 */
761                 uint r = tri2edge[t.x].y;
762                 uint s = tri2edge[t.x].z;
763                 uint p = tri2edge[t.y].y;
764                 uint q = tri2edge[t.y].z;
765 
766                 tris[t.x] = vec3u(d, b, c);
767                 tris[t.t] = vec3u(b, d, a);
768                 tri2edge[t.x] = vec3u(e, q, r);
769                 tri2edge[t.y] = vec3u(e, s, p);
770                 replaceE2T(edge2tri[q], t.y, t.x);
771                 replaceE2T(edge2tri[s], t.x, t.y);
772 
773                 // Mark it as checked
774                 checked[e] = true;
775 
776                 // Check the neighboring edges
777                 if (!checked[p]) affectedEdges ~= p;
778                 if (!checked[q]) affectedEdges ~= q;
779                 if (!checked[r]) affectedEdges ~= r;
780                 if (!checked[s]) affectedEdges ~= s;
781             }
782         }
783 
784         // Copy vertices
785         newMesh.vertices.length = 0;
786         foreach(v; vtx) {
787             newMesh.vertices ~= new MeshVertex(v, []);
788         }
789 
790         // Extract tris into connections
791         foreach(tri; tris) {
792             connect(newMesh.vertices[tri.x], newMesh.vertices[tri.y]);
793             connect(newMesh.vertices[tri.y], newMesh.vertices[tri.z]);
794             connect(newMesh.vertices[tri.z], newMesh.vertices[tri.x]);
795         }
796 
797         // Get rid of corners
798         foreach(i; 0..dropVertices)
799             newMesh.remove(newMesh.vertices[0]);
800 
801         newMesh.refresh();
802         debug(delaunay) writeln("==== autoTriangulate done ====");
803         return newMesh;
804     }
805 }