YuPengClipper.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using FarseerPhysics.Collision.Shapes;
  5. using Microsoft.Xna.Framework;
  6. namespace FarseerPhysics.Common.PolygonManipulation
  7. {
  8. internal enum PolyClipType
  9. {
  10. Intersect,
  11. Union,
  12. Difference
  13. }
  14. public enum PolyClipError
  15. {
  16. None,
  17. DegeneratedOutput,
  18. NonSimpleInput,
  19. BrokenResult
  20. }
  21. //Clipper contributed by Helge Backhaus
  22. public static class YuPengClipper
  23. {
  24. private const float ClipperEpsilonSquared = 1.192092896e-07f;
  25. public static List<Vertices> Union(Vertices polygon1, Vertices polygon2, out PolyClipError error)
  26. {
  27. return Execute(polygon1, polygon2, PolyClipType.Union, out error);
  28. }
  29. public static List<Vertices> Difference(Vertices polygon1, Vertices polygon2, out PolyClipError error)
  30. {
  31. return Execute(polygon1, polygon2, PolyClipType.Difference, out error);
  32. }
  33. public static List<Vertices> Intersect(Vertices polygon1, Vertices polygon2, out PolyClipError error)
  34. {
  35. return Execute(polygon1, polygon2, PolyClipType.Intersect, out error);
  36. }
  37. /// <summary>
  38. /// Implements "A new algorithm for Boolean operations on general polygons"
  39. /// available here: http://liama.ia.ac.cn/wiki/_media/user:dong:dong_cg_05.pdf
  40. /// Merges two polygons, a subject and a clip with the specified operation. Polygons may not be
  41. /// self-intersecting.
  42. ///
  43. /// Warning: May yield incorrect results or even crash if polygons contain collinear points.
  44. /// </summary>
  45. /// <param name="subject">The subject polygon.</param>
  46. /// <param name="clip">The clip polygon, which is added,
  47. /// substracted or intersected with the subject</param>
  48. /// <param name="clipType">The operation to be performed. Either
  49. /// Union, Difference or Intersection.</param>
  50. /// <param name="error">The error generated (if any)</param>
  51. /// <returns>A list of closed polygons, which make up the result of the clipping operation.
  52. /// Outer contours are ordered counter clockwise, holes are ordered clockwise.</returns>
  53. private static List<Vertices> Execute(Vertices subject, Vertices clip,
  54. PolyClipType clipType, out PolyClipError error)
  55. {
  56. Debug.Assert(subject.IsSimple() && clip.IsSimple(), "Non simple input!", "Input polygons must be simple (cannot intersect themselves).");
  57. // Copy polygons
  58. Vertices slicedSubject;
  59. Vertices slicedClip;
  60. // Calculate the intersection and touch points between
  61. // subject and clip and add them to both
  62. CalculateIntersections(subject, clip, out slicedSubject, out slicedClip);
  63. // Translate polygons into upper right quadrant
  64. // as the algorithm depends on it
  65. Vector2 lbSubject = subject.GetCollisionBox().LowerBound;
  66. Vector2 lbClip = clip.GetCollisionBox().LowerBound;
  67. Vector2 translate;
  68. Vector2.Min(ref lbSubject, ref lbClip, out translate);
  69. translate = Vector2.One - translate;
  70. if (translate != Vector2.Zero)
  71. {
  72. slicedSubject.Translate(ref translate);
  73. slicedClip.Translate(ref translate);
  74. }
  75. // Enforce counterclockwise contours
  76. slicedSubject.ForceCounterClockWise();
  77. slicedClip.ForceCounterClockWise();
  78. List<Edge> subjectSimplices;
  79. List<float> subjectCoeff;
  80. List<Edge> clipSimplices;
  81. List<float> clipCoeff;
  82. // Build simplical chains from the polygons and calculate the
  83. // the corresponding coefficients
  84. CalculateSimplicalChain(slicedSubject, out subjectCoeff, out subjectSimplices);
  85. CalculateSimplicalChain(slicedClip, out clipCoeff, out clipSimplices);
  86. List<Edge> resultSimplices;
  87. // Determine the characteristics function for all non-original edges
  88. // in subject and clip simplical chain and combine the edges contributing
  89. // to the result, depending on the clipType
  90. CalculateResultChain(subjectCoeff, subjectSimplices, clipCoeff, clipSimplices, clipType,
  91. out resultSimplices);
  92. List<Vertices> result;
  93. // Convert result chain back to polygon(s)
  94. error = BuildPolygonsFromChain(resultSimplices, out result);
  95. // Reverse the polygon translation from the beginning
  96. // and remove collinear points from output
  97. translate *= -1f;
  98. for (int i = 0; i < result.Count; ++i)
  99. {
  100. result[i].Translate(ref translate);
  101. SimplifyTools.CollinearSimplify(result[i]);
  102. }
  103. return result;
  104. }
  105. /// <summary>
  106. /// Calculates all intersections between two polygons.
  107. /// </summary>
  108. /// <param name="polygon1">The first polygon.</param>
  109. /// <param name="polygon2">The second polygon.</param>
  110. /// <param name="slicedPoly1">Returns the first polygon with added intersection points.</param>
  111. /// <param name="slicedPoly2">Returns the second polygon with added intersection points.</param>
  112. private static void CalculateIntersections(Vertices polygon1, Vertices polygon2,
  113. out Vertices slicedPoly1, out Vertices slicedPoly2)
  114. {
  115. slicedPoly1 = new Vertices(polygon1);
  116. slicedPoly2 = new Vertices(polygon2);
  117. // Iterate through polygon1's edges
  118. for (int i = 0; i < polygon1.Count; i++)
  119. {
  120. // Get edge vertices
  121. Vector2 a = polygon1[i];
  122. Vector2 b = polygon1[polygon1.NextIndex(i)];
  123. // Get intersections between this edge and polygon2
  124. for (int j = 0; j < polygon2.Count; j++)
  125. {
  126. Vector2 c = polygon2[j];
  127. Vector2 d = polygon2[polygon2.NextIndex(j)];
  128. Vector2 intersectionPoint;
  129. // Check if the edges intersect
  130. if (LineTools.LineIntersect(a, b, c, d, out intersectionPoint))
  131. {
  132. // calculate alpha values for sorting multiple intersections points on a edge
  133. float alpha;
  134. // Insert intersection point into first polygon
  135. alpha = GetAlpha(a, b, intersectionPoint);
  136. if (alpha > 0f && alpha < 1f)
  137. {
  138. int index = slicedPoly1.IndexOf(a) + 1;
  139. while (index < slicedPoly1.Count &&
  140. GetAlpha(a, b, slicedPoly1[index]) <= alpha)
  141. {
  142. ++index;
  143. }
  144. slicedPoly1.Insert(index, intersectionPoint);
  145. }
  146. // Insert intersection point into second polygon
  147. alpha = GetAlpha(c, d, intersectionPoint);
  148. if (alpha > 0f && alpha < 1f)
  149. {
  150. int index = slicedPoly2.IndexOf(c) + 1;
  151. while (index < slicedPoly2.Count &&
  152. GetAlpha(c, d, slicedPoly2[index]) <= alpha)
  153. {
  154. ++index;
  155. }
  156. slicedPoly2.Insert(index, intersectionPoint);
  157. }
  158. }
  159. }
  160. }
  161. // Check for very small edges
  162. for (int i = 0; i < slicedPoly1.Count; ++i)
  163. {
  164. int iNext = slicedPoly1.NextIndex(i);
  165. //If they are closer than the distance remove vertex
  166. if ((slicedPoly1[iNext] - slicedPoly1[i]).LengthSquared() <= ClipperEpsilonSquared)
  167. {
  168. slicedPoly1.RemoveAt(i);
  169. --i;
  170. }
  171. }
  172. for (int i = 0; i < slicedPoly2.Count; ++i)
  173. {
  174. int iNext = slicedPoly2.NextIndex(i);
  175. //If they are closer than the distance remove vertex
  176. if ((slicedPoly2[iNext] - slicedPoly2[i]).LengthSquared() <= ClipperEpsilonSquared)
  177. {
  178. slicedPoly2.RemoveAt(i);
  179. --i;
  180. }
  181. }
  182. }
  183. /// <summary>
  184. /// Calculates the simplical chain corresponding to the input polygon.
  185. /// </summary>
  186. /// <remarks>Used by method <c>Execute()</c>.</remarks>
  187. private static void CalculateSimplicalChain(Vertices poly, out List<float> coeff,
  188. out List<Edge> simplicies)
  189. {
  190. simplicies = new List<Edge>();
  191. coeff = new List<float>();
  192. for (int i = 0; i < poly.Count; ++i)
  193. {
  194. simplicies.Add(new Edge(poly[i], poly[poly.NextIndex(i)]));
  195. coeff.Add(CalculateSimplexCoefficient(Vector2.Zero, poly[i], poly[poly.NextIndex(i)]));
  196. }
  197. }
  198. /// <summary>
  199. /// Calculates the characteristics function for all edges of
  200. /// the given simplical chains and builds the result chain.
  201. /// </summary>
  202. /// <remarks>Used by method <c>Execute()</c>.</remarks>
  203. private static void CalculateResultChain(List<float> poly1Coeff, List<Edge> poly1Simplicies,
  204. List<float> poly2Coeff, List<Edge> poly2Simplicies,
  205. PolyClipType clipType, out List<Edge> resultSimplices)
  206. {
  207. resultSimplices = new List<Edge>();
  208. for (int i = 0; i < poly1Simplicies.Count; ++i)
  209. {
  210. float edgeCharacter = 0f;
  211. if (poly2Simplicies.Contains(poly1Simplicies[i]) ||
  212. (poly2Simplicies.Contains(-poly1Simplicies[i]) && clipType == PolyClipType.Union))
  213. {
  214. edgeCharacter = 1f;
  215. }
  216. else
  217. {
  218. for (int j = 0; j < poly2Simplicies.Count; ++j)
  219. {
  220. if (!poly2Simplicies.Contains(-poly1Simplicies[i]))
  221. {
  222. edgeCharacter += CalculateBeta(poly1Simplicies[i].GetCenter(),
  223. poly2Simplicies[j], poly2Coeff[j]);
  224. }
  225. }
  226. }
  227. if (clipType == PolyClipType.Intersect)
  228. {
  229. if (edgeCharacter == 1f)
  230. {
  231. resultSimplices.Add(poly1Simplicies[i]);
  232. }
  233. }
  234. else
  235. {
  236. if (edgeCharacter == 0f)
  237. {
  238. resultSimplices.Add(poly1Simplicies[i]);
  239. }
  240. }
  241. }
  242. for (int i = 0; i < poly2Simplicies.Count; ++i)
  243. {
  244. if (!resultSimplices.Contains(poly2Simplicies[i]) &&
  245. !resultSimplices.Contains(-poly2Simplicies[i]))
  246. {
  247. float edgeCharacter = 0f;
  248. if (poly1Simplicies.Contains(poly2Simplicies[i]) ||
  249. (poly1Simplicies.Contains(-poly2Simplicies[i]) && clipType == PolyClipType.Union))
  250. {
  251. edgeCharacter = 1f;
  252. }
  253. else
  254. {
  255. for (int j = 0; j < poly1Simplicies.Count; ++j)
  256. {
  257. if (!poly1Simplicies.Contains(-poly2Simplicies[i]))
  258. {
  259. edgeCharacter += CalculateBeta(poly2Simplicies[i].GetCenter(),
  260. poly1Simplicies[j], poly1Coeff[j]);
  261. }
  262. }
  263. }
  264. if (clipType == PolyClipType.Intersect || clipType == PolyClipType.Difference)
  265. {
  266. if (edgeCharacter == 1f)
  267. {
  268. resultSimplices.Add(-poly2Simplicies[i]);
  269. }
  270. }
  271. else
  272. {
  273. if (edgeCharacter == 0f)
  274. {
  275. resultSimplices.Add(poly2Simplicies[i]);
  276. }
  277. }
  278. }
  279. }
  280. }
  281. /// <summary>
  282. /// Calculates the polygon(s) from the result simplical chain.
  283. /// </summary>
  284. /// <remarks>Used by method <c>Execute()</c>.</remarks>
  285. private static PolyClipError BuildPolygonsFromChain(List<Edge> simplicies, out List<Vertices> result)
  286. {
  287. result = new List<Vertices>();
  288. PolyClipError errVal = PolyClipError.None;
  289. while (simplicies.Count > 0)
  290. {
  291. Vertices output = new Vertices();
  292. output.Add(simplicies[0].EdgeStart);
  293. output.Add(simplicies[0].EdgeEnd);
  294. simplicies.RemoveAt(0);
  295. bool closed = false;
  296. int index = 0;
  297. int count = simplicies.Count; // Needed to catch infinite loops
  298. while (!closed && simplicies.Count > 0)
  299. {
  300. if (VectorEqual(output[output.Count - 1], simplicies[index].EdgeStart))
  301. {
  302. if (VectorEqual(simplicies[index].EdgeEnd, output[0]))
  303. {
  304. closed = true;
  305. }
  306. else
  307. {
  308. output.Add(simplicies[index].EdgeEnd);
  309. }
  310. simplicies.RemoveAt(index);
  311. --index;
  312. }
  313. else if (VectorEqual(output[output.Count - 1], simplicies[index].EdgeEnd))
  314. {
  315. if (VectorEqual(simplicies[index].EdgeStart, output[0]))
  316. {
  317. closed = true;
  318. }
  319. else
  320. {
  321. output.Add(simplicies[index].EdgeStart);
  322. }
  323. simplicies.RemoveAt(index);
  324. --index;
  325. }
  326. if (!closed)
  327. {
  328. if (++index == simplicies.Count)
  329. {
  330. if (count == simplicies.Count)
  331. {
  332. result = new List<Vertices>();
  333. Debug.WriteLine("Undefined error while building result polygon(s).");
  334. return PolyClipError.BrokenResult;
  335. }
  336. index = 0;
  337. count = simplicies.Count;
  338. }
  339. }
  340. }
  341. if (output.Count < 3)
  342. {
  343. errVal = PolyClipError.DegeneratedOutput;
  344. Debug.WriteLine("Degenerated output polygon produced (vertices < 3).");
  345. }
  346. result.Add(output);
  347. }
  348. return errVal;
  349. }
  350. /// <summary>
  351. /// Needed to calculate the characteristics function of a simplex.
  352. /// </summary>
  353. /// <remarks>Used by method <c>CalculateEdgeCharacter()</c>.</remarks>
  354. private static float CalculateBeta(Vector2 point, Edge e, float coefficient)
  355. {
  356. float result = 0f;
  357. if (PointInSimplex(point, e))
  358. {
  359. result = coefficient;
  360. }
  361. if (PointOnLineSegment(Vector2.Zero, e.EdgeStart, point) ||
  362. PointOnLineSegment(Vector2.Zero, e.EdgeEnd, point))
  363. {
  364. result = .5f * coefficient;
  365. }
  366. return result;
  367. }
  368. /// <summary>
  369. /// Needed for sorting multiple intersections points on the same edge.
  370. /// </summary>
  371. /// <remarks>Used by method <c>CalculateIntersections()</c>.</remarks>
  372. private static float GetAlpha(Vector2 start, Vector2 end, Vector2 point)
  373. {
  374. return (point - start).LengthSquared() / (end - start).LengthSquared();
  375. }
  376. /// <summary>
  377. /// Returns the coefficient of a simplex.
  378. /// </summary>
  379. /// <remarks>Used by method <c>CalculateSimplicalChain()</c>.</remarks>
  380. private static float CalculateSimplexCoefficient(Vector2 a, Vector2 b, Vector2 c)
  381. {
  382. float isLeft = MathUtils.Area(ref a, ref b, ref c);
  383. if (isLeft < 0f)
  384. {
  385. return -1f;
  386. }
  387. if (isLeft > 0f)
  388. {
  389. return 1f;
  390. }
  391. return 0f;
  392. }
  393. /// <summary>
  394. /// Winding number test for a point in a simplex.
  395. /// </summary>
  396. /// <param name="point">The point to be tested.</param>
  397. /// <param name="edge">The edge that the point is tested against.</param>
  398. /// <returns>False if the winding number is even and the point is outside
  399. /// the simplex and True otherwise.</returns>
  400. private static bool PointInSimplex(Vector2 point, Edge edge)
  401. {
  402. Vertices polygon = new Vertices();
  403. polygon.Add(Vector2.Zero);
  404. polygon.Add(edge.EdgeStart);
  405. polygon.Add(edge.EdgeEnd);
  406. return (polygon.PointInPolygon(ref point) == 1);
  407. }
  408. /// <summary>
  409. /// Tests if a point lies on a line segment.
  410. /// </summary>
  411. /// <remarks>Used by method <c>CalculateBeta()</c>.</remarks>
  412. private static bool PointOnLineSegment(Vector2 start, Vector2 end, Vector2 point)
  413. {
  414. Vector2 segment = end - start;
  415. return MathUtils.Area(ref start, ref end, ref point) == 0f &&
  416. Vector2.Dot(point - start, segment) >= 0f &&
  417. Vector2.Dot(point - end, segment) <= 0f;
  418. }
  419. private static bool VectorEqual(Vector2 vec1, Vector2 vec2)
  420. {
  421. return (vec2 - vec1).LengthSquared() <= ClipperEpsilonSquared;
  422. }
  423. /// <summary>Specifies an Edge. Edges are used to represent simplicies in simplical chains</summary>
  424. private sealed class Edge
  425. {
  426. public Edge(Vector2 edgeStart, Vector2 edgeEnd)
  427. {
  428. EdgeStart = edgeStart;
  429. EdgeEnd = edgeEnd;
  430. }
  431. public Vector2 EdgeStart { get; private set; }
  432. public Vector2 EdgeEnd { get; private set; }
  433. public Vector2 GetCenter()
  434. {
  435. return (EdgeStart + EdgeEnd) / 2f;
  436. }
  437. public static Edge operator -(Edge e)
  438. {
  439. return new Edge(e.EdgeEnd, e.EdgeStart);
  440. }
  441. public override bool Equals(Object obj)
  442. {
  443. // If parameter is null return false.
  444. if (obj == null)
  445. {
  446. return false;
  447. }
  448. // If parameter cannot be cast to Point return false.
  449. return Equals(obj as Edge);
  450. }
  451. public bool Equals(Edge e)
  452. {
  453. // If parameter is null return false:
  454. if (e == null)
  455. {
  456. return false;
  457. }
  458. // Return true if the fields match
  459. return VectorEqual(EdgeStart, e.EdgeStart) && VectorEqual(EdgeEnd, e.EdgeEnd);
  460. }
  461. public override int GetHashCode()
  462. {
  463. return EdgeStart.GetHashCode() ^ EdgeEnd.GetHashCode();
  464. }
  465. }
  466. }
  467. }