mirror of
https://github.com/xibyte/jsketcher
synced 2025-12-06 08:25:19 +01:00
86 lines
2.3 KiB
JavaScript
86 lines
2.3 KiB
JavaScript
import * as vec from 'math/vec';
|
|
import {cubicBezierDer1, cubicBezierDer2, cubicBezierPoint} from './bezierCubic';
|
|
import {closestToCurveParam} from './closestPoint';
|
|
import InvertedCurve from './invertedCurve';
|
|
|
|
export default function CubicHermiteInterpolation(points, tangents) {
|
|
const n = points.length;
|
|
const knots = new Array(n).fill().map((e,i) => i);
|
|
const beziers = [];
|
|
for (let i = 1; i < n; i++) {
|
|
const p0 = points[i - 1];
|
|
const p3 = points[i];
|
|
const tangent1 = tangents[i - 1];
|
|
const tangent2 = tangents[i];
|
|
const length = vec.length(vec.sub(p3, p0)) * 0.5;
|
|
const p1 = vec.add(p0, vec.mul(tangent1, length));
|
|
const p2 = vec.sub(p3, vec.mul(tangent2, length));
|
|
beziers.push({p0, p1, p2, p3});
|
|
}
|
|
|
|
function evalPatch(p0, p1, p2, p3, u, num) {
|
|
switch (num) {
|
|
case 0: return cubicBezierPoint(p0, p1, p2, p3, u);
|
|
case 1: return cubicBezierDer1(p0, p1, p2, p3, u);
|
|
case 2: return cubicBezierDer2(p0, p1, p2, p3, u);
|
|
default: throw 'illegal derivative order for cubic bezier curve';
|
|
}
|
|
}
|
|
|
|
function localizeParam(u) {
|
|
let pieceIndex;
|
|
if (u >= n - 1) {
|
|
pieceIndex = beziers.length - 1;
|
|
u = 1;
|
|
} else {
|
|
pieceIndex = Math.floor(u);
|
|
u = u % 1;
|
|
}
|
|
if (!beziers[pieceIndex]) {
|
|
throw 'parameter out of bounds: ' + u;
|
|
}
|
|
return [pieceIndex, u];
|
|
}
|
|
|
|
function evaluate(u, num) {
|
|
const [pieceIndex, uL] = localizeParam(u);
|
|
const {p0, p1, p2, p3} = beziers[pieceIndex];
|
|
const out = [];
|
|
for (let i = 0; i <= num; ++i) {
|
|
out.push(evalPatch(p0, p1, p2, p3, uL, i));
|
|
}
|
|
return out;
|
|
}
|
|
|
|
function point(u) {
|
|
const [pieceIndex, uL] = localizeParam(u);
|
|
const {p0, p1, p2, p3} = beziers[pieceIndex];
|
|
return cubicBezierPoint(p0, p1, p2, p3, uL);
|
|
}
|
|
|
|
function param(point) {
|
|
return closestToCurveParam(this, point);
|
|
}
|
|
|
|
function transform(tr) {
|
|
return new CubicHermiteInterpolation(
|
|
points.map(p => vec.dotVM(p, tr)),
|
|
tangents.map(p => vec.dotVM(p, tr)));
|
|
}
|
|
|
|
function invert() {
|
|
return new InvertedCurve(this);
|
|
}
|
|
|
|
Object.assign(this, {
|
|
domain: () => [0, n - 1],
|
|
degree: () => 3,
|
|
eval: evaluate,
|
|
point,
|
|
param,
|
|
transform,
|
|
knots: () => knots,
|
|
invert,
|
|
points, tangents
|
|
});
|
|
}
|