123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363 |
- /**
- * @licstart The following is the entire license notice for the
- * JavaScript code in this page
- *
- * Copyright 2022 Mozilla Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * @licend The above is the entire license notice for the
- * JavaScript code in this page
- */
- /******/ var __webpack_modules__ = ([
- /* 0 */,
- /* 1 */
- /***/ ((module) => {
- function fitCurve(points, maxError, progressCallback) {
- if (!Array.isArray(points)) {
- throw new TypeError("First argument should be an array");
- }
- points.forEach(point => {
- if (!Array.isArray(point) || point.some(item => typeof item !== 'number') || point.length !== points[0].length) {
- throw Error("Each point should be an array of numbers. Each point should have the same amount of numbers.");
- }
- });
- points = points.filter((point, i) => i === 0 || !point.every((val, j) => val === points[i - 1][j]));
- if (points.length < 2) {
- return [];
- }
- const len = points.length;
- const leftTangent = createTangent(points[1], points[0]);
- const rightTangent = createTangent(points[len - 2], points[len - 1]);
- return fitCubic(points, leftTangent, rightTangent, maxError, progressCallback);
- }
- function fitCubic(points, leftTangent, rightTangent, error, progressCallback) {
- const MaxIterations = 20;
- var bezCurve, u, uPrime, maxError, prevErr, splitPoint, prevSplit, centerVector, toCenterTangent, fromCenterTangent, beziers, dist, i;
- if (points.length === 2) {
- dist = maths.vectorLen(maths.subtract(points[0], points[1])) / 3.0;
- bezCurve = [points[0], maths.addArrays(points[0], maths.mulItems(leftTangent, dist)), maths.addArrays(points[1], maths.mulItems(rightTangent, dist)), points[1]];
- return [bezCurve];
- }
- u = chordLengthParameterize(points);
- [bezCurve, maxError, splitPoint] = generateAndReport(points, u, u, leftTangent, rightTangent, progressCallback);
- if (maxError === 0 || maxError < error) {
- return [bezCurve];
- }
- if (maxError < error * error) {
- uPrime = u;
- prevErr = maxError;
- prevSplit = splitPoint;
- for (i = 0; i < MaxIterations; i++) {
- uPrime = reparameterize(bezCurve, points, uPrime);
- [bezCurve, maxError, splitPoint] = generateAndReport(points, u, uPrime, leftTangent, rightTangent, progressCallback);
- if (maxError < error) {
- return [bezCurve];
- } else if (splitPoint === prevSplit) {
- let errChange = maxError / prevErr;
- if (errChange > .9999 && errChange < 1.0001) {
- break;
- }
- }
- prevErr = maxError;
- prevSplit = splitPoint;
- }
- }
- beziers = [];
- centerVector = maths.subtract(points[splitPoint - 1], points[splitPoint + 1]);
- if (centerVector.every(val => val === 0)) {
- centerVector = maths.subtract(points[splitPoint - 1], points[splitPoint]);
- [centerVector[0], centerVector[1]] = [-centerVector[1], centerVector[0]];
- }
- toCenterTangent = maths.normalize(centerVector);
- fromCenterTangent = maths.mulItems(toCenterTangent, -1);
- beziers = beziers.concat(fitCubic(points.slice(0, splitPoint + 1), leftTangent, toCenterTangent, error, progressCallback));
- beziers = beziers.concat(fitCubic(points.slice(splitPoint), fromCenterTangent, rightTangent, error, progressCallback));
- return beziers;
- }
- ;
- function generateAndReport(points, paramsOrig, paramsPrime, leftTangent, rightTangent, progressCallback) {
- var bezCurve, maxError, splitPoint;
- bezCurve = generateBezier(points, paramsPrime, leftTangent, rightTangent, progressCallback);
- [maxError, splitPoint] = computeMaxError(points, bezCurve, paramsOrig);
- if (progressCallback) {
- progressCallback({
- bez: bezCurve,
- points: points,
- params: paramsOrig,
- maxErr: maxError,
- maxPoint: splitPoint
- });
- }
- return [bezCurve, maxError, splitPoint];
- }
- function generateBezier(points, parameters, leftTangent, rightTangent) {
- var bezCurve,
- A,
- a,
- C,
- X,
- det_C0_C1,
- det_C0_X,
- det_X_C1,
- alpha_l,
- alpha_r,
- epsilon,
- segLength,
- i,
- len,
- tmp,
- u,
- ux,
- firstPoint = points[0],
- lastPoint = points[points.length - 1];
- bezCurve = [firstPoint, null, null, lastPoint];
- A = maths.zeros_Xx2x2(parameters.length);
- for (i = 0, len = parameters.length; i < len; i++) {
- u = parameters[i];
- ux = 1 - u;
- a = A[i];
- a[0] = maths.mulItems(leftTangent, 3 * u * (ux * ux));
- a[1] = maths.mulItems(rightTangent, 3 * ux * (u * u));
- }
- C = [[0, 0], [0, 0]];
- X = [0, 0];
- for (i = 0, len = points.length; i < len; i++) {
- u = parameters[i];
- a = A[i];
- C[0][0] += maths.dot(a[0], a[0]);
- C[0][1] += maths.dot(a[0], a[1]);
- C[1][0] += maths.dot(a[0], a[1]);
- C[1][1] += maths.dot(a[1], a[1]);
- tmp = maths.subtract(points[i], bezier.q([firstPoint, firstPoint, lastPoint, lastPoint], u));
- X[0] += maths.dot(a[0], tmp);
- X[1] += maths.dot(a[1], tmp);
- }
- det_C0_C1 = C[0][0] * C[1][1] - C[1][0] * C[0][1];
- det_C0_X = C[0][0] * X[1] - C[1][0] * X[0];
- det_X_C1 = X[0] * C[1][1] - X[1] * C[0][1];
- alpha_l = det_C0_C1 === 0 ? 0 : det_X_C1 / det_C0_C1;
- alpha_r = det_C0_C1 === 0 ? 0 : det_C0_X / det_C0_C1;
- segLength = maths.vectorLen(maths.subtract(firstPoint, lastPoint));
- epsilon = 1.0e-6 * segLength;
- if (alpha_l < epsilon || alpha_r < epsilon) {
- bezCurve[1] = maths.addArrays(firstPoint, maths.mulItems(leftTangent, segLength / 3.0));
- bezCurve[2] = maths.addArrays(lastPoint, maths.mulItems(rightTangent, segLength / 3.0));
- } else {
- bezCurve[1] = maths.addArrays(firstPoint, maths.mulItems(leftTangent, alpha_l));
- bezCurve[2] = maths.addArrays(lastPoint, maths.mulItems(rightTangent, alpha_r));
- }
- return bezCurve;
- }
- ;
- function reparameterize(bezier, points, parameters) {
- return parameters.map((p, i) => newtonRaphsonRootFind(bezier, points[i], p));
- }
- ;
- function newtonRaphsonRootFind(bez, point, u) {
- var d = maths.subtract(bezier.q(bez, u), point),
- qprime = bezier.qprime(bez, u),
- numerator = maths.mulMatrix(d, qprime),
- denominator = maths.sum(maths.squareItems(qprime)) + 2 * maths.mulMatrix(d, bezier.qprimeprime(bez, u));
- if (denominator === 0) {
- return u;
- } else {
- return u - numerator / denominator;
- }
- }
- ;
- function chordLengthParameterize(points) {
- var u = [],
- currU,
- prevU,
- prevP;
- points.forEach((p, i) => {
- currU = i ? prevU + maths.vectorLen(maths.subtract(p, prevP)) : 0;
- u.push(currU);
- prevU = currU;
- prevP = p;
- });
- u = u.map(x => x / prevU);
- return u;
- }
- ;
- function computeMaxError(points, bez, parameters) {
- var dist, maxDist, splitPoint, v, i, count, point, t;
- maxDist = 0;
- splitPoint = Math.floor(points.length / 2);
- const t_distMap = mapTtoRelativeDistances(bez, 10);
- for (i = 0, count = points.length; i < count; i++) {
- point = points[i];
- t = find_t(bez, parameters[i], t_distMap, 10);
- v = maths.subtract(bezier.q(bez, t), point);
- dist = v[0] * v[0] + v[1] * v[1];
- if (dist > maxDist) {
- maxDist = dist;
- splitPoint = i;
- }
- }
- return [maxDist, splitPoint];
- }
- ;
- var mapTtoRelativeDistances = function (bez, B_parts) {
- var B_t_curr;
- var B_t_dist = [0];
- var B_t_prev = bez[0];
- var sumLen = 0;
- for (var i = 1; i <= B_parts; i++) {
- B_t_curr = bezier.q(bez, i / B_parts);
- sumLen += maths.vectorLen(maths.subtract(B_t_curr, B_t_prev));
- B_t_dist.push(sumLen);
- B_t_prev = B_t_curr;
- }
- B_t_dist = B_t_dist.map(x => x / sumLen);
- return B_t_dist;
- };
- function find_t(bez, param, t_distMap, B_parts) {
- if (param < 0) {
- return 0;
- }
- if (param > 1) {
- return 1;
- }
- var lenMax, lenMin, tMax, tMin, t;
- for (var i = 1; i <= B_parts; i++) {
- if (param <= t_distMap[i]) {
- tMin = (i - 1) / B_parts;
- tMax = i / B_parts;
- lenMin = t_distMap[i - 1];
- lenMax = t_distMap[i];
- t = (param - lenMin) / (lenMax - lenMin) * (tMax - tMin) + tMin;
- break;
- }
- }
- return t;
- }
- function createTangent(pointA, pointB) {
- return maths.normalize(maths.subtract(pointA, pointB));
- }
- class maths {
- static zeros_Xx2x2(x) {
- var zs = [];
- while (x--) {
- zs.push([0, 0]);
- }
- return zs;
- }
- static mulItems(items, multiplier) {
- return items.map(x => x * multiplier);
- }
- static mulMatrix(m1, m2) {
- return m1.reduce((sum, x1, i) => sum + x1 * m2[i], 0);
- }
- static subtract(arr1, arr2) {
- return arr1.map((x1, i) => x1 - arr2[i]);
- }
- static addArrays(arr1, arr2) {
- return arr1.map((x1, i) => x1 + arr2[i]);
- }
- static addItems(items, addition) {
- return items.map(x => x + addition);
- }
- static sum(items) {
- return items.reduce((sum, x) => sum + x);
- }
- static dot(m1, m2) {
- return maths.mulMatrix(m1, m2);
- }
- static vectorLen(v) {
- return Math.hypot(...v);
- }
- static divItems(items, divisor) {
- return items.map(x => x / divisor);
- }
- static squareItems(items) {
- return items.map(x => x * x);
- }
- static normalize(v) {
- return this.divItems(v, this.vectorLen(v));
- }
- }
- class bezier {
- static q(ctrlPoly, t) {
- var tx = 1.0 - t;
- var pA = maths.mulItems(ctrlPoly[0], tx * tx * tx),
- pB = maths.mulItems(ctrlPoly[1], 3 * tx * tx * t),
- pC = maths.mulItems(ctrlPoly[2], 3 * tx * t * t),
- pD = maths.mulItems(ctrlPoly[3], t * t * t);
- return maths.addArrays(maths.addArrays(pA, pB), maths.addArrays(pC, pD));
- }
- static qprime(ctrlPoly, t) {
- var tx = 1.0 - t;
- var pA = maths.mulItems(maths.subtract(ctrlPoly[1], ctrlPoly[0]), 3 * tx * tx),
- pB = maths.mulItems(maths.subtract(ctrlPoly[2], ctrlPoly[1]), 6 * tx * t),
- pC = maths.mulItems(maths.subtract(ctrlPoly[3], ctrlPoly[2]), 3 * t * t);
- return maths.addArrays(maths.addArrays(pA, pB), pC);
- }
- static qprimeprime(ctrlPoly, t) {
- return maths.addArrays(maths.mulItems(maths.addArrays(maths.subtract(ctrlPoly[2], maths.mulItems(ctrlPoly[1], 2)), ctrlPoly[0]), 6 * (1.0 - t)), maths.mulItems(maths.addArrays(maths.subtract(ctrlPoly[3], maths.mulItems(ctrlPoly[2], 2)), ctrlPoly[1]), 6 * t));
- }
- }
- module.exports = fitCurve;
- module.exports.fitCubic = fitCubic;
- module.exports.createTangent = createTangent;
- /***/ })
- /******/ ]);
- /************************************************************************/
- /******/ // The module cache
- /******/ var __webpack_module_cache__ = {};
- /******/
- /******/ // The require function
- /******/ function __webpack_require__(moduleId) {
- /******/ // Check if module is in cache
- /******/ var cachedModule = __webpack_module_cache__[moduleId];
- /******/ if (cachedModule !== undefined) {
- /******/ return cachedModule.exports;
- /******/ }
- /******/ // Create a new module (and put it into the cache)
- /******/ var module = __webpack_module_cache__[moduleId] = {
- /******/ // no module.id needed
- /******/ // no module.loaded needed
- /******/ exports: {}
- /******/ };
- /******/
- /******/ // Execute the module function
- /******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
- /******/
- /******/ // Return the exports of the module
- /******/ return module.exports;
- /******/ }
- /******/
- /************************************************************************/
- var __webpack_exports__ = {};
- // This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
- (() => {
- var exports = __webpack_exports__;
- Object.defineProperty(exports, "__esModule", ({
- value: true
- }));
- exports.fitCurve = void 0;
- const fitCurve = __webpack_require__(1);
- exports.fitCurve = fitCurve;
- })();
- var __webpack_exports___esModule = __webpack_exports__.__esModule;
- var __webpack_exports__fitCurve = __webpack_exports__.fitCurve;
- export { __webpack_exports___esModule as __esModule, __webpack_exports__fitCurve as fitCurve };
|