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 |
|
39 | const findDotsAtSegment = (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) => {
|
40 | const t1 = 1 - t;
|
41 | const t12 = t1 * t1;
|
42 | const t13 = t12 * t1;
|
43 | const t2 = t * t;
|
44 | const t3 = t2 * t;
|
45 | const x = t13 * p1x + t12 * 3 * t * c1x + t1 * 3 * t * t * c2x + t3 * p2x;
|
46 | const y = t13 * p1y + t12 * 3 * t * c1y + t1 * 3 * t * t * c2y + t3 * p2y;
|
47 | return { x, y };
|
48 | };
|
49 |
|
50 | const catmullRom2bezier = (crp, z) => {
|
51 | const d = [];
|
52 | let end = { x: +crp[0], y: +crp[1] };
|
53 | for (let i = 0, iLen = crp.length; iLen - 2 * !z > i; i += 2) {
|
54 | const p = [
|
55 | { x: +crp[i - 2], y: +crp[i - 1] },
|
56 | { x: +crp[i], y: +crp[i + 1] },
|
57 | { x: +crp[i + 2], y: +crp[i + 3] },
|
58 | { x: +crp[i + 4], y: +crp[i + 5] },
|
59 | ];
|
60 | if (z) {
|
61 | if (!i) {
|
62 | p[0] = { x: +crp[iLen - 2], y: +crp[iLen - 1] };
|
63 | } else if (iLen - 4 === i) {
|
64 | p[3] = { x: +crp[0], y: +crp[1] };
|
65 | } else if (iLen - 2 === i) {
|
66 | p[2] = { x: +crp[0], y: +crp[1] };
|
67 | p[3] = { x: +crp[2], y: +crp[3] };
|
68 | }
|
69 | } else if (iLen - 4 === i) {
|
70 | p[3] = p[2];
|
71 | } else if (!i) {
|
72 | p[0] = { x: +crp[i], y: +crp[i + 1] };
|
73 | }
|
74 | d.push([
|
75 | end.x,
|
76 | end.y,
|
77 | (-p[0].x + 6 * p[1].x + p[2].x) / 6,
|
78 | (-p[0].y + 6 * p[1].y + p[2].y) / 6,
|
79 | (p[1].x + 6 * p[2].x - p[3].x) / 6,
|
80 | (p[1].y + 6 * p[2].y - p[3].y) / 6,
|
81 | p[2].x,
|
82 | p[2].y,
|
83 | ]);
|
84 | end = p[2];
|
85 | }
|
86 |
|
87 | return d;
|
88 | };
|
89 |
|
90 | const bezlen2 = (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) => {
|
91 | const n = 5;
|
92 | let x0 = p1x;
|
93 | let y0 = p1y;
|
94 | let len = 0;
|
95 | for (let i = 1; i < n; i++) {
|
96 | const { x, y } = findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, i / n);
|
97 | len += Math.hypot(x - x0, y - y0);
|
98 | x0 = x;
|
99 | y0 = y;
|
100 | }
|
101 | len += Math.hypot(p2x - x0, p2y - y0);
|
102 | return len;
|
103 | };
|
104 |
|
105 | const prepareCurve = (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) => {
|
106 | const len = Math.floor(bezlen2(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) * .75);
|
107 | const fs = [];
|
108 | let oldi = 0;
|
109 | for (let i = 0; i <= len; i++) {
|
110 | const t = i / len;
|
111 | const xy = findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t);
|
112 | const index = Math.round(xy.x);
|
113 | fs[index] = xy.y;
|
114 | if (index - oldi > 1) {
|
115 | const s = fs[oldi];
|
116 | const f = fs[index];
|
117 | for (let j = oldi + 1; j < index; j++) {
|
118 | fs[j] = s + ((f - s) / (index - oldi)) * (j - oldi);
|
119 | }
|
120 | }
|
121 | oldi = index;
|
122 | }
|
123 | return (x) => fs[Math.round(x)] || null;
|
124 | };
|
125 |
|
126 |
|
127 | export {
|
128 | bezlen,
|
129 | findDotsAtSegment,
|
130 | prepareCurve,
|
131 | catmullRom2bezier
|
132 | };
|
133 |
|