//The original algorithm calculates the intersection between two polygons, this will instead get the outside //Assumes the polygons are oriented counter clockwise //poly is the polygon we want to cut //Assumes the polygon we want to remove from the other polygon is convex, so clipPolygon has to be convex //We will end up with the !intersection of the polygons public static List <List <MyVector2> > ClipPolygonInverted(List <MyVector2> poly, List <Plane2> clippingPlanes) { //The result may be more than one polygons List <List <MyVector2> > finalPolygons = new List <List <MyVector2> >(); List <MyVector2> vertices = new List <MyVector2>(poly); //The remaining polygon after each cut List <MyVector2> vertices_tmp = new List <MyVector2>(); //Clip the polygon for (int i = 0; i < clippingPlanes.Count; i++) { Plane2 plane = clippingPlanes[i]; //A new polygon which is the part of the polygon which is outside of this plane List <MyVector2> outsidePolygon = new List <MyVector2>(); for (int j = 0; j < vertices.Count; j++) { int jPlusOne = MathUtility.ClampListIndex(j + 1, vertices.Count); MyVector2 v1 = vertices[j]; MyVector2 v2 = vertices[jPlusOne]; //Calculate the distance to the plane from each vertex //This is how we will know if they are inside or outside //If they are inside, the distance is positive, which is why the planes normals have to be oriented to the inside float dist_to_v1 = Geometry.DistanceFromPointToPlane(plane.normal, plane.pos, v1); float dist_to_v2 = Geometry.DistanceFromPointToPlane(plane.normal, plane.pos, v2); //TODO: What will happen if they are exactly 0? //Case 1. Both are inside (= to the left), save v2 to the other polygon if (dist_to_v1 >= 0f && dist_to_v2 >= 0f) { vertices_tmp.Add(v2); } //Case 2. Both are outside (= to the right), save v1 else if (dist_to_v1 < 0f && dist_to_v2 < 0f) { outsidePolygon.Add(v2); } //Case 3. Outside -> Inside, save intersection point else if (dist_to_v1 < 0f && dist_to_v2 >= 0f) { MyVector2 rayDir = MyVector2.Normalize(v2 - v1); MyVector2 intersectionPoint = Intersections.GetRayPlaneIntersectionPoint(plane.pos, plane.normal, v1, rayDir); outsidePolygon.Add(intersectionPoint); vertices_tmp.Add(intersectionPoint); vertices_tmp.Add(v2); } //Case 4. Inside -> Outside, save intersection point and v2 else if (dist_to_v1 >= 0f && dist_to_v2 < 0f) { MyVector2 rayDir = MyVector2.Normalize(v2 - v1); MyVector2 intersectionPoint = Intersections.GetRayPlaneIntersectionPoint(plane.pos, plane.normal, v1, rayDir); outsidePolygon.Add(intersectionPoint); outsidePolygon.Add(v2); vertices_tmp.Add(intersectionPoint); } } //Add the polygon outside of this plane to the list of all polygons that are outside of all planes if (outsidePolygon.Count > 0) { finalPolygons.Add(outsidePolygon); } //Add the polygon which was inside of this and previous planes to the polygon we want to test vertices.Clear(); vertices.AddRange(vertices_tmp); vertices_tmp.Clear(); } return(finalPolygons); }
// // Add the constraints to the delaunay triangulation // //timer is for debugging private static HalfEdgeData2 AddConstraints(HalfEdgeData2 triangleData, List <MyVector2> constraints, bool shouldRemoveTriangles, System.Diagnostics.Stopwatch timer = null) { //Validate the data if (constraints == null) { return(triangleData); } //Get a list with all edges //This is faster than first searching for unique edges //The report suggest we should do a triangle walk, but it will not work if the mesh has holes //The mesh has holes because we remove triangles while adding constraints one-by-one //so maybe better to remove triangles after we added all constraints... HashSet <HalfEdge2> edges = triangleData.edges; //The steps numbering is from the report //Step 1. Loop over each constrained edge. For each of these edges, do steps 2-4 for (int i = 0; i < constraints.Count; i++) { //Let each constrained edge be defined by the vertices: MyVector2 c_p1 = constraints[i]; MyVector2 c_p2 = constraints[MathUtility.ClampListIndex(i + 1, constraints.Count)]; //Check if this constraint already exists in the triangulation, //if so we are happy and dont need to worry about this edge //timer.Start(); if (IsEdgeInListOfEdges(edges, c_p1, c_p2)) { continue; } //timer.Stop(); //Step 2. Find all edges in the current triangulation that intersects with this constraint //Is returning unique edges only, so not one edge going in the opposite direction //timer.Start(); Queue <HalfEdge2> intersectingEdges = FindIntersectingEdges_BruteForce(edges, c_p1, c_p2); //timer.Stop(); //Debug.Log("Intersecting edges: " + intersectingEdges.Count); //Step 3. Remove intersecting edges by flipping triangles //This takes 0 seconds so is not bottleneck //timer.Start(); List <HalfEdge2> newEdges = RemoveIntersectingEdges(c_p1, c_p2, intersectingEdges); //timer.Stop(); //Step 4. Try to restore delaunay triangulation //Because we have constraints we will never get a delaunay triangulation //This takes 0 seconds so is not bottleneck //timer.Start(); RestoreDelaunayTriangulation(c_p1, c_p2, newEdges); //timer.Stop(); } //Step 5. Remove superfluous triangles, such as the triangles "inside" the constraints if (shouldRemoveTriangles) { //timer.Start(); RemoveSuperfluousTriangles(triangleData, constraints); //timer.Stop(); } return(triangleData); }
public static List <List <MyVector2> > ClipPolygons(List <MyVector2> polyVector2, List <MyVector2> clipPolyVector2, BooleanOperation booleanOperation) { List <List <MyVector2> > finalPoly = new List <List <MyVector2> >(); //Step 0. Create the data structure needed List <ClipVertex> poly = InitDataStructure(polyVector2); List <ClipVertex> clipPoly = InitDataStructure(clipPolyVector2); //Step 1. Find intersection points //Need to test if we have found an intersection point, //if none is found, the polygons dont intersect, or one polygon is inside the other bool hasFoundIntersection = false; for (int i = 0; i < poly.Count; i++) { ClipVertex currentVertex = poly[i]; //Important to use iPlusOne because poly.next may change int iPlusOne = MathUtility.ClampListIndex(i + 1, poly.Count); MyVector2 a = poly[i].coordinate; MyVector2 b = poly[iPlusOne].coordinate; //Gizmos.DrawWireSphere(poly[i].coordinate, 0.02f); //Gizmos.DrawWireSphere(poly[i].next.coordinate, 0.02f); for (int j = 0; j < clipPoly.Count; j++) { int jPlusOne = MathUtility.ClampListIndex(j + 1, clipPoly.Count); MyVector2 c = clipPoly[j].coordinate; MyVector2 d = clipPoly[jPlusOne].coordinate; //Are these lines intersecting? if (Intersections.LineLine(a, b, c, d, true)) { hasFoundIntersection = true; MyVector2 intersectionPoint2D = Intersections.GetLineLineIntersectionPoint(a, b, c, d); //Vector3 intersectionPoint = new Vector3(intersectionPoint2D.x, 0f, intersectionPoint2D.y); //Gizmos.color = Color.red; //Gizmos.DrawWireSphere(intersectionPoint, 0.04f); //We need to insert this intersection vertex into both polygons //Insert into the polygon ClipVertex vertexOnPolygon = InsertIntersectionVertex(a, b, intersectionPoint2D, currentVertex); //Insert into the clip polygon ClipVertex vertexOnClipPolygon = InsertIntersectionVertex(c, d, intersectionPoint2D, clipPoly[j]); //Also connect the intersection vertices with each other vertexOnPolygon.neighbor = vertexOnClipPolygon; vertexOnClipPolygon.neighbor = vertexOnPolygon; } } } //Debug in which order the vertices are in the linked list //InWhichOrderAreVerticesAdded(poly); //InWhichOrderAreVerticesAdded(clipPoly); //If the polygons are intersecting if (hasFoundIntersection) { //Step 2. Trace each polygon and mark entry and exit points to the other polygon's interior MarkEntryExit(poly, clipPolyVector2); MarkEntryExit(clipPoly, polyVector2); //Debug entry exit points DebugEntryExit(poly); //DebugEntryExit(clipPoly); //Step 3. Create the desired clipped polygon if (booleanOperation == BooleanOperation.Intersection) { //Where the two polygons intersect List <ClipVertex> intersectionVertices = GetClippedPolygon(poly, true); //Debug.Log(intersectionVertices.Count); AddPolygonToList(intersectionVertices, finalPoly, false); //Debug.Log(); } else if (booleanOperation == BooleanOperation.Difference) { //Whats outside of the polygon that doesnt intersect List <ClipVertex> outsidePolyVertices = GetClippedPolygon(poly, false); AddPolygonToList(outsidePolyVertices, finalPoly, true); } else if (booleanOperation == BooleanOperation.ExclusiveOr) { //Whats outside of the polygon that doesnt intersect List <ClipVertex> outsidePolyVertices = GetClippedPolygon(poly, false); AddPolygonToList(outsidePolyVertices, finalPoly, true); //Whats outside of the polygon that doesnt intersect List <ClipVertex> outsideClipPolyVertices = GetClippedPolygon(clipPoly, false); AddPolygonToList(outsideClipPolyVertices, finalPoly, true); } else if (booleanOperation == BooleanOperation.Union) { //Where the two polygons intersect List <ClipVertex> intersectionVertices = GetClippedPolygon(poly, true); AddPolygonToList(intersectionVertices, finalPoly, false); //Whats outside of the polygon that doesnt intersect List <ClipVertex> outsidePolyVertices = GetClippedPolygon(poly, false); AddPolygonToList(outsidePolyVertices, finalPoly, true); //Whats outside of the polygon that doesnt intersect List <ClipVertex> outsideClipPolyVertices = GetClippedPolygon(clipPoly, false); AddPolygonToList(outsideClipPolyVertices, finalPoly, true); } //Where the two polygons intersect //List<ClipVertex> intersectionVertices = GetClippedPolygon(poly, true); //Whats outside of the polygon that doesnt intersect //These will be in clockwise order so remember to change to counter clockwise //List<ClipVertex> outsidePolyVertices = GetClippedPolygon(poly, false); //List<ClipVertex> outsideClipPolyVertices = GetClippedPolygon(clipPoly, false); } //Check if one polygon is inside the other else { //Is the polygon inside the clip polygon? //Depending on the type of boolean operation, we might get a hole if (IsPolygonInsidePolygon(polyVector2, clipPolyVector2)) { Debug.Log("Poly is inside clip poly"); } else if (IsPolygonInsidePolygon(clipPolyVector2, polyVector2)) { Debug.Log("Clip poly is inside poly"); } else { Debug.Log("Polygons are not intersecting"); } } return(finalPoly); }
//Assumes the polygons are oriented counter clockwise //poly is the polygon we want to cut //Assumes the polygon we want to remove from the other polygon is convex, so clipPolygon has to be convex //We will end up with the intersection of the polygons public static List <MyVector2> ClipPolygon(List <MyVector2> poly, List <Plane2> clippingPlanes) { //Clone the vertices because we will remove vertices from this list List <MyVector2> vertices = new List <MyVector2>(poly); //Save the new vertices temporarily in this list before transfering them to vertices List <MyVector2> vertices_tmp = new List <MyVector2>(); //Clip the polygon for (int i = 0; i < clippingPlanes.Count; i++) { Plane2 plane = clippingPlanes[i]; for (int j = 0; j < vertices.Count; j++) { int jPlusOne = MathUtility.ClampListIndex(j + 1, vertices.Count); MyVector2 v1 = vertices[j]; MyVector2 v2 = vertices[jPlusOne]; //Calculate the distance to the plane from each vertex //This is how we will know if they are inside or outside //If they are inside, the distance is positive, which is why the planes normals have to be oriented to the inside float dist_to_v1 = _Geometry.GetSignedDistanceFromPointToPlane(plane, v1); float dist_to_v2 = _Geometry.GetSignedDistanceFromPointToPlane(plane, v2); //TODO: What will happen if they are exactly 0? Should maybe use a tolerance of 0.001 //Case 1. Both are outside (= to the right), do nothing //Case 2. Both are inside (= to the left), save v2 if (dist_to_v1 >= 0f && dist_to_v2 >= 0f) { vertices_tmp.Add(v2); } //Case 3. Outside -> Inside, save intersection point and v2 else if (dist_to_v1 < 0f && dist_to_v2 >= 0f) { MyVector2 rayDir = MyVector2.Normalize(v2 - v1); Ray2 ray = new Ray2(v1, rayDir); MyVector2 intersectionPoint = _Intersections.GetRayPlaneIntersectionPoint(plane, ray); vertices_tmp.Add(intersectionPoint); vertices_tmp.Add(v2); } //Case 4. Inside -> Outside, save intersection point else if (dist_to_v1 >= 0f && dist_to_v2 < 0f) { MyVector2 rayDir = MyVector2.Normalize(v2 - v1); Ray2 ray = new Ray2(v1, rayDir); MyVector2 intersectionPoint = _Intersections.GetRayPlaneIntersectionPoint(plane, ray); vertices_tmp.Add(intersectionPoint); } } //Add the new vertices to the list of vertices vertices.Clear(); vertices.AddRange(vertices_tmp); vertices_tmp.Clear(); } return(vertices); }
//The points on the hull (vertices) should be ordered counter-clockwise (and no doubles) //The holes should be ordered clockwise (and no doubles) //Optimize triangles means that we will get a better-looking triangulation, which resembles a constrained Delaunay triangulation public static HashSet <Triangle2> Triangulate(List <MyVector2> vertices, List <List <MyVector2> > allHoleVertices = null, bool optimizeTriangles = true) { //Validate the data if (vertices == null || vertices.Count <= 2) { Debug.LogWarning("Can't triangulate with Ear Clipping because too few vertices on the hull"); return(null); } //Step -1. Merge the holes with the points on the hull into one big polygon with invisible edges between the holes and the hull if (allHoleVertices != null && allHoleVertices.Count > 0) { vertices = EarClippingHoleMethods.MergeHolesWithHull(vertices, allHoleVertices); } //TestAlgorithmsHelpMethods.DebugDrawCircle(vertices[29].ToVector3(1f), 0.3f, Color.red); //Step 0. Create a linked list connecting all vertices with each other which will make the calculations easier and faster List <LinkedVertex> verticesLinked = new List <LinkedVertex>(); for (int i = 0; i < vertices.Count; i++) { LinkedVertex v = new LinkedVertex(vertices[i]); verticesLinked.Add(v); } //Link them to each other for (int i = 0; i < verticesLinked.Count; i++) { LinkedVertex v = verticesLinked[i]; v.prevLinkedVertex = verticesLinked[MathUtility.ClampListIndex(i - 1, verticesLinked.Count)]; v.nextLinkedVertex = verticesLinked[MathUtility.ClampListIndex(i + 1, verticesLinked.Count)]; } //Debug.Log("Number of vertices: " + CountLinkedVertices(verticesLinked[0])); //Step 1. Find: //- Convex vertices (interior angle smaller than 180 degrees) //- Reflect vertices (interior angle greater than 180 degrees) so should maybe be called concave vertices? //Interior angle is the angle between two vectors inside the polygon if we move around the polygon counter-clockwise //If they are neither we assume they are reflect (or we will end up with odd triangulations) HashSet <LinkedVertex> convexVerts = new HashSet <LinkedVertex>(); HashSet <LinkedVertex> reflectVerts = new HashSet <LinkedVertex>(); foreach (LinkedVertex v in verticesLinked) { bool isConvex = IsVertexConvex(v); if (isConvex) { convexVerts.Add(v); } else { reflectVerts.Add(v); } } //Step 2. Find the initial ears HashSet <LinkedVertex> earVerts = new HashSet <LinkedVertex>(); //An ear is always a convex vertex foreach (LinkedVertex v in convexVerts) { //And we only need to test the reflect vertices if (IsVertexEar(v, reflectVerts)) { earVerts.Add(v); } } //Debug //DisplayVertices(earVertices); //Step 3. Build the triangles HashSet <Triangle2> triangulation = new HashSet <Triangle2>(); //We know how many triangles we will get (number of vertices - 2) which is true for all simple polygons //This can be used to stop the algorithm int maxTriangles = verticesLinked.Count - 2; //Because we use a while loop, having an extra safety is always good so we dont get stuck in infinite loop int safety = 0; while (true) { //Pick an ear vertex and form a triangle LinkedVertex ear = GetEarVertex(earVerts, optimizeTriangles); if (ear == null) { Debug.Log("Cant find ear"); break; } LinkedVertex v_prev = ear.prevLinkedVertex; LinkedVertex v_next = ear.nextLinkedVertex; Triangle2 t = new Triangle2(ear.pos, v_prev.pos, v_next.pos); //Try to flip this triangle according to Delaunay triangulation if (optimizeTriangles) { OptimizeTriangle(t, triangulation); } else { triangulation.Add(t); } //Check if we have found all triangles //This should also prevent us from getting stuck in an infinite loop if (triangulation.Count >= maxTriangles) { break; } //If we havent found all triangles we have to reconfigure the data structure //Remove the ear we used to build a triangle convexVerts.Remove(ear); earVerts.Remove(ear); //Reconnect the vertices because one vertex has now been removed v_prev.nextLinkedVertex = v_next; v_next.prevLinkedVertex = v_prev; //Reconfigure the adjacent vertices ReconfigureAdjacentVertex(v_prev, convexVerts, reflectVerts, earVerts); ReconfigureAdjacentVertex(v_next, convexVerts, reflectVerts, earVerts); //if (safety > 4) //{ // Debug.Log(earVerts.Count); // Debug.DrawLine(v_next.pos.ToVector3(), Vector3.zero, Color.blue, 3f); // //Debug.Log(IsVertexEar(v_next, reflectVerts)); // Debug.Log(earVerts.Contains(v_next)); // break; //} safety += 1; if (safety > 50000) { Debug.Log("Ear Clipping is stuck in an infinite loop!"); break; } } //Step 4. Improve triangulation //Some triangles may be too sharp, and if you want a nice looking triangle, you should try to swap edges //according to Delaunay triangulation //A report suggests that should be done while createing the triangulation //But maybe it's easier to do it afterwards with some standardized constrained Delaunay triangulation? //But that would also be stupid because then we could have used the constrained Delaunay from the beginning! return(triangulation); }
//We assume an edge is visible from a point if the triangle (formed by travering edges in the convex hull //of the existing triangulation) form a clockwise triangle with the point //https://stackoverflow.com/questions/8898255/check-whether-a-point-is-visible-from-a-face-of-a-2d-convex-hull private static HashSet <Triangle2> TriangulatePointsConvexHull(HashSet <MyVector2> points) { if (points.Count < 3) { Debug.Log("You need at least 3 points to form a triangle!"); return(null); } //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); } //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; //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; } } //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); } } return(triangles); }
// // Connected line segments // //isConnected means if the end points are connected to form a loop public static HashSet <Triangle2> ConnectedLineSegments(List <MyVector2> points, float width, bool isConnected) { if (points != null && points.Count < 2) { Debug.Log("Cant form a line with fewer than two points"); return(null); } //Generate the triangles HashSet <Triangle2> lineTriangles = new HashSet <Triangle2>(); //If the lines are connected we need to do plane-plane intersection to find the //coordinate where the lines meet at each point, or the line segments will //not get the same size //(There might be a better way to do it than with plane-plane intersection) List <MyVector2> topCoordinate = new List <MyVector2>(); List <MyVector2> bottomCoordinate = new List <MyVector2>(); float halfWidth = width * 0.5f; for (int i = 0; i < points.Count; i++) { MyVector2 p = points[i]; //First point = special case if the lines are not connected if (i == 0 && !isConnected) { MyVector2 lineDir = points[1] - points[0]; MyVector2 lineNormal = MyVector2.Normalize(new MyVector2(lineDir.y, -lineDir.x)); topCoordinate.Add(p + lineNormal * halfWidth); bottomCoordinate.Add(p - lineNormal * halfWidth); } //Last point = special case if the lines are not connected else if (i == points.Count - 1 && !isConnected) { MyVector2 lineDir = p - points[points.Count - 2]; MyVector2 lineNormal = MyVector2.Normalize(new MyVector2(lineDir.y, -lineDir.x)); topCoordinate.Add(p + lineNormal * halfWidth); bottomCoordinate.Add(p - lineNormal * halfWidth); } else { //Now we need to find the intersection points between the top line and the bottom line MyVector2 p_before = points[MathUtility.ClampListIndex(i - 1, points.Count)]; MyVector2 p_after = points[MathUtility.ClampListIndex(i + 1, points.Count)]; MyVector2 pTop = GetIntersectionPoint(p_before, p, p_after, halfWidth, isTopPoint: true); MyVector2 pBottom = GetIntersectionPoint(p_before, p, p_after, halfWidth, isTopPoint: false); topCoordinate.Add(pTop); bottomCoordinate.Add(pBottom); } } //Debug.Log(); for (int i = 0; i < points.Count; i++) { //Skip the first point if it is not connected to the last point if (i == 0 && !isConnected) { continue; } int i_minus_one = MathUtility.ClampListIndex(i - 1, points.Count); MyVector2 p1_T = topCoordinate[i_minus_one]; MyVector2 p1_B = bottomCoordinate[i_minus_one]; MyVector2 p2_T = topCoordinate[i]; MyVector2 p2_B = bottomCoordinate[i]; HashSet <Triangle2> triangles = LineSegment(p1_T, p1_B, p2_T, p2_B); foreach (Triangle2 t in triangles) { lineTriangles.Add(t); } } //Debug.Log(lineTriangles.Count); return(lineTriangles); }
// // Algorithm 1 // //If you have points on a convex hull, sorted one after each other //If you have colinear points, it will ignore some of them but still triangulate the entire area //Colinear points are not changing the shape public static HashSet <Triangle2> GetTriangles(List <MyVector2> pointsOnHull, bool addColinearPoints) { //If we hadnt have to deal with colinear points, this algorithm would be really simple: HashSet <Triangle2> triangles = new HashSet <Triangle2>(); //This vertex will be a vertex in all triangles MyVector2 a = pointsOnHull[0]; //And then we just loop through the other edges to make all triangles for (int i = 1; i < pointsOnHull.Count; i++) { MyVector2 b = pointsOnHull[i]; MyVector2 c = pointsOnHull[MathUtility.ClampListIndex(i + 1, pointsOnHull.Count)]; //Is this a valid triangle? //If a, b, c are on the same line, the triangle has no area and we can't add it LeftOnRight pointRelation = _Geometry.IsPoint_Left_On_Right_OfVector(a, b, c); if (pointRelation == LeftOnRight.On) { continue; } triangles.Add(new Triangle2(a, b, c)); } //Add the missing colinear points by splitting triangles //Step 2.1. We have now triangulated the entire convex polygon, but if we have colinear points //some of those points were not added //We can add them by splitting triangles //Find colinear points if (addColinearPoints) { HashSet <MyVector2> colinearPoints = new HashSet <MyVector2>(pointsOnHull); //Remove points that are in the triangulation from the points on the convex hull //and we can see which where not added = the colinear points foreach (Triangle2 t in triangles) { colinearPoints.Remove(t.p1); colinearPoints.Remove(t.p2); colinearPoints.Remove(t.p3); } //Debug.Log("Colinear points: " + colinearPoints.Count); //Go through all colinear points and find which edge they should split //On the border we only need to split one edge because this edge has no neighbors foreach (MyVector2 p in colinearPoints) { foreach (Triangle2 t in triangles) { //Is this point in the triangle if (_Intersections.PointTriangle(t, p, includeBorder: true)) { SplitTriangleEdge(t, p, triangles); break; } } } } return(triangles); }
//We assume an edge is visible from a point if the triangle (formed by travering edges in the convex hull //of the existing triangulation) form a clockwise triangle with the point //https://stackoverflow.com/questions/8898255/check-whether-a-point-is-visible-from-a-face-of-a-2d-convex-hull private static HashSet <Triangle2> TriangulatePointsConvexHull(HashSet <MyVector2> pointsHashset) { HashSet <Triangle2> triangles = new HashSet <Triangle2>(); //The points we will add List <MyVector2> originalPoints = new List <MyVector2>(pointsHashset); //Step 1. Sort the points along x-axis //OrderBy is always soring in ascending order - use OrderByDescending to get in the other order //originalPoints = originalPoints.OrderBy(n => n.x).ThenBy(n => n.y).ToList(); originalPoints = originalPoints.OrderBy(n => n.x).ToList(); //Step 2. Create the first triangle so we can start the algorithm //Assumes the convex hull algorithm sorts in the same way in x and y directions as we do above MyVector2 p1Start = originalPoints[0]; MyVector2 p2Start = originalPoints[1]; MyVector2 p3Start = originalPoints[2]; //Theese points for the first triangle Triangle2 newTriangle = new Triangle2(p1Start, p2Start, p3Start); triangles.Add(newTriangle); //All points that form the triangles HashSet <MyVector2> triangulatedPoints = new HashSet <MyVector2>(); triangulatedPoints.Add(p1Start); triangulatedPoints.Add(p2Start); triangulatedPoints.Add(p3Start); //Step 3. Add the other points one-by-one //Add the other points one by one //Starts at 3 because we have already added 0,1,2 for (int i = 3; i < originalPoints.Count; i++) { MyVector2 pointToAdd = originalPoints[i]; //Find the convex hull of the current triangulation //It generates a counter-clockwise convex hull List <MyVector2> pointsOnHull = _ConvexHull.JarvisMarch(triangulatedPoints); if (pointsOnHull == null) { Debug.Log("No points on hull when triangulating"); continue; } bool couldFormTriangle = false; //Loop through all edges in the convex hull for (int j = 0; j < pointsOnHull.Count; j++) { MyVector2 p1 = pointsOnHull[j]; MyVector2 p2 = pointsOnHull[MathUtility.ClampListIndex(j + 1, pointsOnHull.Count)]; //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(p1, p2, pointToAdd)) { triangles.Add(new Triangle2(p1, p2, pointToAdd)); couldFormTriangle = true; } } //Add the point to the list of all points in the current triangulation //if the point could form a triangle if (couldFormTriangle) { triangulatedPoints.Add(pointToAdd); } } return(triangles); }