Source: math/Delaunay.js

  1. /**
  2. *
  3. * @class Delaunay
  4. * @memberof SQR
  5. *
  6. * @description based on:<br>
  7. *
  8. * {@link http://paulbourke.net/papers/triangulate/}<br>
  9. * {@link http://www.travellermap.com/tmp/delaunay.htm} (original code)<br>
  10. * {@link https://github.com/ironwallaby/delaunay/blob/master/delaunay.js}<br>
  11. * {@link http://www.amazon.com/Computational-Geometry-Applications-Mark-Berg/dp/3642096816}
  12. */
  13. SQR.Delaunay = (function() {
  14. var delaunay = {};
  15. var Edge = function(v0, v1) {
  16. this.v0 = v0;
  17. this.v1 = v1;
  18. }
  19. Edge.prototype.equals = function(other) {
  20. return (this.v0 === other.v0 && this.v1 === other.v1);
  21. };
  22. Edge.prototype.inverse = function() {
  23. return new Edge(this.v1, this.v0);
  24. };
  25. var createSuperTriangle = function(vertices) {
  26. // NOTE: There's a bit of a heuristic here. If the bounding triangle
  27. // is too large and you see overflow/underflow errors. If it is too small
  28. // you end up with a non-convex hull.
  29. var minx, miny, maxx, maxy;
  30. vertices.forEach(function(vertex) {
  31. if (minx === undefined || vertex.x < minx) { minx = vertex.x; }
  32. if (miny === undefined || vertex.y < miny) { miny = vertex.y; }
  33. if (maxx === undefined || vertex.x > maxx) { maxx = vertex.x; }
  34. if (maxy === undefined || vertex.y > maxy) { maxy = vertex.y; }
  35. });
  36. var dx = (maxx - minx) * 10;
  37. var dy = (maxy - miny) * 10;
  38. var stv0 = vertices[0].clone().set(minx - dx, miny - dy * 3);
  39. var stv1 = vertices[0].clone().set(minx - dx, maxy + dy);
  40. var stv2 = vertices[0].clone().set(maxx + dx * 3, maxy + dy);
  41. return new SQR.Triangle(stv0, stv1, stv2);
  42. }
  43. function addVertex(vertex, triangles) {
  44. var edges = [];
  45. triangles = triangles.filter(function(triangle) {
  46. if (triangle.vertexInCircumcircle(vertex)) {
  47. edges.push(new Edge(triangle.v0, triangle.v1));
  48. edges.push(new Edge(triangle.v1, triangle.v2));
  49. edges.push(new Edge(triangle.v2, triangle.v0));
  50. return false;
  51. }
  52. return true;
  53. });
  54. edges = uniqueEdges(edges);
  55. edges.forEach(function(edge) {
  56. triangles.push(new SQR.Triangle(edge.v0, edge.v1, vertex));
  57. });
  58. return triangles;
  59. }
  60. var uniqueEdges = function(edges) {
  61. var uniqueEdges = [];
  62. for (var i = 0; i < edges.length; ++i) {
  63. var edge1 = edges[i];
  64. var unique = true;
  65. for (var j = 0; j < edges.length; ++j) {
  66. if (i === j) continue;
  67. var edge2 = edges[j];
  68. if (edge1.equals(edge2) || edge1.inverse().equals(edge2)) {
  69. unique = false;
  70. break;
  71. }
  72. }
  73. if (unique) uniqueEdges.push(edge1);
  74. }
  75. return uniqueEdges;
  76. }
  77. /**
  78. * @method triangulate
  79. * @memberof SQR.Delaunay
  80. *
  81. * @description Performs Delaunay triangulation.
  82. *
  83. * @param vertices - a list of 2d vertices.
  84. * Can be {@link SQR.V2}, {@link SQR.V3} or any object that has `x` and `y` properties.
  85. * In case of a 3d vector, the `z` component is ignored.
  86. * @returns a list of {@link SQR.Triangles}
  87. */
  88. delaunay.triangulate = function(vertices) {
  89. var triangles = [];
  90. var st = createSuperTriangle(vertices);
  91. triangles.push(st);
  92. vertices.forEach(function(vertex) {
  93. // NOTE: This is O(n^2) - can be optimized by sorting vertices
  94. // along the x-axis and only considering triangles that have
  95. // potentially overlapping circumcircles
  96. triangles = addVertex(vertex, triangles);
  97. });
  98. // Remove triangles that shared edges with "supertriangle"
  99. triangles = triangles.filter(function(triangle) {
  100. return !(triangle.v0 == st.v0 || triangle.v0 == st.v1 || triangle.v0 == st.v2 ||
  101. triangle.v1 == st.v0 || triangle.v1 == st.v1 || triangle.v1 == st.v2 ||
  102. triangle.v2 == st.v0 || triangle.v2 == st.v1 || triangle.v2 == st.v2);
  103. });
  104. return triangles;
  105. }
  106. return delaunay;
  107. })();