@sarmal/core 0.24.0 → 0.25.1

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 (79) hide show
  1. package/dist/auto-init.cjs +245 -141
  2. package/dist/auto-init.cjs.map +1 -1
  3. package/dist/auto-init.js +244 -140
  4. package/dist/auto-init.js.map +1 -1
  5. package/dist/curves/artemis2.cjs +87 -14
  6. package/dist/curves/artemis2.cjs.map +1 -1
  7. package/dist/curves/artemis2.d.cts +1 -5
  8. package/dist/curves/artemis2.d.ts +1 -5
  9. package/dist/curves/artemis2.js +86 -13
  10. package/dist/curves/artemis2.js.map +1 -1
  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 +154 -71
  28. package/dist/curves/index.cjs.map +1 -1
  29. package/dist/curves/index.d.cts +29 -29
  30. package/dist/curves/index.d.ts +29 -29
  31. package/dist/curves/index.js +170 -71
  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/curves/rose52.cjs +5 -5
  54. package/dist/curves/rose52.d.cts +1 -1
  55. package/dist/curves/rose52.d.ts +1 -1
  56. package/dist/curves/rose52.js +4 -4
  57. package/dist/curves/star.cjs +8 -5
  58. package/dist/curves/star.d.cts +1 -1
  59. package/dist/curves/star.d.ts +1 -1
  60. package/dist/curves/star.js +7 -4
  61. package/dist/curves/star4.cjs +8 -5
  62. package/dist/curves/star4.d.cts +1 -1
  63. package/dist/curves/star4.d.ts +1 -1
  64. package/dist/curves/star4.js +7 -4
  65. package/dist/curves/star7.cjs +8 -5
  66. package/dist/curves/star7.d.cts +1 -1
  67. package/dist/curves/star7.d.ts +1 -1
  68. package/dist/curves/star7.js +7 -4
  69. package/dist/index.cjs +232 -172
  70. package/dist/index.cjs.map +1 -1
  71. package/dist/index.d.cts +106 -36
  72. package/dist/index.d.ts +106 -36
  73. package/dist/index.js +252 -172
  74. package/dist/index.js.map +1 -1
  75. package/dist/types-Z9i1_AQZ.d.cts +339 -0
  76. package/dist/types-Z9i1_AQZ.d.ts +339 -0
  77. package/package.json +1 -1
  78. package/dist/types-CknrlCAf.d.cts +0 -314
  79. package/dist/types-CknrlCAf.d.ts +0 -314
@@ -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 {
@@ -137,14 +137,14 @@ function createEngine(curveDef, trailLength = 120) {
137
137
  trail.clear();
138
138
  },
139
139
  jump(newT, { clearTrail = false } = {}) {
140
- t = (newT % curve.period + curve.period) % curve.period;
140
+ t = ((newT % curve.period) + curve.period) % curve.period;
141
141
  if (clearTrail) {
142
142
  trail.clear();
143
143
  }
144
144
  },
145
145
  seek(targetT, { wrap = false, step = curve.period / trailLength } = {}) {
146
146
  const advance = curve.speed * step;
147
- const target = (targetT % curve.period + curve.period) % curve.period;
147
+ const target = ((targetT % curve.period) + curve.period) % curve.period;
148
148
  const targetTime = target / curve.speed;
149
149
  t = target;
150
150
  actualTime = targetTime;
@@ -153,7 +153,7 @@ function createEngine(curveDef, trailLength = 120) {
153
153
  const count = wrap ? trailLength : Math.min(trailLength, pointsFromStart);
154
154
  for (let i = count - 1; i >= 0; i--) {
155
155
  const sampleT = target - i * advance;
156
- const wrappedT = (sampleT % curve.period + curve.period) % curve.period;
156
+ const wrappedT = ((sampleT % curve.period) + curve.period) % curve.period;
157
157
  const time = targetTime - i * step;
158
158
  const point = curve.fn(wrappedT, time, EMPTY_PARAMS);
159
159
  trail.push(point.x, point.y);
@@ -170,13 +170,16 @@ function createEngine(curveDef, trailLength = 120) {
170
170
  ...frozenB,
171
171
  fn: (sampleT, time, params) => {
172
172
  const a = frozenA.fn(sampleT, time, params);
173
- const tB = frozenStrategy === "normalized" ? sampleT / frozenA.period * frozenB.period : sampleT;
173
+ const tB =
174
+ frozenStrategy === "normalized"
175
+ ? (sampleT / frozenA.period) * frozenB.period
176
+ : sampleT;
174
177
  const b = frozenB.fn(tB, time, params);
175
178
  return {
176
179
  x: a.x + (b.x - a.x) * frozenAlpha,
177
- y: a.y + (b.y - a.y) * frozenAlpha
180
+ y: a.y + (b.y - a.y) * frozenAlpha,
178
181
  };
179
- }
182
+ },
180
183
  };
181
184
  }
182
185
  _morphStrategy = strategy;
@@ -189,7 +192,7 @@ function createEngine(curveDef, trailLength = 120) {
189
192
  completeMorph() {
190
193
  if (morphCurveB !== null) {
191
194
  if (_morphStrategy === "normalized" && curve.period !== morphCurveB.period) {
192
- t = t / curve.period * morphCurveB.period;
195
+ t = (t / curve.period) * morphCurveB.period;
193
196
  }
194
197
  curve = morphCurveB;
195
198
  }
@@ -198,25 +201,28 @@ function createEngine(curveDef, trailLength = 120) {
198
201
  },
199
202
  getSarmalSkeleton() {
200
203
  const steps = Math.ceil(curve.period * POINTS_PER_PERIOD_UNIT);
201
- const points = new Array(steps);
204
+ const points2 = new Array(steps);
202
205
  if (morphCurveB !== null && _morphAlpha !== null) {
203
206
  for (let i = 0; i < steps; i++) {
204
- const sampleT = i / (steps - 1) * curve.period;
207
+ const sampleT = (i / (steps - 1)) * curve.period;
205
208
  const a = sampleSkeleton(curve, sampleT);
206
- const tB = _morphStrategy === "normalized" ? sampleT / curve.period * morphCurveB.period : sampleT;
209
+ const tB =
210
+ _morphStrategy === "normalized"
211
+ ? (sampleT / curve.period) * morphCurveB.period
212
+ : sampleT;
207
213
  const b = sampleSkeleton(morphCurveB, tB);
208
- points[i] = {
214
+ points2[i] = {
209
215
  x: a.x + (b.x - a.x) * _morphAlpha,
210
- y: a.y + (b.y - a.y) * _morphAlpha
216
+ y: a.y + (b.y - a.y) * _morphAlpha,
211
217
  };
212
218
  }
213
- return points;
219
+ return points2;
214
220
  }
215
221
  for (let i = 0; i < steps; i++) {
216
- const sampleT = i / (steps - 1) * curve.period;
217
- points[i] = sampleSkeleton(curve, sampleT);
222
+ const sampleT = (i / (steps - 1)) * curve.period;
223
+ points2[i] = sampleSkeleton(curve, sampleT);
218
224
  }
219
- return points;
225
+ return points2;
220
226
  },
221
227
  setSpeed(speed) {
222
228
  if (!Number.isFinite(speed)) {
@@ -255,7 +261,7 @@ function createEngine(curveDef, trailLength = 120) {
255
261
  _speedTransition.reject(new Error("Speed transition cancelled"));
256
262
  _speedTransition = null;
257
263
  }
258
- }
264
+ },
259
265
  };
260
266
  }
261
267
 
@@ -294,7 +300,15 @@ function computeNormal(trail, i) {
294
300
  const tangent = computeTangent(trail, i);
295
301
  return { x: -tangent.y, y: tangent.x };
296
302
  }
297
- function computeTrailQuad(trail, i, trailCount, toX, toY, minWidth = TRAIL_MIN_WIDTH, maxWidth = TRAIL_MAX_WIDTH) {
303
+ function computeTrailQuad(
304
+ trail,
305
+ i,
306
+ trailCount,
307
+ toX,
308
+ toY,
309
+ minWidth = TRAIL_MIN_WIDTH,
310
+ maxWidth = TRAIL_MAX_WIDTH,
311
+ ) {
298
312
  const progress = i / (trailCount - 1);
299
313
  const nextProgress = (i + 1) / (trailCount - 1);
300
314
  const opacity = Math.pow(progress, TRAIL_FADE_CURVE) * TRAIL_MAX_OPACITY;
@@ -318,13 +332,16 @@ function computeTrailQuad(trail, i, trailCount, toX, toY, minWidth = TRAIL_MIN_W
318
332
  r1x: nx - n1.x * w1,
319
333
  r1y: ny - n1.y * w1,
320
334
  opacity,
321
- progress
335
+ progress,
322
336
  };
323
337
  }
324
- function computeBoundaries(pts, logicalWidth, logicalHeight) {
338
+ function computeBoundaries(pts, logicalWidth, logicalHeight, minPaddingPx = FIT_PADDING_MIN) {
325
339
  if (pts.length === 0) return null;
326
340
  const first = pts[0];
327
- let minX = first.x, maxX = first.x, minY = first.y, maxY = first.y;
341
+ let minX = first.x,
342
+ maxX = first.x,
343
+ minY = first.y,
344
+ maxY = first.y;
328
345
  for (const p of pts) {
329
346
  if (p.x < minX) {
330
347
  minX = p.x;
@@ -343,23 +360,23 @@ function computeBoundaries(pts, logicalWidth, logicalHeight) {
343
360
  const h = maxY - minY;
344
361
  if (w === 0 && h === 0) {
345
362
  throw new Error(
346
- "[sarmal] Degenerate curve: all skeleton points are identical. Check that your curve fn returns distinct points for different values of t."
363
+ "[sarmal] Degenerate curve: all skeleton points are identical. Check that your curve fn returns distinct points for different values of t.",
347
364
  );
348
365
  }
349
366
  const scaleXProportional = logicalWidth / (w * (1 + FIT_PADDING * 2));
350
367
  const scaleYProportional = logicalHeight / (h * (1 + FIT_PADDING * 2));
351
- const scaleXMinPadding = (logicalWidth - FIT_PADDING_MIN * 2) / w;
352
- const scaleYMinPadding = (logicalHeight - FIT_PADDING_MIN * 2) / h;
368
+ const scaleXMinPadding = (logicalWidth - minPaddingPx * 2) / w;
369
+ const scaleYMinPadding = (logicalHeight - minPaddingPx * 2) / h;
353
370
  const scale = Math.min(
354
371
  scaleXProportional,
355
372
  scaleYProportional,
356
373
  scaleXMinPadding,
357
- scaleYMinPadding
374
+ scaleYMinPadding,
358
375
  );
359
376
  return {
360
377
  scale,
361
378
  offsetX: (logicalWidth - w * scale) / 2 - minX * scale,
362
- offsetY: (logicalHeight - h * scale) / 2 - minY * scale
379
+ offsetY: (logicalHeight - h * scale) / 2 - minY * scale,
363
380
  };
364
381
  }
365
382
  function enginePassthroughs(engine) {
@@ -369,17 +386,18 @@ function enginePassthroughs(engine) {
369
386
  setSpeed: engine.setSpeed,
370
387
  getSpeed: engine.getSpeed,
371
388
  resetSpeed: engine.resetSpeed,
372
- setSpeedOver: engine.setSpeedOver
389
+ setSpeedOver: engine.setSpeedOver,
390
+ getSarmalSkeleton: engine.getSarmalSkeleton,
373
391
  };
374
392
  }
375
393
  function hexToRgb(hex) {
376
394
  const n = parseInt(hex.slice(1), 16);
377
- return { r: n >> 16, g: n >> 8 & 255, b: n & 255 };
395
+ return { r: n >> 16, g: (n >> 8) & 255, b: n & 255 };
378
396
  }
379
397
  var lerpRgb = (a, b, t) => ({
380
398
  r: Math.round(a.r + (b.r - a.r) * t),
381
399
  g: Math.round(a.g + (b.g - a.g) * t),
382
- b: Math.round(a.b + (b.b - a.b) * t)
400
+ b: Math.round(a.b + (b.b - a.b) * t),
383
401
  });
384
402
  function getPaletteColor(palette, position, timeOffset = 0) {
385
403
  if (palette.length === 0) {
@@ -388,7 +406,7 @@ function getPaletteColor(palette, position, timeOffset = 0) {
388
406
  if (palette.length === 1) {
389
407
  return hexToRgb(palette[0]);
390
408
  }
391
- const cyclePos = ((position + timeOffset) % 1 + 1) % 1;
409
+ const cyclePos = (((position + timeOffset) % 1) + 1) % 1;
392
410
  const scaled = cyclePos * palette.length;
393
411
  const idx = Math.floor(scaled);
394
412
  const t = scaled - idx;
@@ -403,7 +421,7 @@ var RENDER_OPTION_KEYS = /* @__PURE__ */ new Set([
403
421
  "headColor",
404
422
  "skeletonColor",
405
423
  "trailStyle",
406
- "headRadius"
424
+ "headRadius",
407
425
  ]);
408
426
  function validateRenderOptions(partial) {
409
427
  for (const key of Object.keys(partial)) {
@@ -431,7 +449,7 @@ function assertTrailColor(value) {
431
449
  if (typeof value === "string") {
432
450
  if (!HEX_COLOR_RE.test(value)) {
433
451
  throw new TypeError(
434
- `[sarmal] setRenderOptions: trailColor must be a 6-digit hex string, got "${value}"`
452
+ `[sarmal] setRenderOptions: trailColor must be a 6-digit hex string, got "${value}"`,
435
453
  );
436
454
  }
437
455
  return;
@@ -439,21 +457,21 @@ function assertTrailColor(value) {
439
457
  if (Array.isArray(value)) {
440
458
  if (value.length < 2) {
441
459
  throw new RangeError(
442
- `[sarmal] setRenderOptions: trailColor array must have at least 2 entries, got ${value.length}`
460
+ `[sarmal] setRenderOptions: trailColor array must have at least 2 entries, got ${value.length}`,
443
461
  );
444
462
  }
445
463
  for (let i = 0; i < value.length; i++) {
446
464
  const entry = value[i];
447
465
  if (typeof entry !== "string" || !HEX_COLOR_RE.test(entry)) {
448
466
  throw new TypeError(
449
- `[sarmal] setRenderOptions: trailColor[${i}] must be a 6-digit hex string, got ${JSON.stringify(entry)}`
467
+ `[sarmal] setRenderOptions: trailColor[${i}] must be a 6-digit hex string, got ${JSON.stringify(entry)}`,
450
468
  );
451
469
  }
452
470
  }
453
471
  return;
454
472
  }
455
473
  throw new TypeError(
456
- `[sarmal] setRenderOptions: trailColor must be a 6-digit hex string or an array of hex strings, got ${JSON.stringify(value)}`
474
+ `[sarmal] setRenderOptions: trailColor must be a 6-digit hex string or an array of hex strings, got ${JSON.stringify(value)}`,
457
475
  );
458
476
  }
459
477
  function assertHeadColor(value) {
@@ -462,7 +480,7 @@ function assertHeadColor(value) {
462
480
  }
463
481
  if (typeof value !== "string" || !HEX_COLOR_RE.test(value)) {
464
482
  throw new TypeError(
465
- `[sarmal] setRenderOptions: headColor must be a 6-digit hex string or null, got ${JSON.stringify(value)}`
483
+ `[sarmal] setRenderOptions: headColor must be a 6-digit hex string or null, got ${JSON.stringify(value)}`,
466
484
  );
467
485
  }
468
486
  }
@@ -472,26 +490,26 @@ function assertSkeletonColor(value) {
472
490
  }
473
491
  if (typeof value !== "string" || !HEX_COLOR_RE.test(value)) {
474
492
  throw new TypeError(
475
- `[sarmal] setRenderOptions: skeletonColor must be a 6-digit hex string or "transparent", got ${JSON.stringify(value)}`
493
+ `[sarmal] setRenderOptions: skeletonColor must be a 6-digit hex string or "transparent", got ${JSON.stringify(value)}`,
476
494
  );
477
495
  }
478
496
  }
479
497
  function assertTrailStyle(value) {
480
498
  if (!TRAIL_STYLES.includes(value)) {
481
499
  throw new RangeError(
482
- `[sarmal] setRenderOptions: trailStyle must be one of "default", "gradient-static", "gradient-animated", got ${JSON.stringify(value)}`
500
+ `[sarmal] setRenderOptions: trailStyle must be one of "default", "gradient-static", "gradient-animated", got ${JSON.stringify(value)}`,
483
501
  );
484
502
  }
485
503
  }
486
504
  function assertHeadRadius(value) {
487
505
  if (typeof value !== "number") {
488
506
  throw new TypeError(
489
- `[sarmal] setRenderOptions: headRadius must be a number, got ${JSON.stringify(value)}`
507
+ `[sarmal] setRenderOptions: headRadius must be a number, got ${JSON.stringify(value)}`,
490
508
  );
491
509
  }
492
510
  if (!Number.isFinite(value) || value <= 0) {
493
511
  throw new TypeError(
494
- `[sarmal] setRenderOptions: headRadius must be a finite positive number, got ${value}`
512
+ `[sarmal] setRenderOptions: headRadius must be a finite positive number, got ${value}`,
495
513
  );
496
514
  }
497
515
  }
@@ -513,13 +531,13 @@ function resolveHeadColor(trailColor, trailStyle) {
513
531
  function warnIfTrailColorMismatch(trailColor, trailStyle) {
514
532
  if (trailStyle === "default" && Array.isArray(trailColor)) {
515
533
  console.warn(
516
- '[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.'
534
+ '[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.',
517
535
  );
518
536
  return;
519
537
  }
520
538
  if (trailStyle !== "default" && typeof trailColor === "string") {
521
539
  console.warn(
522
- `[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.`
540
+ `[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.`,
523
541
  );
524
542
  }
525
543
  }
@@ -529,7 +547,7 @@ var getHeadDotRadius = (w, h) => Math.max(1, 3 * Math.sqrt(Math.min(w, h) / 160)
529
547
  var WHITE_HEX = "#ffffff";
530
548
  function hexToRgbComponents(hex) {
531
549
  const n = parseInt(hex.slice(1), 16);
532
- return `${n >> 16},${n >> 8 & 255},${n & 255}`;
550
+ return `${n >> 16},${(n >> 8) & 255},${n & 255}`;
533
551
  }
534
552
  function applyDprSizing(target, logicalWidth, logicalHeight, dpr) {
535
553
  target.style.width = `${logicalWidth}px`;
@@ -656,7 +674,7 @@ function createRenderer(options) {
656
674
  i,
657
675
  trailCount,
658
676
  toX,
659
- toY
677
+ toY,
660
678
  );
661
679
  if (trailStyle === "default") {
662
680
  ctx.fillStyle = `rgba(${trailSolidRgb},${opacity})`;
@@ -821,7 +839,7 @@ function createRenderer(options) {
821
839
  if (partial.trailColor !== void 0 || partial.trailStyle !== void 0) {
822
840
  warnIfTrailColorMismatch(trailColor, trailStyle);
823
841
  }
824
- }
842
+ },
825
843
  };
826
844
  const pauseOnHidden = options.pauseOnHidden !== false;
827
845
  function handleVisibilityChange() {
@@ -871,7 +889,7 @@ function sampleCurveSkeleton(curveDef) {
871
889
  const samples = Math.ceil(period * 50);
872
890
  const pts = Array.from({ length: samples });
873
891
  for (let i = 0; i < samples; i++) {
874
- const t = i / (samples - 1) * period;
892
+ const t = (i / (samples - 1)) * period;
875
893
  pts[i] = curveDef.skeletonFn ? curveDef.skeletonFn(t) : curveDef.fn(t, 0, EMPTY_PARAMS2);
876
894
  }
877
895
  return pts;
@@ -884,7 +902,7 @@ function createSVGRenderer(options) {
884
902
  const poolSize = engine.trailLength;
885
903
  if (poolSize > HIGH_TRAIL_LENGTH_THRESHOLD) {
886
904
  console.warn(
887
- `[sarmal] High trailLength in SVG renderer (${poolSize}). Consider using the canvas renderer for long trails.`
905
+ `[sarmal] High trailLength in SVG renderer (${poolSize}). Consider using the canvas renderer for long trails.`,
888
906
  );
889
907
  }
890
908
  let trailStyle = options.trailStyle ?? "default";
@@ -903,9 +921,9 @@ function createSVGRenderer(options) {
903
921
  return rect.width && rect.height ? Math.min(rect.width, rect.height) : 200;
904
922
  }
905
923
  const containerPx = getContainerPixelSize();
906
- const svgTrailMinWidth = TRAIL_MIN_WIDTH * viewSize / containerPx;
907
- const svgTrailMaxWidth = TRAIL_MAX_WIDTH * viewSize / containerPx;
908
- const svgSkeletonStrokeWidth = String(SKELETON_STROKE_WIDTH_PX * viewSize / containerPx);
924
+ const svgTrailMinWidth = (TRAIL_MIN_WIDTH * viewSize) / containerPx;
925
+ const svgTrailMaxWidth = (TRAIL_MAX_WIDTH * viewSize) / containerPx;
926
+ const svgSkeletonStrokeWidth = String((SKELETON_STROKE_WIDTH_PX * viewSize) / containerPx);
909
927
  headRadius = options.headRadius ?? SVG_DEFAULT_HEAD_RADIUS;
910
928
  container.setAttribute("viewBox", `0 0 ${viewSize} ${viewSize}`);
911
929
  container.setAttribute("role", "img");
@@ -993,7 +1011,7 @@ function createSVGRenderer(options) {
993
1011
  px,
994
1012
  py,
995
1013
  svgTrailMinWidth,
996
- svgTrailMaxWidth
1014
+ svgTrailMaxWidth,
997
1015
  );
998
1016
  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`;
999
1017
  trailPaths[i].setAttribute("d", d);
@@ -1021,7 +1039,8 @@ function createSVGRenderer(options) {
1021
1039
  let animationId = null;
1022
1040
  let lastTime = 0;
1023
1041
  let pausedByVisibility = false;
1024
- const prefersReducedMotion = typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
1042
+ const prefersReducedMotion =
1043
+ typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
1025
1044
  let morphResolve = null;
1026
1045
  let morphReject = null;
1027
1046
  let morphDurationMs = DEFAULT_MORPH_DURATION_MS;
@@ -1039,7 +1058,7 @@ function createSVGRenderer(options) {
1039
1058
  skeletonPathA.setAttribute("visibility", "visible");
1040
1059
  skeletonPathA.setAttribute(
1041
1060
  "stroke-opacity",
1042
- String((1 - morphAlpha) * DEFAULT_SKELETON_OPACITY)
1061
+ String((1 - morphAlpha) * DEFAULT_SKELETON_OPACITY),
1043
1062
  );
1044
1063
  }
1045
1064
  if (morphPathBBuilt) {
@@ -1193,7 +1212,7 @@ function createSVGRenderer(options) {
1193
1212
  if (partial.trailColor !== void 0 || partial.trailStyle !== void 0) {
1194
1213
  warnIfTrailColorMismatch(trailColor, trailStyle);
1195
1214
  }
1196
- }
1215
+ },
1197
1216
  };
1198
1217
  const pauseOnHidden = options.pauseOnHidden !== false;
1199
1218
  function handleVisibilityChange() {
@@ -1226,241 +1245,324 @@ function createSarmalSVG(container, curveDef, options) {
1226
1245
  return createSVGRenderer({ container, engine, ...rendererOpts });
1227
1246
  }
1228
1247
 
1229
- // src/curves/artemis2.ts
1230
- var TWO_PI2 = Math.PI * 2;
1231
- function artemis2Fn(t, _time, _params) {
1232
- const a = 0.35, b = 0.15, ox = 0.175;
1233
- const s = Math.sin(t), c = Math.cos(t);
1234
- const denom = 1 + s * s;
1248
+ // src/catmull-rom.ts
1249
+ var PERIOD = 2 * Math.PI;
1250
+ function catmullRom1D(p0, p1, p2, p3, u) {
1251
+ const u2 = u * u;
1252
+ const u3 = u2 * u;
1253
+ return (
1254
+ 0.5 *
1255
+ (2 * p1 +
1256
+ (-p0 + p2) * u +
1257
+ (2 * p0 - 5 * p1 + 4 * p2 - p3) * u2 +
1258
+ (-p0 + 3 * p1 - 3 * p2 + p3) * u3)
1259
+ );
1260
+ }
1261
+ function evaluateCatmullRom(points2, t) {
1262
+ const N = points2.length;
1263
+ if (N === 0) {
1264
+ return { x: 0, y: 0 };
1265
+ }
1266
+ if (N === 1) {
1267
+ return { x: points2[0][0], y: points2[0][1] };
1268
+ }
1269
+ t = ((t % PERIOD) + PERIOD) % PERIOD;
1270
+ const segmentSize = PERIOD / N;
1271
+ let i = Math.floor(t / segmentSize);
1272
+ if (i >= N) {
1273
+ i = N - 1;
1274
+ }
1275
+ let u = (t - i * segmentSize) / segmentSize;
1276
+ u = Math.max(0, Math.min(1, u));
1277
+ const p0 = points2[(i - 1 + N) % N];
1278
+ const p1 = points2[i];
1279
+ const p2 = points2[(i + 1) % N];
1280
+ const p3 = points2[(i + 2) % N];
1235
1281
  return {
1236
- x: c * (1 + a * c) / denom - ox,
1237
- y: s * c * (1 + b * c) / denom
1282
+ x: catmullRom1D(p0[0], p1[0], p2[0], p3[0], u),
1283
+ y: catmullRom1D(p0[1], p1[1], p2[1], p3[1], u),
1238
1284
  };
1239
1285
  }
1286
+ function drawCurve(points2, opts) {
1287
+ if (points2.length < 3) {
1288
+ throw new Error(`drawCurve requires at least 3 points, received ${points2.length}.`);
1289
+ }
1290
+ const first = points2[0];
1291
+ if (points2.every((p) => p[0] === first[0] && p[1] === first[1])) {
1292
+ console.warn(
1293
+ "[sarmal].drawCurve: all control points are identical. The curve will be a single point.",
1294
+ );
1295
+ }
1296
+ const maxAbs = points2.reduce((m, p) => Math.max(m, Math.abs(p[0]), Math.abs(p[1])), 0);
1297
+ if (maxAbs > 2) {
1298
+ console.warn(
1299
+ `[sarmal].drawCurve: control points extend to \xB1${maxAbs.toFixed(1)}, which may render off-screen. Coordinates should be in [-1, 1].`,
1300
+ );
1301
+ }
1302
+ const pts = points2.map(([x, y]) => [x, y]);
1303
+ return {
1304
+ name: opts?.name ?? "drawn",
1305
+ fn: (t) => evaluateCatmullRom(pts, t),
1306
+ period: PERIOD,
1307
+ };
1308
+ }
1309
+
1310
+ // src/curves/artemis2.ts
1311
+ var points = [
1312
+ [-0.44, -0.45],
1313
+ [-0.53, -0.77],
1314
+ [-0.82, -0.66],
1315
+ [-0.82, -0.18],
1316
+ [-0.25, -0.04],
1317
+ [0.16, -0.49],
1318
+ [-0.03, -0.87],
1319
+ [-0.68, -0.94],
1320
+ [-0.95, -0.61],
1321
+ [-0.87, -0],
1322
+ [-0.34, 0.21],
1323
+ [0.27, -0.04],
1324
+ [0.87, 0.06],
1325
+ [0.87, 0.57],
1326
+ [0.32, 0.66],
1327
+ [-0.21, -0.43],
1328
+ [-0.43, -0.81],
1329
+ [-0.69, -0.84],
1330
+ [-0.87, -0.66],
1331
+ [-0.9, -0.47],
1332
+ [-0.76, -0.35],
1333
+ ];
1240
1334
  var artemis2 = {
1241
- name: "Artemis II",
1242
- fn: artemis2Fn,
1243
- period: TWO_PI2,
1244
- speed: 0.7
1335
+ ...drawCurve(points, { name: "Artemis II" }),
1336
+ speed: 0.7,
1245
1337
  };
1246
1338
 
1247
1339
  // src/curves/astroid.ts
1248
- var TWO_PI3 = Math.PI * 2;
1340
+ var TWO_PI2 = Math.PI * 2;
1249
1341
  function astroidFn(t, _time, _params) {
1250
1342
  const c = Math.cos(t);
1251
1343
  const s = Math.sin(t);
1252
1344
  return {
1253
1345
  x: c * c * c,
1254
- y: s * s * s
1346
+ y: s * s * s,
1255
1347
  };
1256
1348
  }
1257
1349
  var astroid = {
1258
1350
  name: "Astroid",
1259
1351
  fn: astroidFn,
1260
- period: TWO_PI3,
1261
- speed: 1.1
1352
+ period: TWO_PI2,
1353
+ speed: 1.1,
1262
1354
  };
1263
1355
 
1264
1356
  // src/curves/deltoid.ts
1265
- var TWO_PI4 = Math.PI * 2;
1357
+ var TWO_PI3 = Math.PI * 2;
1266
1358
  function deltoidFn(t, _time, _params) {
1267
1359
  return {
1268
1360
  x: 2 * Math.cos(t) + Math.cos(2 * t),
1269
- y: 2 * Math.sin(t) - Math.sin(2 * t)
1361
+ y: 2 * Math.sin(t) - Math.sin(2 * t),
1270
1362
  };
1271
1363
  }
1272
1364
  var deltoid = {
1273
1365
  name: "Deltoid",
1274
1366
  fn: deltoidFn,
1275
- period: TWO_PI4,
1276
- speed: 0.9
1367
+ period: TWO_PI3,
1368
+ speed: 0.9,
1277
1369
  };
1278
1370
 
1279
1371
  // src/curves/epicycloid3.ts
1280
- var TWO_PI5 = Math.PI * 2;
1372
+ var TWO_PI4 = Math.PI * 2;
1281
1373
  function epicycloid3Fn(t, _time, _params) {
1282
1374
  return {
1283
1375
  x: 4 * Math.cos(t) - Math.cos(4 * t),
1284
- y: 4 * Math.sin(t) - Math.sin(4 * t)
1376
+ y: 4 * Math.sin(t) - Math.sin(4 * t),
1285
1377
  };
1286
1378
  }
1287
1379
  var epicycloid3 = {
1288
1380
  name: "Epicycloid (n=3)",
1289
1381
  fn: epicycloid3Fn,
1290
- period: TWO_PI5,
1291
- speed: 0.75
1382
+ period: TWO_PI4,
1383
+ speed: 0.75,
1292
1384
  };
1293
1385
 
1294
1386
  // src/curves/epitrochoid7.ts
1295
- var TWO_PI6 = Math.PI * 2;
1387
+ var TWO_PI5 = Math.PI * 2;
1296
1388
  function epitrochoid7Fn(t, _time, _params) {
1297
1389
  const d = 1 + 0.55 * Math.sin(t * 0.5);
1298
1390
  return {
1299
1391
  x: 7 * Math.cos(t) - d * Math.cos(7 * t),
1300
- y: 7 * Math.sin(t) - d * Math.sin(7 * t)
1392
+ y: 7 * Math.sin(t) - d * Math.sin(7 * t),
1301
1393
  };
1302
1394
  }
1303
1395
  function epitrochoid7SkeletonFn(t) {
1304
1396
  const d = 1.275;
1305
1397
  return {
1306
1398
  x: 7 * Math.cos(t) - d * Math.cos(7 * t),
1307
- y: 7 * Math.sin(t) - d * Math.sin(7 * t)
1399
+ y: 7 * Math.sin(t) - d * Math.sin(7 * t),
1308
1400
  };
1309
1401
  }
1310
1402
  var epitrochoid7 = {
1311
1403
  name: "Epitrochoid",
1312
1404
  fn: epitrochoid7Fn,
1313
- period: TWO_PI6,
1405
+ period: TWO_PI5,
1314
1406
  speed: 1.4,
1315
- skeletonFn: epitrochoid7SkeletonFn
1407
+ skeletonFn: epitrochoid7SkeletonFn,
1316
1408
  };
1317
1409
 
1318
1410
  // src/curves/lissajous32.ts
1319
- var TWO_PI7 = Math.PI * 2;
1411
+ var TWO_PI6 = Math.PI * 2;
1320
1412
  function lissajous32Fn(t, time, _params) {
1321
1413
  const phi = time * 0.45;
1322
1414
  return {
1323
1415
  x: Math.sin(3 * t + phi),
1324
- y: Math.sin(2 * t)
1416
+ y: Math.sin(2 * t),
1325
1417
  };
1326
1418
  }
1327
1419
  var lissajous32 = {
1328
1420
  name: "Lissajous 3:2",
1329
1421
  fn: lissajous32Fn,
1330
- period: TWO_PI7,
1422
+ period: TWO_PI6,
1331
1423
  speed: 2,
1332
- skeleton: "live"
1424
+ skeleton: "live",
1333
1425
  };
1334
1426
 
1335
1427
  // src/curves/lissajous43.ts
1336
- var TWO_PI8 = Math.PI * 2;
1428
+ var TWO_PI7 = Math.PI * 2;
1337
1429
  function lissajous43Fn(t, time, _params) {
1338
1430
  const phi = time * 0.38;
1339
1431
  return {
1340
1432
  x: Math.sin(4 * t + phi),
1341
- y: Math.sin(3 * t)
1433
+ y: Math.sin(3 * t),
1342
1434
  };
1343
1435
  }
1344
1436
  var lissajous43 = {
1345
1437
  name: "Lissajous 4:3",
1346
1438
  fn: lissajous43Fn,
1347
- period: TWO_PI8,
1439
+ period: TWO_PI7,
1348
1440
  speed: 1.8,
1349
- skeleton: "live"
1441
+ skeleton: "live",
1350
1442
  };
1351
1443
 
1352
1444
  // src/curves/lame.ts
1353
- var TWO_PI9 = Math.PI * 2;
1445
+ var TWO_PI8 = Math.PI * 2;
1354
1446
  function lameFn(t, time, _params) {
1355
1447
  const p = 1.75 + 1.25 * Math.sin(time * 0.48);
1356
- const c = Math.cos(t), s = Math.sin(t);
1448
+ const c = Math.cos(t),
1449
+ s = Math.sin(t);
1357
1450
  return {
1358
1451
  x: Math.sign(c) * Math.pow(Math.abs(c), p),
1359
- y: Math.sign(s) * Math.pow(Math.abs(s), p)
1452
+ y: Math.sign(s) * Math.pow(Math.abs(s), p),
1360
1453
  };
1361
1454
  }
1362
1455
  var lame = {
1363
1456
  name: "Lam\xE9 Curve",
1364
1457
  fn: lameFn,
1365
- period: TWO_PI9,
1458
+ period: TWO_PI8,
1366
1459
  speed: 1,
1367
- skeleton: "live"
1460
+ skeleton: "live",
1368
1461
  };
1369
1462
 
1370
1463
  // src/curves/rose3.ts
1371
- var TWO_PI10 = Math.PI * 2;
1464
+ var TWO_PI9 = Math.PI * 2;
1372
1465
  function rose3Fn(t, _time, _params) {
1373
1466
  const r = Math.cos(3 * t);
1374
1467
  return {
1375
1468
  x: r * Math.cos(t),
1376
- y: r * Math.sin(t)
1469
+ y: r * Math.sin(t),
1377
1470
  };
1378
1471
  }
1379
1472
  var rose3 = {
1380
1473
  name: "Rose (n=3)",
1381
1474
  fn: rose3Fn,
1382
- period: TWO_PI10,
1383
- speed: 1.15
1475
+ period: TWO_PI9,
1476
+ speed: 1.15,
1384
1477
  };
1385
1478
 
1386
1479
  // src/curves/rose5.ts
1387
- var TWO_PI11 = Math.PI * 2;
1480
+ var TWO_PI10 = Math.PI * 2;
1388
1481
  function rose5Fn(t, _time, _params) {
1389
1482
  const r = Math.cos(5 * t);
1390
1483
  return {
1391
1484
  x: r * Math.cos(t),
1392
- y: r * Math.sin(t)
1485
+ y: r * Math.sin(t),
1393
1486
  };
1394
1487
  }
1395
1488
  var rose5 = {
1396
1489
  name: "Rose (n=5)",
1397
1490
  fn: rose5Fn,
1398
- period: TWO_PI11,
1399
- speed: 1
1491
+ period: TWO_PI10,
1492
+ speed: 1,
1400
1493
  };
1401
1494
 
1402
1495
  // src/curves/rose52.ts
1403
1496
  var FOUR_PI = Math.PI * 4;
1404
1497
  function rose52Fn(t, _time, _params) {
1405
- const r = Math.cos(5 / 2 * t);
1498
+ const r = Math.cos((5 / 2) * t);
1406
1499
  return {
1407
1500
  x: r * Math.cos(t),
1408
- y: r * Math.sin(t)
1501
+ y: r * Math.sin(t),
1409
1502
  };
1410
1503
  }
1411
1504
  var rose52 = {
1412
1505
  name: "Rose (n=5/2)",
1413
1506
  fn: rose52Fn,
1414
1507
  period: FOUR_PI,
1415
- speed: 0.8
1508
+ speed: 0.8,
1416
1509
  };
1417
1510
 
1418
1511
  // src/curves/star.ts
1419
- var TWO_PI12 = Math.PI * 2;
1512
+ var TWO_PI11 = Math.PI * 2;
1420
1513
  function starFn(t, _time, _params) {
1421
- const r = Math.abs(Math.cos(5 / 2 * t)) + 0.35 * Math.abs(Math.cos(15 / 2 * t)) + 0.15 * Math.abs(Math.cos(25 / 2 * t));
1514
+ const r =
1515
+ Math.abs(Math.cos((5 / 2) * t)) +
1516
+ 0.35 * Math.abs(Math.cos((15 / 2) * t)) +
1517
+ 0.15 * Math.abs(Math.cos((25 / 2) * t));
1422
1518
  return {
1423
1519
  x: r * Math.cos(t),
1424
- y: r * Math.sin(t)
1520
+ y: r * Math.sin(t),
1425
1521
  };
1426
1522
  }
1427
1523
  var star = {
1428
1524
  name: "Star",
1429
1525
  fn: starFn,
1430
- period: TWO_PI12,
1431
- speed: 1
1526
+ period: TWO_PI11,
1527
+ speed: 1,
1432
1528
  };
1433
1529
 
1434
1530
  // src/curves/star4.ts
1435
- var TWO_PI13 = Math.PI * 2;
1531
+ var TWO_PI12 = Math.PI * 2;
1436
1532
  function star4Fn(t, _time, _params) {
1437
- const r = Math.abs(Math.cos(2 * t)) + 0.35 * Math.abs(Math.cos(6 * t)) + 0.15 * Math.abs(Math.cos(10 * t));
1533
+ const r =
1534
+ Math.abs(Math.cos(2 * t)) +
1535
+ 0.35 * Math.abs(Math.cos(6 * t)) +
1536
+ 0.15 * Math.abs(Math.cos(10 * t));
1438
1537
  return {
1439
1538
  x: r * Math.cos(t),
1440
- y: r * Math.sin(t)
1539
+ y: r * Math.sin(t),
1441
1540
  };
1442
1541
  }
1443
1542
  var star4 = {
1444
1543
  name: "Star (4-arm)",
1445
1544
  fn: star4Fn,
1446
- period: TWO_PI13,
1447
- speed: 1
1545
+ period: TWO_PI12,
1546
+ speed: 1,
1448
1547
  };
1449
1548
 
1450
1549
  // src/curves/star7.ts
1451
- var TWO_PI14 = Math.PI * 2;
1550
+ var TWO_PI13 = Math.PI * 2;
1452
1551
  function star7Fn(t, _time, _params) {
1453
- const r = Math.abs(Math.cos(7 / 2 * t)) + 0.35 * Math.abs(Math.cos(21 / 2 * t)) + 0.15 * Math.abs(Math.cos(35 / 2 * t));
1552
+ const r =
1553
+ Math.abs(Math.cos((7 / 2) * t)) +
1554
+ 0.35 * Math.abs(Math.cos((21 / 2) * t)) +
1555
+ 0.15 * Math.abs(Math.cos((35 / 2) * t));
1454
1556
  return {
1455
1557
  x: r * Math.cos(t),
1456
- y: r * Math.sin(t)
1558
+ y: r * Math.sin(t),
1457
1559
  };
1458
1560
  }
1459
1561
  var star7 = {
1460
1562
  name: "Star (7-arm)",
1461
1563
  fn: star7Fn,
1462
- period: TWO_PI14,
1463
- speed: 1
1564
+ period: TWO_PI13,
1565
+ speed: 1,
1464
1566
  };
1465
1567
 
1466
1568
  // src/curves/index.ts
@@ -1478,7 +1580,7 @@ var curves = {
1478
1580
  lissajous32,
1479
1581
  lissajous43,
1480
1582
  epicycloid3,
1481
- lame
1583
+ lame,
1482
1584
  };
1483
1585
 
1484
1586
  // src/index.ts
@@ -1495,22 +1597,21 @@ function parseTrailColor(value) {
1495
1597
  if (Array.isArray(parsed)) {
1496
1598
  return parsed;
1497
1599
  }
1498
- } catch {
1499
- }
1600
+ } catch {}
1500
1601
  return value;
1501
1602
  }
1502
1603
  function buildOptions(el2) {
1503
1604
  return {
1504
- ...el2.dataset.trailColor && {
1505
- trailColor: parseTrailColor(el2.dataset.trailColor)
1506
- },
1507
- ...el2.dataset.skeletonColor && { skeletonColor: el2.dataset.skeletonColor },
1508
- ...el2.dataset.headColor && { headColor: el2.dataset.headColor },
1509
- ...el2.dataset.headRadius && { headRadius: parseFloat(el2.dataset.headRadius) },
1510
- ...el2.dataset.trailLength && { trailLength: parseInt(el2.dataset.trailLength, 10) },
1511
- ...el2.dataset.trailStyle && {
1512
- trailStyle: el2.dataset.trailStyle
1513
- }
1605
+ ...(el2.dataset.trailColor && {
1606
+ trailColor: parseTrailColor(el2.dataset.trailColor),
1607
+ }),
1608
+ ...(el2.dataset.skeletonColor && { skeletonColor: el2.dataset.skeletonColor }),
1609
+ ...(el2.dataset.headColor && { headColor: el2.dataset.headColor }),
1610
+ ...(el2.dataset.headRadius && { headRadius: parseFloat(el2.dataset.headRadius) }),
1611
+ ...(el2.dataset.trailLength && { trailLength: parseInt(el2.dataset.trailLength, 10) }),
1612
+ ...(el2.dataset.trailStyle && {
1613
+ trailStyle: el2.dataset.trailStyle,
1614
+ }),
1514
1615
  };
1515
1616
  }
1516
1617
  function init() {
@@ -1525,7 +1626,10 @@ function init() {
1525
1626
  return console.error(`[sarmal] "${curveName}" is not a valid curve name`);
1526
1627
  }
1527
1628
  const options = buildOptions(el2);
1528
- const instance = el2 instanceof HTMLCanvasElement ? createSarmal(el2, curveDef, options) : createSarmalSVG(el2, curveDef, options);
1629
+ const instance =
1630
+ el2 instanceof HTMLCanvasElement
1631
+ ? createSarmal(el2, curveDef, options)
1632
+ : createSarmalSVG(el2, curveDef, options);
1529
1633
  if (el2.dataset.speed) {
1530
1634
  instance.setSpeed(parseFloat(el2.dataset.speed));
1531
1635
  }
@@ -1541,4 +1645,4 @@ if (document.readyState === "loading") {
1541
1645
 
1542
1646
  exports.init = init;
1543
1647
  //# sourceMappingURL=auto-init.cjs.map
1544
- //# sourceMappingURL=auto-init.cjs.map
1648
+ //# sourceMappingURL=auto-init.cjs.map