@allyhub/sdk 0.0.0-stage → 0.1.0-alpha.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/LICENSE +241 -0
  2. package/NOTICE +81 -0
  3. package/README.md +16 -2
  4. package/client.d.ts +86 -0
  5. package/dist/anim.d.ts +41 -0
  6. package/dist/anim.d.ts.map +1 -0
  7. package/dist/anim.js +75 -0
  8. package/dist/anim.js.map +1 -0
  9. package/dist/define.d.ts +75 -0
  10. package/dist/define.d.ts.map +1 -0
  11. package/dist/define.js +95 -0
  12. package/dist/define.js.map +1 -0
  13. package/dist/dom.d.ts +210 -0
  14. package/dist/dom.d.ts.map +1 -0
  15. package/dist/dom.js +457 -0
  16. package/dist/dom.js.map +1 -0
  17. package/dist/fit.d.ts +24 -0
  18. package/dist/fit.d.ts.map +1 -0
  19. package/dist/fit.js +60 -0
  20. package/dist/fit.js.map +1 -0
  21. package/dist/index.d.ts +14 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +11 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/media.d.ts +9 -0
  26. package/dist/media.d.ts.map +1 -0
  27. package/dist/media.js +27 -0
  28. package/dist/media.js.map +1 -0
  29. package/dist/messages.d.ts +39 -0
  30. package/dist/messages.d.ts.map +1 -0
  31. package/dist/messages.js +2 -0
  32. package/dist/messages.js.map +1 -0
  33. package/dist/noise.d.ts +15 -0
  34. package/dist/noise.d.ts.map +1 -0
  35. package/dist/noise.js +233 -0
  36. package/dist/noise.js.map +1 -0
  37. package/dist/parts.d.ts +140 -0
  38. package/dist/parts.d.ts.map +1 -0
  39. package/dist/parts.js +414 -0
  40. package/dist/parts.js.map +1 -0
  41. package/dist/paths.d.ts +85 -0
  42. package/dist/paths.d.ts.map +1 -0
  43. package/dist/paths.js +720 -0
  44. package/dist/paths.js.map +1 -0
  45. package/dist/piece.d.ts +64 -0
  46. package/dist/piece.d.ts.map +1 -0
  47. package/dist/piece.js +38 -0
  48. package/dist/piece.js.map +1 -0
  49. package/dist/props.d.ts +81 -0
  50. package/dist/props.d.ts.map +1 -0
  51. package/dist/props.js +35 -0
  52. package/dist/props.js.map +1 -0
  53. package/dist/text.d.ts +13 -0
  54. package/dist/text.d.ts.map +1 -0
  55. package/dist/text.js +38 -0
  56. package/dist/text.js.map +1 -0
  57. package/package.json +61 -3
package/dist/paths.js ADDED
@@ -0,0 +1,720 @@
1
+ // SVG paths measured without a browser: a path's length, the point and the
2
+ // direction at a length, the stroke-dash values that draw it on, the stretch
3
+ // of it between two fractions of its length, and the path between two paths
4
+ // that have the same commands. Pure functions of the path's text, so they
5
+ // give the same numbers in mount, in render and in a test.
6
+ //
7
+ // After Remotion's @remotion/paths
8
+ // (https://github.com/remotion-dev/remotion/tree/main/packages/paths,
9
+ // Copyright (c) 2023 JonnyBurger, MIT): getLength, getPointAtLength,
10
+ // getTangentAtLength, evolvePath and interpolatePath, written again and
11
+ // smaller. Arcs become cubic curves by the SVG specification's endpoint to
12
+ // centre conversion (SVG 1.1, appendix F.6). See NOTICE.
13
+ // ---- reading a path ---------------------------------------------------------
14
+ const NUMBER = /[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?/y;
15
+ const COMMAND = /[MmLlHhVvCcSsQqTtAaZz]/;
16
+ const SEPARATOR = /[\s,]/;
17
+ class Reader {
18
+ i = 0;
19
+ d;
20
+ constructor(d) {
21
+ this.d = d;
22
+ }
23
+ skip() {
24
+ while (this.i < this.d.length && SEPARATOR.test(this.d.charAt(this.i)))
25
+ this.i++;
26
+ }
27
+ done() {
28
+ this.skip();
29
+ return this.i >= this.d.length;
30
+ }
31
+ command() {
32
+ this.skip();
33
+ const c = this.d.charAt(this.i);
34
+ if (!COMMAND.test(c))
35
+ return undefined;
36
+ this.i++;
37
+ return c;
38
+ }
39
+ number() {
40
+ this.skip();
41
+ NUMBER.lastIndex = this.i;
42
+ const m = NUMBER.exec(this.d);
43
+ if (!m)
44
+ throw this.error("a number");
45
+ this.i = NUMBER.lastIndex;
46
+ return Number(m[0]);
47
+ }
48
+ flag() {
49
+ this.skip();
50
+ const c = this.d.charAt(this.i);
51
+ if (c !== "0" && c !== "1")
52
+ throw this.error("an arc flag, 0 or 1");
53
+ this.i++;
54
+ return c === "1" ? 1 : 0;
55
+ }
56
+ error(what) {
57
+ const at = this.d.slice(this.i, this.i + 12);
58
+ return new Error(`Path "${this.d}": expected ${what} at character ${String(this.i)}${at ? ` ("${at}")` : ""}`);
59
+ }
60
+ }
61
+ /** A path's commands, absolute: H and V as L, S as C, T as Q. */
62
+ function parse(d) {
63
+ const r = new Reader(d);
64
+ const out = [];
65
+ let cx = 0;
66
+ let cy = 0;
67
+ let sx = 0;
68
+ let sy = 0;
69
+ // The last control point of a C or Q, for S and T to reflect.
70
+ let ctrl = null;
71
+ let letter;
72
+ while (!r.done()) {
73
+ const read = r.command();
74
+ if (read !== undefined)
75
+ letter = read;
76
+ else if (letter === undefined || letter === "Z" || letter === "z")
77
+ throw r.error("a command");
78
+ else if (letter === "M")
79
+ letter = "L";
80
+ else if (letter === "m")
81
+ letter = "l";
82
+ if (letter === undefined)
83
+ break;
84
+ if (out.length === 0 && letter !== "M" && letter !== "m")
85
+ throw new Error(`Path "${d}": a path starts with M`);
86
+ const rel = letter === letter.toLowerCase();
87
+ const ox = rel ? cx : 0;
88
+ const oy = rel ? cy : 0;
89
+ switch (letter.toUpperCase()) {
90
+ case "M": {
91
+ cx = sx = ox + r.number();
92
+ cy = sy = oy + r.number();
93
+ out.push({ type: "M", x: cx, y: cy });
94
+ ctrl = null;
95
+ break;
96
+ }
97
+ case "L":
98
+ case "H":
99
+ case "V": {
100
+ const up = letter.toUpperCase();
101
+ if (up === "L") {
102
+ cx = ox + r.number();
103
+ cy = oy + r.number();
104
+ }
105
+ else if (up === "H")
106
+ cx = ox + r.number();
107
+ else
108
+ cy = oy + r.number();
109
+ out.push({ type: "L", x: cx, y: cy });
110
+ ctrl = null;
111
+ break;
112
+ }
113
+ case "C":
114
+ case "S": {
115
+ let x1;
116
+ let y1;
117
+ if (letter.toUpperCase() === "C") {
118
+ x1 = ox + r.number();
119
+ y1 = oy + r.number();
120
+ }
121
+ else {
122
+ x1 = ctrl?.type === "C" ? 2 * cx - ctrl.x : cx;
123
+ y1 = ctrl?.type === "C" ? 2 * cy - ctrl.y : cy;
124
+ }
125
+ const x2 = ox + r.number();
126
+ const y2 = oy + r.number();
127
+ cx = ox + r.number();
128
+ cy = oy + r.number();
129
+ out.push({ type: "C", x1, y1, x2, y2, x: cx, y: cy });
130
+ ctrl = { type: "C", x: x2, y: y2 };
131
+ break;
132
+ }
133
+ case "Q":
134
+ case "T": {
135
+ let x1;
136
+ let y1;
137
+ if (letter.toUpperCase() === "Q") {
138
+ x1 = ox + r.number();
139
+ y1 = oy + r.number();
140
+ }
141
+ else {
142
+ x1 = ctrl?.type === "Q" ? 2 * cx - ctrl.x : cx;
143
+ y1 = ctrl?.type === "Q" ? 2 * cy - ctrl.y : cy;
144
+ }
145
+ cx = ox + r.number();
146
+ cy = oy + r.number();
147
+ out.push({ type: "Q", x1, y1, x: cx, y: cy });
148
+ ctrl = { type: "Q", x: x1, y: y1 };
149
+ break;
150
+ }
151
+ case "A": {
152
+ const rx = r.number();
153
+ const ry = r.number();
154
+ const angle = r.number();
155
+ const large = r.flag();
156
+ const sweep = r.flag();
157
+ cx = ox + r.number();
158
+ cy = oy + r.number();
159
+ out.push({ type: "A", rx, ry, angle, large, sweep, x: cx, y: cy });
160
+ ctrl = null;
161
+ break;
162
+ }
163
+ default: {
164
+ out.push({ type: "Z" });
165
+ cx = sx;
166
+ cy = sy;
167
+ ctrl = null;
168
+ }
169
+ }
170
+ }
171
+ return out;
172
+ }
173
+ /** A number as a path writes it: at most 4 decimals, no `-0`. */
174
+ function fmt(n) {
175
+ const r = Math.round(n * 1e4) / 1e4;
176
+ return String(r === 0 ? 0 : r);
177
+ }
178
+ function write(commands) {
179
+ return commands
180
+ .map((c) => {
181
+ switch (c.type) {
182
+ case "M":
183
+ case "L":
184
+ return `${c.type}${fmt(c.x)} ${fmt(c.y)}`;
185
+ case "C":
186
+ return `C${fmt(c.x1)} ${fmt(c.y1)} ${fmt(c.x2)} ${fmt(c.y2)} ${fmt(c.x)} ${fmt(c.y)}`;
187
+ case "Q":
188
+ return `Q${fmt(c.x1)} ${fmt(c.y1)} ${fmt(c.x)} ${fmt(c.y)}`;
189
+ case "A":
190
+ return `A${fmt(c.rx)} ${fmt(c.ry)} ${fmt(c.angle)} ${String(c.large)} ${String(c.sweep)} ${fmt(c.x)} ${fmt(c.y)}`;
191
+ default:
192
+ return "Z";
193
+ }
194
+ })
195
+ .join(" ");
196
+ }
197
+ /**
198
+ * A path written with absolute commands only (H and V as L, S as C, T as Q,
199
+ * numbers to 4 decimals): what `interpolatePath` compares. Throws on a path
200
+ * it cannot read.
201
+ */
202
+ export function normalizePath(d) {
203
+ return write(parse(d));
204
+ }
205
+ // ---- measuring ----------------------------------------------------------------
206
+ // Gauss–Legendre nodes and weights on [-1, 1] (Newton's method on the
207
+ // Legendre polynomial), for the length of a curve.
208
+ const GAUSS = ((n) => {
209
+ const xs = new Float64Array(n);
210
+ const ws = new Float64Array(n);
211
+ for (let i = 0; i < Math.ceil(n / 2); i++) {
212
+ let z = Math.cos((Math.PI * (i + 0.75)) / (n + 0.5));
213
+ let pp = 1;
214
+ for (let iter = 0; iter < 100; iter++) {
215
+ let p1 = 1;
216
+ let p2 = 0;
217
+ for (let j = 1; j <= n; j++) {
218
+ const p3 = p2;
219
+ p2 = p1;
220
+ p1 = ((2 * j - 1) * z * p2 - (j - 1) * p3) / j;
221
+ }
222
+ pp = (n * (z * p1 - p2)) / (z * z - 1);
223
+ const z1 = z;
224
+ z = z1 - p1 / pp;
225
+ if (Math.abs(z - z1) < 1e-15)
226
+ break;
227
+ }
228
+ xs[i] = -z;
229
+ xs[n - 1 - i] = z;
230
+ ws[i] = ws[n - 1 - i] = 2 / ((1 - z * z) * pp * pp);
231
+ }
232
+ return { xs, ws };
233
+ })(16);
234
+ /** Steps a curve's length table is kept at. */
235
+ const STEPS = 16;
236
+ function cubicAt(p, t) {
237
+ const mt = 1 - t;
238
+ const a = mt * mt * mt;
239
+ const b = 3 * mt * mt * t;
240
+ const c = 3 * mt * t * t;
241
+ const d = t * t * t;
242
+ return {
243
+ x: a * n(p, 0) + b * n(p, 2) + c * n(p, 4) + d * n(p, 6),
244
+ y: a * n(p, 1) + b * n(p, 3) + c * n(p, 5) + d * n(p, 7),
245
+ };
246
+ }
247
+ function cubicDerivative(p, t) {
248
+ const mt = 1 - t;
249
+ const a = 3 * mt * mt;
250
+ const b = 6 * mt * t;
251
+ const c = 3 * t * t;
252
+ return {
253
+ x: a * (n(p, 2) - n(p, 0)) +
254
+ b * (n(p, 4) - n(p, 2)) +
255
+ c * (n(p, 6) - n(p, 4)),
256
+ y: a * (n(p, 3) - n(p, 1)) +
257
+ b * (n(p, 5) - n(p, 3)) +
258
+ c * (n(p, 7) - n(p, 5)),
259
+ };
260
+ }
261
+ /** p[i], which is always there. */
262
+ const n = (p, i) => p[i] ?? 0;
263
+ /** A cubic's length from t = a to t = b. */
264
+ function arcLength(p, a, b) {
265
+ const half = (b - a) / 2;
266
+ const mid = (a + b) / 2;
267
+ let sum = 0;
268
+ for (let i = 0; i < GAUSS.xs.length; i++) {
269
+ const d = cubicDerivative(p, mid + half * (GAUSS.xs[i] ?? 0));
270
+ sum += (GAUSS.ws[i] ?? 0) * Math.hypot(d.x, d.y);
271
+ }
272
+ return sum * half;
273
+ }
274
+ function cubicPiece(p, sub) {
275
+ const cum = new Float64Array(STEPS + 1);
276
+ for (let k = 0; k < STEPS; k++)
277
+ cum[k + 1] = (cum[k] ?? 0) + arcLength(p, k / STEPS, (k + 1) / STEPS);
278
+ return { p, cubic: true, length: cum[STEPS] ?? 0, cum, sub, close: false };
279
+ }
280
+ function linePiece(p, sub, close) {
281
+ return {
282
+ p,
283
+ cubic: false,
284
+ length: Math.hypot(n(p, 2) - n(p, 0), n(p, 3) - n(p, 1)),
285
+ cum: null,
286
+ sub,
287
+ close,
288
+ };
289
+ }
290
+ /**
291
+ * The most of a turn one cubic of an arc covers: a quarter, as Chrome draws
292
+ * and measures arcs, so a length here is the browser's (a circle comes out
293
+ * 1.4e-4 longer than 2πr in both).
294
+ */
295
+ const ARC_STEP = Math.PI / 2;
296
+ /**
297
+ * An arc as cubics of at most a quarter turn each (SVG 1.1, F.6.5).
298
+ */
299
+ function arcCubics(x1, y1, a) {
300
+ let rx = Math.abs(a.rx);
301
+ let ry = Math.abs(a.ry);
302
+ if (rx === 0 || ry === 0)
303
+ return null;
304
+ const phi = (a.angle * Math.PI) / 180;
305
+ const cos = Math.cos(phi);
306
+ const sin = Math.sin(phi);
307
+ const dx = (x1 - a.x) / 2;
308
+ const dy = (y1 - a.y) / 2;
309
+ const xp = cos * dx + sin * dy;
310
+ const yp = -sin * dx + cos * dy;
311
+ const lambda = (xp * xp) / (rx * rx) + (yp * yp) / (ry * ry);
312
+ if (lambda > 1) {
313
+ rx *= Math.sqrt(lambda);
314
+ ry *= Math.sqrt(lambda);
315
+ }
316
+ const num = rx * rx * ry * ry - rx * rx * yp * yp - ry * ry * xp * xp;
317
+ const den = rx * rx * yp * yp + ry * ry * xp * xp;
318
+ const coef = (a.large !== a.sweep ? 1 : -1) * Math.sqrt(Math.max(0, num / den));
319
+ const cxp = (coef * rx * yp) / ry;
320
+ const cyp = (-coef * ry * xp) / rx;
321
+ const cx = cos * cxp - sin * cyp + (x1 + a.x) / 2;
322
+ const cy = sin * cxp + cos * cyp + (y1 + a.y) / 2;
323
+ const angle = (ux, uy, vx, vy) => Math.atan2(ux * vy - uy * vx, ux * vx + uy * vy);
324
+ const ux = (xp - cxp) / rx;
325
+ const uy = (yp - cyp) / ry;
326
+ const theta = angle(1, 0, ux, uy);
327
+ let delta = angle(ux, uy, (-xp - cxp) / rx, (-yp - cyp) / ry);
328
+ if (!a.sweep && delta > 0)
329
+ delta -= 2 * Math.PI;
330
+ if (a.sweep && delta < 0)
331
+ delta += 2 * Math.PI;
332
+ const count = Math.max(1, Math.ceil(Math.abs(delta) / ARC_STEP - 1e-9));
333
+ const step = delta / count;
334
+ const k = (4 / 3) * Math.tan(step / 4);
335
+ const map = (u, v) => [
336
+ cx + cos * rx * u - sin * ry * v,
337
+ cy + sin * rx * u + cos * ry * v,
338
+ ];
339
+ const out = [];
340
+ let px = x1;
341
+ let py = y1;
342
+ for (let i = 0; i < count; i++) {
343
+ const a1 = theta + i * step;
344
+ const a2 = a1 + step;
345
+ const c1 = map(Math.cos(a1) - k * Math.sin(a1), Math.sin(a1) + k * Math.cos(a1));
346
+ const c2 = map(Math.cos(a2) + k * Math.sin(a2), Math.sin(a2) - k * Math.cos(a2));
347
+ const end = i === count - 1 ? [a.x, a.y] : map(Math.cos(a2), Math.sin(a2));
348
+ const ex = end[0] ?? 0;
349
+ const ey = end[1] ?? 0;
350
+ out.push([px, py, ...c1, ...c2, ex, ey]);
351
+ px = ex;
352
+ py = ey;
353
+ }
354
+ return out;
355
+ }
356
+ function measureCommands(commands) {
357
+ const pieces = [];
358
+ let x = 0;
359
+ let y = 0;
360
+ let sx = 0;
361
+ let sy = 0;
362
+ let sub = -1;
363
+ for (const c of commands) {
364
+ switch (c.type) {
365
+ case "M":
366
+ x = sx = c.x;
367
+ y = sy = c.y;
368
+ sub++;
369
+ continue;
370
+ case "L":
371
+ pieces.push(linePiece([x, y, c.x, c.y], sub, false));
372
+ break;
373
+ case "C":
374
+ pieces.push(cubicPiece([x, y, c.x1, c.y1, c.x2, c.y2, c.x, c.y], sub));
375
+ break;
376
+ case "Q":
377
+ pieces.push(cubicPiece([
378
+ x,
379
+ y,
380
+ x + (2 / 3) * (c.x1 - x),
381
+ y + (2 / 3) * (c.y1 - y),
382
+ c.x + (2 / 3) * (c.x1 - c.x),
383
+ c.y + (2 / 3) * (c.y1 - c.y),
384
+ c.x,
385
+ c.y,
386
+ ], sub));
387
+ break;
388
+ case "A": {
389
+ if (x === c.x && y === c.y)
390
+ break;
391
+ const cubics = arcCubics(x, y, c);
392
+ if (cubics)
393
+ for (const p of cubics)
394
+ pieces.push(cubicPiece(p, sub));
395
+ else
396
+ pieces.push(linePiece([x, y, c.x, c.y], sub, false));
397
+ break;
398
+ }
399
+ default:
400
+ pieces.push(linePiece([x, y, sx, sy], sub, true));
401
+ x = sx;
402
+ y = sy;
403
+ continue;
404
+ }
405
+ x = c.x;
406
+ y = c.y;
407
+ }
408
+ const offsets = [];
409
+ let total = 0;
410
+ for (const piece of pieces) {
411
+ offsets.push(total);
412
+ total += piece.length;
413
+ }
414
+ return { pieces, offsets, total, last: { x, y } };
415
+ }
416
+ const MEASURED = new Map();
417
+ const MEASURED_CACHE = 64;
418
+ function measured(d) {
419
+ const found = MEASURED.get(d);
420
+ if (found)
421
+ return found;
422
+ const m = measureCommands(parse(d));
423
+ if (MEASURED.size >= MEASURED_CACHE) {
424
+ const oldest = MEASURED.keys().next();
425
+ if (!oldest.done)
426
+ MEASURED.delete(oldest.value);
427
+ }
428
+ MEASURED.set(d, m);
429
+ return m;
430
+ }
431
+ /** The curve parameter t at a length s from a cubic's start. */
432
+ function cubicT(piece, s) {
433
+ const cum = piece.cum;
434
+ if (!cum || piece.length <= 0)
435
+ return 0;
436
+ if (s <= 0)
437
+ return 0;
438
+ if (s >= piece.length)
439
+ return 1;
440
+ let k = 0;
441
+ let hi = STEPS;
442
+ while (hi - k > 1) {
443
+ const mid = (k + hi) >> 1;
444
+ if ((cum[mid] ?? 0) <= s)
445
+ k = mid;
446
+ else
447
+ hi = mid;
448
+ }
449
+ const base = k / STEPS;
450
+ const want = s - (cum[k] ?? 0);
451
+ let lo = base;
452
+ let up = (k + 1) / STEPS;
453
+ const span = (cum[k + 1] ?? 0) - (cum[k] ?? 0);
454
+ let t = span > 0 ? base + (up - lo) * (want / span) : base;
455
+ for (let i = 0; i < 12; i++) {
456
+ const f = arcLength(piece.p, base, t) - want;
457
+ if (Math.abs(f) < 1e-9)
458
+ break;
459
+ if (f > 0)
460
+ up = t;
461
+ else
462
+ lo = t;
463
+ const d = cubicDerivative(piece.p, t);
464
+ const speed = Math.hypot(d.x, d.y);
465
+ let next = speed > 0 ? t - f / speed : (lo + up) / 2;
466
+ if (next <= lo || next >= up)
467
+ next = (lo + up) / 2;
468
+ t = next;
469
+ }
470
+ return t;
471
+ }
472
+ /** The piece at a length along the path, and the length into it. */
473
+ function pieceAt(m, length) {
474
+ const s = Math.min(Math.max(length, 0), m.total);
475
+ for (let i = 0; i < m.pieces.length; i++) {
476
+ const piece = m.pieces[i];
477
+ const offset = m.offsets[i] ?? 0;
478
+ if (piece && piece.length > 0 && offset + piece.length >= s)
479
+ return { piece, s: s - offset };
480
+ }
481
+ return null;
482
+ }
483
+ /** The path's length, in its own units. Throws on a path it cannot read. */
484
+ export function pathLength(d) {
485
+ return measured(d).total;
486
+ }
487
+ /**
488
+ * The point at a length along the path (clamped to 0 – its length). A path
489
+ * with no length gives its last point.
490
+ */
491
+ export function pointAtLength(d, length) {
492
+ const m = measured(d);
493
+ const at = pieceAt(m, length);
494
+ if (!at)
495
+ return { ...m.last };
496
+ const { piece, s } = at;
497
+ if (piece.cubic)
498
+ return cubicAt(piece.p, cubicT(piece, s));
499
+ const f = s / piece.length;
500
+ return {
501
+ x: n(piece.p, 0) + (n(piece.p, 2) - n(piece.p, 0)) * f,
502
+ y: n(piece.p, 1) + (n(piece.p, 3) - n(piece.p, 1)) * f,
503
+ };
504
+ }
505
+ /**
506
+ * The direction the path runs in at a length along it, a vector of length 1
507
+ * (`Math.atan2(v.y, v.x)` is its angle in radians). A path with no length
508
+ * gives `{ x: 1, y: 0 }`.
509
+ */
510
+ export function tangentAtLength(d, length) {
511
+ const at = pieceAt(measured(d), length);
512
+ if (!at)
513
+ return { x: 1, y: 0 };
514
+ const { piece, s } = at;
515
+ const p = piece.p;
516
+ let v;
517
+ if (piece.cubic) {
518
+ const t = cubicT(piece, s);
519
+ v = cubicDerivative(p, t);
520
+ if (Math.hypot(v.x, v.y) < 1e-9) {
521
+ // A control point on its end: the direction the curve leaves it in.
522
+ const a = cubicAt(p, Math.max(0, t - 1e-4));
523
+ const b = cubicAt(p, Math.min(1, t + 1e-4));
524
+ v = { x: b.x - a.x, y: b.y - a.y };
525
+ }
526
+ }
527
+ else
528
+ v = { x: n(p, 2) - n(p, 0), y: n(p, 3) - n(p, 1) };
529
+ const len = Math.hypot(v.x, v.y);
530
+ return len > 0 ? { x: v.x / len, y: v.y / len } : { x: 1, y: 0 };
531
+ }
532
+ /**
533
+ * The stroke-dash values that show the first `progress` (0–1, clamped) of
534
+ * the path's stroke: 0 shows none of it, 1 all of it. Set them in render
535
+ * from an eased progress: `css(path, drawOn(d, ease.out(progress(t, 0.2, 1))))`.
536
+ * The gap is twice the length, so a browser that measures the path a little
537
+ * longer never shows the next dash.
538
+ */
539
+ export function drawOn(d, progress) {
540
+ const length = measured(d).total;
541
+ if (length <= 0)
542
+ return { strokeDasharray: "none", strokeDashoffset: 0 };
543
+ const p = Math.min(Math.max(progress, 0), 1);
544
+ return {
545
+ strokeDasharray: `${fmt(length)} ${fmt(2 * length)}`,
546
+ // At 0 the dash is put wholly before the start, so no cap shows there.
547
+ strokeDashoffset: p <= 0 ? 1.5 * length : length * (1 - p),
548
+ };
549
+ }
550
+ /** A cubic split at t: the curve before and after. */
551
+ function split(p, t) {
552
+ const lerp = (a, b) => a + (b - a) * t;
553
+ const [x0, y0, x1, y1, x2, y2, x3, y3] = [0, 1, 2, 3, 4, 5, 6, 7].map((i) => n(p, i));
554
+ const ax = lerp(x0, x1);
555
+ const ay = lerp(y0, y1);
556
+ const bx = lerp(x1, x2);
557
+ const by = lerp(y1, y2);
558
+ const cx = lerp(x2, x3);
559
+ const cy = lerp(y2, y3);
560
+ const dx = lerp(ax, bx);
561
+ const dy = lerp(ay, by);
562
+ const ex = lerp(bx, cx);
563
+ const ey = lerp(by, cy);
564
+ const fx = lerp(dx, ex);
565
+ const fy = lerp(dy, ey);
566
+ return [
567
+ [x0, y0, ax, ay, dx, dy, fx, fy],
568
+ [fx, fy, ex, ey, cx, cy, x3, y3],
569
+ ];
570
+ }
571
+ /** A cubic between t0 and t1. */
572
+ function between(p, t0, t1) {
573
+ const head = t1 < 1 ? split(p, t1)[0] : [...p];
574
+ if (t0 <= 0)
575
+ return head;
576
+ return split(head, t1 > 0 ? t0 / t1 : 0)[1];
577
+ }
578
+ /**
579
+ * The stretch of the path from `from` to `to`, fractions of its length
580
+ * (0–1, clamped), as a path: lines and cubic curves (Q and A become cubics),
581
+ * a move at the start of each subpath it crosses, and Z where it keeps a
582
+ * whole subpath. `""` when `to` is not after `from`. Animate `to` to grow a
583
+ * line, both to send a stretch along it.
584
+ */
585
+ export function trimPath(d, from, to) {
586
+ const m = measured(d);
587
+ const a = Math.min(Math.max(from, 0), 1) * m.total;
588
+ const b = Math.min(Math.max(to, 0), 1) * m.total;
589
+ if (b <= a)
590
+ return "";
591
+ const out = [];
592
+ let pen = null;
593
+ let sub = -1;
594
+ // Whether the output's current subpath began at its subpath's start.
595
+ let whole = false;
596
+ for (let i = 0; i < m.pieces.length; i++) {
597
+ const piece = m.pieces[i];
598
+ if (!piece)
599
+ continue;
600
+ const o = m.offsets[i] ?? 0;
601
+ const s0 = Math.max(a, o);
602
+ const s1 = Math.min(b, o + piece.length);
603
+ if (piece.length <= 0) {
604
+ // A Z that adds no length closes a subpath kept whole.
605
+ if (piece.close && whole && piece.sub === sub && pen && o <= b)
606
+ out.push({ type: "Z" });
607
+ continue;
608
+ }
609
+ if (s1 <= s0)
610
+ continue;
611
+ let seg;
612
+ if (piece.cubic)
613
+ seg = between(piece.p, cubicT(piece, s0 - o), cubicT(piece, s1 - o));
614
+ else {
615
+ const f0 = (s0 - o) / piece.length;
616
+ const f1 = (s1 - o) / piece.length;
617
+ const p = piece.p;
618
+ seg = [f0, f1].flatMap((f) => [
619
+ n(p, 0) + (n(p, 2) - n(p, 0)) * f,
620
+ n(p, 1) + (n(p, 3) - n(p, 1)) * f,
621
+ ]);
622
+ }
623
+ const start = { x: n(seg, 0), y: n(seg, 1) };
624
+ if (!pen ||
625
+ piece.sub !== sub ||
626
+ Math.hypot(start.x - pen.x, start.y - pen.y) > 1e-9) {
627
+ out.push({ type: "M", ...start });
628
+ whole =
629
+ s0 === o &&
630
+ m.pieces.findIndex((q) => q.sub === piece.sub && q.length > 0) === i;
631
+ sub = piece.sub;
632
+ }
633
+ const kept = s0 === o && s1 === o + piece.length;
634
+ if (piece.close && whole && kept)
635
+ out.push({ type: "Z" });
636
+ else if (piece.cubic)
637
+ out.push({
638
+ type: "C",
639
+ x1: n(seg, 2),
640
+ y1: n(seg, 3),
641
+ x2: n(seg, 4),
642
+ y2: n(seg, 5),
643
+ x: n(seg, 6),
644
+ y: n(seg, 7),
645
+ });
646
+ else
647
+ out.push({ type: "L", x: n(seg, 2), y: n(seg, 3) });
648
+ pen = { x: n(seg, seg.length - 2), y: n(seg, seg.length - 1) };
649
+ }
650
+ return write(out);
651
+ }
652
+ // ---- between two paths ----------------------------------------------------------
653
+ /**
654
+ * The path `x` of the way from `a` to `b` (0 is `a`, 1 is `b`; values
655
+ * outside 0–1 go on past them, for an easing that overshoots). Both paths
656
+ * must have the same commands once absolute (`normalizePath`), and an arc's
657
+ * flags must match; otherwise it throws, naming the first command that
658
+ * differs. Draw the two shapes with the same commands to morph between them.
659
+ */
660
+ export function interpolatePath(a, b, x) {
661
+ const ca = parse(a);
662
+ const cb = parse(b);
663
+ if (ca.length !== cb.length)
664
+ throw new Error(`interpolatePath: the paths have ${String(ca.length)} and ${String(cb.length)} commands ("${write(ca)}", "${write(cb)}")`);
665
+ const lerp = (u, v) => u + (v - u) * x;
666
+ const out = ca.map((u, i) => {
667
+ const v = cb[i];
668
+ const differ = () => new Error(`interpolatePath: command ${String(i + 1)} differs: ${write([u])} and ${v ? write([v]) : "nothing"}`);
669
+ if (v?.type !== u.type)
670
+ throw differ();
671
+ switch (u.type) {
672
+ case "Z":
673
+ return u;
674
+ case "M":
675
+ case "L": {
676
+ const w = v;
677
+ return { type: u.type, x: lerp(u.x, w.x), y: lerp(u.y, w.y) };
678
+ }
679
+ case "C": {
680
+ const w = v;
681
+ return {
682
+ type: "C",
683
+ x1: lerp(u.x1, w.x1),
684
+ y1: lerp(u.y1, w.y1),
685
+ x2: lerp(u.x2, w.x2),
686
+ y2: lerp(u.y2, w.y2),
687
+ x: lerp(u.x, w.x),
688
+ y: lerp(u.y, w.y),
689
+ };
690
+ }
691
+ case "Q": {
692
+ const w = v;
693
+ return {
694
+ type: "Q",
695
+ x1: lerp(u.x1, w.x1),
696
+ y1: lerp(u.y1, w.y1),
697
+ x: lerp(u.x, w.x),
698
+ y: lerp(u.y, w.y),
699
+ };
700
+ }
701
+ default: {
702
+ const w = v;
703
+ if (w.large !== u.large || w.sweep !== u.sweep)
704
+ throw differ();
705
+ return {
706
+ type: "A",
707
+ rx: lerp(u.rx, w.rx),
708
+ ry: lerp(u.ry, w.ry),
709
+ angle: lerp(u.angle, w.angle),
710
+ large: u.large,
711
+ sweep: u.sweep,
712
+ x: lerp(u.x, w.x),
713
+ y: lerp(u.y, w.y),
714
+ };
715
+ }
716
+ }
717
+ });
718
+ return write(out);
719
+ }
720
+ //# sourceMappingURL=paths.js.map