GiftWrap.cs 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. using System;
  2. namespace FarseerPhysics.Common.ConvexHull
  3. {
  4. public static class GiftWrap
  5. {
  6. // From Eric Jordan's convex decomposition library (box2D rev 32)
  7. /// <summary>
  8. /// Find the convex hull of a point cloud using "Gift-wrap" algorithm - start
  9. /// with an extremal point, and walk around the outside edge by testing
  10. /// angles.
  11. ///
  12. /// Runs in O(N*S) time where S is number of sides of resulting polygon.
  13. /// Worst case: point cloud is all vertices of convex polygon: O(N^2).
  14. /// There may be faster algorithms to do this, should you need one -
  15. /// this is just the simplest. You can get O(N log N) expected time if you
  16. /// try, I think, and O(N) if you restrict inputs to simple polygons.
  17. /// Returns null if number of vertices passed is less than 3.
  18. /// Results should be passed through convex decomposition afterwards
  19. /// to ensure that each shape has few enough points to be used in Box2d.
  20. ///
  21. /// Warning: May be buggy with colinear points on hull.
  22. /// </summary>
  23. /// <param name="vertices">The vertices.</param>
  24. /// <returns></returns>
  25. public static Vertices GetConvexHull(Vertices vertices)
  26. {
  27. if (vertices.Count < 3)
  28. return vertices;
  29. int[] edgeList = new int[vertices.Count];
  30. int numEdges = 0;
  31. float minY = float.MaxValue;
  32. int minYIndex = vertices.Count;
  33. for (int i = 0; i < vertices.Count; ++i)
  34. {
  35. if (vertices[i].Y < minY)
  36. {
  37. minY = vertices[i].Y;
  38. minYIndex = i;
  39. }
  40. }
  41. int startIndex = minYIndex;
  42. int winIndex = -1;
  43. float dx = -1.0f;
  44. float dy = 0.0f;
  45. while (winIndex != minYIndex)
  46. {
  47. float maxDot = -2.0f;
  48. float nrm;
  49. for (int i = 0; i < vertices.Count; ++i)
  50. {
  51. if (i == startIndex)
  52. continue;
  53. float newdx = vertices[i].X - vertices[startIndex].X;
  54. float newdy = vertices[i].Y - vertices[startIndex].Y;
  55. nrm = (float)Math.Sqrt(newdx * newdx + newdy * newdy);
  56. nrm = (nrm == 0.0f) ? 1.0f : nrm;
  57. newdx /= nrm;
  58. newdy /= nrm;
  59. //Dot products act as proxy for angle
  60. //without requiring inverse trig.
  61. float newDot = newdx * dx + newdy * dy;
  62. if (newDot > maxDot)
  63. {
  64. maxDot = newDot;
  65. winIndex = i;
  66. }
  67. }
  68. edgeList[numEdges++] = winIndex;
  69. dx = vertices[winIndex].X - vertices[startIndex].X;
  70. dy = vertices[winIndex].Y - vertices[startIndex].Y;
  71. nrm = (float)Math.Sqrt(dx * dx + dy * dy);
  72. nrm = (nrm == 0.0f) ? 1.0f : nrm;
  73. dx /= nrm;
  74. dy /= nrm;
  75. startIndex = winIndex;
  76. }
  77. Vertices returnVal = new Vertices(numEdges);
  78. for (int i = 0; i < numEdges; i++)
  79. {
  80. returnVal.Add(vertices[edgeList[i]]);
  81. //Debug.WriteLine(string.Format("{0}, {1}", vertices[edgeList[i]].X, vertices[edgeList[i]].Y));
  82. }
  83. //Not sure if we need this
  84. //returnVal.MergeParallelEdges(Settings.b2_angularSlop);
  85. return returnVal;
  86. }
  87. }
  88. }