diff options
author | Pirate Praveen <praveen@debian.org> | 2017-10-10 12:57:12 +0530 |
---|---|---|
committer | Pirate Praveen <praveen@debian.org> | 2017-10-10 12:57:12 +0530 |
commit | b7f17f1cee3fd2d63ca365385ce5eba10995c338 (patch) | |
tree | 25a724927abedf46ce386fbda3ced667e6f0cc4e |
Import Upstream version 1.0.3
-rw-r--r-- | .eslintrc | 8 | ||||
-rw-r--r-- | .gitignore | 5 | ||||
-rw-r--r-- | .npmignore | 3 | ||||
-rw-r--r-- | LICENSE | 27 | ||||
-rw-r--r-- | README.md | 42 | ||||
-rw-r--r-- | d3-polygon.sublime-project | 13 | ||||
-rw-r--r-- | img/hull.png | bin | 0 -> 11243 bytes | |||
-rw-r--r-- | index.js | 5 | ||||
-rw-r--r-- | package.json | 39 | ||||
-rw-r--r-- | src/area.js | 15 | ||||
-rw-r--r-- | src/centroid.js | 20 | ||||
-rw-r--r-- | src/contains.js | 16 | ||||
-rw-r--r-- | src/cross.js | 7 | ||||
-rw-r--r-- | src/hull.js | 49 | ||||
-rw-r--r-- | src/length.js | 23 | ||||
-rw-r--r-- | test/area-test.js | 37 | ||||
-rw-r--r-- | test/centroid-test.js | 37 | ||||
-rw-r--r-- | test/contains-test.js | 28 | ||||
-rw-r--r-- | test/hull-test.js | 33 | ||||
-rw-r--r-- | test/length-test.js | 24 |
20 files changed, 431 insertions, 0 deletions
diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..d1da9f4 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,8 @@ +parserOptions: + sourceType: module + +extends: + "eslint:recommended" + +rules: + no-cond-assign: 0 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..75704c6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +*.sublime-workspace +.DS_Store +build/ +node_modules +npm-debug.log diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..dfb770e --- /dev/null +++ b/.npmignore @@ -0,0 +1,3 @@ +*.sublime-* +build/*.zip +test/ @@ -0,0 +1,27 @@ +Copyright 2010-2016 Mike Bostock +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the author nor the names of contributors may be used to + endorse or promote products derived from this software without specific prior + written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..770877a --- /dev/null +++ b/README.md @@ -0,0 +1,42 @@ +# d3-polygon + +This module provides a few basic geometric operations for two-dimensional polygons. Each polygon is represented as an array of two-element arrays [[<i>x1</i>, <i>y1</i>], [<i>x2</i>, <i>y2</i>], …], and may either be closed (wherein the first and last point are the same) or open (wherein they are not). Typically polygons are in counterclockwise order, assuming a coordinate system where the origin ⟨0,0⟩ is in the top-left corner. + +## Installing + +If you use NPM, `npm install d3-polygon`. Otherwise, download the [latest release](https://github.com/d3/d3-polygon/releases/latest). You can also load directly from [d3js.org](https://d3js.org), either as a [standalone library](https://d3js.org/d3-polygon.v1.min.js) or as part of [D3 4.0](https://github.com/d3/d3). AMD, CommonJS, and vanilla environments are supported. In vanilla, a `d3` global is exported: + +```html +<script src="https://d3js.org/d3-polygon.v1.min.js"></script> +<script> + +var hull = d3.polygonHull(points); + +</script> +``` + +[Try d3-polygon in your browser.](https://tonicdev.com/npm/d3-polygon) + +## API Reference + +<a href="#polygonArea" name="polygonArea">#</a> d3.<b>polygonArea</b>(<i>polygon</i>) [<>](https://github.com/d3/d3-polygon/blob/master/src/area.js#L1 "Source Code") + +Returns the signed area of the specified *polygon*. If the vertices of the polygon are in counterclockwise order (assuming a coordinate system where the origin ⟨0,0⟩ is in the top-left corner), the returned area is positive; otherwise it is negative, or zero. + +<a href="#polygonCentroid" name="polygonCentroid">#</a> d3.<b>polygonCentroid</b>(<i>polygon</i>) [<>](https://github.com/d3/d3-polygon/blob/master/src/centroid.js#L1 "Source Code") + +Returns the [centroid](https://en.wikipedia.org/wiki/Centroid) of the specified *polygon*. + +<a href="#polygonHull" name="polygonHull">#</a> d3.<b>polygonHull</b>(<i>points</i>) [<>](https://github.com/d3/d3-polygon/blob/master/src/hull.js#L23 "Source Code") + +<a href="http://bl.ocks.org/mbostock/6f14f7b7f267a85f7cdc"><img src="https://raw.githubusercontent.com/d3/d3-polygon/master/img/hull.png" width="250" height="250"></a> + +Returns the [convex hull](https://en.wikipedia.org/wiki/Convex_hull) of the specified *points* using [Andrew’s monotone chain algorithm](http://en.wikibooks.org/wiki/Algorithm_Implementation/Geometry/Convex_hull/Monotone_chain). The returned hull is represented as an array containing a subset of the input *points* arranged in counterclockwise order. Returns null if *points* has fewer than three elements. + +<a href="#polygonContains" name="polygonContains">#</a> d3.<b>polygonContains</b>(<i>polygon</i>, <i>point</i>) [<>](https://github.com/d3/d3-polygon/blob/master/src/contains.js#L1 "Source Code") + +Returns true if and only if the specified *point* is [inside the specified *polygon*](https://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html). + +<a href="#polygonLength" name="polygonLength">#</a> d3.<b>polygonLength</b>(<i>polygon</i>) [<>](https://github.com/d3/d3-polygon/blob/master/src/length.js#L1 "Source Code") + +Returns the length of the perimeter of the specified *polygon*. diff --git a/d3-polygon.sublime-project b/d3-polygon.sublime-project new file mode 100644 index 0000000..6e0c2d4 --- /dev/null +++ b/d3-polygon.sublime-project @@ -0,0 +1,13 @@ +{ + "folders": [ + { + "path": ".", + "file_exclude_patterns": [ + "*.sublime-workspace" + ], + "folder_exclude_patterns": [ + "build" + ] + } + ] +} diff --git a/img/hull.png b/img/hull.png Binary files differnew file mode 100644 index 0000000..d95ab60 --- /dev/null +++ b/img/hull.png diff --git a/index.js b/index.js new file mode 100644 index 0000000..ffabecc --- /dev/null +++ b/index.js @@ -0,0 +1,5 @@ +export {default as polygonArea} from "./src/area"; +export {default as polygonCentroid} from "./src/centroid"; +export {default as polygonHull} from "./src/hull"; +export {default as polygonContains} from "./src/contains"; +export {default as polygonLength} from "./src/length"; diff --git a/package.json b/package.json new file mode 100644 index 0000000..5b91217 --- /dev/null +++ b/package.json @@ -0,0 +1,39 @@ +{ + "name": "d3-polygon", + "version": "1.0.3", + "description": "Operations for two-dimensional polygons.", + "keywords": [ + "d3", + "d3-module", + "polygon", + "hull", + "geometry", + "graphics" + ], + "homepage": "https://d3js.org/d3-polygon/", + "license": "BSD-3-Clause", + "author": { + "name": "Mike Bostock", + "url": "http://bost.ocks.org/mike" + }, + "main": "build/d3-polygon.js", + "module": "index", + "jsnext:main": "index", + "repository": { + "type": "git", + "url": "https://github.com/d3/d3-polygon.git" + }, + "scripts": { + "pretest": "rm -rf build && mkdir build && rollup --banner \"$(preamble)\" -f umd -n d3 -o build/d3-polygon.js -- index.js", + "test": "tape 'test/**/*-test.js' && eslint index.js src", + "prepublish": "npm run test && uglifyjs --preamble \"$(preamble)\" build/d3-polygon.js -c -m -o build/d3-polygon.min.js", + "postpublish": "git push && git push --tags && cd ../d3.github.com && git pull && cp ../d3-polygon/build/d3-polygon.js d3-polygon.v1.js && cp ../d3-polygon/build/d3-polygon.min.js d3-polygon.v1.min.js && git add d3-polygon.v1.js d3-polygon.v1.min.js && git commit -m \"d3-polygon ${npm_package_version}\" && git push && cd - && zip -j build/d3-polygon.zip -- LICENSE README.md build/d3-polygon.js build/d3-polygon.min.js" + }, + "devDependencies": { + "eslint": "3", + "package-preamble": "0.0", + "rollup": "0.41", + "tape": "4", + "uglify-js": "^2.8.11" + } +} diff --git a/src/area.js b/src/area.js new file mode 100644 index 0000000..109efb4 --- /dev/null +++ b/src/area.js @@ -0,0 +1,15 @@ +export default function(polygon) { + var i = -1, + n = polygon.length, + a, + b = polygon[n - 1], + area = 0; + + while (++i < n) { + a = b; + b = polygon[i]; + area += a[1] * b[0] - a[0] * b[1]; + } + + return area / 2; +} diff --git a/src/centroid.js b/src/centroid.js new file mode 100644 index 0000000..6c8ece1 --- /dev/null +++ b/src/centroid.js @@ -0,0 +1,20 @@ +export default function(polygon) { + var i = -1, + n = polygon.length, + x = 0, + y = 0, + a, + b = polygon[n - 1], + c, + k = 0; + + while (++i < n) { + a = b; + b = polygon[i]; + k += c = a[0] * b[1] - b[0] * a[1]; + x += (a[0] + b[0]) * c; + y += (a[1] + b[1]) * c; + } + + return k *= 3, [x / k, y / k]; +} diff --git a/src/contains.js b/src/contains.js new file mode 100644 index 0000000..a0beabc --- /dev/null +++ b/src/contains.js @@ -0,0 +1,16 @@ +export default function(polygon, point) { + var n = polygon.length, + p = polygon[n - 1], + x = point[0], y = point[1], + x0 = p[0], y0 = p[1], + x1, y1, + inside = false; + + for (var i = 0; i < n; ++i) { + p = polygon[i], x1 = p[0], y1 = p[1]; + if (((y1 > y) !== (y0 > y)) && (x < (x0 - x1) * (y - y1) / (y0 - y1) + x1)) inside = !inside; + x0 = x1, y0 = y1; + } + + return inside; +} diff --git a/src/cross.js b/src/cross.js new file mode 100644 index 0000000..11a6df0 --- /dev/null +++ b/src/cross.js @@ -0,0 +1,7 @@ +// Returns the 2D cross product of AB and AC vectors, i.e., the z-component of +// the 3D cross product in a quadrant I Cartesian coordinate system (+x is +// right, +y is up). Returns a positive value if ABC is counter-clockwise, +// negative if clockwise, and zero if the points are collinear. +export default function(a, b, c) { + return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]); +} diff --git a/src/hull.js b/src/hull.js new file mode 100644 index 0000000..7a8f957 --- /dev/null +++ b/src/hull.js @@ -0,0 +1,49 @@ +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; +} diff --git a/src/length.js b/src/length.js new file mode 100644 index 0000000..79a90b8 --- /dev/null +++ b/src/length.js @@ -0,0 +1,23 @@ +export default function(polygon) { + var i = -1, + n = polygon.length, + b = polygon[n - 1], + xa, + ya, + xb = b[0], + yb = b[1], + perimeter = 0; + + while (++i < n) { + xa = xb; + ya = yb; + b = polygon[i]; + xb = b[0]; + yb = b[1]; + xa -= xb; + ya -= yb; + perimeter += Math.sqrt(xa * xa + ya * ya); + } + + return perimeter; +} diff --git a/test/area-test.js b/test/area-test.js new file mode 100644 index 0000000..55f607e --- /dev/null +++ b/test/area-test.js @@ -0,0 +1,37 @@ +var tape = require("tape"), + polygon = require("../"); + +tape("polygonArea(polygon) returns the expected value for closed counterclockwise polygons", function(test) { + test.equal(polygon.polygonArea([[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]]), 1); + test.end(); +}); + +tape("polygonArea(polygon) returns the expected value for closed clockwise polygons", function(test) { + test.equal(polygon.polygonArea([[0, 0], [1, 0], [1, 1], [0, 1], [0, 0]]), -1); + test.equal(polygon.polygonArea([[1, 1], [3, 2], [2, 3], [1, 1]]), -1.5); + test.end(); +}); + +tape("polygonArea(polygon) returns the expected value for open counterclockwise polygons", function(test) { + test.equal(polygon.polygonArea([[0, 0], [0, 1], [1, 1], [1, 0]]), 1); + test.end(); +}); + +tape("polygonArea(polygon) returns the expected value for open clockwise polygons", function(test) { + test.equal(polygon.polygonArea([[0, 0], [1, 0], [1, 1], [0, 1]]), -1); + test.equal(polygon.polygonArea([[1, 1], [3, 2], [2, 3]]), -1.5); + test.end(); +}); + +tape("polygonArea(polygon) returns the expected value for a very large polygon", function(test) { + var start = 0, + stop = 1e8, + step = 1e4, + points = []; + for (var value = 0; value < stop; value += step) points.push([0, value]); + for (var value = 0; value < stop; value += step) points.push([value, stop]); + for (var value = stop - step; value >= 0; value -= step) points.push([stop, value]); + for (var value = stop - step; value >= 0; value -= step) points.push([value, 0]); + test.equal(polygon.polygonArea(points), 1e16 - 5e7); + test.end(); +}); diff --git a/test/centroid-test.js b/test/centroid-test.js new file mode 100644 index 0000000..1d16e69 --- /dev/null +++ b/test/centroid-test.js @@ -0,0 +1,37 @@ +var tape = require("tape"), + polygon = require("../"); + +tape("polygonCentroid(points) returns the expected value for closed counterclockwise polygons", function(test) { + test.deepEqual(polygon.polygonCentroid([[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]]), [.5, .5]); + test.end(); +}); + +tape("polygonCentroid(points) returns the expected value for closed clockwise polygons", function(test) { + test.deepEqual(polygon.polygonCentroid([[0, 0], [1, 0], [1, 1], [0, 1], [0, 0]]), [.5, .5]); + test.deepEqual(polygon.polygonCentroid([[1, 1], [3, 2], [2, 3], [1, 1]]), [2, 2]); + test.end(); +}); + +tape("polygonCentroid(points) returns the expected value for open counterclockwise polygons", function(test) { + test.deepEqual(polygon.polygonCentroid([[0, 0], [0, 1], [1, 1], [1, 0]]), [.5, .5]); + test.end(); +}); + +tape("polygonCentroid(points) returns the expected value for closed counterclockwise polygons", function(test) { + test.deepEqual(polygon.polygonCentroid([[0, 0], [1, 0], [1, 1], [0, 1]]), [.5, .5]); + test.deepEqual(polygon.polygonCentroid([[1, 1], [3, 2], [2, 3]]), [2, 2]); + test.end(); +}); + +tape("polygonCentroid(polygon) returns the expected value for a very large polygon", function(test) { + var start = 0, + stop = 1e8, + step = 1e4, + points = []; + for (var value = 0; value < stop; value += step) points.push([0, value]); + for (var value = 0; value < stop; value += step) points.push([value, stop]); + for (var value = stop - step; value >= 0; value -= step) points.push([stop, value]); + for (var value = stop - step; value >= 0; value -= step) points.push([value, 0]); + test.deepEqual(polygon.polygonCentroid(points), [49999999.75000187, 49999999.75001216]); + test.end(); +}); diff --git a/test/contains-test.js b/test/contains-test.js new file mode 100644 index 0000000..0f3315d --- /dev/null +++ b/test/contains-test.js @@ -0,0 +1,28 @@ +var tape = require("tape"), + polygon = require("../"); + +tape("polygonContains(polygon, point) returns the expected value for closed counterclockwise polygons", function(test) { + test.equal(polygon.polygonContains([[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]], [0.5, 0.5]), true); + test.equal(polygon.polygonContains([[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]], [1.5, 0.5]), false); + test.equal(polygon.polygonContains([[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]], [-0.5, 0.5]), false); + test.equal(polygon.polygonContains([[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]], [0.5, 1.5]), false); + test.equal(polygon.polygonContains([[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]], [0.5, -0.5]), false); + test.end(); +}); + +tape("polygonContains(polygon, point) returns the expected value for closed clockwise polygons", function(test) { + test.equal(polygon.polygonContains([[0, 0], [1, 0], [1, 1], [0, 1], [0, 0]], [0.5, 0.5]), true); + test.equal(polygon.polygonContains([[1, 1], [3, 2], [2, 3], [1, 1]], [1.5, 1.5]), true); + test.end(); +}); + +tape("polygonContains(polygon, point) returns the expected value for open counterclockwise polygons", function(test) { + test.equal(polygon.polygonContains([[0, 0], [0, 1], [1, 1], [1, 0]], [0.5, 0.5]), true); + test.end(); +}); + +tape("polygonContains(polygon, point) returns the expected value for open clockwise polygons", function(test) { + test.equal(polygon.polygonContains([[0, 0], [1, 0], [1, 1], [0, 1]], [0.5, 0.5]), true); + test.equal(polygon.polygonContains([[1, 1], [3, 2], [2, 3]], [1.5, 1.5]), true); + test.end(); +}); diff --git a/test/hull-test.js b/test/hull-test.js new file mode 100644 index 0000000..73b834b --- /dev/null +++ b/test/hull-test.js @@ -0,0 +1,33 @@ +var tape = require("tape"), + polygon = require("../"); + +tape("polygonHull(points) returns null if points has fewer than three elements", function(test) { + test.equal(polygon.polygonHull([]), null); + test.equal(polygon.polygonHull([[0, 1]]), null); + test.equal(polygon.polygonHull([[0, 1], [1, 0]]), null); + test.end(); +}); + +tape("polygonHull(points) returns the convex hull of the specified points", function(test) { + test.deepEqual(polygon.polygonHull([[200, 200], [760, 300], [500, 500]]), [[760, 300], [200, 200], [500, 500]]); + test.deepEqual(polygon.polygonHull([[200, 200], [760, 300], [500, 500], [400, 400]]), [[760, 300], [200, 200], [500, 500]]); + test.end(); +}); + +tape("polygonHull(points) handles points with duplicate ordinates", function(test) { + test.deepEqual(polygon.polygonHull([[-10, -10], [10, 10], [10, -10], [-10, 10]]), [[10, 10], [10, -10], [-10, -10], [-10, 10]]); + test.end(); +}); + +tape("polygonHull(points) handles overlapping upper and lower hulls", function(test) { + test.deepEqual(polygon.polygonHull([[0, -10], [0, 10], [0, 0], [10, 0], [-10, 0]]), [[10, 0], [0, -10], [-10, 0], [0, 10]]); + test.end(); +}); + +// Cases below taken from http://uva.onlinejudge.org/external/6/681.html +tape("polygonHull(points) handles various non-trivial hulls", function(test) { + test.deepEqual(polygon.polygonHull([[60, 20], [250, 140], [180, 170], [79, 140], [50, 60], [60, 20]]), [[250, 140], [60, 20], [50, 60], [79, 140], [180, 170]]); + test.deepEqual(polygon.polygonHull([[50, 60], [60, 20], [70, 45], [100, 70], [125, 90], [200, 113], [250, 140], [180, 170], [105, 140], [79, 140], [60, 85], [50, 60]]), [[250, 140], [60, 20], [50, 60], [79, 140], [180, 170]]); + test.deepEqual(polygon.polygonHull([[30, 30], [50, 60], [60, 20], [70, 45], [86, 39], [112, 60], [200, 113], [250, 50], [300, 200], [130, 240], [76, 150], [47, 76], [36, 40], [33, 35], [30, 30]]), [[300, 200], [250, 50], [60, 20], [30, 30], [47, 76], [76, 150], [130, 240]]); + test.end(); +}); diff --git a/test/length-test.js b/test/length-test.js new file mode 100644 index 0000000..3cf1178 --- /dev/null +++ b/test/length-test.js @@ -0,0 +1,24 @@ +var tape = require("tape"), + polygon = require("../"); + +tape("polygonLength(polygon) returns the expected value for closed counterclockwise polygons", function(test) { + test.equal(polygon.polygonLength([[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]]), 4); + test.end(); +}); + +tape("polygonLength(polygon) returns the expected value for closed clockwise polygons", function(test) { + test.equal(polygon.polygonLength([[0, 0], [1, 0], [1, 1], [0, 1], [0, 0]]), 4); + test.equal(polygon.polygonLength([[1, 1], [3, 2], [2, 3], [1, 1]]), Math.sqrt(20) + Math.sqrt(2)); + test.end(); +}); + +tape("polygonLength(polygon) returns the expected value for open counterclockwise polygons", function(test) { + test.equal(polygon.polygonLength([[0, 0], [0, 1], [1, 1], [1, 0]]), 4); + test.end(); +}); + +tape("polygonLength(polygon) returns the expected value for open clockwise polygons", function(test) { + test.equal(polygon.polygonLength([[0, 0], [1, 0], [1, 1], [0, 1]]), 4); + test.equal(polygon.polygonLength([[1, 1], [3, 2], [2, 3]]), Math.sqrt(20) + Math.sqrt(2)); + test.end(); +}); |