cubic-beziers-through-points
Version:
A function to fit fair (bending energy minimizing) cubic bezier curves through a set of given ordered points in the plane.
74 lines (55 loc) • 2.25 kB
text/typescript
/// <reference path="../chai-extensions.d.ts" />
import { ObjOrArray } from "./obj-or-array";
const { EPSILON: eps, POSITIVE_INFINITY: inf } = Number;
const abs = Math.abs;
/**
* @param ulpsOrEps If a number then 2\*\*1 means last bit, 2\*\*2 means last 2 bits, etc...
* else if an array containing a single number then 1 means 1 eps, 2 means 2 eps, etc...
*/
function closeTo(ulpsOrEps: number | number[]) {
let isUlps = true;
if (Array.isArray(ulpsOrEps)) {
isUlps = false;
ulpsOrEps = ulpsOrEps[0];
}
function check(
expected: ObjOrArray<number>,
actual: ObjOrArray<number>): boolean {
if (typeof expected === 'number') {
if (typeof actual !== 'number') { return false; }
const actual_ = actual as number;
if (expected === inf) { return actual_ === inf; }
const error = abs((isUlps ? expected : 1)*(ulpsOrEps as number)*eps);
return (
(actual_ >= expected - error) &&
(actual_ <= expected + error)
);
}
if (Array.isArray(expected)) {
if (!Array.isArray(actual)) { return false; }
if (expected.length !== actual.length) { return false; }
for (let i=0; i<expected.length; i++) {
const e = expected[i];
const a = actual[i];
if (!check(e,a)) { return false; }
}
return true;
}
if (typeof expected === 'object') {
if (typeof actual !== 'object') { return false; }
const actual_ = actual as { [key: string]: ObjOrArray<number> };
const keys = Object.keys(expected);
const keysE = Object.keys(actual_);
if (keys.length !== keysE.length) { return false; }
for (let key of keys) {
const e = expected[key];
const a = actual_[key];
if (!check(e,a)) { return false; }
}
return true;
}
return false; // unsupported types
}
return check;
}
export { closeTo }