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