Vertices.cs 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Text;
  5. using FarseerPhysics.Collision;
  6. using Microsoft.Xna.Framework;
  7. namespace FarseerPhysics.Common
  8. {
  9. #if !(XBOX360)
  10. [DebuggerDisplay("Count = {Count} Vertices = {ToString()}")]
  11. #endif
  12. public class Vertices : List<Vector2>
  13. {
  14. public Vertices()
  15. {
  16. }
  17. public Vertices(int capacity)
  18. {
  19. Capacity = capacity;
  20. }
  21. public Vertices(Vector2[] vector2)
  22. {
  23. for (int i = 0; i < vector2.Length; i++)
  24. {
  25. Add(vector2[i]);
  26. }
  27. }
  28. public Vertices(IList<Vector2> vertices)
  29. {
  30. for (int i = 0; i < vertices.Count; i++)
  31. {
  32. Add(vertices[i]);
  33. }
  34. }
  35. /// <summary>
  36. /// Nexts the index.
  37. /// </summary>
  38. /// <param name="index">The index.</param>
  39. /// <returns></returns>
  40. public int NextIndex(int index)
  41. {
  42. if (index == Count - 1)
  43. {
  44. return 0;
  45. }
  46. return index + 1;
  47. }
  48. public Vector2 NextVertex(int index)
  49. {
  50. return this[NextIndex(index)];
  51. }
  52. /// <summary>
  53. /// Gets the previous index.
  54. /// </summary>
  55. /// <param name="index">The index.</param>
  56. /// <returns></returns>
  57. public int PreviousIndex(int index)
  58. {
  59. if (index == 0)
  60. {
  61. return Count - 1;
  62. }
  63. return index - 1;
  64. }
  65. public Vector2 PreviousVertex(int index)
  66. {
  67. return this[PreviousIndex(index)];
  68. }
  69. /// <summary>
  70. /// Gets the signed area.
  71. /// </summary>
  72. /// <returns></returns>
  73. public float GetSignedArea()
  74. {
  75. int i;
  76. float area = 0;
  77. for (i = 0; i < Count; i++)
  78. {
  79. int j = (i + 1) % Count;
  80. area += this[i].X * this[j].Y;
  81. area -= this[i].Y * this[j].X;
  82. }
  83. area /= 2.0f;
  84. return area;
  85. }
  86. /// <summary>
  87. /// Gets the area.
  88. /// </summary>
  89. /// <returns></returns>
  90. public float GetArea()
  91. {
  92. int i;
  93. float area = 0;
  94. for (i = 0; i < Count; i++)
  95. {
  96. int j = (i + 1) % Count;
  97. area += this[i].X * this[j].Y;
  98. area -= this[i].Y * this[j].X;
  99. }
  100. area /= 2.0f;
  101. return (area < 0 ? -area : area);
  102. }
  103. /// <summary>
  104. /// Gets the centroid.
  105. /// </summary>
  106. /// <returns></returns>
  107. public Vector2 GetCentroid()
  108. {
  109. // Same algorithm is used by Box2D
  110. Vector2 c = Vector2.Zero;
  111. float area = 0.0f;
  112. const float inv3 = 1.0f / 3.0f;
  113. Vector2 pRef = Vector2.Zero;
  114. for (int i = 0; i < Count; ++i)
  115. {
  116. // Triangle vertices.
  117. Vector2 p1 = pRef;
  118. Vector2 p2 = this[i];
  119. Vector2 p3 = i + 1 < Count ? this[i + 1] : this[0];
  120. Vector2 e1 = p2 - p1;
  121. Vector2 e2 = p3 - p1;
  122. float D = MathUtils.Cross(e1, e2);
  123. float triangleArea = 0.5f * D;
  124. area += triangleArea;
  125. // Area weighted centroid
  126. c += triangleArea * inv3 * (p1 + p2 + p3);
  127. }
  128. // Centroid
  129. c *= 1.0f / area;
  130. return c;
  131. }
  132. /// <summary>
  133. /// Gets the radius based on area.
  134. /// </summary>
  135. /// <returns></returns>
  136. public float GetRadius()
  137. {
  138. float area = GetSignedArea();
  139. double radiusSqrd = (double)area / MathHelper.Pi;
  140. if (radiusSqrd < 0)
  141. {
  142. radiusSqrd *= -1;
  143. }
  144. return (float)Math.Sqrt(radiusSqrd);
  145. }
  146. /// <summary>
  147. /// Returns an AABB for vertex.
  148. /// </summary>
  149. /// <returns></returns>
  150. public AABB GetCollisionBox()
  151. {
  152. AABB aabb;
  153. Vector2 lowerBound = new Vector2(float.MaxValue, float.MaxValue);
  154. Vector2 upperBound = new Vector2(float.MinValue, float.MinValue);
  155. for (int i = 0; i < Count; ++i)
  156. {
  157. if (this[i].X < lowerBound.X)
  158. {
  159. lowerBound.X = this[i].X;
  160. }
  161. if (this[i].X > upperBound.X)
  162. {
  163. upperBound.X = this[i].X;
  164. }
  165. if (this[i].Y < lowerBound.Y)
  166. {
  167. lowerBound.Y = this[i].Y;
  168. }
  169. if (this[i].Y > upperBound.Y)
  170. {
  171. upperBound.Y = this[i].Y;
  172. }
  173. }
  174. aabb.LowerBound = lowerBound;
  175. aabb.UpperBound = upperBound;
  176. return aabb;
  177. }
  178. public void Translate(Vector2 vector)
  179. {
  180. Translate(ref vector);
  181. }
  182. /// <summary>
  183. /// Translates the vertices with the specified vector.
  184. /// </summary>
  185. /// <param name="vector">The vector.</param>
  186. public void Translate(ref Vector2 vector)
  187. {
  188. for (int i = 0; i < Count; i++)
  189. this[i] = Vector2.Add(this[i], vector);
  190. }
  191. /// <summary>
  192. /// Scales the vertices with the specified vector.
  193. /// </summary>
  194. /// <param name="value">The Value.</param>
  195. public void Scale(ref Vector2 value)
  196. {
  197. for (int i = 0; i < Count; i++)
  198. this[i] = Vector2.Multiply(this[i], value);
  199. }
  200. /// <summary>
  201. /// Rotate the vertices with the defined value in radians.
  202. /// </summary>
  203. /// <param name="value">The amount to rotate by in radians.</param>
  204. public void Rotate(float value)
  205. {
  206. Matrix rotationMatrix;
  207. Matrix.CreateRotationZ(value, out rotationMatrix);
  208. for (int i = 0; i < Count; i++)
  209. this[i] = Vector2.Transform(this[i], rotationMatrix);
  210. }
  211. /// <summary>
  212. /// Assuming the polygon is simple; determines whether the polygon is convex.
  213. /// NOTE: It will also return false if the input contains colinear edges.
  214. /// </summary>
  215. /// <returns>
  216. /// <c>true</c> if it is convex; otherwise, <c>false</c>.
  217. /// </returns>
  218. public bool IsConvex()
  219. {
  220. // Ensure the polygon is convex and the interior
  221. // is to the left of each edge.
  222. for (int i = 0; i < Count; ++i)
  223. {
  224. int i1 = i;
  225. int i2 = i + 1 < Count ? i + 1 : 0;
  226. Vector2 edge = this[i2] - this[i1];
  227. for (int j = 0; j < Count; ++j)
  228. {
  229. // Don't check vertices on the current edge.
  230. if (j == i1 || j == i2)
  231. {
  232. continue;
  233. }
  234. Vector2 r = this[j] - this[i1];
  235. float s = edge.X * r.Y - edge.Y * r.X;
  236. if (s <= 0.0f)
  237. return false;
  238. }
  239. }
  240. return true;
  241. }
  242. public bool IsCounterClockWise()
  243. {
  244. //We just return true for lines
  245. if (Count < 3)
  246. return true;
  247. return (GetSignedArea() > 0.0f);
  248. }
  249. /// <summary>
  250. /// Forces counter clock wise order.
  251. /// </summary>
  252. public void ForceCounterClockWise()
  253. {
  254. if (!IsCounterClockWise())
  255. {
  256. Reverse();
  257. }
  258. }
  259. /// <summary>
  260. /// Check for edge crossings
  261. /// </summary>
  262. /// <returns></returns>
  263. public bool IsSimple()
  264. {
  265. for (int i = 0; i < Count; ++i)
  266. {
  267. int iplus = (i + 1 > Count - 1) ? 0 : i + 1;
  268. Vector2 a1 = new Vector2(this[i].X, this[i].Y);
  269. Vector2 a2 = new Vector2(this[iplus].X, this[iplus].Y);
  270. for (int j = i + 1; j < Count; ++j)
  271. {
  272. int jplus = (j + 1 > Count - 1) ? 0 : j + 1;
  273. Vector2 b1 = new Vector2(this[j].X, this[j].Y);
  274. Vector2 b2 = new Vector2(this[jplus].X, this[jplus].Y);
  275. Vector2 temp;
  276. if (LineTools.LineIntersect2(a1, a2, b1, b2, out temp))
  277. {
  278. return false;
  279. }
  280. }
  281. }
  282. return true;
  283. }
  284. //TODO: Test
  285. //Implementation found here: http://www.gamedev.net/community/forums/topic.asp?topic_id=548477
  286. public bool IsSimple2()
  287. {
  288. for (int i = 0; i < Count; ++i)
  289. {
  290. if (i < Count - 1)
  291. {
  292. for (int h = i + 1; h < Count; ++h)
  293. {
  294. // Do two vertices lie on top of one another?
  295. if (this[i] == this[h])
  296. {
  297. return true;
  298. }
  299. }
  300. }
  301. int j = (i + 1) % Count;
  302. Vector2 iToj = this[j] - this[i];
  303. Vector2 iTojNormal = new Vector2(iToj.Y, -iToj.X);
  304. // i is the first vertex and j is the second
  305. int startK = (j + 1) % Count;
  306. int endK = (i - 1 + Count) % Count;
  307. endK += startK < endK ? 0 : startK + 1;
  308. int k = startK;
  309. Vector2 iTok = this[k] - this[i];
  310. bool onLeftSide = Vector2.Dot(iTok, iTojNormal) >= 0;
  311. Vector2 prevK = this[k];
  312. ++k;
  313. for (; k <= endK; ++k)
  314. {
  315. int modK = k % Count;
  316. iTok = this[modK] - this[i];
  317. if (onLeftSide != Vector2.Dot(iTok, iTojNormal) >= 0)
  318. {
  319. Vector2 prevKtoK = this[modK] - prevK;
  320. Vector2 prevKtoKNormal = new Vector2(prevKtoK.Y, -prevKtoK.X);
  321. if ((Vector2.Dot(this[i] - prevK, prevKtoKNormal) >= 0) !=
  322. (Vector2.Dot(this[j] - prevK, prevKtoKNormal) >= 0))
  323. {
  324. return true;
  325. }
  326. }
  327. onLeftSide = Vector2.Dot(iTok, iTojNormal) > 0;
  328. prevK = this[modK];
  329. }
  330. }
  331. return false;
  332. }
  333. // From Eric Jordan's convex decomposition library
  334. /// <summary>
  335. /// Checks if polygon is valid for use in Box2d engine.
  336. /// Last ditch effort to ensure no invalid polygons are
  337. /// added to world geometry.
  338. ///
  339. /// Performs a full check, for simplicity, convexity,
  340. /// orientation, minimum angle, and volume. This won't
  341. /// be very efficient, and a lot of it is redundant when
  342. /// other tools in this section are used.
  343. /// </summary>
  344. /// <returns></returns>
  345. public bool CheckPolygon()
  346. {
  347. int error = -1;
  348. if (Count < 3 || Count > Settings.MaxPolygonVertices)
  349. {
  350. error = 0;
  351. }
  352. if (!IsConvex())
  353. {
  354. error = 1;
  355. }
  356. if (!IsSimple())
  357. {
  358. error = 2;
  359. }
  360. if (GetArea() < Settings.Epsilon)
  361. {
  362. error = 3;
  363. }
  364. //Compute normals
  365. Vector2[] normals = new Vector2[Count];
  366. Vertices vertices = new Vertices(Count);
  367. for (int i = 0; i < Count; ++i)
  368. {
  369. vertices.Add(new Vector2(this[i].X, this[i].Y));
  370. int i1 = i;
  371. int i2 = i + 1 < Count ? i + 1 : 0;
  372. Vector2 edge = new Vector2(this[i2].X - this[i1].X, this[i2].Y - this[i1].Y);
  373. normals[i] = MathUtils.Cross(edge, 1.0f);
  374. normals[i].Normalize();
  375. }
  376. //Required side checks
  377. for (int i = 0; i < Count; ++i)
  378. {
  379. int iminus = (i == 0) ? Count - 1 : i - 1;
  380. //Parallel sides check
  381. float cross = MathUtils.Cross(normals[iminus], normals[i]);
  382. cross = MathUtils.Clamp(cross, -1.0f, 1.0f);
  383. float angle = (float)Math.Asin(cross);
  384. if (angle <= Settings.AngularSlop)
  385. {
  386. error = 4;
  387. break;
  388. }
  389. //Too skinny check
  390. for (int j = 0; j < Count; ++j)
  391. {
  392. if (j == i || j == (i + 1) % Count)
  393. {
  394. continue;
  395. }
  396. float s = Vector2.Dot(normals[i], vertices[j] - vertices[i]);
  397. if (s >= -Settings.LinearSlop)
  398. {
  399. error = 5;
  400. }
  401. }
  402. Vector2 centroid = vertices.GetCentroid();
  403. Vector2 n1 = normals[iminus];
  404. Vector2 n2 = normals[i];
  405. Vector2 v = vertices[i] - centroid;
  406. Vector2 d = new Vector2();
  407. d.X = Vector2.Dot(n1, v); // - toiSlop;
  408. d.Y = Vector2.Dot(n2, v); // - toiSlop;
  409. // Shifting the edge inward by toiSlop should
  410. // not cause the plane to pass the centroid.
  411. if ((d.X < 0.0f) || (d.Y < 0.0f))
  412. {
  413. error = 6;
  414. }
  415. }
  416. if (error != -1)
  417. {
  418. Debug.WriteLine("Found invalid polygon, ");
  419. switch (error)
  420. {
  421. case 0:
  422. Debug.WriteLine(string.Format("must have between 3 and {0} vertices.\n",
  423. Settings.MaxPolygonVertices));
  424. break;
  425. case 1:
  426. Debug.WriteLine("must be convex.\n");
  427. break;
  428. case 2:
  429. Debug.WriteLine("must be simple (cannot intersect itself).\n");
  430. break;
  431. case 3:
  432. Debug.WriteLine("area is too small.\n");
  433. break;
  434. case 4:
  435. Debug.WriteLine("sides are too close to parallel.\n");
  436. break;
  437. case 5:
  438. Debug.WriteLine("polygon is too thin.\n");
  439. break;
  440. case 6:
  441. Debug.WriteLine("core shape generation would move edge past centroid (too thin).\n");
  442. break;
  443. default:
  444. Debug.WriteLine("don't know why.\n");
  445. break;
  446. }
  447. }
  448. return error != -1;
  449. }
  450. // From Eric Jordan's convex decomposition library
  451. /// <summary>
  452. /// Trace the edge of a non-simple polygon and return a simple polygon.
  453. ///
  454. /// Method:
  455. /// Start at vertex with minimum y (pick maximum x one if there are two).
  456. /// We aim our "lastDir" vector at (1.0, 0)
  457. /// We look at the two rays going off from our start vertex, and follow whichever
  458. /// has the smallest angle (in -Pi . Pi) wrt lastDir ("rightest" turn)
  459. /// Loop until we hit starting vertex:
  460. /// We add our current vertex to the list.
  461. /// We check the seg from current vertex to next vertex for intersections
  462. /// - if no intersections, follow to next vertex and continue
  463. /// - if intersections, pick one with minimum distance
  464. /// - if more than one, pick one with "rightest" next point (two possibilities for each)
  465. /// </summary>
  466. /// <param name="verts">The vertices.</param>
  467. /// <returns></returns>
  468. public Vertices TraceEdge(Vertices verts)
  469. {
  470. PolyNode[] nodes = new PolyNode[verts.Count * verts.Count];
  471. //overkill, but sufficient (order of mag. is right)
  472. int nNodes = 0;
  473. //Add base nodes (raw outline)
  474. for (int i = 0; i < verts.Count; ++i)
  475. {
  476. Vector2 pos = new Vector2(verts[i].X, verts[i].Y);
  477. nodes[i].Position = pos;
  478. ++nNodes;
  479. int iplus = (i == verts.Count - 1) ? 0 : i + 1;
  480. int iminus = (i == 0) ? verts.Count - 1 : i - 1;
  481. nodes[i].AddConnection(nodes[iplus]);
  482. nodes[i].AddConnection(nodes[iminus]);
  483. }
  484. //Process intersection nodes - horribly inefficient
  485. bool dirty = true;
  486. int counter = 0;
  487. while (dirty)
  488. {
  489. dirty = false;
  490. for (int i = 0; i < nNodes; ++i)
  491. {
  492. for (int j = 0; j < nodes[i].NConnected; ++j)
  493. {
  494. for (int k = 0; k < nNodes; ++k)
  495. {
  496. if (k == i || nodes[k] == nodes[i].Connected[j]) continue;
  497. for (int l = 0; l < nodes[k].NConnected; ++l)
  498. {
  499. if (nodes[k].Connected[l] == nodes[i].Connected[j] ||
  500. nodes[k].Connected[l] == nodes[i]) continue;
  501. //Check intersection
  502. Vector2 intersectPt;
  503. bool crosses = LineTools.LineIntersect(nodes[i].Position, nodes[i].Connected[j].Position,
  504. nodes[k].Position, nodes[k].Connected[l].Position,
  505. out intersectPt);
  506. if (crosses)
  507. {
  508. dirty = true;
  509. //Destroy and re-hook connections at crossing point
  510. PolyNode connj = nodes[i].Connected[j];
  511. PolyNode connl = nodes[k].Connected[l];
  512. nodes[i].Connected[j].RemoveConnection(nodes[i]);
  513. nodes[i].RemoveConnection(connj);
  514. nodes[k].Connected[l].RemoveConnection(nodes[k]);
  515. nodes[k].RemoveConnection(connl);
  516. nodes[nNodes] = new PolyNode(intersectPt);
  517. nodes[nNodes].AddConnection(nodes[i]);
  518. nodes[i].AddConnection(nodes[nNodes]);
  519. nodes[nNodes].AddConnection(nodes[k]);
  520. nodes[k].AddConnection(nodes[nNodes]);
  521. nodes[nNodes].AddConnection(connj);
  522. connj.AddConnection(nodes[nNodes]);
  523. nodes[nNodes].AddConnection(connl);
  524. connl.AddConnection(nodes[nNodes]);
  525. ++nNodes;
  526. goto SkipOut;
  527. }
  528. }
  529. }
  530. }
  531. }
  532. SkipOut:
  533. ++counter;
  534. }
  535. //Collapse duplicate points
  536. bool foundDupe = true;
  537. int nActive = nNodes;
  538. while (foundDupe)
  539. {
  540. foundDupe = false;
  541. for (int i = 0; i < nNodes; ++i)
  542. {
  543. if (nodes[i].NConnected == 0) continue;
  544. for (int j = i + 1; j < nNodes; ++j)
  545. {
  546. if (nodes[j].NConnected == 0) continue;
  547. Vector2 diff = nodes[i].Position - nodes[j].Position;
  548. if (diff.LengthSquared() <= Settings.Epsilon * Settings.Epsilon)
  549. {
  550. if (nActive <= 3)
  551. return new Vertices();
  552. //printf("Found dupe, %d left\n",nActive);
  553. --nActive;
  554. foundDupe = true;
  555. PolyNode inode = nodes[i];
  556. PolyNode jnode = nodes[j];
  557. //Move all of j's connections to i, and orphan j
  558. int njConn = jnode.NConnected;
  559. for (int k = 0; k < njConn; ++k)
  560. {
  561. PolyNode knode = jnode.Connected[k];
  562. Debug.Assert(knode != jnode);
  563. if (knode != inode)
  564. {
  565. inode.AddConnection(knode);
  566. knode.AddConnection(inode);
  567. }
  568. knode.RemoveConnection(jnode);
  569. }
  570. jnode.NConnected = 0;
  571. }
  572. }
  573. }
  574. }
  575. //Now walk the edge of the list
  576. //Find node with minimum y value (max x if equal)
  577. float minY = float.MaxValue;
  578. float maxX = -float.MaxValue;
  579. int minYIndex = -1;
  580. for (int i = 0; i < nNodes; ++i)
  581. {
  582. if (nodes[i].Position.Y < minY && nodes[i].NConnected > 1)
  583. {
  584. minY = nodes[i].Position.Y;
  585. minYIndex = i;
  586. maxX = nodes[i].Position.X;
  587. }
  588. else if (nodes[i].Position.Y == minY && nodes[i].Position.X > maxX && nodes[i].NConnected > 1)
  589. {
  590. minYIndex = i;
  591. maxX = nodes[i].Position.X;
  592. }
  593. }
  594. Vector2 origDir = new Vector2(1.0f, 0.0f);
  595. Vector2[] resultVecs = new Vector2[4 * nNodes];
  596. // nodes may be visited more than once, unfortunately - change to growable array!
  597. int nResultVecs = 0;
  598. PolyNode currentNode = nodes[minYIndex];
  599. PolyNode startNode = currentNode;
  600. Debug.Assert(currentNode.NConnected > 0);
  601. PolyNode nextNode = currentNode.GetRightestConnection(origDir);
  602. if (nextNode == null)
  603. {
  604. Vertices vertices = new Vertices(nResultVecs);
  605. for (int i = 0; i < nResultVecs; ++i)
  606. {
  607. vertices.Add(resultVecs[i]);
  608. }
  609. return vertices;
  610. }
  611. // Borked, clean up our mess and return
  612. resultVecs[0] = startNode.Position;
  613. ++nResultVecs;
  614. while (nextNode != startNode)
  615. {
  616. if (nResultVecs > 4 * nNodes)
  617. {
  618. Debug.Assert(false);
  619. }
  620. resultVecs[nResultVecs++] = nextNode.Position;
  621. PolyNode oldNode = currentNode;
  622. currentNode = nextNode;
  623. nextNode = currentNode.GetRightestConnection(oldNode);
  624. if (nextNode == null)
  625. {
  626. Vertices vertices = new Vertices(nResultVecs);
  627. for (int i = 0; i < nResultVecs; ++i)
  628. {
  629. vertices.Add(resultVecs[i]);
  630. }
  631. return vertices;
  632. }
  633. // There was a problem, so jump out of the loop and use whatever garbage we've generated so far
  634. }
  635. return new Vertices();
  636. }
  637. private class PolyNode
  638. {
  639. private const int MaxConnected = 32;
  640. /*
  641. * Given sines and cosines, tells if A's angle is less than B's on -Pi, Pi
  642. * (in other words, is A "righter" than B)
  643. */
  644. public PolyNode[] Connected = new PolyNode[MaxConnected];
  645. public int NConnected;
  646. public Vector2 Position;
  647. public PolyNode(Vector2 pos)
  648. {
  649. Position = pos;
  650. NConnected = 0;
  651. }
  652. private bool IsRighter(float sinA, float cosA, float sinB, float cosB)
  653. {
  654. if (sinA < 0)
  655. {
  656. if (sinB > 0 || cosA <= cosB) return true;
  657. else return false;
  658. }
  659. else
  660. {
  661. if (sinB < 0 || cosA <= cosB) return false;
  662. else return true;
  663. }
  664. }
  665. public void AddConnection(PolyNode toMe)
  666. {
  667. Debug.Assert(NConnected < MaxConnected);
  668. // Ignore duplicate additions
  669. for (int i = 0; i < NConnected; ++i)
  670. {
  671. if (Connected[i] == toMe) return;
  672. }
  673. Connected[NConnected] = toMe;
  674. ++NConnected;
  675. }
  676. public void RemoveConnection(PolyNode fromMe)
  677. {
  678. bool isFound = false;
  679. int foundIndex = -1;
  680. for (int i = 0; i < NConnected; ++i)
  681. {
  682. if (fromMe == Connected[i])
  683. {
  684. //.position == connected[i].position){
  685. isFound = true;
  686. foundIndex = i;
  687. break;
  688. }
  689. }
  690. Debug.Assert(isFound);
  691. --NConnected;
  692. for (int i = foundIndex; i < NConnected; ++i)
  693. {
  694. Connected[i] = Connected[i + 1];
  695. }
  696. }
  697. public PolyNode GetRightestConnection(PolyNode incoming)
  698. {
  699. if (NConnected == 0) Debug.Assert(false); // This means the connection graph is inconsistent
  700. if (NConnected == 1)
  701. {
  702. //b2Assert(false);
  703. // Because of the possibility of collapsing nearby points,
  704. // we may end up with "spider legs" dangling off of a region.
  705. // The correct behavior here is to turn around.
  706. return incoming;
  707. }
  708. Vector2 inDir = Position - incoming.Position;
  709. float inLength = inDir.Length();
  710. inDir.Normalize();
  711. Debug.Assert(inLength > Settings.Epsilon);
  712. PolyNode result = null;
  713. for (int i = 0; i < NConnected; ++i)
  714. {
  715. if (Connected[i] == incoming) continue;
  716. Vector2 testDir = Connected[i].Position - Position;
  717. float testLengthSqr = testDir.LengthSquared();
  718. testDir.Normalize();
  719. Debug.Assert(testLengthSqr >= Settings.Epsilon * Settings.Epsilon);
  720. float myCos = Vector2.Dot(inDir, testDir);
  721. float mySin = MathUtils.Cross(inDir, testDir);
  722. if (result != null)
  723. {
  724. Vector2 resultDir = result.Position - Position;
  725. resultDir.Normalize();
  726. float resCos = Vector2.Dot(inDir, resultDir);
  727. float resSin = MathUtils.Cross(inDir, resultDir);
  728. if (IsRighter(mySin, myCos, resSin, resCos))
  729. {
  730. result = Connected[i];
  731. }
  732. }
  733. else
  734. {
  735. result = Connected[i];
  736. }
  737. }
  738. Debug.Assert(result != null);
  739. return result;
  740. }
  741. public PolyNode GetRightestConnection(Vector2 incomingDir)
  742. {
  743. Vector2 diff = Position - incomingDir;
  744. PolyNode temp = new PolyNode(diff);
  745. PolyNode res = GetRightestConnection(temp);
  746. Debug.Assert(res != null);
  747. return res;
  748. }
  749. }
  750. public override string ToString()
  751. {
  752. StringBuilder builder = new StringBuilder();
  753. for (int i = 0; i < Count; i++)
  754. {
  755. builder.Append(this[i].ToString());
  756. if (i < Count - 1)
  757. {
  758. builder.Append(" ");
  759. }
  760. }
  761. return builder.ToString();
  762. }
  763. /// <summary>
  764. /// Projects to axis.
  765. /// </summary>
  766. /// <param name="axis">The axis.</param>
  767. /// <param name="min">The min.</param>
  768. /// <param name="max">The max.</param>
  769. public void ProjectToAxis(ref Vector2 axis, out float min, out float max)
  770. {
  771. // To project a point on an axis use the dot product
  772. float dotProduct = Vector2.Dot(axis, this[0]);
  773. min = dotProduct;
  774. max = dotProduct;
  775. for (int i = 0; i < Count; i++)
  776. {
  777. dotProduct = Vector2.Dot(this[i], axis);
  778. if (dotProduct < min)
  779. {
  780. min = dotProduct;
  781. }
  782. else
  783. {
  784. if (dotProduct > max)
  785. {
  786. max = dotProduct;
  787. }
  788. }
  789. }
  790. }
  791. /// <summary>
  792. /// Winding number test for a point in a polygon.
  793. /// </summary>
  794. /// See more info about the algorithm here: http://softsurfer.com/Archive/algorithm_0103/algorithm_0103.htm
  795. /// <param name="point">The point to be tested.</param>
  796. /// <returns>-1 if the winding number is zero and the point is outside
  797. /// the polygon, 1 if the point is inside the polygon, and 0 if the point
  798. /// is on the polygons edge.</returns>
  799. public int PointInPolygon(ref Vector2 point)
  800. {
  801. // Winding number
  802. int wn = 0;
  803. // Iterate through polygon's edges
  804. for (int i = 0; i < Count; i++)
  805. {
  806. // Get points
  807. Vector2 p1 = this[i];
  808. Vector2 p2 = this[NextIndex(i)];
  809. // Test if a point is directly on the edge
  810. Vector2 edge = p2 - p1;
  811. float area = MathUtils.Area(ref p1, ref p2, ref point);
  812. if (area == 0f && Vector2.Dot(point - p1, edge) >= 0f && Vector2.Dot(point - p2, edge) <= 0f)
  813. {
  814. return 0;
  815. }
  816. // Test edge for intersection with ray from point
  817. if (p1.Y <= point.Y)
  818. {
  819. if (p2.Y > point.Y && area > 0f)
  820. {
  821. ++wn;
  822. }
  823. }
  824. else
  825. {
  826. if (p2.Y <= point.Y && area < 0f)
  827. {
  828. --wn;
  829. }
  830. }
  831. }
  832. return (wn == 0 ? -1 : 1);
  833. }
  834. /// <summary>
  835. /// Compute the sum of the angles made between the test point and each pair of points making up the polygon.
  836. /// If this sum is 2pi then the point is an interior point, if 0 then the point is an exterior point.
  837. /// ref: http://ozviz.wasp.uwa.edu.au/~pbourke/geometry/insidepoly/ - Solution 2
  838. /// </summary>
  839. public bool PointInPolygonAngle(ref Vector2 point)
  840. {
  841. double angle = 0;
  842. // Iterate through polygon's edges
  843. for (int i = 0; i < Count; i++)
  844. {
  845. // Get points
  846. Vector2 p1 = this[i] - point;
  847. Vector2 p2 = this[NextIndex(i)] - point;
  848. angle += MathUtils.VectorAngle(ref p1, ref p2);
  849. }
  850. if (Math.Abs(angle) < Math.PI)
  851. {
  852. return false;
  853. }
  854. return true;
  855. }
  856. }
  857. }