using System; using System.Collections.Generic; using System.Diagnostics; using FarseerPhysics.Collision.Shapes; using Microsoft.Xna.Framework; namespace FarseerPhysics.Common.PolygonManipulation { internal enum PolyClipType { Intersect, Union, Difference } public enum PolyClipError { None, DegeneratedOutput, NonSimpleInput, BrokenResult } //Clipper contributed by Helge Backhaus public static class YuPengClipper { private const float ClipperEpsilonSquared = 1.192092896e-07f; public static List Union(Vertices polygon1, Vertices polygon2, out PolyClipError error) { return Execute(polygon1, polygon2, PolyClipType.Union, out error); } public static List Difference(Vertices polygon1, Vertices polygon2, out PolyClipError error) { return Execute(polygon1, polygon2, PolyClipType.Difference, out error); } public static List Intersect(Vertices polygon1, Vertices polygon2, out PolyClipError error) { return Execute(polygon1, polygon2, PolyClipType.Intersect, out error); } /// /// 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. /// /// The subject polygon. /// The clip polygon, which is added, /// substracted or intersected with the subject /// The operation to be performed. Either /// Union, Difference or Intersection. /// The error generated (if any) /// A list of closed polygons, which make up the result of the clipping operation. /// Outer contours are ordered counter clockwise, holes are ordered clockwise. private static List 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.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 subjectSimplices; List subjectCoeff; List clipSimplices; List 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 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 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; } /// /// Calculates all intersections between two polygons. /// /// The first polygon. /// The second polygon. /// Returns the first polygon with added intersection points. /// Returns the second polygon with added intersection points. private static void CalculateIntersections(Vertices polygon1, Vertices polygon2, out Vertices slicedPoly1, out Vertices slicedPoly2) { slicedPoly1 = new Vertices(polygon1); slicedPoly2 = new Vertices(polygon2); // Iterate through polygon1's edges for (int i = 0; i < polygon1.Count; i++) { // Get edge vertices Vector2 a = polygon1[i]; Vector2 b = polygon1[polygon1.NextIndex(i)]; // Get intersections between this edge and polygon2 for (int j = 0; j < polygon2.Count; j++) { Vector2 c = polygon2[j]; Vector2 d = polygon2[polygon2.NextIndex(j)]; Vector2 intersectionPoint; // Check if the edges intersect if (LineTools.LineIntersect(a, b, c, d, out intersectionPoint)) { // calculate alpha values for sorting multiple intersections points on a edge float alpha; // Insert intersection point into first polygon alpha = GetAlpha(a, b, intersectionPoint); if (alpha > 0f && alpha < 1f) { int index = slicedPoly1.IndexOf(a) + 1; while (index < slicedPoly1.Count && GetAlpha(a, b, slicedPoly1[index]) <= alpha) { ++index; } slicedPoly1.Insert(index, intersectionPoint); } // Insert intersection point into second polygon alpha = GetAlpha(c, d, intersectionPoint); if (alpha > 0f && alpha < 1f) { int index = slicedPoly2.IndexOf(c) + 1; while (index < slicedPoly2.Count && GetAlpha(c, d, slicedPoly2[index]) <= alpha) { ++index; } slicedPoly2.Insert(index, intersectionPoint); } } } } // Check for very small edges for (int i = 0; i < slicedPoly1.Count; ++i) { int iNext = slicedPoly1.NextIndex(i); //If they are closer than the distance remove vertex if ((slicedPoly1[iNext] - slicedPoly1[i]).LengthSquared() <= ClipperEpsilonSquared) { slicedPoly1.RemoveAt(i); --i; } } for (int i = 0; i < slicedPoly2.Count; ++i) { int iNext = slicedPoly2.NextIndex(i); //If they are closer than the distance remove vertex if ((slicedPoly2[iNext] - slicedPoly2[i]).LengthSquared() <= ClipperEpsilonSquared) { slicedPoly2.RemoveAt(i); --i; } } } /// /// Calculates the simplical chain corresponding to the input polygon. /// /// Used by method Execute(). private static void CalculateSimplicalChain(Vertices poly, out List coeff, out List simplicies) { simplicies = new List(); coeff = new List(); for (int i = 0; i < poly.Count; ++i) { simplicies.Add(new Edge(poly[i], poly[poly.NextIndex(i)])); coeff.Add(CalculateSimplexCoefficient(Vector2.Zero, poly[i], poly[poly.NextIndex(i)])); } } /// /// Calculates the characteristics function for all edges of /// the given simplical chains and builds the result chain. /// /// Used by method Execute(). private static void CalculateResultChain(List poly1Coeff, List poly1Simplicies, List poly2Coeff, List poly2Simplicies, PolyClipType clipType, out List resultSimplices) { resultSimplices = new List(); for (int i = 0; i < poly1Simplicies.Count; ++i) { float edgeCharacter = 0f; if (poly2Simplicies.Contains(poly1Simplicies[i]) || (poly2Simplicies.Contains(-poly1Simplicies[i]) && clipType == PolyClipType.Union)) { edgeCharacter = 1f; } else { for (int j = 0; j < poly2Simplicies.Count; ++j) { if (!poly2Simplicies.Contains(-poly1Simplicies[i])) { edgeCharacter += CalculateBeta(poly1Simplicies[i].GetCenter(), poly2Simplicies[j], poly2Coeff[j]); } } } if (clipType == PolyClipType.Intersect) { if (edgeCharacter == 1f) { resultSimplices.Add(poly1Simplicies[i]); } } else { if (edgeCharacter == 0f) { resultSimplices.Add(poly1Simplicies[i]); } } } for (int i = 0; i < poly2Simplicies.Count; ++i) { if (!resultSimplices.Contains(poly2Simplicies[i]) && !resultSimplices.Contains(-poly2Simplicies[i])) { float edgeCharacter = 0f; if (poly1Simplicies.Contains(poly2Simplicies[i]) || (poly1Simplicies.Contains(-poly2Simplicies[i]) && clipType == PolyClipType.Union)) { edgeCharacter = 1f; } else { for (int j = 0; j < poly1Simplicies.Count; ++j) { if (!poly1Simplicies.Contains(-poly2Simplicies[i])) { edgeCharacter += CalculateBeta(poly2Simplicies[i].GetCenter(), poly1Simplicies[j], poly1Coeff[j]); } } } if (clipType == PolyClipType.Intersect || clipType == PolyClipType.Difference) { if (edgeCharacter == 1f) { resultSimplices.Add(-poly2Simplicies[i]); } } else { if (edgeCharacter == 0f) { resultSimplices.Add(poly2Simplicies[i]); } } } } } /// /// Calculates the polygon(s) from the result simplical chain. /// /// Used by method Execute(). private static PolyClipError BuildPolygonsFromChain(List simplicies, out List result) { result = new List(); 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(); 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; } /// /// Needed to calculate the characteristics function of a simplex. /// /// Used by method CalculateEdgeCharacter(). private static float CalculateBeta(Vector2 point, Edge e, float coefficient) { float result = 0f; if (PointInSimplex(point, e)) { result = coefficient; } if (PointOnLineSegment(Vector2.Zero, e.EdgeStart, point) || PointOnLineSegment(Vector2.Zero, e.EdgeEnd, point)) { result = .5f * coefficient; } return result; } /// /// Needed for sorting multiple intersections points on the same edge. /// /// Used by method CalculateIntersections(). private static float GetAlpha(Vector2 start, Vector2 end, Vector2 point) { return (point - start).LengthSquared() / (end - start).LengthSquared(); } /// /// Returns the coefficient of a simplex. /// /// Used by method CalculateSimplicalChain(). private static float CalculateSimplexCoefficient(Vector2 a, Vector2 b, Vector2 c) { float isLeft = MathUtils.Area(ref a, ref b, ref c); if (isLeft < 0f) { return -1f; } if (isLeft > 0f) { return 1f; } return 0f; } /// /// Winding number test for a point in a simplex. /// /// The point to be tested. /// The edge that the point is tested against. /// False if the winding number is even and the point is outside /// the simplex and True otherwise. private static bool PointInSimplex(Vector2 point, Edge edge) { Vertices polygon = new Vertices(); polygon.Add(Vector2.Zero); polygon.Add(edge.EdgeStart); polygon.Add(edge.EdgeEnd); return (polygon.PointInPolygon(ref point) == 1); } /// /// Tests if a point lies on a line segment. /// /// Used by method CalculateBeta(). private static bool PointOnLineSegment(Vector2 start, Vector2 end, Vector2 point) { Vector2 segment = end - start; return MathUtils.Area(ref start, ref end, ref point) == 0f && Vector2.Dot(point - start, segment) >= 0f && Vector2.Dot(point - end, segment) <= 0f; } private static bool VectorEqual(Vector2 vec1, Vector2 vec2) { return (vec2 - vec1).LengthSquared() <= ClipperEpsilonSquared; } #region Nested type: Edge /// Specifies an Edge. Edges are used to represent simplicies in simplical chains private sealed class Edge { public Edge(Vector2 edgeStart, Vector2 edgeEnd) { EdgeStart = edgeStart; EdgeEnd = edgeEnd; } public Vector2 EdgeStart { get; private set; } public Vector2 EdgeEnd { get; private set; } public Vector2 GetCenter() { return (EdgeStart + EdgeEnd) / 2f; } public static Edge operator -(Edge e) { return new Edge(e.EdgeEnd, e.EdgeStart); } public override bool Equals(Object obj) { // If parameter is null return false. if (obj == null) { return false; } // If parameter cannot be cast to Point return false. return Equals(obj as Edge); } public bool Equals(Edge e) { // If parameter is null return false: if (e == null) { return false; } // Return true if the fields match return VectorEqual(EdgeStart, e.EdgeStart) && VectorEqual(EdgeEnd, e.EdgeEnd); } public override int GetHashCode() { return EdgeStart.GetHashCode() ^ EdgeEnd.GetHashCode(); } } #endregion } }