// // Remove all triangles that are inside the constraint // //This assumes the vertices in the constraint are ordered clockwise private IEnumerator RemoveSuperfluousTriangles(HalfEdgeData2 triangleData, List <MyVector2> constraints, Normalizer2 normalizer) { //This assumes we have at least 3 vertices in the constraint because we cant delete triangles inside a line if (constraints.Count < 3) { yield return(null); } HashSet <HalfEdgeFace2> trianglesToBeDeleted = FindTrianglesWithinConstraint(triangleData, constraints); if (trianglesToBeDeleted == null) { Debug.Log("There are no triangles to delete"); yield return(null); } //Delete the triangles foreach (HalfEdgeFace2 t in trianglesToBeDeleted) { HalfEdgeHelpMethods.DeleteTriangleFace(t, triangleData, true); // // PAUSE AND VISUALIZE // visualizeController.DisplayMeshMain(triangleData, normalizer); yield return(new WaitForSeconds(0.5f)); } }
//Remove the supertriangle IEnumerator RemoveSuperTriangle(Triangle2 superTriangle, HalfEdgeData2 triangulationData) { //The super triangle doesnt exists anymore because we have split it into many new triangles //But we can use its vertices to figure out which new triangles (or faces belonging to the triangle) //we should delete HashSet <HalfEdgeFace2> triangleFacesToDelete = new HashSet <HalfEdgeFace2>(); //Loop through all vertices belongin to the triangulation foreach (HalfEdgeVertex2 v in triangulationData.vertices) { //If the face attached to this vertex already exists in the list of faces we want to delete //Then dont add it again if (triangleFacesToDelete.Contains(v.edge.face)) { continue; } MyVector2 v1 = v.position; //Is this vertex in the triangulation a vertex in the super triangle? if (v1.Equals(superTriangle.p1) || v1.Equals(superTriangle.p2) || v1.Equals(superTriangle.p3)) { triangleFacesToDelete.Add(v.edge.face); } } //Debug.Log("Triangles to delete: " + trianglesToDelete.Count); //Delete the new triangles with vertices attached to the super triangle foreach (HalfEdgeFace2 f in triangleFacesToDelete) { HalfEdgeHelpMethods.DeleteTriangleFace(f, triangulationData, shouldSetOppositeToNull: true); //VISUALZ ShowTriangles(triangulationData); yield return(new WaitForSeconds(controller.pauseTime)); } //VISUALZ - show the colored mesh when its finished controller.shouldDisplayColoredMesh = true; yield return(null); }
//Flip edges until we get a delaunay triangulation private IEnumerator FlipEdges(HalfEdgeData2 triangleData) { //The edges we want to flip HashSet <HalfEdge2> edges = triangleData.edges; //To avoid getting stuck in infinite loop int safety = 0; //Count how many edges we have flipped, which may be interesting to display int flippedEdges = 0; while (true) { safety += 1; if (safety > 100000) { Debug.Log("Stuck in endless loop when flipping edges to get a Delaunay triangulation"); break; } bool hasFlippedEdge = false; //Search through all edges to see if we can flip an edge foreach (HalfEdge2 thisEdge in edges) { //Is this edge sharing an edge with another triangle, otherwise its a border, and then we cant flip the edge if (thisEdge.oppositeEdge == null) { continue; } //The positions of the vertices belonging to the two triangles that we might flip //a-c should be the edge that we might flip MyVector2 a = thisEdge.v.position; MyVector2 b = thisEdge.nextEdge.v.position; MyVector2 c = thisEdge.nextEdge.nextEdge.v.position; MyVector2 d = thisEdge.oppositeEdge.nextEdge.v.position; //If we want to display the test circle //controller.GenerateDelaunayCircleMeshes(a, b, c, d); //yield return new WaitForSeconds(controller.pauseTime); //Test if we should flip this edge if (DelaunayMethods.ShouldFlipEdge(a, b, c, d)) { flippedEdges += 1; hasFlippedEdge = true; HalfEdgeHelpMethods.FlipTriangleEdge(thisEdge); controller.flipText.text = "Flipped edges: " + flippedEdges; controller.GenerateTriangulationMesh(triangleData); yield return(new WaitForSeconds(controller.pauseTime)); } } //We have searched through all edges and havent found an edge to flip, so we have a Delaunay triangulation! if (!hasFlippedEdge) { Debug.Log("Found a delaunay triangulation in " + flippedEdges + " flips"); break; } } //Remove the circle meshes so we see that we are finished controller.ClearBlackMeshes(); yield return(null); }
// // Try to restore the delaunay triangulation by flipping newly created edges // //This process is similar to when we created the original delaunay triangulation //This step can maybe be skipped if you just want a triangulation and Ive noticed its often not flipping any triangles private IEnumerator RestoreDelaunayTriangulation(MyVector2 c_p1, MyVector2 c_p2, List <HalfEdge2> newEdges, HalfEdgeData2 triangleData, Normalizer2 normalizer) { int safety = 0; int flippedEdges = 0; //Repeat 4.1 - 4.3 until no further swaps take place while (true) { safety += 1; if (safety > 100000) { Debug.Log("Stuck in endless loop when delaunay after fixing constrained edges"); break; } bool hasFlippedEdge = false; //Step 4.1. Loop over each edge in the list of newly created edges for (int j = 0; j < newEdges.Count; j++) { HalfEdge2 e = newEdges[j]; //Step 4.2. Let the newly created edge be defined by the vertices MyVector2 v_k = e.v.position; MyVector2 v_l = e.prevEdge.v.position; //If this edge is equal to the constrained edge, then skip to step 4.1 //because we are not allowed to flip the constrained edge if ((v_k.Equals(c_p1) && v_l.Equals(c_p2)) || (v_l.Equals(c_p1) && v_k.Equals(c_p2))) { continue; } //Step 4.3. If the two triangles that share edge v_k and v_l don't satisfy the delaunay criterion, //so that a vertex of one of the triangles is inside the circumcircle of the other triangle, flip the edge //The third vertex of the triangle belonging to this edge MyVector2 v_third_pos = e.nextEdge.v.position; //The vertice belonging to the triangle on the opposite side of the edge and this vertex is not a part of the edge MyVector2 v_opposite_pos = e.oppositeEdge.nextEdge.v.position; //Test if we should flip this edge if (DelaunayMethods.ShouldFlipEdge(v_l, v_k, v_third_pos, v_opposite_pos)) { //Flip the edge hasFlippedEdge = true; HalfEdgeHelpMethods.FlipTriangleEdge(e); flippedEdges += 1; // // PAUSE AND VISUALIZE // visualizeController.DisplayMeshMain(triangleData, normalizer); yield return(new WaitForSeconds(0.5f)); } } //We have searched through all edges and havent found an edge to flip, so we cant improve anymore if (!hasFlippedEdge) { //Debug.Log("Found a constrained delaunay triangulation in " + flippedEdges + " flips"); break; } } }
// // Remove the edges that intersects with a constraint by flipping triangles // //The idea here is that all possible triangulations for a set of points can be found //by systematically swapping the diagonal in each convex quadrilateral formed by a pair of triangles //So we will test all possible arrangements and will always find a triangulation which includes the constrained edge private IEnumerator RemoveIntersectingEdges(MyVector2 v_i, MyVector2 v_j, Queue <HalfEdge2> intersectingEdges, List <HalfEdge2> newEdges, HalfEdgeData2 triangleData, Normalizer2 normalizer) { int safety = 0; //While some edges still cross the constrained edge, do steps 3.1 and 3.2 while (intersectingEdges.Count > 0) { safety += 1; if (safety > 100000) { Debug.Log("Stuck in infinite loop when fixing constrained edges"); break; } //Step 3.1. Remove an edge from the list of edges that intersects the constrained edge HalfEdge2 e = intersectingEdges.Dequeue(); //The vertices belonging to the two triangles MyVector2 v_k = e.v.position; MyVector2 v_l = e.prevEdge.v.position; MyVector2 v_3rd = e.nextEdge.v.position; //The vertex belonging to the opposite triangle and isn't shared by the current edge MyVector2 v_opposite_pos = e.oppositeEdge.nextEdge.v.position; //Step 3.2. If the two triangles don't form a convex quadtrilateral //place the edge back on the list of intersecting edges (because this edge cant be flipped) //and go to step 3.1 if (!_Geometry.IsQuadrilateralConvex(v_k, v_l, v_3rd, v_opposite_pos)) { intersectingEdges.Enqueue(e); continue; } else { //Flip the edge like we did when we created the delaunay triangulation HalfEdgeHelpMethods.FlipTriangleEdge(e); // // PAUSE AND VISUALIZE // visualizeController.DisplayMeshMain(triangleData, normalizer); yield return(new WaitForSeconds(0.5f)); //The new diagonal is defined by the vertices MyVector2 v_m = e.v.position; MyVector2 v_n = e.prevEdge.v.position; //If this new diagonal intersects with the constrained edge, add it to the list of intersecting edges if (IsEdgeCrossingEdge(v_i, v_j, v_m, v_n)) { intersectingEdges.Enqueue(e); } //Place it in the list of newly created edges else { newEdges.Add(e); } } } }
IEnumerator InsertPoints(HashSet <MyVector2> points, HalfEdgeData2 triangulationData, Triangle2 superTriangle) { //VISUALZ ShowTriangles(triangulationData); //VISUALZ - dont show the colored mesh until its finished because its flickering controller.shouldDisplayColoredMesh = false; yield return(new WaitForSeconds(controller.pauseTime)); //Step 4. Loop over each point we want to insert and do Steps 5-7 //These are for display purposes only int missedPoints = 0; int flippedEdges = 0; foreach (MyVector2 p in points) { //Step 5. Insert the new point in the triangulation //Find the existing triangle the point is in HalfEdgeFace2 f = PointTriangulationIntersection.TriangulationWalk(p, null, triangulationData); //We couldnt find a triangle maybe because the point is not in the triangulation? if (f == null) { missedPoints += 1; } //Delete this triangle and form 3 new triangles by connecting p to each of the vertices in the old triangle HalfEdgeHelpMethods.SplitTriangleFaceAtPoint(f, p, triangulationData); //VISUALZ //Display the point as a black circle ShowCircle(p); yield return(new WaitForSeconds(controller.pauseTime)); ShowTriangles(triangulationData); yield return(new WaitForSeconds(controller.pauseTime)); //Step 6. Initialize stack. Place all triangles which are adjacent to the edges opposite p on a LIFO stack //The report says we should place triangles, but it's easier to place edges with our data structure Stack <HalfEdge2> trianglesToInvestigate = new Stack <HalfEdge2>(); AddTrianglesOppositePToStack(p, trianglesToInvestigate, triangulationData); //Step 7. Restore delaunay triangulation //While the stack is not empty int safety = 0; while (trianglesToInvestigate.Count > 0) { safety += 1; if (safety > 1000000) { Debug.Log("Stuck in infinite loop when restoring delaunay in incremental sloan algorithm"); break; } //Step 7.1. Remove a triangle from the stack HalfEdge2 edgeToTest = trianglesToInvestigate.Pop(); //Step 7.2. Do we need to flip this edge? //If p is outside or on the circumcircle for this triangle, we have a delaunay triangle and can return to next loop MyVector2 a = edgeToTest.v.position; MyVector2 b = edgeToTest.prevEdge.v.position; MyVector2 c = edgeToTest.nextEdge.v.position; //abc are here counter-clockwise if (DelaunayMethods.ShouldFlipEdgeStable(a, b, c, p)) { HalfEdgeHelpMethods.FlipTriangleEdge(edgeToTest); //Step 7.3. Place any triangles which are now opposite p on the stack AddTrianglesOppositePToStack(p, trianglesToInvestigate, triangulationData); flippedEdges += 1; //VISUALZ controller.flipText.text = "Flipped edges: " + flippedEdges; ShowTriangles(triangulationData); yield return(new WaitForSeconds(controller.pauseTime)); } } } //Dont show the last point we added controller.ResetBlackMeshes(); //Step 8. Delete the vertices belonging to the supertriangle StartCoroutine(RemoveSuperTriangle(superTriangle, triangulationData)); yield return(null); }