Skip to content

Commit 11af13b

Browse files
committed
Fixes bug when collinear points sharing the same polar angle get being passed through to the resultant hull array, when they should be being removed. closes #7
1 parent e70df4d commit 11af13b

File tree

9 files changed

+125
-20
lines changed

9 files changed

+125
-20
lines changed

bower.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "graham_scan",
3-
"version": "1.0.2",
3+
"version": "1.0.3",
44
"main": "graham_scan.min.js",
55
"license": "MIT",
66
"ignore": [

example/app1.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ function initialize() {
1515
{'lat' : '48.9167', 'lon' : '11.3'},
1616
{'lat' : '48.8333', 'lon' : '11.4167'},
1717
{'lat' : '48.8667', 'lon' : '11.0667'},
18+
{'lat' : '48.7667', 'lon' : '11.0667'},
1819
{'lat' : '48.893175', 'lon' : '10.990565'},
1920
{'lat' : '48.8833', 'lon' : '11'},
2021
{'lat' : '48.8', 'lon' : '11.1'},

example/app2.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ function initialize() {
153153

154154
//Convert to google latlng objects
155155
hullPoints = hullPoints.map(function (item) {
156+
console.log('x:'+item.x + ', y:' + item.y);
156157
return new google.maps.LatLng(item.y, item.x);
157158
});
158159

example/app5.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
(function() {
2+
var canvas = document.getElementById('hullCanvas'),
3+
ctx = canvas.getContext('2d'),
4+
convexHull = new ConvexHullGrahamScan(),
5+
points = [
6+
[2,2], [2,-2], [-2,-2], [-2,2],
7+
[3,3], [3,-3], [-3,-3], [-3,3],
8+
[4,4], [4,-4], [-4,-4], [-4,4],
9+
[5,5], [5,-5], [-5,-5], [-5,5]
10+
],
11+
hull,
12+
drawPoint = function(point, fill) {
13+
ctx.beginPath();
14+
ctx.arc((point.x*20)+200, (point.y*20)+200, 3, 0, 3* Math.PI);
15+
if (fill){
16+
ctx.fillStyle = fill;
17+
ctx.fill();
18+
} else {
19+
ctx.stroke();
20+
}
21+
};
22+
23+
points.forEach(function(p) {
24+
convexHull.addPoint(p[0], p[1]);
25+
drawPoint({x:p[0], y:p[1]});
26+
});
27+
28+
hull = convexHull.getHull();
29+
console.log(hull);
30+
31+
ctx.beginPath();
32+
ctx.moveTo((hull[0].x*20)+200, (hull[0].y*20)+200);
33+
hull.forEach(function(p) {
34+
ctx.lineTo((p.x*20)+200, (p.y*20)+200);
35+
});
36+
ctx.lineTo((hull[0].x*20)+200, (hull[0].y*20)+200);
37+
ctx.stroke();
38+
ctx.closePath();
39+
40+
hull.forEach(function(p) {
41+
drawPoint(p, 'green');
42+
});
43+
44+
45+
})();

example/canvas_example5.html

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
5+
<meta charset="utf-8">
6+
<title>Graham's Scan Canvas Example</title>
7+
8+
<link href="main.css" rel="stylesheet">
9+
</head>
10+
<body>
11+
<canvas id="hullCanvas" height="800" width="800"></canvas>
12+
</body>
13+
<script src="../src/graham_scan.js"></script>
14+
<script src="app5.js"></script>
15+
16+
</html>

graham_scan.min.js

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "graham_scan",
3-
"version": "1.0.2",
3+
"version": "1.0.3",
44
"description": "Implementation of the Graham Scan algorithm to calculate a convex hull from a given array of x, y coordinates.",
55
"license": "MIT",
66
"directories": {

src/graham_scan.js

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
/**
22
* Graham's Scan Convex Hull Algorithm
3-
* @desc An implementation of the Graham's Scan Convex Hull algorithm in Javascript.
3+
* @desc An implementation of the Graham's Scan Convex Hull algorithm in JavaScript.
44
* @author Brian Barnett, brian@3kb.co.uk, http://brianbar.net/ || http://3kb.co.uk/
5-
* @version 1.0.2
5+
* @version 1.0.3
66
*/
77
function ConvexHullGrahamScan() {
88
this.anchorPoint = undefined;
@@ -48,12 +48,15 @@ ConvexHullGrahamScan.prototype = {
4848
if (this.anchorPoint === undefined) {
4949
//Create new anchorPoint.
5050
this.anchorPoint = new this.Point(x, y);
51-
51+
return;
5252
// Sets anchorPoint if point being added is further left.
53-
} else if (this.anchorPoint.y > y || (this.anchorPoint.y == y && this.anchorPoint.x > x)) {
54-
this.anchorPoint.y = y;
55-
this.anchorPoint.x = x;
56-
this.points.unshift(new this.Point(x, y));
53+
} else if (
54+
(this.anchorPoint.y > y && this.anchorPoint.x > x) ||
55+
(this.anchorPoint.y === y && this.anchorPoint.x > x) ||
56+
(this.anchorPoint.y > y && this.anchorPoint.x === x)
57+
) {
58+
this.points.push(new this.Point(this.anchorPoint.x, this.anchorPoint.y));
59+
this.anchorPoint = new this.Point(x, y);
5760
return;
5861
}
5962

@@ -97,7 +100,7 @@ ConvexHullGrahamScan.prototype = {
97100

98101
}
99102

100-
return false;
103+
return true;
101104
},
102105

103106
getHull: function () {
@@ -114,6 +117,7 @@ ConvexHullGrahamScan.prototype = {
114117

115118
//If there are less than 4 points, joining these points creates a correct hull.
116119
if (pointsLength < 4) {
120+
points.unshift(this.anchorPoint);
117121
return points;
118122
}
119123

@@ -138,6 +142,13 @@ ConvexHullGrahamScan.prototype = {
138142

139143
if (points.length == 0) {
140144
if (pointsLength == hullPoints.length) {
145+
//check for duplicate anchorPoint edge-case, if not found, add the anchorpoint as the first item.
146+
var ap = this.anchorPoint;
147+
if (!hullPoints.some(function(p){
148+
return(p.x == ap.x && p.y == ap.y);
149+
})) {
150+
hullPoints.unshift(this.anchorPoint);
151+
}
141152
return hullPoints;
142153
}
143154
points = hullPoints;

test/graham_scan.Test.js

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ test('Add a single new point',2 , function() {
44
var testGSHull = new ConvexHullGrahamScan();
55

66
testGSHull.addPoint(11, 50);
7-
console.log(testGSHull.points[0]);
7+
console.log(testGSHull.anchorPoint);
88
console.log(new testGSHull.Point(11, 50));
9-
equal(testGSHull.points[0], new testGSHull.Point(11, 50),
9+
equal(testGSHull.anchorPoint, new testGSHull.Point(11, 50),
1010
'Tests a point has been added to the points array correctly.');
1111
testGSHull.addPoint(10, 50);
12-
equal(testGSHull.points[0], new testGSHull.Point(10, 50),
12+
equal(testGSHull.anchorPoint, new testGSHull.Point(10, 50),
1313
'Tests that same y value then checks x value for comparison.');
1414
});
1515

@@ -63,8 +63,8 @@ test('Polar angle point comparison check.',3, function(){
6363
'Check if last point added results in a concave.');
6464

6565
equal(testGSHull._checkPoints( {'y' : '48.1', 'x' : '11.1'},
66-
{'y' : '48.1', 'x' : '11.1'},
67-
{'y' : '48.1', 'x' : '11.1'}),
66+
{'y' : '48.1', 'x' : '-11.1'},
67+
{'y' : '-48.1', 'x' : '11.1'}),
6868
false,
6969
'Check if last point added results in a concave.');
7070
});
@@ -133,9 +133,40 @@ test('Test handling 4 points rectangular.',1, function(){
133133
testGSHull.anchorPoint = {'y':50.157913235507706,'x':29.900512524414125};
134134
testGSHull.points = [
135135
{'y' : '50.157913235507706', 'x' : '29.900512524414125'},
136+
{'y' : '50.15791323550770611', 'x' : '31.146087475586'},
136137
{'y' : '50.74029471119741', 'x' : '31.146087475586'},
137-
{'y' : '50.74029471119741', 'x' : '29.900512524414125'},
138-
{'y' : '50.15791323550770611', 'x' : '31.146087475586'}
138+
{'y' : '50.74029471119741', 'x' : '29.900512524414125'}
139+
];
140+
141+
equal(testGSHull.getHull(), expectedHull, 'Check output hull is as expected.');
142+
});
143+
144+
test('that collinear points sharing the same polar angle are removed from resultant hull.',1, function(){
145+
var expectedHull = [
146+
{'y':-5,'x':-5},
147+
{'y':-5,'x':5},
148+
{'y':5,'x':5},
149+
{'y':5,'x':-5}
150+
];
151+
var testGSHull = new ConvexHullGrahamScan();
152+
testGSHull.anchorPoint = {'y':-5,'x':-5};
153+
testGSHull.points = [
154+
{'y' : '2', 'x' : '2'},
155+
{'y' : '-2', 'x' : '2'},
156+
{'y' : '-2', 'x' : '-2'},
157+
{'y' : '2', 'x' : '-2'},
158+
{'y' : '3', 'x' : '3'},
159+
{'y' : '-3', 'x' : '3'},
160+
{'y' : '-3', 'x' : '-3'},
161+
{'y' : '3', 'x' : '-3'},
162+
{'y' : '4', 'x' : '4'},
163+
{'y' : '-4', 'x' : '4'},
164+
{'y' : '-4', 'x' : '-4'},
165+
{'y' : '4', 'x' : '-4'},
166+
{'y' : '5', 'x' : '5'},
167+
{'y' : '-5', 'x' : '5'},
168+
{'y' : '-5', 'x' : '-5'},
169+
{'y' : '5', 'x' : '-5'}
139170
];
140171

141172
equal(testGSHull.getHull(), expectedHull, 'Check output hull is as expected.');

0 commit comments

Comments
 (0)