@sarmal/core 0.10.0 → 0.13.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 (63) hide show
  1. package/dist/auto-init.cjs +189 -94
  2. package/dist/auto-init.cjs.map +1 -1
  3. package/dist/auto-init.d.cts +1 -2
  4. package/dist/auto-init.d.ts +1 -2
  5. package/dist/auto-init.js +188 -93
  6. package/dist/auto-init.js.map +1 -1
  7. package/dist/curves/artemis2.cjs +10 -7
  8. package/dist/curves/artemis2.d.cts +1 -1
  9. package/dist/curves/artemis2.d.ts +1 -1
  10. package/dist/curves/artemis2.js +9 -6
  11. package/dist/curves/astroid.cjs +4 -4
  12. package/dist/curves/astroid.d.cts +1 -1
  13. package/dist/curves/astroid.d.ts +1 -1
  14. package/dist/curves/astroid.js +3 -3
  15. package/dist/curves/deltoid.cjs +4 -4
  16. package/dist/curves/deltoid.d.cts +1 -1
  17. package/dist/curves/deltoid.d.ts +1 -1
  18. package/dist/curves/deltoid.js +3 -3
  19. package/dist/curves/epicycloid3.cjs +4 -4
  20. package/dist/curves/epicycloid3.d.cts +1 -1
  21. package/dist/curves/epicycloid3.d.ts +1 -1
  22. package/dist/curves/epicycloid3.js +3 -3
  23. package/dist/curves/epitrochoid7.cjs +5 -5
  24. package/dist/curves/epitrochoid7.d.cts +1 -1
  25. package/dist/curves/epitrochoid7.d.ts +1 -1
  26. package/dist/curves/epitrochoid7.js +4 -4
  27. package/dist/curves/index.cjs +32 -28
  28. package/dist/curves/index.cjs.map +1 -1
  29. package/dist/curves/index.d.cts +25 -13
  30. package/dist/curves/index.d.ts +25 -13
  31. package/dist/curves/index.js +44 -28
  32. package/dist/curves/index.js.map +1 -1
  33. package/dist/curves/lame.cjs +6 -5
  34. package/dist/curves/lame.d.cts +1 -1
  35. package/dist/curves/lame.d.ts +1 -1
  36. package/dist/curves/lame.js +5 -4
  37. package/dist/curves/lissajous32.cjs +4 -4
  38. package/dist/curves/lissajous32.d.cts +1 -1
  39. package/dist/curves/lissajous32.d.ts +1 -1
  40. package/dist/curves/lissajous32.js +3 -3
  41. package/dist/curves/lissajous43.cjs +4 -4
  42. package/dist/curves/lissajous43.d.cts +1 -1
  43. package/dist/curves/lissajous43.d.ts +1 -1
  44. package/dist/curves/lissajous43.js +3 -3
  45. package/dist/curves/rose3.cjs +4 -4
  46. package/dist/curves/rose3.d.cts +1 -1
  47. package/dist/curves/rose3.d.ts +1 -1
  48. package/dist/curves/rose3.js +3 -3
  49. package/dist/curves/rose5.cjs +4 -4
  50. package/dist/curves/rose5.d.cts +1 -1
  51. package/dist/curves/rose5.d.ts +1 -1
  52. package/dist/curves/rose5.js +3 -3
  53. package/dist/index.cjs +216 -108
  54. package/dist/index.cjs.map +1 -1
  55. package/dist/index.d.cts +69 -34
  56. package/dist/index.d.ts +69 -34
  57. package/dist/index.js +233 -108
  58. package/dist/index.js.map +1 -1
  59. package/dist/types-BW0bpL1Z.d.cts +290 -0
  60. package/dist/types-BW0bpL1Z.d.ts +290 -0
  61. package/package.json +1 -1
  62. package/dist/types-DcyISvnH.d.cts +0 -230
  63. package/dist/types-DcyISvnH.d.ts +0 -230
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,22 +63,24 @@ 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);
76
76
  const trail = new CircularBuffer(trailLength);
77
77
  let t = 0;
78
78
  let actualTime = 0;
79
+ let userSpeedOverride = null;
79
80
  let morphCurveB = null;
80
81
  let _morphAlpha = null;
81
82
  let _morphStrategy = "normalized";
83
+ let _speedTransition = null;
82
84
  function sampleSkeleton(c, sampleT) {
83
85
  if (c.skeletonFn) {
84
86
  return c.skeletonFn(sampleT);
@@ -90,15 +92,25 @@ function createEngine(curveDef, trailLength = 120) {
90
92
  }
91
93
  return {
92
94
  tick(deltaTime) {
93
- let effectiveSpeed = curve.speed;
95
+ if (_speedTransition !== null) {
96
+ _speedTransition.elapsed += deltaTime * 1e3;
97
+ const alpha = Math.min(_speedTransition.elapsed / _speedTransition.duration, 1);
98
+ userSpeedOverride = lerp(_speedTransition.from, _speedTransition.to, alpha);
99
+ if (alpha >= 1) {
100
+ userSpeedOverride = _speedTransition.to;
101
+ _speedTransition.resolve();
102
+ _speedTransition = null;
103
+ }
104
+ }
105
+ let effectiveSpeed = userSpeedOverride ?? curve.speed;
94
106
  if (morphCurveB !== null && _morphAlpha !== null) {
95
- effectiveSpeed = lerp(curve.speed, morphCurveB.speed, _morphAlpha);
107
+ effectiveSpeed = lerp(effectiveSpeed, morphCurveB.speed, _morphAlpha);
96
108
  }
97
109
  t = (t + effectiveSpeed * deltaTime) % curve.period;
98
110
  actualTime += deltaTime;
99
111
  if (morphCurveB !== null && _morphAlpha !== null) {
100
112
  const a = curve.fn(t, actualTime, EMPTY_PARAMS);
101
- const tB = _morphStrategy === "normalized" ? t / curve.period * morphCurveB.period : t;
113
+ const tB = _morphStrategy === "normalized" ? (t / curve.period) * morphCurveB.period : t;
102
114
  const b = morphCurveB.fn(tB, actualTime, EMPTY_PARAMS);
103
115
  trail.push(a.x + (b.x - a.x) * _morphAlpha, a.y + (b.y - a.y) * _morphAlpha);
104
116
  } else {
@@ -122,14 +134,14 @@ function createEngine(curveDef, trailLength = 120) {
122
134
  trail.clear();
123
135
  },
124
136
  jump(newT, { clearTrail = false } = {}) {
125
- t = (newT % curve.period + curve.period) % curve.period;
137
+ t = ((newT % curve.period) + curve.period) % curve.period;
126
138
  if (clearTrail) {
127
139
  trail.clear();
128
140
  }
129
141
  },
130
142
  seek(targetT, { wrap = false, step = curve.period / trailLength } = {}) {
131
143
  const advance = curve.speed * step;
132
- const target = (targetT % curve.period + curve.period) % curve.period;
144
+ const target = ((targetT % curve.period) + curve.period) % curve.period;
133
145
  const targetTime = target / curve.speed;
134
146
  t = target;
135
147
  actualTime = targetTime;
@@ -138,7 +150,7 @@ function createEngine(curveDef, trailLength = 120) {
138
150
  const count = wrap ? trailLength : Math.min(trailLength, pointsFromStart);
139
151
  for (let i = count - 1; i >= 0; i--) {
140
152
  const sampleT = target - i * advance;
141
- const wrappedT = (sampleT % curve.period + curve.period) % curve.period;
153
+ const wrappedT = ((sampleT % curve.period) + curve.period) % curve.period;
142
154
  const time = targetTime - i * step;
143
155
  const point = curve.fn(wrappedT, time, EMPTY_PARAMS);
144
156
  trail.push(point.x, point.y);
@@ -155,13 +167,16 @@ function createEngine(curveDef, trailLength = 120) {
155
167
  ...frozenB,
156
168
  fn: (sampleT, time, params) => {
157
169
  const a = frozenA.fn(sampleT, time, params);
158
- const tB = frozenStrategy === "normalized" ? sampleT / frozenA.period * frozenB.period : sampleT;
170
+ const tB =
171
+ frozenStrategy === "normalized"
172
+ ? (sampleT / frozenA.period) * frozenB.period
173
+ : sampleT;
159
174
  const b = frozenB.fn(tB, time, params);
160
175
  return {
161
176
  x: a.x + (b.x - a.x) * frozenAlpha,
162
- y: a.y + (b.y - a.y) * frozenAlpha
177
+ y: a.y + (b.y - a.y) * frozenAlpha,
163
178
  };
164
- }
179
+ },
165
180
  };
166
181
  }
167
182
  _morphStrategy = strategy;
@@ -174,7 +189,7 @@ function createEngine(curveDef, trailLength = 120) {
174
189
  completeMorph() {
175
190
  if (morphCurveB !== null) {
176
191
  if (_morphStrategy === "normalized" && curve.period !== morphCurveB.period) {
177
- t = t / curve.period * morphCurveB.period;
192
+ t = (t / curve.period) * morphCurveB.period;
178
193
  }
179
194
  curve = morphCurveB;
180
195
  }
@@ -186,23 +201,64 @@ function createEngine(curveDef, trailLength = 120) {
186
201
  const points = new Array(steps);
187
202
  if (morphCurveB !== null && _morphAlpha !== null) {
188
203
  for (let i = 0; i < steps; i++) {
189
- const sampleT = i / (steps - 1) * curve.period;
204
+ const sampleT = (i / (steps - 1)) * curve.period;
190
205
  const a = sampleSkeleton(curve, sampleT);
191
- const tB = _morphStrategy === "normalized" ? sampleT / curve.period * morphCurveB.period : sampleT;
206
+ const tB =
207
+ _morphStrategy === "normalized"
208
+ ? (sampleT / curve.period) * morphCurveB.period
209
+ : sampleT;
192
210
  const b = sampleSkeleton(morphCurveB, tB);
193
211
  points[i] = {
194
212
  x: a.x + (b.x - a.x) * _morphAlpha,
195
- y: a.y + (b.y - a.y) * _morphAlpha
213
+ y: a.y + (b.y - a.y) * _morphAlpha,
196
214
  };
197
215
  }
198
216
  return points;
199
217
  }
200
218
  for (let i = 0; i < steps; i++) {
201
- const sampleT = i / (steps - 1) * curve.period;
219
+ const sampleT = (i / (steps - 1)) * curve.period;
202
220
  points[i] = sampleSkeleton(curve, sampleT);
203
221
  }
204
222
  return points;
205
- }
223
+ },
224
+ setSpeed(speed) {
225
+ if (!Number.isFinite(speed)) {
226
+ throw new Error("speed must be a finite number");
227
+ }
228
+ if (_speedTransition !== null) {
229
+ _speedTransition.reject(new Error("Speed transition cancelled"));
230
+ _speedTransition = null;
231
+ }
232
+ userSpeedOverride = speed;
233
+ },
234
+ getSpeed() {
235
+ return userSpeedOverride ?? curve.speed;
236
+ },
237
+ resetSpeed() {
238
+ userSpeedOverride = null;
239
+ },
240
+ setSpeedOver(speed, duration) {
241
+ if (!Number.isFinite(speed)) {
242
+ throw new Error("speed must be a finite number");
243
+ }
244
+ if (!Number.isFinite(duration) || duration <= 0) {
245
+ throw new Error("duration must be a finite number greater than 0");
246
+ }
247
+ if (_speedTransition !== null) {
248
+ _speedTransition.reject(new Error("Speed transition cancelled"));
249
+ _speedTransition = null;
250
+ }
251
+ const from = userSpeedOverride ?? curve.speed;
252
+ return new Promise((resolve, reject) => {
253
+ _speedTransition = { from, to: speed, elapsed: 0, duration, resolve, reject };
254
+ });
255
+ },
256
+ cancelSpeedTransition() {
257
+ if (_speedTransition !== null) {
258
+ _speedTransition.reject(new Error("Speed transition cancelled"));
259
+ _speedTransition = null;
260
+ }
261
+ },
206
262
  };
207
263
  }
208
264
 
@@ -210,6 +266,7 @@ function createEngine(curveDef, trailLength = 120) {
210
266
  var DEFAULT_MORPH_DURATION_MS = 300;
211
267
  var DEFAULT_SKELETON_OPACITY = 0.15;
212
268
  var FIT_PADDING = 0.1;
269
+ var FIT_PADDING_MIN = 4;
213
270
  var TRAIL_FADE_CURVE = 1.5;
214
271
  var TRAIL_MAX_OPACITY = 0.88;
215
272
  var TRAIL_MIN_WIDTH = 0.5;
@@ -264,13 +321,16 @@ function computeTrailQuad(trail, i, trailCount, toX, toY) {
264
321
  r1x: nx - n1.x * w1,
265
322
  r1y: ny - n1.y * w1,
266
323
  opacity,
267
- progress
324
+ progress,
268
325
  };
269
326
  }
270
327
  function computeBoundaries(pts, logicalWidth, logicalHeight) {
271
328
  if (pts.length === 0) return null;
272
329
  const first = pts[0];
273
- 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;
274
334
  for (const p of pts) {
275
335
  if (p.x < minX) {
276
336
  minX = p.x;
@@ -289,21 +349,37 @@ function computeBoundaries(pts, logicalWidth, logicalHeight) {
289
349
  const h = maxY - minY;
290
350
  if (w === 0 && h === 0) {
291
351
  throw new Error(
292
- "[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.",
293
353
  );
294
354
  }
295
- const scaleX = logicalWidth / (w * (1 + FIT_PADDING * 2));
296
- const scaleY = logicalHeight / (h * (1 + FIT_PADDING * 2));
297
- const scale = Math.min(scaleX, scaleY);
355
+ const scaleXProportional = logicalWidth / (w * (1 + FIT_PADDING * 2));
356
+ const scaleYProportional = logicalHeight / (h * (1 + FIT_PADDING * 2));
357
+ const scaleXMinPadding = (logicalWidth - FIT_PADDING_MIN * 2) / w;
358
+ const scaleYMinPadding = (logicalHeight - FIT_PADDING_MIN * 2) / h;
359
+ const scale = Math.min(
360
+ scaleXProportional,
361
+ scaleYProportional,
362
+ scaleXMinPadding,
363
+ scaleYMinPadding,
364
+ );
298
365
  return {
299
366
  scale,
300
367
  offsetX: (logicalWidth - w * scale) / 2 - minX * scale,
301
- offsetY: (logicalHeight - h * scale) / 2 - minY * scale
368
+ offsetY: (logicalHeight - h * scale) / 2 - minY * scale,
369
+ };
370
+ }
371
+ function enginePassthroughs(engine) {
372
+ return {
373
+ jump: engine.jump,
374
+ seek: engine.seek,
375
+ setSpeed: engine.setSpeed,
376
+ getSpeed: engine.getSpeed,
377
+ resetSpeed: engine.resetSpeed,
378
+ setSpeedOver: engine.setSpeedOver,
302
379
  };
303
380
  }
304
381
 
305
382
  // src/renderer.ts
306
- var DEFAULT_HEAD_RADIUS = 4;
307
383
  var DEFAULT_SKELETON_COLOR = "#ffffff";
308
384
  var GRADIENT = {
309
385
  bard: ["#a855f7", "#3b82f6", "#14b8a6", "#ec4899"],
@@ -311,7 +387,7 @@ var GRADIENT = {
311
387
  ocean: ["#1e3a8a", "#06b6d4", "#22d3ee", "#e0f2fe"],
312
388
  ice: ["#1e3a8a", "#67e8f9"],
313
389
  fire: ["#7f1d1d", "#fbbf24"],
314
- forest: ["#14532d", "#86efac"]
390
+ forest: ["#14532d", "#86efac"],
315
391
  };
316
392
  var PRESETS = {
317
393
  bard: GRADIENT.bard,
@@ -319,16 +395,16 @@ var PRESETS = {
319
395
  ocean: GRADIENT.ocean,
320
396
  ice: GRADIENT.ice,
321
397
  fire: GRADIENT.fire,
322
- forest: GRADIENT.forest
398
+ forest: GRADIENT.forest,
323
399
  };
324
400
  function hexToRgb(hex) {
325
401
  const n = parseInt(hex.slice(1), 16);
326
- return { r: n >> 16, g: n >> 8 & 255, b: n & 255 };
402
+ return { r: n >> 16, g: (n >> 8) & 255, b: n & 255 };
327
403
  }
328
404
  var lerpRgb = (a, b, t) => ({
329
405
  r: Math.round(a.r + (b.r - a.r) * t),
330
406
  g: Math.round(a.g + (b.g - a.g) * t),
331
- b: Math.round(a.b + (b.b - a.b) * t)
407
+ b: Math.round(a.b + (b.b - a.b) * t),
332
408
  });
333
409
  function getPaletteColor(palette, position, timeOffset = 0) {
334
410
  if (palette.length === 0) return { r: 255, g: 255, b: 255 };
@@ -348,7 +424,7 @@ function resolvePalette(palette, trailStyle) {
348
424
  }
349
425
  function hexToRgbComponents(hex) {
350
426
  const n = parseInt(hex.slice(1), 16);
351
- return `${n >> 16},${n >> 8 & 255},${n & 255}`;
427
+ return `${n >> 16},${(n >> 8) & 255},${n & 255}`;
352
428
  }
353
429
  function applyDprSizing(target, logicalWidth, logicalHeight, dpr) {
354
430
  target.style.width = `${logicalWidth}px`;
@@ -363,14 +439,21 @@ function createRenderer(options) {
363
439
  }
364
440
  const ctx = canvas.getContext("2d");
365
441
  const engine = options.engine;
442
+ const trailStyle = options.trailStyle ?? "default";
443
+ const trailColor = options.trailColor ?? "#ffffff";
444
+ const palette = resolvePalette(options.palette, trailStyle);
445
+ function defaultHeadColor() {
446
+ if (trailStyle !== "default") {
447
+ const { r, g, b } = getPaletteColor(palette, 1);
448
+ return `rgb(${r},${g},${b})`;
449
+ }
450
+ return trailColor;
451
+ }
366
452
  const opts = {
367
453
  skeletonColor: options.skeletonColor ?? DEFAULT_SKELETON_COLOR,
368
- trailColor: options.trailColor ?? "#ffffff",
369
- headColor: options.headColor ?? "#ffffff",
370
- headRadius: options.headRadius ?? DEFAULT_HEAD_RADIUS
454
+ trailColor,
455
+ headColor: options.headColor ?? defaultHeadColor(),
371
456
  };
372
- const trailStyle = options.trailStyle ?? "default";
373
- const palette = resolvePalette(options.palette, trailStyle);
374
457
  const trailRgb = hexToRgbComponents(opts.trailColor);
375
458
  const dpr = typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1;
376
459
  function setupCanvas() {
@@ -470,7 +553,7 @@ function createRenderer(options) {
470
553
  i,
471
554
  trailCount,
472
555
  toX,
473
- toY
556
+ toY,
474
557
  );
475
558
  if (trailStyle === "default") {
476
559
  ctx.fillStyle = `rgba(${trailRgb},${opacity})`;
@@ -494,15 +577,14 @@ function createRenderer(options) {
494
577
  }
495
578
  const x = head.x * scale + offsetX;
496
579
  const y = head.y * scale + offsetY;
580
+ const r =
581
+ options.headRadius ?? Math.max(2, 3 * Math.sqrt(Math.min(logicalWidth, logicalHeight) / 160));
497
582
  ctx.fillStyle = opts.headColor;
498
583
  ctx.beginPath();
499
- ctx.arc(x, y, opts.headRadius, 0, Math.PI * 2);
584
+ ctx.arc(x, y, r, 0, Math.PI * 2);
500
585
  ctx.fill();
501
586
  }
502
- function render() {
503
- const now = performance.now();
504
- const deltaTime = Math.min((now - lastTime) / 1e3, 1 / 30);
505
- lastTime = now;
587
+ function renderFrame(deltaTime) {
506
588
  if (trailStyle === "gradient-animated") {
507
589
  gradientAnimTime += deltaTime * 1e3;
508
590
  }
@@ -538,27 +620,39 @@ function createRenderer(options) {
538
620
  drawSkeleton();
539
621
  drawTrail();
540
622
  drawHead();
541
- animationId = requestAnimationFrame(render);
623
+ }
624
+ function loop() {
625
+ const now = performance.now();
626
+ const deltaTime = Math.min((now - lastTime) / 1e3, 1 / 30);
627
+ lastTime = now;
628
+ renderFrame(deltaTime);
629
+ animationId = requestAnimationFrame(loop);
542
630
  }
543
631
  skeleton = engine.getSarmalSkeleton();
544
632
  calculateBoundaries();
545
633
  if (!engine.isLiveSkeleton) {
546
634
  buildSkeletonCanvas();
547
635
  }
548
- return {
549
- start() {
636
+ if (options.initialT !== void 0) {
637
+ engine.seek(options.initialT);
638
+ }
639
+ renderFrame(0);
640
+ const shouldAutoStart = options.autoStart !== false;
641
+ const instance = {
642
+ play() {
550
643
  if (animationId !== null) {
551
644
  return;
552
645
  }
553
646
  lastTime = performance.now();
554
- render();
647
+ loop();
555
648
  },
556
- stop() {
649
+ pause() {
557
650
  if (animationId === null) {
558
651
  return;
559
652
  }
560
653
  cancelAnimationFrame(animationId);
561
654
  animationId = null;
655
+ engine.cancelSpeedTransition();
562
656
  },
563
657
  reset() {
564
658
  engine.reset();
@@ -571,12 +665,7 @@ function createRenderer(options) {
571
665
  animationId = null;
572
666
  }
573
667
  },
574
- jump(t, options2) {
575
- engine.jump(t, options2);
576
- },
577
- seek(t, options2) {
578
- engine.seek(t, options2);
579
- },
668
+ ...enginePassthroughs(engine),
580
669
  morphTo(target, options2) {
581
670
  if (morphResolve !== null) {
582
671
  engine.completeMorph();
@@ -590,8 +679,12 @@ function createRenderer(options) {
590
679
  return new Promise((resolve) => {
591
680
  morphResolve = resolve;
592
681
  });
593
- }
682
+ },
594
683
  };
684
+ if (shouldAutoStart) {
685
+ instance.play();
686
+ }
687
+ return instance;
595
688
  }
596
689
 
597
690
  // src/renderer-svg.ts
@@ -612,7 +705,7 @@ function sampleCurveSkeleton(curveDef) {
612
705
  const samples = Math.ceil(period * 50);
613
706
  const pts = Array.from({ length: samples });
614
707
  for (let i = 0; i < samples; i++) {
615
- const t = i / (samples - 1) * period;
708
+ const t = (i / (samples - 1)) * period;
616
709
  pts[i] = curveDef.skeletonFn ? curveDef.skeletonFn(t) : curveDef.fn(t, 0, EMPTY_PARAMS2);
617
710
  }
618
711
  return pts;
@@ -622,16 +715,18 @@ function el(tag) {
622
715
  }
623
716
  function createSVGRenderer(options) {
624
717
  const { container, engine } = options;
718
+ const trailColor = options.trailColor ?? "#ffffff";
625
719
  const opts = {
626
720
  skeletonColor: options.skeletonColor ?? "#ffffff",
627
- trailColor: options.trailColor ?? "#ffffff",
628
- headColor: options.headColor ?? "#ffffff",
629
- headRadius: options.headRadius ?? 4,
630
- ariaLabel: options.ariaLabel ?? "Loading"
721
+ trailColor,
722
+ headColor: options.headColor ?? trailColor,
723
+ ariaLabel: options.ariaLabel ?? "Loading",
631
724
  };
632
725
  const rect = container.getBoundingClientRect();
633
726
  const width = rect.width || 200;
634
727
  const height = rect.height || 200;
728
+ const headRadius =
729
+ options.headRadius ?? Math.max(2, 3 * Math.sqrt(Math.min(width, height) / 160));
635
730
  const svg = el("svg");
636
731
  svg.setAttribute("width", String(width));
637
732
  svg.setAttribute("height", String(height));
@@ -670,7 +765,7 @@ function createSVGRenderer(options) {
670
765
  }
671
766
  const headCircle = el("circle");
672
767
  headCircle.setAttribute("fill", opts.headColor);
673
- headCircle.setAttribute("r", String(opts.headRadius));
768
+ headCircle.setAttribute("r", String(headRadius));
674
769
  svg.appendChild(headCircle);
675
770
  container.appendChild(svg);
676
771
  let scale = 1;
@@ -711,7 +806,7 @@ function createSVGRenderer(options) {
711
806
  i,
712
807
  trailCount,
713
808
  px,
714
- py
809
+ py,
715
810
  );
716
811
  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`;
717
812
  trailPaths[i].setAttribute("d", d);
@@ -733,24 +828,22 @@ function createSVGRenderer(options) {
733
828
  }
734
829
  let animationId = null;
735
830
  let lastTime = 0;
736
- const prefersReducedMotion = typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
831
+ const prefersReducedMotion =
832
+ typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
737
833
  let morphResolve = null;
738
834
  let morphDurationMs = DEFAULT_MORPH_DURATION_MS;
739
835
  let morphTarget = null;
740
836
  let morphAlpha = 0;
741
- function renderFrame() {
742
- const now = performance.now();
743
- const dt = Math.min((now - lastTime) / 1e3, 1 / 30);
744
- lastTime = now;
837
+ function renderFrame(deltaTime) {
745
838
  if (engine.morphAlpha !== null) {
746
- morphAlpha = Math.min(1, morphAlpha + dt / (morphDurationMs / 1e3));
839
+ morphAlpha = Math.min(1, morphAlpha + deltaTime / (morphDurationMs / 1e3));
747
840
  engine.setMorphAlpha(morphAlpha);
748
841
  if (morphPathABuilt) {
749
842
  skeletonPathA.setAttribute("d", morphPathABuilt);
750
843
  skeletonPathA.setAttribute("visibility", "visible");
751
844
  skeletonPathA.setAttribute(
752
845
  "stroke-opacity",
753
- String((1 - morphAlpha) * DEFAULT_SKELETON_OPACITY)
846
+ String((1 - morphAlpha) * DEFAULT_SKELETON_OPACITY),
754
847
  );
755
848
  }
756
849
  if (morphPathBBuilt) {
@@ -773,7 +866,7 @@ function createSVGRenderer(options) {
773
866
  updateSkeleton(newSkeleton);
774
867
  }
775
868
  }
776
- const trail = engine.tick(dt);
869
+ const trail = engine.tick(deltaTime);
777
870
  const trailCount = engine.trailCount;
778
871
  if (engine.isLiveSkeleton && engine.morphAlpha === null) {
779
872
  const liveSkeleton = engine.getSarmalSkeleton();
@@ -782,24 +875,36 @@ function createSVGRenderer(options) {
782
875
  }
783
876
  updateTrail(trail, trailCount);
784
877
  updateHead(trail, trailCount);
878
+ }
879
+ function loop() {
880
+ const now = performance.now();
881
+ const deltaTime = Math.min((now - lastTime) / 1e3, 1 / 30);
882
+ lastTime = now;
883
+ renderFrame(deltaTime);
785
884
  if (!prefersReducedMotion) {
786
- animationId = requestAnimationFrame(renderFrame);
885
+ animationId = requestAnimationFrame(loop);
787
886
  }
788
887
  }
789
- return {
790
- start() {
888
+ if (options.initialT !== void 0) {
889
+ engine.seek(options.initialT);
890
+ }
891
+ renderFrame(0);
892
+ const shouldAutoStart = options.autoStart !== false;
893
+ const instance = {
894
+ play() {
791
895
  if (animationId !== null) {
792
896
  return;
793
897
  }
794
898
  lastTime = performance.now();
795
- renderFrame();
899
+ loop();
796
900
  },
797
- stop() {
901
+ pause() {
798
902
  if (animationId === null) {
799
903
  return;
800
904
  }
801
905
  cancelAnimationFrame(animationId);
802
906
  animationId = null;
907
+ engine.cancelSpeedTransition();
803
908
  },
804
909
  reset() {
805
910
  engine.reset();
@@ -811,12 +916,7 @@ function createSVGRenderer(options) {
811
916
  }
812
917
  svg.remove();
813
918
  },
814
- jump(t, options2) {
815
- engine.jump(t, options2);
816
- },
817
- seek(t, options2) {
818
- engine.seek(t, options2);
819
- },
919
+ ...enginePassthroughs(engine),
820
920
  morphTo(target, options2) {
821
921
  if (morphResolve !== null) {
822
922
  engine.completeMorph();
@@ -839,8 +939,12 @@ function createSVGRenderer(options) {
839
939
  return new Promise((resolve) => {
840
940
  morphResolve = resolve;
841
941
  });
842
- }
942
+ },
843
943
  };
944
+ if (shouldAutoStart) {
945
+ instance.play();
946
+ }
947
+ return instance;
844
948
  }
845
949
  function createSarmalSVG(container, curveDef, options) {
846
950
  const { trailLength, ...rendererOpts } = options ?? {};
@@ -851,19 +955,22 @@ function createSarmalSVG(container, curveDef, options) {
851
955
  // src/curves/artemis2.ts
852
956
  var TWO_PI2 = Math.PI * 2;
853
957
  function artemis2Fn(t, _time, _params) {
854
- const a = 0.35, b = 0.15, ox = 0.175;
855
- const s = Math.sin(t), c = Math.cos(t);
958
+ const a = 0.35,
959
+ b = 0.15,
960
+ ox = 0.175;
961
+ const s = Math.sin(t),
962
+ c = Math.cos(t);
856
963
  const denom = 1 + s * s;
857
964
  return {
858
- x: c * (1 + a * c) / denom - ox,
859
- y: s * c * (1 + b * c) / denom
965
+ x: (c * (1 + a * c)) / denom - ox,
966
+ y: (s * c * (1 + b * c)) / denom,
860
967
  };
861
968
  }
862
969
  var artemis2 = {
863
970
  name: "Artemis II",
864
971
  fn: artemis2Fn,
865
972
  period: TWO_PI2,
866
- speed: 0.7
973
+ speed: 0.7,
867
974
  };
868
975
 
869
976
  // src/curves/astroid.ts
@@ -873,14 +980,14 @@ function astroidFn(t, _time, _params) {
873
980
  const s = Math.sin(t);
874
981
  return {
875
982
  x: c * c * c,
876
- y: s * s * s
983
+ y: s * s * s,
877
984
  };
878
985
  }
879
986
  var astroid = {
880
987
  name: "Astroid",
881
988
  fn: astroidFn,
882
989
  period: TWO_PI3,
883
- speed: 1.1
990
+ speed: 1.1,
884
991
  };
885
992
 
886
993
  // src/curves/deltoid.ts
@@ -888,14 +995,14 @@ var TWO_PI4 = Math.PI * 2;
888
995
  function deltoidFn(t, _time, _params) {
889
996
  return {
890
997
  x: 2 * Math.cos(t) + Math.cos(2 * t),
891
- y: 2 * Math.sin(t) - Math.sin(2 * t)
998
+ y: 2 * Math.sin(t) - Math.sin(2 * t),
892
999
  };
893
1000
  }
894
1001
  var deltoid = {
895
1002
  name: "Deltoid",
896
1003
  fn: deltoidFn,
897
1004
  period: TWO_PI4,
898
- speed: 0.9
1005
+ speed: 0.9,
899
1006
  };
900
1007
 
901
1008
  // src/curves/epicycloid3.ts
@@ -903,14 +1010,14 @@ var TWO_PI5 = Math.PI * 2;
903
1010
  function epicycloid3Fn(t, _time, _params) {
904
1011
  return {
905
1012
  x: 4 * Math.cos(t) - Math.cos(4 * t),
906
- y: 4 * Math.sin(t) - Math.sin(4 * t)
1013
+ y: 4 * Math.sin(t) - Math.sin(4 * t),
907
1014
  };
908
1015
  }
909
1016
  var epicycloid3 = {
910
1017
  name: "Epicycloid (n=3)",
911
1018
  fn: epicycloid3Fn,
912
1019
  period: TWO_PI5,
913
- speed: 0.75
1020
+ speed: 0.75,
914
1021
  };
915
1022
 
916
1023
  // src/curves/epitrochoid7.ts
@@ -919,14 +1026,14 @@ function epitrochoid7Fn(t, _time, _params) {
919
1026
  const d = 1 + 0.55 * Math.sin(t * 0.5);
920
1027
  return {
921
1028
  x: 7 * Math.cos(t) - d * Math.cos(7 * t),
922
- y: 7 * Math.sin(t) - d * Math.sin(7 * t)
1029
+ y: 7 * Math.sin(t) - d * Math.sin(7 * t),
923
1030
  };
924
1031
  }
925
1032
  function epitrochoid7SkeletonFn(t) {
926
1033
  const d = 1.275;
927
1034
  return {
928
1035
  x: 7 * Math.cos(t) - d * Math.cos(7 * t),
929
- y: 7 * Math.sin(t) - d * Math.sin(7 * t)
1036
+ y: 7 * Math.sin(t) - d * Math.sin(7 * t),
930
1037
  };
931
1038
  }
932
1039
  var epitrochoid7 = {
@@ -934,7 +1041,7 @@ var epitrochoid7 = {
934
1041
  fn: epitrochoid7Fn,
935
1042
  period: TWO_PI6,
936
1043
  speed: 1.4,
937
- skeletonFn: epitrochoid7SkeletonFn
1044
+ skeletonFn: epitrochoid7SkeletonFn,
938
1045
  };
939
1046
 
940
1047
  // src/curves/lissajous32.ts
@@ -943,7 +1050,7 @@ function lissajous32Fn(t, time, _params) {
943
1050
  const phi = time * 0.45;
944
1051
  return {
945
1052
  x: Math.sin(3 * t + phi),
946
- y: Math.sin(2 * t)
1053
+ y: Math.sin(2 * t),
947
1054
  };
948
1055
  }
949
1056
  var lissajous32 = {
@@ -951,7 +1058,7 @@ var lissajous32 = {
951
1058
  fn: lissajous32Fn,
952
1059
  period: TWO_PI7,
953
1060
  speed: 2,
954
- skeleton: "live"
1061
+ skeleton: "live",
955
1062
  };
956
1063
 
957
1064
  // src/curves/lissajous43.ts
@@ -960,7 +1067,7 @@ function lissajous43Fn(t, time, _params) {
960
1067
  const phi = time * 0.38;
961
1068
  return {
962
1069
  x: Math.sin(4 * t + phi),
963
- y: Math.sin(3 * t)
1070
+ y: Math.sin(3 * t),
964
1071
  };
965
1072
  }
966
1073
  var lissajous43 = {
@@ -968,17 +1075,18 @@ var lissajous43 = {
968
1075
  fn: lissajous43Fn,
969
1076
  period: TWO_PI8,
970
1077
  speed: 1.8,
971
- skeleton: "live"
1078
+ skeleton: "live",
972
1079
  };
973
1080
 
974
1081
  // src/curves/lame.ts
975
1082
  var TWO_PI9 = Math.PI * 2;
976
1083
  function lameFn(t, time, _params) {
977
1084
  const p = 1.75 + 1.25 * Math.sin(time * 0.48);
978
- const c = Math.cos(t), s = Math.sin(t);
1085
+ const c = Math.cos(t),
1086
+ s = Math.sin(t);
979
1087
  return {
980
1088
  x: Math.sign(c) * Math.pow(Math.abs(c), p),
981
- y: Math.sign(s) * Math.pow(Math.abs(s), p)
1089
+ y: Math.sign(s) * Math.pow(Math.abs(s), p),
982
1090
  };
983
1091
  }
984
1092
  var lame = {
@@ -986,7 +1094,7 @@ var lame = {
986
1094
  fn: lameFn,
987
1095
  period: TWO_PI9,
988
1096
  speed: 1,
989
- skeleton: "live"
1097
+ skeleton: "live",
990
1098
  };
991
1099
 
992
1100
  // src/curves/rose3.ts
@@ -995,14 +1103,14 @@ function rose3Fn(t, _time, _params) {
995
1103
  const r = Math.cos(3 * t);
996
1104
  return {
997
1105
  x: r * Math.cos(t),
998
- y: r * Math.sin(t)
1106
+ y: r * Math.sin(t),
999
1107
  };
1000
1108
  }
1001
1109
  var rose3 = {
1002
1110
  name: "Rose (n=3)",
1003
1111
  fn: rose3Fn,
1004
1112
  period: TWO_PI10,
1005
- speed: 1.15
1113
+ speed: 1.15,
1006
1114
  };
1007
1115
 
1008
1116
  // src/curves/rose5.ts
@@ -1011,14 +1119,14 @@ function rose5Fn(t, _time, _params) {
1011
1119
  const r = Math.cos(5 * t);
1012
1120
  return {
1013
1121
  x: r * Math.cos(t),
1014
- y: r * Math.sin(t)
1122
+ y: r * Math.sin(t),
1015
1123
  };
1016
1124
  }
1017
1125
  var rose5 = {
1018
1126
  name: "Rose (n=5)",
1019
1127
  fn: rose5Fn,
1020
1128
  period: TWO_PI11,
1021
- speed: 1
1129
+ speed: 1,
1022
1130
  };
1023
1131
 
1024
1132
  // src/curves/index.ts
@@ -1032,7 +1140,7 @@ var curves = {
1032
1140
  lissajous32,
1033
1141
  lissajous43,
1034
1142
  epicycloid3,
1035
- lame
1143
+ lame,
1036
1144
  };
1037
1145
 
1038
1146
  // src/index.ts
@@ -1059,4 +1167,4 @@ exports.lissajous43 = lissajous43;
1059
1167
  exports.rose3 = rose3;
1060
1168
  exports.rose5 = rose5;
1061
1169
  //# sourceMappingURL=index.cjs.map
1062
- //# sourceMappingURL=index.cjs.map
1170
+ //# sourceMappingURL=index.cjs.map