PolygonTools.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using Microsoft.Xna.Framework;
  5. namespace FarseerPhysics.Common
  6. {
  7. public static class PolygonTools
  8. {
  9. /// <summary>
  10. /// Build vertices to represent an axis-aligned box.
  11. /// </summary>
  12. /// <param name="hx">the half-width.</param>
  13. /// <param name="hy">the half-height.</param>
  14. public static Vertices CreateRectangle(float hx, float hy)
  15. {
  16. Vertices vertices = new Vertices(4);
  17. vertices.Add(new Vector2(-hx, -hy));
  18. vertices.Add(new Vector2(hx, -hy));
  19. vertices.Add(new Vector2(hx, hy));
  20. vertices.Add(new Vector2(-hx, hy));
  21. return vertices;
  22. }
  23. /// <summary>
  24. /// Build vertices to represent an oriented box.
  25. /// </summary>
  26. /// <param name="hx">the half-width.</param>
  27. /// <param name="hy">the half-height.</param>
  28. /// <param name="center">the center of the box in local coordinates.</param>
  29. /// <param name="angle">the rotation of the box in local coordinates.</param>
  30. public static Vertices CreateRectangle(float hx, float hy, Vector2 center, float angle)
  31. {
  32. Vertices vertices = CreateRectangle(hx, hy);
  33. Transform xf = new Transform();
  34. xf.Position = center;
  35. xf.R.Set(angle);
  36. // Transform vertices
  37. for (int i = 0; i < 4; ++i)
  38. {
  39. vertices[i] = MathUtils.Multiply(ref xf, vertices[i]);
  40. }
  41. return vertices;
  42. }
  43. //Rounded rectangle contributed by Jonathan Smars - [email protected]
  44. /// <summary>
  45. /// Creates a rounded rectangle with the specified width and height.
  46. /// </summary>
  47. /// <param name="width">The width.</param>
  48. /// <param name="height">The height.</param>
  49. /// <param name="xRadius">The rounding X radius.</param>
  50. /// <param name="yRadius">The rounding Y radius.</param>
  51. /// <param name="segments">The number of segments to subdivide the edges.</param>
  52. /// <returns></returns>
  53. public static Vertices CreateRoundedRectangle(float width, float height, float xRadius, float yRadius,
  54. int segments)
  55. {
  56. if (yRadius > height / 2 || xRadius > width / 2)
  57. throw new Exception("Rounding amount can't be more than half the height and width respectively.");
  58. if (segments < 0)
  59. throw new Exception("Segments must be zero or more.");
  60. //We need at least 8 vertices to create a rounded rectangle
  61. Debug.Assert(Settings.MaxPolygonVertices >= 8);
  62. Vertices vertices = new Vertices();
  63. if (segments == 0)
  64. {
  65. vertices.Add(new Vector2(width * .5f - xRadius, -height * .5f));
  66. vertices.Add(new Vector2(width * .5f, -height * .5f + yRadius));
  67. vertices.Add(new Vector2(width * .5f, height * .5f - yRadius));
  68. vertices.Add(new Vector2(width * .5f - xRadius, height * .5f));
  69. vertices.Add(new Vector2(-width * .5f + xRadius, height * .5f));
  70. vertices.Add(new Vector2(-width * .5f, height * .5f - yRadius));
  71. vertices.Add(new Vector2(-width * .5f, -height * .5f + yRadius));
  72. vertices.Add(new Vector2(-width * .5f + xRadius, -height * .5f));
  73. }
  74. else
  75. {
  76. int numberOfEdges = (segments * 4 + 8);
  77. float stepSize = MathHelper.TwoPi / (numberOfEdges - 4);
  78. int perPhase = numberOfEdges / 4;
  79. Vector2 posOffset = new Vector2(width / 2 - xRadius, height / 2 - yRadius);
  80. vertices.Add(posOffset + new Vector2(xRadius, -yRadius + yRadius));
  81. short phase = 0;
  82. for (int i = 1; i < numberOfEdges; i++)
  83. {
  84. if (i - perPhase == 0 || i - perPhase * 3 == 0)
  85. {
  86. posOffset.X *= -1;
  87. phase--;
  88. }
  89. else if (i - perPhase * 2 == 0)
  90. {
  91. posOffset.Y *= -1;
  92. phase--;
  93. }
  94. vertices.Add(posOffset + new Vector2(xRadius * (float)Math.Cos(stepSize * -(i + phase)),
  95. -yRadius * (float)Math.Sin(stepSize * -(i + phase))));
  96. }
  97. }
  98. return vertices;
  99. }
  100. /// <summary>
  101. /// Set this as a single edge.
  102. /// </summary>
  103. /// <param name="start">The first point.</param>
  104. /// <param name="end">The second point.</param>
  105. public static Vertices CreateLine(Vector2 start, Vector2 end)
  106. {
  107. Vertices vertices = new Vertices(2);
  108. vertices.Add(start);
  109. vertices.Add(end);
  110. return vertices;
  111. }
  112. /// <summary>
  113. /// Creates a circle with the specified radius and number of edges.
  114. /// </summary>
  115. /// <param name="radius">The radius.</param>
  116. /// <param name="numberOfEdges">The number of edges. The more edges, the more it resembles a circle</param>
  117. /// <returns></returns>
  118. public static Vertices CreateCircle(float radius, int numberOfEdges)
  119. {
  120. return CreateEllipse(radius, radius, numberOfEdges);
  121. }
  122. /// <summary>
  123. /// Creates a ellipse with the specified width, height and number of edges.
  124. /// </summary>
  125. /// <param name="xRadius">Width of the ellipse.</param>
  126. /// <param name="yRadius">Height of the ellipse.</param>
  127. /// <param name="numberOfEdges">The number of edges. The more edges, the more it resembles an ellipse</param>
  128. /// <returns></returns>
  129. public static Vertices CreateEllipse(float xRadius, float yRadius, int numberOfEdges)
  130. {
  131. Vertices vertices = new Vertices();
  132. float stepSize = MathHelper.TwoPi / numberOfEdges;
  133. vertices.Add(new Vector2(xRadius, 0));
  134. for (int i = numberOfEdges - 1; i > 0; --i)
  135. vertices.Add(new Vector2(xRadius * (float)Math.Cos(stepSize * i),
  136. -yRadius * (float)Math.Sin(stepSize * i)));
  137. return vertices;
  138. }
  139. public static Vertices CreateArc(float radians, int sides, float radius)
  140. {
  141. Debug.Assert(radians > 0, "The arc needs to be larger than 0");
  142. Debug.Assert(sides > 1, "The arc needs to have more than 1 sides");
  143. Debug.Assert(radius > 0, "The arc needs to have a radius larger than 0");
  144. Vertices vertices = new Vertices();
  145. float stepSize = radians / sides;
  146. for (int i = sides - 1; i > 0; i--)
  147. {
  148. vertices.Add(new Vector2(radius * (float)Math.Cos(stepSize * i),
  149. radius * (float)Math.Sin(stepSize * i)));
  150. }
  151. return vertices;
  152. }
  153. //Capsule contributed by Yobiv
  154. /// <summary>
  155. /// Creates an capsule with the specified height, radius and number of edges.
  156. /// A capsule has the same form as a pill capsule.
  157. /// </summary>
  158. /// <param name="height">Height (inner height + 2 * radius) of the capsule.</param>
  159. /// <param name="endRadius">Radius of the capsule ends.</param>
  160. /// <param name="edges">The number of edges of the capsule ends. The more edges, the more it resembles an capsule</param>
  161. /// <returns></returns>
  162. public static Vertices CreateCapsule(float height, float endRadius, int edges)
  163. {
  164. if (endRadius >= height / 2)
  165. throw new ArgumentException(
  166. "The radius must be lower than height / 2. Higher values of radius would create a circle, and not a half circle.",
  167. "endRadius");
  168. return CreateCapsule(height, endRadius, edges, endRadius, edges);
  169. }
  170. /// <summary>
  171. /// Creates an capsule with the specified height, radius and number of edges.
  172. /// A capsule has the same form as a pill capsule.
  173. /// </summary>
  174. /// <param name="height">Height (inner height + radii) of the capsule.</param>
  175. /// <param name="topRadius">Radius of the top.</param>
  176. /// <param name="topEdges">The number of edges of the top. The more edges, the more it resembles an capsule</param>
  177. /// <param name="bottomRadius">Radius of bottom.</param>
  178. /// <param name="bottomEdges">The number of edges of the bottom. The more edges, the more it resembles an capsule</param>
  179. /// <returns></returns>
  180. public static Vertices CreateCapsule(float height, float topRadius, int topEdges, float bottomRadius,
  181. int bottomEdges)
  182. {
  183. if (height <= 0)
  184. throw new ArgumentException("Height must be longer than 0", "height");
  185. if (topRadius <= 0)
  186. throw new ArgumentException("The top radius must be more than 0", "topRadius");
  187. if (topEdges <= 0)
  188. throw new ArgumentException("Top edges must be more than 0", "topEdges");
  189. if (bottomRadius <= 0)
  190. throw new ArgumentException("The bottom radius must be more than 0", "bottomRadius");
  191. if (bottomEdges <= 0)
  192. throw new ArgumentException("Bottom edges must be more than 0", "bottomEdges");
  193. if (topRadius >= height / 2)
  194. throw new ArgumentException(
  195. "The top radius must be lower than height / 2. Higher values of top radius would create a circle, and not a half circle.",
  196. "topRadius");
  197. if (bottomRadius >= height / 2)
  198. throw new ArgumentException(
  199. "The bottom radius must be lower than height / 2. Higher values of bottom radius would create a circle, and not a half circle.",
  200. "bottomRadius");
  201. Vertices vertices = new Vertices();
  202. float newHeight = (height - topRadius - bottomRadius) * 0.5f;
  203. // top
  204. vertices.Add(new Vector2(topRadius, newHeight));
  205. float stepSize = MathHelper.Pi / topEdges;
  206. for (int i = 1; i < topEdges; i++)
  207. {
  208. vertices.Add(new Vector2(topRadius * (float)Math.Cos(stepSize * i),
  209. topRadius * (float)Math.Sin(stepSize * i) + newHeight));
  210. }
  211. vertices.Add(new Vector2(-topRadius, newHeight));
  212. // bottom
  213. vertices.Add(new Vector2(-bottomRadius, -newHeight));
  214. stepSize = MathHelper.Pi / bottomEdges;
  215. for (int i = 1; i < bottomEdges; i++)
  216. {
  217. vertices.Add(new Vector2(-bottomRadius * (float)Math.Cos(stepSize * i),
  218. -bottomRadius * (float)Math.Sin(stepSize * i) - newHeight));
  219. }
  220. vertices.Add(new Vector2(bottomRadius, -newHeight));
  221. return vertices;
  222. }
  223. /// <summary>
  224. /// Creates a gear shape with the specified radius and number of teeth.
  225. /// </summary>
  226. /// <param name="radius">The radius.</param>
  227. /// <param name="numberOfTeeth">The number of teeth.</param>
  228. /// <param name="tipPercentage">The tip percentage.</param>
  229. /// <param name="toothHeight">Height of the tooth.</param>
  230. /// <returns></returns>
  231. public static Vertices CreateGear(float radius, int numberOfTeeth, float tipPercentage, float toothHeight)
  232. {
  233. Vertices vertices = new Vertices();
  234. float stepSize = MathHelper.TwoPi / numberOfTeeth;
  235. tipPercentage /= 100f;
  236. MathHelper.Clamp(tipPercentage, 0f, 1f);
  237. float toothTipStepSize = (stepSize / 2f) * tipPercentage;
  238. float toothAngleStepSize = (stepSize - (toothTipStepSize * 2f)) / 2f;
  239. for (int i = numberOfTeeth - 1; i >= 0; --i)
  240. {
  241. if (toothTipStepSize > 0f)
  242. {
  243. vertices.Add(
  244. new Vector2(radius *
  245. (float)Math.Cos(stepSize * i + toothAngleStepSize * 2f + toothTipStepSize),
  246. -radius *
  247. (float)Math.Sin(stepSize * i + toothAngleStepSize * 2f + toothTipStepSize)));
  248. vertices.Add(
  249. new Vector2((radius + toothHeight) *
  250. (float)Math.Cos(stepSize * i + toothAngleStepSize + toothTipStepSize),
  251. -(radius + toothHeight) *
  252. (float)Math.Sin(stepSize * i + toothAngleStepSize + toothTipStepSize)));
  253. }
  254. vertices.Add(new Vector2((radius + toothHeight) *
  255. (float)Math.Cos(stepSize * i + toothAngleStepSize),
  256. -(radius + toothHeight) *
  257. (float)Math.Sin(stepSize * i + toothAngleStepSize)));
  258. vertices.Add(new Vector2(radius * (float)Math.Cos(stepSize * i),
  259. -radius * (float)Math.Sin(stepSize * i)));
  260. }
  261. return vertices;
  262. }
  263. /// <summary>
  264. /// Detects the vertices by analyzing the texture data.
  265. /// </summary>
  266. /// <param name="data">The texture data.</param>
  267. /// <param name="width">The texture width.</param>
  268. /// <returns></returns>
  269. public static Vertices CreatePolygon(uint[] data, int width)
  270. {
  271. return TextureConverter.DetectVertices(data, width);
  272. }
  273. /// <summary>
  274. /// Detects the vertices by analyzing the texture data.
  275. /// </summary>
  276. /// <param name="data">The texture data.</param>
  277. /// <param name="width">The texture width.</param>
  278. /// <param name="holeDetection">if set to <c>true</c> it will perform hole detection.</param>
  279. /// <returns></returns>
  280. public static Vertices CreatePolygon(uint[] data, int width, bool holeDetection)
  281. {
  282. return TextureConverter.DetectVertices(data, width, holeDetection);
  283. }
  284. /// <summary>
  285. /// Detects the vertices by analyzing the texture data.
  286. /// </summary>
  287. /// <param name="data">The texture data.</param>
  288. /// <param name="width">The texture width.</param>
  289. /// <param name="hullTolerance">The hull tolerance.</param>
  290. /// <param name="alphaTolerance">The alpha tolerance.</param>
  291. /// <param name="multiPartDetection">if set to <c>true</c> it will perform multi part detection.</param>
  292. /// <param name="holeDetection">if set to <c>true</c> it will perform hole detection.</param>
  293. /// <returns></returns>
  294. public static List<Vertices> CreatePolygon(uint[] data, int width, float hullTolerance,
  295. byte alphaTolerance, bool multiPartDetection, bool holeDetection)
  296. {
  297. return TextureConverter.DetectVertices(data, width, hullTolerance, alphaTolerance,
  298. multiPartDetection, holeDetection);
  299. }
  300. }
  301. }