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, []);
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 draw(mat4 trans = mat4.identity) {
364         drawLines(trans);
365         drawPoints(trans);
366     }
367 
368     bool isPointOverVertex(vec2 point) {
369         foreach(vert; vertices) {
370             if (abs(vert.position.distance(point)) < selectRadius/incViewportZoom) return true;
371         }
372         return false;
373     }
374 
375     void removeVertexAt(vec2 point) {
376         foreach(i; 0..vertices.length) {
377             if (abs(vertices[i].position.distance(point)) < selectRadius/incViewportZoom) {
378                 this.remove(vertices[i]);
379                 return;
380             }
381         }
382     }
383 
384     MeshVertex* getVertexFromPoint(vec2 point) {
385         foreach(ref vert; vertices) {
386             if (abs(vert.position.distance(point)) < selectRadius/incViewportZoom) return vert;
387         }
388         return null;
389     }
390 
391     void remove(MeshVertex* vert) {
392         import std.algorithm.searching : countUntil;
393         import std.algorithm.mutation : remove;
394         
395         auto idx = vertices.countUntil(vert);
396         if (idx != -1) {
397             disconnectAll(vert);
398             vertices = vertices.remove(idx);
399         }
400         changed = true;
401     }
402 
403     vec2[] getOffsets() {
404         vec2[] offsets;
405 
406         offsets.length = vertices.length;
407         foreach(idx, vertex; vertices) {
408             offsets[idx] = vertex.position - data.vertices[idx];
409         }
410         return offsets;
411     }
412 
413     void applyOffsets(vec2[] offsets) {
414         foreach(idx, vertex; vertices) {
415             vertex.position += offsets[idx];
416         }
417         regen();
418         regenConnections();
419         changed = true;
420     }
421 
422     /**
423         Flips all vertices horizontally
424     */
425     void flipHorz() {
426         foreach(ref vert; vertices) {
427             vert.position.x *= -1;
428         }
429         refresh();
430         changed = true;
431     }
432 
433     /**
434         Flips all vertices vertically
435     */
436     void flipVert() {
437         foreach(ref vert; vertices) {
438             vert.position.y *= -1;
439         }
440         refresh();
441         changed = true;
442     }
443 
444     void getBounds(out vec2 min, out vec2 max) {
445         min = vec2(float.infinity, float.infinity);
446         max = vec2(-float.infinity, -float.infinity);
447 
448         foreach(idx, vertex; vertices) {
449             if (min.x > vertex.position.x) min.x = vertex.position.x;
450             if (min.y > vertex.position.y) min.y = vertex.position.y;
451             if (max.x < vertex.position.x) max.x = vertex.position.x;
452             if (max.y < vertex.position.y) max.y = vertex.position.y;
453         }
454     }
455 
456     MeshVertex*[] getInRect(vec2 min, vec2 max) {
457         if (min.x > max.x) swap(min.x, max.x);
458         if (min.y > max.y) swap(min.y, max.y);
459 
460         MeshVertex*[] matching;
461         foreach(idx, vertex; vertices) {
462             if (min.x > vertex.position.x) continue;
463             if (min.y > vertex.position.y) continue;
464             if (max.x < vertex.position.x) continue;
465             if (max.y < vertex.position.y) continue;
466             matching ~= vertex;
467         }
468 
469         return matching;
470     }
471 
472     IncMesh autoTriangulate() {
473         import std.stdio;
474         debug(delaunay) writeln("==== autoTriangulate ====");
475         if (vertices.length < 3) return new IncMesh(*data);
476 
477         IncMesh newMesh = new IncMesh(*data);
478         newMesh.changed = true;
479 
480         vec2 min, max;
481         getBounds(min, max);
482 
483         // Pad (fudge factors are a hack to work around contains() instability, TODO: fix)
484         vec2 range = max - min;
485         min -= range + vec2(range.y, range.x) + vec2(0.123, 0.125);
486         max += range + vec2(range.y, range.x) + vec2(0.127, 0.129);
487 
488         vec3u[] tris;
489         vec3u[] tri2edge;
490         vec2u[] edge2tri;
491 
492         vec2[] vtx;
493         vtx.length = 4;
494 
495         // Define initial state (two tris)
496         vtx[0] = vec2(min.x, min.y);
497         vtx[1] = vec2(min.x, max.y);
498         vtx[2] = vec2(max.x, max.y);
499         vtx[3] = vec2(max.x, min.y);
500         tris ~= vec3u(0, 1, 3);
501         tris ~= vec3u(1, 2, 3);
502         tri2edge ~= vec3u(0, 1, 2);
503         tri2edge ~= vec3u(3, 4, 1);
504         edge2tri ~= vec2u(0, 0);
505         edge2tri ~= vec2u(0, 1);
506         edge2tri ~= vec2u(0, 0);
507         edge2tri ~= vec2u(1, 1);
508         edge2tri ~= vec2u(1, 1);
509 
510         // Helpers
511         float sign(vec2 p1, vec2 p2, vec2 p3) {
512             return (p1.x - p3.x) * (p2.y - p3.y) - (p2.x - p3.x) * (p1.y - p3.y);
513         }
514 
515         bool contains(vec3u tri, vec2 pt) {
516             float d1, d2, d3;
517             bool hasNeg, hasPos;
518 
519             d1 = sign(pt, vtx[tri.x], vtx[tri.y]);
520             d2 = sign(pt, vtx[tri.y], vtx[tri.z]);
521             d3 = sign(pt, vtx[tri.z], vtx[tri.x]);
522 
523             hasNeg = (d1 < 0) || (d2 < 0) || (d3 < 0);
524             hasPos = (d1 > 0) || (d2 > 0) || (d3 > 0);
525 
526             return !(hasNeg && hasPos);
527         }
528 
529         void replaceE2T(ref vec2u e2t, uint from, uint to) {
530             if (e2t.x == from) {
531                 e2t.x = to;
532                 if (e2t.y == from) e2t.y = to;
533             } else if (e2t.y == from) {
534                 e2t.y = to;
535             } else assert(false, "edge mismatch");
536         }
537 
538         void orientTri(uint tri, uint edge) {
539             vec3u t2e = tri2edge[tri];
540             vec3u pt = tris[tri];
541             if (t2e.x == edge) {
542                 return;
543             } else if (t2e.y == edge) {
544                 tri2edge[tri] = vec3u(t2e.y, t2e.z, t2e.x);
545                 tris[tri] = vec3u(pt.y, pt.z, pt.x);
546             } else if (t2e.z == edge) {
547                 tri2edge[tri] = vec3u(t2e.z, t2e.x, t2e.y);
548                 tris[tri] = vec3u(pt.z, pt.x, pt.y);
549             } else {
550                 assert(false, "triangle does not own edge");
551             }
552         }
553 
554         void splitEdges() {
555             uint edgeCnt = cast(uint)edge2tri.length;
556             for(uint e = 0; e < edgeCnt; e++) {
557                 vec2u tr = edge2tri[e];
558 
559                 if (tr.x != tr.y) continue; // Only handle outer edges
560 
561                 orientTri(tr.x, e);
562 
563                 uint t1 = tr.x;
564                 uint t2 = cast(uint)tris.length;
565                 uint l = tris[t1].x;
566                 uint r = tris[t1].y;
567                 uint z = tris[t1].z;
568                 uint m = cast(uint)vtx.length;
569                 vtx ~= (vtx[l] + vtx[r]) / 2;
570 
571                 uint xe = cast(uint)edge2tri.length;
572                 uint me = xe + 1;
573                 uint re = tri2edge[t1].y;
574 
575                 tris[t1].y = m;
576                 tri2edge[t1].y = me;
577                 tris ~= vec3u(m, r, z);
578                 tri2edge ~= vec3u(xe, re, me);
579                 edge2tri ~= vec2u(t2, t2);
580                 edge2tri ~= vec2u(t1, t2);
581                 replaceE2T(edge2tri[re], t1, t2);
582             }
583         }
584 
585         bool inCircle(vec2 pa, vec2 pb, vec2 pc, vec2 pd) {
586             debug(delaunay) writefln("in_circle(%s, %s, %s, %s)", pa, pb, pc, pd);
587             float adx = pa.x - pd.x;
588             float ady = pa.y - pd.y;
589             float bdx = pb.x - pd.x;
590             float bdy = pb.y - pd.y;
591 
592             float adxbdy = adx * bdy;
593             float bdxady = bdx * ady;
594             float oabd = adxbdy - bdxady;
595 
596             if (oabd <= 0) return false;
597 
598             float cdx = pc.x - pd.x;
599             float cdy = pc.y - pd.y;
600 
601             float cdxady = cdx * ady;
602             float adxcdy = adx * cdy;
603             float ocad = cdxady - adxcdy;
604 
605             if (ocad <= 0) return false;
606 
607             float bdxcdy = bdx * cdy;
608             float cdxbdy = cdx * bdy;
609 
610             float alift = adx * adx + ady * ady;
611             float blift = bdx * bdx + bdy * bdy;
612             float clift = cdx * cdx + cdy * cdy;
613 
614             float det = alift * (bdxcdy - cdxbdy) + blift * ocad + clift * oabd;
615 
616             debug(delaunay) writefln("det=%s", det);
617             return det > 0;
618         }
619 
620         splitEdges();
621         splitEdges();
622         splitEdges();
623         splitEdges();
624 
625         uint dropVertices = cast(uint)vtx.length;
626 
627         // Add vertices, preserving Delaunay condition
628         foreach(orig_i, vertex; vertices) {
629             uint i = cast(uint)orig_i + dropVertices;
630             debug(delaunay) writefln("Add @%d: %s", i, vertex.position);
631             vtx ~= vertex.position;
632             bool found = false;
633 
634             uint[] affectedEdges;
635 
636             foreach(a_, tri; tris) {
637                 if (!contains(tri, vertex.position)) continue;
638 
639                 /*
640                            x
641                   Y-----------------X
642                    \`,            '/    XYZ = original vertices
643                     \ `q   a   p' /     a = original triangle
644                      \  `,    '  /      bc = new triangles
645                       \   `i'   /       xyz = original edges
646                      y \ b | c / z      pqr = new edges
647                         \  r  /
648                          \ | /
649                           \|/
650                            Z
651                 */
652 
653                 // Subdivide containing triangle
654                 // New triangles
655                 uint a = cast(uint)a_;
656                 uint b = cast(uint)tris.length;
657                 uint c = b + 1;
658                 tris[a] = vec3u(tri.x, tri.y, i);
659                 tris ~= vec3u(tri.y, tri.z, i); // b
660                 tris ~= vec3u(tri.z, tri.x, i); // c
661 
662                 debug(delaunay) writefln("*** Tri %d: %s Edges: %s", a, tris[a], tri2edge[a]);
663 
664                 // New inner edges
665                 uint p = cast(uint)edge2tri.length;
666                 uint q = p + 1;
667                 uint r = q + 1;
668 
669                 // Get outer edges
670                 uint x = tri2edge[a].x;
671                 uint y = tri2edge[a].y;
672                 uint z = tri2edge[a].z;
673 
674                 // Update triangle to edge mappings
675                 tri2edge[a] = vec3u(x, q, p);
676                 tri2edge ~= vec3u(y, r, q);
677                 tri2edge ~= vec3u(z, p, r);
678 
679                 debug(delaunay) writefln("  * Tri a %d: %s Edges: %s", a, tris[a], tri2edge[a]);
680                 debug(delaunay) writefln("  + Tri b %d: %s Edges: %s", b, tris[b], tri2edge[b]);
681                 debug(delaunay) writefln("  + Tri c %d: %s Edges: %s", c, tris[c], tri2edge[c]);
682 
683                 // Save new edges
684                 edge2tri ~= vec2u(c, a);
685                 edge2tri ~= vec2u(a, b);
686                 edge2tri ~= vec2u(b, c);
687                 debug(delaunay) writefln("  + Edg p %d: Tris %s", p, edge2tri[p]);
688                 debug(delaunay) writefln("  + Edg q %d: Tris %s", q, edge2tri[q]);
689                 debug(delaunay) writefln("  + Edg r %d: Tris %s", r, edge2tri[r]);
690 
691                 // Update two outer edges
692                 debug(delaunay) writefln("  - Edg y %d: Tris %s", y, edge2tri[y]);
693                 replaceE2T(edge2tri[y], a, b);
694                 debug(delaunay) writefln("  + Edg y %d: Tris %s", y, edge2tri[y]);
695                 debug(delaunay) writefln("  - Edg z %d: Tris %s", y, edge2tri[z]);
696                 replaceE2T(edge2tri[z], a, c);
697                 debug(delaunay) writefln("  + Edg z %d: Tris %s", z, edge2tri[z]);
698 
699                 // Keep track of what edges we have to look at
700                 affectedEdges ~= [x, y, z, p, q, r];
701 
702                 found = true;
703                 break;
704             }
705             if (!found) {
706                 debug(delaunay) writeln("FAILED!");
707                 break;
708             }
709 
710             bool[] checked;
711             checked.length = edge2tri.length;
712 
713             for (uint j = 0; j < affectedEdges.length; j++) {
714                 uint e = affectedEdges[j];
715                 vec2u t = edge2tri[e];
716 
717                 debug(delaunay) writefln(" ## Edge %d: T %s: %s %s", e, t, tris[t.x], tris[t.y]);
718 
719                 if (t.x == t.y) {
720                     debug(delaunay) writefln("  + Outer edge");
721                     continue; // Outer edge
722                 }
723 
724                 // Orient triangles so 1st edge is shared
725                 orientTri(t.x, e);
726                 orientTri(t.y, e);
727 
728                 assert(tris[t.x].x == tris[t.y].y, "triangles do not share edge");
729                 assert(tris[t.y].x == tris[t.x].y, "triangles do not share edge");
730 
731                 uint a = tris[t.x].x;
732                 uint c = tris[t.x].y;
733                 uint d = tris[t.x].z;
734                 uint b = tris[t.y].z;
735 
736                 // Delaunay check
737                 if (!inCircle(vtx[b], vtx[a], vtx[c], vtx[d])) {
738                     // We're good
739                     debug(delaunay) writefln("  + Meets condition");
740                     continue;
741                 }
742 
743                 debug(delaunay) writefln("  - Flip!");
744 
745                 // Flip edge
746                 /*
747                    c          c
748                   /|\      r / \ q
749                  / | \      / x \
750                 d x|y b -> d-----b
751                  \ | /      \ y /
752                   \|/      s \ / p
753                    a          a
754                 */
755                 uint r = tri2edge[t.x].y;
756                 uint s = tri2edge[t.x].z;
757                 uint p = tri2edge[t.y].y;
758                 uint q = tri2edge[t.y].z;
759 
760                 tris[t.x] = vec3u(d, b, c);
761                 tris[t.t] = vec3u(b, d, a);
762                 tri2edge[t.x] = vec3u(e, q, r);
763                 tri2edge[t.y] = vec3u(e, s, p);
764                 replaceE2T(edge2tri[q], t.y, t.x);
765                 replaceE2T(edge2tri[s], t.x, t.y);
766 
767                 // Mark it as checked
768                 checked[e] = true;
769 
770                 // Check the neighboring edges
771                 if (!checked[p]) affectedEdges ~= p;
772                 if (!checked[q]) affectedEdges ~= q;
773                 if (!checked[r]) affectedEdges ~= r;
774                 if (!checked[s]) affectedEdges ~= s;
775             }
776         }
777 
778         // Copy vertices
779         newMesh.vertices.length = 0;
780         foreach(v; vtx) {
781             newMesh.vertices ~= new MeshVertex(v, []);
782         }
783 
784         // Extract tris into connections
785         foreach(tri; tris) {
786             connect(newMesh.vertices[tri.x], newMesh.vertices[tri.y]);
787             connect(newMesh.vertices[tri.y], newMesh.vertices[tri.z]);
788             connect(newMesh.vertices[tri.z], newMesh.vertices[tri.x]);
789         }
790 
791         // Get rid of corners
792         foreach(i; 0..dropVertices)
793             newMesh.remove(newMesh.vertices[0]);
794 
795         newMesh.refresh();
796         debug(delaunay) writeln("==== autoTriangulate done ====");
797         return newMesh;
798     }
799 }