You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
50 lines
1.7 KiB
50 lines
1.7 KiB
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;
|
|
}
|