1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 | const base3 = (t, p1, p2, p3, p4) => {
|
14 | const t1 = -3 * p1 + 9 * p2 - 9 * p3 + 3 * p4;
|
15 | const t2 = t * t1 + 6 * p1 - 12 * p2 + 6 * p3;
|
16 | return t * t2 - 3 * p1 + 3 * p2;
|
17 | };
|
18 |
|
19 | const bezlen = (x1, y1, x2, y2, x3, y3, x4, y4, z) => {
|
20 | if (z == null) {
|
21 | z = 1;
|
22 | }
|
23 | z = Math.max(0, Math.min(z, 1));
|
24 | const z2 = z / 2;
|
25 | const n = 12;
|
26 | const Tvalues = [-.1252, .1252, -.3678, .3678, -.5873, .5873, -.7699, .7699, -.9041, .9041, -.9816, .9816];
|
27 | const Cvalues = [0.2491, 0.2491, 0.2335, 0.2335, 0.2032, 0.2032, 0.1601, 0.1601, 0.1069, 0.1069, 0.0472, 0.0472];
|
28 | let sum = 0;
|
29 | for (let i = 0; i < n; i++) {
|
30 | const ct = z2 * Tvalues[i] + z2;
|
31 | const xbase = base3(ct, x1, x2, x3, x4);
|
32 | const ybase = base3(ct, y1, y2, y3, y4);
|
33 | const comb = xbase * xbase + ybase * ybase;
|
34 | sum += Cvalues[i] * Math.sqrt(comb);
|
35 | }
|
36 | return z2 * sum;
|
37 | };
|
38 | exports.bezlen = bezlen;
|
39 |
|
40 | const findDotsAtSegment = (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) => {
|
41 | const t1 = 1 - t;
|
42 | const t12 = t1 * t1;
|
43 | const t13 = t12 * t1;
|
44 | const t2 = t * t;
|
45 | const t3 = t2 * t;
|
46 | const x = t13 * p1x + t12 * 3 * t * c1x + t1 * 3 * t * t * c2x + t3 * p2x;
|
47 | const y = t13 * p1y + t12 * 3 * t * c1y + t1 * 3 * t * t * c2y + t3 * p2y;
|
48 | return { x, y };
|
49 | };
|
50 | exports.findDotsAtSegment = findDotsAtSegment;
|
51 |
|
52 | exports.catmullRom2bezier = (crp, z) => {
|
53 | const d = [];
|
54 | let end = { x: +crp[0], y: +crp[1] };
|
55 | for (let i = 0, iLen = crp.length; iLen - 2 * !z > i; i += 2) {
|
56 | const p = [
|
57 | { x: +crp[i - 2], y: +crp[i - 1] },
|
58 | { x: +crp[i], y: +crp[i + 1] },
|
59 | { x: +crp[i + 2], y: +crp[i + 3] },
|
60 | { x: +crp[i + 4], y: +crp[i + 5] },
|
61 | ];
|
62 | if (z) {
|
63 | if (!i) {
|
64 | p[0] = { x: +crp[iLen - 2], y: +crp[iLen - 1] };
|
65 | } else if (iLen - 4 === i) {
|
66 | p[3] = { x: +crp[0], y: +crp[1] };
|
67 | } else if (iLen - 2 === i) {
|
68 | p[2] = { x: +crp[0], y: +crp[1] };
|
69 | p[3] = { x: +crp[2], y: +crp[3] };
|
70 | }
|
71 | } else if (iLen - 4 === i) {
|
72 | p[3] = p[2];
|
73 | } else if (!i) {
|
74 | p[0] = { x: +crp[i], y: +crp[i + 1] };
|
75 | }
|
76 | d.push([
|
77 | end.x,
|
78 | end.y,
|
79 | (-p[0].x + 6 * p[1].x + p[2].x) / 6,
|
80 | (-p[0].y + 6 * p[1].y + p[2].y) / 6,
|
81 | (p[1].x + 6 * p[2].x - p[3].x) / 6,
|
82 | (p[1].y + 6 * p[2].y - p[3].y) / 6,
|
83 | p[2].x,
|
84 | p[2].y,
|
85 | ]);
|
86 | end = p[2];
|
87 | }
|
88 |
|
89 | return d;
|
90 | };
|
91 |
|
92 | const bezlen2 = (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) => {
|
93 | const n = 5;
|
94 | let x0 = p1x;
|
95 | let y0 = p1y;
|
96 | let len = 0;
|
97 | for (let i = 1; i < n; i++) {
|
98 | const { x, y } = findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, i / n);
|
99 | len += Math.hypot(x - x0, y - y0);
|
100 | x0 = x;
|
101 | y0 = y;
|
102 | }
|
103 | len += Math.hypot(p2x - x0, p2y - y0);
|
104 | return len;
|
105 | };
|
106 |
|
107 | exports.prepareCurve = (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) => {
|
108 | const len = Math.floor(bezlen2(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) * .75);
|
109 | const fs = [];
|
110 | let oldi = 0;
|
111 | for (let i = 0; i <= len; i++) {
|
112 | const t = i / len;
|
113 | const xy = findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t);
|
114 | const index = Math.round(xy.x);
|
115 | fs[index] = xy.y;
|
116 | if (index - oldi > 1) {
|
117 | const s = fs[oldi];
|
118 | const f = fs[index];
|
119 | for (let j = oldi + 1; j < index; j++) {
|
120 | fs[j] = s + ((f - s) / (index - oldi)) * (j - oldi);
|
121 | }
|
122 | }
|
123 | oldi = index;
|
124 | }
|
125 | return (x) => fs[Math.round(x)] || null;
|
126 | };
|