diff --git a/build/geometric.js b/build/geometric.js index ef2b4f4..cdd4896 100644 --- a/build/geometric.js +++ b/build/geometric.js @@ -14,42 +14,109 @@ return angleToDegrees(Math.atan2(line[1][1] - line[0][1], line[1][0] - line[0][0])); } - // Calculates the distance between the endpoints of a line segment. - function lineLength(line) { - return Math.sqrt(Math.pow(line[1][0] - line[0][0], 2) + Math.pow(line[1][1] - line[0][1], 2)); + function _slicedToArray(arr, i) { + return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); } - // Converts degrees to radians. - function angleToRadians(angle) { - return angle / 180 * Math.PI; + function _toConsumableArray(arr) { + return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); } - function pointTranslate(point) { - var angle = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; - var distance = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; - var r = angleToRadians(angle); - return [point[0] + distance * Math.cos(r), point[1] + distance * Math.sin(r)]; + function _arrayWithoutHoles(arr) { + if (Array.isArray(arr)) { + for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; + + return arr2; + } + } + + function _arrayWithHoles(arr) { + if (Array.isArray(arr)) return arr; + } + + function _iterableToArray(iter) { + if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); + } + + function _iterableToArrayLimit(arr, i) { + var _arr = []; + var _n = true; + var _d = false; + var _e = undefined; + + try { + for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { + _arr.push(_s.value); + + if (i && _arr.length === i) break; + } + } catch (err) { + _d = true; + _e = err; + } finally { + try { + if (!_n && _i["return"] != null) _i["return"](); + } finally { + if (_d) throw _e; + } + } + + return _arr; } + function _nonIterableSpread() { + throw new TypeError("Invalid attempt to spread non-iterable instance"); + } + + function _nonIterableRest() { + throw new TypeError("Invalid attempt to destructure non-iterable instance"); + } + + // Returns an interpolator function given a line [a, b]. // The returned interpolator function takes a single argument t, where t is a number ranging from 0 to 1; // a value of 0 returns a, while a value of 1 returns b. // Intermediate values interpolate from start to end along the line segment. // By default, the returned interpolator will output points outside of the line segment if t is less than 0 or greater than 1. // You can pass an optional boolean indicating whether to the returned point to inside of the line segment, // even if t is greater than 1 or less then 0. - function lineInterpolate(line) { var clamp = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; - if (clamp) { - return function (t) { - return t <= 0 ? line[0] : t >= 1 ? line[1] : pointTranslate(line[0], lineAngle(line), lineLength(line) * t); - }; - } else { - return function (t) { - return t === 0 ? line[0] : t === 1 ? line[1] : pointTranslate(line[0], lineAngle(line), lineLength(line) * t); - }; - } + var _line = _slicedToArray(line, 2), + _line$ = _slicedToArray(_line[0], 2), + x1 = _line$[0], + y1 = _line$[1], + _line$2 = _slicedToArray(_line[1], 2), + x2 = _line$2[0], + y2 = _line$2[1]; + + var x = scale([0, 1], [x1, x2]); + var y = scale([0, 1], [y1, y2]); + return function (t) { + var t0 = clamp ? t < 0 ? 0 : t > 1 ? 1 : t : t; + return [x(t0), y(t0)]; + }; + } // A linear scale + + function scale(_ref, _ref2) { + var _ref3 = _slicedToArray(_ref, 2), + d0 = _ref3[0], + d1 = _ref3[1]; + + var _ref4 = _slicedToArray(_ref2, 2), + r0 = _ref4[0], + r1 = _ref4[1]; + + var dx = d1 - d0; + var rx = r1 - r0; + return function (v) { + return rx * ((v - d0) / dx) + r0; + }; + } + + // Calculates the distance between the endpoints of a line segment. + function lineLength(line) { + return Math.sqrt(Math.pow(line[1][0] - line[0][0], 2) + Math.pow(line[1][1] - line[0][1], 2)); } // Calculates the midpoint of a line segment. @@ -57,6 +124,11 @@ return [(line[0][0] + line[1][0]) / 2, (line[0][1] + line[1][1]) / 2]; } + // Converts degrees to radians. + function angleToRadians(angle) { + return angle / 180 * Math.PI; + } + function pointRotate(point, angle, origin) { var r = angleToRadians(angle || 0); @@ -87,6 +159,13 @@ }); } + function pointTranslate(point) { + var angle = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; + var distance = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; + var r = angleToRadians(angle); + return [point[0] + distance * Math.cos(r), point[1] + distance * Math.sin(r)]; + } + function lineTranslate(line, angle, distance) { return line.map(function (point) { return pointTranslate(point, angle, distance); @@ -199,64 +278,6 @@ return lower.concat(upper); } - function _slicedToArray(arr, i) { - return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); - } - - function _toConsumableArray(arr) { - return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); - } - - function _arrayWithoutHoles(arr) { - if (Array.isArray(arr)) { - for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; - - return arr2; - } - } - - function _arrayWithHoles(arr) { - if (Array.isArray(arr)) return arr; - } - - function _iterableToArray(iter) { - if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); - } - - function _iterableToArrayLimit(arr, i) { - var _arr = []; - var _n = true; - var _d = false; - var _e = undefined; - - try { - for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { - _arr.push(_s.value); - - if (i && _arr.length === i) break; - } - } catch (err) { - _d = true; - _e = err; - } finally { - try { - if (!_n && _i["return"] != null) _i["return"](); - } finally { - if (_d) throw _e; - } - } - - return _arr; - } - - function _nonIterableSpread() { - throw new TypeError("Invalid attempt to spread non-iterable instance"); - } - - function _nonIterableRest() { - throw new TypeError("Invalid attempt to destructure non-iterable instance"); - } - // Closes a polygon if it's not closed already. Does not modify input polygon. function close(polygon) { return isClosed(polygon) ? polygon : [].concat(_toConsumableArray(polygon), [polygon[0]]); diff --git a/build/geometric.min.js b/build/geometric.min.js deleted file mode 100644 index 67b0bf7..0000000 --- a/build/geometric.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(n,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t(n.geometric={})}(this,function(n){"use strict";function t(n){return 180*n/Math.PI}function r(n){return t(Math.atan2(n[1][1]-n[0][1],n[1][0]-n[0][0]))}function e(n){return Math.sqrt(Math.pow(n[1][0]-n[0][0],2)+Math.pow(n[1][1]-n[0][1],2))}function o(n){return n/180*Math.PI}function i(n){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0,e=o(t);return[n[0]+r*Math.cos(e),n[1]+r*Math.sin(e)]}function a(n){return arguments.length>1&&void 0!==arguments[1]&&arguments[1]?function(t){return t<=0?n[0]:t>=1?n[1]:i(n[0],r(n),e(n)*t)}:function(t){return 0===t?n[0]:1===t?n[1]:i(n[0],r(n),e(n)*t)}}function u(n){return[(n[0][0]+n[1][0])/2,(n[0][1]+n[1][1])/2]}function l(n,t,r){var e=o(t||0);return!r||0===r[0]&&0===r[1]?f(n,e):f(n.map(function(n,t){return n-r[t]}),e).map(function(n,t){return n+r[t]})}function f(n,t){return[n[0]*Math.cos(t)-n[1]*Math.sin(t),n[0]*Math.sin(t)+n[1]*Math.cos(t)]}function c(n,t,r){return n.map(function(e){return l(e,t,r||u(n))})}function h(n,t,r){return n.map(function(n){return i(n,t,r)})}function g(n){for(var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1],r=0,e=0,o=n.length;er&&(r=f),co&&(o=c))}return i?[[t,e],[r,o]]:null}function p(n){for(var t=0,r=0,e=0,o=n.length,i=0;i=2&&s(r[r.length-2],r[r.length-1],t[e])<=0;)r.pop();r.push(t[e])}for(var o=[],i=t.length-1;i>=0;i--){for(;o.length>=2&&s(o[o.length-2],o[o.length-1],t[i])<=0;)o.pop();o.push(t[i])}return o.pop(),r.pop(),r.concat(o)}function d(n,t){return b(n)||A(n,t)||P()}function M(n){return m(n)||I(n)||w()}function m(n){if(Array.isArray(n)){for(var t=0,r=new Array(n.length);t=1)return o[o.length-1];for(var a=L(o)*t,u=[],l=0,f=0;f0&&void 0!==arguments[0]?arguments[0]:3,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:100,o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[0,0],i=Math.sqrt(t/Math.PI),a=Array.from({length:n},function(){return 2*i*Math.random()}),u=Array.from({length:n},function(){return 2*i*Math.random()});a.sort(function(n,t){return n-t}),u.sort(function(n,t){return n-t});var l=O(a,a[0],a[a.length-1]),f=O(u,u[0],u[u.length-1]);E(f);var c=[],h=0,v=0,s=(l.map(function(n,t){return[n,f[t]]}).sort(function(n,t){return Math.atan2(t[1],t[0])-Math.atan2(n[1],n[0])}).forEach(function(n){h+=1*n[0],v+=1*n[1],c.push([h,v])}),p(c));return j(x(c,t/g(c)),r([s,o]),e([s,o]))}function O(n,t,r){for(var e=t,o=t,i=[],a=1;a.5?(i.push(u-e),e=u):(i.push(o-u),o=u)}return i.push(r-e),i.push(o-r),i}function E(n){for(var t=n.length-1;t>0;t--){var r=Math.floor(Math.random()*(t+1)),e=[n[r],n[t]];n[t]=e[0],n[r]=e[1]}}function F(n){for(var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:1,r=v(n),e=d(r,2),o=d(e[0],2),i=o[0],u=(o[1],d(e[1],2)),l=u[0],f=(u[1],[]),c=0,h=n.length;c1&&void 0!==arguments[1]?arguments[1]:1,r=v(n),e=d(r,2),o=d(e[0],2),i=(o[0],o[1]),u=d(e[1],2),l=(u[0],u[1]),f=[],c=0,h=n.length;c0&&void 0!==arguments[0]?arguments[0]:3,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:100,o=arguments.length>2?arguments[2]:void 0,a=[],u=[0,0],l=[0,0],f=0,c=0;c1&&void 0!==arguments[1]?arguments[1]:"ccw";if(n.length<3)return null;var r=n.slice().reverse(),e=g(n,!0)>0;return"cw"===t||"clockwise"===t?e?n:r:e?r:n}function H(n){return n[1][1]>n[0][1]?n:[n[1],n[0]]}function z(n,t){var r=H(t);return s(n,r[1],r[0])<0}function G(n,t){var r=H(t);return s(n,r[1],r[0])>0}function J(n,t){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0,o=e(t);return K(n,t,r)&&e([t[0],n])<=o&&e([t[1],n])<=o}function K(n,t){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;return Math.abs(s(n,t[0],t[1]))<=r}function N(n,t){var r=d(n,2),e=d(r[0],2),o=e[0],i=e[1],a=d(r[1],2),u=a[0],l=a[1],f=d(t,2),c=d(f[0],2),h=c[0],g=c[1],v=d(f[1],2),p=v[0],s=v[1];if(o===h&&i===g)return!0;if(u===p&&l===s)return!0;if(J(n[0],t)||J(n[1],t))return!0;if(J(t[0],n)||J(t[1],n))return!0;var y=(s-g)*(u-o)-(p-h)*(l-i);if(0===y)return!1;var M=i-g,m=o-h,b=(p-h)*M-(s-g)*m,I=(u-o)*M-(l-i)*m,A=b/y,w=I/y;return A>0&&A<1&&w>0&&w<1}function Q(n,t){for(var r=!1,e=R(t),o=0,i=e.length-1;oe!=c>e&&r<(f-u)*(e-l)/(c-l)+u&&(o=!o)}return o}function V(n,t){for(var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0,e=!1,o=R(t),i=0,a=o.length-1;i=360?r-360:r<0?r+360:r}n.lineAngle=r,n.lineInterpolate=a,n.lineLength=e,n.lineMidpoint=u,n.lineRotate=c,n.lineTranslate=h,n.pointRotate=l,n.pointTranslate=i,n.polygonArea=g,n.polygonBounds=v,n.polygonCentroid=p,n.polygonHull=y,n.polygonInterpolate=S,n.polygonLength=L,n.polygonMean=T,n.polygonRandom=q,n.polygonReflectX=F,n.polygonReflectY=W,n.polygonRegular=X,n.polygonRotate=Y,n.polygonScale=_,n.polygonScaleArea=x,n.polygonScaleX=B,n.polygonScaleY=C,n.polygonTranslate=j,n.polygonWind=D,n.lineIntersectsLine=N,n.lineIntersectsPolygon=Q,n.pointInPolygon=U,n.pointOnPolygon=V,n.pointLeftofLine=z,n.pointRightofLine=G,n.pointOnLine=J,n.pointWithLine=K,n.polygonInPolygon=Z,n.polygonIntersectsPolygon=$,n.angleReflect=nn,n.angleToDegrees=t,n.angleToRadians=o,Object.defineProperty(n,"__esModule",{value:!0})}); \ No newline at end of file diff --git a/build/geometric.zip b/build/geometric.zip deleted file mode 100644 index d15c426..0000000 Binary files a/build/geometric.zip and /dev/null differ diff --git a/src/lines/lineInterpolate.js b/src/lines/lineInterpolate.js index 96e8964..17c8472 100644 --- a/src/lines/lineInterpolate.js +++ b/src/lines/lineInterpolate.js @@ -1,7 +1,3 @@ -import { lineAngle } from "./lineAngle"; -import { lineLength } from "./lineLength"; -import { pointTranslate } from "../points/pointTranslate"; - // Returns an interpolator function given a line [a, b]. // The returned interpolator function takes a single argument t, where t is a number ranging from 0 to 1; // a value of 0 returns a, while a value of 1 returns b. @@ -10,10 +6,18 @@ import { pointTranslate } from "../points/pointTranslate"; // You can pass an optional boolean indicating whether to the returned point to inside of the line segment, // even if t is greater than 1 or less then 0. export function lineInterpolate(line, clamp = false){ - if (clamp) { - return t => t <= 0 ? line[0] : t >= 1 ? line[1] : pointTranslate(line[0], lineAngle(line), lineLength(line) * t); - } - else { - return t => t === 0 ? line[0] : t === 1 ? line[1] : pointTranslate(line[0], lineAngle(line), lineLength(line) * t); + const [[x1, y1], [x2, y2]] = line; + const x = scale([0, 1], [x1, x2]); + const y = scale([0, 1], [y1, y2]); + return t => { + const t0 = clamp ? t < 0 ? 0 : t > 1 ? 1 : t : t; + return [x(t0), y(t0)]; } +} + +// A linear scale +function scale([d0, d1], [r0, r1]){ + const dx = d1 - d0; + const rx = r1 - r0; + return v => rx * ((v - d0) / dx) + r0; } \ No newline at end of file diff --git a/test/lineInterpolate-test.js b/test/lineInterpolate-test.js index 4d57047..f575009 100644 --- a/test/lineInterpolate-test.js +++ b/test/lineInterpolate-test.js @@ -1,28 +1,30 @@ -const tape = require("tape"), - geometric = require("../"); +const tape = require("tape"); +const geometric = require("../"); +const round = require("./utils/roundArray"); + tape("lineInterpolate(line) returns points along a line", test => { const line = [[236, 0], [708, 190]]; const interpolator = geometric.lineInterpolate(line); - test.deepEqual(interpolator(0), line[0]); - test.deepEqual(interpolator(1), line[1]); - test.deepEqual(interpolator(.1), [283.2, 19]); - test.deepEqual(interpolator(.2), [330.4, 38]); - test.deepEqual(interpolator(.3), [377.6, 56.99999999999999]); - test.deepEqual(interpolator(.4), [424.8, 76]); - test.deepEqual(interpolator(.5), [472, 94.99999999999999]); - test.deepEqual(interpolator(.6), [519.2, 113.99999999999999]); - test.deepEqual(interpolator(.7), [566.4, 132.99999999999997]); - test.deepEqual(interpolator(.8), [613.6, 152]); - test.deepEqual(interpolator(.9), [660.8, 171]); + test.deepEqual(round(interpolator(0), 1), line[0]); + test.deepEqual(round(interpolator(1), 1), line[1]); + test.deepEqual(round(interpolator(.1), 1), [283.2, 19]); + test.deepEqual(round(interpolator(.2), 1), [330.4, 38]); + test.deepEqual(round(interpolator(.3), 1), [377.6, 57]); + test.deepEqual(round(interpolator(.4), 1), [424.8, 76]); + test.deepEqual(round(interpolator(.5), 1), [472, 95]); + test.deepEqual(round(interpolator(.6), 1), [519.2, 114]); + test.deepEqual(round(interpolator(.7), 1), [566.4, 133]); + test.deepEqual(round(interpolator(.8), 1), [613.6, 152]); + test.deepEqual(round(interpolator(.9), 1), [660.8, 171]); test.end(); }); tape("lineInterpolate(line) returns points outside the line segment if clamp is false", test => { const line = [[236, 0], [708, 190]]; const interpolator = geometric.lineInterpolate(line); - test.deepEqual(interpolator(-0.1), [188.8, -19]); - test.deepEqual(interpolator(1.1), [755.2, 209]); + test.deepEqual(round(interpolator(-0.1), 1), [188.8, -19]); + test.deepEqual(round(interpolator(1.1), 1), [755.2, 209]); test.end(); }); diff --git a/test/polygonReflectX-test.js b/test/polygonReflectX-test.js index b36b728..96a1566 100644 --- a/test/polygonReflectX-test.js +++ b/test/polygonReflectX-test.js @@ -8,7 +8,7 @@ tape("polygonReflectX(p) reflects a polygon horizontally", (test) => { test.deepEqual(geometric.polygonReflectX(polygon, 1), [[646,200],[606,200],[606,160],[586,103],[578,100],[576,70],[574,100],[566,103],[546,160],[546,200],[476,200],[506,320],[676,320]]); test.deepEqual(geometric.polygonReflectX(polygon, 0.5), [[576,200],[576,200],[576,160],[576,103],[576,100],[576,70],[576,100],[576,103],[576,160],[576,200],[576,200],[576,320],[576,320]]); test.deepEqual(geometric.polygonReflectX(polygon, 0.25), [[541,200],[561,200],[561,160],[571,103],[575,100],[576,70],[577,100],[581,103],[591,160],[591,200],[626,200],[611,320],[526,320]]); - test.deepEqual(geometric.polygonReflectX(polygon, 0.75), [[611,200],[591,200],[591,160],[581,103],[577,100],[576,70],[575,100],[571,103],[561,160],[561,200],[526,200.00000000000003],[541,320],[626,320]]); + test.deepEqual(geometric.polygonReflectX(polygon, 0.75), [[611,200],[591,200],[591,160],[581,103],[577,100],[576,70],[575,100],[571,103],[561,160],[561,200],[526,200],[541,320],[626,320]]); test.deepEqual(geometric.polygonReflectX(polygon, -1), polygon); test.deepEqual(geometric.polygonReflectX(polygon, 2), [[646,200],[606,200],[606,160],[586,103],[578,100],[576,70],[574,100],[566,103],[546,160],[546,200],[476,200],[506,320],[676,320]]); diff --git a/test/utils/roundArray.js b/test/utils/roundArray.js new file mode 100644 index 0000000..f65b5d1 --- /dev/null +++ b/test/utils/roundArray.js @@ -0,0 +1,3 @@ +module.exports = function roundArray(array, precision){ + return array.map(n => +n.toFixed(precision)); +} \ No newline at end of file