Skip to content

Instantly share code, notes, and snippets.

@lovasoa
Last active July 29, 2016 23:52
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lovasoa/510ba3f1ece9a623b703 to your computer and use it in GitHub Desktop.
Save lovasoa/510ba3f1ece9a623b703 to your computer and use it in GitHub Desktop.
Graham scan
function graham_scan(points) {
const pivot = points.reduce((c, p) =>
p.y < c.y || p.y===c.y && p.x<c.x ? p : c);
const sorted = points
.map(p=>({x:p.x,y:p.y,angle:Math.atan2(p.y-pivot.y,p.x-pivot.x)}))
.sort((a,b) => a.angle === b.angle ? a.x - b.x : a.angle - b.angle);
function cross_p (a,b,c) {return (b.x-a.x)*(c.y-a.y) <= (b.y-a.y)*(c.x-a.x);}
var result = [sorted[0], sorted[1]], top = 1;
for(var i=2; i<sorted.length; i++){
while (top >= 1 && cross_p(result[top-1], result[top], sorted[i])) top--;
result[++top] = sorted[i];
}
return result.slice(0,top+1);
}
function graham_scan(points) {
var pivot = points.reduce(function (c, p) {
return p.y < c.y || p.y === c.y && p.x < c.x ? p : c;
});
var sorted = points.map(function (p) {
return { x: p.x, y: p.y, angle: Math.atan2(p.y - pivot.y, p.x - pivot.x) };
}).sort(function (a, b) {
return a.angle === b.angle ? a.x - b.x : a.angle - b.angle;
});
function cross_p(a, b, c) {
return (b.x - a.x) * (c.y - a.y) <= (b.y - a.y) * (c.x - a.x);
}
var result = [sorted[0], sorted[1]],
top = 1;
for (var i = 2; i < sorted.length; i++) {
while (top >= 1 && cross_p(result[top - 1], result[top], sorted[i])) top--;
result[++top] = sorted[i];
}
return result.slice(0, top + 1);
}
/* Fast optimized version of the graham scan in javascript
computes the hull of 1 million points in 600ms in firefox
Note: the orginal points array will be reordered in place, and a "_graham_angle" property will be added to all the points.
@param points : an array of points. Each point is represented as an array [x,y]
@return : an array of points. The points form the convex hull of the original set of points
*/
function graham_scan(points) {
// The enveloppe is the points themselves
if (points.length <= 3) return points;
// Find the pivot
var pivot = points[0];
for (var i=0; i<points.length; i++) {
if (points[i][1] < pivot[1] || (points[i][1] === pivot[1] && points[i][0] < pivot[0]))
pivot = points[i];
}
// Attribute an angle to the points
for (var i=0; i<points.length; i++) {
points[i]._graham_angle = Math.atan2(points[i][1] - pivot[1], points[i][0] - pivot[0]);
}
points.sort(function(a, b){return a._graham_angle === b._graham_angle
? a[0] - b[0]
: a._graham_angle - b._graham_angle});
// Adding points to the result if they "turn left"
var result = [points[0]], len=1;
for(var i=1; i<points.length; i++){
var a = result[len-2],
b = result[len-1],
c = points[i];
while (
(len === 1 && b[0] === c[0] && b[1] === c[1]) ||
(len > 1 && (b[0]-a[0]) * (c[1]-a[1]) <= (b[1]-a[1]) * (c[0]-a[0]))) {
len--;
b = a;
a = result[len-2];
}
result[len++] = c;
}
result.length = len;
return result;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment