@sarmal/core 0.33.0 → 0.35.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 (49) hide show
  1. package/README.md +2 -2
  2. package/dist/auto-init.cjs.map +1 -1
  3. package/dist/auto-init.js.map +1 -1
  4. package/dist/cli.js.map +1 -1
  5. package/dist/curves/artemis2.d.cts +1 -1
  6. package/dist/curves/artemis2.d.ts +1 -1
  7. package/dist/curves/astroid.d.cts +1 -1
  8. package/dist/curves/astroid.d.ts +1 -1
  9. package/dist/curves/deltoid.d.cts +1 -1
  10. package/dist/curves/deltoid.d.ts +1 -1
  11. package/dist/curves/epicycloid3.d.cts +1 -1
  12. package/dist/curves/epicycloid3.d.ts +1 -1
  13. package/dist/curves/epitrochoid7.d.cts +1 -1
  14. package/dist/curves/epitrochoid7.d.ts +1 -1
  15. package/dist/curves/index.d.cts +1 -1
  16. package/dist/curves/index.d.ts +1 -1
  17. package/dist/curves/lame.d.cts +1 -1
  18. package/dist/curves/lame.d.ts +1 -1
  19. package/dist/curves/lissajous32.d.cts +1 -1
  20. package/dist/curves/lissajous32.d.ts +1 -1
  21. package/dist/curves/lissajous43.d.cts +1 -1
  22. package/dist/curves/lissajous43.d.ts +1 -1
  23. package/dist/curves/rose3.d.cts +1 -1
  24. package/dist/curves/rose3.d.ts +1 -1
  25. package/dist/curves/rose5.d.cts +1 -1
  26. package/dist/curves/rose5.d.ts +1 -1
  27. package/dist/curves/rose52.d.cts +1 -1
  28. package/dist/curves/rose52.d.ts +1 -1
  29. package/dist/curves/star.d.cts +1 -1
  30. package/dist/curves/star.d.ts +1 -1
  31. package/dist/curves/star4.d.cts +1 -1
  32. package/dist/curves/star4.d.ts +1 -1
  33. package/dist/curves/star7.d.cts +1 -1
  34. package/dist/curves/star7.d.ts +1 -1
  35. package/dist/index.cjs +380 -0
  36. package/dist/index.cjs.map +1 -1
  37. package/dist/index.d.cts +80 -4
  38. package/dist/index.d.ts +80 -4
  39. package/dist/index.js +380 -1
  40. package/dist/index.js.map +1 -1
  41. package/dist/{renderer-shared-Ke9BeK1P.d.cts → renderer-shared-C3KCEABq.d.cts} +1 -1
  42. package/dist/{renderer-shared-Bdca4O4G.d.ts → renderer-shared-DyOI68gd.d.ts} +1 -1
  43. package/dist/terminal.cjs.map +1 -1
  44. package/dist/terminal.d.cts +2 -2
  45. package/dist/terminal.d.ts +2 -2
  46. package/dist/terminal.js.map +1 -1
  47. package/dist/{types-BBuUk6nn.d.cts → types-_f27GDkU.d.cts} +21 -9
  48. package/dist/{types-BBuUk6nn.d.ts → types-_f27GDkU.d.ts} +21 -9
  49. package/package.json +4 -4
@@ -1,4 +1,4 @@
1
- import { C as CurveDef } from '../types-BBuUk6nn.cjs';
1
+ import { C as CurveDef } from '../types-_f27GDkU.cjs';
2
2
 
3
3
  /**
4
4
  * Lissajous curve with frequency ratio 3:2
@@ -1,4 +1,4 @@
1
- import { C as CurveDef } from '../types-BBuUk6nn.js';
1
+ import { C as CurveDef } from '../types-_f27GDkU.js';
2
2
 
3
3
  /**
4
4
  * Lissajous curve with frequency ratio 3:2
@@ -1,4 +1,4 @@
1
- import { C as CurveDef } from '../types-BBuUk6nn.cjs';
1
+ import { C as CurveDef } from '../types-_f27GDkU.cjs';
2
2
 
3
3
  /**
4
4
  * Lissajous curve with frequency ratio 4:3
@@ -1,4 +1,4 @@
1
- import { C as CurveDef } from '../types-BBuUk6nn.js';
1
+ import { C as CurveDef } from '../types-_f27GDkU.js';
2
2
 
3
3
  /**
4
4
  * Lissajous curve with frequency ratio 4:3
@@ -1,4 +1,4 @@
1
- import { C as CurveDef } from '../types-BBuUk6nn.cjs';
1
+ import { C as CurveDef } from '../types-_f27GDkU.cjs';
2
2
 
3
3
  /**
4
4
  * Rose curve with 3 petals
@@ -1,4 +1,4 @@
1
- import { C as CurveDef } from '../types-BBuUk6nn.js';
1
+ import { C as CurveDef } from '../types-_f27GDkU.js';
2
2
 
3
3
  /**
4
4
  * Rose curve with 3 petals
@@ -1,4 +1,4 @@
1
- import { C as CurveDef } from '../types-BBuUk6nn.cjs';
1
+ import { C as CurveDef } from '../types-_f27GDkU.cjs';
2
2
 
3
3
  /**
4
4
  * Rose curve with 5 petals
@@ -1,4 +1,4 @@
1
- import { C as CurveDef } from '../types-BBuUk6nn.js';
1
+ import { C as CurveDef } from '../types-_f27GDkU.js';
2
2
 
3
3
  /**
4
4
  * Rose curve with 5 petals
@@ -1,4 +1,4 @@
1
- import { C as CurveDef } from '../types-BBuUk6nn.cjs';
1
+ import { C as CurveDef } from '../types-_f27GDkU.cjs';
2
2
 
3
3
  /**
4
4
  * Rose curve with n=5/2 that traces 5 petals over two full revolutions
@@ -1,4 +1,4 @@
1
- import { C as CurveDef } from '../types-BBuUk6nn.js';
1
+ import { C as CurveDef } from '../types-_f27GDkU.js';
2
2
 
3
3
  /**
4
4
  * Rose curve with n=5/2 that traces 5 petals over two full revolutions
@@ -1,4 +1,4 @@
1
- import { C as CurveDef } from '../types-BBuUk6nn.cjs';
1
+ import { C as CurveDef } from '../types-_f27GDkU.cjs';
2
2
 
3
3
  /**
4
4
  * 5-pointed star based on Fourier harmonics.
@@ -1,4 +1,4 @@
1
- import { C as CurveDef } from '../types-BBuUk6nn.js';
1
+ import { C as CurveDef } from '../types-_f27GDkU.js';
2
2
 
3
3
  /**
4
4
  * 5-pointed star based on Fourier harmonics.
@@ -1,4 +1,4 @@
1
- import { C as CurveDef } from '../types-BBuUk6nn.cjs';
1
+ import { C as CurveDef } from '../types-_f27GDkU.cjs';
2
2
 
3
3
  /**
4
4
  * 4-pointed star based on Fourier harmonics.
@@ -1,4 +1,4 @@
1
- import { C as CurveDef } from '../types-BBuUk6nn.js';
1
+ import { C as CurveDef } from '../types-_f27GDkU.js';
2
2
 
3
3
  /**
4
4
  * 4-pointed star based on Fourier harmonics.
@@ -1,4 +1,4 @@
1
- import { C as CurveDef } from '../types-BBuUk6nn.cjs';
1
+ import { C as CurveDef } from '../types-_f27GDkU.cjs';
2
2
 
3
3
  /**
4
4
  * 7-pointed star based on Fourier harmonics.
@@ -1,4 +1,4 @@
1
- import { C as CurveDef } from '../types-BBuUk6nn.js';
1
+ import { C as CurveDef } from '../types-_f27GDkU.js';
2
2
 
3
3
  /**
4
4
  * 7-pointed star based on Fourier harmonics.
package/dist/index.cjs CHANGED
@@ -520,6 +520,7 @@ function getPaletteColor(palette, position, timeOffset = 0) {
520
520
  return lerpOklab(c1, c2, t);
521
521
  }
522
522
  var TRAIL_STYLES = ["default", "gradient-static", "gradient-animated"];
523
+ var BASE_RENDER_OPTION_KEYS = /* @__PURE__ */ new Set(["trailColor", "trailStyle"]);
523
524
  var RENDER_OPTION_KEYS = /* @__PURE__ */ new Set([
524
525
  "trailColor",
525
526
  "headColor",
@@ -528,6 +529,19 @@ var RENDER_OPTION_KEYS = /* @__PURE__ */ new Set([
528
529
  "headRadius",
529
530
  "trailWidth"
530
531
  ]);
532
+ function validateBaseRenderOptions(partial) {
533
+ for (const key of Object.keys(partial)) {
534
+ if (!BASE_RENDER_OPTION_KEYS.has(key)) {
535
+ throw new TypeError(`[sarmal] setRenderOptions: unsupported key "${key}" for this renderer`);
536
+ }
537
+ }
538
+ if (partial.trailColor !== void 0) {
539
+ assertTrailColor(partial.trailColor);
540
+ }
541
+ if (partial.trailStyle !== void 0) {
542
+ assertTrailStyle(partial.trailStyle);
543
+ }
544
+ }
531
545
  function validateRenderOptions(partial) {
532
546
  for (const key of Object.keys(partial)) {
533
547
  if (!RENDER_OPTION_KEYS.has(key)) {
@@ -1403,6 +1417,371 @@ function createSarmalSVG(container, curveDef, options) {
1403
1417
  return createSVGRenderer({ container, engine, ...rendererOpts });
1404
1418
  }
1405
1419
 
1420
+ // src/renderer-dot-matrix.ts
1421
+ function createSarmalDotMatrix(canvas, curveDef, options) {
1422
+ const {
1423
+ cols = 32,
1424
+ rows = 32,
1425
+ roundness = 1,
1426
+ trailLength: trailLengthOpt,
1427
+ trailColor: initialColor = "#ffffff",
1428
+ trailStyle: initialTrailStyle = "default",
1429
+ autoStart = true,
1430
+ pauseOnHidden: pauseOnHiddenOpt = true,
1431
+ initialPhase
1432
+ } = options ?? {};
1433
+ const trailLength = trailLengthOpt ?? cols * 3;
1434
+ const engine = createEngine(curveDef, trailLength);
1435
+ if (!canvas.getContext("2d")) {
1436
+ throw new Error("[sarmal] Could not get 2d context from canvas");
1437
+ }
1438
+ const ctx = canvas.getContext("2d");
1439
+ const W = canvas.width;
1440
+ const H = canvas.height;
1441
+ const cellW = W / cols;
1442
+ const cellH = H / rows;
1443
+ const dotR = Math.min(cellW, cellH) * 0.36;
1444
+ let gradientRgb;
1445
+ if (Array.isArray(initialColor)) {
1446
+ validateBaseRenderOptions({ trailColor: initialColor });
1447
+ gradientRgb = initialColor.map(colorToRgb);
1448
+ } else {
1449
+ gradientRgb = null;
1450
+ }
1451
+ let colorRgb = gradientRgb ? gradientRgb[0] : colorToRgb(initialColor);
1452
+ let currentTrailStyle = initialTrailStyle;
1453
+ let animTime = 0;
1454
+ const ANIM_PERIOD = 6;
1455
+ const grid = new Float32Array(cols * rows);
1456
+ let scale = 1;
1457
+ let offsetX = 0;
1458
+ let offsetY = 0;
1459
+ let pixelMaskStarts = new Uint32Array(0);
1460
+ let pixelMaskLengths = new Uint32Array(0);
1461
+ let pixelMaskIndices = new Uint32Array(0);
1462
+ let pixelMaskCoverages = new Float32Array(0);
1463
+ let bgImageData = null;
1464
+ let frameImageData = null;
1465
+ let animationId = null;
1466
+ let lastTime = 0;
1467
+ let pausedByVisibility = false;
1468
+ let morphResolve = null;
1469
+ let morphReject = null;
1470
+ let morphDurationMs = DEFAULT_MORPH_DURATION_MS;
1471
+ let morphProgress = 0;
1472
+ function computePixelMask() {
1473
+ const starts = new Uint32Array(cols * rows);
1474
+ const lengths = new Uint32Array(cols * rows);
1475
+ const allIndices = [];
1476
+ const allCoverages = [];
1477
+ const cornerR = roundness * dotR;
1478
+ const cornerR2 = cornerR * cornerR;
1479
+ const SSAA = 4;
1480
+ const SSAA2 = SSAA * SSAA;
1481
+ for (let row = 0; row < rows; row++) {
1482
+ for (let col = 0; col < cols; col++) {
1483
+ const dotIdx = row * cols + col;
1484
+ const cx = (col + 0.5) * cellW;
1485
+ const cy = (row + 0.5) * cellH;
1486
+ const x0 = Math.max(0, Math.floor(cx - dotR - 1));
1487
+ const x1 = Math.min(W - 1, Math.ceil(cx + dotR + 1));
1488
+ const y0 = Math.max(0, Math.floor(cy - dotR - 1));
1489
+ const y1 = Math.min(H - 1, Math.ceil(cy + dotR + 1));
1490
+ starts[dotIdx] = allIndices.length;
1491
+ let count = 0;
1492
+ for (let py = y0; py <= y1; py++) {
1493
+ for (let px = x0; px <= x1; px++) {
1494
+ let hits = 0;
1495
+ for (let sy = 0; sy < SSAA; sy++) {
1496
+ const spyCenter = py + (sy + 0.5) / SSAA;
1497
+ for (let sx = 0; sx < SSAA; sx++) {
1498
+ const spxCenter = px + (sx + 0.5) / SSAA;
1499
+ const dx = Math.max(Math.abs(spxCenter - cx) - (dotR - cornerR), 0);
1500
+ const dy = Math.max(Math.abs(spyCenter - cy) - (dotR - cornerR), 0);
1501
+ if (dx * dx + dy * dy <= cornerR2) {
1502
+ hits++;
1503
+ }
1504
+ }
1505
+ }
1506
+ if (hits > 0) {
1507
+ allIndices.push((py * W + px) * 4);
1508
+ allCoverages.push(hits / SSAA2);
1509
+ count++;
1510
+ }
1511
+ }
1512
+ }
1513
+ lengths[dotIdx] = count;
1514
+ }
1515
+ }
1516
+ pixelMaskStarts = starts;
1517
+ pixelMaskLengths = lengths;
1518
+ pixelMaskIndices = new Uint32Array(allIndices);
1519
+ pixelMaskCoverages = new Float32Array(allCoverages);
1520
+ }
1521
+ function buildBgImageData() {
1522
+ bgImageData = new ImageData(W, H);
1523
+ const bg = gradientRgb ? gradientRgb[0] : colorRgb;
1524
+ const baseAlpha = 0.05 * 255;
1525
+ const { data } = bgImageData;
1526
+ const n = cols * rows;
1527
+ for (let dotIdx = 0; dotIdx < n; dotIdx++) {
1528
+ const start = pixelMaskStarts[dotIdx];
1529
+ const len = pixelMaskLengths[dotIdx];
1530
+ for (let k = 0; k < len; k++) {
1531
+ const px = pixelMaskIndices[start + k];
1532
+ const coverage = pixelMaskCoverages[start + k];
1533
+ data[px] = bg.r;
1534
+ data[px + 1] = bg.g;
1535
+ data[px + 2] = bg.b;
1536
+ data[px + 3] = Math.round(baseAlpha * coverage);
1537
+ }
1538
+ }
1539
+ }
1540
+ function sampleGradientRgb(stops, t) {
1541
+ const n = stops.length;
1542
+ const scaled = Math.max(0, Math.min(1, t)) * (n - 1);
1543
+ const i = Math.min(Math.floor(scaled), n - 2);
1544
+ const a = stops[i];
1545
+ const bStop = stops[i + 1];
1546
+ const mix = scaled - i;
1547
+ return {
1548
+ r: Math.round(a.r + (bStop.r - a.r) * mix),
1549
+ g: Math.round(a.g + (bStop.g - a.g) * mix),
1550
+ b: Math.round(a.b + (bStop.b - a.b) * mix)
1551
+ };
1552
+ }
1553
+ function calculateBoundaries(skel) {
1554
+ const b = computeBoundaries(skel, W, H);
1555
+ if (b) {
1556
+ scale = b.scale;
1557
+ offsetX = b.offsetX;
1558
+ offsetY = b.offsetY;
1559
+ }
1560
+ }
1561
+ function mapPt(x, y) {
1562
+ const px = x * scale + offsetX;
1563
+ const py = y * scale + offsetY;
1564
+ return [
1565
+ Math.max(0, Math.min(cols - 1, Math.round(px / W * (cols - 1)))),
1566
+ Math.max(0, Math.min(rows - 1, Math.round(py / H * (rows - 1))))
1567
+ ];
1568
+ }
1569
+ function stamp(c, r, intensity) {
1570
+ const idx = r * cols + c;
1571
+ if (intensity > grid[idx]) {
1572
+ grid[idx] = intensity;
1573
+ }
1574
+ }
1575
+ function buildGrid(deltaTime) {
1576
+ const trail = engine.tick(deltaTime);
1577
+ const count = engine.trailCount;
1578
+ grid.fill(0);
1579
+ for (let i = 0; i < count; i++) {
1580
+ const pt = trail[i];
1581
+ const intensity = (i + 1) / count;
1582
+ const [c, r] = mapPt(pt.x, pt.y);
1583
+ stamp(c, r, intensity);
1584
+ if (i < count - 1) {
1585
+ const next = trail[i + 1];
1586
+ const [nc, nr] = mapPt(next.x, next.y);
1587
+ const steps = Math.ceil(Math.max(Math.abs(nc - c), Math.abs(nr - r))) * 2;
1588
+ for (let s = 1; s < steps; s++) {
1589
+ const t = s / steps;
1590
+ const ix = pt.x + (next.x - pt.x) * t;
1591
+ const iy = pt.y + (next.y - pt.y) * t;
1592
+ const ii = intensity + 1 / count * t;
1593
+ const [ic, ir] = mapPt(ix, iy);
1594
+ stamp(ic, ir, ii);
1595
+ }
1596
+ }
1597
+ }
1598
+ }
1599
+ function draw() {
1600
+ if (!bgImageData || !frameImageData) {
1601
+ return;
1602
+ }
1603
+ frameImageData.data.set(bgImageData.data);
1604
+ const { data } = frameImageData;
1605
+ const sineOffset = currentTrailStyle === "gradient-animated" ? 0.15 * Math.sin(animTime / ANIM_PERIOD * 2 * Math.PI) : 0;
1606
+ const n = cols * rows;
1607
+ for (let dotIdx = 0; dotIdx < n; dotIdx++) {
1608
+ const intensity = grid[dotIdx];
1609
+ if (intensity <= 0) {
1610
+ continue;
1611
+ }
1612
+ let r, g, b;
1613
+ if (gradientRgb !== null) {
1614
+ const t = Math.max(0, Math.min(1, intensity + sineOffset));
1615
+ ({ r, g, b } = sampleGradientRgb(gradientRgb, t));
1616
+ } else {
1617
+ ({ r, g, b } = colorRgb);
1618
+ }
1619
+ const baseA = (0.08 + intensity * 0.92) * 255;
1620
+ const start = pixelMaskStarts[dotIdx];
1621
+ const len = pixelMaskLengths[dotIdx];
1622
+ for (let k = 0; k < len; k++) {
1623
+ const px = pixelMaskIndices[start + k];
1624
+ const coverage = pixelMaskCoverages[start + k];
1625
+ data[px] = r;
1626
+ data[px + 1] = g;
1627
+ data[px + 2] = b;
1628
+ data[px + 3] = Math.round(baseA * coverage);
1629
+ }
1630
+ }
1631
+ ctx.putImageData(frameImageData, 0, 0);
1632
+ }
1633
+ function renderFrame(deltaTime) {
1634
+ if (engine.morphAlpha !== null) {
1635
+ morphProgress = Math.min(1, morphProgress + deltaTime / (morphDurationMs / 1e3));
1636
+ engine.setMorphAlpha(morphProgress);
1637
+ calculateBoundaries(engine.getSarmalSkeleton());
1638
+ if (morphProgress >= 1) {
1639
+ engine.completeMorph();
1640
+ morphResolve?.();
1641
+ morphResolve = null;
1642
+ morphReject = null;
1643
+ morphProgress = 0;
1644
+ calculateBoundaries(engine.getSarmalSkeleton());
1645
+ }
1646
+ } else if (engine.isLiveSkeleton) {
1647
+ calculateBoundaries(engine.getSarmalSkeleton());
1648
+ }
1649
+ if (currentTrailStyle === "gradient-animated") {
1650
+ animTime += deltaTime;
1651
+ }
1652
+ buildGrid(deltaTime);
1653
+ draw();
1654
+ }
1655
+ function loop(timestamp = performance.now()) {
1656
+ const deltaTime = Math.min((timestamp - lastTime) / 1e3, 1 / 30);
1657
+ lastTime = timestamp;
1658
+ renderFrame(deltaTime);
1659
+ animationId = requestAnimationFrame(loop);
1660
+ }
1661
+ calculateBoundaries(engine.getSarmalSkeleton());
1662
+ computePixelMask();
1663
+ frameImageData = new ImageData(W, H);
1664
+ buildBgImageData();
1665
+ if (initialPhase !== void 0) {
1666
+ engine.seek(initialPhase);
1667
+ }
1668
+ renderFrame(0);
1669
+ const instance = {
1670
+ /** Starts the animation loop. Does nothing if already running. */
1671
+ play() {
1672
+ if (animationId !== null) return;
1673
+ lastTime = performance.now();
1674
+ loop();
1675
+ },
1676
+ /** Pauses the animation loop. Preserves current trail state. */
1677
+ pause() {
1678
+ if (animationId === null) return;
1679
+ cancelAnimationFrame(animationId);
1680
+ animationId = null;
1681
+ engine.cancelSpeedTransition();
1682
+ },
1683
+ /** Resets the animation to the start of the curve and clears the grid. */
1684
+ reset() {
1685
+ engine.reset();
1686
+ grid.fill(0);
1687
+ },
1688
+ /** Stops the animation and removes all event listeners. */
1689
+ destroy() {
1690
+ if (animationId !== null) {
1691
+ cancelAnimationFrame(animationId);
1692
+ animationId = null;
1693
+ }
1694
+ document.removeEventListener("visibilitychange", handleVisibilityChange);
1695
+ if (morphReject !== null) {
1696
+ morphReject(new Error("[sarmal] Instance destroyed during morph"));
1697
+ morphResolve = null;
1698
+ morphReject = null;
1699
+ }
1700
+ },
1701
+ ...enginePassthroughs(engine),
1702
+ /**
1703
+ * Smoothly transitions from the current curve to `target`.
1704
+ * If a morph is already in progress, it is snapped to completion before the new one starts.
1705
+ * @returns A Promise that resolves when the transition finishes.
1706
+ */
1707
+ morphTo(target, opts) {
1708
+ if (morphResolve !== null) {
1709
+ engine.completeMorph();
1710
+ morphResolve();
1711
+ morphResolve = null;
1712
+ morphReject = null;
1713
+ morphProgress = 0;
1714
+ }
1715
+ morphDurationMs = opts?.duration ?? DEFAULT_MORPH_DURATION_MS;
1716
+ morphProgress = 0;
1717
+ engine.startMorph(target, opts?.morphStrategy);
1718
+ return new Promise((resolve, reject) => {
1719
+ morphResolve = resolve;
1720
+ morphReject = reject;
1721
+ });
1722
+ },
1723
+ /**
1724
+ * Updates render options on a live instance without stopping the animation.
1725
+ *
1726
+ * Supported: `trailColor` and `trailStyle`.
1727
+ * ! Unsupported fields (`headColor`, `skeletonColor`, `headRadius`, `trailWidth`) throw.
1728
+ * ! Validation fails the entire call if any field is invalid, leaving options unchanged.
1729
+ */
1730
+ setRenderOptions(partial) {
1731
+ validateBaseRenderOptions(partial);
1732
+ let needsRebuildBg = false;
1733
+ if (partial.trailColor !== void 0) {
1734
+ if (Array.isArray(partial.trailColor)) {
1735
+ gradientRgb = partial.trailColor.map(colorToRgb);
1736
+ colorRgb = gradientRgb[0];
1737
+ } else {
1738
+ gradientRgb = null;
1739
+ colorRgb = colorToRgb(partial.trailColor);
1740
+ }
1741
+ needsRebuildBg = true;
1742
+ }
1743
+ if (partial.trailStyle !== void 0) {
1744
+ currentTrailStyle = partial.trailStyle;
1745
+ if (currentTrailStyle === "default") {
1746
+ animTime = 0;
1747
+ }
1748
+ }
1749
+ if (needsRebuildBg) {
1750
+ buildBgImageData();
1751
+ }
1752
+ if (currentTrailStyle !== "default" && gradientRgb === null) {
1753
+ console.warn(
1754
+ "[sarmal] dot matrix: gradient trailStyle has no effect without a trailColor array"
1755
+ );
1756
+ }
1757
+ }
1758
+ };
1759
+ function handleVisibilityChange() {
1760
+ if (document.hidden) {
1761
+ if (animationId !== null) {
1762
+ instance.pause();
1763
+ pausedByVisibility = true;
1764
+ }
1765
+ } else {
1766
+ if (pausedByVisibility) {
1767
+ pausedByVisibility = false;
1768
+ instance.play();
1769
+ }
1770
+ }
1771
+ }
1772
+ if (pauseOnHiddenOpt) {
1773
+ document.addEventListener("visibilitychange", handleVisibilityChange);
1774
+ }
1775
+ const shouldAutoStart = autoStart !== false;
1776
+ const actuallyAutoStart = shouldAutoStart && !(pauseOnHiddenOpt && document.hidden);
1777
+ if (actuallyAutoStart) {
1778
+ instance.play();
1779
+ } else if (shouldAutoStart) {
1780
+ pausedByVisibility = true;
1781
+ }
1782
+ return instance;
1783
+ }
1784
+
1406
1785
  // src/catmull-rom.ts
1407
1786
  var PERIOD = 2 * Math.PI;
1408
1787
  function catmullRom1D(p0, p1, p2, p3, u) {
@@ -1739,6 +2118,7 @@ exports.createEngine = createEngine;
1739
2118
  exports.createRenderer = createRenderer;
1740
2119
  exports.createSVGRenderer = createSVGRenderer;
1741
2120
  exports.createSarmal = createSarmal;
2121
+ exports.createSarmalDotMatrix = createSarmalDotMatrix;
1742
2122
  exports.createSarmalSVG = createSarmalSVG;
1743
2123
  exports.curves = curves;
1744
2124
  exports.deltoid = deltoid;