/// <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); }