/// <summary> /// Implements "A new algorithm for Boolean operations on general polygons" /// available here: http://liama.ia.ac.cn/wiki/_media/user:dong:dong_cg_05.pdf /// Merges two polygons, a subject and a clip with the specified operation. Polygons may not be /// self-intersecting. /// /// Warning: May yield incorrect results or even crash if polygons contain collinear points. /// </summary> /// <param name="subject">The subject polygon.</param> /// <param name="clip">The clip polygon, which is added, /// substracted 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<Vertices> Execute(Vertices subject, Vertices clip, PolyClipType clipType, out PolyClipError error) { Debug.Assert(subject.IsSimple() && clip.IsSimple(), "Non simple input!", "Input polygons must be simple (cannot intersect themselves)."); // Copy polygons Vertices slicedSubject; Vertices 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 Vector2 lbSubject = subject.GetAABB().LowerBound; Vector2 lbClip = clip.GetAABB().LowerBound; Vector2 translate; Vector2.Min(ref lbSubject, ref lbClip, out translate); translate = Vector2.One - translate; if (translate != Vector2.Zero) { slicedSubject.Translate(ref translate); slicedClip.Translate(ref translate); } // Enforce counterclockwise contours slicedSubject.ForceCounterClockWise(); slicedClip.ForceCounterClockWise(); List<Edge> subjectSimplices; List<float> subjectCoeff; List<Edge> clipSimplices; List<float> clipCoeff; // Build simplical chains from the polygons and calculate the // the corresponding coefficients CalculateSimplicalChain(slicedSubject, out subjectCoeff, out subjectSimplices); CalculateSimplicalChain(slicedClip, out clipCoeff, out clipSimplices); List<Edge> resultSimplices; // 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 CalculateResultChain(subjectCoeff, subjectSimplices, clipCoeff, clipSimplices, clipType, out resultSimplices); List<Vertices> result; // Convert result chain back to polygon(s) error = BuildPolygonsFromChain(resultSimplices, out result); // Reverse the polygon translation from the beginning // and remove collinear points from output translate *= -1f; for (int i = 0; i < result.Count; ++i) { result[i].Translate(ref translate); SimplifyTools.CollinearSimplify(result[i]); } return result; }
/// <summary> /// Implements "A new algorithm for Boolean operations on general polygons" available here: /// http://liama.ia.ac.cn/wiki/_media/user:dong:dong_cg_05.pdf Merges two polygons, a subject and a clip with the /// specified /// operation. Polygons may not be self-intersecting. Warning: May yield incorrect results or even crash if polygons /// contain collinear points. /// </summary> /// <param name="subject">The subject polygon.</param> /// <param name="clip">The clip polygon, which is added, substracted 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 <Vertices> Execute(Vertices subject, Vertices clip, PolyClipType clipType, out PolyClipError error) { Debug.Assert(subject.IsSimple() && clip.IsSimple(), "Non simple input!", "Input polygons must be simple (cannot intersect themselves)."); // Copy polygons // Calculate the intersection and touch points between // subject and clip and add them to both CalculateIntersections(subject, clip, out Vertices slicedSubject, out Vertices slicedClip); // Translate polygons into upper right quadrant // as the algorithm depends on it Vector2 lbSubject = subject.GetAabb().LowerBound; Vector2 lbClip = clip.GetAabb().LowerBound; Vector2 translate = Vector2.Min(lbSubject, lbClip); translate = Vector2.One - translate; if (translate != Vector2.Zero) { slicedSubject.Translate(ref translate); slicedClip.Translate(ref translate); } // Enforce counterclockwise contours slicedSubject.ForceCounterClockWise(); slicedClip.ForceCounterClockWise(); // Build simplical chains from the polygons and calculate the // the corresponding coefficients CalculateSimplicalChain(slicedSubject, out List <float> subjectCoeff, out List <Edge> subjectSimplices); CalculateSimplicalChain(slicedClip, out List <float> clipCoeff, out List <Edge> 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 CalculateResultChain(subjectCoeff, subjectSimplices, clipCoeff, clipSimplices, clipType, out List <Edge> resultSimplices); // Convert result chain back to polygon(s) error = BuildPolygonsFromChain(resultSimplices, out List <Vertices> result); // Reverse the polygon translation from the beginning // and remove collinear points from output translate *= -1f; for (int i = 0; i < result.Count; ++i) { result[i].Translate(ref translate); SimplifyTools.CollinearSimplify(result[i]); } return(result); }
public static List<Vertices> Difference(Vertices polygon1, Vertices polygon2, out PolyClipError error) { return Execute(polygon1, polygon2, PolyClipType.Difference, out error); }
/// <summary> /// Prepares the polygons. /// </summary> /// <param name="polygon1">The polygon1.</param> /// <param name="polygon2">The polygon2.</param> /// <param name="poly1">The poly1.</param> /// <param name="poly2">The poly2.</param> /// <param name="intersections">The intersections.</param> /// <param name="error">The error.</param> /// <returns></returns> private static int PreparePolygons(Vertices polygon1, Vertices polygon2, out Vertices poly1, out Vertices poly2, out List <EdgeIntersectInfo> intersections, out PolyClipError error) { error = PolyClipError.None; // Make a copy of the polygons so that we dont modify the originals, and // force vertices to integer (pixel) values. //Note: We no longer use pixels. Rounding removed. poly1 = new Vertices(polygon1); poly2 = new Vertices(polygon2); // Find intersection points if (!VerticesIntersect(poly1, poly2, out intersections)) { // No intersections found - polygons do not overlap. error = PolyClipError.NoIntersections; return(-1); } // Add intersection points to original polygons, ignoring existing points. foreach (EdgeIntersectInfo intersect in intersections) { if (!poly1.Contains(intersect.IntersectionPoint)) { poly1.Insert(poly1.IndexOf(intersect.EdgeOne.EdgeStart) + 1, intersect.IntersectionPoint); } if (!poly2.Contains(intersect.IntersectionPoint)) { poly2.Insert(poly2.IndexOf(intersect.EdgeTwo.EdgeStart) + 1, intersect.IntersectionPoint); } } // Find starting point on the edge of polygon1 // that is outside of the intersected area // to begin polygon trace. int startingIndex = -1; int currentIndex = 0; do { if (!poly2.PointInPolygonAngle(poly1[currentIndex])) { startingIndex = currentIndex; break; } currentIndex = poly1.NextIndex(currentIndex); } while (currentIndex != 0); // If we dont find a point on polygon1 thats outside of the // intersect area, the polygon1 must be inside of polygon2, // in which case, polygon2 IS the union of the two. if (startingIndex == -1) { error = PolyClipError.Poly1InsidePoly2; } return(startingIndex); }
/// <summary> /// Finds the intersection between two polygons. /// </summary> /// <param name="polygon1">The first polygon.</param> /// <param name="polygon2">The second polygon.</param> /// <param name="error">The error.</param> /// <returns> /// The intersection of the two polygons, or null if there was an error. /// </returns> public static Vertices Intersect(Vertices polygon1, Vertices polygon2, out PolyClipError error) { error = PolyClipError.None; Vertices poly1; Vertices poly2; List <EdgeIntersectInfo> intersections; PolyClipError gotError; int startingIndex = PreparePolygons(polygon1, polygon2, out poly1, out poly2, out intersections, out gotError); if (startingIndex == -1) { switch (gotError) { case PolyClipError.NoIntersections: return(null); case PolyClipError.Poly1InsidePoly2: return(polygon2); } } Vertices intersectOut = new Vertices(); Vertices currentPoly = poly1; Vertices otherPoly = poly2; // Store the starting vertex so we can refer to it later. int currentIndex = poly1.IndexOf(intersections[0].IntersectionPoint); Vector2 startingVertex = poly1[currentIndex]; do { // Add the current vertex to the final union intersectOut.Add(currentPoly[currentIndex]); foreach (EdgeIntersectInfo intersect in intersections) { // If the current point is an intersection point if (currentPoly[currentIndex] == intersect.IntersectionPoint) { // Make sure we want to swap polygons here. int otherIndex = otherPoly.IndexOf(intersect.IntersectionPoint); // If the next vertex, if we do swap, is inside the current polygon, // then its safe to swap, otherwise, just carry on with the current poly. if (currentPoly.PointInPolygonAngle(otherPoly[otherPoly.NextIndex(otherIndex)])) { // switch polygons if (currentPoly == poly1) { currentPoly = poly2; otherPoly = poly1; } else { currentPoly = poly1; otherPoly = poly2; } // set currentIndex currentIndex = otherIndex; // Stop checking intersections for this point. break; } } } // Move to next index currentIndex = currentPoly.NextIndex(currentIndex); } while ((currentPoly[currentIndex] != startingVertex) && (intersectOut.Count <= (poly1.Count + poly2.Count))); // If the number of vertices in the union is more than the combined vertices // of the input polygons, then something is wrong and the algorithm will // loop forever. Luckily, we check for that. if (intersectOut.Count > (poly1.Count + poly2.Count)) { error = PolyClipError.InfiniteLoop; } return(intersectOut); }
private static List <Vertices> Execute(Vertices subject, Vertices clip, PolyClipType clipType, out PolyClipError error) { Debug.Assert(subject.IsSimple() && clip.IsSimple(), "Non simple input!", "Input polygons must be simple (cannot intersect themselves)."); Vertices vertices; Vertices vertices2; YuPengClipper.CalculateIntersections(subject, clip, out vertices, out vertices2); TSVector2 lowerBound = subject.GetAABB().LowerBound; TSVector2 lowerBound2 = clip.GetAABB().LowerBound; TSVector2 tSVector; TSVector2.Min(ref lowerBound, ref lowerBound2, out tSVector); tSVector = TSVector2.one - tSVector; bool flag = tSVector != TSVector2.zero; if (flag) { vertices.Translate(ref tSVector); vertices2.Translate(ref tSVector); } vertices.ForceCounterClockWise(); vertices2.ForceCounterClockWise(); List <FP> poly1Coeff; List <YuPengClipper.Edge> poly1Simplicies; YuPengClipper.CalculateSimplicalChain(vertices, out poly1Coeff, out poly1Simplicies); List <FP> poly2Coeff; List <YuPengClipper.Edge> poly2Simplicies; YuPengClipper.CalculateSimplicalChain(vertices2, out poly2Coeff, out poly2Simplicies); List <YuPengClipper.Edge> simplicies; YuPengClipper.CalculateResultChain(poly1Coeff, poly1Simplicies, poly2Coeff, poly2Simplicies, clipType, out simplicies); List <Vertices> list; error = YuPengClipper.BuildPolygonsFromChain(simplicies, out list); tSVector *= -1f; for (int i = 0; i < list.Count; i++) { list[i].Translate(ref tSVector); SimplifyTools.CollinearSimplify(list[i], FP.Zero); } return(list); }
public static List <Vertices> Difference(Vertices polygon1, Vertices polygon2, out PolyClipError error) { return(Execute(polygon1, polygon2, PolyClipType.Difference, out error)); }
public static List <Vertices> Union(Vertices polygon1, Vertices polygon2, out PolyClipError error) { return(Execute(polygon1, polygon2, PolyClipType.Union, out error)); }
public static List <List <Vector2> > Intersect( List <Vector2> polygon1, List <Vector2> polygon2, out PolyClipError error) { return(Execute(polygon1, polygon2, PolyClipType.Intersect, out error)); }
public static List <List <Vector2> > Difference( List <Vector2> polygon1, List <Vector2> polygon2, out PolyClipError error) { return(Execute(polygon1, polygon2, PolyClipType.Difference, out error)); }
public static List <List <Vector2> > Union(List <Vector2> polygon1, List <Vector2> polygon2, out PolyClipError error) { return(Execute(polygon1, polygon2, PolyClipType.Union, out error)); }
public static List<Polygon> Union(Polygon polygon1, Polygon polygon2, out PolyClipError error) { return Execute(polygon1, polygon2, PolyClipType.Union, out error); }
public static List<Polygon> Intersect(Polygon polygon1, Polygon polygon2, out PolyClipError error) { return Execute(polygon1, polygon2, PolyClipType.Intersect, out error); }
public static List<Polygon> Difference(Polygon polygon1, Polygon polygon2, out PolyClipError error) { return Execute(polygon1, polygon2, PolyClipType.Difference, out error); }
public static List<Vertices> Intersect(Vertices polygon1, Vertices polygon2, out PolyClipError error) { return Execute(polygon1, polygon2, PolyClipType.Intersect, out error); }
public static List<Vertices> Union(Vertices polygon1, Vertices polygon2, out PolyClipError error) { return Execute(polygon1, polygon2, PolyClipType.Union, out error); }
/// <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> /// Calculates the polygon(s) from the result simplical chain. /// </summary> /// <remarks>Used by method <c>Execute()</c>.</remarks> private static PolyClipError BuildPolygonsFromChain(List <Edge> simplicies, out List <Vertices> result) { result = new List <Vertices>(); PolyClipError errVal = PolyClipError.None; while (simplicies.Count > 0) { Vertices output = new Vertices(); output.Add(simplicies[0].EdgeStart); output.Add(simplicies[0].EdgeEnd); simplicies.RemoveAt(0); bool closed = false; int index = 0; int count = simplicies.Count; // Needed to catch infinite loops while (!closed && simplicies.Count > 0) { if (VectorEqual(output[output.Count - 1], simplicies[index].EdgeStart)) { if (VectorEqual(simplicies[index].EdgeEnd, output[0])) { closed = true; } else { output.Add(simplicies[index].EdgeEnd); } simplicies.RemoveAt(index); --index; } else if (VectorEqual(output[output.Count - 1], simplicies[index].EdgeEnd)) { if (VectorEqual(simplicies[index].EdgeStart, output[0])) { closed = true; } else { output.Add(simplicies[index].EdgeStart); } simplicies.RemoveAt(index); --index; } if (!closed) { if (++index == simplicies.Count) { if (count == simplicies.Count) { result = new List <Vertices>(); Debug.WriteLine("Undefined error while building result polygon(s)."); return(PolyClipError.BrokenResult); } index = 0; count = simplicies.Count; } } } if (output.Count < 3) { errVal = PolyClipError.DegeneratedOutput; Debug.WriteLine("Degenerated output polygon produced (vertices < 3)."); } result.Add(output); } return(errVal); }
/// <summary> /// Implements "A new algorithm for Boolean operations on general polygons" /// available here: http://liama.ia.ac.cn/wiki/_media/user:dong:dong_cg_05.pdf /// Merges two polygons, a subject and a clip with the specified operation. Polygons may not be /// self-intersecting. /// /// Warning: May yield incorrect results or even crash if polygons contain colinear points. /// </summary> /// <param name="subject">The subject polygon.</param> /// <param name="clip">The clip polygon, which is added, /// substracted 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 <Vertices> Execute(Vertices subject, Vertices clip, PolyClipType clipType, out PolyClipError error) { if (!subject.IsSimple() || !clip.IsSimple()) { error = PolyClipError.NonSimpleInput; Debug.WriteLine("Input polygons must be simple (cannot intersect themselves)."); return(new List <Vertices>()); } // Copy polygons Vertices slicedSubject; Vertices 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 Vector2 lbSubject = subject.GetCollisionBox().LowerBound; Vector2 lbClip = clip.GetCollisionBox().LowerBound; Vector2 translate; Vector2.Min(ref lbSubject, ref lbClip, out translate); translate = Vector2.One - translate; if (translate != Vector2.Zero) { slicedSubject.Translate(ref translate); slicedClip.Translate(ref translate); } // Enforce counterclockwise contours slicedSubject.ForceCounterClockWise(); slicedClip.ForceCounterClockWise(); List <Edge> subjectSimplices; List <float> subjectCoeff; List <Edge> clipSimplices; List <float> clipCoeff; // Build simplical chains from the polygons and calculate the // the corresponding coefficients CalculateSimplicalChain(slicedSubject, out subjectCoeff, out subjectSimplices); CalculateSimplicalChain(slicedClip, out clipCoeff, out clipSimplices); List <float> subjectCharacter; List <float> clipCharacter; // Determine the characteristics function for all non-original edges // in subject and clip simplical chain CalculateEdgeCharacter(subjectCoeff, subjectSimplices, clipCoeff, clipSimplices, out subjectCharacter, out clipCharacter); List <Edge> resultSimplices; // Combine the edges contributing to the result, depending on the clipType CalculateResultChain(subjectSimplices, subjectCharacter, clipSimplices, clipCharacter, clipType, out resultSimplices); List <Vertices> result; // Convert result chain back to polygon(s) error = BuildPolygonsFromChain(resultSimplices, out result); // Reverse the polygon translation from the beginning translate *= -1f; for (int i = 0; i < result.Count; ++i) { result[i].Translate(ref translate); } return(result); }
public static List <Vertices> Intersect(Vertices polygon1, Vertices polygon2, out PolyClipError error) { return(Execute(polygon1, polygon2, PolyClipType.Intersect, out error)); }
private static PolyClipError BuildPolygonsFromChain(List <YuPengClipper.Edge> simplicies, out List <Vertices> result) { result = new List <Vertices>(); PolyClipError polyClipError = PolyClipError.None; PolyClipError result2; while (simplicies.Count > 0) { Vertices vertices = new Vertices(); vertices.Add(simplicies[0].EdgeStart); vertices.Add(simplicies[0].EdgeEnd); simplicies.RemoveAt(0); bool flag = false; int num = 0; int count = simplicies.Count; while (!flag && simplicies.Count > 0) { bool flag2 = YuPengClipper.VectorEqual(vertices[vertices.Count - 1], simplicies[num].EdgeStart); if (flag2) { bool flag3 = YuPengClipper.VectorEqual(simplicies[num].EdgeEnd, vertices[0]); if (flag3) { flag = true; } else { vertices.Add(simplicies[num].EdgeEnd); } simplicies.RemoveAt(num); num--; } else { bool flag4 = YuPengClipper.VectorEqual(vertices[vertices.Count - 1], simplicies[num].EdgeEnd); if (flag4) { bool flag5 = YuPengClipper.VectorEqual(simplicies[num].EdgeStart, vertices[0]); if (flag5) { flag = true; } else { vertices.Add(simplicies[num].EdgeStart); } simplicies.RemoveAt(num); num--; } } bool flag6 = !flag; if (flag6) { bool flag7 = ++num == simplicies.Count; if (flag7) { bool flag8 = count == simplicies.Count; if (flag8) { result = new List <Vertices>(); Debug.WriteLine("Undefined error while building result polygon(s)."); result2 = PolyClipError.BrokenResult; return(result2); } num = 0; count = simplicies.Count; } } } bool flag9 = vertices.Count < 3; if (flag9) { polyClipError = PolyClipError.DegeneratedOutput; Debug.WriteLine("Degenerated output polygon produced (vertices < 3)."); } result.Add(vertices); } result2 = polyClipError; return(result2); }