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