@sarmal/core 0.35.0 → 0.36.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 (54) hide show
  1. package/README.md +5 -3
  2. package/dist/auto-init.cjs +3 -1
  3. package/dist/auto-init.cjs.map +1 -1
  4. package/dist/auto-init.js +3 -1
  5. package/dist/auto-init.js.map +1 -1
  6. package/dist/cli.js +9 -3
  7. package/dist/cli.js.map +1 -1
  8. package/dist/curves/artemis2.d.cts +1 -1
  9. package/dist/curves/artemis2.d.ts +1 -1
  10. package/dist/curves/astroid.d.cts +1 -1
  11. package/dist/curves/astroid.d.ts +1 -1
  12. package/dist/curves/deltoid.d.cts +1 -1
  13. package/dist/curves/deltoid.d.ts +1 -1
  14. package/dist/curves/epicycloid3.d.cts +1 -1
  15. package/dist/curves/epicycloid3.d.ts +1 -1
  16. package/dist/curves/epitrochoid7.d.cts +1 -1
  17. package/dist/curves/epitrochoid7.d.ts +1 -1
  18. package/dist/curves/index.d.cts +1 -1
  19. package/dist/curves/index.d.ts +1 -1
  20. package/dist/curves/lame.d.cts +1 -1
  21. package/dist/curves/lame.d.ts +1 -1
  22. package/dist/curves/lissajous32.d.cts +1 -1
  23. package/dist/curves/lissajous32.d.ts +1 -1
  24. package/dist/curves/lissajous43.d.cts +1 -1
  25. package/dist/curves/lissajous43.d.ts +1 -1
  26. package/dist/curves/rose3.d.cts +1 -1
  27. package/dist/curves/rose3.d.ts +1 -1
  28. package/dist/curves/rose5.d.cts +1 -1
  29. package/dist/curves/rose5.d.ts +1 -1
  30. package/dist/curves/rose52.d.cts +1 -1
  31. package/dist/curves/rose52.d.ts +1 -1
  32. package/dist/curves/star.d.cts +1 -1
  33. package/dist/curves/star.d.ts +1 -1
  34. package/dist/curves/star4.d.cts +1 -1
  35. package/dist/curves/star4.d.ts +1 -1
  36. package/dist/curves/star7.d.cts +1 -1
  37. package/dist/curves/star7.d.ts +1 -1
  38. package/dist/index.cjs +109 -50
  39. package/dist/index.cjs.map +1 -1
  40. package/dist/index.d.cts +4 -4
  41. package/dist/index.d.ts +4 -4
  42. package/dist/index.js +109 -50
  43. package/dist/index.js.map +1 -1
  44. package/dist/{renderer-shared-DyOI68gd.d.ts → renderer-shared-BZV9ELOa.d.ts} +1 -1
  45. package/dist/{renderer-shared-C3KCEABq.d.cts → renderer-shared-CFimm7VD.d.cts} +1 -1
  46. package/dist/terminal.cjs +9 -3
  47. package/dist/terminal.cjs.map +1 -1
  48. package/dist/terminal.d.cts +2 -2
  49. package/dist/terminal.d.ts +2 -2
  50. package/dist/terminal.js +9 -3
  51. package/dist/terminal.js.map +1 -1
  52. package/dist/{types-_f27GDkU.d.cts → types-CCgSK31t.d.cts} +8 -4
  53. package/dist/{types-_f27GDkU.d.ts → types-CCgSK31t.d.ts} +8 -4
  54. package/package.json +2 -2
package/dist/index.d.cts CHANGED
@@ -1,7 +1,7 @@
1
- import { B as BaseRendererOptions, E as Engine, S as SarmalInstance, C as CurveDef, T as TrailColor, a as TrailStyle, D as DotMatrixRuntimeRenderOptions, R as RendererOptions, b as ControlPoint, P as Point, c as SarmalOptions } from './types-_f27GDkU.cjs';
2
- export { d as BaseRuntimeRenderOptions, J as JumpOptions, e as RuntimeRenderOptions, f as SeekOptions } from './types-_f27GDkU.cjs';
1
+ import { B as BaseRendererOptions, E as Engine, S as SarmalInstance, C as CurveDef, T as TrailColor, a as TrailStyle, D as DotMatrixRuntimeRenderOptions, R as RendererOptions, b as ControlPoint, P as Point, c as SarmalOptions } from './types-CCgSK31t.cjs';
2
+ export { d as BaseRuntimeRenderOptions, J as JumpOptions, e as RuntimeRenderOptions, f as SeekOptions } from './types-CCgSK31t.cjs';
3
3
  export { CurveName, curves } from './curves/index.cjs';
4
- export { B as BoundaryResult, S as SarmalPalette, c as computeBoundaries, p as palettes } from './renderer-shared-C3KCEABq.cjs';
4
+ export { B as BoundaryResult, S as SarmalPalette, c as computeBoundaries, p as palettes } from './renderer-shared-CFimm7VD.cjs';
5
5
  export { artemis2 } from './curves/artemis2.cjs';
6
6
  export { astroid } from './curves/astroid.cjs';
7
7
  export { deltoid } from './curves/deltoid.cjs';
@@ -53,7 +53,7 @@ declare function createSVGRenderer(options: SVGRendererOptions): SarmalInstance;
53
53
  */
54
54
  declare function createSarmalSVG(container: SVGSVGElement, curveDef: CurveDef, options?: SVGSarmalOptions): SarmalInstance;
55
55
 
56
- interface DotMatrixSarmalOptions extends Pick<BaseRendererOptions, "autoStart" | "pauseOnHidden" | "initialPhase"> {
56
+ interface DotMatrixSarmalOptions extends Pick<BaseRendererOptions, "autoStart" | "pauseOnHidden" | "initialPhase" | "skeletonColor"> {
57
57
  /**
58
58
  * Number of dot columns in the grid.
59
59
  * @default 32
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { B as BaseRendererOptions, E as Engine, S as SarmalInstance, C as CurveDef, T as TrailColor, a as TrailStyle, D as DotMatrixRuntimeRenderOptions, R as RendererOptions, b as ControlPoint, P as Point, c as SarmalOptions } from './types-_f27GDkU.js';
2
- export { d as BaseRuntimeRenderOptions, J as JumpOptions, e as RuntimeRenderOptions, f as SeekOptions } from './types-_f27GDkU.js';
1
+ import { B as BaseRendererOptions, E as Engine, S as SarmalInstance, C as CurveDef, T as TrailColor, a as TrailStyle, D as DotMatrixRuntimeRenderOptions, R as RendererOptions, b as ControlPoint, P as Point, c as SarmalOptions } from './types-CCgSK31t.js';
2
+ export { d as BaseRuntimeRenderOptions, J as JumpOptions, e as RuntimeRenderOptions, f as SeekOptions } from './types-CCgSK31t.js';
3
3
  export { CurveName, curves } from './curves/index.js';
4
- export { B as BoundaryResult, S as SarmalPalette, c as computeBoundaries, p as palettes } from './renderer-shared-DyOI68gd.js';
4
+ export { B as BoundaryResult, S as SarmalPalette, c as computeBoundaries, p as palettes } from './renderer-shared-BZV9ELOa.js';
5
5
  export { artemis2 } from './curves/artemis2.js';
6
6
  export { astroid } from './curves/astroid.js';
7
7
  export { deltoid } from './curves/deltoid.js';
@@ -53,7 +53,7 @@ declare function createSVGRenderer(options: SVGRendererOptions): SarmalInstance;
53
53
  */
54
54
  declare function createSarmalSVG(container: SVGSVGElement, curveDef: CurveDef, options?: SVGSarmalOptions): SarmalInstance;
55
55
 
56
- interface DotMatrixSarmalOptions extends Pick<BaseRendererOptions, "autoStart" | "pauseOnHidden" | "initialPhase"> {
56
+ interface DotMatrixSarmalOptions extends Pick<BaseRendererOptions, "autoStart" | "pauseOnHidden" | "initialPhase" | "skeletonColor"> {
57
57
  /**
58
58
  * Number of dot columns in the grid.
59
59
  * @default 32
package/dist/index.js CHANGED
@@ -320,7 +320,9 @@ function computeTrailQuad(trail, i, trailCount, toX, toY, minWidth = TRAIL_MIN_W
320
320
  };
321
321
  }
322
322
  function computeBoundaries(pts, logicalWidth, logicalHeight, minPaddingPx = FIT_PADDING_MIN) {
323
- if (pts.length === 0) return null;
323
+ if (pts.length === 0) {
324
+ return null;
325
+ }
324
326
  const first = pts[0];
325
327
  let minX = first.x, maxX = first.x, minY = first.y, maxY = first.y;
326
328
  for (const p of pts) {
@@ -518,7 +520,11 @@ function getPaletteColor(palette, position, timeOffset = 0) {
518
520
  return lerpOklab(c1, c2, t);
519
521
  }
520
522
  var TRAIL_STYLES = ["default", "gradient-static", "gradient-animated"];
521
- var BASE_RENDER_OPTION_KEYS = /* @__PURE__ */ new Set(["trailColor", "trailStyle"]);
523
+ var BASE_RENDER_OPTION_KEYS = /* @__PURE__ */ new Set([
524
+ "trailColor",
525
+ "trailStyle",
526
+ "skeletonColor"
527
+ ]);
522
528
  var RENDER_OPTION_KEYS = /* @__PURE__ */ new Set([
523
529
  "trailColor",
524
530
  "headColor",
@@ -539,6 +545,9 @@ function validateBaseRenderOptions(partial) {
539
545
  if (partial.trailStyle !== void 0) {
540
546
  assertTrailStyle(partial.trailStyle);
541
547
  }
548
+ if (partial.skeletonColor !== void 0) {
549
+ assertSkeletonColor(partial.skeletonColor);
550
+ }
542
551
  }
543
552
  function validateRenderOptions(partial) {
544
553
  for (const key of Object.keys(partial)) {
@@ -1424,6 +1433,7 @@ function createSarmalDotMatrix(canvas, curveDef, options) {
1424
1433
  trailLength: trailLengthOpt,
1425
1434
  trailColor: initialColor = "#ffffff",
1426
1435
  trailStyle: initialTrailStyle = "default",
1436
+ skeletonColor: skeletonColorOpt = "#ffffff",
1427
1437
  autoStart = true,
1428
1438
  pauseOnHidden: pauseOnHiddenOpt = true,
1429
1439
  initialPhase
@@ -1439,14 +1449,8 @@ function createSarmalDotMatrix(canvas, curveDef, options) {
1439
1449
  const cellW = W / cols;
1440
1450
  const cellH = H / rows;
1441
1451
  const dotR = Math.min(cellW, cellH) * 0.36;
1442
- let gradientRgb;
1443
- if (Array.isArray(initialColor)) {
1444
- validateBaseRenderOptions({ trailColor: initialColor });
1445
- gradientRgb = initialColor.map(colorToRgb);
1446
- } else {
1447
- gradientRgb = null;
1448
- }
1449
- let colorRgb = gradientRgb ? gradientRgb[0] : colorToRgb(initialColor);
1452
+ let gradientOklab = null;
1453
+ let colorRgb = { r: 255, g: 255, b: 255 };
1450
1454
  let currentTrailStyle = initialTrailStyle;
1451
1455
  let animTime = 0;
1452
1456
  const ANIM_PERIOD = 6;
@@ -1460,6 +1464,8 @@ function createSarmalDotMatrix(canvas, curveDef, options) {
1460
1464
  let pixelMaskCoverages = new Float32Array(0);
1461
1465
  let bgImageData = null;
1462
1466
  let frameImageData = null;
1467
+ let skeletonColorOklab = null;
1468
+ const skeletonDotGrid = new Uint8Array(cols * rows);
1463
1469
  let animationId = null;
1464
1470
  let lastTime = 0;
1465
1471
  let pausedByVisibility = false;
@@ -1518,7 +1524,7 @@ function createSarmalDotMatrix(canvas, curveDef, options) {
1518
1524
  }
1519
1525
  function buildBgImageData() {
1520
1526
  bgImageData = new ImageData(W, H);
1521
- const bg = gradientRgb ? gradientRgb[0] : colorRgb;
1527
+ const bg = colorRgb;
1522
1528
  const baseAlpha = 0.05 * 255;
1523
1529
  const { data } = bgImageData;
1524
1530
  const n = cols * rows;
@@ -1535,18 +1541,61 @@ function createSarmalDotMatrix(canvas, curveDef, options) {
1535
1541
  }
1536
1542
  }
1537
1543
  }
1538
- function sampleGradientRgb(stops, t) {
1539
- const n = stops.length;
1540
- const scaled = Math.max(0, Math.min(1, t)) * (n - 1);
1541
- const i = Math.min(Math.floor(scaled), n - 2);
1542
- const a = stops[i];
1543
- const bStop = stops[i + 1];
1544
- const mix = scaled - i;
1545
- return {
1546
- r: Math.round(a.r + (bStop.r - a.r) * mix),
1547
- g: Math.round(a.g + (bStop.g - a.g) * mix),
1548
- b: Math.round(a.b + (bStop.b - a.b) * mix)
1549
- };
1544
+ function applyColor(color) {
1545
+ if (Array.isArray(color)) {
1546
+ gradientOklab = color.map((c) => parseColorToOklab(c));
1547
+ colorRgb = oklabToRgb(gradientOklab[0]);
1548
+ } else {
1549
+ gradientOklab = null;
1550
+ colorRgb = colorToRgb(color);
1551
+ }
1552
+ }
1553
+ function applySkeletonColor(color) {
1554
+ skeletonColorOklab = color === "transparent" ? null : parseColorToOklab(color);
1555
+ }
1556
+ function computeSkeletonGrid(skel) {
1557
+ skeletonDotGrid.fill(0);
1558
+ const count = skel.length;
1559
+ for (let i = 0; i < count; i++) {
1560
+ const pt = skel[i];
1561
+ const [c, r] = mapPt(pt.x, pt.y);
1562
+ skeletonDotGrid[r * cols + c] = 1;
1563
+ if (i < count - 1) {
1564
+ const next = skel[i + 1];
1565
+ const [nc, nr] = mapPt(next.x, next.y);
1566
+ const steps = Math.ceil(Math.max(Math.abs(nc - c), Math.abs(nr - r))) * 2;
1567
+ for (let s = 1; s < steps; s++) {
1568
+ const t = s / steps;
1569
+ const ix = pt.x + (next.x - pt.x) * t;
1570
+ const iy = pt.y + (next.y - pt.y) * t;
1571
+ const [ic, ir] = mapPt(ix, iy);
1572
+ skeletonDotGrid[ir * cols + ic] = 1;
1573
+ }
1574
+ }
1575
+ }
1576
+ }
1577
+ function writeSkeletonPixels(data) {
1578
+ if (skeletonColorOklab === null) {
1579
+ return;
1580
+ }
1581
+ const { r, g, b } = oklabToRgb(skeletonColorOklab);
1582
+ const skelBaseAlpha = DEFAULT_SKELETON_OPACITY * 255;
1583
+ const n = cols * rows;
1584
+ for (let dotIdx = 0; dotIdx < n; dotIdx++) {
1585
+ if (!skeletonDotGrid[dotIdx]) {
1586
+ continue;
1587
+ }
1588
+ const start = pixelMaskStarts[dotIdx];
1589
+ const len = pixelMaskLengths[dotIdx];
1590
+ for (let k = 0; k < len; k++) {
1591
+ const px = pixelMaskIndices[start + k];
1592
+ const coverage = pixelMaskCoverages[start + k];
1593
+ data[px] = r;
1594
+ data[px + 1] = g;
1595
+ data[px + 2] = b;
1596
+ data[px + 3] = Math.round(skelBaseAlpha * coverage);
1597
+ }
1598
+ }
1550
1599
  }
1551
1600
  function calculateBoundaries(skel) {
1552
1601
  const b = computeBoundaries(skel, W, H);
@@ -1600,7 +1649,8 @@ function createSarmalDotMatrix(canvas, curveDef, options) {
1600
1649
  }
1601
1650
  frameImageData.data.set(bgImageData.data);
1602
1651
  const { data } = frameImageData;
1603
- const sineOffset = currentTrailStyle === "gradient-animated" ? 0.15 * Math.sin(animTime / ANIM_PERIOD * 2 * Math.PI) : 0;
1652
+ writeSkeletonPixels(data);
1653
+ const timeOffset = currentTrailStyle === "gradient-animated" ? animTime / ANIM_PERIOD : 0;
1604
1654
  const n = cols * rows;
1605
1655
  for (let dotIdx = 0; dotIdx < n; dotIdx++) {
1606
1656
  const intensity = grid[dotIdx];
@@ -1608,9 +1658,8 @@ function createSarmalDotMatrix(canvas, curveDef, options) {
1608
1658
  continue;
1609
1659
  }
1610
1660
  let r, g, b;
1611
- if (gradientRgb !== null) {
1612
- const t = Math.max(0, Math.min(1, intensity + sineOffset));
1613
- ({ r, g, b } = sampleGradientRgb(gradientRgb, t));
1661
+ if (gradientOklab !== null) {
1662
+ ({ r, g, b } = oklabToRgb(getPaletteColor(gradientOklab, intensity, timeOffset)));
1614
1663
  } else {
1615
1664
  ({ r, g, b } = colorRgb);
1616
1665
  }
@@ -1628,21 +1677,26 @@ function createSarmalDotMatrix(canvas, curveDef, options) {
1628
1677
  }
1629
1678
  ctx.putImageData(frameImageData, 0, 0);
1630
1679
  }
1680
+ function completeMorphNow() {
1681
+ engine.completeMorph();
1682
+ morphResolve?.();
1683
+ morphResolve = null;
1684
+ morphReject = null;
1685
+ morphProgress = 0;
1686
+ }
1631
1687
  function renderFrame(deltaTime) {
1632
1688
  if (engine.morphAlpha !== null) {
1633
1689
  morphProgress = Math.min(1, morphProgress + deltaTime / (morphDurationMs / 1e3));
1634
1690
  engine.setMorphAlpha(morphProgress);
1635
1691
  calculateBoundaries(engine.getSarmalSkeleton());
1636
1692
  if (morphProgress >= 1) {
1637
- engine.completeMorph();
1638
- morphResolve?.();
1639
- morphResolve = null;
1640
- morphReject = null;
1641
- morphProgress = 0;
1693
+ completeMorphNow();
1642
1694
  calculateBoundaries(engine.getSarmalSkeleton());
1643
1695
  }
1696
+ computeSkeletonGrid(engine.getSarmalSkeleton());
1644
1697
  } else if (engine.isLiveSkeleton) {
1645
1698
  calculateBoundaries(engine.getSarmalSkeleton());
1699
+ computeSkeletonGrid(engine.getSarmalSkeleton());
1646
1700
  }
1647
1701
  if (currentTrailStyle === "gradient-animated") {
1648
1702
  animTime += deltaTime;
@@ -1656,8 +1710,12 @@ function createSarmalDotMatrix(canvas, curveDef, options) {
1656
1710
  renderFrame(deltaTime);
1657
1711
  animationId = requestAnimationFrame(loop);
1658
1712
  }
1713
+ validateBaseRenderOptions({ trailColor: initialColor, skeletonColor: skeletonColorOpt });
1714
+ applyColor(initialColor);
1715
+ applySkeletonColor(skeletonColorOpt);
1659
1716
  calculateBoundaries(engine.getSarmalSkeleton());
1660
1717
  computePixelMask();
1718
+ computeSkeletonGrid(engine.getSarmalSkeleton());
1661
1719
  frameImageData = new ImageData(W, H);
1662
1720
  buildBgImageData();
1663
1721
  if (initialPhase !== void 0) {
@@ -1667,13 +1725,17 @@ function createSarmalDotMatrix(canvas, curveDef, options) {
1667
1725
  const instance = {
1668
1726
  /** Starts the animation loop. Does nothing if already running. */
1669
1727
  play() {
1670
- if (animationId !== null) return;
1728
+ if (animationId !== null) {
1729
+ return;
1730
+ }
1671
1731
  lastTime = performance.now();
1672
1732
  loop();
1673
1733
  },
1674
1734
  /** Pauses the animation loop. Preserves current trail state. */
1675
1735
  pause() {
1676
- if (animationId === null) return;
1736
+ if (animationId === null) {
1737
+ return;
1738
+ }
1677
1739
  cancelAnimationFrame(animationId);
1678
1740
  animationId = null;
1679
1741
  engine.cancelSpeedTransition();
@@ -1704,11 +1766,7 @@ function createSarmalDotMatrix(canvas, curveDef, options) {
1704
1766
  */
1705
1767
  morphTo(target, opts) {
1706
1768
  if (morphResolve !== null) {
1707
- engine.completeMorph();
1708
- morphResolve();
1709
- morphResolve = null;
1710
- morphReject = null;
1711
- morphProgress = 0;
1769
+ completeMorphNow();
1712
1770
  }
1713
1771
  morphDurationMs = opts?.duration ?? DEFAULT_MORPH_DURATION_MS;
1714
1772
  morphProgress = 0;
@@ -1721,23 +1779,20 @@ function createSarmalDotMatrix(canvas, curveDef, options) {
1721
1779
  /**
1722
1780
  * Updates render options on a live instance without stopping the animation.
1723
1781
  *
1724
- * Supported: `trailColor` and `trailStyle`.
1725
- * ! Unsupported fields (`headColor`, `skeletonColor`, `headRadius`, `trailWidth`) throw.
1782
+ * Supported: `trailColor`, `trailStyle`, and `skeletonColor`.
1783
+ * ! Unsupported fields (`headColor`, `headRadius`, `trailWidth`) throw.
1726
1784
  * ! Validation fails the entire call if any field is invalid, leaving options unchanged.
1727
1785
  */
1728
1786
  setRenderOptions(partial) {
1729
1787
  validateBaseRenderOptions(partial);
1730
1788
  let needsRebuildBg = false;
1731
1789
  if (partial.trailColor !== void 0) {
1732
- if (Array.isArray(partial.trailColor)) {
1733
- gradientRgb = partial.trailColor.map(colorToRgb);
1734
- colorRgb = gradientRgb[0];
1735
- } else {
1736
- gradientRgb = null;
1737
- colorRgb = colorToRgb(partial.trailColor);
1738
- }
1790
+ applyColor(partial.trailColor);
1739
1791
  needsRebuildBg = true;
1740
1792
  }
1793
+ if (partial.skeletonColor !== void 0) {
1794
+ applySkeletonColor(partial.skeletonColor);
1795
+ }
1741
1796
  if (partial.trailStyle !== void 0) {
1742
1797
  currentTrailStyle = partial.trailStyle;
1743
1798
  if (currentTrailStyle === "default") {
@@ -1747,9 +1802,13 @@ function createSarmalDotMatrix(canvas, curveDef, options) {
1747
1802
  if (needsRebuildBg) {
1748
1803
  buildBgImageData();
1749
1804
  }
1750
- if (currentTrailStyle !== "default" && gradientRgb === null) {
1805
+ if (currentTrailStyle !== "default" && gradientOklab === null) {
1806
+ console.warn(
1807
+ `[sarmal] dot matrix: trailColor is a single color but trailStyle is "${currentTrailStyle}"; the trail will render as a solid color. Pass an array of hex colors to use a real gradient.`
1808
+ );
1809
+ } else if (currentTrailStyle === "default" && gradientOklab !== null) {
1751
1810
  console.warn(
1752
- "[sarmal] dot matrix: gradient trailStyle has no effect without a trailColor array"
1811
+ '[sarmal] dot matrix: trailColor is an array but trailStyle is "default"; only the first color will be used. Pass a gradient trailStyle to use the whole palette.'
1753
1812
  );
1754
1813
  }
1755
1814
  }