//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 }
//Insert a new point in the triangulation we already have, so we need at least one triangle public static void InsertNewPointInTriangulation(MyVector2 p, HalfEdgeData2 triangulationData, ref int missedPoints, ref int flippedEdges) { //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); //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; } } }
// // Which triangle in a triangulation is a point in? Should maybe be a part of the intersection library? // public static HalfEdgeFace2 FindWhichTriangleAPointIsIn(MyVector2 p, HalfEdgeFace2 startTriangle, HalfEdgeData2 triangulationData) { //Alternative 1. Search through all triangles and use point-in-triangle //HalfEdgeFace2 intersectingTriangle = FindWhichTriangleAPointIsIn_BruteForce(p, triangulationData); //Alternative 2.Triangulation walk HalfEdgeFace2 intersectingTriangle = FindWhichTriangleAPointIsIn_TriangulationWalk(p, startTriangle, triangulationData); return(intersectingTriangle); }
// // Split triangle edge // //Split an edge at a point on the edge to form four new triangles, while removing two old //public static void SplitTriangleEdge(HalfEdge e, Vector3 splitPosition) //{ //} // // Split triangle face // //Split a face (which we know is a triangle) at a point to create three new triangles while removing the old triangle //Could maybe make it more general so we can split a face, which consists of n edges public static void SplitTriangleFaceAtPoint(HalfEdgeFace2 f, MyVector2 splitPosition, HalfEdgeData2 data) { //The edges that belongs to this face HalfEdge2 e_1 = f.edge; HalfEdge2 e_2 = e_1.nextEdge; HalfEdge2 e_3 = e_2.nextEdge; //A list with new edges so we can connect the new edges with an edge on the opposite side HashSet <HalfEdge2> newEdges = new HashSet <HalfEdge2>(); CreateNewFace(e_1, splitPosition, data, newEdges); CreateNewFace(e_2, splitPosition, data, newEdges); CreateNewFace(e_3, splitPosition, data, newEdges); //Debug.Log("New edges " + newEdges.Count); //Find the opposite connections foreach (HalfEdge2 e in newEdges) { //If we have already found a opposite if (e.oppositeEdge != null) { continue; } MyVector2 eGoingTo = e.v.position; MyVector2 eGoingFrom = e.prevEdge.v.position; foreach (HalfEdge2 eOpposite in newEdges) { if (e == eOpposite || eOpposite.oppositeEdge != null) { continue; } MyVector2 eGoingTo_Other = eOpposite.v.position; MyVector2 eGoingFrom_Other = eOpposite.prevEdge.v.position; if (eGoingTo.Equals(eGoingFrom_Other) && eGoingFrom.Equals(eGoingTo_Other)) { e.oppositeEdge = eOpposite; //Might as well connect it from the other way as well eOpposite.oppositeEdge = e; //Debug.Log("Found opposite"); } } } //Delete the old triangle DeleteTriangleFace(f, data, false); }
//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; } }
// // Delete a triangle // public static void DeleteTriangleFace(HalfEdgeFace2 t, HalfEdgeData2 data, bool shouldSetOppositeToNull) { //Update the data structure //In the half-edge data structure there's an edge going in the opposite direction //on the other side of this triangle with a reference to this edge, so we have to set these to null HalfEdge2 t_e1 = t.edge; HalfEdge2 t_e2 = t_e1.nextEdge; HalfEdge2 t_e3 = t_e2.nextEdge; //If we want to remove the triangle and create a hole //But sometimes we have created a new triangle and then we cant set the opposite to null if (shouldSetOppositeToNull) { if (t_e1.oppositeEdge != null) { t_e1.oppositeEdge.oppositeEdge = null; } if (t_e2.oppositeEdge != null) { t_e2.oppositeEdge.oppositeEdge = null; } if (t_e3.oppositeEdge != null) { t_e3.oppositeEdge.oppositeEdge = null; } } //Remove from the data structure //Remove from the list of all triangles data.faces.Remove(t); //Remove the edges from the list of all edges data.edges.Remove(t_e1); data.edges.Remove(t_e2); data.edges.Remove(t_e3); //Remove the vertices data.vertices.Remove(t_e1.v); data.vertices.Remove(t_e2.v); data.vertices.Remove(t_e3.v); }
// // Alternative 1. Search through all triangles and use point-in-triangle // //Simple but slow public static HalfEdgeFace2 BruteForce(MyVector2 p, HalfEdgeData2 triangulationData) { HalfEdgeFace2 intersectingTriangle = null; foreach (HalfEdgeFace2 f in triangulationData.faces) { //The corners of this triangle MyVector2 v1 = f.edge.v.position; MyVector2 v2 = f.edge.nextEdge.v.position; MyVector2 v3 = f.edge.nextEdge.nextEdge.v.position; Triangle2 t = new Triangle2(v1, v2, v3); //Is the point in this triangle? if (_Intersections.PointTriangle(t, p, true)) { intersectingTriangle = f; break; } } return(intersectingTriangle); }
// // Triangle to half-edge // public static HalfEdgeData2 Triangle2ToHalfEdge2(HashSet <Triangle2> triangles, HalfEdgeData2 data) { //Make sure the triangles have the same orientation, which is clockwise triangles = HelpMethods.OrientTrianglesClockwise(triangles); //Fill the data structure foreach (Triangle2 t in triangles) { HalfEdgeVertex2 v1 = new HalfEdgeVertex2(t.p1); HalfEdgeVertex2 v2 = new HalfEdgeVertex2(t.p2); HalfEdgeVertex2 v3 = new HalfEdgeVertex2(t.p3); //The vertices the edge points to HalfEdge2 he1 = new HalfEdge2(v1); HalfEdge2 he2 = new HalfEdge2(v2); HalfEdge2 he3 = new HalfEdge2(v3); he1.nextEdge = he2; he2.nextEdge = he3; he3.nextEdge = he1; he1.prevEdge = he3; he2.prevEdge = he1; he3.prevEdge = he2; //The vertex needs to know of an edge going from it v1.edge = he2; v2.edge = he3; v3.edge = he1; //The face the half-edge is connected to HalfEdgeFace2 face = new HalfEdgeFace2(he1); //Each edge needs to know of the face connected to this edge he1.face = face; he2.face = face; he3.face = face; //Add everything to the lists data.edges.Add(he1); data.edges.Add(he2); data.edges.Add(he3); data.faces.Add(face); data.vertices.Add(v1); data.vertices.Add(v2); data.vertices.Add(v3); } //Step 4. Find the half-edges going in the opposite direction of each edge we have //Is there a faster way to do this because this is the bottleneck? foreach (HalfEdge2 e in data.edges) { HalfEdgeVertex2 goingToVertex = e.v; HalfEdgeVertex2 goingFromVertex = e.prevEdge.v; foreach (HalfEdge2 eOther in data.edges) { //Dont compare with itself if (e == eOther) { continue; } //Is this edge going between the vertices in the opposite direction if (goingFromVertex.position.Equals(eOther.v.position) && goingToVertex.position.Equals(eOther.prevEdge.v.position)) { e.oppositeEdge = eOther; break; } } } return(data); }
// // 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 triangles with an edge that is a constraint //We have to find all because they are not always connected, so we cant just find one and flood fill from it //for (int i = 0; i < constraints.Count; i++) //{ // MyVector2 c_p1 = constraints[i]; // MyVector2 c_p2 = constraints[MathUtility.ClampListIndex(i + 1, constraints.Count)]; // HalfEdgeFace2 borderTriangle = FindTriangleWithEdge(c_p1, c_p2, triangleData); // //Maybe this edge has not triangle, which can happen if it's on the border // if (borderTriangle != null) // { // trianglesToCheck.Enqueue(borderTriangle); // } //} HashSet <HalfEdgeFace2> borderFaces = FindAllTrianglesBorderingTheConstraint(constraints, triangleData); foreach (HalfEdgeFace2 f in borderFaces) { trianglesToCheck.Enqueue(f); } //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; } //We have to check if this edge is a constraint because it might be alone on the border without neighbors that are also constraints MyVector2 p1 = e.prevEdge.v.position; MyVector2 p2 = e.v.position; if (IsEdgeAConstraint(p1, p2, constraints)) { continue; } trianglesToCheck.Enqueue(neighbor); } } return(trianglesToDelete); }
// // Find which triangles are within a constraint // public static HashSet <HalfEdgeFace2> FindTrianglesWithinConstraint(HalfEdgeData2 triangleData, List <MyVector2> constraints) { HashSet <HalfEdgeFace2> trianglesToDelete = new HashSet <HalfEdgeFace2>(); //Step 1. Find a triangle with an edge that shares an edge with the first constraint edge in the list //Since both are clockwise we know we are "inside" of the constraint, so this is a triangle we should delete HalfEdgeFace2 borderTriangle = null; MyVector2 c_p1 = constraints[0]; MyVector2 c_p2 = constraints[1]; //Search through all triangles foreach (HalfEdgeFace2 t in triangleData.faces) { //The edges in this triangle HalfEdge2 e1 = t.edge; HalfEdge2 e2 = e1.nextEdge; HalfEdge2 e3 = e2.nextEdge; //Is any of these edges a constraint? If so we have find the first triangle if (e1.v.position.Equals(c_p2) && e1.prevEdge.v.position.Equals(c_p1)) { borderTriangle = t; break; } if (e2.v.position.Equals(c_p2) && e2.prevEdge.v.position.Equals(c_p1)) { borderTriangle = t; break; } if (e3.v.position.Equals(c_p2) && e3.prevEdge.v.position.Equals(c_p1)) { borderTriangle = t; break; } } if (borderTriangle == null) { return(null); } //Step 2. Find the rest of the triangles within the constraint by using a flood fill algorithm //Maybe better to first find all the other border triangles? //We know this triangle should be deleted trianglesToDelete.Add(borderTriangle); //Store the triangles we flood filling in this queue Queue <HalfEdgeFace2> trianglesToCheck = new Queue <HalfEdgeFace2>(); //Start at the triangle we know is within the constraints trianglesToCheck.Enqueue(borderTriangle); int safety = 0; 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(); //Investigate the triangles on the opposite sides of these edges HalfEdge2 e1 = t.edge; HalfEdge2 e2 = e1.nextEdge; HalfEdge2 e3 = e2.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 if (e1.oppositeEdge != null && !trianglesToDelete.Contains(e1.oppositeEdge.face) && !trianglesToCheck.Contains(e1.oppositeEdge.face) && !IsEdgeAConstraint(e1.v.position, e1.prevEdge.v.position, constraints)) { trianglesToCheck.Enqueue(e1.oppositeEdge.face); trianglesToDelete.Add(e1.oppositeEdge.face); } if (e2.oppositeEdge != null && !trianglesToDelete.Contains(e2.oppositeEdge.face) && !trianglesToCheck.Contains(e2.oppositeEdge.face) && !IsEdgeAConstraint(e2.v.position, e2.prevEdge.v.position, constraints)) { trianglesToCheck.Enqueue(e2.oppositeEdge.face); trianglesToDelete.Add(e2.oppositeEdge.face); } if (e3.oppositeEdge != null && !trianglesToDelete.Contains(e3.oppositeEdge.face) && !trianglesToCheck.Contains(e3.oppositeEdge.face) && !IsEdgeAConstraint(e3.v.position, e3.prevEdge.v.position, constraints)) { trianglesToCheck.Enqueue(e3.oppositeEdge.face); trianglesToDelete.Add(e3.oppositeEdge.face); } } return(trianglesToDelete); }
// // 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); }
// // Alternative 2. Triangulation walk // //Fast but a little more complicated to understand //We can also give it a list, which should be empty so we can display the triangulation walk public static HalfEdgeFace2 TriangulationWalk(MyVector2 p, HalfEdgeFace2 startTriangle, HalfEdgeData2 triangulationData, List <HalfEdgeFace2> visitedTriangles = null) { HalfEdgeFace2 intersectingTriangle = null; //If we have a triangle to start in which may speed up the algorithm HalfEdgeFace2 currentTriangle = null; //We can feed it a start triangle to sometimes make the algorithm faster if (startTriangle != null) { currentTriangle = startTriangle; } //Find a random start triangle which is faster than starting at the first triangle? else { int randomPos = Random.Range(0, triangulationData.faces.Count); int i = 0; //faces are stored in a hashset so we have to loop through them while counting //to find the start triangle foreach (HalfEdgeFace2 f in triangulationData.faces) { if (i == randomPos) { currentTriangle = f; break; } i += 1; } } if (currentTriangle == null) { Debug.Log("Couldnt find start triangle when walking in triangulation"); return(null); } if (visitedTriangles != null) { visitedTriangles.Add(currentTriangle); } //Start the triangulation walk to find the intersecting triangle int safety = 0; while (true) { safety += 1; if (safety > 1000000) { Debug.Log("Stuck in endless loop when walking in triangulation"); break; } //Is the point intersecting with the current triangle? //We need to do 3 tests where each test is using the triangles edges //If the point is to the right of all edges, then it's inside the triangle //If the point is to the left we jump to that triangle instead HalfEdge2 e1 = currentTriangle.edge; HalfEdge2 e2 = e1.nextEdge; HalfEdge2 e3 = e2.nextEdge; //Test 1 if (IsPointToTheRightOrOnLine(e1.prevEdge.v.position, e1.v.position, p)) { //Test 2 if (IsPointToTheRightOrOnLine(e2.prevEdge.v.position, e2.v.position, p)) { //Test 3 if (IsPointToTheRightOrOnLine(e3.prevEdge.v.position, e3.v.position, p)) { //We have found the triangle the point is in intersectingTriangle = currentTriangle; break; } //If to the left, move to this triangle else { currentTriangle = e3.oppositeEdge.face; } } //If to the left, move to this triangle else { currentTriangle = e2.oppositeEdge.face; } } //If to the left, move to this triangle else { currentTriangle = e1.oppositeEdge.face; } if (visitedTriangles != null) { visitedTriangles.Add(currentTriangle); } } //Add the last triangle if we found it if (visitedTriangles != null && intersectingTriangle != null) { visitedTriangles.Add(intersectingTriangle); } return(intersectingTriangle); }
//Create a new triangle face when splitting triangle face private static void CreateNewFace(HalfEdge2 e_old, MyVector2 splitPosition, HalfEdgeData2 data, HashSet <HalfEdge2> newEdges) { //This triangle has the following positons MyVector2 p_split = splitPosition; MyVector2 p_next = e_old.prevEdge.v.position; MyVector2 p_prev = e_old.v.position; //Create the new stuff HalfEdgeVertex2 v_split = new HalfEdgeVertex2(p_split); HalfEdgeVertex2 v_next = new HalfEdgeVertex2(p_next); HalfEdgeVertex2 v_prev = new HalfEdgeVertex2(p_prev); //This is the edge that has the same position as the old edge HalfEdge2 e_1 = new HalfEdge2(v_prev); HalfEdge2 e_2 = new HalfEdge2(v_split); HalfEdge2 e_3 = new HalfEdge2(v_next); //The new face HalfEdgeFace2 f = new HalfEdgeFace2(e_1); //Create the connections //The new edge e has the same opposite as the old edge e_1.oppositeEdge = e_old.oppositeEdge; //But the opposite edge needs a new reference to this edge if its not a border if (e_1.oppositeEdge != null) { e_old.oppositeEdge.oppositeEdge = e_1; } //The other new edges will find the opposite in a loop when we have created all new edges newEdges.Add(e_2); newEdges.Add(e_3); //Create the connections between the edges e_1.nextEdge = e_2; e_1.prevEdge = e_3; e_2.nextEdge = e_3; e_2.prevEdge = e_1; e_3.nextEdge = e_1; e_3.prevEdge = e_2; //Each edge needs to connect to a face e_1.face = f; e_2.face = f; e_3.face = f; //The vertices need an edge that starts at that point v_split.edge = e_3; v_next.edge = e_1; v_prev.edge = e_2; //Add them to the lists data.faces.Add(f); data.edges.Add(e_1); data.edges.Add(e_2); data.edges.Add(e_3); data.vertices.Add(v_split); data.vertices.Add(v_next); data.vertices.Add(v_prev); }
// // Flip triangle edge // //So the edge shared by two triangles is going between the two other vertices originally not part of the edge public static void FlipTriangleEdge(HalfEdge2 e) { //The data we need //This edge's triangle edges HalfEdge2 e_1 = e; HalfEdge2 e_2 = e_1.nextEdge; HalfEdge2 e_3 = e_1.prevEdge; //The opposite edge's triangle edges HalfEdge2 e_4 = e_1.oppositeEdge; HalfEdge2 e_5 = e_4.nextEdge; HalfEdge2 e_6 = e_4.prevEdge; //The 4 vertex positions MyVector2 aPos = e_1.v.position; MyVector2 bPos = e_2.v.position; MyVector2 cPos = e_3.v.position; MyVector2 dPos = e_5.v.position; //The 6 old vertices, we can use HalfEdgeVertex2 a_old = e_1.v; HalfEdgeVertex2 b_old = e_1.nextEdge.v; HalfEdgeVertex2 c_old = e_1.prevEdge.v; HalfEdgeVertex2 a_opposite_old = e_4.prevEdge.v; HalfEdgeVertex2 c_opposite_old = e_4.v; HalfEdgeVertex2 d_old = e_4.nextEdge.v; //Flip //Vertices //Triangle 1: b-c-d HalfEdgeVertex2 b = b_old; HalfEdgeVertex2 c = c_old; HalfEdgeVertex2 d = d_old; //Triangle 1: b-d-a HalfEdgeVertex2 b_opposite = a_opposite_old; b_opposite.position = bPos; HalfEdgeVertex2 d_opposite = c_opposite_old; d_opposite.position = dPos; HalfEdgeVertex2 a = a_old; //Change half-edge - half-edge connections e_1.nextEdge = e_3; e_1.prevEdge = e_5; e_2.nextEdge = e_4; e_2.prevEdge = e_6; e_3.nextEdge = e_5; e_3.prevEdge = e_1; e_4.nextEdge = e_6; e_4.prevEdge = e_2; e_5.nextEdge = e_1; e_5.prevEdge = e_3; e_6.nextEdge = e_2; e_6.prevEdge = e_4; //Half-edge - vertex connection e_1.v = b; e_2.v = b_opposite; e_3.v = c; e_4.v = d_opposite; e_5.v = d; e_6.v = a; //Half-edge - face connection HalfEdgeFace2 f1 = e_1.face; HalfEdgeFace2 f2 = e_4.face; e_1.face = f1; e_3.face = f1; e_5.face = f1; e_2.face = f2; e_4.face = f2; e_6.face = f2; //Face - half-edge connection f1.edge = e_3; f2.edge = e_4; //Vertices connection, which should have a reference to a half-edge going away from the vertex //Triangle 1: b-c-d b.edge = e_3; c.edge = e_5; d.edge = e_1; //Triangle 1: b-d-a b_opposite.edge = e_4; d_opposite.edge = e_6; a.edge = e_2; //Opposite-edges are not changing! //And neither are we adding, removing data so we dont need to update the lists with all data }