// Simple heuristic procedure for removing holes from a list of polygons // works by creating a diagonal from the rightmost hole vertex to some visible vertex // time complexity: O(h*(n^2)), h is the number of holes, n is the number of vertices // space complexity: O(n) // params: // inpolys : a list of polygons that can contain holes // vertices of all non-hole polys have to be in counter-clockwise order // vertices of all hole polys have to be in clockwise order // outpolys : a list of polygons without holes // Returns true on success, false on failure private static bool RemoveHoles(TPPLPolyList inpolys, out TPPLPolyList outpolys) { TPPLPolyList polys = new TPPLPolyList(inpolys); // Check for trivial case (no holes) bool hasholes = false; foreach (var poly in inpolys) { if (poly.IsHole()) { hasholes = true; break; } } if (!hasholes) { outpolys = new TPPLPolyList(inpolys); return(true); } while (true) { int holeIndex = 0; int polyIndex = 0; int holepointindex = 0; int polypointindex = 0; //find the hole point with the largest x hasholes = false; for (int index = 0; index < polys.Count; index++) { var poly = polys[index]; if (!poly.IsHole()) { continue; } if (!hasholes) { hasholes = true; holeIndex = index; holepointindex = 0; } for (int i = 0; i < poly.NumPoints; i++) { if (poly.GetPoint(i).x > polys[holeIndex].GetPoint(holepointindex).x) { holeIndex = index; holepointindex = i; } } } if (!hasholes) { break; } Vector2 holepoint = polys[holeIndex].GetPoint(holepointindex); Vector2 bestpolypoint = new Vector2(); bool pointfound = false; for (int index = 0; index < polys.Count; index++) { var poly = polys[index]; if (poly.IsHole()) { continue; } for (int i = 0; i < poly.NumPoints; i++) { if (poly.GetPoint(i).x <= holepoint.x) { continue; } if (!InCone(poly.GetPoint((i + poly.NumPoints - 1) % poly.NumPoints), poly.GetPoint(i), poly.GetPoint((i + 1) % poly.NumPoints), holepoint)) { continue; } Vector2 polypoint = poly.GetPoint(i); if (pointfound) { Vector2 v1 = Normalize(polypoint - holepoint); Vector2 v2 = Normalize(bestpolypoint - holepoint); if (v2.x > v1.x) { continue; } } bool pointvisible = true; foreach (var poly2 in polys) { if (poly2.IsHole()) { continue; } for (int j = 0; j < poly2.NumPoints; j++) { Vector2 linep1 = poly2.GetPoint(j); Vector2 linep2 = poly2.GetPoint((j + 1) % poly2.NumPoints); if (Intersects(holepoint, polypoint, linep1, linep2)) { pointvisible = false; break; } } if (!pointvisible) { break; } } if (pointvisible) { pointfound = true; bestpolypoint = polypoint; polyIndex = index; polypointindex = i; } } } if (!pointfound) { outpolys = null; return(false); } TPPLPoly newpoly = new TPPLPoly(polys[holeIndex].NumPoints + polys[polyIndex].NumPoints + 2); int i2 = 0; for (int i = 0; i <= polypointindex; i++) { newpoly[i2] = polys[polyIndex].GetPoint(i); i2++; } for (int i = 0; i <= polys[holeIndex].NumPoints; i++) { newpoly[i2] = polys[holeIndex].GetPoint((i + holepointindex) % polys[holeIndex].NumPoints); i2++; } for (int i = polypointindex; i < polys[polyIndex].NumPoints; i++) { newpoly[i2] = polys[polyIndex].GetPoint(i); i2++; } polys.RemoveAt(holeIndex); if (polyIndex > holeIndex) { polyIndex--; } polys.RemoveAt(polyIndex); polys.Add(newpoly); } outpolys = polys; return(true); }
// Triangulates a polygon by ear clipping // time complexity O(n^2), n is the number of vertices // space complexity: O(n) // params: // poly : an input polygon to be triangulated // vertices have to be in counter-clockwise order // triangles : a list of triangles (result) // returns true on success, false on failure private static bool Triangulate_EC(TPPLPoly poly, out TPPLPolyList triangles) { triangles = new TPPLPolyList(); if (!poly.Valid()) { return(false); } int numvertices = poly.NumPoints; if (numvertices < 3) { return(false); } if (numvertices == 3) { triangles.Add(poly); return(true); } PartitionVertex[] vertices = new PartitionVertex[numvertices]; PartitionVertex ear = null; for (int i = 0; i < numvertices; i++) { vertices[i] = new PartitionVertex(); } for (int i = 0; i < numvertices; i++) { vertices[i].isActive = true; vertices[i].p = poly.GetPoint(i); if (i == (numvertices - 1)) { vertices[i].next = vertices[0]; } else { vertices[i].next = vertices[i + 1]; } if (i == 0) { vertices[i].previous = vertices[numvertices - 1]; } else { vertices[i].previous = vertices[i - 1]; } } for (int i = 0; i < numvertices; i++) { UpdateVertex(vertices[i], vertices, numvertices); } for (int i = 0; i < numvertices - 3; i++) { bool earfound = false; //find the most extruded ear for (int j = 0; j < numvertices; j++) { if (!vertices[j].isActive) { continue; } if (!vertices[j].isEar) { continue; } if (!earfound) { earfound = true; ear = vertices[j]; } else { if (vertices[j].angle > ear.angle) { ear = vertices[j]; } } } if (!earfound) { return(false); } triangles.Add(new TPPLPoly(ear.previous.p, ear.p, ear.next.p)); ear.isActive = false; ear.previous.next = ear.next; ear.next.previous = ear.previous; if (i == numvertices - 4) { break; } UpdateVertex(ear.previous, vertices, numvertices); UpdateVertex(ear.next, vertices, numvertices); } for (int i = 0; i < numvertices; i++) { if (vertices[i].isActive) { triangles.Add(new TPPLPoly(vertices[i].previous.p, vertices[i].p, vertices[i].next.p)); break; } } return(true); }