/// <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) { #if WINDOWS Debug.Assert(subject.IsSimple() && clip.IsSimple(), "Non simple input!", "Input polygons must be simple (cannot intersect themselves)."); #endif // 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); }
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); }
/// <summary> /// Calculates the result between the subject and clip simplical chains, /// based on the provided operation. /// </summary> /// <remarks>Used by method <c>Execute()</c>.</remarks> private static void CalculateResultChain(List <Edge> poly1Simplicies, List <float> poly1Char, List <Edge> poly2Simplicies, List <float> poly2Char, PolyClipType clipType, out List <Edge> resultSimplices) { resultSimplices = new List <Edge>(); for (int i = 0; i < poly1Simplicies.Count; ++i) { if (clipType == PolyClipType.Intersect) { if (poly1Char[i] == 1f) { resultSimplices.Add(poly1Simplicies[i]); } } else { if (poly1Char[i] == 0f) { resultSimplices.Add(poly1Simplicies[i]); } } } for (int i = 0; i < poly2Simplicies.Count; ++i) { if (clipType == PolyClipType.Intersect || clipType == PolyClipType.Difference) { if (poly2Char[i] == 1f) { resultSimplices.Add(-poly2Simplicies[i]); } } else { if (poly2Char[i] == 0f) { resultSimplices.Add(poly2Simplicies[i]); } } } }
/// <summary> /// Calculates the characteristics function for all edges of /// the given simplical chains and builds the result chain. /// </summary> /// <remarks>Used by method <c>Execute()</c>.</remarks> private static void CalculateResultChain(List <FP> poly1Coeff, List <Edge> poly1Simplicies, List <FP> poly2Coeff, List <Edge> poly2Simplicies, PolyClipType clipType, out List <Edge> resultSimplices) { resultSimplices = new List <Edge>(); for (int i = 0; i < poly1Simplicies.Count; ++i) { FP edgeCharacter = 0; if (poly2Simplicies.Contains(poly1Simplicies[i])) { edgeCharacter = 1f; } else if (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) { FP edgeCharacter = 0f; if (!resultSimplices.Contains(poly2Simplicies[i]) && !resultSimplices.Contains(-poly2Simplicies[i])) { if (poly1Simplicies.Contains(-poly2Simplicies[i]) && clipType == PolyClipType.Union) { edgeCharacter = 1f; } else { edgeCharacter = 0f; for (int j = 0; j < poly1Simplicies.Count; ++j) { if (!poly1Simplicies.Contains(poly2Simplicies[i]) && !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]); } } } } } }
/// <summary> /// Calculates the result between the subject and clip simplical chains, /// based on the provided operation. /// </summary> /// <remarks>Used by method <c>Execute()</c>.</remarks> private static void CalculateResultChain(List<Edge> poly1Simplicies, List<float> poly1Char, List<Edge> poly2Simplicies, List<float> poly2Char, PolyClipType clipType, out List<Edge> resultSimplices) { resultSimplices = new List<Edge>(); for (int i = 0; i < poly1Simplicies.Count; ++i) { if (clipType == PolyClipType.Intersect) { if (poly1Char[i] == 1f) { resultSimplices.Add(poly1Simplicies[i]); } } else { if (poly1Char[i] == 0f) { resultSimplices.Add(poly1Simplicies[i]); } } } for (int i = 0; i < poly2Simplicies.Count; ++i) { if (clipType == PolyClipType.Intersect || clipType == PolyClipType.Difference) { if (poly2Char[i] == 1f) { resultSimplices.Add(-poly2Simplicies[i]); } } else { if (poly2Char[i] == 0f) { resultSimplices.Add(poly2Simplicies[i]); } } } }
/// <summary> /// Calculates the characteristics function for all edges of /// the given simplical chains and builds the result chain. /// </summary> /// <remarks>Used by method <c>Execute()</c>.</remarks> private static void CalculateResultChain(List<float> poly1Coeff, List<Edge> poly1Simplicies, List<float> poly2Coeff, List<Edge> poly2Simplicies, PolyClipType clipType, out List<Edge> resultSimplices) { resultSimplices = new List<Edge>(); for (int i = 0; i < poly1Simplicies.Count; ++i) { float edgeCharacter = 0; if (poly2Simplicies.Contains(poly1Simplicies[i])) { edgeCharacter = 1f; } else if (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) { float edgeCharacter = 0f; if (!resultSimplices.Contains(poly2Simplicies[i]) && !resultSimplices.Contains(-poly2Simplicies[i])) { if (poly1Simplicies.Contains(-poly2Simplicies[i]) && clipType == PolyClipType.Union) { edgeCharacter = 1f; } else { edgeCharacter = 0f; for (int j = 0; j < poly1Simplicies.Count; ++j) { if (!poly1Simplicies.Contains(poly2Simplicies[i]) && !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]); } } } } } }
/// <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 characteristics function for all edges of the given simplical chains and builds the result /// chain. /// </summary> private static List <Edge> CalculateResultChain( IList <float> poly1Coefficient, IList <Edge> poly1Simplices, IList <float> poly2Coefficient, IList <Edge> poly2Simplices, PolyClipType clipType) { var resultSimplices = new List <Edge>(); foreach (var simplex in poly1Simplices) { var edgeCharacter = 0.0f; if (poly2Simplices.Contains(simplex) || (poly2Simplices.Contains(-simplex) && clipType == PolyClipType.Union)) { edgeCharacter = 1.0f; } else { for (var j = 0; j < poly2Simplices.Count; ++j) { if (!poly2Simplices.Contains(-simplex)) { edgeCharacter += CalculateBeta( simplex.GetCenter(), poly2Simplices[j], poly2Coefficient[j]); } } } switch (clipType) { case PolyClipType.Intersect: // ReSharper disable CompareOfFloatsByEqualityOperator if (edgeCharacter == 1.0f) // ReSharper restore CompareOfFloatsByEqualityOperator { resultSimplices.Add(simplex); } break; default: // ReSharper disable CompareOfFloatsByEqualityOperator if (edgeCharacter == 0.0f) // ReSharper restore CompareOfFloatsByEqualityOperator { resultSimplices.Add(simplex); } break; } } foreach (var simplex in poly2Simplices) { if (resultSimplices.Contains(simplex) || resultSimplices.Contains(-simplex)) { continue; } var edgeCharacter = 0.0f; if (poly1Simplices.Contains(simplex) || (poly1Simplices.Contains(-simplex) && clipType == PolyClipType.Union)) { edgeCharacter = 1.0f; } else { for (var j = 0; j < poly1Simplices.Count; ++j) { if (!poly1Simplices.Contains(-simplex)) { edgeCharacter += CalculateBeta( simplex.GetCenter(), poly1Simplices[j], poly1Coefficient[j]); } } } switch (clipType) { case PolyClipType.Difference: case PolyClipType.Intersect: // ReSharper disable CompareOfFloatsByEqualityOperator if (edgeCharacter == 1.0f) // ReSharper restore CompareOfFloatsByEqualityOperator { resultSimplices.Add(-simplex); } break; default: // ReSharper disable CompareOfFloatsByEqualityOperator if (edgeCharacter == 0.0f) // ReSharper restore CompareOfFloatsByEqualityOperator { resultSimplices.Add(simplex); } break; } } return(resultSimplices); }
/// <summary> /// Calculates the characteristics function for all edges of /// the given simplical chains and builds the result chain. /// </summary> /// <remarks>Used by method <c>Execute()</c>.</remarks> private static void CalculateResultChain(List<double> poly1Coeff, List<Edge> poly1Simplicies, List<double> poly2Coeff, List<Edge> poly2Simplicies, PolyClipType clipType, out List<Edge> resultSimplices) { resultSimplices = new List<Edge>(); for (int i = 0; i < poly1Simplicies.Count; ++i) { double edgeCharacter = 0; if (poly2Simplicies.Contains(poly1Simplicies[i]) || (poly2Simplicies.Contains(-poly1Simplicies[i]) && clipType == PolyClipType.Union)) { edgeCharacter = 1; } 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 (Math.Abs(edgeCharacter - 1) < ClipperEpsilonSquared) { resultSimplices.Add(poly1Simplicies[i]); } } else { if (Math.Abs(edgeCharacter - 0) < ClipperEpsilonSquared) { resultSimplices.Add(poly1Simplicies[i]); } } } for (int i = 0; i < poly2Simplicies.Count; ++i) { if (!resultSimplices.Contains(poly2Simplicies[i]) && !resultSimplices.Contains(-poly2Simplicies[i])) { double 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 (Math.Abs(edgeCharacter - 1) < ClipperEpsilonSquared) { resultSimplices.Add(-poly2Simplicies[i]); } } else { if (Math.Abs(edgeCharacter) < ClipperEpsilonSquared) { resultSimplices.Add(poly2Simplicies[i]); } } } } }
private static void CalculateResultChain(List <FP> poly1Coeff, List <YuPengClipper.Edge> poly1Simplicies, List <FP> poly2Coeff, List <YuPengClipper.Edge> poly2Simplicies, PolyClipType clipType, out List <YuPengClipper.Edge> resultSimplices) { resultSimplices = new List <YuPengClipper.Edge>(); for (int i = 0; i < poly1Simplicies.Count; i++) { FP x = 0; bool flag = poly2Simplicies.Contains(poly1Simplicies[i]); if (flag) { x = 1f; } else { bool flag2 = poly2Simplicies.Contains(-poly1Simplicies[i]) && clipType == PolyClipType.Union; if (flag2) { x = 1f; } else { for (int j = 0; j < poly2Simplicies.Count; j++) { bool flag3 = !poly2Simplicies.Contains(-poly1Simplicies[i]); if (flag3) { x += YuPengClipper.CalculateBeta(poly1Simplicies[i].GetCenter(), poly2Simplicies[j], poly2Coeff[j]); } } } } bool flag4 = clipType == PolyClipType.Intersect; if (flag4) { bool flag5 = x == 1f; if (flag5) { resultSimplices.Add(poly1Simplicies[i]); } } else { bool flag6 = x == 0f; if (flag6) { resultSimplices.Add(poly1Simplicies[i]); } } } for (int k = 0; k < poly2Simplicies.Count; k++) { FP x2 = 0f; bool flag7 = !resultSimplices.Contains(poly2Simplicies[k]) && !resultSimplices.Contains(-poly2Simplicies[k]); if (flag7) { bool flag8 = poly1Simplicies.Contains(-poly2Simplicies[k]) && clipType == PolyClipType.Union; if (flag8) { x2 = 1f; } else { x2 = 0f; for (int l = 0; l < poly1Simplicies.Count; l++) { bool flag9 = !poly1Simplicies.Contains(poly2Simplicies[k]) && !poly1Simplicies.Contains(-poly2Simplicies[k]); if (flag9) { x2 += YuPengClipper.CalculateBeta(poly2Simplicies[k].GetCenter(), poly1Simplicies[l], poly1Coeff[l]); } } bool flag10 = clipType == PolyClipType.Intersect || clipType == PolyClipType.Difference; if (flag10) { bool flag11 = x2 == 1f; if (flag11) { resultSimplices.Add(-poly2Simplicies[k]); } } else { bool flag12 = x2 == 0f; if (flag12) { resultSimplices.Add(poly2Simplicies[k]); } } } } } }