ConvexHull.cpp 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. // This code is in the public domain -- Ignacio Castaño <[email protected]>
  2. #include "ConvexHull.h"
  3. #include "Vector.inl"
  4. #include "nvcore/RadixSort.h"
  5. #include "nvcore/Array.inl"
  6. using namespace nv;
  7. inline static float triangleArea(Vector2::Arg v1, Vector2::Arg v2, Vector2::Arg v3)
  8. {
  9. return 0.5f * (v3.x * v1.y + v1.x * v2.y + v2.x * v3.y - v2.x * v1.y - v3.x * v2.y - v1.x * v3.y);
  10. }
  11. // Compute the convex hull using Graham Scan.
  12. void nv::convexHull(const Array<Vector2> & input, Array<Vector2> & output, float epsilon/*=0*/)
  13. {
  14. const uint inputCount = input.count();
  15. Array<float> coords;
  16. coords.resize(inputCount);
  17. for (uint i = 0; i < inputCount; i++) {
  18. coords[i] = input[i].x;
  19. }
  20. RadixSort radix;
  21. radix.sort(coords);
  22. const uint * ranks = radix.ranks();
  23. Array<Vector2> top(inputCount);
  24. Array<Vector2> bottom(inputCount);
  25. Vector2 P = input[ranks[0]];
  26. Vector2 Q = input[ranks[inputCount-1]];
  27. float topy = max(P.y, Q.y);
  28. float boty = min(P.y, Q.y);
  29. for (uint i = 0; i < inputCount; i++) {
  30. Vector2 p = input[ranks[i]];
  31. if (p.y >= boty) top.append(p);
  32. }
  33. for (uint i = 0; i < inputCount; i++) {
  34. Vector2 p = input[ranks[inputCount-1-i]];
  35. if (p.y <= topy) bottom.append(p);
  36. }
  37. // Filter top list.
  38. output.clear();
  39. output.append(top[0]);
  40. output.append(top[1]);
  41. for (uint i = 2; i < top.count(); ) {
  42. Vector2 a = output[output.count()-2];
  43. Vector2 b = output[output.count()-1];
  44. Vector2 c = top[i];
  45. float area = triangleArea(a, b, c);
  46. if (area >= -epsilon) {
  47. output.popBack();
  48. }
  49. if (area < -epsilon || output.count() == 1) {
  50. output.append(c);
  51. i++;
  52. }
  53. }
  54. uint top_count = output.count();
  55. output.append(bottom[1]);
  56. // Filter bottom list.
  57. for (uint i = 2; i < bottom.count(); ) {
  58. Vector2 a = output[output.count()-2];
  59. Vector2 b = output[output.count()-1];
  60. Vector2 c = bottom[i];
  61. float area = triangleArea(a, b, c);
  62. if (area >= -epsilon) {
  63. output.popBack();
  64. }
  65. if (area < -epsilon || output.count() == top_count) {
  66. output.append(c);
  67. i++;
  68. }
  69. }
  70. // Remove duplicate element.
  71. nvDebugCheck(output.front() == output.back());
  72. output.popBack();
  73. }
  74. /*
  75. void testConvexHull() {
  76. Array<Vector2> points;
  77. points.append(Vector2(1.00, 1.00));
  78. points.append(Vector2(0.00, 0.00));
  79. points.append(Vector2(1.00, 1.00));
  80. points.append(Vector2(1.00, -1.00));
  81. points.append(Vector2(2.00, 5.00));
  82. points.append(Vector2(-5.00, 3.00));
  83. points.append(Vector2(-4.00, -3.00));
  84. points.append(Vector2(7.00, -4.00));
  85. Array<Vector2> hull;
  86. convexHull(points, hull);
  87. }
  88. */