import cross from "./cross"; 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) { var n = points.length, indexes = [0, 1], size = 2; for (var 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; }