// // Visualz // //Show triangles private void ShowTriangles(HalfEdgeData2 triangles) { controller.ResetMultiColoredMeshes(); List <Mesh> meshes = controller.GenerateTriangulationMesh(triangles, shouldUnNormalize: true); List <Material> materials = controller.GenerateRandomMaterials(meshes.Count); controller.multiColoredMeshes = meshes; controller.multiColoredMeshesMaterials = materials; }
// // Display stuff // //Show points private void ShowPoints(HashSet <MyVector2> points) { controller.ResetBlackMeshes(); HashSet <Triangle2> triangles = new HashSet <Triangle2>(); foreach (MyVector2 p in points) { MyVector2 point = controller.UnNormalize(p); HashSet <Triangle2> circleTriangles = _GenerateMesh.Circle(point, 0.1f, 10); triangles.UnionWith(circleTriangles); } //Will unnormalize List <Mesh> meshes = controller.GenerateTriangulationMesh(triangles, shouldUnNormalize: false); controller.blackMeshes = meshes; }
// // For visualization when pressing Play button // private void Start() { GenerateTriangulation(); //To access standardized methods for visualizations VisualizerController visualizerController = GetComponent <VisualizerController>(); //Generate the meshes and materials once meshes = visualizerController.GenerateTriangulationMesh(triangulation, shouldUnNormalize: false); materials = visualizerController.GenerateRandomMaterials(meshes.Count); StartCoroutine(DisplayTriangleByTriangle(meshes)); }
public void StartVisualizer(HashSet <MyVector2> points, HalfEdgeData2 triangleData) { controller = GetComponent <VisualizerController>(); //Step 1. Triangulate the points with some algorithm. The result is a convex triangulation HashSet <Triangle2> triangles = _TriangulatePoints.VisibleEdgesTriangulation(points); //HashSet<Triangle2> triangles = _TriangulatePoints.TriangleSplitting(points); //Step 2. Change the data structure from triangle to half-edge to make it easier to flip edges triangleData = _TransformBetweenDataStructures.Triangle2ToHalfEdge2(triangles, triangleData); //Generate the visual triangles controller.GenerateTriangulationMesh(triangleData); //Step 3. Flip edges until we have a delaunay triangulation StartCoroutine(FlipEdges(triangleData)); }
// // Visualize stuff // private void ShowAllPoints(HashSet <MyVector2> points) { HashSet <Triangle2> triangles = new HashSet <Triangle2>(); foreach (MyVector2 p in points) { HashSet <Triangle2> tCircle = _GenerateMesh.Circle(controller.UnNormalize(p), 0.1f, 10); triangles.UnionWith(tCircle); } //To mesh List <Mesh> meshes = controller.GenerateTriangulationMesh(triangles, shouldUnNormalize: false); controller.ResetBlackMeshes(); controller.blackMeshes = meshes; //Debug.Log(meshes.Count); }
//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); }
private IEnumerator RunVisualization(HashSet <MyVector2> points) { //Step 0. Init the triangles we will return HashSet <Triangle2> triangles = new HashSet <Triangle2>(); //Step 1. Sort the points List <MyVector2> sortedPoints = new List <MyVector2>(points); //OrderBy is always soring in ascending order - use OrderByDescending to get in the other order //sortedPoints = sortedPoints.OrderBy(n => n.x).ToList(); //If we have colinear points we have to sort in both x and y sortedPoints = sortedPoints.OrderBy(n => n.x).ThenBy(n => n.y).ToList(); //Step 2. Create the first triangle so we can start the algorithm because we need edges to look at //and see if they are visible //Pick the first two points in the sorted list - These are always a part of the first triangle MyVector2 p1 = sortedPoints[0]; MyVector2 p2 = sortedPoints[1]; //Remove them sortedPoints.RemoveAt(0); sortedPoints.RemoveAt(0); //The problem is the third point //If we have colinear points, then the third point in the sorted list is not always a valid point //to form a triangle because then it will be flat //So we have to look for a better point for (int i = 0; i < sortedPoints.Count; i++) { //We have found a non-colinear point LeftOnRight pointRelation = Geometry.IsPoint_Left_On_Right_OfVector(p1, p2, sortedPoints[i]); if (pointRelation == LeftOnRight.Left || pointRelation == LeftOnRight.Right) { MyVector2 p3 = sortedPoints[i]; //Remove this point sortedPoints.RemoveAt(i); //Build the first triangle Triangle2 newTriangle = new Triangle2(p1, p2, p3); triangles.Add(newTriangle); break; } } ////If we have finished search and not found a triangle, that means that all points ////are colinear and we cant form any triangles //if (triangles.Count == 0) //{ // Debug.Log("All points you want to triangulate a co-linear"); // return null; //} //Show the triangulation controller.GenerateTriangulationMesh(triangles); yield return(new WaitForSeconds(controller.pauseTime)); //Step 3. Add the other points one-by-one //For each point we add we have to calculate a convex hull of the previous points //An optimization is to not use all points in the triangulation //to calculate the hull because many of them might be inside of the hull //So we will use the previous points on the hull and add the point we added last iteration //to generate the new convex hull //First we need to init the convex hull HashSet <MyVector2> triangulatePoints = new HashSet <MyVector2>(); foreach (Triangle2 t in triangles) { triangulatePoints.Add(t.p1); triangulatePoints.Add(t.p2); triangulatePoints.Add(t.p3); } //Calculate the first convex hull List <MyVector2> pointsOnHull = _ConvexHull.JarvisMarch(triangulatePoints); //Add the other points one-by-one foreach (MyVector2 pointToAdd in sortedPoints) { bool couldFormTriangle = false; controller.SetActivePoint(pointToAdd); //Loop through all edges in the convex hull for (int j = 0; j < pointsOnHull.Count; j++) { MyVector2 hull_p1 = pointsOnHull[j]; MyVector2 hull_p2 = pointsOnHull[MathUtility.ClampListIndex(j + 1, pointsOnHull.Count)]; //First we have to check if the points are colinear, then we cant form a triangle LeftOnRight pointRelation = Geometry.IsPoint_Left_On_Right_OfVector(hull_p1, hull_p2, pointToAdd); if (pointRelation == LeftOnRight.On) { continue; } //If this triangle is clockwise, then we can see the edge //so we should create a new triangle with this edge and the point if (Geometry.IsTriangleOrientedClockwise(hull_p1, hull_p2, pointToAdd)) { triangles.Add(new Triangle2(hull_p1, hull_p2, pointToAdd)); couldFormTriangle = true; //Show the triangulation controller.GenerateTriangulationMesh(triangles); yield return(new WaitForSeconds(controller.pauseTime)); } } //Add the point to the list of points on the hull //Will re-generate the hull by using these points so dont worry that the //list is not valid anymore if (couldFormTriangle) { pointsOnHull.Add(pointToAdd); //Find the convex hull of the current triangulation //It generates a counter-clockwise convex hull pointsOnHull = _ConvexHull.JarvisMarch(new HashSet <MyVector2>(pointsOnHull)); } else { Debug.Log("This point could not form any triangles " + pointToAdd.x + " " + pointToAdd.y); } } yield return(null); }
IEnumerator InsertPoints(HashSet <MyVector2> points, HalfEdgeData2 triangulationData, Triangle2 superTriangle) { //Visualize the first triangle controller.GenerateTriangulationMesh(triangulationData); 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); //Visualize //Display the point as a black circle controller.GenerateCircleMesh(p, shouldResetAllMeshes: true); yield return(new WaitForSeconds(controller.pauseTime)); controller.GenerateTriangulationMesh(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; //Visualize controller.flipText.text = "Flipped edges: " + flippedEdges; controller.GenerateTriangulationMesh(triangulationData); yield return(new WaitForSeconds(controller.pauseTime)); } } } controller.ClearBlackMeshes(); //Step 8. Delete the vertices belonging to the supertriangle StartCoroutine(RemoveSuperTriangle(superTriangle, triangulationData)); yield return(null); }