private Triangle[] Triangulate(List <Vertex> verts) { //Randomize vertices for faster average triangulation Algorithm.Shuffle <Vertex>(ref verts); //Construct circle enclosing all the vertices Vector2 centroid = Vector2.zero; foreach (Vertex vert in verts) { centroid += vert.p; } centroid /= verts.Count; float r = 0.0f; foreach (Vertex vert in verts) { r = Mathf.Max(r, Vector2.Distance(vert.p, centroid)); } //Add some padding to avoid floating point errors later on r *= 1.2f; Vector2 pi0 = new Vector2(centroid.x, centroid.y - 2 * r); Vector2 pi1 = new Vector2(centroid.x + r * Mathf.Sqrt(3), centroid.y + r); Vector2 pi2 = new Vector2(centroid.x - r * Mathf.Sqrt(3), centroid.y + r); Debug.DrawLine(pi0, pi1, Color.cyan, 5.0f, false); Debug.DrawLine(pi1, pi2, Color.cyan, 5.0f, false); Debug.DrawLine(pi2, pi0, Color.cyan, 5.0f, false); Vertex vi0 = new Vertex(pi0, -1); Vertex vi1 = new Vertex(pi1, -1); Vertex vi2 = new Vertex(pi2, -1); //Imaginary triangle HalfEdge e01Imag = new HalfEdge(vi0); HalfEdge e12Imag = new HalfEdge(vi1); HalfEdge e20Imag = new HalfEdge(vi2); Triangle treeRoot = new Triangle(e01Imag, e12Imag, e20Imag); //Perform Delaunay Triangulation for (int i = 0; i < verts.Count; i++) { Vertex v = verts[i]; Triangle containingTri = treeRoot.FindContainingTriangle(v.p); //Check for potential degenerate case when point lies on edge of triangle HalfEdge e01 = containingTri.edge; HalfEdge e12 = e01.next; HalfEdge e20 = e12.next; Vector3 uvw = Geometry.ToBarycentricCoordinates(e01.origin.p, e12.origin.p, e20.origin.p, v.p); HalfEdge[] edges; if (uvw[0] < VecMath.epsilon) { edges = e12.InsertVertex(v); } else if (uvw[1] < VecMath.epsilon) { edges = e20.InsertVertex(v); } else if (uvw[2] < VecMath.epsilon) { edges = e01.InsertVertex(v); } else { edges = containingTri.InsertVertex(v); } //Flip triangles that don't satisfy delaunay property HashSet <HalfEdge> sptSet = new HashSet <HalfEdge>(); LinkedList <HalfEdge> frontier = new LinkedList <HalfEdge>(); foreach (HalfEdge edge in edges) { frontier.AddLast(edge); } LinkedListNode <HalfEdge> curNode; int t = 0; while (frontier.Count > 0 && t < 100) { t += 1; curNode = frontier.First; frontier.RemoveFirst(); HalfEdge ab = curNode.Value; HalfEdge ba = ab.twin; if (!sptSet.Contains(ab) && ba != null) { HalfEdge bc = ab.next; HalfEdge ca = bc.next; HalfEdge ad = ba.next; HalfEdge db = ad.next; //Check if Delaunay Property is violated if (Geometry.IsInCircumscribedCircle(ab.origin.p, bc.origin.p, ca.origin.p, db.origin.p)) { //Flip triangle HalfEdge dc = new HalfEdge(db.origin); HalfEdge cd = new HalfEdge(ca.origin); HalfEdge.SetTwins(dc, cd); Triangle adc = new Triangle(ad, dc, ca); Triangle bcd = new Triangle(bc, cd, db); //Remove old edges from vertices ab.origin.RemoveOutgoingEdge(ab); ba.origin.RemoveOutgoingEdge(ba); //Add new edges from flip to vertices cd.origin.AddOutgoingEdge(cd); dc.origin.AddOutgoingEdge(dc); ab.incidentTriangle.children = new List <Triangle> { adc, bcd }; ba.incidentTriangle.children = new List <Triangle> { adc, bcd }; frontier.AddLast(ad); frontier.AddLast(db); } sptSet.Add(ab); sptSet.Add(ba); } } } //Insert constrained edges foreach (ConstrainedVertex[] segments in constrainedVerts) { HashSet <HalfEdge> holeBounds = new HashSet <HalfEdge>(); HalfEdge holeEdge = null; bool isHole = segments.Length > 2; int n = isHole ? segments.Length : segments.Length - 1; for (int i = 0; i < n; i++) { Vertex v1 = segments[i % segments.Length]; Vertex v2 = segments[(i + 1) % segments.Length]; Debug.DrawLine(v1.p, v2.p, Color.magenta, 5.0f, false); Vector2 dir = v2.p - v1.p; HalfEdge eStart = v1.GetOutgoingEdgeClockwiseFrom(dir); HalfEdge eEnd = v2.GetOutgoingEdgeClockwiseFrom(-dir); List <HalfEdge> edgePortals = new List <HalfEdge>(); HalfEdge intersected = eStart.next.twin; Vertex v = v1; int t = 0; while (v != v2 && t < 100) { t += 1; edgePortals.Add(intersected); v = intersected.prev.origin; Vector2 newDir = v.p - v1.p; if (VecMath.Det(dir, newDir) >= 0) { intersected = intersected.next.twin; } else { intersected = intersected.prev.twin; } } List <HalfEdge> forwardEdgePortals = new List <HalfEdge>(); forwardEdgePortals.Add(eStart); for (int j = 0; j < edgePortals.Count; j++) { forwardEdgePortals.Add(edgePortals[j]); } List <HalfEdge> backwardEdgePortals = new List <HalfEdge>(); backwardEdgePortals.Add(eEnd); for (int j = edgePortals.Count - 1; j >= 0; j--) { backwardEdgePortals.Add(edgePortals[j].twin); } int sL = 0; HalfEdge eConstrainedL = PolygonTriangulation(ref sL, forwardEdgePortals); holeBounds.Add(eConstrainedL); int sR = 0; HalfEdge eConstrainedR = PolygonTriangulation(ref sR, backwardEdgePortals); holeEdge = eConstrainedR; HalfEdge.SetTwins(eConstrainedL, eConstrainedR); foreach (HalfEdge e in edgePortals) { Vector2 ep1 = e.origin.p; Vector2 ep2 = e.next.origin.p; Debug.DrawLine(ep1, ep2, Color.green, 5.0f, false); } //v1.DrawOutgoingEdges(); //Debug.DrawLine(eStart.origin.p, eStart.next.origin.p, Color.red, 5.0f, false); } if (isHole && holeEdge != null) { //We have a hole. Hide all triangles that are in hole CreateHole(holeEdge, holeBounds); } } //Generate Triangle list List <Triangle> leafs = new List <Triangle>(); HashSet <Triangle> visited = new HashSet <Triangle>(); int count = 0; treeRoot.GetRealLeafs(ref leafs, ref visited, ref count, 0); Debug.Log(count); foreach (Triangle leaf in leafs) { Vector3 p0 = leaf.edge.origin.p; Vector3 p1 = leaf.edge.next.origin.p; Vector3 p2 = leaf.edge.next.next.origin.p; Debug.DrawLine(p0, p1, Color.cyan, 5.0f, false); Debug.DrawLine(p1, p2, Color.cyan, 5.0f, false); Debug.DrawLine(p2, p0, Color.cyan, 5.0f, false); } return(leafs.ToArray()); }