Skip to content

Commit

Permalink
Simplify and speed up lineInterpolate. Fixes #45
Browse files Browse the repository at this point in the history
  • Loading branch information
HarryStevens committed Jun 4, 2023
1 parent 31b6566 commit a4528ce
Show file tree
Hide file tree
Showing 7 changed files with 134 additions and 105 deletions.
179 changes: 100 additions & 79 deletions build/geometric.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,49 +14,121 @@
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.
function lineMidpoint(line) {
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);

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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]]);
Expand Down
1 change: 0 additions & 1 deletion build/geometric.min.js

This file was deleted.

Binary file removed build/geometric.zip
Binary file not shown.
22 changes: 13 additions & 9 deletions src/lines/lineInterpolate.js
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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;
}
32 changes: 17 additions & 15 deletions test/lineInterpolate-test.js
Original file line number Diff line number Diff line change
@@ -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();
});

Expand Down
Loading

0 comments on commit a4528ce

Please sign in to comment.