diff --git a/README.md b/README.md index 78913bd..e8f0db5 100644 --- a/README.md +++ b/README.md @@ -137,3 +137,11 @@ require('algorithms').String; * longestCommonSubsequence * longestCommonSubstring * rabinKarp + +#### Computational Geometry +```javascript +require('algorithms/geometry'); +// or +require('algorithms').Geometry; +``` +* inPolygon \ No newline at end of file diff --git a/src/algorithms/geometry/in_polygon.js b/src/algorithms/geometry/in_polygon.js new file mode 100644 index 0000000..0c7c0c8 --- /dev/null +++ b/src/algorithms/geometry/in_polygon.js @@ -0,0 +1,26 @@ +'use strict'; + +function inPolygon(polygonPoints, testPoint){ + // Detect whether a point is inside a polygon using even-odd winding rule. + var x = testPoint.x, y = testPoint.y; + var inside = false; + var i = 0; + var j = polygonPoints.length - 1; + for (; i < polygonPoints.length; j = i++) { + var xi = polygonPoints[i].x, yi = polygonPoints[i].y; + var xj = polygonPoints[j].x, yj = polygonPoints[j].y; + if ((xi - x) * (yj - y) === (xj - x) * (yi - y) + && (xi <= x && x <= xj || xj <= x && x <= xi) + && (yi <= y && y <= yj || yj <= y && y <= yi)) { + // The test point is on the boundary. + // Simply return YES. + return true; + }; + var intersect = ((yi > y) !== (yj > y)) + && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); + if (intersect) inside = !inside; + }; + return inside; +}; + +module.exports = inPolygon; \ No newline at end of file diff --git a/src/data_structures.js b/src/data_structures.js index 43cbbc1..51965ec 100644 --- a/src/data_structures.js +++ b/src/data_structures.js @@ -14,5 +14,6 @@ module.exports = { Stack: require('./data_structures/stack'), Set: require('./data_structures/set'), DisjointSetForest: require('./data_structures/disjoint_set_forest'), - FenwickTree: require('./data_structures/fenwick_tree') + FenwickTree: require('./data_structures/fenwick_tree'), + Vector2D: require('./data_structures/vector2d') }; diff --git a/src/data_structures/vector2d.js b/src/data_structures/vector2d.js new file mode 100644 index 0000000..8842af4 --- /dev/null +++ b/src/data_structures/vector2d.js @@ -0,0 +1,24 @@ +'use strict'; + +function Vector2D(x, y){ + this.x = x; + this.y = y; +}; +Vector2D.prototype.add = function (that){ + return new Vector2D(this.x + that.x, this.y + that.y); +}; +Vector2D.prototype.minus = function (that){ + return new Vector2D(this.x - that.x, this.y - that.y); +}; +Vector2D.prototype.scale = function (amount){ + return new Vector2D(this.x * amount, this.y * amount); +}; +Vector2D.prototype.dot = function (that){ + return this.x * that.x + this.y * that.y; +}; +Vector2D.prototype.cross = function (that){ + return this.x * that.y - this.y * that.x; +}; + + +module.exports = Vector2D; \ No newline at end of file diff --git a/src/geometry.js b/src/geometry.js new file mode 100644 index 0000000..8d3338f --- /dev/null +++ b/src/geometry.js @@ -0,0 +1,6 @@ +'use strict'; + +// Graph algorithms +module.exports = { + inPolygon: require('./algorithms/geometry/in_polygon') +}; diff --git a/src/index.js b/src/index.js index 95c99fb..faacce3 100644 --- a/src/index.js +++ b/src/index.js @@ -6,7 +6,8 @@ var lib = { Math: require('./math'), Search: require('./search'), Sorting: require('./sorting'), - String: require('./string') + String: require('./string'), + Geometry: require('./geometry') }; module.exports = lib; diff --git a/src/test/algorithms/geometry/in_polygon.js b/src/test/algorithms/geometry/in_polygon.js new file mode 100644 index 0000000..558c55e --- /dev/null +++ b/src/test/algorithms/geometry/in_polygon.js @@ -0,0 +1,29 @@ +'use strict'; + +var root = require('../../../'), + inPolygon = root.Geometry.inPolygon, + Vector2D = root.DataStructures.Vector2D, + assert = require('assert'); + +describe('In-polygon detection', function (){ + it('should be able to detect if a point is inside a polygon', function (){ + var polygon = [ + new Vector2D(1, 1), + new Vector2D(1, 2), + new Vector2D(2, 2), + new Vector2D(2, 1) + ]; + assert(inPolygon(polygon, new Vector2D(1.5, 1.5))); + assert(!inPolygon(polygon, new Vector2D(5, 1))); + }); + it('should treat on-boundary points as inside', function (){ + var polygon = [ + new Vector2D(1, 1), + new Vector2D(1, 2), + new Vector2D(2, 2), + new Vector2D(2, 1) + ]; + assert(inPolygon(polygon, new Vector2D(1, 1.5))); + assert(inPolygon(polygon, new Vector2D(1.5, 2))); + }); +}); \ No newline at end of file diff --git a/src/test/data_structures/vector2d.js b/src/test/data_structures/vector2d.js new file mode 100644 index 0000000..c6e2458 --- /dev/null +++ b/src/test/data_structures/vector2d.js @@ -0,0 +1,44 @@ +'use strict'; + +var root = require('../..'), + Vector2D = root.DataStructures.Vector2D, + assert = require('assert'); + +describe('Vector 2D', function (){ + it('should be able to perform correct addition', function (){ + var p = new Vector2D(1, 2); + var q = new Vector2D(3, 4); + + var r = p.add(q); + assert.equal(r.x, 4); + assert.equal(r.y, 6); + }); + it('should be able to perform correct substraction', function (){ + var p = new Vector2D(1, 2); + var q = new Vector2D(3, 4); + + var r = p.minus(q); + assert.equal(r.x, -2); + assert.equal(r.y, -2); + }); + it('should be able to perform correct scaling', function (){ + var p = new Vector2D(1, 2); + var r = p.scale(9); + assert.equal(r.x, 9); + assert.equal(r.y, 18); + }); + it('should be able to perform correct dot product', function (){ + var p = new Vector2D(1, 2); + var q = new Vector2D(3, 4); + + var r = p.dot(q); + assert.equal(r, 11); + }); + it('should be able to perform correct scalar cross product', function (){ + var p = new Vector2D(1, 2); + var q = new Vector2D(3, 4); + + var r = p.cross(q); + assert.equal(r, -2); + }); +}); \ No newline at end of file