/// <summary> /// Decomposes a non-convex polygon into a number of convex polygons, up to maxPolygons (remaining pieces are thrown /// out). Each resulting polygon will have no more than Settings.MaxPolygonVertices vertices. /// <para/> /// Warning: Only works on simple polygons /// </summary> /// <param name="vertices">The vertices.</param> /// <param name="maxPolygons">The maximum number of polygons.</param> /// <param name="tolerance">The tolerance.</param> /// <returns></returns> public static List <List <Vector2> > ConvexPartition( List <Vector2> vertices, int maxPolygons = int.MaxValue, float tolerance = 0) { if (vertices.Count < 3) { return(new List <List <Vector2> > { vertices }); } List <Triangle> triangulated; if (IsCounterClockWise(vertices)) { var tempP = new List <Vector2>(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."); } var polygonizedTriangles = PolygonizeTriangles(triangulated, maxPolygons, tolerance); //The polygonized triangles are not guaranteed to be without collinear points. We remove //them to be sure. for (var i = 0; i < polygonizedTriangles.Count; i++) { polygonizedTriangles[i] = Simplification.CollinearSimplify(polygonizedTriangles[i], 0); } // Remove empty vertex collections. for (var i = polygonizedTriangles.Count - 1; i >= 0; i--) { if (polygonizedTriangles[i].Count == 0) { polygonizedTriangles.RemoveAt(i); } } return(polygonizedTriangles); }
/// <summary>Actual algorithm.</summary> /// <param name="subject">The subject polygon.</param> /// <param name="clip">The clip polygon, which is added, subtracted or intersected with the subject</param> /// <param name="clipType">The operation to be performed. Either Union, Difference or Intersection.</param> /// <param name="error">The error generated (if any)</param> /// <returns> /// A list of closed polygons, which make up the result of the clipping operation. Outer contours are ordered /// counter clockwise, holes are ordered clockwise. /// </returns> private static List <List <Vector2> > Execute( IList <Vector2> subject, IList <Vector2> clip, PolyClipType clipType, out PolyClipError error) { if (!IsSimple(subject)) { throw new ArgumentException( "Input subject polygon must be simple (cannot intersect themselves).", "subject"); } if (!IsSimple(clip)) { throw new ArgumentException("Input clip polygon must be simple (cannot intersect themselves).", "clip"); } // Copy polygons. List <Vector2> slicedSubject; List <Vector2> slicedClip; // Calculate the intersection and touch points between subject and clip and add them to both. CalculateIntersections(subject, clip, out slicedSubject, out slicedClip); // Translate polygons into upper right quadrant as the algorithm depends on it. var lbSubject = GetLowerBound(subject); var lbClip = GetLowerBound(clip); Vector2 translate; Vector2.Min(ref lbSubject, ref lbClip, out translate); translate = Vector2.One - translate; if (translate != Vector2.Zero) { for (int i = 0, count = slicedSubject.Count; i < count; ++i) { slicedSubject[i] += translate; } for (int i = 0, count = slicedClip.Count; i < count; ++i) { slicedClip[i] += translate; } } // Enforce counterclockwise contours. ForceCounterClockWise(slicedSubject); ForceCounterClockWise(slicedClip); // Build simplical chains from the polygons and calculate the the corresponding coefficients. List <Edge> subjectSimplices; List <float> subjectCoefficient; List <Edge> clipSimplices; List <float> clipCoefficient; CalculateSimplicalChain(slicedSubject, out subjectCoefficient, out subjectSimplices); CalculateSimplicalChain(slicedClip, out clipCoefficient, out clipSimplices); // Determine the characteristics function for all non-original edges // in subject and clip simplical chain and combine the edges contributing // to the result, depending on the clipType var resultSimplices = CalculateResultChain( subjectCoefficient, subjectSimplices, clipCoefficient, clipSimplices, clipType); // Convert result chain back to polygon(s). List <List <Vector2> > result; error = BuildPolygonsFromChain(resultSimplices, out result); // Reverse the polygon translation from the beginning // and remove collinear points from output translate *= -1.0f; foreach (var vertices in result) { for (int i = 0, count = vertices.Count; i < count; ++i) { vertices[i] += translate; } Simplification.CollinearSimplify(vertices); } return(result); }
/// <summary> /// Turns a list of triangles into a list of convex polygons. Very simple method - start with a seed triangle, keep /// adding triangles to it until you can't add any more without making the polygon non-convex. /// <para/> /// Returns an integer telling how many polygons were created. Will fill polygons array up to maxPolygons entries, /// which may be smaller or larger than the return value. /// <para/> /// Takes O(N///P) where P is the number of resultant polygons, N is triangle count. /// <para/> /// The final polygon list will not necessarily be minimal, though in practice it works fairly well. /// </summary> /// <param name="triangulated">The triangulated.</param> /// <param name="maxPolygons">The maximum number of polygons</param> /// <param name="tolerance">The tolerance</param> /// <returns></returns> private static List <List <Vector2> > PolygonizeTriangles( IList <Triangle> triangulated, int maxPolygons, float tolerance) { var polygons = new List <List <Vector2> >(50); var polyIndex = 0; if (triangulated.Count <= 0) { // Return empty polygon list. return(polygons); } var covered = new bool[triangulated.Count]; for (var i = 0; i < triangulated.Count; ++i) { covered[i] = false; //Check here for degenerate triangles // ReSharper disable CompareOfFloatsByEqualityOperator if (((triangulated[i].X[0] == triangulated[i].X[1]) && (triangulated[i].Y[0] == triangulated[i].Y[1])) || ((triangulated[i].X[1] == triangulated[i].X[2]) && (triangulated[i].Y[1] == triangulated[i].Y[2])) || ((triangulated[i].X[0] == triangulated[i].X[2]) && (triangulated[i].Y[0] == triangulated[i].Y[2]))) // ReSharper restore CompareOfFloatsByEqualityOperator { covered[i] = true; } } var notDone = true; while (notDone) { var current = -1; for (var i = 0; i < triangulated.Count; ++i) { if (covered[i]) { continue; } current = i; break; } if (current == -1) { notDone = false; } else { var poly = new List <Vector2>(3); for (var i = 0; i < 3; i++) { poly.Add(new Vector2(triangulated[current].X[i], triangulated[current].Y[i])); } covered[current] = true; var index = 0; for (var i = 0; i < 2 * triangulated.Count; ++i, ++index) { while (index >= triangulated.Count) { index -= triangulated.Count; } if (covered[index]) { continue; } var newPolygon = AddTriangle(triangulated[index], poly); if (newPolygon == null) { continue; } if (newPolygon.Count > 8) { continue; } if (IsConvex(newPolygon)) { poly = new List <Vector2>(newPolygon); covered[index] = true; } } // We have a maximum of polygons that we need to keep under. if (polyIndex < maxPolygons) { Simplification.MergeParallelEdges(poly, tolerance); // If identical points are present, a triangle gets // borked by the MergeParallelEdges function, hence // the vertex number check. if (poly.Count >= 3) { polygons.Add(new List <Vector2>(poly)); } } if (poly.Count >= 3) { polyIndex++; } } } return(polygons); }