@sarmal/core 0.17.3 → 0.19.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 (76) hide show
  1. package/README.md +1 -12
  2. package/dist/auto-init.cjs +124 -92
  3. package/dist/auto-init.cjs.map +1 -1
  4. package/dist/auto-init.js +123 -91
  5. package/dist/auto-init.js.map +1 -1
  6. package/dist/curves/artemis2.cjs +10 -7
  7. package/dist/curves/artemis2.d.cts +1 -1
  8. package/dist/curves/artemis2.d.ts +1 -1
  9. package/dist/curves/artemis2.js +9 -6
  10. package/dist/curves/astroid.cjs +4 -4
  11. package/dist/curves/astroid.d.cts +1 -1
  12. package/dist/curves/astroid.d.ts +1 -1
  13. package/dist/curves/astroid.js +3 -3
  14. package/dist/curves/deltoid.cjs +4 -4
  15. package/dist/curves/deltoid.d.cts +1 -1
  16. package/dist/curves/deltoid.d.ts +1 -1
  17. package/dist/curves/deltoid.js +3 -3
  18. package/dist/curves/epicycloid3.cjs +4 -4
  19. package/dist/curves/epicycloid3.d.cts +1 -1
  20. package/dist/curves/epicycloid3.d.ts +1 -1
  21. package/dist/curves/epicycloid3.js +3 -3
  22. package/dist/curves/epitrochoid7.cjs +5 -5
  23. package/dist/curves/epitrochoid7.d.cts +1 -1
  24. package/dist/curves/epitrochoid7.d.ts +1 -1
  25. package/dist/curves/epitrochoid7.js +4 -4
  26. package/dist/curves/index.cjs +53 -40
  27. package/dist/curves/index.d.cts +29 -29
  28. package/dist/curves/index.d.ts +29 -29
  29. package/dist/curves/index.js +69 -40
  30. package/dist/curves/lame.cjs +6 -5
  31. package/dist/curves/lame.d.cts +1 -1
  32. package/dist/curves/lame.d.ts +1 -1
  33. package/dist/curves/lame.js +5 -4
  34. package/dist/curves/lissajous32.cjs +4 -4
  35. package/dist/curves/lissajous32.d.cts +1 -1
  36. package/dist/curves/lissajous32.d.ts +1 -1
  37. package/dist/curves/lissajous32.js +3 -3
  38. package/dist/curves/lissajous43.cjs +4 -4
  39. package/dist/curves/lissajous43.d.cts +1 -1
  40. package/dist/curves/lissajous43.d.ts +1 -1
  41. package/dist/curves/lissajous43.js +3 -3
  42. package/dist/curves/rose3.cjs +4 -4
  43. package/dist/curves/rose3.d.cts +1 -1
  44. package/dist/curves/rose3.d.ts +1 -1
  45. package/dist/curves/rose3.js +3 -3
  46. package/dist/curves/rose5.cjs +4 -4
  47. package/dist/curves/rose5.d.cts +1 -1
  48. package/dist/curves/rose5.d.ts +1 -1
  49. package/dist/curves/rose5.js +3 -3
  50. package/dist/curves/rose52.cjs +5 -5
  51. package/dist/curves/rose52.d.cts +1 -1
  52. package/dist/curves/rose52.d.ts +1 -1
  53. package/dist/curves/rose52.js +4 -4
  54. package/dist/curves/star.cjs +8 -5
  55. package/dist/curves/star.d.cts +1 -1
  56. package/dist/curves/star.d.ts +1 -1
  57. package/dist/curves/star.js +7 -4
  58. package/dist/curves/star4.cjs +8 -5
  59. package/dist/curves/star4.d.cts +1 -1
  60. package/dist/curves/star4.d.ts +1 -1
  61. package/dist/curves/star4.js +7 -4
  62. package/dist/curves/star7.cjs +8 -5
  63. package/dist/curves/star7.d.cts +1 -1
  64. package/dist/curves/star7.d.ts +1 -1
  65. package/dist/curves/star7.js +7 -4
  66. package/dist/index.cjs +159 -118
  67. package/dist/index.cjs.map +1 -1
  68. package/dist/index.d.cts +78 -37
  69. package/dist/index.d.ts +78 -37
  70. package/dist/index.js +177 -118
  71. package/dist/index.js.map +1 -1
  72. package/dist/types-frtEoAq6.d.cts +317 -0
  73. package/dist/types-frtEoAq6.d.ts +317 -0
  74. package/package.json +1 -1
  75. package/dist/types-BL9HhEmk.d.cts +0 -299
  76. package/dist/types-BL9HhEmk.d.ts +0 -299
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 {
@@ -120,6 +120,9 @@ function createEngine(curveDef, trailLength = 120) {
120
120
  get trailCount() {
121
121
  return trail.length;
122
122
  },
123
+ get trailLength() {
124
+ return trailLength;
125
+ },
123
126
  get isLiveSkeleton() {
124
127
  return curve.skeleton === "live";
125
128
  },
@@ -132,14 +135,14 @@ function createEngine(curveDef, trailLength = 120) {
132
135
  trail.clear();
133
136
  },
134
137
  jump(newT, { clearTrail = false } = {}) {
135
- t = (newT % curve.period + curve.period) % curve.period;
138
+ t = ((newT % curve.period) + curve.period) % curve.period;
136
139
  if (clearTrail) {
137
140
  trail.clear();
138
141
  }
139
142
  },
140
143
  seek(targetT, { wrap = false, step = curve.period / trailLength } = {}) {
141
144
  const advance = curve.speed * step;
142
- const target = (targetT % curve.period + curve.period) % curve.period;
145
+ const target = ((targetT % curve.period) + curve.period) % curve.period;
143
146
  const targetTime = target / curve.speed;
144
147
  t = target;
145
148
  actualTime = targetTime;
@@ -148,7 +151,7 @@ function createEngine(curveDef, trailLength = 120) {
148
151
  const count = wrap ? trailLength : Math.min(trailLength, pointsFromStart);
149
152
  for (let i = count - 1; i >= 0; i--) {
150
153
  const sampleT = target - i * advance;
151
- const wrappedT = (sampleT % curve.period + curve.period) % curve.period;
154
+ const wrappedT = ((sampleT % curve.period) + curve.period) % curve.period;
152
155
  const time = targetTime - i * step;
153
156
  const point = curve.fn(wrappedT, time, EMPTY_PARAMS);
154
157
  trail.push(point.x, point.y);
@@ -165,13 +168,16 @@ function createEngine(curveDef, trailLength = 120) {
165
168
  ...frozenB,
166
169
  fn: (sampleT, time, params) => {
167
170
  const a = frozenA.fn(sampleT, time, params);
168
- const tB = frozenStrategy === "normalized" ? sampleT / frozenA.period * frozenB.period : sampleT;
171
+ const tB =
172
+ frozenStrategy === "normalized"
173
+ ? (sampleT / frozenA.period) * frozenB.period
174
+ : sampleT;
169
175
  const b = frozenB.fn(tB, time, params);
170
176
  return {
171
177
  x: a.x + (b.x - a.x) * frozenAlpha,
172
- y: a.y + (b.y - a.y) * frozenAlpha
178
+ y: a.y + (b.y - a.y) * frozenAlpha,
173
179
  };
174
- }
180
+ },
175
181
  };
176
182
  }
177
183
  _morphStrategy = strategy;
@@ -184,7 +190,7 @@ function createEngine(curveDef, trailLength = 120) {
184
190
  completeMorph() {
185
191
  if (morphCurveB !== null) {
186
192
  if (_morphStrategy === "normalized" && curve.period !== morphCurveB.period) {
187
- t = t / curve.period * morphCurveB.period;
193
+ t = (t / curve.period) * morphCurveB.period;
188
194
  }
189
195
  curve = morphCurveB;
190
196
  }
@@ -196,19 +202,22 @@ function createEngine(curveDef, trailLength = 120) {
196
202
  const points = new Array(steps);
197
203
  if (morphCurveB !== null && _morphAlpha !== null) {
198
204
  for (let i = 0; i < steps; i++) {
199
- const sampleT = i / (steps - 1) * curve.period;
205
+ const sampleT = (i / (steps - 1)) * curve.period;
200
206
  const a = sampleSkeleton(curve, sampleT);
201
- const tB = _morphStrategy === "normalized" ? sampleT / curve.period * morphCurveB.period : sampleT;
207
+ const tB =
208
+ _morphStrategy === "normalized"
209
+ ? (sampleT / curve.period) * morphCurveB.period
210
+ : sampleT;
202
211
  const b = sampleSkeleton(morphCurveB, tB);
203
212
  points[i] = {
204
213
  x: a.x + (b.x - a.x) * _morphAlpha,
205
- y: a.y + (b.y - a.y) * _morphAlpha
214
+ y: a.y + (b.y - a.y) * _morphAlpha,
206
215
  };
207
216
  }
208
217
  return points;
209
218
  }
210
219
  for (let i = 0; i < steps; i++) {
211
- const sampleT = i / (steps - 1) * curve.period;
220
+ const sampleT = (i / (steps - 1)) * curve.period;
212
221
  points[i] = sampleSkeleton(curve, sampleT);
213
222
  }
214
223
  return points;
@@ -250,7 +259,7 @@ function createEngine(curveDef, trailLength = 120) {
250
259
  _speedTransition.reject(new Error("Speed transition cancelled"));
251
260
  _speedTransition = null;
252
261
  }
253
- }
262
+ },
254
263
  };
255
264
  }
256
265
 
@@ -289,12 +298,20 @@ function computeNormal(trail, i) {
289
298
  const tangent = computeTangent(trail, i);
290
299
  return { x: -tangent.y, y: tangent.x };
291
300
  }
292
- function computeTrailQuad(trail, i, trailCount, toX, toY) {
301
+ function computeTrailQuad(
302
+ trail,
303
+ i,
304
+ trailCount,
305
+ toX,
306
+ toY,
307
+ minWidth = TRAIL_MIN_WIDTH,
308
+ maxWidth = TRAIL_MAX_WIDTH,
309
+ ) {
293
310
  const progress = i / (trailCount - 1);
294
311
  const nextProgress = (i + 1) / (trailCount - 1);
295
312
  const opacity = Math.pow(progress, TRAIL_FADE_CURVE) * TRAIL_MAX_OPACITY;
296
- const w0 = (TRAIL_MIN_WIDTH + progress * (TRAIL_MAX_WIDTH - TRAIL_MIN_WIDTH)) / 2;
297
- const w1 = (TRAIL_MIN_WIDTH + nextProgress * (TRAIL_MAX_WIDTH - TRAIL_MIN_WIDTH)) / 2;
313
+ const w0 = (minWidth + progress * (maxWidth - minWidth)) / 2;
314
+ const w1 = (minWidth + nextProgress * (maxWidth - minWidth)) / 2;
298
315
  const curr = trail[i];
299
316
  const next = trail[i + 1];
300
317
  const n0 = computeNormal(trail, i);
@@ -313,13 +330,16 @@ function computeTrailQuad(trail, i, trailCount, toX, toY) {
313
330
  r1x: nx - n1.x * w1,
314
331
  r1y: ny - n1.y * w1,
315
332
  opacity,
316
- progress
333
+ progress,
317
334
  };
318
335
  }
319
336
  function computeBoundaries(pts, logicalWidth, logicalHeight) {
320
337
  if (pts.length === 0) return null;
321
338
  const first = pts[0];
322
- 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;
323
343
  for (const p of pts) {
324
344
  if (p.x < minX) {
325
345
  minX = p.x;
@@ -338,7 +358,7 @@ function computeBoundaries(pts, logicalWidth, logicalHeight) {
338
358
  const h = maxY - minY;
339
359
  if (w === 0 && h === 0) {
340
360
  throw new Error(
341
- "[sarmal] Degenerate curve: all skeleton points are identical. Check that your curve fn returns distinct points for different values of t."
361
+ "[sarmal] Degenerate curve: all skeleton points are identical. Check that your curve fn returns distinct points for different values of t.",
342
362
  );
343
363
  }
344
364
  const scaleXProportional = logicalWidth / (w * (1 + FIT_PADDING * 2));
@@ -349,12 +369,12 @@ function computeBoundaries(pts, logicalWidth, logicalHeight) {
349
369
  scaleXProportional,
350
370
  scaleYProportional,
351
371
  scaleXMinPadding,
352
- scaleYMinPadding
372
+ scaleYMinPadding,
353
373
  );
354
374
  return {
355
375
  scale,
356
376
  offsetX: (logicalWidth - w * scale) / 2 - minX * scale,
357
- offsetY: (logicalHeight - h * scale) / 2 - minY * scale
377
+ offsetY: (logicalHeight - h * scale) / 2 - minY * scale,
358
378
  };
359
379
  }
360
380
  function enginePassthroughs(engine) {
@@ -364,7 +384,7 @@ function enginePassthroughs(engine) {
364
384
  setSpeed: engine.setSpeed,
365
385
  getSpeed: engine.getSpeed,
366
386
  resetSpeed: engine.resetSpeed,
367
- setSpeedOver: engine.setSpeedOver
387
+ setSpeedOver: engine.setSpeedOver,
368
388
  };
369
389
  }
370
390
  var palettes = {
@@ -373,16 +393,16 @@ var palettes = {
373
393
  ocean: ["#1e3a8a", "#06b6d4", "#22d3ee", "#e0f2fe"],
374
394
  ice: ["#1e3a8a", "#67e8f9"],
375
395
  fire: ["#7f1d1d", "#fbbf24"],
376
- forest: ["#14532d", "#86efac"]
396
+ forest: ["#14532d", "#86efac"],
377
397
  };
378
398
  function hexToRgb(hex) {
379
399
  const n = parseInt(hex.slice(1), 16);
380
- return { r: n >> 16, g: n >> 8 & 255, b: n & 255 };
400
+ return { r: n >> 16, g: (n >> 8) & 255, b: n & 255 };
381
401
  }
382
402
  var lerpRgb = (a, b, t) => ({
383
403
  r: Math.round(a.r + (b.r - a.r) * t),
384
404
  g: Math.round(a.g + (b.g - a.g) * t),
385
- b: Math.round(a.b + (b.b - a.b) * t)
405
+ b: Math.round(a.b + (b.b - a.b) * t),
386
406
  });
387
407
  function getPaletteColor(palette, position, timeOffset = 0) {
388
408
  if (palette.length === 0) {
@@ -391,7 +411,7 @@ function getPaletteColor(palette, position, timeOffset = 0) {
391
411
  if (palette.length === 1) {
392
412
  return hexToRgb(palette[0]);
393
413
  }
394
- const cyclePos = ((position + timeOffset) % 1 + 1) % 1;
414
+ const cyclePos = (((position + timeOffset) % 1) + 1) % 1;
395
415
  const scaled = cyclePos * palette.length;
396
416
  const idx = Math.floor(scaled);
397
417
  const t = scaled - idx;
@@ -405,7 +425,7 @@ var RENDER_OPTION_KEYS = /* @__PURE__ */ new Set([
405
425
  "trailColor",
406
426
  "headColor",
407
427
  "skeletonColor",
408
- "trailStyle"
428
+ "trailStyle",
409
429
  ]);
410
430
  function validateRenderOptions(partial) {
411
431
  for (const key of Object.keys(partial)) {
@@ -430,7 +450,7 @@ function assertTrailColor(value) {
430
450
  if (typeof value === "string") {
431
451
  if (!HEX_COLOR_RE.test(value)) {
432
452
  throw new TypeError(
433
- `[sarmal] setRenderOptions: trailColor must be a 6-digit hex string, got "${value}"`
453
+ `[sarmal] setRenderOptions: trailColor must be a 6-digit hex string, got "${value}"`,
434
454
  );
435
455
  }
436
456
  return;
@@ -438,21 +458,21 @@ function assertTrailColor(value) {
438
458
  if (Array.isArray(value)) {
439
459
  if (value.length < 2) {
440
460
  throw new RangeError(
441
- `[sarmal] setRenderOptions: trailColor array must have at least 2 entries, got ${value.length}`
461
+ `[sarmal] setRenderOptions: trailColor array must have at least 2 entries, got ${value.length}`,
442
462
  );
443
463
  }
444
464
  for (let i = 0; i < value.length; i++) {
445
465
  const entry = value[i];
446
466
  if (typeof entry !== "string" || !HEX_COLOR_RE.test(entry)) {
447
467
  throw new TypeError(
448
- `[sarmal] setRenderOptions: trailColor[${i}] must be a 6-digit hex string, got ${JSON.stringify(entry)}`
468
+ `[sarmal] setRenderOptions: trailColor[${i}] must be a 6-digit hex string, got ${JSON.stringify(entry)}`,
449
469
  );
450
470
  }
451
471
  }
452
472
  return;
453
473
  }
454
474
  throw new TypeError(
455
- `[sarmal] setRenderOptions: trailColor must be a 6-digit hex string or an array of hex strings, got ${JSON.stringify(value)}`
475
+ `[sarmal] setRenderOptions: trailColor must be a 6-digit hex string or an array of hex strings, got ${JSON.stringify(value)}`,
456
476
  );
457
477
  }
458
478
  function assertHeadColor(value) {
@@ -461,7 +481,7 @@ function assertHeadColor(value) {
461
481
  }
462
482
  if (typeof value !== "string" || !HEX_COLOR_RE.test(value)) {
463
483
  throw new TypeError(
464
- `[sarmal] setRenderOptions: headColor must be a 6-digit hex string or null, got ${JSON.stringify(value)}`
484
+ `[sarmal] setRenderOptions: headColor must be a 6-digit hex string or null, got ${JSON.stringify(value)}`,
465
485
  );
466
486
  }
467
487
  }
@@ -471,14 +491,14 @@ function assertSkeletonColor(value) {
471
491
  }
472
492
  if (typeof value !== "string" || !HEX_COLOR_RE.test(value)) {
473
493
  throw new TypeError(
474
- `[sarmal] setRenderOptions: skeletonColor must be a 6-digit hex string or "transparent", got ${JSON.stringify(value)}`
494
+ `[sarmal] setRenderOptions: skeletonColor must be a 6-digit hex string or "transparent", got ${JSON.stringify(value)}`,
475
495
  );
476
496
  }
477
497
  }
478
498
  function assertTrailStyle(value) {
479
499
  if (!TRAIL_STYLES.includes(value)) {
480
500
  throw new RangeError(
481
- `[sarmal] setRenderOptions: trailStyle must be one of "default", "gradient-static", "gradient-animated", got ${JSON.stringify(value)}`
501
+ `[sarmal] setRenderOptions: trailStyle must be one of "default", "gradient-static", "gradient-animated", got ${JSON.stringify(value)}`,
482
502
  );
483
503
  }
484
504
  }
@@ -500,13 +520,13 @@ function resolveHeadColor(trailColor, trailStyle) {
500
520
  function warnIfTrailColorMismatch(trailColor, trailStyle) {
501
521
  if (trailStyle === "default" && Array.isArray(trailColor)) {
502
522
  console.warn(
503
- '[sarmal] trailColor is an array but trailStyle is "default"; only the first color will be used. Pass a gradient trailStyle to use the whole palette.'
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.',
504
524
  );
505
525
  return;
506
526
  }
507
527
  if (trailStyle !== "default" && typeof trailColor === "string") {
508
528
  console.warn(
509
- `[sarmal] trailColor is a single color but trailStyle is "${trailStyle}"; the trail will render as a solid color. Pass an array of hex colors to use a real gradient.`
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.`,
510
530
  );
511
531
  }
512
532
  }
@@ -516,7 +536,7 @@ var getHeadDotRadius = (w, h) => Math.max(1, 3 * Math.sqrt(Math.min(w, h) / 160)
516
536
  var WHITE_HEX = "#ffffff";
517
537
  function hexToRgbComponents(hex) {
518
538
  const n = parseInt(hex.slice(1), 16);
519
- return `${n >> 16},${n >> 8 & 255},${n & 255}`;
539
+ return `${n >> 16},${(n >> 8) & 255},${n & 255}`;
520
540
  }
521
541
  function applyDprSizing(target, logicalWidth, logicalHeight, dpr) {
522
542
  target.style.width = `${logicalWidth}px`;
@@ -641,7 +661,7 @@ function createRenderer(options) {
641
661
  i,
642
662
  trailCount,
643
663
  toX,
644
- toY
664
+ toY,
645
665
  );
646
666
  if (trailStyle === "default") {
647
667
  ctx.fillStyle = `rgba(${trailSolidRgb},${opacity})`;
@@ -802,7 +822,7 @@ function createRenderer(options) {
802
822
  if (partial.trailColor !== void 0 || partial.trailStyle !== void 0) {
803
823
  warnIfTrailColorMismatch(trailColor, trailStyle);
804
824
  }
805
- }
825
+ },
806
826
  };
807
827
  if (shouldAutoStart) {
808
828
  instance.play();
@@ -811,10 +831,12 @@ function createRenderer(options) {
811
831
  }
812
832
 
813
833
  // src/renderer-svg.ts
814
- var MAX_TRAIL_SEGMENTS = 200;
815
834
  var EMPTY_PARAMS2 = {};
835
+ var HIGH_TRAIL_LENGTH_THRESHOLD = 5e3;
816
836
  function pointsToPathString(pts, scale, offsetX, offsetY) {
817
- if (pts.length < 2) return "";
837
+ if (pts.length < 2) {
838
+ return "";
839
+ }
818
840
  const px = (p) => (p.x * scale + offsetX).toFixed(2);
819
841
  const py = (p) => (p.y * scale + offsetY).toFixed(2);
820
842
  let d = `M${px(pts[0])} ${py(pts[0])}`;
@@ -828,7 +850,7 @@ function sampleCurveSkeleton(curveDef) {
828
850
  const samples = Math.ceil(period * 50);
829
851
  const pts = Array.from({ length: samples });
830
852
  for (let i = 0; i < samples; i++) {
831
- const t = i / (samples - 1) * period;
853
+ const t = (i / (samples - 1)) * period;
832
854
  pts[i] = curveDef.skeletonFn ? curveDef.skeletonFn(t) : curveDef.fn(t, 0, EMPTY_PARAMS2);
833
855
  }
834
856
  return pts;
@@ -838,6 +860,12 @@ function el(tag) {
838
860
  }
839
861
  function createSVGRenderer(options) {
840
862
  const { container, engine } = options;
863
+ const poolSize = engine.trailLength;
864
+ if (poolSize > HIGH_TRAIL_LENGTH_THRESHOLD) {
865
+ console.warn(
866
+ `[sarmal] High trailLength in SVG renderer (${poolSize}). Consider using the canvas renderer for long trails.`,
867
+ );
868
+ }
841
869
  let trailStyle = options.trailStyle ?? "default";
842
870
  let trailColor = options.trailColor ?? "#ffffff";
843
871
  let skeletonColor = options.skeletonColor ?? "#ffffff";
@@ -847,62 +875,61 @@ function createSVGRenderer(options) {
847
875
  let trailPalette = resolveTrailPalette(trailColor);
848
876
  const ariaLabel = options.ariaLabel ?? "Loading";
849
877
  warnIfTrailColorMismatch(trailColor, trailStyle);
850
- const htmlContainer = container;
851
- const width = htmlContainer.offsetWidth || 200;
852
- const height = htmlContainer.offsetHeight || 200;
853
- const headRadius = options.headRadius ?? getHeadDotRadius(width, height);
854
- const svg = el("svg");
855
- svg.setAttribute("width", String(width));
856
- svg.setAttribute("height", String(height));
857
- svg.setAttribute("viewBox", `0 0 ${width} ${height}`);
858
- svg.setAttribute("role", "img");
859
- svg.setAttribute("aria-label", ariaLabel);
878
+ const viewSize = 100;
879
+ const headRadius = options.headRadius ?? 1.5;
880
+ const svgTrailMinWidth = 0.25;
881
+ const svgTrailMaxWidth = 1.25;
882
+ const svgSkeletonStrokeWidth = "0.75";
883
+ container.setAttribute("viewBox", `0 0 ${viewSize} ${viewSize}`);
884
+ container.setAttribute("role", "img");
885
+ container.setAttribute("aria-label", ariaLabel);
886
+ const group = el("g");
860
887
  const titleEl = el("title");
861
888
  titleEl.textContent = ariaLabel;
862
- svg.appendChild(titleEl);
889
+ group.appendChild(titleEl);
863
890
  const skeletonPath = el("path");
864
891
  skeletonPath.setAttribute("data-sarmal-role", "skeleton");
865
892
  skeletonPath.setAttribute("fill", "none");
866
893
  skeletonPath.setAttribute("stroke", skeletonColor);
867
894
  skeletonPath.setAttribute("stroke-opacity", String(DEFAULT_SKELETON_OPACITY));
868
- skeletonPath.setAttribute("stroke-width", "1.5");
895
+ skeletonPath.setAttribute("stroke-width", svgSkeletonStrokeWidth);
869
896
  if (skeletonColor === "transparent") {
870
897
  skeletonPath.setAttribute("visibility", "hidden");
871
898
  }
872
- svg.appendChild(skeletonPath);
899
+ group.appendChild(skeletonPath);
873
900
  const skeletonPathA = el("path");
874
901
  skeletonPathA.setAttribute("fill", "none");
875
902
  skeletonPathA.setAttribute("stroke", skeletonColor);
876
- skeletonPathA.setAttribute("stroke-width", "1.5");
903
+ skeletonPathA.setAttribute("stroke-width", svgSkeletonStrokeWidth);
877
904
  skeletonPathA.setAttribute("visibility", "hidden");
878
- svg.appendChild(skeletonPathA);
905
+ group.appendChild(skeletonPathA);
879
906
  const skeletonPathB = el("path");
880
907
  skeletonPathB.setAttribute("fill", "none");
881
908
  skeletonPathB.setAttribute("stroke", skeletonColor);
882
- skeletonPathB.setAttribute("stroke-width", "1.5");
909
+ skeletonPathB.setAttribute("stroke-width", svgSkeletonStrokeWidth);
883
910
  skeletonPathB.setAttribute("visibility", "hidden");
884
- svg.appendChild(skeletonPathB);
911
+ group.appendChild(skeletonPathB);
885
912
  let morphPathABuilt = "";
886
913
  let morphPathBBuilt = "";
887
914
  const trailPaths = [];
888
- for (let i = 0; i < MAX_TRAIL_SEGMENTS; i++) {
915
+ for (let i = 0; i < poolSize; i++) {
889
916
  const path = el("path");
890
917
  path.setAttribute("fill", trailSolid);
891
- svg.appendChild(path);
918
+ group.appendChild(path);
892
919
  trailPaths.push(path);
893
920
  }
894
921
  const headCircle = el("circle");
895
922
  headCircle.setAttribute("data-sarmal-role", "head");
896
923
  headCircle.setAttribute("fill", headColor);
897
924
  headCircle.setAttribute("r", String(headRadius));
898
- svg.appendChild(headCircle);
899
- container.appendChild(svg);
925
+ group.appendChild(headCircle);
926
+ container.appendChild(group);
900
927
  let gradientAnimTime = 0;
901
928
  let scale = 1;
902
929
  let offsetX = 0;
903
930
  let offsetY = 0;
904
931
  function applyBoundaries(skeleton2) {
905
- const b = computeBoundaries(skeleton2, width, height);
932
+ const b = computeBoundaries(skeleton2, viewSize, viewSize);
906
933
  if (b) {
907
934
  scale = b.scale;
908
935
  offsetX = b.offsetX;
@@ -930,24 +957,24 @@ function createSVGRenderer(options) {
930
957
  }
931
958
  return;
932
959
  }
933
- const startIdx = Math.max(0, trailCount - 1 - MAX_TRAIL_SEGMENTS);
934
- const drawnCount = trailCount - 1 - startIdx;
935
- for (let i = startIdx; i < trailCount - 1; i++) {
936
- const j = i - startIdx;
960
+ const drawnCount = trailCount - 1;
961
+ for (let i = 0; i < drawnCount; i++) {
937
962
  const { l0x, l0y, r0x, r0y, l1x, l1y, r1x, r1y, opacity, progress } = computeTrailQuad(
938
963
  trail,
939
964
  i,
940
965
  trailCount,
941
966
  px,
942
- py
967
+ py,
968
+ svgTrailMinWidth,
969
+ svgTrailMaxWidth,
943
970
  );
944
971
  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`;
945
- trailPaths[j].setAttribute("d", d);
946
- trailPaths[j].setAttribute("fill-opacity", opacity.toFixed(3));
972
+ trailPaths[i].setAttribute("d", d);
973
+ trailPaths[i].setAttribute("fill-opacity", opacity.toFixed(3));
947
974
  if (trailStyle !== "default") {
948
975
  const timeOffset = trailStyle === "gradient-animated" ? gradientAnimTime * 5e-4 : 0;
949
976
  const { r, g, b } = getPaletteColor(trailPalette, progress, timeOffset);
950
- trailPaths[j].setAttribute("fill", `rgb(${r},${g},${b})`);
977
+ trailPaths[i].setAttribute("fill", `rgb(${r},${g},${b})`);
951
978
  }
952
979
  }
953
980
  for (let i = drawnCount; i < trailPaths.length; i++) {
@@ -966,7 +993,8 @@ function createSVGRenderer(options) {
966
993
  }
967
994
  let animationId = null;
968
995
  let lastTime = 0;
969
- const prefersReducedMotion = typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
996
+ const prefersReducedMotion =
997
+ typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
970
998
  let morphResolve = null;
971
999
  let morphReject = null;
972
1000
  let morphDurationMs = DEFAULT_MORPH_DURATION_MS;
@@ -984,7 +1012,7 @@ function createSVGRenderer(options) {
984
1012
  skeletonPathA.setAttribute("visibility", "visible");
985
1013
  skeletonPathA.setAttribute(
986
1014
  "stroke-opacity",
987
- String((1 - morphAlpha) * DEFAULT_SKELETON_OPACITY)
1015
+ String((1 - morphAlpha) * DEFAULT_SKELETON_OPACITY),
988
1016
  );
989
1017
  }
990
1018
  if (morphPathBBuilt) {
@@ -1061,7 +1089,7 @@ function createSVGRenderer(options) {
1061
1089
  morphResolve = null;
1062
1090
  morphReject = null;
1063
1091
  }
1064
- svg.remove();
1092
+ group.remove();
1065
1093
  },
1066
1094
  ...enginePassthroughs(engine),
1067
1095
  morphTo(target, options2) {
@@ -1133,7 +1161,7 @@ function createSVGRenderer(options) {
1133
1161
  if (partial.trailColor !== void 0 || partial.trailStyle !== void 0) {
1134
1162
  warnIfTrailColorMismatch(trailColor, trailStyle);
1135
1163
  }
1136
- }
1164
+ },
1137
1165
  };
1138
1166
  if (shouldAutoStart) {
1139
1167
  instance.play();
@@ -1149,19 +1177,22 @@ function createSarmalSVG(container, curveDef, options) {
1149
1177
  // src/curves/artemis2.ts
1150
1178
  var TWO_PI2 = Math.PI * 2;
1151
1179
  function artemis2Fn(t, _time, _params) {
1152
- const a = 0.35, b = 0.15, ox = 0.175;
1153
- const s = Math.sin(t), c = Math.cos(t);
1180
+ const a = 0.35,
1181
+ b = 0.15,
1182
+ ox = 0.175;
1183
+ const s = Math.sin(t),
1184
+ c = Math.cos(t);
1154
1185
  const denom = 1 + s * s;
1155
1186
  return {
1156
- x: c * (1 + a * c) / denom - ox,
1157
- y: s * c * (1 + b * c) / denom
1187
+ x: (c * (1 + a * c)) / denom - ox,
1188
+ y: (s * c * (1 + b * c)) / denom,
1158
1189
  };
1159
1190
  }
1160
1191
  var artemis2 = {
1161
1192
  name: "Artemis II",
1162
1193
  fn: artemis2Fn,
1163
1194
  period: TWO_PI2,
1164
- speed: 0.7
1195
+ speed: 0.7,
1165
1196
  };
1166
1197
 
1167
1198
  // src/curves/astroid.ts
@@ -1171,14 +1202,14 @@ function astroidFn(t, _time, _params) {
1171
1202
  const s = Math.sin(t);
1172
1203
  return {
1173
1204
  x: c * c * c,
1174
- y: s * s * s
1205
+ y: s * s * s,
1175
1206
  };
1176
1207
  }
1177
1208
  var astroid = {
1178
1209
  name: "Astroid",
1179
1210
  fn: astroidFn,
1180
1211
  period: TWO_PI3,
1181
- speed: 1.1
1212
+ speed: 1.1,
1182
1213
  };
1183
1214
 
1184
1215
  // src/curves/deltoid.ts
@@ -1186,14 +1217,14 @@ var TWO_PI4 = Math.PI * 2;
1186
1217
  function deltoidFn(t, _time, _params) {
1187
1218
  return {
1188
1219
  x: 2 * Math.cos(t) + Math.cos(2 * t),
1189
- y: 2 * Math.sin(t) - Math.sin(2 * t)
1220
+ y: 2 * Math.sin(t) - Math.sin(2 * t),
1190
1221
  };
1191
1222
  }
1192
1223
  var deltoid = {
1193
1224
  name: "Deltoid",
1194
1225
  fn: deltoidFn,
1195
1226
  period: TWO_PI4,
1196
- speed: 0.9
1227
+ speed: 0.9,
1197
1228
  };
1198
1229
 
1199
1230
  // src/curves/epicycloid3.ts
@@ -1201,14 +1232,14 @@ var TWO_PI5 = Math.PI * 2;
1201
1232
  function epicycloid3Fn(t, _time, _params) {
1202
1233
  return {
1203
1234
  x: 4 * Math.cos(t) - Math.cos(4 * t),
1204
- y: 4 * Math.sin(t) - Math.sin(4 * t)
1235
+ y: 4 * Math.sin(t) - Math.sin(4 * t),
1205
1236
  };
1206
1237
  }
1207
1238
  var epicycloid3 = {
1208
1239
  name: "Epicycloid (n=3)",
1209
1240
  fn: epicycloid3Fn,
1210
1241
  period: TWO_PI5,
1211
- speed: 0.75
1242
+ speed: 0.75,
1212
1243
  };
1213
1244
 
1214
1245
  // src/curves/epitrochoid7.ts
@@ -1217,14 +1248,14 @@ function epitrochoid7Fn(t, _time, _params) {
1217
1248
  const d = 1 + 0.55 * Math.sin(t * 0.5);
1218
1249
  return {
1219
1250
  x: 7 * Math.cos(t) - d * Math.cos(7 * t),
1220
- y: 7 * Math.sin(t) - d * Math.sin(7 * t)
1251
+ y: 7 * Math.sin(t) - d * Math.sin(7 * t),
1221
1252
  };
1222
1253
  }
1223
1254
  function epitrochoid7SkeletonFn(t) {
1224
1255
  const d = 1.275;
1225
1256
  return {
1226
1257
  x: 7 * Math.cos(t) - d * Math.cos(7 * t),
1227
- y: 7 * Math.sin(t) - d * Math.sin(7 * t)
1258
+ y: 7 * Math.sin(t) - d * Math.sin(7 * t),
1228
1259
  };
1229
1260
  }
1230
1261
  var epitrochoid7 = {
@@ -1232,7 +1263,7 @@ var epitrochoid7 = {
1232
1263
  fn: epitrochoid7Fn,
1233
1264
  period: TWO_PI6,
1234
1265
  speed: 1.4,
1235
- skeletonFn: epitrochoid7SkeletonFn
1266
+ skeletonFn: epitrochoid7SkeletonFn,
1236
1267
  };
1237
1268
 
1238
1269
  // src/curves/lissajous32.ts
@@ -1241,7 +1272,7 @@ function lissajous32Fn(t, time, _params) {
1241
1272
  const phi = time * 0.45;
1242
1273
  return {
1243
1274
  x: Math.sin(3 * t + phi),
1244
- y: Math.sin(2 * t)
1275
+ y: Math.sin(2 * t),
1245
1276
  };
1246
1277
  }
1247
1278
  var lissajous32 = {
@@ -1249,7 +1280,7 @@ var lissajous32 = {
1249
1280
  fn: lissajous32Fn,
1250
1281
  period: TWO_PI7,
1251
1282
  speed: 2,
1252
- skeleton: "live"
1283
+ skeleton: "live",
1253
1284
  };
1254
1285
 
1255
1286
  // src/curves/lissajous43.ts
@@ -1258,7 +1289,7 @@ function lissajous43Fn(t, time, _params) {
1258
1289
  const phi = time * 0.38;
1259
1290
  return {
1260
1291
  x: Math.sin(4 * t + phi),
1261
- y: Math.sin(3 * t)
1292
+ y: Math.sin(3 * t),
1262
1293
  };
1263
1294
  }
1264
1295
  var lissajous43 = {
@@ -1266,17 +1297,18 @@ var lissajous43 = {
1266
1297
  fn: lissajous43Fn,
1267
1298
  period: TWO_PI8,
1268
1299
  speed: 1.8,
1269
- skeleton: "live"
1300
+ skeleton: "live",
1270
1301
  };
1271
1302
 
1272
1303
  // src/curves/lame.ts
1273
1304
  var TWO_PI9 = Math.PI * 2;
1274
1305
  function lameFn(t, time, _params) {
1275
1306
  const p = 1.75 + 1.25 * Math.sin(time * 0.48);
1276
- const c = Math.cos(t), s = Math.sin(t);
1307
+ const c = Math.cos(t),
1308
+ s = Math.sin(t);
1277
1309
  return {
1278
1310
  x: Math.sign(c) * Math.pow(Math.abs(c), p),
1279
- y: Math.sign(s) * Math.pow(Math.abs(s), p)
1311
+ y: Math.sign(s) * Math.pow(Math.abs(s), p),
1280
1312
  };
1281
1313
  }
1282
1314
  var lame = {
@@ -1284,7 +1316,7 @@ var lame = {
1284
1316
  fn: lameFn,
1285
1317
  period: TWO_PI9,
1286
1318
  speed: 1,
1287
- skeleton: "live"
1319
+ skeleton: "live",
1288
1320
  };
1289
1321
 
1290
1322
  // src/curves/rose3.ts
@@ -1293,14 +1325,14 @@ function rose3Fn(t, _time, _params) {
1293
1325
  const r = Math.cos(3 * t);
1294
1326
  return {
1295
1327
  x: r * Math.cos(t),
1296
- y: r * Math.sin(t)
1328
+ y: r * Math.sin(t),
1297
1329
  };
1298
1330
  }
1299
1331
  var rose3 = {
1300
1332
  name: "Rose (n=3)",
1301
1333
  fn: rose3Fn,
1302
1334
  period: TWO_PI10,
1303
- speed: 1.15
1335
+ speed: 1.15,
1304
1336
  };
1305
1337
 
1306
1338
  // src/curves/rose5.ts
@@ -1309,78 +1341,87 @@ function rose5Fn(t, _time, _params) {
1309
1341
  const r = Math.cos(5 * t);
1310
1342
  return {
1311
1343
  x: r * Math.cos(t),
1312
- y: r * Math.sin(t)
1344
+ y: r * Math.sin(t),
1313
1345
  };
1314
1346
  }
1315
1347
  var rose5 = {
1316
1348
  name: "Rose (n=5)",
1317
1349
  fn: rose5Fn,
1318
1350
  period: TWO_PI11,
1319
- speed: 1
1351
+ speed: 1,
1320
1352
  };
1321
1353
 
1322
1354
  // src/curves/rose52.ts
1323
1355
  var FOUR_PI = Math.PI * 4;
1324
1356
  function rose52Fn(t, _time, _params) {
1325
- const r = Math.cos(5 / 2 * t);
1357
+ const r = Math.cos((5 / 2) * t);
1326
1358
  return {
1327
1359
  x: r * Math.cos(t),
1328
- y: r * Math.sin(t)
1360
+ y: r * Math.sin(t),
1329
1361
  };
1330
1362
  }
1331
1363
  var rose52 = {
1332
1364
  name: "Rose (n=5/2)",
1333
1365
  fn: rose52Fn,
1334
1366
  period: FOUR_PI,
1335
- speed: 0.8
1367
+ speed: 0.8,
1336
1368
  };
1337
1369
 
1338
1370
  // src/curves/star.ts
1339
1371
  var TWO_PI12 = Math.PI * 2;
1340
1372
  function starFn(t, _time, _params) {
1341
- 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));
1373
+ const r =
1374
+ Math.abs(Math.cos((5 / 2) * t)) +
1375
+ 0.35 * Math.abs(Math.cos((15 / 2) * t)) +
1376
+ 0.15 * Math.abs(Math.cos((25 / 2) * t));
1342
1377
  return {
1343
1378
  x: r * Math.cos(t),
1344
- y: r * Math.sin(t)
1379
+ y: r * Math.sin(t),
1345
1380
  };
1346
1381
  }
1347
1382
  var star = {
1348
1383
  name: "Star",
1349
1384
  fn: starFn,
1350
1385
  period: TWO_PI12,
1351
- speed: 1
1386
+ speed: 1,
1352
1387
  };
1353
1388
 
1354
1389
  // src/curves/star4.ts
1355
1390
  var TWO_PI13 = Math.PI * 2;
1356
1391
  function star4Fn(t, _time, _params) {
1357
- const r = Math.abs(Math.cos(2 * t)) + 0.35 * Math.abs(Math.cos(6 * t)) + 0.15 * Math.abs(Math.cos(10 * t));
1392
+ const r =
1393
+ Math.abs(Math.cos(2 * t)) +
1394
+ 0.35 * Math.abs(Math.cos(6 * t)) +
1395
+ 0.15 * Math.abs(Math.cos(10 * t));
1358
1396
  return {
1359
1397
  x: r * Math.cos(t),
1360
- y: r * Math.sin(t)
1398
+ y: r * Math.sin(t),
1361
1399
  };
1362
1400
  }
1363
1401
  var star4 = {
1364
1402
  name: "Star (4-arm)",
1365
1403
  fn: star4Fn,
1366
1404
  period: TWO_PI13,
1367
- speed: 1
1405
+ speed: 1,
1368
1406
  };
1369
1407
 
1370
1408
  // src/curves/star7.ts
1371
1409
  var TWO_PI14 = Math.PI * 2;
1372
1410
  function star7Fn(t, _time, _params) {
1373
- 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));
1411
+ const r =
1412
+ Math.abs(Math.cos((7 / 2) * t)) +
1413
+ 0.35 * Math.abs(Math.cos((21 / 2) * t)) +
1414
+ 0.15 * Math.abs(Math.cos((35 / 2) * t));
1374
1415
  return {
1375
1416
  x: r * Math.cos(t),
1376
- y: r * Math.sin(t)
1417
+ y: r * Math.sin(t),
1377
1418
  };
1378
1419
  }
1379
1420
  var star7 = {
1380
1421
  name: "Star (7-arm)",
1381
1422
  fn: star7Fn,
1382
1423
  period: TWO_PI14,
1383
- speed: 1
1424
+ speed: 1,
1384
1425
  };
1385
1426
 
1386
1427
  // src/curves/index.ts
@@ -1398,7 +1439,7 @@ var curves = {
1398
1439
  lissajous32,
1399
1440
  lissajous43,
1400
1441
  epicycloid3,
1401
- lame
1442
+ lame,
1402
1443
  };
1403
1444
 
1404
1445
  // src/index.ts
@@ -1408,6 +1449,24 @@ function createSarmal(canvas, curveDef, options) {
1408
1449
  return createRenderer({ canvas, engine, ...rendererOpts });
1409
1450
  }
1410
1451
 
1411
- export { artemis2, astroid, createEngine, createRenderer, createSVGRenderer, createSarmal, createSarmalSVG, curves, deltoid, epicycloid3, epitrochoid7, lame, lissajous32, lissajous43, palettes, rose3, rose5 };
1452
+ export {
1453
+ artemis2,
1454
+ astroid,
1455
+ createEngine,
1456
+ createRenderer,
1457
+ createSVGRenderer,
1458
+ createSarmal,
1459
+ createSarmalSVG,
1460
+ curves,
1461
+ deltoid,
1462
+ epicycloid3,
1463
+ epitrochoid7,
1464
+ lame,
1465
+ lissajous32,
1466
+ lissajous43,
1467
+ palettes,
1468
+ rose3,
1469
+ rose5,
1470
+ };
1471
+ //# sourceMappingURL=index.js.map
1412
1472
  //# sourceMappingURL=index.js.map
1413
- //# sourceMappingURL=index.js.map