@sarmal/core 0.23.0 → 0.25.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 (79) hide show
  1. package/dist/auto-init.cjs +255 -175
  2. package/dist/auto-init.cjs.map +1 -1
  3. package/dist/auto-init.js +254 -174
  4. package/dist/auto-init.js.map +1 -1
  5. package/dist/curves/artemis2.cjs +82 -17
  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 +81 -16
  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 +139 -84
  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 +139 -100
  32. package/dist/curves/index.js.map +1 -1
  33. package/dist/curves/lame.cjs +5 -6
  34. package/dist/curves/lame.d.cts +1 -1
  35. package/dist/curves/lame.d.ts +1 -1
  36. package/dist/curves/lame.js +4 -5
  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 +5 -8
  58. package/dist/curves/star.d.cts +1 -1
  59. package/dist/curves/star.d.ts +1 -1
  60. package/dist/curves/star.js +4 -7
  61. package/dist/curves/star4.cjs +5 -8
  62. package/dist/curves/star4.d.cts +1 -1
  63. package/dist/curves/star4.d.ts +1 -1
  64. package/dist/curves/star4.js +4 -7
  65. package/dist/curves/star7.cjs +5 -8
  66. package/dist/curves/star7.d.cts +1 -1
  67. package/dist/curves/star7.d.ts +1 -1
  68. package/dist/curves/star7.js +4 -7
  69. package/dist/index.cjs +244 -210
  70. package/dist/index.cjs.map +1 -1
  71. package/dist/index.d.cts +55 -78
  72. package/dist/index.d.ts +55 -78
  73. package/dist/index.js +243 -230
  74. package/dist/index.js.map +1 -1
  75. package/dist/types-BZpzgNau.d.cts +332 -0
  76. package/dist/types-BZpzgNau.d.ts +332 -0
  77. package/package.json +1 -1
  78. package/dist/types-C0b4MPtI.d.cts +0 -321
  79. package/dist/types-C0b4MPtI.d.ts +0 -321
package/dist/index.js CHANGED
@@ -61,13 +61,13 @@ function resolveCurve(curveDef) {
61
61
  period,
62
62
  speed,
63
63
  skeleton: curveDef.skeleton,
64
- skeletonFn: curveDef.skeletonFn,
64
+ skeletonFn: curveDef.skeletonFn
65
65
  };
66
66
  }
67
67
  function createEngine(curveDef, trailLength = 120) {
68
68
  if (!Number.isFinite(trailLength) || trailLength <= 0) {
69
69
  throw new RangeError(
70
- `[sarmal] trailLength must be a positive finite number, got ${trailLength}`,
70
+ `[sarmal] trailLength must be a positive finite number, got ${trailLength}`
71
71
  );
72
72
  }
73
73
  let curve = resolveCurve(curveDef);
@@ -108,7 +108,7 @@ function createEngine(curveDef, trailLength = 120) {
108
108
  actualTime += deltaTime;
109
109
  if (morphCurveB !== null && _morphAlpha !== null) {
110
110
  const a = curve.fn(t, actualTime, EMPTY_PARAMS);
111
- const tB = _morphStrategy === "normalized" ? (t / curve.period) * morphCurveB.period : t;
111
+ const tB = _morphStrategy === "normalized" ? t / curve.period * morphCurveB.period : t;
112
112
  const b = morphCurveB.fn(tB, actualTime, EMPTY_PARAMS);
113
113
  trail.push(a.x + (b.x - a.x) * _morphAlpha, a.y + (b.y - a.y) * _morphAlpha);
114
114
  } else {
@@ -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,16 +168,13 @@ 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 =
172
- frozenStrategy === "normalized"
173
- ? (sampleT / frozenA.period) * frozenB.period
174
- : sampleT;
171
+ const tB = frozenStrategy === "normalized" ? sampleT / frozenA.period * frozenB.period : sampleT;
175
172
  const b = frozenB.fn(tB, time, params);
176
173
  return {
177
174
  x: a.x + (b.x - a.x) * frozenAlpha,
178
- y: a.y + (b.y - a.y) * frozenAlpha,
175
+ y: a.y + (b.y - a.y) * frozenAlpha
179
176
  };
180
- },
177
+ }
181
178
  };
182
179
  }
183
180
  _morphStrategy = strategy;
@@ -190,7 +187,7 @@ function createEngine(curveDef, trailLength = 120) {
190
187
  completeMorph() {
191
188
  if (morphCurveB !== null) {
192
189
  if (_morphStrategy === "normalized" && curve.period !== morphCurveB.period) {
193
- t = (t / curve.period) * morphCurveB.period;
190
+ t = t / curve.period * morphCurveB.period;
194
191
  }
195
192
  curve = morphCurveB;
196
193
  }
@@ -199,28 +196,25 @@ function createEngine(curveDef, trailLength = 120) {
199
196
  },
200
197
  getSarmalSkeleton() {
201
198
  const steps = Math.ceil(curve.period * POINTS_PER_PERIOD_UNIT);
202
- const points = new Array(steps);
199
+ const points2 = new Array(steps);
203
200
  if (morphCurveB !== null && _morphAlpha !== null) {
204
201
  for (let i = 0; i < steps; i++) {
205
- const sampleT = (i / (steps - 1)) * curve.period;
202
+ const sampleT = i / (steps - 1) * curve.period;
206
203
  const a = sampleSkeleton(curve, sampleT);
207
- const tB =
208
- _morphStrategy === "normalized"
209
- ? (sampleT / curve.period) * morphCurveB.period
210
- : sampleT;
204
+ const tB = _morphStrategy === "normalized" ? sampleT / curve.period * morphCurveB.period : sampleT;
211
205
  const b = sampleSkeleton(morphCurveB, tB);
212
- points[i] = {
206
+ points2[i] = {
213
207
  x: a.x + (b.x - a.x) * _morphAlpha,
214
- y: a.y + (b.y - a.y) * _morphAlpha,
208
+ y: a.y + (b.y - a.y) * _morphAlpha
215
209
  };
216
210
  }
217
- return points;
211
+ return points2;
218
212
  }
219
213
  for (let i = 0; i < steps; i++) {
220
- const sampleT = (i / (steps - 1)) * curve.period;
221
- points[i] = sampleSkeleton(curve, sampleT);
214
+ const sampleT = i / (steps - 1) * curve.period;
215
+ points2[i] = sampleSkeleton(curve, sampleT);
222
216
  }
223
- return points;
217
+ return points2;
224
218
  },
225
219
  setSpeed(speed) {
226
220
  if (!Number.isFinite(speed)) {
@@ -259,7 +253,7 @@ function createEngine(curveDef, trailLength = 120) {
259
253
  _speedTransition.reject(new Error("Speed transition cancelled"));
260
254
  _speedTransition = null;
261
255
  }
262
- },
256
+ }
263
257
  };
264
258
  }
265
259
 
@@ -298,15 +292,7 @@ function computeNormal(trail, i) {
298
292
  const tangent = computeTangent(trail, i);
299
293
  return { x: -tangent.y, y: tangent.x };
300
294
  }
301
- function computeTrailQuad(
302
- trail,
303
- i,
304
- trailCount,
305
- toX,
306
- toY,
307
- minWidth = TRAIL_MIN_WIDTH,
308
- maxWidth = TRAIL_MAX_WIDTH,
309
- ) {
295
+ function computeTrailQuad(trail, i, trailCount, toX, toY, minWidth = TRAIL_MIN_WIDTH, maxWidth = TRAIL_MAX_WIDTH) {
310
296
  const progress = i / (trailCount - 1);
311
297
  const nextProgress = (i + 1) / (trailCount - 1);
312
298
  const opacity = Math.pow(progress, TRAIL_FADE_CURVE) * TRAIL_MAX_OPACITY;
@@ -330,16 +316,13 @@ function computeTrailQuad(
330
316
  r1x: nx - n1.x * w1,
331
317
  r1y: ny - n1.y * w1,
332
318
  opacity,
333
- progress,
319
+ progress
334
320
  };
335
321
  }
336
- function computeBoundaries(pts, logicalWidth, logicalHeight) {
322
+ function computeBoundaries(pts, logicalWidth, logicalHeight, minPaddingPx = FIT_PADDING_MIN) {
337
323
  if (pts.length === 0) return null;
338
324
  const first = pts[0];
339
- let minX = first.x,
340
- maxX = first.x,
341
- minY = first.y,
342
- maxY = first.y;
325
+ let minX = first.x, maxX = first.x, minY = first.y, maxY = first.y;
343
326
  for (const p of pts) {
344
327
  if (p.x < minX) {
345
328
  minX = p.x;
@@ -358,23 +341,23 @@ function computeBoundaries(pts, logicalWidth, logicalHeight) {
358
341
  const h = maxY - minY;
359
342
  if (w === 0 && h === 0) {
360
343
  throw new Error(
361
- "[sarmal] Degenerate curve: all skeleton points are identical. Check that your curve fn returns distinct points for different values of t.",
344
+ "[sarmal] Degenerate curve: all skeleton points are identical. Check that your curve fn returns distinct points for different values of t."
362
345
  );
363
346
  }
364
347
  const scaleXProportional = logicalWidth / (w * (1 + FIT_PADDING * 2));
365
348
  const scaleYProportional = logicalHeight / (h * (1 + FIT_PADDING * 2));
366
- const scaleXMinPadding = (logicalWidth - FIT_PADDING_MIN * 2) / w;
367
- const scaleYMinPadding = (logicalHeight - FIT_PADDING_MIN * 2) / h;
349
+ const scaleXMinPadding = (logicalWidth - minPaddingPx * 2) / w;
350
+ const scaleYMinPadding = (logicalHeight - minPaddingPx * 2) / h;
368
351
  const scale = Math.min(
369
352
  scaleXProportional,
370
353
  scaleYProportional,
371
354
  scaleXMinPadding,
372
- scaleYMinPadding,
355
+ scaleYMinPadding
373
356
  );
374
357
  return {
375
358
  scale,
376
359
  offsetX: (logicalWidth - w * scale) / 2 - minX * scale,
377
- offsetY: (logicalHeight - h * scale) / 2 - minY * scale,
360
+ offsetY: (logicalHeight - h * scale) / 2 - minY * scale
378
361
  };
379
362
  }
380
363
  function enginePassthroughs(engine) {
@@ -385,6 +368,7 @@ function enginePassthroughs(engine) {
385
368
  getSpeed: engine.getSpeed,
386
369
  resetSpeed: engine.resetSpeed,
387
370
  setSpeedOver: engine.setSpeedOver,
371
+ getSarmalSkeleton: engine.getSarmalSkeleton
388
372
  };
389
373
  }
390
374
  var palettes = {
@@ -393,16 +377,16 @@ var palettes = {
393
377
  ocean: ["#1e3a8a", "#06b6d4", "#22d3ee", "#e0f2fe"],
394
378
  ice: ["#1e3a8a", "#67e8f9"],
395
379
  fire: ["#7f1d1d", "#fbbf24"],
396
- forest: ["#14532d", "#86efac"],
380
+ forest: ["#14532d", "#86efac"]
397
381
  };
398
382
  function hexToRgb(hex) {
399
383
  const n = parseInt(hex.slice(1), 16);
400
- return { r: n >> 16, g: (n >> 8) & 255, b: n & 255 };
384
+ return { r: n >> 16, g: n >> 8 & 255, b: n & 255 };
401
385
  }
402
386
  var lerpRgb = (a, b, t) => ({
403
387
  r: Math.round(a.r + (b.r - a.r) * t),
404
388
  g: Math.round(a.g + (b.g - a.g) * t),
405
- b: Math.round(a.b + (b.b - a.b) * t),
389
+ b: Math.round(a.b + (b.b - a.b) * t)
406
390
  });
407
391
  function getPaletteColor(palette, position, timeOffset = 0) {
408
392
  if (palette.length === 0) {
@@ -411,7 +395,7 @@ function getPaletteColor(palette, position, timeOffset = 0) {
411
395
  if (palette.length === 1) {
412
396
  return hexToRgb(palette[0]);
413
397
  }
414
- const cyclePos = (((position + timeOffset) % 1) + 1) % 1;
398
+ const cyclePos = ((position + timeOffset) % 1 + 1) % 1;
415
399
  const scaled = cyclePos * palette.length;
416
400
  const idx = Math.floor(scaled);
417
401
  const t = scaled - idx;
@@ -426,7 +410,7 @@ var RENDER_OPTION_KEYS = /* @__PURE__ */ new Set([
426
410
  "headColor",
427
411
  "skeletonColor",
428
412
  "trailStyle",
429
- "headRadius",
413
+ "headRadius"
430
414
  ]);
431
415
  function validateRenderOptions(partial) {
432
416
  for (const key of Object.keys(partial)) {
@@ -454,7 +438,7 @@ function assertTrailColor(value) {
454
438
  if (typeof value === "string") {
455
439
  if (!HEX_COLOR_RE.test(value)) {
456
440
  throw new TypeError(
457
- `[sarmal] setRenderOptions: trailColor must be a 6-digit hex string, got "${value}"`,
441
+ `[sarmal] setRenderOptions: trailColor must be a 6-digit hex string, got "${value}"`
458
442
  );
459
443
  }
460
444
  return;
@@ -462,21 +446,21 @@ function assertTrailColor(value) {
462
446
  if (Array.isArray(value)) {
463
447
  if (value.length < 2) {
464
448
  throw new RangeError(
465
- `[sarmal] setRenderOptions: trailColor array must have at least 2 entries, got ${value.length}`,
449
+ `[sarmal] setRenderOptions: trailColor array must have at least 2 entries, got ${value.length}`
466
450
  );
467
451
  }
468
452
  for (let i = 0; i < value.length; i++) {
469
453
  const entry = value[i];
470
454
  if (typeof entry !== "string" || !HEX_COLOR_RE.test(entry)) {
471
455
  throw new TypeError(
472
- `[sarmal] setRenderOptions: trailColor[${i}] must be a 6-digit hex string, got ${JSON.stringify(entry)}`,
456
+ `[sarmal] setRenderOptions: trailColor[${i}] must be a 6-digit hex string, got ${JSON.stringify(entry)}`
473
457
  );
474
458
  }
475
459
  }
476
460
  return;
477
461
  }
478
462
  throw new TypeError(
479
- `[sarmal] setRenderOptions: trailColor must be a 6-digit hex string or an array of hex strings, got ${JSON.stringify(value)}`,
463
+ `[sarmal] setRenderOptions: trailColor must be a 6-digit hex string or an array of hex strings, got ${JSON.stringify(value)}`
480
464
  );
481
465
  }
482
466
  function assertHeadColor(value) {
@@ -485,7 +469,7 @@ function assertHeadColor(value) {
485
469
  }
486
470
  if (typeof value !== "string" || !HEX_COLOR_RE.test(value)) {
487
471
  throw new TypeError(
488
- `[sarmal] setRenderOptions: headColor must be a 6-digit hex string or null, got ${JSON.stringify(value)}`,
472
+ `[sarmal] setRenderOptions: headColor must be a 6-digit hex string or null, got ${JSON.stringify(value)}`
489
473
  );
490
474
  }
491
475
  }
@@ -495,26 +479,26 @@ function assertSkeletonColor(value) {
495
479
  }
496
480
  if (typeof value !== "string" || !HEX_COLOR_RE.test(value)) {
497
481
  throw new TypeError(
498
- `[sarmal] setRenderOptions: skeletonColor must be a 6-digit hex string or "transparent", got ${JSON.stringify(value)}`,
482
+ `[sarmal] setRenderOptions: skeletonColor must be a 6-digit hex string or "transparent", got ${JSON.stringify(value)}`
499
483
  );
500
484
  }
501
485
  }
502
486
  function assertTrailStyle(value) {
503
487
  if (!TRAIL_STYLES.includes(value)) {
504
488
  throw new RangeError(
505
- `[sarmal] setRenderOptions: trailStyle must be one of "default", "gradient-static", "gradient-animated", got ${JSON.stringify(value)}`,
489
+ `[sarmal] setRenderOptions: trailStyle must be one of "default", "gradient-static", "gradient-animated", got ${JSON.stringify(value)}`
506
490
  );
507
491
  }
508
492
  }
509
493
  function assertHeadRadius(value) {
510
494
  if (typeof value !== "number") {
511
495
  throw new TypeError(
512
- `[sarmal] setRenderOptions: headRadius must be a number, got ${JSON.stringify(value)}`,
496
+ `[sarmal] setRenderOptions: headRadius must be a number, got ${JSON.stringify(value)}`
513
497
  );
514
498
  }
515
499
  if (!Number.isFinite(value) || value <= 0) {
516
500
  throw new TypeError(
517
- `[sarmal] setRenderOptions: headRadius must be a finite positive number, got ${value}`,
501
+ `[sarmal] setRenderOptions: headRadius must be a finite positive number, got ${value}`
518
502
  );
519
503
  }
520
504
  }
@@ -536,13 +520,13 @@ function resolveHeadColor(trailColor, trailStyle) {
536
520
  function warnIfTrailColorMismatch(trailColor, trailStyle) {
537
521
  if (trailStyle === "default" && Array.isArray(trailColor)) {
538
522
  console.warn(
539
- '[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.',
523
+ '[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.'
540
524
  );
541
525
  return;
542
526
  }
543
527
  if (trailStyle !== "default" && typeof trailColor === "string") {
544
528
  console.warn(
545
- `[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.`,
529
+ `[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.`
546
530
  );
547
531
  }
548
532
  }
@@ -552,7 +536,7 @@ var getHeadDotRadius = (w, h) => Math.max(1, 3 * Math.sqrt(Math.min(w, h) / 160)
552
536
  var WHITE_HEX = "#ffffff";
553
537
  function hexToRgbComponents(hex) {
554
538
  const n = parseInt(hex.slice(1), 16);
555
- return `${n >> 16},${(n >> 8) & 255},${n & 255}`;
539
+ return `${n >> 16},${n >> 8 & 255},${n & 255}`;
556
540
  }
557
541
  function applyDprSizing(target, logicalWidth, logicalHeight, dpr) {
558
542
  target.style.width = `${logicalWidth}px`;
@@ -596,6 +580,7 @@ function createRenderer(options) {
596
580
  let offsetY = 0;
597
581
  let animationId = null;
598
582
  let lastTime = 0;
583
+ let pausedByVisibility = false;
599
584
  let morphResolve = null;
600
585
  let morphReject = null;
601
586
  let morphDurationMs = DEFAULT_MORPH_DURATION_MS;
@@ -678,7 +663,7 @@ function createRenderer(options) {
678
663
  i,
679
664
  trailCount,
680
665
  toX,
681
- toY,
666
+ toY
682
667
  );
683
668
  if (trailStyle === "default") {
684
669
  ctx.fillStyle = `rgba(${trailSolidRgb},${opacity})`;
@@ -789,6 +774,7 @@ function createRenderer(options) {
789
774
  cancelAnimationFrame(animationId);
790
775
  animationId = null;
791
776
  }
777
+ document.removeEventListener("visibilitychange", handleVisibilityChange);
792
778
  if (morphReject !== null) {
793
779
  morphReject(new Error("Instance destroyed during morph"));
794
780
  morphResolve = null;
@@ -842,10 +828,30 @@ function createRenderer(options) {
842
828
  if (partial.trailColor !== void 0 || partial.trailStyle !== void 0) {
843
829
  warnIfTrailColorMismatch(trailColor, trailStyle);
844
830
  }
845
- },
831
+ }
846
832
  };
847
- if (shouldAutoStart) {
833
+ const pauseOnHidden = options.pauseOnHidden !== false;
834
+ function handleVisibilityChange() {
835
+ if (document.hidden) {
836
+ if (animationId !== null) {
837
+ instance.pause();
838
+ pausedByVisibility = true;
839
+ }
840
+ } else {
841
+ if (pausedByVisibility) {
842
+ pausedByVisibility = false;
843
+ instance.play();
844
+ }
845
+ }
846
+ }
847
+ if (pauseOnHidden) {
848
+ document.addEventListener("visibilitychange", handleVisibilityChange);
849
+ }
850
+ const actuallyAutoStart = shouldAutoStart && !(pauseOnHidden && document.hidden);
851
+ if (actuallyAutoStart) {
848
852
  instance.play();
853
+ } else if (shouldAutoStart) {
854
+ pausedByVisibility = true;
849
855
  }
850
856
  return instance;
851
857
  }
@@ -872,7 +878,7 @@ function sampleCurveSkeleton(curveDef) {
872
878
  const samples = Math.ceil(period * 50);
873
879
  const pts = Array.from({ length: samples });
874
880
  for (let i = 0; i < samples; i++) {
875
- const t = (i / (samples - 1)) * period;
881
+ const t = i / (samples - 1) * period;
876
882
  pts[i] = curveDef.skeletonFn ? curveDef.skeletonFn(t) : curveDef.fn(t, 0, EMPTY_PARAMS2);
877
883
  }
878
884
  return pts;
@@ -885,7 +891,7 @@ function createSVGRenderer(options) {
885
891
  const poolSize = engine.trailLength;
886
892
  if (poolSize > HIGH_TRAIL_LENGTH_THRESHOLD) {
887
893
  console.warn(
888
- `[sarmal] High trailLength in SVG renderer (${poolSize}). Consider using the canvas renderer for long trails.`,
894
+ `[sarmal] High trailLength in SVG renderer (${poolSize}). Consider using the canvas renderer for long trails.`
889
895
  );
890
896
  }
891
897
  let trailStyle = options.trailStyle ?? "default";
@@ -904,9 +910,9 @@ function createSVGRenderer(options) {
904
910
  return rect.width && rect.height ? Math.min(rect.width, rect.height) : 200;
905
911
  }
906
912
  const containerPx = getContainerPixelSize();
907
- const svgTrailMinWidth = (TRAIL_MIN_WIDTH * viewSize) / containerPx;
908
- const svgTrailMaxWidth = (TRAIL_MAX_WIDTH * viewSize) / containerPx;
909
- const svgSkeletonStrokeWidth = String((SKELETON_STROKE_WIDTH_PX * viewSize) / containerPx);
913
+ const svgTrailMinWidth = TRAIL_MIN_WIDTH * viewSize / containerPx;
914
+ const svgTrailMaxWidth = TRAIL_MAX_WIDTH * viewSize / containerPx;
915
+ const svgSkeletonStrokeWidth = String(SKELETON_STROKE_WIDTH_PX * viewSize / containerPx);
910
916
  headRadius = options.headRadius ?? SVG_DEFAULT_HEAD_RADIUS;
911
917
  container.setAttribute("viewBox", `0 0 ${viewSize} ${viewSize}`);
912
918
  container.setAttribute("role", "img");
@@ -994,7 +1000,7 @@ function createSVGRenderer(options) {
994
1000
  px,
995
1001
  py,
996
1002
  svgTrailMinWidth,
997
- svgTrailMaxWidth,
1003
+ svgTrailMaxWidth
998
1004
  );
999
1005
  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`;
1000
1006
  trailPaths[i].setAttribute("d", d);
@@ -1021,8 +1027,8 @@ function createSVGRenderer(options) {
1021
1027
  }
1022
1028
  let animationId = null;
1023
1029
  let lastTime = 0;
1024
- const prefersReducedMotion =
1025
- typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
1030
+ let pausedByVisibility = false;
1031
+ const prefersReducedMotion = typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
1026
1032
  let morphResolve = null;
1027
1033
  let morphReject = null;
1028
1034
  let morphDurationMs = DEFAULT_MORPH_DURATION_MS;
@@ -1040,7 +1046,7 @@ function createSVGRenderer(options) {
1040
1046
  skeletonPathA.setAttribute("visibility", "visible");
1041
1047
  skeletonPathA.setAttribute(
1042
1048
  "stroke-opacity",
1043
- String((1 - morphAlpha) * DEFAULT_SKELETON_OPACITY),
1049
+ String((1 - morphAlpha) * DEFAULT_SKELETON_OPACITY)
1044
1050
  );
1045
1051
  }
1046
1052
  if (morphPathBBuilt) {
@@ -1112,6 +1118,7 @@ function createSVGRenderer(options) {
1112
1118
  cancelAnimationFrame(animationId);
1113
1119
  animationId = null;
1114
1120
  }
1121
+ document.removeEventListener("visibilitychange", handleVisibilityChange);
1115
1122
  if (morphReject !== null) {
1116
1123
  morphReject(new Error("Instance destroyed during morph"));
1117
1124
  morphResolve = null;
@@ -1193,10 +1200,30 @@ function createSVGRenderer(options) {
1193
1200
  if (partial.trailColor !== void 0 || partial.trailStyle !== void 0) {
1194
1201
  warnIfTrailColorMismatch(trailColor, trailStyle);
1195
1202
  }
1196
- },
1203
+ }
1197
1204
  };
1198
- if (shouldAutoStart) {
1205
+ const pauseOnHidden = options.pauseOnHidden !== false;
1206
+ function handleVisibilityChange() {
1207
+ if (document.hidden) {
1208
+ if (animationId !== null) {
1209
+ instance.pause();
1210
+ pausedByVisibility = true;
1211
+ }
1212
+ } else {
1213
+ if (pausedByVisibility) {
1214
+ pausedByVisibility = false;
1215
+ instance.play();
1216
+ }
1217
+ }
1218
+ }
1219
+ if (pauseOnHidden) {
1220
+ document.addEventListener("visibilitychange", handleVisibilityChange);
1221
+ }
1222
+ const actuallyAutoStart = shouldAutoStart && !(pauseOnHidden && document.hidden);
1223
+ if (actuallyAutoStart) {
1199
1224
  instance.play();
1225
+ } else if (shouldAutoStart) {
1226
+ pausedByVisibility = true;
1200
1227
  }
1201
1228
  return instance;
1202
1229
  }
@@ -1206,254 +1233,309 @@ function createSarmalSVG(container, curveDef, options) {
1206
1233
  return createSVGRenderer({ container, engine, ...rendererOpts });
1207
1234
  }
1208
1235
 
1209
- // src/curves/artemis2.ts
1210
- var TWO_PI2 = Math.PI * 2;
1211
- function artemis2Fn(t, _time, _params) {
1212
- const a = 0.35,
1213
- b = 0.15,
1214
- ox = 0.175;
1215
- const s = Math.sin(t),
1216
- c = Math.cos(t);
1217
- const denom = 1 + s * s;
1236
+ // src/catmull-rom.ts
1237
+ var PERIOD = 2 * Math.PI;
1238
+ function catmullRom1D(p0, p1, p2, p3, u) {
1239
+ const u2 = u * u;
1240
+ const u3 = u2 * u;
1241
+ return 0.5 * (2 * p1 + (-p0 + p2) * u + (2 * p0 - 5 * p1 + 4 * p2 - p3) * u2 + (-p0 + 3 * p1 - 3 * p2 + p3) * u3);
1242
+ }
1243
+ function evaluateCatmullRom(points2, t) {
1244
+ const N = points2.length;
1245
+ if (N === 0) {
1246
+ return { x: 0, y: 0 };
1247
+ }
1248
+ if (N === 1) {
1249
+ return { x: points2[0][0], y: points2[0][1] };
1250
+ }
1251
+ t = (t % PERIOD + PERIOD) % PERIOD;
1252
+ const segmentSize = PERIOD / N;
1253
+ let i = Math.floor(t / segmentSize);
1254
+ if (i >= N) {
1255
+ i = N - 1;
1256
+ }
1257
+ let u = (t - i * segmentSize) / segmentSize;
1258
+ u = Math.max(0, Math.min(1, u));
1259
+ const p0 = points2[(i - 1 + N) % N];
1260
+ const p1 = points2[i];
1261
+ const p2 = points2[(i + 1) % N];
1262
+ const p3 = points2[(i + 2) % N];
1263
+ return {
1264
+ x: catmullRom1D(p0[0], p1[0], p2[0], p3[0], u),
1265
+ y: catmullRom1D(p0[1], p1[1], p2[1], p3[1], u)
1266
+ };
1267
+ }
1268
+ function drawCurve(points2, opts) {
1269
+ if (points2.length < 3) {
1270
+ throw new Error(`drawCurve requires at least 3 points, received ${points2.length}.`);
1271
+ }
1272
+ const first = points2[0];
1273
+ if (points2.every((p) => p[0] === first[0] && p[1] === first[1])) {
1274
+ console.warn(
1275
+ "[sarmal].drawCurve: all control points are identical. The curve will be a single point."
1276
+ );
1277
+ }
1278
+ const maxAbs = points2.reduce((m, p) => Math.max(m, Math.abs(p[0]), Math.abs(p[1])), 0);
1279
+ if (maxAbs > 2) {
1280
+ console.warn(
1281
+ `[sarmal].drawCurve: control points extend to \xB1${maxAbs.toFixed(1)}, which may render off-screen. Coordinates should be in [-1, 1].`
1282
+ );
1283
+ }
1284
+ const pts = points2.map(([x, y]) => [x, y]);
1218
1285
  return {
1219
- x: (c * (1 + a * c)) / denom - ox,
1220
- y: (s * c * (1 + b * c)) / denom,
1286
+ name: opts?.name ?? "drawn",
1287
+ fn: (t) => evaluateCatmullRom(pts, t),
1288
+ period: PERIOD,
1289
+ kind: "drawn"
1221
1290
  };
1222
1291
  }
1292
+
1293
+ // src/curves/artemis2.ts
1294
+ var points = [
1295
+ [-0.44, -0.45],
1296
+ [-0.53, -0.77],
1297
+ [-0.82, -0.66],
1298
+ [-0.82, -0.18],
1299
+ [-0.25, -0.04],
1300
+ [0.16, -0.49],
1301
+ [-0.03, -0.87],
1302
+ [-0.68, -0.94],
1303
+ [-0.95, -0.61],
1304
+ [-0.87, -0],
1305
+ [-0.34, 0.21],
1306
+ [0.27, -0.04],
1307
+ [0.87, 0.06],
1308
+ [0.87, 0.57],
1309
+ [0.32, 0.66],
1310
+ [-0.21, -0.43],
1311
+ [-0.43, -0.81],
1312
+ [-0.69, -0.84],
1313
+ [-0.87, -0.66],
1314
+ [-0.9, -0.47],
1315
+ [-0.76, -0.35]
1316
+ ];
1223
1317
  var artemis2 = {
1224
- name: "Artemis II",
1225
- fn: artemis2Fn,
1226
- period: TWO_PI2,
1227
- speed: 0.7,
1318
+ ...drawCurve(points, { name: "Artemis II" }),
1319
+ speed: 0.7
1228
1320
  };
1229
1321
 
1230
1322
  // src/curves/astroid.ts
1231
- var TWO_PI3 = Math.PI * 2;
1323
+ var TWO_PI2 = Math.PI * 2;
1232
1324
  function astroidFn(t, _time, _params) {
1233
1325
  const c = Math.cos(t);
1234
1326
  const s = Math.sin(t);
1235
1327
  return {
1236
1328
  x: c * c * c,
1237
- y: s * s * s,
1329
+ y: s * s * s
1238
1330
  };
1239
1331
  }
1240
1332
  var astroid = {
1241
1333
  name: "Astroid",
1242
1334
  fn: astroidFn,
1243
- period: TWO_PI3,
1244
- speed: 1.1,
1335
+ period: TWO_PI2,
1336
+ speed: 1.1
1245
1337
  };
1246
1338
 
1247
1339
  // src/curves/deltoid.ts
1248
- var TWO_PI4 = Math.PI * 2;
1340
+ var TWO_PI3 = Math.PI * 2;
1249
1341
  function deltoidFn(t, _time, _params) {
1250
1342
  return {
1251
1343
  x: 2 * Math.cos(t) + Math.cos(2 * t),
1252
- y: 2 * Math.sin(t) - Math.sin(2 * t),
1344
+ y: 2 * Math.sin(t) - Math.sin(2 * t)
1253
1345
  };
1254
1346
  }
1255
1347
  var deltoid = {
1256
1348
  name: "Deltoid",
1257
1349
  fn: deltoidFn,
1258
- period: TWO_PI4,
1259
- speed: 0.9,
1350
+ period: TWO_PI3,
1351
+ speed: 0.9
1260
1352
  };
1261
1353
 
1262
1354
  // src/curves/epicycloid3.ts
1263
- var TWO_PI5 = Math.PI * 2;
1355
+ var TWO_PI4 = Math.PI * 2;
1264
1356
  function epicycloid3Fn(t, _time, _params) {
1265
1357
  return {
1266
1358
  x: 4 * Math.cos(t) - Math.cos(4 * t),
1267
- y: 4 * Math.sin(t) - Math.sin(4 * t),
1359
+ y: 4 * Math.sin(t) - Math.sin(4 * t)
1268
1360
  };
1269
1361
  }
1270
1362
  var epicycloid3 = {
1271
1363
  name: "Epicycloid (n=3)",
1272
1364
  fn: epicycloid3Fn,
1273
- period: TWO_PI5,
1274
- speed: 0.75,
1365
+ period: TWO_PI4,
1366
+ speed: 0.75
1275
1367
  };
1276
1368
 
1277
1369
  // src/curves/epitrochoid7.ts
1278
- var TWO_PI6 = Math.PI * 2;
1370
+ var TWO_PI5 = Math.PI * 2;
1279
1371
  function epitrochoid7Fn(t, _time, _params) {
1280
1372
  const d = 1 + 0.55 * Math.sin(t * 0.5);
1281
1373
  return {
1282
1374
  x: 7 * Math.cos(t) - d * Math.cos(7 * t),
1283
- y: 7 * Math.sin(t) - d * Math.sin(7 * t),
1375
+ y: 7 * Math.sin(t) - d * Math.sin(7 * t)
1284
1376
  };
1285
1377
  }
1286
1378
  function epitrochoid7SkeletonFn(t) {
1287
1379
  const d = 1.275;
1288
1380
  return {
1289
1381
  x: 7 * Math.cos(t) - d * Math.cos(7 * t),
1290
- y: 7 * Math.sin(t) - d * Math.sin(7 * t),
1382
+ y: 7 * Math.sin(t) - d * Math.sin(7 * t)
1291
1383
  };
1292
1384
  }
1293
1385
  var epitrochoid7 = {
1294
1386
  name: "Epitrochoid",
1295
1387
  fn: epitrochoid7Fn,
1296
- period: TWO_PI6,
1388
+ period: TWO_PI5,
1297
1389
  speed: 1.4,
1298
- skeletonFn: epitrochoid7SkeletonFn,
1390
+ skeletonFn: epitrochoid7SkeletonFn
1299
1391
  };
1300
1392
 
1301
1393
  // src/curves/lissajous32.ts
1302
- var TWO_PI7 = Math.PI * 2;
1394
+ var TWO_PI6 = Math.PI * 2;
1303
1395
  function lissajous32Fn(t, time, _params) {
1304
1396
  const phi = time * 0.45;
1305
1397
  return {
1306
1398
  x: Math.sin(3 * t + phi),
1307
- y: Math.sin(2 * t),
1399
+ y: Math.sin(2 * t)
1308
1400
  };
1309
1401
  }
1310
1402
  var lissajous32 = {
1311
1403
  name: "Lissajous 3:2",
1312
1404
  fn: lissajous32Fn,
1313
- period: TWO_PI7,
1405
+ period: TWO_PI6,
1314
1406
  speed: 2,
1315
- skeleton: "live",
1407
+ skeleton: "live"
1316
1408
  };
1317
1409
 
1318
1410
  // src/curves/lissajous43.ts
1319
- var TWO_PI8 = Math.PI * 2;
1411
+ var TWO_PI7 = Math.PI * 2;
1320
1412
  function lissajous43Fn(t, time, _params) {
1321
1413
  const phi = time * 0.38;
1322
1414
  return {
1323
1415
  x: Math.sin(4 * t + phi),
1324
- y: Math.sin(3 * t),
1416
+ y: Math.sin(3 * t)
1325
1417
  };
1326
1418
  }
1327
1419
  var lissajous43 = {
1328
1420
  name: "Lissajous 4:3",
1329
1421
  fn: lissajous43Fn,
1330
- period: TWO_PI8,
1422
+ period: TWO_PI7,
1331
1423
  speed: 1.8,
1332
- skeleton: "live",
1424
+ skeleton: "live"
1333
1425
  };
1334
1426
 
1335
1427
  // src/curves/lame.ts
1336
- var TWO_PI9 = Math.PI * 2;
1428
+ var TWO_PI8 = Math.PI * 2;
1337
1429
  function lameFn(t, time, _params) {
1338
1430
  const p = 1.75 + 1.25 * Math.sin(time * 0.48);
1339
- const c = Math.cos(t),
1340
- s = Math.sin(t);
1431
+ const c = Math.cos(t), s = Math.sin(t);
1341
1432
  return {
1342
1433
  x: Math.sign(c) * Math.pow(Math.abs(c), p),
1343
- y: Math.sign(s) * Math.pow(Math.abs(s), p),
1434
+ y: Math.sign(s) * Math.pow(Math.abs(s), p)
1344
1435
  };
1345
1436
  }
1346
1437
  var lame = {
1347
1438
  name: "Lam\xE9 Curve",
1348
1439
  fn: lameFn,
1349
- period: TWO_PI9,
1440
+ period: TWO_PI8,
1350
1441
  speed: 1,
1351
- skeleton: "live",
1442
+ skeleton: "live"
1352
1443
  };
1353
1444
 
1354
1445
  // src/curves/rose3.ts
1355
- var TWO_PI10 = Math.PI * 2;
1446
+ var TWO_PI9 = Math.PI * 2;
1356
1447
  function rose3Fn(t, _time, _params) {
1357
1448
  const r = Math.cos(3 * t);
1358
1449
  return {
1359
1450
  x: r * Math.cos(t),
1360
- y: r * Math.sin(t),
1451
+ y: r * Math.sin(t)
1361
1452
  };
1362
1453
  }
1363
1454
  var rose3 = {
1364
1455
  name: "Rose (n=3)",
1365
1456
  fn: rose3Fn,
1366
- period: TWO_PI10,
1367
- speed: 1.15,
1457
+ period: TWO_PI9,
1458
+ speed: 1.15
1368
1459
  };
1369
1460
 
1370
1461
  // src/curves/rose5.ts
1371
- var TWO_PI11 = Math.PI * 2;
1462
+ var TWO_PI10 = Math.PI * 2;
1372
1463
  function rose5Fn(t, _time, _params) {
1373
1464
  const r = Math.cos(5 * t);
1374
1465
  return {
1375
1466
  x: r * Math.cos(t),
1376
- y: r * Math.sin(t),
1467
+ y: r * Math.sin(t)
1377
1468
  };
1378
1469
  }
1379
1470
  var rose5 = {
1380
1471
  name: "Rose (n=5)",
1381
1472
  fn: rose5Fn,
1382
- period: TWO_PI11,
1383
- speed: 1,
1473
+ period: TWO_PI10,
1474
+ speed: 1
1384
1475
  };
1385
1476
 
1386
1477
  // src/curves/rose52.ts
1387
1478
  var FOUR_PI = Math.PI * 4;
1388
1479
  function rose52Fn(t, _time, _params) {
1389
- const r = Math.cos((5 / 2) * t);
1480
+ const r = Math.cos(5 / 2 * t);
1390
1481
  return {
1391
1482
  x: r * Math.cos(t),
1392
- y: r * Math.sin(t),
1483
+ y: r * Math.sin(t)
1393
1484
  };
1394
1485
  }
1395
1486
  var rose52 = {
1396
1487
  name: "Rose (n=5/2)",
1397
1488
  fn: rose52Fn,
1398
1489
  period: FOUR_PI,
1399
- speed: 0.8,
1490
+ speed: 0.8
1400
1491
  };
1401
1492
 
1402
1493
  // src/curves/star.ts
1403
- var TWO_PI12 = Math.PI * 2;
1494
+ var TWO_PI11 = Math.PI * 2;
1404
1495
  function starFn(t, _time, _params) {
1405
- const r =
1406
- Math.abs(Math.cos((5 / 2) * t)) +
1407
- 0.35 * Math.abs(Math.cos((15 / 2) * t)) +
1408
- 0.15 * Math.abs(Math.cos((25 / 2) * t));
1496
+ 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));
1409
1497
  return {
1410
1498
  x: r * Math.cos(t),
1411
- y: r * Math.sin(t),
1499
+ y: r * Math.sin(t)
1412
1500
  };
1413
1501
  }
1414
1502
  var star = {
1415
1503
  name: "Star",
1416
1504
  fn: starFn,
1417
- period: TWO_PI12,
1418
- speed: 1,
1505
+ period: TWO_PI11,
1506
+ speed: 1
1419
1507
  };
1420
1508
 
1421
1509
  // src/curves/star4.ts
1422
- var TWO_PI13 = Math.PI * 2;
1510
+ var TWO_PI12 = Math.PI * 2;
1423
1511
  function star4Fn(t, _time, _params) {
1424
- const r =
1425
- Math.abs(Math.cos(2 * t)) +
1426
- 0.35 * Math.abs(Math.cos(6 * t)) +
1427
- 0.15 * Math.abs(Math.cos(10 * t));
1512
+ const r = Math.abs(Math.cos(2 * t)) + 0.35 * Math.abs(Math.cos(6 * t)) + 0.15 * Math.abs(Math.cos(10 * t));
1428
1513
  return {
1429
1514
  x: r * Math.cos(t),
1430
- y: r * Math.sin(t),
1515
+ y: r * Math.sin(t)
1431
1516
  };
1432
1517
  }
1433
1518
  var star4 = {
1434
1519
  name: "Star (4-arm)",
1435
1520
  fn: star4Fn,
1436
- period: TWO_PI13,
1437
- speed: 1,
1521
+ period: TWO_PI12,
1522
+ speed: 1
1438
1523
  };
1439
1524
 
1440
1525
  // src/curves/star7.ts
1441
- var TWO_PI14 = Math.PI * 2;
1526
+ var TWO_PI13 = Math.PI * 2;
1442
1527
  function star7Fn(t, _time, _params) {
1443
- const r =
1444
- Math.abs(Math.cos((7 / 2) * t)) +
1445
- 0.35 * Math.abs(Math.cos((21 / 2) * t)) +
1446
- 0.15 * Math.abs(Math.cos((35 / 2) * t));
1528
+ 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));
1447
1529
  return {
1448
1530
  x: r * Math.cos(t),
1449
- y: r * Math.sin(t),
1531
+ y: r * Math.sin(t)
1450
1532
  };
1451
1533
  }
1452
1534
  var star7 = {
1453
1535
  name: "Star (7-arm)",
1454
1536
  fn: star7Fn,
1455
- period: TWO_PI14,
1456
- speed: 1,
1537
+ period: TWO_PI13,
1538
+ speed: 1
1457
1539
  };
1458
1540
 
1459
1541
  // src/curves/index.ts
@@ -1471,58 +1553,9 @@ var curves = {
1471
1553
  lissajous32,
1472
1554
  lissajous43,
1473
1555
  epicycloid3,
1474
- lame,
1556
+ lame
1475
1557
  };
1476
1558
 
1477
- // src/catmull-rom.ts
1478
- var PERIOD = 2 * Math.PI;
1479
- function catmullRom1D(p0, p1, p2, p3, u) {
1480
- const u2 = u * u;
1481
- const u3 = u2 * u;
1482
- return (
1483
- 0.5 *
1484
- (2 * p1 +
1485
- (-p0 + p2) * u +
1486
- (2 * p0 - 5 * p1 + 4 * p2 - p3) * u2 +
1487
- (-p0 + 3 * p1 - 3 * p2 + p3) * u3)
1488
- );
1489
- }
1490
- function evaluateCatmullRom(points, t) {
1491
- const N = points.length;
1492
- if (N === 0) {
1493
- return { x: 0, y: 0 };
1494
- }
1495
- if (N === 1) {
1496
- return { x: points[0][0], y: points[0][1] };
1497
- }
1498
- t = ((t % PERIOD) + PERIOD) % PERIOD;
1499
- const segmentSize = PERIOD / N;
1500
- let i = Math.floor(t / segmentSize);
1501
- if (i >= N) {
1502
- i = N - 1;
1503
- }
1504
- let u = (t - i * segmentSize) / segmentSize;
1505
- u = Math.max(0, Math.min(1, u));
1506
- const p0 = points[(i - 1 + N) % N];
1507
- const p1 = points[i];
1508
- const p2 = points[(i + 1) % N];
1509
- const p3 = points[(i + 2) % N];
1510
- return {
1511
- x: catmullRom1D(p0[0], p1[0], p2[0], p3[0], u),
1512
- y: catmullRom1D(p0[1], p1[1], p2[1], p3[1], u),
1513
- };
1514
- }
1515
- function drawCurve(points) {
1516
- if (points.length < 3) {
1517
- throw new Error(`drawCurve requires at least 3 points, received ${points.length}.`);
1518
- }
1519
- return {
1520
- name: "custom",
1521
- fn: (t) => evaluateCatmullRom(points, t),
1522
- period: PERIOD,
1523
- };
1524
- }
1525
-
1526
1559
  // src/index.ts
1527
1560
  function createSarmal(canvas, curveDef, options) {
1528
1561
  const { trailLength, ...rendererOpts } = options ?? {};
@@ -1530,26 +1563,6 @@ function createSarmal(canvas, curveDef, options) {
1530
1563
  return createRenderer({ canvas, engine, ...rendererOpts });
1531
1564
  }
1532
1565
 
1533
- export {
1534
- artemis2,
1535
- astroid,
1536
- createEngine,
1537
- createRenderer,
1538
- createSVGRenderer,
1539
- createSarmal,
1540
- createSarmalSVG,
1541
- curves,
1542
- deltoid,
1543
- drawCurve,
1544
- epicycloid3,
1545
- epitrochoid7,
1546
- evaluateCatmullRom,
1547
- lame,
1548
- lissajous32,
1549
- lissajous43,
1550
- palettes,
1551
- rose3,
1552
- rose5,
1553
- };
1554
- //# sourceMappingURL=index.js.map
1566
+ export { artemis2, astroid, computeBoundaries, createEngine, createRenderer, createSVGRenderer, createSarmal, createSarmalSVG, curves, deltoid, drawCurve, epicycloid3, epitrochoid7, evaluateCatmullRom, lame, lissajous32, lissajous43, palettes, rose3, rose5 };
1555
1567
  //# sourceMappingURL=index.js.map
1568
+ //# sourceMappingURL=index.js.map