UNPKG

ts-math

Version:

A collection of math functions and packages written in Typescript

307 lines 9.89 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ConstrainedCubicSpline = exports.PeriodicCubicSpline = exports.NaturalCubicSpline = exports.CubicHermite = exports.Linear = exports.Curve = exports.Node = void 0; const _1 = require("."); const { hypot, pow, floor, max } = Math; class Node { constructor(x, y) { this.x = x; this.y = y; } get length() { return hypot(this.x, this.y); } } exports.Node = Node; class Curve { constructor(xs, ys) { this.xs = xs; this.ys = ys; } } exports.Curve = Curve; class Linear extends Curve { y(x) { const n = this.xs.length; if (n === 0) { return Number.NaN; } const idx = _1.indexOf(x, this.xs); if (idx === -1) { return this.ys[0]; } if (idx === n - 1) { return this.ys[n - 1]; } const x1 = this.xs[idx]; const y1 = this.ys[idx]; const x2 = this.xs[idx + 1]; const y2 = this.ys[idx + 1]; return y1 + ((x - x1) / (x2 - x1)) * (y2 - y1); } } exports.Linear = Linear; class CubicHermite extends Curve { y(x) { const n = this.xs.length; if (n === 0) { return Number.NaN; } const idx = _1.indexOf(x, this.xs); if (idx === -1) { return this.ys[0]; } if (idx === n - 1) { return this.ys[n - 1]; } const x1 = this.xs[idx]; const y1 = this.ys[idx]; const x2 = this.xs[idx + 1]; const y2 = this.ys[idx + 1]; let x0, y0; if (idx === 0) { x0 = 2 * x1 - x2; y0 = 2 * y1 - y2; } else { x0 = this.xs[idx - 1]; y0 = this.ys[idx - 1]; } let x3, y3; if (idx === n - 2) { x3 = 2 * x2 - x1; y3 = 2 * y2 - y1; } else { x3 = this.xs[idx + 2]; y3 = this.ys[idx + 2]; } const d = (x - x1) / (x2 - x1); const r0 = (((y1 - y0) * (x2 - x1)) / (x1 - x0) + ((y2 - y1) * (x1 - x0)) / (x2 - x1)) / (x2 - x0); const r1 = (((y2 - y1) * (x3 - x2)) / (x2 - x1) + ((y3 - y2) * (x2 - x1)) / (x3 - x2)) / (x3 - x1); const g = (x2 - x1) * r0 - (y2 - y1); const c = 2 * (y2 - y1) - (x2 - x1) * (r0 + r1); return y1 + d * (y2 - y1) + d * (1 - d) * g + d * d * (1 - d) * c; } } exports.CubicHermite = CubicHermite; class NaturalCubicSpline extends Curve { constructor(xs, ys) { super(xs, ys); this.calcualate(); } calcualate() { const n = this.xs.length; const hs = new Array(n - 1); const bs = new Array(n - 1); for (let i = 0; i < n - 1; i++) { const h = this.xs[i + 1] - this.xs[i]; hs[i] = h; bs[i] = (6 * (this.ys[i + 1] - this.ys[i])) / h; } const us = new Array(n - 1); const vs = new Array(n - 1); us[0] = 0; us[1] = 2 * (hs[0] + hs[1]); vs[0] = 0; vs[1] = bs[1] - bs[0]; for (let i = 2; i < n - 1; i++) { us[i] = 2 * (hs[i] + hs[i - 1]) - pow(hs[i - 1], 2) / us[i - 1]; vs[i] = bs[i] - bs[i - 1] - (hs[i - 1] * vs[i - 1]) / us[i - 1]; } const zs = new Array(n); zs[0] = 0; zs[n - 1] = 0; for (let i = n - 2; i > 0; i--) { zs[i] = (vs[i] - hs[i] * zs[i + 1]) / us[i]; } this.a = new Array(n - 1); this.b = new Array(n - 1); this.c = new Array(n - 1); for (let i = 0; i < n - 1; i++) { this.a[i] = (zs[i + 1] - zs[i]) / (6 * hs[i]); this.b[i] = zs[i] / 2; this.c[i] = (-hs[i] * zs[i + 1]) / 6 - (hs[i] * zs[i]) / 3 + (this.ys[i + 1] - this.ys[i]) / hs[i]; } } y(x) { const n = this.xs.length; if (n === 0) { return Number.NaN; } const idx = _1.indexOf(x, this.xs); if (idx === -1) { return this.ys[0]; } if (idx === n - 1) { return this.ys[n - 1]; } const x0 = this.xs[idx]; const t = x - x0; const y0 = this.ys[idx]; return y0 + t * (this.c[idx] + t * (this.b[idx] + t * this.a[idx])); } } exports.NaturalCubicSpline = NaturalCubicSpline; function mat2str(x) { const fourDec = (d) => _1.round(d, 4); const vec2str = (vs) => `[${vs.map(fourDec).join(", ")}]`; if (!Array.isArray(x)) { return String(fourDec(x)); } if (x.length > 0 && Array.isArray(x[0])) { return `[${x.map(vec2str).join(",\n")}]`; } return vec2str(x); } function addAbcEquation(aMat, bVec, colStartIndex, rowIndex, coeffs, b) { const n = bVec.length / 3; bVec[rowIndex] = b; const ar = new Array(3 * n); for (let j = 0; j < n; j++) { for (let k = 0; k < 3; k++) { let a = 0; if (j === colStartIndex) { a = coeffs[k]; } ar[3 * j + k] = a; } } aMat[rowIndex] = ar; } function addAbcEquations(aMat, bVec, startIndex, currCoeffs, nextCoeffs, bs, loop = true) { const n = bVec.length / 3; for (let i = 0; i < n - (loop ? 0 : 1); i++) { bVec[i + startIndex] = bs[i]; const ar = new Array(3 * n); for (let j = 0; j < n; j++) { for (let k = 0; k < 3; k++) { let a = 0; if (j === i) { a = currCoeffs[k]; } else if (j === (i + 1) % n) { a = nextCoeffs[k]; } ar[3 * j + k] = a; } } aMat[i + startIndex] = ar; } } // Periodic cubic spline, where start matches end. // x-values are equal to index, i.e x0=0, x1=1 etc. class PeriodicCubicSpline { constructor(ys) { this.ys = ys; this.calcualate(); } calcualate() { const n = this.ys.length; const bs = new Array(3 * n); const aa = new Array(3 * n); const zs = [...Array(n)].map(() => 0); const ds = this.ys.map((d, i, a) => a[(i + 1) % n] - d); addAbcEquations(aa, bs, 0, [1, 1, 1], [0, 0, 0], ds); addAbcEquations(aa, bs, n, [3, 2, 1], [0, 0, -1], zs); addAbcEquations(aa, bs, 2 * n, [6, 2, 0], [0, -2, 0], zs); const res = _1.numeric.solve(aa, bs); this.a = new Array(n - 1); this.b = new Array(n - 1); this.c = new Array(n - 1); for (let i = 0; i < n; i++) { this.a[i] = res[3 * i]; this.b[i] = res[3 * i + 1]; this.c[i] = res[3 * i + 2]; } // console.log(mat2str(aa), mat2str(bs), this.a, this.b, this.c); } y(x) { const n = this.ys.length; if (n === 0) { return Number.NaN; } if (n === 1) { return this.ys[0]; } const xm = x - floor(x / n) * n; const idx = floor(xm); const x0 = idx; const t = xm - x0; const y0 = this.ys[idx]; return y0 + t * (this.c[idx] + t * (this.b[idx] + t * this.a[idx])); } } exports.PeriodicCubicSpline = PeriodicCubicSpline; // Calculates constrained variants of natural cubic spline. // 1) With known first derivative in start and/or end. // 2) With symmetric (first/last interval is mirrored) start and/or end (derivative=0) // x-values are equal to index, i.e x0=0, x1=1 etc. class ConstrainedCubicSpline { constructor(ys, startDerivative = null, endDerivative = null) { this.ys = ys; this.startDerivative = startDerivative; this.endDerivative = endDerivative; this.calcualate(); } calcualate() { const n = this.ys.length - 1; const bs = new Array(3 * n); const aa = new Array(3 * n); const ds = new Array(n - 1); for (let i = 0; i < n; i++) { ds[i] = this.ys[i + 1] - this.ys[i]; } const zs = ds.map(() => 0); addAbcEquations(aa, bs, 0, [1, 1, 1], [0, 0, 0], ds, true); addAbcEquations(aa, bs, n, [3, 2, 1], [0, 0, -1], zs, false); addAbcEquations(aa, bs, 2 * n - 1, [6, 2, 0], [0, -2, 0], zs, false); if (this.startDerivative === null) { addAbcEquation(aa, bs, 0, 3 * n - 2, [0, 2, 0], 0); // Natural constraint (second derivative=0) } else { addAbcEquation(aa, bs, 0, 3 * n - 2, [0, 0, 1], this.startDerivative); } if (this.endDerivative === null) { addAbcEquation(aa, bs, n - 1, 3 * n - 1, [6, 2, 0], 0); // Natural constraint (second derivative=0) } else { addAbcEquation(aa, bs, n - 1, 3 * n - 1, [3, 2, 1], this.endDerivative); } const res = _1.numeric.solve(aa, bs); this.a = new Array(n - 1); this.b = new Array(n - 1); this.c = new Array(n - 1); for (let i = 0; i < n; i++) { this.a[i] = res[3 * i]; this.b[i] = res[3 * i + 1]; this.c[i] = res[3 * i + 2]; } // console.log(mat2str(aa), mat2str(bs), this.a, this.b, this.c); } y(x) { const n = this.ys.length; if (n === 0) { return Number.NaN; } if (n === 1) { return this.ys[0]; } let idx; if (x >= n - 1) { idx = n - 2; } else if (x < 0) { idx = 0; } else { idx = floor(x); } const x0 = idx; const t = x - x0; const y0 = this.ys[idx]; return y0 + t * (this.c[idx] + t * (this.b[idx] + t * this.a[idx])); } } exports.ConstrainedCubicSpline = ConstrainedCubicSpline; //# sourceMappingURL=interpolation.js.map