UNPKG

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.

54 lines (42 loc) 1.78 kB
import { scale, translate } from 'flo-vector2d'; import { solveCirculantTridiagonal } from './solve-circulant-tridiagonal.js'; /** * Returns the unique algebraically C² smooth piecewise cubic bezier curve * that interpolates the given set of points. * * * modified from the medium article [Bézier Interpolation - Create smooth shapes using Bézier curves](https://medium.com/towards-data-science/b%C3%A9zier-interpolation-8033e9a262c2) * to account for closed loops * * * The array of returned curves are order 3 (cubic) bezier curves given as an * ordered array of its control point coordinates, e.g. `[[0,0], [1,1], [2,1], [2,0]]` */ function cubicBeziersThroughPoints_C2( ps: number[][]): number[][][] { const n = ps.length; // build coefficents matrix const a = [...Array(n)].map(x => 1); const b = [...Array(n)].map(x => 4); const c = [...Array(n)].map(x => 1); // build points vector const P: number[][] = []; for (let i=0; i<n; i++) { const i_ = i + 1 === n ? 0 : i + 1; P.push(scale(translate(scale(ps[i],2), ps[i_]), 2)); } // solve system, i.e. find `A` and `B` const X = solveCirculantTridiagonal(a, b, c, P.map(p => p[0]))!; const Y = solveCirculantTridiagonal(a, b, c, P.map(p => p[1]))!; // const A = [X,Y]; const B: number[][] = []; for (let i=0; i<n; i++) { const i_ = i + 1 === n ? 0 : i + 1; B.push(translate(scale(ps[i_],2), [-X[i_],-Y[i_]])); } const cubics: number[][][] = []; for (let i=0; i<n; i++) { const i_ = i + 1 === n ? 0 : i + 1; cubics.push([ps[i], [X[i],Y[i]], B[i], ps[i_]]); } return cubics; } export { cubicBeziersThroughPoints_C2 }