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