//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); }
//Is an edge between p1 and p2 a constraint? //private static bool IsEdgeAConstraint(MyVector2 p1, MyVector2 p2, List<MyVector2> constraints) //{ // for (int i = 0; i < constraints.Count; i++) // { // MyVector2 c_p1 = constraints[i]; // MyVector2 c_p2 = constraints[MathUtility.ClampListIndex(i + 1, constraints.Count)]; // if (AreTwoEdgesTheSame(p1, p2, c_p1, c_p2)) // { // return true; // } // } // return false; //} //Is an edge crossing another edge? private static bool IsEdgeCrossingEdge(MyVector2 e1_p1, MyVector2 e1_p2, MyVector2 e2_p1, MyVector2 e2_p2) { //We will here run into floating point precision issues so we have to be careful //To solve that you can first check the end points //and modify the line-line intersection algorithm to include a small epsilon //First check if the edges are sharing a point, if so they are not crossing if (e1_p1.Equals(e2_p1) || e1_p1.Equals(e2_p2) || e1_p2.Equals(e2_p1) || e1_p2.Equals(e2_p2)) { return(false); } //Then check if the lines are intersecting if (!_Intersections.LineLine(new Edge2(e1_p1, e1_p2), new Edge2(e2_p1, e2_p2), includeEndPoints: false)) { return(false); } return(true); }
//Find a triangle which has an edge going from p1 to p2 //private static HalfEdgeFace2 FindTriangleWithEdge(MyVector2 p1, MyVector2 p2, HalfEdgeData2 triangleData) //{ // HashSet<HalfEdge2> edges = triangleData.edges; // foreach (HalfEdge2 e in edges) // { // //An edge is going TO a vertex // MyVector2 e_p1 = e.prevEdge.v.position; // MyVector2 e_p2 = e.v.position; // if (e_p1.Equals(p1) && e_p2.Equals(p2)) // { // return e.face; // } // } // return null; //} //Find all half-edges that are constraint private static HashSet <HalfEdge2> FindAllConstraintEdges(List <MyVector2> constraints, HalfEdgeData2 triangleData) { HashSet <HalfEdge2> constrainEdges = new HashSet <HalfEdge2>(); //Create a new set with all constrains, and as we discover new constraints, we delete constrains, which will make searching faster //A constraint can only exist once! HashSet <Edge2> constraintsEdges = new HashSet <Edge2>(); for (int i = 0; i < constraints.Count; i++) { MyVector2 c_p1 = constraints[i]; MyVector2 c_p2 = constraints[MathUtility.ClampListIndex(i + 1, constraints.Count)]; constraintsEdges.Add(new Edge2(c_p1, c_p2)); } //All edges we have to search HashSet <HalfEdge2> edges = triangleData.edges; foreach (HalfEdge2 e in edges) { //An edge is going TO a vertex MyVector2 e_p1 = e.prevEdge.v.position; MyVector2 e_p2 = e.v.position; //Is this edge a constraint? foreach (Edge2 c_edge in constraintsEdges) { if (e_p1.Equals(c_edge.p1) && e_p2.Equals(c_edge.p2)) { constrainEdges.Add(e); constraintsEdges.Remove(c_edge); //Move on to the next edge break; } } //We have found all constraint, so don't need to search anymore if (constraintsEdges.Count == 0) { break; } } return(constrainEdges); }
// // 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; } } }
private IEnumerator RunAlgorithm(List <MyVector2> points) { //The list with points on the convex hull List <MyVector2> pointsOnConvexHull = new List <MyVector2>(); //Step 0. Normalize the data to range [0, 1] or everything will break at larger sizes :( //Make sure the data is already normalized!!! //Step 1. Find the vertex with the smallest x coordinate //If several points have the same x coordinate, find the one with the smallest y MyVector2 startPos = points[0]; for (int i = 1; i < points.Count; i++) { MyVector2 testPos = points[i]; //Because of precision issues, we use a small value to test if they are the same if (testPos.x < startPos.x || ((Mathf.Abs(testPos.x - startPos.x) < MathUtility.EPSILON && testPos.y < startPos.y))) { startPos = points[i]; } } //This vertex is always on the convex hull pointsOnConvexHull.Add(startPos); //VISUALIZE ShowHull(pointsOnConvexHull, null); yield return(new WaitForSeconds(controller.pauseTime)); //But we can't remove it from the list of all points because we need it to stop the algorithm //points.Remove(startPos); //Step 2. Loop to find the other points on the hull MyVector2 previousPoint = pointsOnConvexHull[0]; int counter = 0; while (true) { //We might have colinear points, so we need a list to save all points added this iteration List <MyVector2> pointsToAddToTheHull = new List <MyVector2>(); //Pick next point randomly MyVector2 nextPoint = points[Random.Range(0, points.Count)]; //If we are coming from the first point on the convex hull //then we are not allowed to pick it as next point, so we have to try again if (previousPoint.Equals(pointsOnConvexHull[0]) && nextPoint.Equals(pointsOnConvexHull[0])) { counter += 1; continue; } //VISUALIZE ShowHull(pointsOnConvexHull, new List <MyVector2>() { nextPoint }); //ShowActivePoint(nextPoint); yield return(new WaitForSeconds(controller.pauseTime)); //This point is assumed to be on the convex hull pointsToAddToTheHull.Add(nextPoint); //But this randomly selected point might not be the best next point, so we have to see if we can improve //by finding a point that is more to the right //We also have to check if this point has colinear points if it happens to be on the hull for (int i = 0; i < points.Count; i++) { MyVector2 testPoint = points[i]; //Dont test the point we picked randomly //Or the point we are coming from which might happen when we move from the first point on the hull if (testPoint.Equals(nextPoint) || testPoint.Equals(previousPoint)) { continue; } //VISUALIZE //ShowHull(pointsOnConvexHull, new List<MyVector2>() { testPoint }); ShowActivePoint(testPoint); yield return(new WaitForSeconds(controller.pauseTime)); //Where is the test point in relation to the line between the point we are coming from //which we know is on the hull, and the point we think is on the hull LeftOnRight pointRelation = _Geometry.IsPoint_Left_On_Right_OfVector(previousPoint, nextPoint, testPoint); //The test point is on the line, so we have found a colinear point if (pointRelation == LeftOnRight.On) { pointsToAddToTheHull.Add(testPoint); } //To the right = better point, so pick it as next point we want to test if it is on the hull else if (pointRelation == LeftOnRight.Right) { nextPoint = testPoint; //Clear colinear points because they belonged to the old point which was worse pointsToAddToTheHull.Clear(); pointsToAddToTheHull.Add(nextPoint); //We dont have to start over because the previous points weve gone through were worse //VISUALIZE ShowHull(pointsOnConvexHull, pointsToAddToTheHull); yield return(new WaitForSeconds(controller.pauseTime)); } //To the left = worse point so do nothing } //Sort this list, so we can add the colinear points in correct order pointsToAddToTheHull = pointsToAddToTheHull.OrderBy(n => MyVector2.SqrMagnitude(n - previousPoint)).ToList(); pointsOnConvexHull.AddRange(pointsToAddToTheHull); //Remove the points that are now on the convex hull, which should speed up the algorithm //Or will it be slower because it also takes some time to remove points? for (int i = 0; i < pointsToAddToTheHull.Count; i++) { points.Remove(pointsToAddToTheHull[i]); } //The point we are coming from in the next iteration previousPoint = pointsOnConvexHull[pointsOnConvexHull.Count - 1]; //Have we found the first point on the hull? If so we have completed the hull if (previousPoint.Equals(pointsOnConvexHull[0])) { //Then remove it because it is the same as the first point, and we want a convex hull with no duplicates pointsOnConvexHull.RemoveAt(pointsOnConvexHull.Count - 1); //Stop the loop! break; } //Safety if (counter > 100000) { Debug.Log("Stuck in endless loop when generating convex hull with jarvis march"); break; } counter += 1; } //Dont forget to unnormalize the points! //VISUALIZE ShowHull(pointsOnConvexHull, new List <MyVector2>() { pointsOnConvexHull[0] }); yield return(null); }