// Converts a polygon triangulation to a decomposition of fewer convex partitions by removing // some internal edges with the Hertel-Mehlhorn algorithm. // The algorithm gives at most four times the number of parts as the optimal algorithm, // though in practice it works much better than that and often gives optimal partition. // time complexity O(n^2), n is the number of vertices // space complexity: O(n) // params: // triangles : a triangulation of a polygon // vertices have to be in counter-clockwise order // parts : resulting list of convex polygons // Returns true on success, false on failure private static bool HertelMehlhorn(TPPLPolyList triangles, out TPPLPolyList parts) { int i11; int i12; int i13; int i21 = 0; int i22 = 0; int i23; Vector2 d1, d2, p1, p2, p3; bool isdiagonal; for (int iter1 = 0; iter1 < triangles.Count; iter1++) { TPPLPoly poly1 = triangles[iter1]; for (i11 = 0; i11 < poly1.NumPoints; i11++) { d1 = poly1.GetPoint(i11); i12 = (i11 + 1) % poly1.NumPoints; d2 = poly1.GetPoint(i12); TPPLPoly poly2 = null; isdiagonal = false; int iter2; for (iter2 = iter1 + 1; iter2 < triangles.Count; iter2++) { poly2 = triangles[iter2]; for (i21 = 0; i21 < poly2.NumPoints; i21++) { if ((d2.x != poly2.GetPoint(i21).x) || (d2.y != poly2.GetPoint(i21).y)) { continue; } i22 = (i21 + 1) % poly2.NumPoints; if ((d1.x != poly2.GetPoint(i22).x) || (d1.y != poly2.GetPoint(i22).y)) { continue; } isdiagonal = true; break; } if (isdiagonal) { break; } } if (!isdiagonal) { continue; } p2 = poly1.GetPoint(i11); if (i11 == 0) { i13 = poly1.NumPoints - 1; } else { i13 = i11 - 1; } p1 = poly1.GetPoint(i13); if (i22 == (poly2.NumPoints - 1)) { i23 = 0; } else { i23 = i22 + 1; } p3 = poly2.GetPoint(i23); if (!IsConvex(p1, p2, p3)) { continue; } p2 = poly1.GetPoint(i12); if (i12 == (poly1.NumPoints - 1)) { i13 = 0; } else { i13 = i12 + 1; } p3 = poly1.GetPoint(i13); if (i21 == 0) { i23 = poly2.NumPoints - 1; } else { i23 = i21 - 1; } p1 = poly2.GetPoint(i23); if (!IsConvex(p1, p2, p3)) { continue; } TPPLPoly newpoly = new TPPLPoly(poly1.NumPoints + poly2.NumPoints - 2); int k = 0; for (int j = i12; j != i11; j = (j + 1) % poly1.NumPoints) { newpoly[k] = poly1.GetPoint(j); k++; } for (int j = i22; j != i21; j = (j + 1) % poly2.NumPoints) { newpoly[k] = poly2.GetPoint(j); k++; } triangles.RemoveAt(iter2); if (iter1 > iter2) { iter1--; } triangles[iter1] = newpoly; poly1 = newpoly; i11 = -1; continue; } } parts = triangles; return(true); }
// 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); }