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.

103 lines 3.89 kB
import { cubicToAnglesAndSpeeds } from "flo-bezier3"; import { getEα, getPssByα } from './get-e-alpha.js'; import { miniAlphaLoops } from './consts.js'; import { getInfos } from './get-infos/get-infos.js'; import { removeIdenticalPoints } from './remove-identical-points.js'; /** * Returns an array of cubic bezier curves forming a loop going through all * the given ordered points such that the binding energy of the loop is near * a local minimum. * * Such loops are 'fair' meaning they look 'pretty' somehow. * * * curve terminal speeds are not optimized (even though it would not be too * hard to do so); only angles are optimized but the resulting curves are quite * close to each other. In any case, the research paper this library is based * on suggest speeds to be fixed at the theoretical value of 1 and this is how * it is implemented. * * * see [A CONSTRUCTIVE FRAMEWORK FOR MINIMAL ENERGY PLANAR CURVES by Michael J. Johnson & Hakim S. Johnson](https://www.sciencedirect.com/science/article/abs/pii/S0096300315015490) * * * 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]]` * * @param points an ordered array of planar points */ function cubicsAndEnergyThroughPoints(points) { const infos = getInfos(removeIdenticalPoints(points)); const len = infos.length; let Energy = 0; for (let j = 0; j < miniAlphaLoops; j++) { Energy = 0; // reset for (let i = 0; i < len; i++) { const info = infos[i]; const _info = info._info; const getE_ = getEα(info); let halfSpan; let { l, m, r } = info; let L = getE_(l); let M = getE_(m); let R = getE_(r); // 1d pattern search while (true) { ({ l, m, r } = info); halfSpan = (r - l) / 2; if ((L >= M && M <= R)) { info.l = m - halfSpan / 2; info.r = m + halfSpan / 2; Energy += M; break; } else if ((L <= M && M <= R)) { info.l = l - halfSpan; info.m = l; info.r = m; R = M; M = L; L = getE_(info.l); } else if ((L >= M && M >= R)) { info.l = m; info.m = r; info.r = r + halfSpan; L = M; M = R; R = getE_(info.r); } else if ((L <= M && M >= R)) { info.l = l - halfSpan; info.m = l; info.r = m; R = M; M = L; L = getE_(info.l); } } const α = info.m; const [_ps, ps_] = getPssByα(α, info); info.ps = ps_; info.$ps = cubicToAnglesAndSpeeds(ps_); _info.ps = _ps; _info.$ps = cubicToAnglesAndSpeeds(_ps); } } const cubics = infos.map(info => info.ps); return { cubics, Energy }; } function energyThroughPoints(points) { const infos = getInfos(removeIdenticalPoints(points)); const len = infos.length; let Energy = 0; for (let i = 0; i < len; i++) { const info = infos[i]; const getE_ = getEα(info); let M = getE_(info.m); Energy += M; } return Energy; } function cubicBeziersThroughPoints(points) { return cubicsAndEnergyThroughPoints(points).cubics; } export { cubicBeziersThroughPoints, energyThroughPoints, cubicsAndEnergyThroughPoints }; //# sourceMappingURL=cubic-beziers-through-points.js.map