| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849 |
- import cross from "./cross.js";
- function lexicographicOrder(a, b) {
- return a[0] - b[0] || a[1] - b[1];
- }
- // Computes the upper convex hull per the monotone chain algorithm.
- // Assumes points.length >= 3, is sorted by x, unique in y.
- // Returns an array of indices into points in left-to-right order.
- function computeUpperHullIndexes(points) {
- const n = points.length,
- indexes = [0, 1];
- let size = 2, i;
- for (i = 2; i < n; ++i) {
- while (size > 1 && cross(points[indexes[size - 2]], points[indexes[size - 1]], points[i]) <= 0) --size;
- indexes[size++] = i;
- }
- return indexes.slice(0, size); // remove popped points
- }
- export default function(points) {
- if ((n = points.length) < 3) return null;
- var i,
- n,
- sortedPoints = new Array(n),
- flippedPoints = new Array(n);
- for (i = 0; i < n; ++i) sortedPoints[i] = [+points[i][0], +points[i][1], i];
- sortedPoints.sort(lexicographicOrder);
- for (i = 0; i < n; ++i) flippedPoints[i] = [sortedPoints[i][0], -sortedPoints[i][1]];
- var upperIndexes = computeUpperHullIndexes(sortedPoints),
- lowerIndexes = computeUpperHullIndexes(flippedPoints);
- // Construct the hull polygon, removing possible duplicate endpoints.
- var skipLeft = lowerIndexes[0] === upperIndexes[0],
- skipRight = lowerIndexes[lowerIndexes.length - 1] === upperIndexes[upperIndexes.length - 1],
- hull = [];
- // Add upper hull in right-to-l order.
- // Then add lower hull in left-to-right order.
- for (i = upperIndexes.length - 1; i >= 0; --i) hull.push(points[sortedPoints[upperIndexes[i]][2]]);
- for (i = +skipLeft; i < lowerIndexes.length - skipRight; ++i) hull.push(points[sortedPoints[lowerIndexes[i]][2]]);
- return hull;
- }
|