//Method 2. Triangulation walk //This assumes there are no holes in the mesh //And that we have a super-triangle around the triangulation private static void FindIntersectingEdges_TriangleWalk(HalfEdgeData2 triangleData, MyVector2 c_p1, MyVector2 c_p2, List <HalfEdge2> intersectingEdges) { //Step 1. Begin at a triangle connected to the constraint edges's vertex c_p1 HalfEdgeFace2 f = null; foreach (HalfEdgeFace2 testFace in triangleData.faces) { //The edges the triangle consists of HalfEdge2 e1 = testFace.edge; HalfEdge2 e2 = e1.nextEdge; HalfEdge2 e3 = e2.nextEdge; //Does one of these edges include the first vertex in the constraint edge if (e1.v.position.Equals(c_p1) || e2.v.position.Equals(c_p1) || e3.v.position.Equals(c_p1)) { f = testFace; break; } } //Step2. Walk around p1 until we find a triangle with an edge that intersects with the edge p1-p2 //Step3. March from one triangle to the next in the general direction of p2 }
//Find all triangles opposite of vertex p //But we will find all edges opposite to p, and from these edges we can find the triangles private static void AddTrianglesOppositePToStack(MyVector2 p, Stack <HalfEdge2> trianglesOppositeP, HalfEdgeData2 triangulationData) { //Find a vertex at position p and then rotate around it, triangle-by-triangle, to find all opposite edges HalfEdgeVertex2 rotateAroundThis = null; foreach (HalfEdgeVertex2 v in triangulationData.vertices) { if (v.position.Equals(p)) { rotateAroundThis = v; } } //Which triangle is this vertex a part of, so we know when we have rotated all the way around HalfEdgeFace2 tStart = rotateAroundThis.edge.face; HalfEdgeFace2 tCurrent = null; int safety = 0; while (tCurrent != tStart) { safety += 1; if (safety > 10000) { Debug.Log("Stuck in endless loop when finding opposite edges in Delaunay Sloan"); break; } //The edge opposite to p HalfEdge2 edgeOppositeRotateVertex = rotateAroundThis.edge.nextEdge.oppositeEdge; //Try to add the edge to the list iof triangles we are interested in //Null might happen if we are at the border //A stack might include duplicates so we have to check for that as well if (edgeOppositeRotateVertex != null && !trianglesOppositeP.Contains(edgeOppositeRotateVertex)) { trianglesOppositeP.Push(edgeOppositeRotateVertex); } //Rotate left - this assumes we can always rotate left so no holes are allowed //and neither can we investigate one of the vertices thats a part of the supertriangle //which we dont need to worry about because p is never a part of the supertriangle rotateAroundThis = rotateAroundThis.edge.oppositeEdge.v; //In which triangle are we now? tCurrent = rotateAroundThis.edge.face; } }
// // Find which triangles are within a constraint // public static HashSet <HalfEdgeFace2> FindTrianglesWithinConstraint(HalfEdgeData2 triangleData, List <MyVector2> constraints) { HashSet <HalfEdgeFace2> trianglesToDelete = new HashSet <HalfEdgeFace2>(); //Store the triangles we flood fill in this queue Queue <HalfEdgeFace2> trianglesToCheck = new Queue <HalfEdgeFace2>(); //Step 1. Find all half-edges in the current triangulation which are constraint //Maybe faster to find all constraintEdges for ALL constraints because we are doing this per hole and hull //We have to find ALL because some triangles are not connected and will thus be missed if we find just a single start-triangle //Is also needed when flood-filling so we dont jump over a constraint HashSet <HalfEdge2> constraintEdges = FindAllConstraintEdges(constraints, triangleData); //Each edge is associated with a face which should be deleted foreach (HalfEdge2 e in constraintEdges) { if (!trianglesToCheck.Contains(e.face)) { trianglesToCheck.Enqueue(e.face); } } //Step 2. Find the rest of the triangles within the constraint by using a flood-fill algorithm int safety = 0; List <HalfEdge2> edgesToCheck = new List <HalfEdge2>(); while (true) { safety += 1; if (safety > 100000) { Debug.Log("Stuck in infinite loop when looking for triangles within constraint"); break; } //Stop if we are out of neighbors if (trianglesToCheck.Count == 0) { break; } //Pick the first triangle in the list and investigate its neighbors HalfEdgeFace2 t = trianglesToCheck.Dequeue(); //Add it for deletion trianglesToDelete.Add(t); //Investigate the triangles on the opposite sides of these edges edgesToCheck.Clear(); edgesToCheck.Add(t.edge); edgesToCheck.Add(t.edge.nextEdge); edgesToCheck.Add(t.edge.nextEdge.nextEdge); //A triangle is a neighbor within the constraint if: //- The neighbor is not an outer border meaning no neighbor exists //- If we have not already visited the neighbor //- If the edge between the neighbor and this triangle is not a constraint foreach (HalfEdge2 e in edgesToCheck) { //No neighbor exists if (e.oppositeEdge == null) { continue; } HalfEdgeFace2 neighbor = e.oppositeEdge.face; //We have already visited this neighbor if (trianglesToDelete.Contains(neighbor) || trianglesToCheck.Contains(neighbor)) { continue; } //This edge is a constraint and we can't jump across constraints if (constraintEdges.Contains(e)) { continue; } trianglesToCheck.Enqueue(neighbor); } } return(trianglesToDelete); }
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); }