summaryrefslogtreecommitdiff
path: root/web-build/static/media/bezier.799a5c8c.cjs
diff options
context:
space:
mode:
authorThomas F. K. Jorna <[email protected]>2021-07-17 14:50:36 +0200
committerThomas F. K. Jorna <[email protected]>2021-07-17 14:50:36 +0200
commitc8b850133a9acf63f421da4643da07d7cfaa7f98 (patch)
tree3e180fd8d70d8110f509057d58050910ddfefa80 /web-build/static/media/bezier.799a5c8c.cjs
parent7b128c660a798759c6f9e1d6751b811ce1d2874f (diff)
added web-build
Diffstat (limited to 'web-build/static/media/bezier.799a5c8c.cjs')
-rw-r--r--web-build/static/media/bezier.799a5c8c.cjs1943
1 files changed, 1943 insertions, 0 deletions
diff --git a/web-build/static/media/bezier.799a5c8c.cjs b/web-build/static/media/bezier.799a5c8c.cjs
new file mode 100644
index 0000000..fe90607
--- /dev/null
+++ b/web-build/static/media/bezier.799a5c8c.cjs
@@ -0,0 +1,1943 @@
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.Bezier = void 0;
+// math-inlining.
+const {
+ abs,
+ cos,
+ sin,
+ acos,
+ atan2,
+ sqrt,
+ pow
+} = Math; // cube root function yielding real roots
+
+function crt(v) {
+ return v < 0 ? -pow(-v, 1 / 3) : pow(v, 1 / 3);
+} // trig constants
+
+
+const pi = Math.PI,
+ tau = 2 * pi,
+ quart = pi / 2,
+ // float precision significant decimal
+epsilon = 0.000001,
+ // extremas used in bbox calculation and similar algorithms
+nMax = Number.MAX_SAFE_INTEGER || 9007199254740991,
+ nMin = Number.MIN_SAFE_INTEGER || -9007199254740991,
+ // a zero coordinate, which is surprisingly useful
+ZERO = {
+ x: 0,
+ y: 0,
+ z: 0
+}; // Bezier utility functions
+
+const utils = {
+ // Legendre-Gauss abscissae with n=24 (x_i values, defined at i=n as the roots of the nth order Legendre polynomial Pn(x))
+ Tvalues: [-0.0640568928626056260850430826247450385909, 0.0640568928626056260850430826247450385909, -0.1911188674736163091586398207570696318404, 0.1911188674736163091586398207570696318404, -0.3150426796961633743867932913198102407864, 0.3150426796961633743867932913198102407864, -0.4337935076260451384870842319133497124524, 0.4337935076260451384870842319133497124524, -0.5454214713888395356583756172183723700107, 0.5454214713888395356583756172183723700107, -0.6480936519369755692524957869107476266696, 0.6480936519369755692524957869107476266696, -0.7401241915785543642438281030999784255232, 0.7401241915785543642438281030999784255232, -0.8200019859739029219539498726697452080761, 0.8200019859739029219539498726697452080761, -0.8864155270044010342131543419821967550873, 0.8864155270044010342131543419821967550873, -0.9382745520027327585236490017087214496548, 0.9382745520027327585236490017087214496548, -0.9747285559713094981983919930081690617411, 0.9747285559713094981983919930081690617411, -0.9951872199970213601799974097007368118745, 0.9951872199970213601799974097007368118745],
+ // Legendre-Gauss weights with n=24 (w_i values, defined by a function linked to in the Bezier primer article)
+ Cvalues: [0.1279381953467521569740561652246953718517, 0.1279381953467521569740561652246953718517, 0.1258374563468282961213753825111836887264, 0.1258374563468282961213753825111836887264, 0.121670472927803391204463153476262425607, 0.121670472927803391204463153476262425607, 0.1155056680537256013533444839067835598622, 0.1155056680537256013533444839067835598622, 0.1074442701159656347825773424466062227946, 0.1074442701159656347825773424466062227946, 0.0976186521041138882698806644642471544279, 0.0976186521041138882698806644642471544279, 0.086190161531953275917185202983742667185, 0.086190161531953275917185202983742667185, 0.0733464814110803057340336152531165181193, 0.0733464814110803057340336152531165181193, 0.0592985849154367807463677585001085845412, 0.0592985849154367807463677585001085845412, 0.0442774388174198061686027482113382288593, 0.0442774388174198061686027482113382288593, 0.0285313886289336631813078159518782864491, 0.0285313886289336631813078159518782864491, 0.0123412297999871995468056670700372915759, 0.0123412297999871995468056670700372915759],
+ arcfn: function (t, derivativeFn) {
+ const d = derivativeFn(t);
+ let l = d.x * d.x + d.y * d.y;
+
+ if (typeof d.z !== "undefined") {
+ l += d.z * d.z;
+ }
+
+ return sqrt(l);
+ },
+ compute: function (t, points, _3d) {
+ // shortcuts
+ if (t === 0) {
+ points[0].t = 0;
+ return points[0];
+ }
+
+ const order = points.length - 1;
+
+ if (t === 1) {
+ points[order].t = 1;
+ return points[order];
+ }
+
+ const mt = 1 - t;
+ let p = points; // constant?
+
+ if (order === 0) {
+ points[0].t = t;
+ return points[0];
+ } // linear?
+
+
+ if (order === 1) {
+ const ret = {
+ x: mt * p[0].x + t * p[1].x,
+ y: mt * p[0].y + t * p[1].y,
+ t: t
+ };
+
+ if (_3d) {
+ ret.z = mt * p[0].z + t * p[1].z;
+ }
+
+ return ret;
+ } // quadratic/cubic curve?
+
+
+ if (order < 4) {
+ let mt2 = mt * mt,
+ t2 = t * t,
+ a,
+ b,
+ c,
+ d = 0;
+
+ if (order === 2) {
+ p = [p[0], p[1], p[2], ZERO];
+ a = mt2;
+ b = mt * t * 2;
+ c = t2;
+ } else if (order === 3) {
+ a = mt2 * mt;
+ b = mt2 * t * 3;
+ c = mt * t2 * 3;
+ d = t * t2;
+ }
+
+ const ret = {
+ x: a * p[0].x + b * p[1].x + c * p[2].x + d * p[3].x,
+ y: a * p[0].y + b * p[1].y + c * p[2].y + d * p[3].y,
+ t: t
+ };
+
+ if (_3d) {
+ ret.z = a * p[0].z + b * p[1].z + c * p[2].z + d * p[3].z;
+ }
+
+ return ret;
+ } // higher order curves: use de Casteljau's computation
+
+
+ const dCpts = JSON.parse(JSON.stringify(points));
+
+ while (dCpts.length > 1) {
+ for (let i = 0; i < dCpts.length - 1; i++) {
+ dCpts[i] = {
+ x: dCpts[i].x + (dCpts[i + 1].x - dCpts[i].x) * t,
+ y: dCpts[i].y + (dCpts[i + 1].y - dCpts[i].y) * t
+ };
+
+ if (typeof dCpts[i].z !== "undefined") {
+ dCpts[i] = dCpts[i].z + (dCpts[i + 1].z - dCpts[i].z) * t;
+ }
+ }
+
+ dCpts.splice(dCpts.length - 1, 1);
+ }
+
+ dCpts[0].t = t;
+ return dCpts[0];
+ },
+ computeWithRatios: function (t, points, ratios, _3d) {
+ const mt = 1 - t,
+ r = ratios,
+ p = points;
+ let f1 = r[0],
+ f2 = r[1],
+ f3 = r[2],
+ f4 = r[3],
+ d; // spec for linear
+
+ f1 *= mt;
+ f2 *= t;
+
+ if (p.length === 2) {
+ d = f1 + f2;
+ return {
+ x: (f1 * p[0].x + f2 * p[1].x) / d,
+ y: (f1 * p[0].y + f2 * p[1].y) / d,
+ z: !_3d ? false : (f1 * p[0].z + f2 * p[1].z) / d,
+ t: t
+ };
+ } // upgrade to quadratic
+
+
+ f1 *= mt;
+ f2 *= 2 * mt;
+ f3 *= t * t;
+
+ if (p.length === 3) {
+ d = f1 + f2 + f3;
+ return {
+ x: (f1 * p[0].x + f2 * p[1].x + f3 * p[2].x) / d,
+ y: (f1 * p[0].y + f2 * p[1].y + f3 * p[2].y) / d,
+ z: !_3d ? false : (f1 * p[0].z + f2 * p[1].z + f3 * p[2].z) / d,
+ t: t
+ };
+ } // upgrade to cubic
+
+
+ f1 *= mt;
+ f2 *= 1.5 * mt;
+ f3 *= 3 * mt;
+ f4 *= t * t * t;
+
+ if (p.length === 4) {
+ d = f1 + f2 + f3 + f4;
+ return {
+ x: (f1 * p[0].x + f2 * p[1].x + f3 * p[2].x + f4 * p[3].x) / d,
+ y: (f1 * p[0].y + f2 * p[1].y + f3 * p[2].y + f4 * p[3].y) / d,
+ z: !_3d ? false : (f1 * p[0].z + f2 * p[1].z + f3 * p[2].z + f4 * p[3].z) / d,
+ t: t
+ };
+ }
+ },
+ derive: function (points, _3d) {
+ const dpoints = [];
+
+ for (let p = points, d = p.length, c = d - 1; d > 1; d--, c--) {
+ const list = [];
+
+ for (let j = 0, dpt; j < c; j++) {
+ dpt = {
+ x: c * (p[j + 1].x - p[j].x),
+ y: c * (p[j + 1].y - p[j].y)
+ };
+
+ if (_3d) {
+ dpt.z = c * (p[j + 1].z - p[j].z);
+ }
+
+ list.push(dpt);
+ }
+
+ dpoints.push(list);
+ p = list;
+ }
+
+ return dpoints;
+ },
+ between: function (v, m, M) {
+ return m <= v && v <= M || utils.approximately(v, m) || utils.approximately(v, M);
+ },
+ approximately: function (a, b, precision) {
+ return abs(a - b) <= (precision || epsilon);
+ },
+ length: function (derivativeFn) {
+ const z = 0.5,
+ len = utils.Tvalues.length;
+ let sum = 0;
+
+ for (let i = 0, t; i < len; i++) {
+ t = z * utils.Tvalues[i] + z;
+ sum += utils.Cvalues[i] * utils.arcfn(t, derivativeFn);
+ }
+
+ return z * sum;
+ },
+ map: function (v, ds, de, ts, te) {
+ const d1 = de - ds,
+ d2 = te - ts,
+ v2 = v - ds,
+ r = v2 / d1;
+ return ts + d2 * r;
+ },
+ lerp: function (r, v1, v2) {
+ const ret = {
+ x: v1.x + r * (v2.x - v1.x),
+ y: v1.y + r * (v2.y - v1.y)
+ };
+
+ if (!!v1.z && !!v2.z) {
+ ret.z = v1.z + r * (v2.z - v1.z);
+ }
+
+ return ret;
+ },
+ pointToString: function (p) {
+ let s = p.x + "/" + p.y;
+
+ if (typeof p.z !== "undefined") {
+ s += "/" + p.z;
+ }
+
+ return s;
+ },
+ pointsToString: function (points) {
+ return "[" + points.map(utils.pointToString).join(", ") + "]";
+ },
+ copy: function (obj) {
+ return JSON.parse(JSON.stringify(obj));
+ },
+ angle: function (o, v1, v2) {
+ const dx1 = v1.x - o.x,
+ dy1 = v1.y - o.y,
+ dx2 = v2.x - o.x,
+ dy2 = v2.y - o.y,
+ cross = dx1 * dy2 - dy1 * dx2,
+ dot = dx1 * dx2 + dy1 * dy2;
+ return atan2(cross, dot);
+ },
+ // round as string, to avoid rounding errors
+ round: function (v, d) {
+ const s = "" + v;
+ const pos = s.indexOf(".");
+ return parseFloat(s.substring(0, pos + 1 + d));
+ },
+ dist: function (p1, p2) {
+ const dx = p1.x - p2.x,
+ dy = p1.y - p2.y;
+ return sqrt(dx * dx + dy * dy);
+ },
+ closest: function (LUT, point) {
+ let mdist = pow(2, 63),
+ mpos,
+ d;
+ LUT.forEach(function (p, idx) {
+ d = utils.dist(point, p);
+
+ if (d < mdist) {
+ mdist = d;
+ mpos = idx;
+ }
+ });
+ return {
+ mdist: mdist,
+ mpos: mpos
+ };
+ },
+ abcratio: function (t, n) {
+ // see ratio(t) note on http://pomax.github.io/bezierinfo/#abc
+ if (n !== 2 && n !== 3) {
+ return false;
+ }
+
+ if (typeof t === "undefined") {
+ t = 0.5;
+ } else if (t === 0 || t === 1) {
+ return t;
+ }
+
+ const bottom = pow(t, n) + pow(1 - t, n),
+ top = bottom - 1;
+ return abs(top / bottom);
+ },
+ projectionratio: function (t, n) {
+ // see u(t) note on http://pomax.github.io/bezierinfo/#abc
+ if (n !== 2 && n !== 3) {
+ return false;
+ }
+
+ if (typeof t === "undefined") {
+ t = 0.5;
+ } else if (t === 0 || t === 1) {
+ return t;
+ }
+
+ const top = pow(1 - t, n),
+ bottom = pow(t, n) + top;
+ return top / bottom;
+ },
+ lli8: function (x1, y1, x2, y2, x3, y3, x4, y4) {
+ const nx = (x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4),
+ ny = (x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4),
+ d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
+
+ if (d == 0) {
+ return false;
+ }
+
+ return {
+ x: nx / d,
+ y: ny / d
+ };
+ },
+ lli4: function (p1, p2, p3, p4) {
+ const x1 = p1.x,
+ y1 = p1.y,
+ x2 = p2.x,
+ y2 = p2.y,
+ x3 = p3.x,
+ y3 = p3.y,
+ x4 = p4.x,
+ y4 = p4.y;
+ return utils.lli8(x1, y1, x2, y2, x3, y3, x4, y4);
+ },
+ lli: function (v1, v2) {
+ return utils.lli4(v1, v1.c, v2, v2.c);
+ },
+ makeline: function (p1, p2) {
+ const x1 = p1.x,
+ y1 = p1.y,
+ x2 = p2.x,
+ y2 = p2.y,
+ dx = (x2 - x1) / 3,
+ dy = (y2 - y1) / 3;
+ return new Bezier(x1, y1, x1 + dx, y1 + dy, x1 + 2 * dx, y1 + 2 * dy, x2, y2);
+ },
+ findbbox: function (sections) {
+ let mx = nMax,
+ my = nMax,
+ MX = nMin,
+ MY = nMin;
+ sections.forEach(function (s) {
+ const bbox = s.bbox();
+ if (mx > bbox.x.min) mx = bbox.x.min;
+ if (my > bbox.y.min) my = bbox.y.min;
+ if (MX < bbox.x.max) MX = bbox.x.max;
+ if (MY < bbox.y.max) MY = bbox.y.max;
+ });
+ return {
+ x: {
+ min: mx,
+ mid: (mx + MX) / 2,
+ max: MX,
+ size: MX - mx
+ },
+ y: {
+ min: my,
+ mid: (my + MY) / 2,
+ max: MY,
+ size: MY - my
+ }
+ };
+ },
+ shapeintersections: function (s1, bbox1, s2, bbox2, curveIntersectionThreshold) {
+ if (!utils.bboxoverlap(bbox1, bbox2)) return [];
+ const intersections = [];
+ const a1 = [s1.startcap, s1.forward, s1.back, s1.endcap];
+ const a2 = [s2.startcap, s2.forward, s2.back, s2.endcap];
+ a1.forEach(function (l1) {
+ if (l1.virtual) return;
+ a2.forEach(function (l2) {
+ if (l2.virtual) return;
+ const iss = l1.intersects(l2, curveIntersectionThreshold);
+
+ if (iss.length > 0) {
+ iss.c1 = l1;
+ iss.c2 = l2;
+ iss.s1 = s1;
+ iss.s2 = s2;
+ intersections.push(iss);
+ }
+ });
+ });
+ return intersections;
+ },
+ makeshape: function (forward, back, curveIntersectionThreshold) {
+ const bpl = back.points.length;
+ const fpl = forward.points.length;
+ const start = utils.makeline(back.points[bpl - 1], forward.points[0]);
+ const end = utils.makeline(forward.points[fpl - 1], back.points[0]);
+ const shape = {
+ startcap: start,
+ forward: forward,
+ back: back,
+ endcap: end,
+ bbox: utils.findbbox([start, forward, back, end])
+ };
+
+ shape.intersections = function (s2) {
+ return utils.shapeintersections(shape, shape.bbox, s2, s2.bbox, curveIntersectionThreshold);
+ };
+
+ return shape;
+ },
+ getminmax: function (curve, d, list) {
+ if (!list) return {
+ min: 0,
+ max: 0
+ };
+ let min = nMax,
+ max = nMin,
+ t,
+ c;
+
+ if (list.indexOf(0) === -1) {
+ list = [0].concat(list);
+ }
+
+ if (list.indexOf(1) === -1) {
+ list.push(1);
+ }
+
+ for (let i = 0, len = list.length; i < len; i++) {
+ t = list[i];
+ c = curve.get(t);
+
+ if (c[d] < min) {
+ min = c[d];
+ }
+
+ if (c[d] > max) {
+ max = c[d];
+ }
+ }
+
+ return {
+ min: min,
+ mid: (min + max) / 2,
+ max: max,
+ size: max - min
+ };
+ },
+ align: function (points, line) {
+ const tx = line.p1.x,
+ ty = line.p1.y,
+ a = -atan2(line.p2.y - ty, line.p2.x - tx),
+ d = function (v) {
+ return {
+ x: (v.x - tx) * cos(a) - (v.y - ty) * sin(a),
+ y: (v.x - tx) * sin(a) + (v.y - ty) * cos(a)
+ };
+ };
+
+ return points.map(d);
+ },
+ roots: function (points, line) {
+ line = line || {
+ p1: {
+ x: 0,
+ y: 0
+ },
+ p2: {
+ x: 1,
+ y: 0
+ }
+ };
+ const order = points.length - 1;
+ const aligned = utils.align(points, line);
+
+ const reduce = function (t) {
+ return 0 <= t && t <= 1;
+ };
+
+ if (order === 2) {
+ const a = aligned[0].y,
+ b = aligned[1].y,
+ c = aligned[2].y,
+ d = a - 2 * b + c;
+
+ if (d !== 0) {
+ const m1 = -sqrt(b * b - a * c),
+ m2 = -a + b,
+ v1 = -(m1 + m2) / d,
+ v2 = -(-m1 + m2) / d;
+ return [v1, v2].filter(reduce);
+ } else if (b !== c && d === 0) {
+ return [(2 * b - c) / (2 * b - 2 * c)].filter(reduce);
+ }
+
+ return [];
+ } // see http://www.trans4mind.com/personal_development/mathematics/polynomials/cubicAlgebra.htm
+
+
+ const pa = aligned[0].y,
+ pb = aligned[1].y,
+ pc = aligned[2].y,
+ pd = aligned[3].y;
+ let d = -pa + 3 * pb - 3 * pc + pd,
+ a = 3 * pa - 6 * pb + 3 * pc,
+ b = -3 * pa + 3 * pb,
+ c = pa;
+
+ if (utils.approximately(d, 0)) {
+ // this is not a cubic curve.
+ if (utils.approximately(a, 0)) {
+ // in fact, this is not a quadratic curve either.
+ if (utils.approximately(b, 0)) {
+ // in fact in fact, there are no solutions.
+ return [];
+ } // linear solution:
+
+
+ return [-c / b].filter(reduce);
+ } // quadratic solution:
+
+
+ const q = sqrt(b * b - 4 * a * c),
+ a2 = 2 * a;
+ return [(q - b) / a2, (-b - q) / a2].filter(reduce);
+ } // at this point, we know we need a cubic solution:
+
+
+ a /= d;
+ b /= d;
+ c /= d;
+ const p = (3 * b - a * a) / 3,
+ p3 = p / 3,
+ q = (2 * a * a * a - 9 * a * b + 27 * c) / 27,
+ q2 = q / 2,
+ discriminant = q2 * q2 + p3 * p3 * p3;
+ let u1, v1, x1, x2, x3;
+
+ if (discriminant < 0) {
+ const mp3 = -p / 3,
+ mp33 = mp3 * mp3 * mp3,
+ r = sqrt(mp33),
+ t = -q / (2 * r),
+ cosphi = t < -1 ? -1 : t > 1 ? 1 : t,
+ phi = acos(cosphi),
+ crtr = crt(r),
+ t1 = 2 * crtr;
+ x1 = t1 * cos(phi / 3) - a / 3;
+ x2 = t1 * cos((phi + tau) / 3) - a / 3;
+ x3 = t1 * cos((phi + 2 * tau) / 3) - a / 3;
+ return [x1, x2, x3].filter(reduce);
+ } else if (discriminant === 0) {
+ u1 = q2 < 0 ? crt(-q2) : -crt(q2);
+ x1 = 2 * u1 - a / 3;
+ x2 = -u1 - a / 3;
+ return [x1, x2].filter(reduce);
+ } else {
+ const sd = sqrt(discriminant);
+ u1 = crt(-q2 + sd);
+ v1 = crt(q2 + sd);
+ return [u1 - v1 - a / 3].filter(reduce);
+ }
+ },
+ droots: function (p) {
+ // quadratic roots are easy
+ if (p.length === 3) {
+ const a = p[0],
+ b = p[1],
+ c = p[2],
+ d = a - 2 * b + c;
+
+ if (d !== 0) {
+ const m1 = -sqrt(b * b - a * c),
+ m2 = -a + b,
+ v1 = -(m1 + m2) / d,
+ v2 = -(-m1 + m2) / d;
+ return [v1, v2];
+ } else if (b !== c && d === 0) {
+ return [(2 * b - c) / (2 * (b - c))];
+ }
+
+ return [];
+ } // linear roots are even easier
+
+
+ if (p.length === 2) {
+ const a = p[0],
+ b = p[1];
+
+ if (a !== b) {
+ return [a / (a - b)];
+ }
+
+ return [];
+ }
+
+ return [];
+ },
+ curvature: function (t, d1, d2, _3d, kOnly) {
+ let num,
+ dnm,
+ adk,
+ dk,
+ k = 0,
+ r = 0; //
+ // We're using the following formula for curvature:
+ //
+ // x'y" - y'x"
+ // k(t) = ------------------
+ // (x'² + y'²)^(3/2)
+ //
+ // from https://en.wikipedia.org/wiki/Radius_of_curvature#Definition
+ //
+ // With it corresponding 3D counterpart:
+ //
+ // sqrt( (y'z" - y"z')² + (z'x" - z"x')² + (x'y" - x"y')²)
+ // k(t) = -------------------------------------------------------
+ // (x'² + y'² + z'²)^(3/2)
+ //
+
+ const d = utils.compute(t, d1);
+ const dd = utils.compute(t, d2);
+ const qdsum = d.x * d.x + d.y * d.y;
+
+ if (_3d) {
+ num = sqrt(pow(d.y * dd.z - dd.y * d.z, 2) + pow(d.z * dd.x - dd.z * d.x, 2) + pow(d.x * dd.y - dd.x * d.y, 2));
+ dnm = pow(qdsum + d.z * d.z, 3 / 2);
+ } else {
+ num = d.x * dd.y - d.y * dd.x;
+ dnm = pow(qdsum, 3 / 2);
+ }
+
+ if (num === 0 || dnm === 0) {
+ return {
+ k: 0,
+ r: 0
+ };
+ }
+
+ k = num / dnm;
+ r = dnm / num; // We're also computing the derivative of kappa, because
+ // there is value in knowing the rate of change for the
+ // curvature along the curve. And we're just going to
+ // ballpark it based on an epsilon.
+
+ if (!kOnly) {
+ // compute k'(t) based on the interval before, and after it,
+ // to at least try to not introduce forward/backward pass bias.
+ const pk = utils.curvature(t - 0.001, d1, d2, _3d, true).k;
+ const nk = utils.curvature(t + 0.001, d1, d2, _3d, true).k;
+ dk = (nk - k + (k - pk)) / 2;
+ adk = (abs(nk - k) + abs(k - pk)) / 2;
+ }
+
+ return {
+ k: k,
+ r: r,
+ dk: dk,
+ adk: adk
+ };
+ },
+ inflections: function (points) {
+ if (points.length < 4) return []; // FIXME: TODO: add in inflection abstraction for quartic+ curves?
+
+ const p = utils.align(points, {
+ p1: points[0],
+ p2: points.slice(-1)[0]
+ }),
+ a = p[2].x * p[1].y,
+ b = p[3].x * p[1].y,
+ c = p[1].x * p[2].y,
+ d = p[3].x * p[2].y,
+ v1 = 18 * (-3 * a + 2 * b + 3 * c - d),
+ v2 = 18 * (3 * a - b - 3 * c),
+ v3 = 18 * (c - a);
+
+ if (utils.approximately(v1, 0)) {
+ if (!utils.approximately(v2, 0)) {
+ let t = -v3 / v2;
+ if (0 <= t && t <= 1) return [t];
+ }
+
+ return [];
+ }
+
+ const trm = v2 * v2 - 4 * v1 * v3,
+ sq = Math.sqrt(trm),
+ d2 = 2 * v1;
+ if (utils.approximately(d2, 0)) return [];
+ return [(sq - v2) / d2, -(v2 + sq) / d2].filter(function (r) {
+ return 0 <= r && r <= 1;
+ });
+ },
+ bboxoverlap: function (b1, b2) {
+ const dims = ["x", "y"],
+ len = dims.length;
+
+ for (let i = 0, dim, l, t, d; i < len; i++) {
+ dim = dims[i];
+ l = b1[dim].mid;
+ t = b2[dim].mid;
+ d = (b1[dim].size + b2[dim].size) / 2;
+ if (abs(l - t) >= d) return false;
+ }
+
+ return true;
+ },
+ expandbox: function (bbox, _bbox) {
+ if (_bbox.x.min < bbox.x.min) {
+ bbox.x.min = _bbox.x.min;
+ }
+
+ if (_bbox.y.min < bbox.y.min) {
+ bbox.y.min = _bbox.y.min;
+ }
+
+ if (_bbox.z && _bbox.z.min < bbox.z.min) {
+ bbox.z.min = _bbox.z.min;
+ }
+
+ if (_bbox.x.max > bbox.x.max) {
+ bbox.x.max = _bbox.x.max;
+ }
+
+ if (_bbox.y.max > bbox.y.max) {
+ bbox.y.max = _bbox.y.max;
+ }
+
+ if (_bbox.z && _bbox.z.max > bbox.z.max) {
+ bbox.z.max = _bbox.z.max;
+ }
+
+ bbox.x.mid = (bbox.x.min + bbox.x.max) / 2;
+ bbox.y.mid = (bbox.y.min + bbox.y.max) / 2;
+
+ if (bbox.z) {
+ bbox.z.mid = (bbox.z.min + bbox.z.max) / 2;
+ }
+
+ bbox.x.size = bbox.x.max - bbox.x.min;
+ bbox.y.size = bbox.y.max - bbox.y.min;
+
+ if (bbox.z) {
+ bbox.z.size = bbox.z.max - bbox.z.min;
+ }
+ },
+ pairiteration: function (c1, c2, curveIntersectionThreshold) {
+ const c1b = c1.bbox(),
+ c2b = c2.bbox(),
+ r = 100000,
+ threshold = curveIntersectionThreshold || 0.5;
+
+ if (c1b.x.size + c1b.y.size < threshold && c2b.x.size + c2b.y.size < threshold) {
+ return [(r * (c1._t1 + c1._t2) / 2 | 0) / r + "/" + (r * (c2._t1 + c2._t2) / 2 | 0) / r];
+ }
+
+ let cc1 = c1.split(0.5),
+ cc2 = c2.split(0.5),
+ pairs = [{
+ left: cc1.left,
+ right: cc2.left
+ }, {
+ left: cc1.left,
+ right: cc2.right
+ }, {
+ left: cc1.right,
+ right: cc2.right
+ }, {
+ left: cc1.right,
+ right: cc2.left
+ }];
+ pairs = pairs.filter(function (pair) {
+ return utils.bboxoverlap(pair.left.bbox(), pair.right.bbox());
+ });
+ let results = [];
+ if (pairs.length === 0) return results;
+ pairs.forEach(function (pair) {
+ results = results.concat(utils.pairiteration(pair.left, pair.right, threshold));
+ });
+ results = results.filter(function (v, i) {
+ return results.indexOf(v) === i;
+ });
+ return results;
+ },
+ getccenter: function (p1, p2, p3) {
+ const dx1 = p2.x - p1.x,
+ dy1 = p2.y - p1.y,
+ dx2 = p3.x - p2.x,
+ dy2 = p3.y - p2.y,
+ dx1p = dx1 * cos(quart) - dy1 * sin(quart),
+ dy1p = dx1 * sin(quart) + dy1 * cos(quart),
+ dx2p = dx2 * cos(quart) - dy2 * sin(quart),
+ dy2p = dx2 * sin(quart) + dy2 * cos(quart),
+ // chord midpoints
+ mx1 = (p1.x + p2.x) / 2,
+ my1 = (p1.y + p2.y) / 2,
+ mx2 = (p2.x + p3.x) / 2,
+ my2 = (p2.y + p3.y) / 2,
+ // midpoint offsets
+ mx1n = mx1 + dx1p,
+ my1n = my1 + dy1p,
+ mx2n = mx2 + dx2p,
+ my2n = my2 + dy2p,
+ // intersection of these lines:
+ arc = utils.lli8(mx1, my1, mx1n, my1n, mx2, my2, mx2n, my2n),
+ r = utils.dist(arc, p1); // arc start/end values, over mid point:
+
+ let s = atan2(p1.y - arc.y, p1.x - arc.x),
+ m = atan2(p2.y - arc.y, p2.x - arc.x),
+ e = atan2(p3.y - arc.y, p3.x - arc.x),
+ _; // determine arc direction (cw/ccw correction)
+
+
+ if (s < e) {
+ // if s<m<e, arc(s, e)
+ // if m<s<e, arc(e, s + tau)
+ // if s<e<m, arc(e, s + tau)
+ if (s > m || m > e) {
+ s += tau;
+ }
+
+ if (s > e) {
+ _ = e;
+ e = s;
+ s = _;
+ }
+ } else {
+ // if e<m<s, arc(e, s)
+ // if m<e<s, arc(s, e + tau)
+ // if e<s<m, arc(s, e + tau)
+ if (e < m && m < s) {
+ _ = e;
+ e = s;
+ s = _;
+ } else {
+ e += tau;
+ }
+ } // assign and done.
+
+
+ arc.s = s;
+ arc.e = e;
+ arc.r = r;
+ return arc;
+ },
+ numberSort: function (a, b) {
+ return a - b;
+ }
+};
+/**
+ * Poly Bezier
+ * @param {[type]} curves [description]
+ */
+
+class PolyBezier {
+ constructor(curves) {
+ this.curves = [];
+ this._3d = false;
+
+ if (!!curves) {
+ this.curves = curves;
+ this._3d = this.curves[0]._3d;
+ }
+ }
+
+ valueOf() {
+ return this.toString();
+ }
+
+ toString() {
+ return "[" + this.curves.map(function (curve) {
+ return utils.pointsToString(curve.points);
+ }).join(", ") + "]";
+ }
+
+ addCurve(curve) {
+ this.curves.push(curve);
+ this._3d = this._3d || curve._3d;
+ }
+
+ length() {
+ return this.curves.map(function (v) {
+ return v.length();
+ }).reduce(function (a, b) {
+ return a + b;
+ });
+ }
+
+ curve(idx) {
+ return this.curves[idx];
+ }
+
+ bbox() {
+ const c = this.curves;
+ var bbox = c[0].bbox();
+
+ for (var i = 1; i < c.length; i++) {
+ utils.expandbox(bbox, c[i].bbox());
+ }
+
+ return bbox;
+ }
+
+ offset(d) {
+ const offset = [];
+ this.curves.forEach(function (v) {
+ offset.push(...v.offset(d));
+ });
+ return new PolyBezier(offset);
+ }
+
+}
+/**
+ A javascript Bezier curve library by Pomax.
+
+ Based on http://pomax.github.io/bezierinfo
+
+ This code is MIT licensed.
+**/
+// math-inlining.
+
+
+const {
+ abs: abs$1,
+ min,
+ max,
+ cos: cos$1,
+ sin: sin$1,
+ acos: acos$1,
+ sqrt: sqrt$1
+} = Math;
+const pi$1 = Math.PI;
+/**
+ * Bezier curve constructor.
+ *
+ * ...docs pending...
+ */
+
+class Bezier {
+ constructor(coords) {
+ let args = coords && coords.forEach ? coords : Array.from(arguments).slice();
+ let coordlen = false;
+
+ if (typeof args[0] === "object") {
+ coordlen = args.length;
+ const newargs = [];
+ args.forEach(function (point) {
+ ["x", "y", "z"].forEach(function (d) {
+ if (typeof point[d] !== "undefined") {
+ newargs.push(point[d]);
+ }
+ });
+ });
+ args = newargs;
+ }
+
+ let higher = false;
+ const len = args.length;
+
+ if (coordlen) {
+ if (coordlen > 4) {
+ if (arguments.length !== 1) {
+ throw new Error("Only new Bezier(point[]) is accepted for 4th and higher order curves");
+ }
+
+ higher = true;
+ }
+ } else {
+ if (len !== 6 && len !== 8 && len !== 9 && len !== 12) {
+ if (arguments.length !== 1) {
+ throw new Error("Only new Bezier(point[]) is accepted for 4th and higher order curves");
+ }
+ }
+ }
+
+ const _3d = this._3d = !higher && (len === 9 || len === 12) || coords && coords[0] && typeof coords[0].z !== "undefined";
+
+ const points = this.points = [];
+
+ for (let idx = 0, step = _3d ? 3 : 2; idx < len; idx += step) {
+ var point = {
+ x: args[idx],
+ y: args[idx + 1]
+ };
+
+ if (_3d) {
+ point.z = args[idx + 2];
+ }
+
+ points.push(point);
+ }
+
+ const order = this.order = points.length - 1;
+ const dims = this.dims = ["x", "y"];
+ if (_3d) dims.push("z");
+ this.dimlen = dims.length;
+ const aligned = utils.align(points, {
+ p1: points[0],
+ p2: points[order]
+ });
+ this._linear = !aligned.some(p => abs$1(p.y) > 0.0001);
+ this._lut = [];
+ this._t1 = 0;
+ this._t2 = 1;
+ this.update();
+ }
+
+ static quadraticFromPoints(p1, p2, p3, t) {
+ if (typeof t === "undefined") {
+ t = 0.5;
+ } // shortcuts, although they're really dumb
+
+
+ if (t === 0) {
+ return new Bezier(p2, p2, p3);
+ }
+
+ if (t === 1) {
+ return new Bezier(p1, p2, p2);
+ } // real fitting.
+
+
+ const abc = Bezier.getABC(2, p1, p2, p3, t);
+ return new Bezier(p1, abc.A, p3);
+ }
+
+ static cubicFromPoints(S, B, E, t, d1) {
+ if (typeof t === "undefined") {
+ t = 0.5;
+ }
+
+ const abc = Bezier.getABC(3, S, B, E, t);
+
+ if (typeof d1 === "undefined") {
+ d1 = utils.dist(B, abc.C);
+ }
+
+ const d2 = d1 * (1 - t) / t;
+ const selen = utils.dist(S, E),
+ lx = (E.x - S.x) / selen,
+ ly = (E.y - S.y) / selen,
+ bx1 = d1 * lx,
+ by1 = d1 * ly,
+ bx2 = d2 * lx,
+ by2 = d2 * ly; // derivation of new hull coordinates
+
+ const e1 = {
+ x: B.x - bx1,
+ y: B.y - by1
+ },
+ e2 = {
+ x: B.x + bx2,
+ y: B.y + by2
+ },
+ A = abc.A,
+ v1 = {
+ x: A.x + (e1.x - A.x) / (1 - t),
+ y: A.y + (e1.y - A.y) / (1 - t)
+ },
+ v2 = {
+ x: A.x + (e2.x - A.x) / t,
+ y: A.y + (e2.y - A.y) / t
+ },
+ nc1 = {
+ x: S.x + (v1.x - S.x) / t,
+ y: S.y + (v1.y - S.y) / t
+ },
+ nc2 = {
+ x: E.x + (v2.x - E.x) / (1 - t),
+ y: E.y + (v2.y - E.y) / (1 - t)
+ }; // ...done
+
+ return new Bezier(S, nc1, nc2, E);
+ }
+
+ static getUtils() {
+ return utils;
+ }
+
+ getUtils() {
+ return Bezier.getUtils();
+ }
+
+ static get PolyBezier() {
+ return PolyBezier;
+ }
+
+ valueOf() {
+ return this.toString();
+ }
+
+ toString() {
+ return utils.pointsToString(this.points);
+ }
+
+ toSVG() {
+ if (this._3d) return false;
+ const p = this.points,
+ x = p[0].x,
+ y = p[0].y,
+ s = ["M", x, y, this.order === 2 ? "Q" : "C"];
+
+ for (let i = 1, last = p.length; i < last; i++) {
+ s.push(p[i].x);
+ s.push(p[i].y);
+ }
+
+ return s.join(" ");
+ }
+
+ setRatios(ratios) {
+ if (ratios.length !== this.points.length) {
+ throw new Error("incorrect number of ratio values");
+ }
+
+ this.ratios = ratios;
+ this._lut = []; // invalidate any precomputed LUT
+ }
+
+ verify() {
+ const print = this.coordDigest();
+
+ if (print !== this._print) {
+ this._print = print;
+ this.update();
+ }
+ }
+
+ coordDigest() {
+ return this.points.map(function (c, pos) {
+ return "" + pos + c.x + c.y + (c.z ? c.z : 0);
+ }).join("");
+ }
+
+ update() {
+ // invalidate any precomputed LUT
+ this._lut = [];
+ this.dpoints = utils.derive(this.points, this._3d);
+ this.computedirection();
+ }
+
+ computedirection() {
+ const points = this.points;
+ const angle = utils.angle(points[0], points[this.order], points[1]);
+ this.clockwise = angle > 0;
+ }
+
+ length() {
+ return utils.length(this.derivative.bind(this));
+ }
+
+ static getABC(order = 2, S, B, E, t = 0.5) {
+ const u = utils.projectionratio(t, order),
+ um = 1 - u,
+ C = {
+ x: u * S.x + um * E.x,
+ y: u * S.y + um * E.y
+ },
+ s = utils.abcratio(t, order),
+ A = {
+ x: B.x + (B.x - C.x) / s,
+ y: B.y + (B.y - C.y) / s
+ };
+ return {
+ A,
+ B,
+ C,
+ S,
+ E
+ };
+ }
+
+ getABC(t, B) {
+ B = B || this.get(t);
+ let S = this.points[0];
+ let E = this.points[this.order];
+ return Bezier.getABC(this.order, S, B, E, t);
+ }
+
+ getLUT(steps) {
+ this.verify();
+ steps = steps || 100;
+
+ if (this._lut.length === steps) {
+ return this._lut;
+ }
+
+ this._lut = []; // We want a range from 0 to 1 inclusive, so
+ // we decrement and then use <= rather than <:
+
+ steps--;
+
+ for (let i = 0, p, t; i < steps; i++) {
+ t = i / (steps - 1);
+ p = this.compute(t);
+ p.t = t;
+
+ this._lut.push(p);
+ }
+
+ return this._lut;
+ }
+
+ on(point, error) {
+ error = error || 5;
+ const lut = this.getLUT(),
+ hits = [];
+
+ for (let i = 0, c, t = 0; i < lut.length; i++) {
+ c = lut[i];
+
+ if (utils.dist(c, point) < error) {
+ hits.push(c);
+ t += i / lut.length;
+ }
+ }
+
+ if (!hits.length) return false;
+ return t /= hits.length;
+ }
+
+ project(point) {
+ // step 1: coarse check
+ const LUT = this.getLUT(),
+ l = LUT.length - 1,
+ closest = utils.closest(LUT, point),
+ mpos = closest.mpos,
+ t1 = (mpos - 1) / l,
+ t2 = (mpos + 1) / l,
+ step = 0.1 / l; // step 2: fine check
+
+ let mdist = closest.mdist,
+ t = t1,
+ ft = t,
+ p;
+ mdist += 1;
+
+ for (let d; t < t2 + step; t += step) {
+ p = this.compute(t);
+ d = utils.dist(point, p);
+
+ if (d < mdist) {
+ mdist = d;
+ ft = t;
+ }
+ }
+
+ ft = ft < 0 ? 0 : ft > 1 ? 1 : ft;
+ p = this.compute(ft);
+ p.t = ft;
+ p.d = mdist;
+ return p;
+ }
+
+ get(t) {
+ return this.compute(t);
+ }
+
+ point(idx) {
+ return this.points[idx];
+ }
+
+ compute(t) {
+ if (this.ratios) {
+ return utils.computeWithRatios(t, this.points, this.ratios, this._3d);
+ }
+
+ return utils.compute(t, this.points, this._3d, this.ratios);
+ }
+
+ raise() {
+ const p = this.points,
+ np = [p[0]],
+ k = p.length;
+
+ for (let i = 1, pi, pim; i < k; i++) {
+ pi = p[i];
+ pim = p[i - 1];
+ np[i] = {
+ x: (k - i) / k * pi.x + i / k * pim.x,
+ y: (k - i) / k * pi.y + i / k * pim.y
+ };
+ }
+
+ np[k] = p[k - 1];
+ return new Bezier(np);
+ }
+
+ derivative(t) {
+ return utils.compute(t, this.dpoints[0]);
+ }
+
+ dderivative(t) {
+ return utils.compute(t, this.dpoints[1]);
+ }
+
+ align() {
+ let p = this.points;
+ return new Bezier(utils.align(p, {
+ p1: p[0],
+ p2: p[p.length - 1]
+ }));
+ }
+
+ curvature(t) {
+ return utils.curvature(t, this.dpoints[0], this.dpoints[1], this._3d);
+ }
+
+ inflections() {
+ return utils.inflections(this.points);
+ }
+
+ normal(t) {
+ return this._3d ? this.__normal3(t) : this.__normal2(t);
+ }
+
+ __normal2(t) {
+ const d = this.derivative(t);
+ const q = sqrt$1(d.x * d.x + d.y * d.y);
+ return {
+ x: -d.y / q,
+ y: d.x / q
+ };
+ }
+
+ __normal3(t) {
+ // see http://stackoverflow.com/questions/25453159
+ const r1 = this.derivative(t),
+ r2 = this.derivative(t + 0.01),
+ q1 = sqrt$1(r1.x * r1.x + r1.y * r1.y + r1.z * r1.z),
+ q2 = sqrt$1(r2.x * r2.x + r2.y * r2.y + r2.z * r2.z);
+ r1.x /= q1;
+ r1.y /= q1;
+ r1.z /= q1;
+ r2.x /= q2;
+ r2.y /= q2;
+ r2.z /= q2; // cross product
+
+ const c = {
+ x: r2.y * r1.z - r2.z * r1.y,
+ y: r2.z * r1.x - r2.x * r1.z,
+ z: r2.x * r1.y - r2.y * r1.x
+ };
+ const m = sqrt$1(c.x * c.x + c.y * c.y + c.z * c.z);
+ c.x /= m;
+ c.y /= m;
+ c.z /= m; // rotation matrix
+
+ const R = [c.x * c.x, c.x * c.y - c.z, c.x * c.z + c.y, c.x * c.y + c.z, c.y * c.y, c.y * c.z - c.x, c.x * c.z - c.y, c.y * c.z + c.x, c.z * c.z]; // normal vector:
+
+ const n = {
+ x: R[0] * r1.x + R[1] * r1.y + R[2] * r1.z,
+ y: R[3] * r1.x + R[4] * r1.y + R[5] * r1.z,
+ z: R[6] * r1.x + R[7] * r1.y + R[8] * r1.z
+ };
+ return n;
+ }
+
+ hull(t) {
+ let p = this.points,
+ _p = [],
+ q = [],
+ idx = 0;
+ q[idx++] = p[0];
+ q[idx++] = p[1];
+ q[idx++] = p[2];
+
+ if (this.order === 3) {
+ q[idx++] = p[3];
+ } // we lerp between all points at each iteration, until we have 1 point left.
+
+
+ while (p.length > 1) {
+ _p = [];
+
+ for (let i = 0, pt, l = p.length - 1; i < l; i++) {
+ pt = utils.lerp(t, p[i], p[i + 1]);
+ q[idx++] = pt;
+
+ _p.push(pt);
+ }
+
+ p = _p;
+ }
+
+ return q;
+ }
+
+ split(t1, t2) {
+ // shortcuts
+ if (t1 === 0 && !!t2) {
+ return this.split(t2).left;
+ }
+
+ if (t2 === 1) {
+ return this.split(t1).right;
+ } // no shortcut: use "de Casteljau" iteration.
+
+
+ const q = this.hull(t1);
+ const result = {
+ left: this.order === 2 ? new Bezier([q[0], q[3], q[5]]) : new Bezier([q[0], q[4], q[7], q[9]]),
+ right: this.order === 2 ? new Bezier([q[5], q[4], q[2]]) : new Bezier([q[9], q[8], q[6], q[3]]),
+ span: q
+ }; // make sure we bind _t1/_t2 information!
+
+ result.left._t1 = utils.map(0, 0, 1, this._t1, this._t2);
+ result.left._t2 = utils.map(t1, 0, 1, this._t1, this._t2);
+ result.right._t1 = utils.map(t1, 0, 1, this._t1, this._t2);
+ result.right._t2 = utils.map(1, 0, 1, this._t1, this._t2); // if we have no t2, we're done
+
+ if (!t2) {
+ return result;
+ } // if we have a t2, split again:
+
+
+ t2 = utils.map(t2, t1, 1, 0, 1);
+ return result.right.split(t2).left;
+ }
+
+ extrema() {
+ const result = {};
+ let roots = [];
+ this.dims.forEach(function (dim) {
+ let mfn = function (v) {
+ return v[dim];
+ };
+
+ let p = this.dpoints[0].map(mfn);
+ result[dim] = utils.droots(p);
+
+ if (this.order === 3) {
+ p = this.dpoints[1].map(mfn);
+ result[dim] = result[dim].concat(utils.droots(p));
+ }
+
+ result[dim] = result[dim].filter(function (t) {
+ return t >= 0 && t <= 1;
+ });
+ roots = roots.concat(result[dim].sort(utils.numberSort));
+ }.bind(this));
+ result.values = roots.sort(utils.numberSort).filter(function (v, idx) {
+ return roots.indexOf(v) === idx;
+ });
+ return result;
+ }
+
+ bbox() {
+ const extrema = this.extrema(),
+ result = {};
+ this.dims.forEach(function (d) {
+ result[d] = utils.getminmax(this, d, extrema[d]);
+ }.bind(this));
+ return result;
+ }
+
+ overlaps(curve) {
+ const lbbox = this.bbox(),
+ tbbox = curve.bbox();
+ return utils.bboxoverlap(lbbox, tbbox);
+ }
+
+ offset(t, d) {
+ if (typeof d !== "undefined") {
+ const c = this.get(t),
+ n = this.normal(t);
+ const ret = {
+ c: c,
+ n: n,
+ x: c.x + n.x * d,
+ y: c.y + n.y * d
+ };
+
+ if (this._3d) {
+ ret.z = c.z + n.z * d;
+ }
+
+ return ret;
+ }
+
+ if (this._linear) {
+ const nv = this.normal(0),
+ coords = this.points.map(function (p) {
+ const ret = {
+ x: p.x + t * nv.x,
+ y: p.y + t * nv.y
+ };
+
+ if (p.z && nv.z) {
+ ret.z = p.z + t * nv.z;
+ }
+
+ return ret;
+ });
+ return [new Bezier(coords)];
+ }
+
+ return this.reduce().map(function (s) {
+ if (s._linear) {
+ return s.offset(t)[0];
+ }
+
+ return s.scale(t);
+ });
+ }
+
+ simple() {
+ if (this.order === 3) {
+ const a1 = utils.angle(this.points[0], this.points[3], this.points[1]);
+ const a2 = utils.angle(this.points[0], this.points[3], this.points[2]);
+ if (a1 > 0 && a2 < 0 || a1 < 0 && a2 > 0) return false;
+ }
+
+ const n1 = this.normal(0);
+ const n2 = this.normal(1);
+ let s = n1.x * n2.x + n1.y * n2.y;
+
+ if (this._3d) {
+ s += n1.z * n2.z;
+ }
+
+ return abs$1(acos$1(s)) < pi$1 / 3;
+ }
+
+ reduce() {
+ // TODO: examine these var types in more detail...
+ let i,
+ t1 = 0,
+ t2 = 0,
+ step = 0.01,
+ segment,
+ pass1 = [],
+ pass2 = []; // first pass: split on extrema
+
+ let extrema = this.extrema().values;
+
+ if (extrema.indexOf(0) === -1) {
+ extrema = [0].concat(extrema);
+ }
+
+ if (extrema.indexOf(1) === -1) {
+ extrema.push(1);
+ }
+
+ for (t1 = extrema[0], i = 1; i < extrema.length; i++) {
+ t2 = extrema[i];
+ segment = this.split(t1, t2);
+ segment._t1 = t1;
+ segment._t2 = t2;
+ pass1.push(segment);
+ t1 = t2;
+ } // second pass: further reduce these segments to simple segments
+
+
+ pass1.forEach(function (p1) {
+ t1 = 0;
+ t2 = 0;
+
+ while (t2 <= 1) {
+ for (t2 = t1 + step; t2 <= 1 + step; t2 += step) {
+ segment = p1.split(t1, t2);
+
+ if (!segment.simple()) {
+ t2 -= step;
+
+ if (abs$1(t1 - t2) < step) {
+ // we can never form a reduction
+ return [];
+ }
+
+ segment = p1.split(t1, t2);
+ segment._t1 = utils.map(t1, 0, 1, p1._t1, p1._t2);
+ segment._t2 = utils.map(t2, 0, 1, p1._t1, p1._t2);
+ pass2.push(segment);
+ t1 = t2;
+ break;
+ }
+ }
+ }
+
+ if (t1 < 1) {
+ segment = p1.split(t1, 1);
+ segment._t1 = utils.map(t1, 0, 1, p1._t1, p1._t2);
+ segment._t2 = p1._t2;
+ pass2.push(segment);
+ }
+ });
+ return pass2;
+ }
+
+ scale(d) {
+ const order = this.order;
+ let distanceFn = false;
+
+ if (typeof d === "function") {
+ distanceFn = d;
+ }
+
+ if (distanceFn && order === 2) {
+ return this.raise().scale(distanceFn);
+ } // TODO: add special handling for degenerate (=linear) curves.
+
+
+ const clockwise = this.clockwise;
+ const r1 = distanceFn ? distanceFn(0) : d;
+ const r2 = distanceFn ? distanceFn(1) : d;
+ const v = [this.offset(0, 10), this.offset(1, 10)];
+ const points = this.points;
+ const np = [];
+ const o = utils.lli4(v[0], v[0].c, v[1], v[1].c);
+
+ if (!o) {
+ throw new Error("cannot scale this curve. Try reducing it first.");
+ } // move all points by distance 'd' wrt the origin 'o'
+ // move end points by fixed distance along normal.
+
+
+ [0, 1].forEach(function (t) {
+ const p = np[t * order] = utils.copy(points[t * order]);
+ p.x += (t ? r2 : r1) * v[t].n.x;
+ p.y += (t ? r2 : r1) * v[t].n.y;
+ });
+
+ if (!distanceFn) {
+ // move control points to lie on the intersection of the offset
+ // derivative vector, and the origin-through-control vector
+ [0, 1].forEach(t => {
+ if (order === 2 && !!t) return;
+ const p = np[t * order];
+ const d = this.derivative(t);
+ const p2 = {
+ x: p.x + d.x,
+ y: p.y + d.y
+ };
+ np[t + 1] = utils.lli4(p, p2, o, points[t + 1]);
+ });
+ return new Bezier(np);
+ } // move control points by "however much necessary to
+ // ensure the correct tangent to endpoint".
+
+
+ [0, 1].forEach(function (t) {
+ if (order === 2 && !!t) return;
+ var p = points[t + 1];
+ var ov = {
+ x: p.x - o.x,
+ y: p.y - o.y
+ };
+ var rc = distanceFn ? distanceFn((t + 1) / order) : d;
+ if (distanceFn && !clockwise) rc = -rc;
+ var m = sqrt$1(ov.x * ov.x + ov.y * ov.y);
+ ov.x /= m;
+ ov.y /= m;
+ np[t + 1] = {
+ x: p.x + rc * ov.x,
+ y: p.y + rc * ov.y
+ };
+ });
+ return new Bezier(np);
+ }
+
+ outline(d1, d2, d3, d4) {
+ d2 = typeof d2 === "undefined" ? d1 : d2;
+ const reduced = this.reduce(),
+ len = reduced.length,
+ fcurves = [];
+ let bcurves = [],
+ p,
+ alen = 0,
+ tlen = this.length();
+ const graduated = typeof d3 !== "undefined" && typeof d4 !== "undefined";
+
+ function linearDistanceFunction(s, e, tlen, alen, slen) {
+ return function (v) {
+ const f1 = alen / tlen,
+ f2 = (alen + slen) / tlen,
+ d = e - s;
+ return utils.map(v, 0, 1, s + f1 * d, s + f2 * d);
+ };
+ } // form curve oulines
+
+
+ reduced.forEach(function (segment) {
+ const slen = segment.length();
+
+ if (graduated) {
+ fcurves.push(segment.scale(linearDistanceFunction(d1, d3, tlen, alen, slen)));
+ bcurves.push(segment.scale(linearDistanceFunction(-d2, -d4, tlen, alen, slen)));
+ } else {
+ fcurves.push(segment.scale(d1));
+ bcurves.push(segment.scale(-d2));
+ }
+
+ alen += slen;
+ }); // reverse the "return" outline
+
+ bcurves = bcurves.map(function (s) {
+ p = s.points;
+
+ if (p[3]) {
+ s.points = [p[3], p[2], p[1], p[0]];
+ } else {
+ s.points = [p[2], p[1], p[0]];
+ }
+
+ return s;
+ }).reverse(); // form the endcaps as lines
+
+ const fs = fcurves[0].points[0],
+ fe = fcurves[len - 1].points[fcurves[len - 1].points.length - 1],
+ bs = bcurves[len - 1].points[bcurves[len - 1].points.length - 1],
+ be = bcurves[0].points[0],
+ ls = utils.makeline(bs, fs),
+ le = utils.makeline(fe, be),
+ segments = [ls].concat(fcurves).concat([le]).concat(bcurves);
+ return new PolyBezier(segments);
+ }
+
+ outlineshapes(d1, d2, curveIntersectionThreshold) {
+ d2 = d2 || d1;
+ const outline = this.outline(d1, d2).curves;
+ const shapes = [];
+
+ for (let i = 1, len = outline.length; i < len / 2; i++) {
+ const shape = utils.makeshape(outline[i], outline[len - i], curveIntersectionThreshold);
+ shape.startcap.virtual = i > 1;
+ shape.endcap.virtual = i < len / 2 - 1;
+ shapes.push(shape);
+ }
+
+ return shapes;
+ }
+
+ intersects(curve, curveIntersectionThreshold) {
+ if (!curve) return this.selfintersects(curveIntersectionThreshold);
+
+ if (curve.p1 && curve.p2) {
+ return this.lineIntersects(curve);
+ }
+
+ if (curve instanceof Bezier) {
+ curve = curve.reduce();
+ }
+
+ return this.curveintersects(this.reduce(), curve, curveIntersectionThreshold);
+ }
+
+ lineIntersects(line) {
+ const mx = min(line.p1.x, line.p2.x),
+ my = min(line.p1.y, line.p2.y),
+ MX = max(line.p1.x, line.p2.x),
+ MY = max(line.p1.y, line.p2.y);
+ return utils.roots(this.points, line).filter(t => {
+ var p = this.get(t);
+ return utils.between(p.x, mx, MX) && utils.between(p.y, my, MY);
+ });
+ }
+
+ selfintersects(curveIntersectionThreshold) {
+ // "simple" curves cannot intersect with their direct
+ // neighbour, so for each segment X we check whether
+ // it intersects [0:x-2][x+2:last].
+ const reduced = this.reduce(),
+ len = reduced.length - 2,
+ results = [];
+
+ for (let i = 0, result, left, right; i < len; i++) {
+ left = reduced.slice(i, i + 1);
+ right = reduced.slice(i + 2);
+ result = this.curveintersects(left, right, curveIntersectionThreshold);
+ results.push(...result);
+ }
+
+ return results;
+ }
+
+ curveintersects(c1, c2, curveIntersectionThreshold) {
+ const pairs = []; // step 1: pair off any overlapping segments
+
+ c1.forEach(function (l) {
+ c2.forEach(function (r) {
+ if (l.overlaps(r)) {
+ pairs.push({
+ left: l,
+ right: r
+ });
+ }
+ });
+ }); // step 2: for each pairing, run through the convergence algorithm.
+
+ let intersections = [];
+ pairs.forEach(function (pair) {
+ const result = utils.pairiteration(pair.left, pair.right, curveIntersectionThreshold);
+
+ if (result.length > 0) {
+ intersections = intersections.concat(result);
+ }
+ });
+ return intersections;
+ }
+
+ arcs(errorThreshold) {
+ errorThreshold = errorThreshold || 0.5;
+ return this._iterate(errorThreshold, []);
+ }
+
+ _error(pc, np1, s, e) {
+ const q = (e - s) / 4,
+ c1 = this.get(s + q),
+ c2 = this.get(e - q),
+ ref = utils.dist(pc, np1),
+ d1 = utils.dist(pc, c1),
+ d2 = utils.dist(pc, c2);
+ return abs$1(d1 - ref) + abs$1(d2 - ref);
+ }
+
+ _iterate(errorThreshold, circles) {
+ let t_s = 0,
+ t_e = 1,
+ safety; // we do a binary search to find the "good `t` closest to no-longer-good"
+
+ do {
+ safety = 0; // step 1: start with the maximum possible arc
+
+ t_e = 1; // points:
+
+ let np1 = this.get(t_s),
+ np2,
+ np3,
+ arc,
+ prev_arc; // booleans:
+
+ let curr_good = false,
+ prev_good = false,
+ done; // numbers:
+
+ let t_m = t_e,
+ prev_e = 1; // step 2: find the best possible arc
+
+ do {
+ prev_good = curr_good;
+ prev_arc = arc;
+ t_m = (t_s + t_e) / 2;
+ np2 = this.get(t_m);
+ np3 = this.get(t_e);
+ arc = utils.getccenter(np1, np2, np3); //also save the t values
+
+ arc.interval = {
+ start: t_s,
+ end: t_e
+ };
+
+ let error = this._error(arc, np1, t_s, t_e);
+
+ curr_good = error <= errorThreshold;
+ done = prev_good && !curr_good;
+ if (!done) prev_e = t_e; // this arc is fine: we can move 'e' up to see if we can find a wider arc
+
+ if (curr_good) {
+ // if e is already at max, then we're done for this arc.
+ if (t_e >= 1) {
+ // make sure we cap at t=1
+ arc.interval.end = prev_e = 1;
+ prev_arc = arc; // if we capped the arc segment to t=1 we also need to make sure that
+ // the arc's end angle is correct with respect to the bezier end point.
+
+ if (t_e > 1) {
+ let d = {
+ x: arc.x + arc.r * cos$1(arc.e),
+ y: arc.y + arc.r * sin$1(arc.e)
+ };
+ arc.e += utils.angle({
+ x: arc.x,
+ y: arc.y
+ }, d, this.get(1));
+ }
+
+ break;
+ } // if not, move it up by half the iteration distance
+
+
+ t_e = t_e + (t_e - t_s) / 2;
+ } else {
+ // this is a bad arc: we need to move 'e' down to find a good arc
+ t_e = t_m;
+ }
+ } while (!done && safety++ < 100);
+
+ if (safety >= 100) {
+ break;
+ } // console.log("L835: [F] arc found", t_s, prev_e, prev_arc.x, prev_arc.y, prev_arc.s, prev_arc.e);
+
+
+ prev_arc = prev_arc ? prev_arc : arc;
+ circles.push(prev_arc);
+ t_s = prev_e;
+ } while (t_e < 1);
+
+ return circles;
+ }
+
+}
+
+exports.Bezier = Bezier;