BayazitDecomposer.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. using System.Collections.Generic;
  2. using FarseerPhysics.Common.PolygonManipulation;
  3. using Microsoft.Xna.Framework;
  4. namespace FarseerPhysics.Common.Decomposition
  5. {
  6. //From phed rev 36
  7. /// <summary>
  8. /// Convex decomposition algorithm created by Mark Bayazit (http://mnbayazit.com/)
  9. /// For more information about this algorithm, see http://mnbayazit.com/406/bayazit
  10. /// </summary>
  11. public static class BayazitDecomposer
  12. {
  13. private static Vector2 At(int i, Vertices vertices)
  14. {
  15. int s = vertices.Count;
  16. return vertices[i < 0 ? s - (-i % s) : i % s];
  17. }
  18. private static Vertices Copy(int i, int j, Vertices vertices)
  19. {
  20. Vertices p = new Vertices();
  21. while (j < i) j += vertices.Count;
  22. //p.reserve(j - i + 1);
  23. for (; i <= j; ++i)
  24. {
  25. p.Add(At(i, vertices));
  26. }
  27. return p;
  28. }
  29. /// <summary>
  30. /// Decompose the polygon into several smaller non-concave polygon.
  31. /// If the polygon is already convex, it will return the original polygon, unless it is over Settings.MaxPolygonVertices.
  32. /// Precondition: Counter Clockwise polygon
  33. /// </summary>
  34. /// <param name="vertices"></param>
  35. /// <returns></returns>
  36. public static List<Vertices> ConvexPartition(Vertices vertices)
  37. {
  38. //We force it to CCW as it is a precondition in this algorithm.
  39. vertices.ForceCounterClockWise();
  40. List<Vertices> list = new List<Vertices>();
  41. float d, lowerDist, upperDist;
  42. Vector2 p;
  43. Vector2 lowerInt = new Vector2();
  44. Vector2 upperInt = new Vector2(); // intersection points
  45. int lowerIndex = 0, upperIndex = 0;
  46. Vertices lowerPoly, upperPoly;
  47. for (int i = 0; i < vertices.Count; ++i)
  48. {
  49. if (Reflex(i, vertices))
  50. {
  51. lowerDist = upperDist = float.MaxValue; // std::numeric_limits<qreal>::max();
  52. for (int j = 0; j < vertices.Count; ++j)
  53. {
  54. // if line intersects with an edge
  55. if (Left(At(i - 1, vertices), At(i, vertices), At(j, vertices)) &&
  56. RightOn(At(i - 1, vertices), At(i, vertices), At(j - 1, vertices)))
  57. {
  58. // find the point of intersection
  59. p = LineTools.LineIntersect(At(i - 1, vertices), At(i, vertices), At(j, vertices),
  60. At(j - 1, vertices));
  61. if (Right(At(i + 1, vertices), At(i, vertices), p))
  62. {
  63. // make sure it's inside the poly
  64. d = SquareDist(At(i, vertices), p);
  65. if (d < lowerDist)
  66. {
  67. // keep only the closest intersection
  68. lowerDist = d;
  69. lowerInt = p;
  70. lowerIndex = j;
  71. }
  72. }
  73. }
  74. if (Left(At(i + 1, vertices), At(i, vertices), At(j + 1, vertices)) &&
  75. RightOn(At(i + 1, vertices), At(i, vertices), At(j, vertices)))
  76. {
  77. p = LineTools.LineIntersect(At(i + 1, vertices), At(i, vertices), At(j, vertices),
  78. At(j + 1, vertices));
  79. if (Left(At(i - 1, vertices), At(i, vertices), p))
  80. {
  81. d = SquareDist(At(i, vertices), p);
  82. if (d < upperDist)
  83. {
  84. upperDist = d;
  85. upperIndex = j;
  86. upperInt = p;
  87. }
  88. }
  89. }
  90. }
  91. // if there are no vertices to connect to, choose a point in the middle
  92. if (lowerIndex == (upperIndex + 1) % vertices.Count)
  93. {
  94. Vector2 sp = ((lowerInt + upperInt) / 2);
  95. lowerPoly = Copy(i, upperIndex, vertices);
  96. lowerPoly.Add(sp);
  97. upperPoly = Copy(lowerIndex, i, vertices);
  98. upperPoly.Add(sp);
  99. }
  100. else
  101. {
  102. double highestScore = 0, bestIndex = lowerIndex;
  103. while (upperIndex < lowerIndex) upperIndex += vertices.Count;
  104. for (int j = lowerIndex; j <= upperIndex; ++j)
  105. {
  106. if (CanSee(i, j, vertices))
  107. {
  108. double score = 1 / (SquareDist(At(i, vertices), At(j, vertices)) + 1);
  109. if (Reflex(j, vertices))
  110. {
  111. if (RightOn(At(j - 1, vertices), At(j, vertices), At(i, vertices)) &&
  112. LeftOn(At(j + 1, vertices), At(j, vertices), At(i, vertices)))
  113. {
  114. score += 3;
  115. }
  116. else
  117. {
  118. score += 2;
  119. }
  120. }
  121. else
  122. {
  123. score += 1;
  124. }
  125. if (score > highestScore)
  126. {
  127. bestIndex = j;
  128. highestScore = score;
  129. }
  130. }
  131. }
  132. lowerPoly = Copy(i, (int)bestIndex, vertices);
  133. upperPoly = Copy((int)bestIndex, i, vertices);
  134. }
  135. list.AddRange(ConvexPartition(lowerPoly));
  136. list.AddRange(ConvexPartition(upperPoly));
  137. return list;
  138. }
  139. }
  140. // polygon is already convex
  141. if (vertices.Count > Settings.MaxPolygonVertices)
  142. {
  143. lowerPoly = Copy(0, vertices.Count / 2, vertices);
  144. upperPoly = Copy(vertices.Count / 2, 0, vertices);
  145. list.AddRange(ConvexPartition(lowerPoly));
  146. list.AddRange(ConvexPartition(upperPoly));
  147. }
  148. else
  149. list.Add(vertices);
  150. //The polygons are not guaranteed to be without collinear points. We remove
  151. //them to be sure.
  152. for (int i = 0; i < list.Count; i++)
  153. {
  154. list[i] = SimplifyTools.CollinearSimplify(list[i], 0);
  155. }
  156. //Remove empty vertice collections
  157. for (int i = list.Count - 1; i >= 0; i--)
  158. {
  159. if (list[i].Count == 0)
  160. list.RemoveAt(i);
  161. }
  162. return list;
  163. }
  164. private static bool CanSee(int i, int j, Vertices vertices)
  165. {
  166. if (Reflex(i, vertices))
  167. {
  168. if (LeftOn(At(i, vertices), At(i - 1, vertices), At(j, vertices)) &&
  169. RightOn(At(i, vertices), At(i + 1, vertices), At(j, vertices))) return false;
  170. }
  171. else
  172. {
  173. if (RightOn(At(i, vertices), At(i + 1, vertices), At(j, vertices)) ||
  174. LeftOn(At(i, vertices), At(i - 1, vertices), At(j, vertices))) return false;
  175. }
  176. if (Reflex(j, vertices))
  177. {
  178. if (LeftOn(At(j, vertices), At(j - 1, vertices), At(i, vertices)) &&
  179. RightOn(At(j, vertices), At(j + 1, vertices), At(i, vertices))) return false;
  180. }
  181. else
  182. {
  183. if (RightOn(At(j, vertices), At(j + 1, vertices), At(i, vertices)) ||
  184. LeftOn(At(j, vertices), At(j - 1, vertices), At(i, vertices))) return false;
  185. }
  186. for (int k = 0; k < vertices.Count; ++k)
  187. {
  188. if ((k + 1) % vertices.Count == i || k == i || (k + 1) % vertices.Count == j || k == j)
  189. {
  190. continue; // ignore incident edges
  191. }
  192. Vector2 intersectionPoint;
  193. if (LineTools.LineIntersect(At(i, vertices), At(j, vertices), At(k, vertices), At(k + 1, vertices), out intersectionPoint))
  194. {
  195. return false;
  196. }
  197. }
  198. return true;
  199. }
  200. // precondition: ccw
  201. private static bool Reflex(int i, Vertices vertices)
  202. {
  203. return Right(i, vertices);
  204. }
  205. private static bool Right(int i, Vertices vertices)
  206. {
  207. return Right(At(i - 1, vertices), At(i, vertices), At(i + 1, vertices));
  208. }
  209. private static bool Left(Vector2 a, Vector2 b, Vector2 c)
  210. {
  211. return MathUtils.Area(ref a, ref b, ref c) > 0;
  212. }
  213. private static bool LeftOn(Vector2 a, Vector2 b, Vector2 c)
  214. {
  215. return MathUtils.Area(ref a, ref b, ref c) >= 0;
  216. }
  217. private static bool Right(Vector2 a, Vector2 b, Vector2 c)
  218. {
  219. return MathUtils.Area(ref a, ref b, ref c) < 0;
  220. }
  221. private static bool RightOn(Vector2 a, Vector2 b, Vector2 c)
  222. {
  223. return MathUtils.Area(ref a, ref b, ref c) <= 0;
  224. }
  225. private static float SquareDist(Vector2 a, Vector2 b)
  226. {
  227. float dx = b.X - a.X;
  228. float dy = b.Y - a.Y;
  229. return dx * dx + dy * dy;
  230. }
  231. }
  232. }