@sarmal/core 0.15.1 → 0.17.0

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 (83) hide show
  1. package/dist/auto-init.cjs +176 -78
  2. package/dist/auto-init.cjs.map +1 -1
  3. package/dist/auto-init.js +175 -77
  4. package/dist/auto-init.js.map +1 -1
  5. package/dist/curves/artemis2.cjs +10 -7
  6. package/dist/curves/artemis2.d.cts +1 -1
  7. package/dist/curves/artemis2.d.ts +1 -1
  8. package/dist/curves/artemis2.js +9 -6
  9. package/dist/curves/astroid.cjs +4 -4
  10. package/dist/curves/astroid.d.cts +1 -1
  11. package/dist/curves/astroid.d.ts +1 -1
  12. package/dist/curves/astroid.js +3 -3
  13. package/dist/curves/deltoid.cjs +4 -4
  14. package/dist/curves/deltoid.d.cts +1 -1
  15. package/dist/curves/deltoid.d.ts +1 -1
  16. package/dist/curves/deltoid.js +3 -3
  17. package/dist/curves/epicycloid3.cjs +4 -4
  18. package/dist/curves/epicycloid3.d.cts +1 -1
  19. package/dist/curves/epicycloid3.d.ts +1 -1
  20. package/dist/curves/epicycloid3.js +3 -3
  21. package/dist/curves/epitrochoid7.cjs +5 -5
  22. package/dist/curves/epitrochoid7.d.cts +1 -1
  23. package/dist/curves/epitrochoid7.d.ts +1 -1
  24. package/dist/curves/epitrochoid7.js +4 -4
  25. package/dist/curves/index.cjs +114 -29
  26. package/dist/curves/index.cjs.map +1 -1
  27. package/dist/curves/index.d.cts +29 -21
  28. package/dist/curves/index.d.ts +29 -21
  29. package/dist/curves/index.js +126 -29
  30. package/dist/curves/index.js.map +1 -1
  31. package/dist/curves/lame.cjs +6 -5
  32. package/dist/curves/lame.d.cts +1 -1
  33. package/dist/curves/lame.d.ts +1 -1
  34. package/dist/curves/lame.js +5 -4
  35. package/dist/curves/lissajous32.cjs +4 -4
  36. package/dist/curves/lissajous32.d.cts +1 -1
  37. package/dist/curves/lissajous32.d.ts +1 -1
  38. package/dist/curves/lissajous32.js +3 -3
  39. package/dist/curves/lissajous43.cjs +4 -4
  40. package/dist/curves/lissajous43.d.cts +1 -1
  41. package/dist/curves/lissajous43.d.ts +1 -1
  42. package/dist/curves/lissajous43.js +3 -3
  43. package/dist/curves/rose3.cjs +4 -4
  44. package/dist/curves/rose3.d.cts +1 -1
  45. package/dist/curves/rose3.d.ts +1 -1
  46. package/dist/curves/rose3.js +3 -3
  47. package/dist/curves/rose5.cjs +4 -4
  48. package/dist/curves/rose5.d.cts +1 -1
  49. package/dist/curves/rose5.d.ts +1 -1
  50. package/dist/curves/rose5.js +3 -3
  51. package/dist/curves/rose52.cjs +21 -0
  52. package/dist/curves/rose52.cjs.map +1 -0
  53. package/dist/curves/rose52.d.cts +9 -0
  54. package/dist/curves/rose52.d.ts +9 -0
  55. package/dist/curves/rose52.js +19 -0
  56. package/dist/curves/rose52.js.map +1 -0
  57. package/dist/curves/star.cjs +24 -0
  58. package/dist/curves/star.cjs.map +1 -0
  59. package/dist/curves/star.d.cts +8 -0
  60. package/dist/curves/star.d.ts +8 -0
  61. package/dist/curves/star.js +22 -0
  62. package/dist/curves/star.js.map +1 -0
  63. package/dist/curves/star4.cjs +24 -0
  64. package/dist/curves/star4.cjs.map +1 -0
  65. package/dist/curves/star4.d.cts +9 -0
  66. package/dist/curves/star4.d.ts +9 -0
  67. package/dist/curves/star4.js +22 -0
  68. package/dist/curves/star4.js.map +1 -0
  69. package/dist/curves/star7.cjs +24 -0
  70. package/dist/curves/star7.cjs.map +1 -0
  71. package/dist/curves/star7.d.cts +9 -0
  72. package/dist/curves/star7.d.ts +9 -0
  73. package/dist/curves/star7.js +22 -0
  74. package/dist/curves/star7.js.map +1 -0
  75. package/dist/index.cjs +190 -78
  76. package/dist/index.cjs.map +1 -1
  77. package/dist/index.d.cts +70 -29
  78. package/dist/index.d.ts +70 -29
  79. package/dist/index.js +208 -78
  80. package/dist/index.js.map +1 -1
  81. package/dist/types-BL9HhEmk.d.cts +259 -246
  82. package/dist/types-BL9HhEmk.d.ts +259 -246
  83. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -1,4 +1,4 @@
1
- 'use strict';
1
+ "use strict";
2
2
 
3
3
  // src/engine.ts
4
4
  var TWO_PI = Math.PI * 2;
@@ -63,13 +63,13 @@ function resolveCurve(curveDef) {
63
63
  period,
64
64
  speed,
65
65
  skeleton: curveDef.skeleton,
66
- skeletonFn: curveDef.skeletonFn
66
+ skeletonFn: curveDef.skeletonFn,
67
67
  };
68
68
  }
69
69
  function createEngine(curveDef, trailLength = 120) {
70
70
  if (!Number.isFinite(trailLength) || trailLength <= 0) {
71
71
  throw new RangeError(
72
- `[sarmal] trailLength must be a positive finite number, got ${trailLength}`
72
+ `[sarmal] trailLength must be a positive finite number, got ${trailLength}`,
73
73
  );
74
74
  }
75
75
  let curve = resolveCurve(curveDef);
@@ -110,7 +110,7 @@ function createEngine(curveDef, trailLength = 120) {
110
110
  actualTime += deltaTime;
111
111
  if (morphCurveB !== null && _morphAlpha !== null) {
112
112
  const a = curve.fn(t, actualTime, EMPTY_PARAMS);
113
- const tB = _morphStrategy === "normalized" ? t / curve.period * morphCurveB.period : t;
113
+ const tB = _morphStrategy === "normalized" ? (t / curve.period) * morphCurveB.period : t;
114
114
  const b = morphCurveB.fn(tB, actualTime, EMPTY_PARAMS);
115
115
  trail.push(a.x + (b.x - a.x) * _morphAlpha, a.y + (b.y - a.y) * _morphAlpha);
116
116
  } else {
@@ -134,14 +134,14 @@ function createEngine(curveDef, trailLength = 120) {
134
134
  trail.clear();
135
135
  },
136
136
  jump(newT, { clearTrail = false } = {}) {
137
- t = (newT % curve.period + curve.period) % curve.period;
137
+ t = ((newT % curve.period) + curve.period) % curve.period;
138
138
  if (clearTrail) {
139
139
  trail.clear();
140
140
  }
141
141
  },
142
142
  seek(targetT, { wrap = false, step = curve.period / trailLength } = {}) {
143
143
  const advance = curve.speed * step;
144
- const target = (targetT % curve.period + curve.period) % curve.period;
144
+ const target = ((targetT % curve.period) + curve.period) % curve.period;
145
145
  const targetTime = target / curve.speed;
146
146
  t = target;
147
147
  actualTime = targetTime;
@@ -150,7 +150,7 @@ function createEngine(curveDef, trailLength = 120) {
150
150
  const count = wrap ? trailLength : Math.min(trailLength, pointsFromStart);
151
151
  for (let i = count - 1; i >= 0; i--) {
152
152
  const sampleT = target - i * advance;
153
- const wrappedT = (sampleT % curve.period + curve.period) % curve.period;
153
+ const wrappedT = ((sampleT % curve.period) + curve.period) % curve.period;
154
154
  const time = targetTime - i * step;
155
155
  const point = curve.fn(wrappedT, time, EMPTY_PARAMS);
156
156
  trail.push(point.x, point.y);
@@ -167,13 +167,16 @@ function createEngine(curveDef, trailLength = 120) {
167
167
  ...frozenB,
168
168
  fn: (sampleT, time, params) => {
169
169
  const a = frozenA.fn(sampleT, time, params);
170
- const tB = frozenStrategy === "normalized" ? sampleT / frozenA.period * frozenB.period : sampleT;
170
+ const tB =
171
+ frozenStrategy === "normalized"
172
+ ? (sampleT / frozenA.period) * frozenB.period
173
+ : sampleT;
171
174
  const b = frozenB.fn(tB, time, params);
172
175
  return {
173
176
  x: a.x + (b.x - a.x) * frozenAlpha,
174
- y: a.y + (b.y - a.y) * frozenAlpha
177
+ y: a.y + (b.y - a.y) * frozenAlpha,
175
178
  };
176
- }
179
+ },
177
180
  };
178
181
  }
179
182
  _morphStrategy = strategy;
@@ -186,7 +189,7 @@ function createEngine(curveDef, trailLength = 120) {
186
189
  completeMorph() {
187
190
  if (morphCurveB !== null) {
188
191
  if (_morphStrategy === "normalized" && curve.period !== morphCurveB.period) {
189
- t = t / curve.period * morphCurveB.period;
192
+ t = (t / curve.period) * morphCurveB.period;
190
193
  }
191
194
  curve = morphCurveB;
192
195
  }
@@ -198,19 +201,22 @@ function createEngine(curveDef, trailLength = 120) {
198
201
  const points = new Array(steps);
199
202
  if (morphCurveB !== null && _morphAlpha !== null) {
200
203
  for (let i = 0; i < steps; i++) {
201
- const sampleT = i / (steps - 1) * curve.period;
204
+ const sampleT = (i / (steps - 1)) * curve.period;
202
205
  const a = sampleSkeleton(curve, sampleT);
203
- const tB = _morphStrategy === "normalized" ? sampleT / curve.period * morphCurveB.period : sampleT;
206
+ const tB =
207
+ _morphStrategy === "normalized"
208
+ ? (sampleT / curve.period) * morphCurveB.period
209
+ : sampleT;
204
210
  const b = sampleSkeleton(morphCurveB, tB);
205
211
  points[i] = {
206
212
  x: a.x + (b.x - a.x) * _morphAlpha,
207
- y: a.y + (b.y - a.y) * _morphAlpha
213
+ y: a.y + (b.y - a.y) * _morphAlpha,
208
214
  };
209
215
  }
210
216
  return points;
211
217
  }
212
218
  for (let i = 0; i < steps; i++) {
213
- const sampleT = i / (steps - 1) * curve.period;
219
+ const sampleT = (i / (steps - 1)) * curve.period;
214
220
  points[i] = sampleSkeleton(curve, sampleT);
215
221
  }
216
222
  return points;
@@ -252,7 +258,7 @@ function createEngine(curveDef, trailLength = 120) {
252
258
  _speedTransition.reject(new Error("Speed transition cancelled"));
253
259
  _speedTransition = null;
254
260
  }
255
- }
261
+ },
256
262
  };
257
263
  }
258
264
 
@@ -315,13 +321,16 @@ function computeTrailQuad(trail, i, trailCount, toX, toY) {
315
321
  r1x: nx - n1.x * w1,
316
322
  r1y: ny - n1.y * w1,
317
323
  opacity,
318
- progress
324
+ progress,
319
325
  };
320
326
  }
321
327
  function computeBoundaries(pts, logicalWidth, logicalHeight) {
322
328
  if (pts.length === 0) return null;
323
329
  const first = pts[0];
324
- let minX = first.x, maxX = first.x, minY = first.y, maxY = first.y;
330
+ let minX = first.x,
331
+ maxX = first.x,
332
+ minY = first.y,
333
+ maxY = first.y;
325
334
  for (const p of pts) {
326
335
  if (p.x < minX) {
327
336
  minX = p.x;
@@ -340,7 +349,7 @@ function computeBoundaries(pts, logicalWidth, logicalHeight) {
340
349
  const h = maxY - minY;
341
350
  if (w === 0 && h === 0) {
342
351
  throw new Error(
343
- "[sarmal] Degenerate curve: all skeleton points are identical. Check that your curve fn returns distinct points for different values of t."
352
+ "[sarmal] Degenerate curve: all skeleton points are identical. Check that your curve fn returns distinct points for different values of t.",
344
353
  );
345
354
  }
346
355
  const scaleXProportional = logicalWidth / (w * (1 + FIT_PADDING * 2));
@@ -351,12 +360,12 @@ function computeBoundaries(pts, logicalWidth, logicalHeight) {
351
360
  scaleXProportional,
352
361
  scaleYProportional,
353
362
  scaleXMinPadding,
354
- scaleYMinPadding
363
+ scaleYMinPadding,
355
364
  );
356
365
  return {
357
366
  scale,
358
367
  offsetX: (logicalWidth - w * scale) / 2 - minX * scale,
359
- offsetY: (logicalHeight - h * scale) / 2 - minY * scale
368
+ offsetY: (logicalHeight - h * scale) / 2 - minY * scale,
360
369
  };
361
370
  }
362
371
  function enginePassthroughs(engine) {
@@ -366,7 +375,7 @@ function enginePassthroughs(engine) {
366
375
  setSpeed: engine.setSpeed,
367
376
  getSpeed: engine.getSpeed,
368
377
  resetSpeed: engine.resetSpeed,
369
- setSpeedOver: engine.setSpeedOver
378
+ setSpeedOver: engine.setSpeedOver,
370
379
  };
371
380
  }
372
381
  var palettes = {
@@ -375,16 +384,16 @@ var palettes = {
375
384
  ocean: ["#1e3a8a", "#06b6d4", "#22d3ee", "#e0f2fe"],
376
385
  ice: ["#1e3a8a", "#67e8f9"],
377
386
  fire: ["#7f1d1d", "#fbbf24"],
378
- forest: ["#14532d", "#86efac"]
387
+ forest: ["#14532d", "#86efac"],
379
388
  };
380
389
  function hexToRgb(hex) {
381
390
  const n = parseInt(hex.slice(1), 16);
382
- return { r: n >> 16, g: n >> 8 & 255, b: n & 255 };
391
+ return { r: n >> 16, g: (n >> 8) & 255, b: n & 255 };
383
392
  }
384
393
  var lerpRgb = (a, b, t) => ({
385
394
  r: Math.round(a.r + (b.r - a.r) * t),
386
395
  g: Math.round(a.g + (b.g - a.g) * t),
387
- b: Math.round(a.b + (b.b - a.b) * t)
396
+ b: Math.round(a.b + (b.b - a.b) * t),
388
397
  });
389
398
  function getPaletteColor(palette, position, timeOffset = 0) {
390
399
  if (palette.length === 0) {
@@ -407,7 +416,7 @@ var RENDER_OPTION_KEYS = /* @__PURE__ */ new Set([
407
416
  "trailColor",
408
417
  "headColor",
409
418
  "skeletonColor",
410
- "trailStyle"
419
+ "trailStyle",
411
420
  ]);
412
421
  function validateRenderOptions(partial) {
413
422
  for (const key of Object.keys(partial)) {
@@ -432,7 +441,7 @@ function assertTrailColor(value) {
432
441
  if (typeof value === "string") {
433
442
  if (!HEX_COLOR_RE.test(value)) {
434
443
  throw new TypeError(
435
- `[sarmal] setRenderOptions: trailColor must be a 6-digit hex string, got "${value}"`
444
+ `[sarmal] setRenderOptions: trailColor must be a 6-digit hex string, got "${value}"`,
436
445
  );
437
446
  }
438
447
  return;
@@ -440,21 +449,21 @@ function assertTrailColor(value) {
440
449
  if (Array.isArray(value)) {
441
450
  if (value.length < 2) {
442
451
  throw new RangeError(
443
- `[sarmal] setRenderOptions: trailColor array must have at least 2 entries, got ${value.length}`
452
+ `[sarmal] setRenderOptions: trailColor array must have at least 2 entries, got ${value.length}`,
444
453
  );
445
454
  }
446
455
  for (let i = 0; i < value.length; i++) {
447
456
  const entry = value[i];
448
457
  if (typeof entry !== "string" || !HEX_COLOR_RE.test(entry)) {
449
458
  throw new TypeError(
450
- `[sarmal] setRenderOptions: trailColor[${i}] must be a 6-digit hex string, got ${JSON.stringify(entry)}`
459
+ `[sarmal] setRenderOptions: trailColor[${i}] must be a 6-digit hex string, got ${JSON.stringify(entry)}`,
451
460
  );
452
461
  }
453
462
  }
454
463
  return;
455
464
  }
456
465
  throw new TypeError(
457
- `[sarmal] setRenderOptions: trailColor must be a 6-digit hex string or an array of hex strings, got ${JSON.stringify(value)}`
466
+ `[sarmal] setRenderOptions: trailColor must be a 6-digit hex string or an array of hex strings, got ${JSON.stringify(value)}`,
458
467
  );
459
468
  }
460
469
  function assertHeadColor(value) {
@@ -463,7 +472,7 @@ function assertHeadColor(value) {
463
472
  }
464
473
  if (typeof value !== "string" || !HEX_COLOR_RE.test(value)) {
465
474
  throw new TypeError(
466
- `[sarmal] setRenderOptions: headColor must be a 6-digit hex string or null, got ${JSON.stringify(value)}`
475
+ `[sarmal] setRenderOptions: headColor must be a 6-digit hex string or null, got ${JSON.stringify(value)}`,
467
476
  );
468
477
  }
469
478
  }
@@ -473,14 +482,14 @@ function assertSkeletonColor(value) {
473
482
  }
474
483
  if (typeof value !== "string" || !HEX_COLOR_RE.test(value)) {
475
484
  throw new TypeError(
476
- `[sarmal] setRenderOptions: skeletonColor must be a 6-digit hex string or "transparent", got ${JSON.stringify(value)}`
485
+ `[sarmal] setRenderOptions: skeletonColor must be a 6-digit hex string or "transparent", got ${JSON.stringify(value)}`,
477
486
  );
478
487
  }
479
488
  }
480
489
  function assertTrailStyle(value) {
481
490
  if (!TRAIL_STYLES.includes(value)) {
482
491
  throw new RangeError(
483
- `[sarmal] setRenderOptions: trailStyle must be one of "default", "gradient-static", "gradient-animated", got ${JSON.stringify(value)}`
492
+ `[sarmal] setRenderOptions: trailStyle must be one of "default", "gradient-static", "gradient-animated", got ${JSON.stringify(value)}`,
484
493
  );
485
494
  }
486
495
  }
@@ -502,13 +511,13 @@ function resolveHeadColor(trailColor, trailStyle) {
502
511
  function warnIfTrailColorMismatch(trailColor, trailStyle) {
503
512
  if (trailStyle === "default" && Array.isArray(trailColor)) {
504
513
  console.warn(
505
- '[sarmal] trailColor is an array but trailStyle is "default"; only the first color will be used. Pass a gradient trailStyle to use the whole palette.'
514
+ '[sarmal] trailColor is an array but trailStyle is "default"; only the first color will be used. Pass a gradient trailStyle to use the whole palette.',
506
515
  );
507
516
  return;
508
517
  }
509
518
  if (trailStyle !== "default" && typeof trailColor === "string") {
510
519
  console.warn(
511
- `[sarmal] trailColor is a single color but trailStyle is "${trailStyle}"; the trail will render as a solid color. Pass an array of hex colors to use a real gradient.`
520
+ `[sarmal] trailColor is a single color but trailStyle is "${trailStyle}"; the trail will render as a solid color. Pass an array of hex colors to use a real gradient.`,
512
521
  );
513
522
  }
514
523
  }
@@ -518,7 +527,7 @@ var getHeadDotRadius = (w, h) => Math.max(1, 3 * Math.sqrt(Math.min(w, h) / 160)
518
527
  var WHITE_HEX = "#ffffff";
519
528
  function hexToRgbComponents(hex) {
520
529
  const n = parseInt(hex.slice(1), 16);
521
- return `${n >> 16},${n >> 8 & 255},${n & 255}`;
530
+ return `${n >> 16},${(n >> 8) & 255},${n & 255}`;
522
531
  }
523
532
  function applyDprSizing(target, logicalWidth, logicalHeight, dpr) {
524
533
  target.style.width = `${logicalWidth}px`;
@@ -562,6 +571,7 @@ function createRenderer(options) {
562
571
  let animationId = null;
563
572
  let lastTime = 0;
564
573
  let morphResolve = null;
574
+ let morphReject = null;
565
575
  let morphDurationMs = DEFAULT_MORPH_DURATION_MS;
566
576
  let morphAlpha = 0;
567
577
  let gradientAnimTime = 0;
@@ -642,7 +652,7 @@ function createRenderer(options) {
642
652
  i,
643
653
  trailCount,
644
654
  toX,
645
- toY
655
+ toY,
646
656
  );
647
657
  if (trailStyle === "default") {
648
658
  ctx.fillStyle = `rgba(${trailSolidRgb},${opacity})`;
@@ -690,6 +700,7 @@ function createRenderer(options) {
690
700
  engine.completeMorph();
691
701
  morphResolve?.();
692
702
  morphResolve = null;
703
+ morphReject = null;
693
704
  morphAlpha = 0;
694
705
  skeleton = engine.getSarmalSkeleton();
695
706
  if (!engine.isLiveSkeleton) {
@@ -752,6 +763,11 @@ function createRenderer(options) {
752
763
  cancelAnimationFrame(animationId);
753
764
  animationId = null;
754
765
  }
766
+ if (morphReject !== null) {
767
+ morphReject(new Error("Instance destroyed during morph"));
768
+ morphResolve = null;
769
+ morphReject = null;
770
+ }
755
771
  },
756
772
  ...enginePassthroughs(engine),
757
773
  morphTo(target, options2) {
@@ -759,13 +775,15 @@ function createRenderer(options) {
759
775
  engine.completeMorph();
760
776
  morphResolve();
761
777
  morphResolve = null;
778
+ morphReject = null;
762
779
  morphAlpha = 0;
763
780
  }
764
781
  morphDurationMs = options2?.duration ?? DEFAULT_MORPH_DURATION_MS;
765
782
  morphAlpha = 0;
766
783
  engine.startMorph(target, options2?.morphStrategy);
767
- return new Promise((resolve) => {
784
+ return new Promise((resolve, reject) => {
768
785
  morphResolve = resolve;
786
+ morphReject = reject;
769
787
  });
770
788
  },
771
789
  setRenderOptions(partial) {
@@ -795,7 +813,7 @@ function createRenderer(options) {
795
813
  if (partial.trailColor !== void 0 || partial.trailStyle !== void 0) {
796
814
  warnIfTrailColorMismatch(trailColor, trailStyle);
797
815
  }
798
- }
816
+ },
799
817
  };
800
818
  if (shouldAutoStart) {
801
819
  instance.play();
@@ -821,7 +839,7 @@ function sampleCurveSkeleton(curveDef) {
821
839
  const samples = Math.ceil(period * 50);
822
840
  const pts = Array.from({ length: samples });
823
841
  for (let i = 0; i < samples; i++) {
824
- const t = i / (samples - 1) * period;
842
+ const t = (i / (samples - 1)) * period;
825
843
  pts[i] = curveDef.skeletonFn ? curveDef.skeletonFn(t) : curveDef.fn(t, 0, EMPTY_PARAMS2);
826
844
  }
827
845
  return pts;
@@ -923,24 +941,27 @@ function createSVGRenderer(options) {
923
941
  }
924
942
  return;
925
943
  }
926
- for (let i = 0; i < trailCount - 1; i++) {
944
+ const startIdx = Math.max(0, trailCount - 1 - MAX_TRAIL_SEGMENTS);
945
+ const drawnCount = trailCount - 1 - startIdx;
946
+ for (let i = startIdx; i < trailCount - 1; i++) {
947
+ const j = i - startIdx;
927
948
  const { l0x, l0y, r0x, r0y, l1x, l1y, r1x, r1y, opacity, progress } = computeTrailQuad(
928
949
  trail,
929
950
  i,
930
951
  trailCount,
931
952
  px,
932
- py
953
+ py,
933
954
  );
934
955
  const d = `M${l0x.toFixed(2)} ${l0y.toFixed(2)} L${l1x.toFixed(2)} ${l1y.toFixed(2)} L${r1x.toFixed(2)} ${r1y.toFixed(2)} L${r0x.toFixed(2)} ${r0y.toFixed(2)} Z`;
935
- trailPaths[i].setAttribute("d", d);
936
- trailPaths[i].setAttribute("fill-opacity", opacity.toFixed(3));
956
+ trailPaths[j].setAttribute("d", d);
957
+ trailPaths[j].setAttribute("fill-opacity", opacity.toFixed(3));
937
958
  if (trailStyle !== "default") {
938
959
  const timeOffset = trailStyle === "gradient-animated" ? gradientAnimTime * 5e-4 : 0;
939
960
  const { r, g, b } = getPaletteColor(trailPalette, progress, timeOffset);
940
- trailPaths[i].setAttribute("fill", `rgb(${r},${g},${b})`);
961
+ trailPaths[j].setAttribute("fill", `rgb(${r},${g},${b})`);
941
962
  }
942
963
  }
943
- for (let i = trailCount - 1; i < trailPaths.length; i++) {
964
+ for (let i = drawnCount; i < trailPaths.length; i++) {
944
965
  trailPaths[i].setAttribute("d", "");
945
966
  }
946
967
  }
@@ -956,8 +977,10 @@ function createSVGRenderer(options) {
956
977
  }
957
978
  let animationId = null;
958
979
  let lastTime = 0;
959
- const prefersReducedMotion = typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
980
+ const prefersReducedMotion =
981
+ typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
960
982
  let morphResolve = null;
983
+ let morphReject = null;
961
984
  let morphDurationMs = DEFAULT_MORPH_DURATION_MS;
962
985
  let morphTarget = null;
963
986
  let morphAlpha = 0;
@@ -973,7 +996,7 @@ function createSVGRenderer(options) {
973
996
  skeletonPathA.setAttribute("visibility", "visible");
974
997
  skeletonPathA.setAttribute(
975
998
  "stroke-opacity",
976
- String((1 - morphAlpha) * DEFAULT_SKELETON_OPACITY)
999
+ String((1 - morphAlpha) * DEFAULT_SKELETON_OPACITY),
977
1000
  );
978
1001
  }
979
1002
  if (morphPathBBuilt) {
@@ -985,6 +1008,7 @@ function createSVGRenderer(options) {
985
1008
  engine.completeMorph();
986
1009
  morphResolve?.();
987
1010
  morphResolve = null;
1011
+ morphReject = null;
988
1012
  morphTarget = null;
989
1013
  morphAlpha = 0;
990
1014
  morphPathABuilt = "";
@@ -1044,6 +1068,11 @@ function createSVGRenderer(options) {
1044
1068
  cancelAnimationFrame(animationId);
1045
1069
  animationId = null;
1046
1070
  }
1071
+ if (morphReject !== null) {
1072
+ morphReject(new Error("Instance destroyed during morph"));
1073
+ morphResolve = null;
1074
+ morphReject = null;
1075
+ }
1047
1076
  svg.remove();
1048
1077
  },
1049
1078
  ...enginePassthroughs(engine),
@@ -1052,6 +1081,7 @@ function createSVGRenderer(options) {
1052
1081
  engine.completeMorph();
1053
1082
  morphResolve();
1054
1083
  morphResolve = null;
1084
+ morphReject = null;
1055
1085
  morphAlpha = 0;
1056
1086
  skeletonPathA.setAttribute("visibility", "hidden");
1057
1087
  skeletonPathB.setAttribute("visibility", "hidden");
@@ -1066,8 +1096,9 @@ function createSVGRenderer(options) {
1066
1096
  const targetSkeleton = sampleCurveSkeleton(target);
1067
1097
  morphPathBBuilt = pointsToPathString(targetSkeleton, scale, offsetX, offsetY);
1068
1098
  }
1069
- return new Promise((resolve) => {
1099
+ return new Promise((resolve, reject) => {
1070
1100
  morphResolve = resolve;
1101
+ morphReject = reject;
1071
1102
  });
1072
1103
  },
1073
1104
  setRenderOptions(partial) {
@@ -1114,7 +1145,7 @@ function createSVGRenderer(options) {
1114
1145
  if (partial.trailColor !== void 0 || partial.trailStyle !== void 0) {
1115
1146
  warnIfTrailColorMismatch(trailColor, trailStyle);
1116
1147
  }
1117
- }
1148
+ },
1118
1149
  };
1119
1150
  if (shouldAutoStart) {
1120
1151
  instance.play();
@@ -1130,19 +1161,22 @@ function createSarmalSVG(container, curveDef, options) {
1130
1161
  // src/curves/artemis2.ts
1131
1162
  var TWO_PI2 = Math.PI * 2;
1132
1163
  function artemis2Fn(t, _time, _params) {
1133
- const a = 0.35, b = 0.15, ox = 0.175;
1134
- const s = Math.sin(t), c = Math.cos(t);
1164
+ const a = 0.35,
1165
+ b = 0.15,
1166
+ ox = 0.175;
1167
+ const s = Math.sin(t),
1168
+ c = Math.cos(t);
1135
1169
  const denom = 1 + s * s;
1136
1170
  return {
1137
- x: c * (1 + a * c) / denom - ox,
1138
- y: s * c * (1 + b * c) / denom
1171
+ x: (c * (1 + a * c)) / denom - ox,
1172
+ y: (s * c * (1 + b * c)) / denom,
1139
1173
  };
1140
1174
  }
1141
1175
  var artemis2 = {
1142
1176
  name: "Artemis II",
1143
1177
  fn: artemis2Fn,
1144
1178
  period: TWO_PI2,
1145
- speed: 0.7
1179
+ speed: 0.7,
1146
1180
  };
1147
1181
 
1148
1182
  // src/curves/astroid.ts
@@ -1152,14 +1186,14 @@ function astroidFn(t, _time, _params) {
1152
1186
  const s = Math.sin(t);
1153
1187
  return {
1154
1188
  x: c * c * c,
1155
- y: s * s * s
1189
+ y: s * s * s,
1156
1190
  };
1157
1191
  }
1158
1192
  var astroid = {
1159
1193
  name: "Astroid",
1160
1194
  fn: astroidFn,
1161
1195
  period: TWO_PI3,
1162
- speed: 1.1
1196
+ speed: 1.1,
1163
1197
  };
1164
1198
 
1165
1199
  // src/curves/deltoid.ts
@@ -1167,14 +1201,14 @@ var TWO_PI4 = Math.PI * 2;
1167
1201
  function deltoidFn(t, _time, _params) {
1168
1202
  return {
1169
1203
  x: 2 * Math.cos(t) + Math.cos(2 * t),
1170
- y: 2 * Math.sin(t) - Math.sin(2 * t)
1204
+ y: 2 * Math.sin(t) - Math.sin(2 * t),
1171
1205
  };
1172
1206
  }
1173
1207
  var deltoid = {
1174
1208
  name: "Deltoid",
1175
1209
  fn: deltoidFn,
1176
1210
  period: TWO_PI4,
1177
- speed: 0.9
1211
+ speed: 0.9,
1178
1212
  };
1179
1213
 
1180
1214
  // src/curves/epicycloid3.ts
@@ -1182,14 +1216,14 @@ var TWO_PI5 = Math.PI * 2;
1182
1216
  function epicycloid3Fn(t, _time, _params) {
1183
1217
  return {
1184
1218
  x: 4 * Math.cos(t) - Math.cos(4 * t),
1185
- y: 4 * Math.sin(t) - Math.sin(4 * t)
1219
+ y: 4 * Math.sin(t) - Math.sin(4 * t),
1186
1220
  };
1187
1221
  }
1188
1222
  var epicycloid3 = {
1189
1223
  name: "Epicycloid (n=3)",
1190
1224
  fn: epicycloid3Fn,
1191
1225
  period: TWO_PI5,
1192
- speed: 0.75
1226
+ speed: 0.75,
1193
1227
  };
1194
1228
 
1195
1229
  // src/curves/epitrochoid7.ts
@@ -1198,14 +1232,14 @@ function epitrochoid7Fn(t, _time, _params) {
1198
1232
  const d = 1 + 0.55 * Math.sin(t * 0.5);
1199
1233
  return {
1200
1234
  x: 7 * Math.cos(t) - d * Math.cos(7 * t),
1201
- y: 7 * Math.sin(t) - d * Math.sin(7 * t)
1235
+ y: 7 * Math.sin(t) - d * Math.sin(7 * t),
1202
1236
  };
1203
1237
  }
1204
1238
  function epitrochoid7SkeletonFn(t) {
1205
1239
  const d = 1.275;
1206
1240
  return {
1207
1241
  x: 7 * Math.cos(t) - d * Math.cos(7 * t),
1208
- y: 7 * Math.sin(t) - d * Math.sin(7 * t)
1242
+ y: 7 * Math.sin(t) - d * Math.sin(7 * t),
1209
1243
  };
1210
1244
  }
1211
1245
  var epitrochoid7 = {
@@ -1213,7 +1247,7 @@ var epitrochoid7 = {
1213
1247
  fn: epitrochoid7Fn,
1214
1248
  period: TWO_PI6,
1215
1249
  speed: 1.4,
1216
- skeletonFn: epitrochoid7SkeletonFn
1250
+ skeletonFn: epitrochoid7SkeletonFn,
1217
1251
  };
1218
1252
 
1219
1253
  // src/curves/lissajous32.ts
@@ -1222,7 +1256,7 @@ function lissajous32Fn(t, time, _params) {
1222
1256
  const phi = time * 0.45;
1223
1257
  return {
1224
1258
  x: Math.sin(3 * t + phi),
1225
- y: Math.sin(2 * t)
1259
+ y: Math.sin(2 * t),
1226
1260
  };
1227
1261
  }
1228
1262
  var lissajous32 = {
@@ -1230,7 +1264,7 @@ var lissajous32 = {
1230
1264
  fn: lissajous32Fn,
1231
1265
  period: TWO_PI7,
1232
1266
  speed: 2,
1233
- skeleton: "live"
1267
+ skeleton: "live",
1234
1268
  };
1235
1269
 
1236
1270
  // src/curves/lissajous43.ts
@@ -1239,7 +1273,7 @@ function lissajous43Fn(t, time, _params) {
1239
1273
  const phi = time * 0.38;
1240
1274
  return {
1241
1275
  x: Math.sin(4 * t + phi),
1242
- y: Math.sin(3 * t)
1276
+ y: Math.sin(3 * t),
1243
1277
  };
1244
1278
  }
1245
1279
  var lissajous43 = {
@@ -1247,17 +1281,18 @@ var lissajous43 = {
1247
1281
  fn: lissajous43Fn,
1248
1282
  period: TWO_PI8,
1249
1283
  speed: 1.8,
1250
- skeleton: "live"
1284
+ skeleton: "live",
1251
1285
  };
1252
1286
 
1253
1287
  // src/curves/lame.ts
1254
1288
  var TWO_PI9 = Math.PI * 2;
1255
1289
  function lameFn(t, time, _params) {
1256
1290
  const p = 1.75 + 1.25 * Math.sin(time * 0.48);
1257
- const c = Math.cos(t), s = Math.sin(t);
1291
+ const c = Math.cos(t),
1292
+ s = Math.sin(t);
1258
1293
  return {
1259
1294
  x: Math.sign(c) * Math.pow(Math.abs(c), p),
1260
- y: Math.sign(s) * Math.pow(Math.abs(s), p)
1295
+ y: Math.sign(s) * Math.pow(Math.abs(s), p),
1261
1296
  };
1262
1297
  }
1263
1298
  var lame = {
@@ -1265,7 +1300,7 @@ var lame = {
1265
1300
  fn: lameFn,
1266
1301
  period: TWO_PI9,
1267
1302
  speed: 1,
1268
- skeleton: "live"
1303
+ skeleton: "live",
1269
1304
  };
1270
1305
 
1271
1306
  // src/curves/rose3.ts
@@ -1274,14 +1309,14 @@ function rose3Fn(t, _time, _params) {
1274
1309
  const r = Math.cos(3 * t);
1275
1310
  return {
1276
1311
  x: r * Math.cos(t),
1277
- y: r * Math.sin(t)
1312
+ y: r * Math.sin(t),
1278
1313
  };
1279
1314
  }
1280
1315
  var rose3 = {
1281
1316
  name: "Rose (n=3)",
1282
1317
  fn: rose3Fn,
1283
1318
  period: TWO_PI10,
1284
- speed: 1.15
1319
+ speed: 1.15,
1285
1320
  };
1286
1321
 
1287
1322
  // src/curves/rose5.ts
@@ -1290,14 +1325,87 @@ function rose5Fn(t, _time, _params) {
1290
1325
  const r = Math.cos(5 * t);
1291
1326
  return {
1292
1327
  x: r * Math.cos(t),
1293
- y: r * Math.sin(t)
1328
+ y: r * Math.sin(t),
1294
1329
  };
1295
1330
  }
1296
1331
  var rose5 = {
1297
1332
  name: "Rose (n=5)",
1298
1333
  fn: rose5Fn,
1299
1334
  period: TWO_PI11,
1300
- speed: 1
1335
+ speed: 1,
1336
+ };
1337
+
1338
+ // src/curves/rose52.ts
1339
+ var FOUR_PI = Math.PI * 4;
1340
+ function rose52Fn(t, _time, _params) {
1341
+ const r = Math.cos((5 / 2) * t);
1342
+ return {
1343
+ x: r * Math.cos(t),
1344
+ y: r * Math.sin(t),
1345
+ };
1346
+ }
1347
+ var rose52 = {
1348
+ name: "Rose (n=5/2)",
1349
+ fn: rose52Fn,
1350
+ period: FOUR_PI,
1351
+ speed: 0.8,
1352
+ };
1353
+
1354
+ // src/curves/star.ts
1355
+ var TWO_PI12 = Math.PI * 2;
1356
+ function starFn(t, _time, _params) {
1357
+ const r =
1358
+ Math.abs(Math.cos((5 / 2) * t)) +
1359
+ 0.35 * Math.abs(Math.cos((15 / 2) * t)) +
1360
+ 0.15 * Math.abs(Math.cos((25 / 2) * t));
1361
+ return {
1362
+ x: r * Math.cos(t),
1363
+ y: r * Math.sin(t),
1364
+ };
1365
+ }
1366
+ var star = {
1367
+ name: "Star",
1368
+ fn: starFn,
1369
+ period: TWO_PI12,
1370
+ speed: 1,
1371
+ };
1372
+
1373
+ // src/curves/star4.ts
1374
+ var TWO_PI13 = Math.PI * 2;
1375
+ function star4Fn(t, _time, _params) {
1376
+ const r =
1377
+ Math.abs(Math.cos(2 * t)) +
1378
+ 0.35 * Math.abs(Math.cos(6 * t)) +
1379
+ 0.15 * Math.abs(Math.cos(10 * t));
1380
+ return {
1381
+ x: r * Math.cos(t),
1382
+ y: r * Math.sin(t),
1383
+ };
1384
+ }
1385
+ var star4 = {
1386
+ name: "Star (4-arm)",
1387
+ fn: star4Fn,
1388
+ period: TWO_PI13,
1389
+ speed: 1,
1390
+ };
1391
+
1392
+ // src/curves/star7.ts
1393
+ var TWO_PI14 = Math.PI * 2;
1394
+ function star7Fn(t, _time, _params) {
1395
+ const r =
1396
+ Math.abs(Math.cos((7 / 2) * t)) +
1397
+ 0.35 * Math.abs(Math.cos((21 / 2) * t)) +
1398
+ 0.15 * Math.abs(Math.cos((35 / 2) * t));
1399
+ return {
1400
+ x: r * Math.cos(t),
1401
+ y: r * Math.sin(t),
1402
+ };
1403
+ }
1404
+ var star7 = {
1405
+ name: "Star (7-arm)",
1406
+ fn: star7Fn,
1407
+ period: TWO_PI14,
1408
+ speed: 1,
1301
1409
  };
1302
1410
 
1303
1411
  // src/curves/index.ts
@@ -1306,12 +1414,16 @@ var curves = {
1306
1414
  epitrochoid7,
1307
1415
  astroid,
1308
1416
  deltoid,
1309
- rose5,
1310
1417
  rose3,
1418
+ rose5,
1419
+ rose52,
1420
+ star,
1421
+ star4,
1422
+ star7,
1311
1423
  lissajous32,
1312
1424
  lissajous43,
1313
1425
  epicycloid3,
1314
- lame
1426
+ lame,
1315
1427
  };
1316
1428
 
1317
1429
  // src/index.ts
@@ -1339,4 +1451,4 @@ exports.palettes = palettes;
1339
1451
  exports.rose3 = rose3;
1340
1452
  exports.rose5 = rose5;
1341
1453
  //# sourceMappingURL=index.cjs.map
1342
- //# sourceMappingURL=index.cjs.map
1454
+ //# sourceMappingURL=index.cjs.map