/// <summary> /// Decomposes a non-convex polygon into a number of convex polygons, up /// to maxPolys (remaining pieces are thrown out). /// Each resulting polygon will have no more than Box2D.Settings.MaxPolygonVertices /// vertices. /// Warning: Only works on simple polygons /// </summary> /// <param name="vertices">The vertices.</param> /// <param name="maxPolys">The maximum number of polygons.</param> /// <param name="tolerance">The tolerance.</param> /// <returns></returns> public static List <Vertices> ConvexPartition(Vertices vertices, int maxPolys, float tolerance) { if (vertices.Count < 3) { return new List <Vertices> { vertices } } ; /* * if (vertices.IsConvex() && vertices.Count <= Box2D.Settings.MaxPolygonVertices) * { * if (vertices.IsCounterClockWise()) * { * Vertices tempP = new Vertices(vertices); * tempP.Reverse(); * tempP = SimplifyTools.CollinearSimplify(tempP); * tempP.ForceCounterClockWise(); * return new List<Vertices> { tempP }; * } * vertices = SimplifyTools.CollinearSimplify(vertices); * vertices.ForceCounterClockWise(); * return new List<Vertices> { vertices }; * } */ List <Triangle> triangulated; if (vertices.IsCounterClockWise()) { Vertices tempP = new Vertices(vertices); tempP.Reverse(); triangulated = TriangulatePolygon(tempP); } else { triangulated = TriangulatePolygon(vertices); } if (triangulated.Count < 1) { //Still no luck? Oh well... throw new Exception("Can't triangulate your polygon."); } List <Vertices> polygonizedTriangles = PolygonizeTriangles(triangulated, maxPolys, tolerance); //The polygonized triangles are not guaranteed to be without collinear points. We remove //them to be sure. for (int i = 0; i < polygonizedTriangles.Count; i++) { polygonizedTriangles[i] = SimplifyTools.CollinearSimplify(polygonizedTriangles[i], 0); } //Remove empty vertice collections for (int i = polygonizedTriangles.Count - 1; i >= 0; i--) { if (polygonizedTriangles[i].Count == 0) { polygonizedTriangles.RemoveAt(i); } } return(polygonizedTriangles); }
/// <summary> /// Decompose the polygon into several smaller non-concave polygon. /// If the polygon is already convex, it will return the original polygon, unless it is over Box2D.Settings.MaxPolygonVertices. /// Precondition: Counter Clockwise polygon /// </summary> /// <param name="vertices"></param> /// <returns></returns> public static List <Vertices> ConvexPartition(Vertices vertices) { //We force it to CCW as it is a precondition in this algorithm. vertices.ForceCounterClockWise(); List <Vertices> list = new List <Vertices>(); float d, lowerDist, upperDist; FVector2 p; FVector2 lowerInt = new FVector2(); FVector2 upperInt = new FVector2(); // intersection points int lowerIndex = 0, upperIndex = 0; Vertices lowerPoly, upperPoly; for (int i = 0; i < vertices.Count; ++i) { if (Reflex(i, vertices)) { lowerDist = upperDist = float.MaxValue; // std::numeric_limits<qreal>::max(); for (int j = 0; j < vertices.Count; ++j) { // if line intersects with an edge if (Left(At(i - 1, vertices), At(i, vertices), At(j, vertices)) && RightOn(At(i - 1, vertices), At(i, vertices), At(j - 1, vertices))) { // find the point of intersection p = LineTools.LineIntersect(At(i - 1, vertices), At(i, vertices), At(j, vertices), At(j - 1, vertices)); if (Right(At(i + 1, vertices), At(i, vertices), p)) { // make sure it's inside the poly d = SquareDist(At(i, vertices), p); if (d < lowerDist) { // keep only the closest intersection lowerDist = d; lowerInt = p; lowerIndex = j; } } } if (Left(At(i + 1, vertices), At(i, vertices), At(j + 1, vertices)) && RightOn(At(i + 1, vertices), At(i, vertices), At(j, vertices))) { p = LineTools.LineIntersect(At(i + 1, vertices), At(i, vertices), At(j, vertices), At(j + 1, vertices)); if (Left(At(i - 1, vertices), At(i, vertices), p)) { d = SquareDist(At(i, vertices), p); if (d < upperDist) { upperDist = d; upperIndex = j; upperInt = p; } } } } // if there are no vertices to connect to, choose a point in the middle if (lowerIndex == (upperIndex + 1) % vertices.Count) { FVector2 sp = ((lowerInt + upperInt) / 2); lowerPoly = Copy(i, upperIndex, vertices); lowerPoly.Add(sp); upperPoly = Copy(lowerIndex, i, vertices); upperPoly.Add(sp); } else { double highestScore = 0, bestIndex = lowerIndex; while (upperIndex < lowerIndex) { upperIndex += vertices.Count; } for (int j = lowerIndex; j <= upperIndex; ++j) { if (CanSee(i, j, vertices)) { double score = 1 / (SquareDist(At(i, vertices), At(j, vertices)) + 1); if (Reflex(j, vertices)) { if (RightOn(At(j - 1, vertices), At(j, vertices), At(i, vertices)) && LeftOn(At(j + 1, vertices), At(j, vertices), At(i, vertices))) { score += 3; } else { score += 2; } } else { score += 1; } if (score > highestScore) { bestIndex = j; highestScore = score; } } } lowerPoly = Copy(i, (int)bestIndex, vertices); upperPoly = Copy((int)bestIndex, i, vertices); } list.AddRange(ConvexPartition(lowerPoly)); list.AddRange(ConvexPartition(upperPoly)); return(list); } } // polygon is already convex if (vertices.Count > Box2D.Settings.b2_maxPolygonVertices) { lowerPoly = Copy(0, vertices.Count / 2, vertices); upperPoly = Copy(vertices.Count / 2, 0, vertices); list.AddRange(ConvexPartition(lowerPoly)); list.AddRange(ConvexPartition(upperPoly)); } else { list.Add(vertices); } //The polygons are not guaranteed to be without collinear points. We remove //them to be sure. for (int i = 0; i < list.Count; i++) { list[i] = SimplifyTools.CollinearSimplify(list[i], 0); } //Remove empty vertice collections for (int i = list.Count - 1; i >= 0; i--) { if (list[i].Count == 0) { list.RemoveAt(i); } } return(list); }