@sarmal/core 0.14.0 → 0.15.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 (61) hide show
  1. package/dist/auto-init.cjs +239 -115
  2. package/dist/auto-init.cjs.map +1 -1
  3. package/dist/auto-init.d.cts +1 -2
  4. package/dist/auto-init.d.ts +1 -2
  5. package/dist/auto-init.js +238 -114
  6. package/dist/auto-init.js.map +1 -1
  7. package/dist/curves/artemis2.cjs +10 -7
  8. package/dist/curves/artemis2.d.cts +1 -1
  9. package/dist/curves/artemis2.d.ts +1 -1
  10. package/dist/curves/artemis2.js +9 -6
  11. package/dist/curves/astroid.cjs +4 -4
  12. package/dist/curves/astroid.d.cts +1 -1
  13. package/dist/curves/astroid.d.ts +1 -1
  14. package/dist/curves/astroid.js +3 -3
  15. package/dist/curves/deltoid.cjs +4 -4
  16. package/dist/curves/deltoid.d.cts +1 -1
  17. package/dist/curves/deltoid.d.ts +1 -1
  18. package/dist/curves/deltoid.js +3 -3
  19. package/dist/curves/epicycloid3.cjs +4 -4
  20. package/dist/curves/epicycloid3.d.cts +1 -1
  21. package/dist/curves/epicycloid3.d.ts +1 -1
  22. package/dist/curves/epicycloid3.js +3 -3
  23. package/dist/curves/epitrochoid7.cjs +5 -5
  24. package/dist/curves/epitrochoid7.d.cts +1 -1
  25. package/dist/curves/epitrochoid7.d.ts +1 -1
  26. package/dist/curves/epitrochoid7.js +4 -4
  27. package/dist/curves/index.cjs +32 -28
  28. package/dist/curves/index.d.cts +21 -21
  29. package/dist/curves/index.d.ts +21 -21
  30. package/dist/curves/index.js +44 -28
  31. package/dist/curves/lame.cjs +6 -5
  32. package/dist/curves/lame.d.cts +1 -1
  33. package/dist/curves/lame.d.ts +1 -1
  34. package/dist/curves/lame.js +5 -4
  35. package/dist/curves/lissajous32.cjs +4 -4
  36. package/dist/curves/lissajous32.d.cts +1 -1
  37. package/dist/curves/lissajous32.d.ts +1 -1
  38. package/dist/curves/lissajous32.js +3 -3
  39. package/dist/curves/lissajous43.cjs +4 -4
  40. package/dist/curves/lissajous43.d.cts +1 -1
  41. package/dist/curves/lissajous43.d.ts +1 -1
  42. package/dist/curves/lissajous43.js +3 -3
  43. package/dist/curves/rose3.cjs +4 -4
  44. package/dist/curves/rose3.d.cts +1 -1
  45. package/dist/curves/rose3.d.ts +1 -1
  46. package/dist/curves/rose3.js +3 -3
  47. package/dist/curves/rose5.cjs +4 -4
  48. package/dist/curves/rose5.d.cts +1 -1
  49. package/dist/curves/rose5.d.ts +1 -1
  50. package/dist/curves/rose5.js +3 -3
  51. package/dist/index.cjs +303 -123
  52. package/dist/index.cjs.map +1 -1
  53. package/dist/index.d.cts +73 -23
  54. package/dist/index.d.ts +73 -23
  55. package/dist/index.js +320 -123
  56. package/dist/index.js.map +1 -1
  57. package/dist/types-BL9HhEmk.d.cts +312 -0
  58. package/dist/types-BL9HhEmk.d.ts +312 -0
  59. package/package.json +1 -1
  60. package/dist/types-BQosOzlf.d.cts +0 -276
  61. package/dist/types-BQosOzlf.d.ts +0 -276
@@ -1,4 +1,4 @@
1
- 'use strict';
1
+ "use strict";
2
2
 
3
3
  // src/engine.ts
4
4
  var TWO_PI = Math.PI * 2;
@@ -63,13 +63,13 @@ function resolveCurve(curveDef) {
63
63
  period,
64
64
  speed,
65
65
  skeleton: curveDef.skeleton,
66
- skeletonFn: curveDef.skeletonFn
66
+ skeletonFn: curveDef.skeletonFn,
67
67
  };
68
68
  }
69
69
  function createEngine(curveDef, trailLength = 120) {
70
70
  if (!Number.isFinite(trailLength) || trailLength <= 0) {
71
71
  throw new RangeError(
72
- `[sarmal] trailLength must be a positive finite number, got ${trailLength}`
72
+ `[sarmal] trailLength must be a positive finite number, got ${trailLength}`,
73
73
  );
74
74
  }
75
75
  let curve = resolveCurve(curveDef);
@@ -110,7 +110,7 @@ function createEngine(curveDef, trailLength = 120) {
110
110
  actualTime += deltaTime;
111
111
  if (morphCurveB !== null && _morphAlpha !== null) {
112
112
  const a = curve.fn(t, actualTime, EMPTY_PARAMS);
113
- const tB = _morphStrategy === "normalized" ? t / curve.period * morphCurveB.period : t;
113
+ const tB = _morphStrategy === "normalized" ? (t / curve.period) * morphCurveB.period : t;
114
114
  const b = morphCurveB.fn(tB, actualTime, EMPTY_PARAMS);
115
115
  trail.push(a.x + (b.x - a.x) * _morphAlpha, a.y + (b.y - a.y) * _morphAlpha);
116
116
  } else {
@@ -134,14 +134,14 @@ function createEngine(curveDef, trailLength = 120) {
134
134
  trail.clear();
135
135
  },
136
136
  jump(newT, { clearTrail = false } = {}) {
137
- t = (newT % curve.period + curve.period) % curve.period;
137
+ t = ((newT % curve.period) + curve.period) % curve.period;
138
138
  if (clearTrail) {
139
139
  trail.clear();
140
140
  }
141
141
  },
142
142
  seek(targetT, { wrap = false, step = curve.period / trailLength } = {}) {
143
143
  const advance = curve.speed * step;
144
- const target = (targetT % curve.period + curve.period) % curve.period;
144
+ const target = ((targetT % curve.period) + curve.period) % curve.period;
145
145
  const targetTime = target / curve.speed;
146
146
  t = target;
147
147
  actualTime = targetTime;
@@ -150,7 +150,7 @@ function createEngine(curveDef, trailLength = 120) {
150
150
  const count = wrap ? trailLength : Math.min(trailLength, pointsFromStart);
151
151
  for (let i = count - 1; i >= 0; i--) {
152
152
  const sampleT = target - i * advance;
153
- const wrappedT = (sampleT % curve.period + curve.period) % curve.period;
153
+ const wrappedT = ((sampleT % curve.period) + curve.period) % curve.period;
154
154
  const time = targetTime - i * step;
155
155
  const point = curve.fn(wrappedT, time, EMPTY_PARAMS);
156
156
  trail.push(point.x, point.y);
@@ -167,13 +167,16 @@ function createEngine(curveDef, trailLength = 120) {
167
167
  ...frozenB,
168
168
  fn: (sampleT, time, params) => {
169
169
  const a = frozenA.fn(sampleT, time, params);
170
- const tB = frozenStrategy === "normalized" ? sampleT / frozenA.period * frozenB.period : sampleT;
170
+ const tB =
171
+ frozenStrategy === "normalized"
172
+ ? (sampleT / frozenA.period) * frozenB.period
173
+ : sampleT;
171
174
  const b = frozenB.fn(tB, time, params);
172
175
  return {
173
176
  x: a.x + (b.x - a.x) * frozenAlpha,
174
- y: a.y + (b.y - a.y) * frozenAlpha
177
+ y: a.y + (b.y - a.y) * frozenAlpha,
175
178
  };
176
- }
179
+ },
177
180
  };
178
181
  }
179
182
  _morphStrategy = strategy;
@@ -186,7 +189,7 @@ function createEngine(curveDef, trailLength = 120) {
186
189
  completeMorph() {
187
190
  if (morphCurveB !== null) {
188
191
  if (_morphStrategy === "normalized" && curve.period !== morphCurveB.period) {
189
- t = t / curve.period * morphCurveB.period;
192
+ t = (t / curve.period) * morphCurveB.period;
190
193
  }
191
194
  curve = morphCurveB;
192
195
  }
@@ -198,19 +201,22 @@ function createEngine(curveDef, trailLength = 120) {
198
201
  const points = new Array(steps);
199
202
  if (morphCurveB !== null && _morphAlpha !== null) {
200
203
  for (let i = 0; i < steps; i++) {
201
- const sampleT = i / (steps - 1) * curve.period;
204
+ const sampleT = (i / (steps - 1)) * curve.period;
202
205
  const a = sampleSkeleton(curve, sampleT);
203
- const tB = _morphStrategy === "normalized" ? sampleT / curve.period * morphCurveB.period : sampleT;
206
+ const tB =
207
+ _morphStrategy === "normalized"
208
+ ? (sampleT / curve.period) * morphCurveB.period
209
+ : sampleT;
204
210
  const b = sampleSkeleton(morphCurveB, tB);
205
211
  points[i] = {
206
212
  x: a.x + (b.x - a.x) * _morphAlpha,
207
- y: a.y + (b.y - a.y) * _morphAlpha
213
+ y: a.y + (b.y - a.y) * _morphAlpha,
208
214
  };
209
215
  }
210
216
  return points;
211
217
  }
212
218
  for (let i = 0; i < steps; i++) {
213
- const sampleT = i / (steps - 1) * curve.period;
219
+ const sampleT = (i / (steps - 1)) * curve.period;
214
220
  points[i] = sampleSkeleton(curve, sampleT);
215
221
  }
216
222
  return points;
@@ -252,7 +258,7 @@ function createEngine(curveDef, trailLength = 120) {
252
258
  _speedTransition.reject(new Error("Speed transition cancelled"));
253
259
  _speedTransition = null;
254
260
  }
255
- }
261
+ },
256
262
  };
257
263
  }
258
264
 
@@ -315,13 +321,16 @@ function computeTrailQuad(trail, i, trailCount, toX, toY) {
315
321
  r1x: nx - n1.x * w1,
316
322
  r1y: ny - n1.y * w1,
317
323
  opacity,
318
- progress
324
+ progress,
319
325
  };
320
326
  }
321
327
  function computeBoundaries(pts, logicalWidth, logicalHeight) {
322
328
  if (pts.length === 0) return null;
323
329
  const first = pts[0];
324
- let minX = first.x, maxX = first.x, minY = first.y, maxY = first.y;
330
+ let minX = first.x,
331
+ maxX = first.x,
332
+ minY = first.y,
333
+ maxY = first.y;
325
334
  for (const p of pts) {
326
335
  if (p.x < minX) {
327
336
  minX = p.x;
@@ -340,7 +349,7 @@ function computeBoundaries(pts, logicalWidth, logicalHeight) {
340
349
  const h = maxY - minY;
341
350
  if (w === 0 && h === 0) {
342
351
  throw new Error(
343
- "[sarmal] Degenerate curve: all skeleton points are identical. Check that your curve fn returns distinct points for different values of t."
352
+ "[sarmal] Degenerate curve: all skeleton points are identical. Check that your curve fn returns distinct points for different values of t.",
344
353
  );
345
354
  }
346
355
  const scaleXProportional = logicalWidth / (w * (1 + FIT_PADDING * 2));
@@ -351,12 +360,12 @@ function computeBoundaries(pts, logicalWidth, logicalHeight) {
351
360
  scaleXProportional,
352
361
  scaleYProportional,
353
362
  scaleXMinPadding,
354
- scaleYMinPadding
363
+ scaleYMinPadding,
355
364
  );
356
365
  return {
357
366
  scale,
358
367
  offsetX: (logicalWidth - w * scale) / 2 - minX * scale,
359
- offsetY: (logicalHeight - h * scale) / 2 - minY * scale
368
+ offsetY: (logicalHeight - h * scale) / 2 - minY * scale,
360
369
  };
361
370
  }
362
371
  function enginePassthroughs(engine) {
@@ -366,33 +375,17 @@ function enginePassthroughs(engine) {
366
375
  setSpeed: engine.setSpeed,
367
376
  getSpeed: engine.getSpeed,
368
377
  resetSpeed: engine.resetSpeed,
369
- setSpeedOver: engine.setSpeedOver
378
+ setSpeedOver: engine.setSpeedOver,
370
379
  };
371
380
  }
372
- var GRADIENT = {
373
- bard: ["#a855f7", "#3b82f6", "#14b8a6", "#ec4899"],
374
- sunset: ["#f97316", "#dc2626", "#9333ea", "#f472b6"],
375
- ocean: ["#1e3a8a", "#06b6d4", "#22d3ee", "#e0f2fe"],
376
- ice: ["#1e3a8a", "#67e8f9"],
377
- fire: ["#7f1d1d", "#fbbf24"],
378
- forest: ["#14532d", "#86efac"]
379
- };
380
- var PRESETS = {
381
- bard: GRADIENT.bard,
382
- sunset: GRADIENT.sunset,
383
- ocean: GRADIENT.ocean,
384
- ice: GRADIENT.ice,
385
- fire: GRADIENT.fire,
386
- forest: GRADIENT.forest
387
- };
388
381
  function hexToRgb(hex) {
389
382
  const n = parseInt(hex.slice(1), 16);
390
- return { r: n >> 16, g: n >> 8 & 255, b: n & 255 };
383
+ return { r: n >> 16, g: (n >> 8) & 255, b: n & 255 };
391
384
  }
392
385
  var lerpRgb = (a, b, t) => ({
393
386
  r: Math.round(a.r + (b.r - a.r) * t),
394
387
  g: Math.round(a.g + (b.g - a.g) * t),
395
- b: Math.round(a.b + (b.b - a.b) * t)
388
+ b: Math.round(a.b + (b.b - a.b) * t),
396
389
  });
397
390
  function getPaletteColor(palette, position, timeOffset = 0) {
398
391
  if (palette.length === 0) {
@@ -409,21 +402,123 @@ function getPaletteColor(palette, position, timeOffset = 0) {
409
402
  const c2 = hexToRgb(palette[(idx + 1) % palette.length]);
410
403
  return lerpRgb(c1, c2, t);
411
404
  }
412
- function resolvePalette(palette, trailStyle) {
413
- if (Array.isArray(palette)) {
414
- return palette;
405
+ var HEX_COLOR_RE = /^#[0-9a-fA-F]{6}$/;
406
+ var TRAIL_STYLES = ["default", "gradient-static", "gradient-animated"];
407
+ var RENDER_OPTION_KEYS = /* @__PURE__ */ new Set([
408
+ "trailColor",
409
+ "headColor",
410
+ "skeletonColor",
411
+ "trailStyle",
412
+ ]);
413
+ function validateRenderOptions(partial) {
414
+ for (const key of Object.keys(partial)) {
415
+ if (!RENDER_OPTION_KEYS.has(key)) {
416
+ throw new TypeError(`[sarmal] setRenderOptions: unknown key "${key}"`);
417
+ }
418
+ }
419
+ if (partial.trailColor !== void 0) {
420
+ assertTrailColor(partial.trailColor);
421
+ }
422
+ if (partial.headColor !== void 0) {
423
+ assertHeadColor(partial.headColor);
424
+ }
425
+ if (partial.skeletonColor !== void 0) {
426
+ assertSkeletonColor(partial.skeletonColor);
415
427
  }
416
- if (palette && palette in PRESETS) {
417
- return PRESETS[palette];
428
+ if (partial.trailStyle !== void 0) {
429
+ assertTrailStyle(partial.trailStyle);
430
+ }
431
+ }
432
+ function assertTrailColor(value) {
433
+ if (typeof value === "string") {
434
+ if (!HEX_COLOR_RE.test(value)) {
435
+ throw new TypeError(
436
+ `[sarmal] setRenderOptions: trailColor must be a 6-digit hex string, got "${value}"`,
437
+ );
438
+ }
439
+ return;
440
+ }
441
+ if (Array.isArray(value)) {
442
+ if (value.length < 2) {
443
+ throw new RangeError(
444
+ `[sarmal] setRenderOptions: trailColor array must have at least 2 entries, got ${value.length}`,
445
+ );
446
+ }
447
+ for (let i = 0; i < value.length; i++) {
448
+ const entry = value[i];
449
+ if (typeof entry !== "string" || !HEX_COLOR_RE.test(entry)) {
450
+ throw new TypeError(
451
+ `[sarmal] setRenderOptions: trailColor[${i}] must be a 6-digit hex string, got ${JSON.stringify(entry)}`,
452
+ );
453
+ }
454
+ }
455
+ return;
456
+ }
457
+ throw new TypeError(
458
+ `[sarmal] setRenderOptions: trailColor must be a 6-digit hex string or an array of hex strings, got ${JSON.stringify(value)}`,
459
+ );
460
+ }
461
+ function assertHeadColor(value) {
462
+ if (value === null) {
463
+ return;
464
+ }
465
+ if (typeof value !== "string" || !HEX_COLOR_RE.test(value)) {
466
+ throw new TypeError(
467
+ `[sarmal] setRenderOptions: headColor must be a 6-digit hex string or null, got ${JSON.stringify(value)}`,
468
+ );
469
+ }
470
+ }
471
+ function assertSkeletonColor(value) {
472
+ if (value === "transparent") {
473
+ return;
474
+ }
475
+ if (typeof value !== "string" || !HEX_COLOR_RE.test(value)) {
476
+ throw new TypeError(
477
+ `[sarmal] setRenderOptions: skeletonColor must be a 6-digit hex string or "transparent", got ${JSON.stringify(value)}`,
478
+ );
479
+ }
480
+ }
481
+ function assertTrailStyle(value) {
482
+ if (!TRAIL_STYLES.includes(value)) {
483
+ throw new RangeError(
484
+ `[sarmal] setRenderOptions: trailStyle must be one of "default", "gradient-static", "gradient-animated", got ${JSON.stringify(value)}`,
485
+ );
486
+ }
487
+ }
488
+ function resolveTrailMainColor(trailColor) {
489
+ return typeof trailColor === "string" ? trailColor : trailColor[0];
490
+ }
491
+ function resolveTrailPalette(trailColor) {
492
+ return typeof trailColor === "string" ? [trailColor] : trailColor;
493
+ }
494
+ function resolveHeadColor(trailColor, trailStyle) {
495
+ if (trailStyle === "default") {
496
+ return resolveTrailMainColor(trailColor);
497
+ }
498
+ const palette = resolveTrailPalette(trailColor);
499
+ const last = palette[palette.length - 1];
500
+ const { r, g, b } = hexToRgb(last);
501
+ return `rgb(${r},${g},${b})`;
502
+ }
503
+ function warnIfTrailColorMismatch(trailColor, trailStyle) {
504
+ if (trailStyle === "default" && Array.isArray(trailColor)) {
505
+ console.warn(
506
+ '[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.',
507
+ );
508
+ return;
509
+ }
510
+ if (trailStyle !== "default" && typeof trailColor === "string") {
511
+ console.warn(
512
+ `[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.`,
513
+ );
418
514
  }
419
- return trailStyle === "gradient-animated" ? GRADIENT.bard : GRADIENT.ice;
420
515
  }
421
516
 
422
517
  // src/renderer.ts
423
- var DEFAULT_SKELETON_COLOR = "#ffffff";
518
+ var WHITE_HEX = "#ffffff";
424
519
  function hexToRgbComponents(hex) {
425
520
  const n = parseInt(hex.slice(1), 16);
426
- return `${n >> 16},${n >> 8 & 255},${n & 255}`;
521
+ return `${n >> 16},${(n >> 8) & 255},${n & 255}`;
427
522
  }
428
523
  function applyDprSizing(target, logicalWidth, logicalHeight, dpr) {
429
524
  target.style.width = `${logicalWidth}px`;
@@ -438,22 +533,14 @@ function createRenderer(options) {
438
533
  }
439
534
  const ctx = canvas.getContext("2d");
440
535
  const engine = options.engine;
441
- const trailStyle = options.trailStyle ?? "default";
442
- const trailColor = options.trailColor ?? "#ffffff";
443
- const palette = resolvePalette(options.palette, trailStyle);
444
- function defaultHeadColor() {
445
- if (trailStyle !== "default") {
446
- const { r, g, b } = getPaletteColor(palette, 1);
447
- return `rgb(${r},${g},${b})`;
448
- }
449
- return trailColor;
450
- }
451
- const opts = {
452
- skeletonColor: options.skeletonColor ?? DEFAULT_SKELETON_COLOR,
453
- trailColor,
454
- headColor: options.headColor ?? defaultHeadColor()
455
- };
456
- const trailRgb = hexToRgbComponents(opts.trailColor);
536
+ let trailStyle = options.trailStyle ?? "default";
537
+ let trailColor = options.trailColor ?? WHITE_HEX;
538
+ let skeletonColor = options.skeletonColor ?? WHITE_HEX;
539
+ let userHeadColor = options.headColor ?? null;
540
+ let headColor = userHeadColor ?? resolveHeadColor(trailColor, trailStyle);
541
+ let trailSolidRgb = hexToRgbComponents(resolveTrailMainColor(trailColor));
542
+ let trailPalette = resolveTrailPalette(trailColor);
543
+ warnIfTrailColorMismatch(trailColor, trailStyle);
457
544
  const dpr = typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1;
458
545
  function setupCanvas() {
459
546
  const rect = canvas.getBoundingClientRect();
@@ -488,11 +575,13 @@ function createRenderer(options) {
488
575
  }
489
576
  }
490
577
  function buildSkeletonCanvas() {
491
- if (skeleton.length < 2) return;
578
+ if (skeleton.length < 2) {
579
+ return;
580
+ }
492
581
  skeletonCanvas = new OffscreenCanvas(canvas.width, canvas.height);
493
582
  const skeletonCtx = skeletonCanvas.getContext("2d");
494
583
  skeletonCtx.setTransform(dpr, 0, 0, dpr, 0, 0);
495
- skeletonCtx.strokeStyle = `rgba(${hexToRgbComponents(opts.skeletonColor)},${DEFAULT_SKELETON_OPACITY})`;
584
+ skeletonCtx.strokeStyle = `rgba(${hexToRgbComponents(skeletonColor)},${DEFAULT_SKELETON_OPACITY})`;
496
585
  skeletonCtx.lineWidth = 1.5;
497
586
  skeletonCtx.beginPath();
498
587
  const first = skeleton[0];
@@ -504,8 +593,10 @@ function createRenderer(options) {
504
593
  skeletonCtx.stroke();
505
594
  }
506
595
  function drawSkeletonPath(pts, opacity) {
507
- if (pts.length < 2) return;
508
- ctx.strokeStyle = `rgba(${hexToRgbComponents(opts.skeletonColor)},${opacity})`;
596
+ if (pts.length < 2) {
597
+ return;
598
+ }
599
+ ctx.strokeStyle = `rgba(${hexToRgbComponents(skeletonColor)},${opacity})`;
509
600
  ctx.lineWidth = 1.5;
510
601
  ctx.beginPath();
511
602
  ctx.moveTo(pts[0].x * scale + offsetX, pts[0].y * scale + offsetY);
@@ -515,7 +606,7 @@ function createRenderer(options) {
515
606
  ctx.stroke();
516
607
  }
517
608
  function drawSkeleton() {
518
- if (opts.skeletonColor === "transparent") {
609
+ if (skeletonColor === "transparent") {
519
610
  return;
520
611
  }
521
612
  if (engine.morphAlpha !== null) {
@@ -526,7 +617,7 @@ function createRenderer(options) {
526
617
  if (skeleton.length < 2) {
527
618
  return;
528
619
  }
529
- ctx.strokeStyle = `rgba(${hexToRgbComponents(opts.skeletonColor)},${DEFAULT_SKELETON_OPACITY})`;
620
+ ctx.strokeStyle = `rgba(${hexToRgbComponents(skeletonColor)},${DEFAULT_SKELETON_OPACITY})`;
530
621
  ctx.lineWidth = 1.5;
531
622
  ctx.beginPath();
532
623
  const first = skeleton[0];
@@ -552,13 +643,13 @@ function createRenderer(options) {
552
643
  i,
553
644
  trailCount,
554
645
  toX,
555
- toY
646
+ toY,
556
647
  );
557
648
  if (trailStyle === "default") {
558
- ctx.fillStyle = `rgba(${trailRgb},${opacity})`;
649
+ ctx.fillStyle = `rgba(${trailSolidRgb},${opacity})`;
559
650
  } else {
560
651
  const timeOffset = trailStyle === "gradient-animated" ? gradientAnimTime * 5e-4 : 0;
561
- const color = getPaletteColor(palette, progress, timeOffset);
652
+ const color = getPaletteColor(trailPalette, progress, timeOffset);
562
653
  ctx.fillStyle = `rgba(${color.r},${color.g},${color.b},${opacity})`;
563
654
  }
564
655
  ctx.beginPath();
@@ -576,8 +667,9 @@ function createRenderer(options) {
576
667
  }
577
668
  const x = head.x * scale + offsetX;
578
669
  const y = head.y * scale + offsetY;
579
- const r = options.headRadius ?? Math.max(2, 3 * Math.sqrt(Math.min(logicalWidth, logicalHeight) / 160));
580
- ctx.fillStyle = opts.headColor;
670
+ const r =
671
+ options.headRadius ?? Math.max(2, 3 * Math.sqrt(Math.min(logicalWidth, logicalHeight) / 160));
672
+ ctx.fillStyle = headColor;
581
673
  ctx.beginPath();
582
674
  ctx.arc(x, y, r, 0, Math.PI * 2);
583
675
  ctx.fill();
@@ -677,7 +769,35 @@ function createRenderer(options) {
677
769
  return new Promise((resolve) => {
678
770
  morphResolve = resolve;
679
771
  });
680
- }
772
+ },
773
+ setRenderOptions(partial) {
774
+ validateRenderOptions(partial);
775
+ if (partial.trailColor !== void 0) {
776
+ trailColor = partial.trailColor;
777
+ trailSolidRgb = hexToRgbComponents(resolveTrailMainColor(trailColor));
778
+ trailPalette = resolveTrailPalette(trailColor);
779
+ }
780
+ if (partial.skeletonColor !== void 0) {
781
+ skeletonColor = partial.skeletonColor;
782
+ if (skeletonColor !== "transparent" && !engine.isLiveSkeleton) {
783
+ buildSkeletonCanvas();
784
+ }
785
+ }
786
+ if (partial.trailStyle !== void 0) {
787
+ trailStyle = partial.trailStyle;
788
+ }
789
+ if (partial.headColor !== void 0) {
790
+ userHeadColor = partial.headColor;
791
+ }
792
+ if (userHeadColor === null) {
793
+ headColor = resolveHeadColor(trailColor, trailStyle);
794
+ } else {
795
+ headColor = userHeadColor;
796
+ }
797
+ if (partial.trailColor !== void 0 || partial.trailStyle !== void 0) {
798
+ warnIfTrailColorMismatch(trailColor, trailStyle);
799
+ }
800
+ },
681
801
  };
682
802
  if (shouldAutoStart) {
683
803
  instance.play();
@@ -688,19 +808,22 @@ function createRenderer(options) {
688
808
  // src/curves/artemis2.ts
689
809
  var TWO_PI2 = Math.PI * 2;
690
810
  function artemis2Fn(t, _time, _params) {
691
- const a = 0.35, b = 0.15, ox = 0.175;
692
- const s = Math.sin(t), c = Math.cos(t);
811
+ const a = 0.35,
812
+ b = 0.15,
813
+ ox = 0.175;
814
+ const s = Math.sin(t),
815
+ c = Math.cos(t);
693
816
  const denom = 1 + s * s;
694
817
  return {
695
- x: c * (1 + a * c) / denom - ox,
696
- y: s * c * (1 + b * c) / denom
818
+ x: (c * (1 + a * c)) / denom - ox,
819
+ y: (s * c * (1 + b * c)) / denom,
697
820
  };
698
821
  }
699
822
  var artemis2 = {
700
823
  name: "Artemis II",
701
824
  fn: artemis2Fn,
702
825
  period: TWO_PI2,
703
- speed: 0.7
826
+ speed: 0.7,
704
827
  };
705
828
 
706
829
  // src/curves/astroid.ts
@@ -710,14 +833,14 @@ function astroidFn(t, _time, _params) {
710
833
  const s = Math.sin(t);
711
834
  return {
712
835
  x: c * c * c,
713
- y: s * s * s
836
+ y: s * s * s,
714
837
  };
715
838
  }
716
839
  var astroid = {
717
840
  name: "Astroid",
718
841
  fn: astroidFn,
719
842
  period: TWO_PI3,
720
- speed: 1.1
843
+ speed: 1.1,
721
844
  };
722
845
 
723
846
  // src/curves/deltoid.ts
@@ -725,14 +848,14 @@ var TWO_PI4 = Math.PI * 2;
725
848
  function deltoidFn(t, _time, _params) {
726
849
  return {
727
850
  x: 2 * Math.cos(t) + Math.cos(2 * t),
728
- y: 2 * Math.sin(t) - Math.sin(2 * t)
851
+ y: 2 * Math.sin(t) - Math.sin(2 * t),
729
852
  };
730
853
  }
731
854
  var deltoid = {
732
855
  name: "Deltoid",
733
856
  fn: deltoidFn,
734
857
  period: TWO_PI4,
735
- speed: 0.9
858
+ speed: 0.9,
736
859
  };
737
860
 
738
861
  // src/curves/epicycloid3.ts
@@ -740,14 +863,14 @@ var TWO_PI5 = Math.PI * 2;
740
863
  function epicycloid3Fn(t, _time, _params) {
741
864
  return {
742
865
  x: 4 * Math.cos(t) - Math.cos(4 * t),
743
- y: 4 * Math.sin(t) - Math.sin(4 * t)
866
+ y: 4 * Math.sin(t) - Math.sin(4 * t),
744
867
  };
745
868
  }
746
869
  var epicycloid3 = {
747
870
  name: "Epicycloid (n=3)",
748
871
  fn: epicycloid3Fn,
749
872
  period: TWO_PI5,
750
- speed: 0.75
873
+ speed: 0.75,
751
874
  };
752
875
 
753
876
  // src/curves/epitrochoid7.ts
@@ -756,14 +879,14 @@ function epitrochoid7Fn(t, _time, _params) {
756
879
  const d = 1 + 0.55 * Math.sin(t * 0.5);
757
880
  return {
758
881
  x: 7 * Math.cos(t) - d * Math.cos(7 * t),
759
- y: 7 * Math.sin(t) - d * Math.sin(7 * t)
882
+ y: 7 * Math.sin(t) - d * Math.sin(7 * t),
760
883
  };
761
884
  }
762
885
  function epitrochoid7SkeletonFn(t) {
763
886
  const d = 1.275;
764
887
  return {
765
888
  x: 7 * Math.cos(t) - d * Math.cos(7 * t),
766
- y: 7 * Math.sin(t) - d * Math.sin(7 * t)
889
+ y: 7 * Math.sin(t) - d * Math.sin(7 * t),
767
890
  };
768
891
  }
769
892
  var epitrochoid7 = {
@@ -771,7 +894,7 @@ var epitrochoid7 = {
771
894
  fn: epitrochoid7Fn,
772
895
  period: TWO_PI6,
773
896
  speed: 1.4,
774
- skeletonFn: epitrochoid7SkeletonFn
897
+ skeletonFn: epitrochoid7SkeletonFn,
775
898
  };
776
899
 
777
900
  // src/curves/lissajous32.ts
@@ -780,7 +903,7 @@ function lissajous32Fn(t, time, _params) {
780
903
  const phi = time * 0.45;
781
904
  return {
782
905
  x: Math.sin(3 * t + phi),
783
- y: Math.sin(2 * t)
906
+ y: Math.sin(2 * t),
784
907
  };
785
908
  }
786
909
  var lissajous32 = {
@@ -788,7 +911,7 @@ var lissajous32 = {
788
911
  fn: lissajous32Fn,
789
912
  period: TWO_PI7,
790
913
  speed: 2,
791
- skeleton: "live"
914
+ skeleton: "live",
792
915
  };
793
916
 
794
917
  // src/curves/lissajous43.ts
@@ -797,7 +920,7 @@ function lissajous43Fn(t, time, _params) {
797
920
  const phi = time * 0.38;
798
921
  return {
799
922
  x: Math.sin(4 * t + phi),
800
- y: Math.sin(3 * t)
923
+ y: Math.sin(3 * t),
801
924
  };
802
925
  }
803
926
  var lissajous43 = {
@@ -805,17 +928,18 @@ var lissajous43 = {
805
928
  fn: lissajous43Fn,
806
929
  period: TWO_PI8,
807
930
  speed: 1.8,
808
- skeleton: "live"
931
+ skeleton: "live",
809
932
  };
810
933
 
811
934
  // src/curves/lame.ts
812
935
  var TWO_PI9 = Math.PI * 2;
813
936
  function lameFn(t, time, _params) {
814
937
  const p = 1.75 + 1.25 * Math.sin(time * 0.48);
815
- const c = Math.cos(t), s = Math.sin(t);
938
+ const c = Math.cos(t),
939
+ s = Math.sin(t);
816
940
  return {
817
941
  x: Math.sign(c) * Math.pow(Math.abs(c), p),
818
- y: Math.sign(s) * Math.pow(Math.abs(s), p)
942
+ y: Math.sign(s) * Math.pow(Math.abs(s), p),
819
943
  };
820
944
  }
821
945
  var lame = {
@@ -823,7 +947,7 @@ var lame = {
823
947
  fn: lameFn,
824
948
  period: TWO_PI9,
825
949
  speed: 1,
826
- skeleton: "live"
950
+ skeleton: "live",
827
951
  };
828
952
 
829
953
  // src/curves/rose3.ts
@@ -832,14 +956,14 @@ function rose3Fn(t, _time, _params) {
832
956
  const r = Math.cos(3 * t);
833
957
  return {
834
958
  x: r * Math.cos(t),
835
- y: r * Math.sin(t)
959
+ y: r * Math.sin(t),
836
960
  };
837
961
  }
838
962
  var rose3 = {
839
963
  name: "Rose (n=3)",
840
964
  fn: rose3Fn,
841
965
  period: TWO_PI10,
842
- speed: 1.15
966
+ speed: 1.15,
843
967
  };
844
968
 
845
969
  // src/curves/rose5.ts
@@ -848,14 +972,14 @@ function rose5Fn(t, _time, _params) {
848
972
  const r = Math.cos(5 * t);
849
973
  return {
850
974
  x: r * Math.cos(t),
851
- y: r * Math.sin(t)
975
+ y: r * Math.sin(t),
852
976
  };
853
977
  }
854
978
  var rose5 = {
855
979
  name: "Rose (n=5)",
856
980
  fn: rose5Fn,
857
981
  period: TWO_PI11,
858
- speed: 1
982
+ speed: 1,
859
983
  };
860
984
 
861
985
  // src/curves/index.ts
@@ -869,7 +993,7 @@ var curves = {
869
993
  lissajous32,
870
994
  lissajous43,
871
995
  epicycloid3,
872
- lame
996
+ lame,
873
997
  };
874
998
 
875
999
  // src/index.ts
@@ -880,14 +1004,13 @@ function createSarmal(canvas, curveDef, options) {
880
1004
  }
881
1005
 
882
1006
  // src/auto-init.ts
883
- function parsePalette(value) {
1007
+ function parseTrailColor(value) {
884
1008
  try {
885
1009
  const parsed = JSON.parse(value);
886
1010
  if (Array.isArray(parsed)) {
887
1011
  return parsed;
888
1012
  }
889
- } catch {
890
- }
1013
+ } catch {}
891
1014
  return value;
892
1015
  }
893
1016
  function init() {
@@ -902,15 +1025,16 @@ function init() {
902
1025
  return console.error(`[sarmal] "${curveName}" is not a valid curve name`);
903
1026
  }
904
1027
  createSarmal(canvas, curveDef, {
905
- ...canvas.dataset.trailColor && { trailColor: canvas.dataset.trailColor },
906
- ...canvas.dataset.skeletonColor && { skeletonColor: canvas.dataset.skeletonColor },
907
- ...canvas.dataset.headColor && { headColor: canvas.dataset.headColor },
908
- ...canvas.dataset.headRadius && { headRadius: parseFloat(canvas.dataset.headRadius) },
909
- ...canvas.dataset.trailLength && { trailLength: parseInt(canvas.dataset.trailLength, 10) },
910
- ...canvas.dataset.trailStyle && {
911
- trailStyle: canvas.dataset.trailStyle
912
- },
913
- ...canvas.dataset.palette && { palette: parsePalette(canvas.dataset.palette) }
1028
+ ...(canvas.dataset.trailColor && {
1029
+ trailColor: parseTrailColor(canvas.dataset.trailColor),
1030
+ }),
1031
+ ...(canvas.dataset.skeletonColor && { skeletonColor: canvas.dataset.skeletonColor }),
1032
+ ...(canvas.dataset.headColor && { headColor: canvas.dataset.headColor }),
1033
+ ...(canvas.dataset.headRadius && { headRadius: parseFloat(canvas.dataset.headRadius) }),
1034
+ ...(canvas.dataset.trailLength && { trailLength: parseInt(canvas.dataset.trailLength, 10) }),
1035
+ ...(canvas.dataset.trailStyle && {
1036
+ trailStyle: canvas.dataset.trailStyle,
1037
+ }),
914
1038
  });
915
1039
  });
916
1040
  }
@@ -922,4 +1046,4 @@ if (document.readyState === "loading") {
922
1046
  requestAnimationFrame(init);
923
1047
  }
924
1048
  //# sourceMappingURL=auto-init.cjs.map
925
- //# sourceMappingURL=auto-init.cjs.map
1049
+ //# sourceMappingURL=auto-init.cjs.map