hull.js 1.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849
  1. import cross from "./cross.js";
  2. function lexicographicOrder(a, b) {
  3. return a[0] - b[0] || a[1] - b[1];
  4. }
  5. // Computes the upper convex hull per the monotone chain algorithm.
  6. // Assumes points.length >= 3, is sorted by x, unique in y.
  7. // Returns an array of indices into points in left-to-right order.
  8. function computeUpperHullIndexes(points) {
  9. const n = points.length,
  10. indexes = [0, 1];
  11. let size = 2, i;
  12. for (i = 2; i < n; ++i) {
  13. while (size > 1 && cross(points[indexes[size - 2]], points[indexes[size - 1]], points[i]) <= 0) --size;
  14. indexes[size++] = i;
  15. }
  16. return indexes.slice(0, size); // remove popped points
  17. }
  18. export default function(points) {
  19. if ((n = points.length) < 3) return null;
  20. var i,
  21. n,
  22. sortedPoints = new Array(n),
  23. flippedPoints = new Array(n);
  24. for (i = 0; i < n; ++i) sortedPoints[i] = [+points[i][0], +points[i][1], i];
  25. sortedPoints.sort(lexicographicOrder);
  26. for (i = 0; i < n; ++i) flippedPoints[i] = [sortedPoints[i][0], -sortedPoints[i][1]];
  27. var upperIndexes = computeUpperHullIndexes(sortedPoints),
  28. lowerIndexes = computeUpperHullIndexes(flippedPoints);
  29. // Construct the hull polygon, removing possible duplicate endpoints.
  30. var skipLeft = lowerIndexes[0] === upperIndexes[0],
  31. skipRight = lowerIndexes[lowerIndexes.length - 1] === upperIndexes[upperIndexes.length - 1],
  32. hull = [];
  33. // Add upper hull in right-to-l order.
  34. // Then add lower hull in left-to-right order.
  35. for (i = upperIndexes.length - 1; i >= 0; --i) hull.push(points[sortedPoints[upperIndexes[i]][2]]);
  36. for (i = +skipLeft; i < lowerIndexes.length - skipRight; ++i) hull.push(points[sortedPoints[lowerIndexes[i]][2]]);
  37. return hull;
  38. }