@sarmal/core 0.31.0 → 0.34.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.
- package/README.md +2 -2
- package/dist/auto-init.cjs +42 -4
- package/dist/auto-init.cjs.map +1 -1
- package/dist/auto-init.js +42 -4
- package/dist/auto-init.js.map +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/curves/artemis2.d.cts +1 -1
- package/dist/curves/artemis2.d.ts +1 -1
- package/dist/curves/astroid.d.cts +1 -1
- package/dist/curves/astroid.d.ts +1 -1
- package/dist/curves/deltoid.d.cts +1 -1
- package/dist/curves/deltoid.d.ts +1 -1
- package/dist/curves/epicycloid3.d.cts +1 -1
- package/dist/curves/epicycloid3.d.ts +1 -1
- package/dist/curves/epitrochoid7.d.cts +1 -1
- package/dist/curves/epitrochoid7.d.ts +1 -1
- package/dist/curves/index.d.cts +1 -1
- package/dist/curves/index.d.ts +1 -1
- package/dist/curves/lame.d.cts +1 -1
- package/dist/curves/lame.d.ts +1 -1
- package/dist/curves/lissajous32.d.cts +1 -1
- package/dist/curves/lissajous32.d.ts +1 -1
- package/dist/curves/lissajous43.d.cts +1 -1
- package/dist/curves/lissajous43.d.ts +1 -1
- package/dist/curves/rose3.d.cts +1 -1
- package/dist/curves/rose3.d.ts +1 -1
- package/dist/curves/rose5.d.cts +1 -1
- package/dist/curves/rose5.d.ts +1 -1
- package/dist/curves/rose52.d.cts +1 -1
- package/dist/curves/rose52.d.ts +1 -1
- package/dist/curves/star.d.cts +1 -1
- package/dist/curves/star.d.ts +1 -1
- package/dist/curves/star4.d.cts +1 -1
- package/dist/curves/star4.d.ts +1 -1
- package/dist/curves/star7.d.cts +1 -1
- package/dist/curves/star7.d.ts +1 -1
- package/dist/index.cjs +375 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +80 -4
- package/dist/index.d.ts +80 -4
- package/dist/index.js +375 -8
- package/dist/index.js.map +1 -1
- package/dist/{renderer-shared-jqw_Q1WO.d.cts → renderer-shared-C3KCEABq.d.cts} +8 -4
- package/dist/{renderer-shared-OR--cv-t.d.ts → renderer-shared-DyOI68gd.d.ts} +8 -4
- package/dist/terminal.cjs.map +1 -1
- package/dist/terminal.d.cts +2 -2
- package/dist/terminal.d.ts +2 -2
- package/dist/terminal.js.map +1 -1
- package/dist/{types-zbxUgcmZ.d.cts → types-_f27GDkU.d.cts} +30 -9
- package/dist/{types-zbxUgcmZ.d.ts → types-_f27GDkU.d.ts} +30 -9
- package/package.json +4 -4
package/dist/curves/rose3.d.cts
CHANGED
package/dist/curves/rose3.d.ts
CHANGED
package/dist/curves/rose5.d.cts
CHANGED
package/dist/curves/rose5.d.ts
CHANGED
package/dist/curves/rose52.d.cts
CHANGED
package/dist/curves/rose52.d.ts
CHANGED
package/dist/curves/star.d.cts
CHANGED
package/dist/curves/star.d.ts
CHANGED
package/dist/curves/star4.d.cts
CHANGED
package/dist/curves/star4.d.ts
CHANGED
package/dist/curves/star7.d.cts
CHANGED
package/dist/curves/star7.d.ts
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -375,11 +375,15 @@ function enginePassthroughs(engine) {
|
|
|
375
375
|
}
|
|
376
376
|
var palettes = {
|
|
377
377
|
bard: ["#a855f7", "#3b82f6", "#14b8a6", "#ec4899"],
|
|
378
|
-
|
|
378
|
+
carnival: ["#ff6b6b", "#4ecdc4", "#ffe66d"],
|
|
379
379
|
ocean: ["#1e3a8a", "#06b6d4", "#22d3ee", "#e0f2fe"],
|
|
380
|
+
sunset: ["#f97316", "#dc2626", "#9333ea", "#f472b6"],
|
|
380
381
|
ice: ["#1e3a8a", "#67e8f9"],
|
|
381
|
-
|
|
382
|
-
|
|
382
|
+
rocketpop: ["#08b8cd", "#ffffff", "#ff001f"],
|
|
383
|
+
neon: ["#00e5ff", "#7c3aed", "#e040fb"],
|
|
384
|
+
vaporwave: ["#ff71ce", "#01cdfe", "#b967ff"],
|
|
385
|
+
pastel: ["#c4b5fd", "#fbcfe8", "#bae6fd"],
|
|
386
|
+
sakura: ["#fff1f2", "#fda4af", "#fb7185"]
|
|
383
387
|
};
|
|
384
388
|
function hexToRgb(hex) {
|
|
385
389
|
const n = parseInt(hex.slice(1), 16);
|
|
@@ -516,13 +520,28 @@ function getPaletteColor(palette, position, timeOffset = 0) {
|
|
|
516
520
|
return lerpOklab(c1, c2, t);
|
|
517
521
|
}
|
|
518
522
|
var TRAIL_STYLES = ["default", "gradient-static", "gradient-animated"];
|
|
523
|
+
var BASE_RENDER_OPTION_KEYS = /* @__PURE__ */ new Set(["trailColor", "trailStyle"]);
|
|
519
524
|
var RENDER_OPTION_KEYS = /* @__PURE__ */ new Set([
|
|
520
525
|
"trailColor",
|
|
521
526
|
"headColor",
|
|
522
527
|
"skeletonColor",
|
|
523
528
|
"trailStyle",
|
|
524
|
-
"headRadius"
|
|
529
|
+
"headRadius",
|
|
530
|
+
"trailWidth"
|
|
525
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
|
+
}
|
|
526
545
|
function validateRenderOptions(partial) {
|
|
527
546
|
for (const key of Object.keys(partial)) {
|
|
528
547
|
if (!RENDER_OPTION_KEYS.has(key)) {
|
|
@@ -544,6 +563,9 @@ function validateRenderOptions(partial) {
|
|
|
544
563
|
if (partial.headRadius !== void 0) {
|
|
545
564
|
assertHeadRadius(partial.headRadius);
|
|
546
565
|
}
|
|
566
|
+
if (partial.trailWidth !== void 0) {
|
|
567
|
+
assertTrailWidth(partial.trailWidth);
|
|
568
|
+
}
|
|
547
569
|
}
|
|
548
570
|
function assertTrailColor(value) {
|
|
549
571
|
if (typeof value === "string") {
|
|
@@ -613,6 +635,18 @@ function assertHeadRadius(value) {
|
|
|
613
635
|
);
|
|
614
636
|
}
|
|
615
637
|
}
|
|
638
|
+
function assertTrailWidth(value) {
|
|
639
|
+
if (typeof value !== "number") {
|
|
640
|
+
throw new TypeError(
|
|
641
|
+
`[sarmal] setRenderOptions: trailWidth must be a number, got ${JSON.stringify(value)}`
|
|
642
|
+
);
|
|
643
|
+
}
|
|
644
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
645
|
+
throw new TypeError(
|
|
646
|
+
`[sarmal] setRenderOptions: trailWidth must be a finite positive number, got ${value}`
|
|
647
|
+
);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
616
650
|
function resolveTrailMainColor(trailColor) {
|
|
617
651
|
return typeof trailColor === "string" ? trailColor : trailColor[0];
|
|
618
652
|
}
|
|
@@ -681,7 +715,14 @@ function createRenderer(options) {
|
|
|
681
715
|
setupCanvas();
|
|
682
716
|
let logicalWidth = canvas.width / dpr;
|
|
683
717
|
let logicalHeight = canvas.height / dpr;
|
|
718
|
+
if (options.headRadius !== void 0) {
|
|
719
|
+
validateRenderOptions({ headRadius: options.headRadius });
|
|
720
|
+
}
|
|
721
|
+
if (options.trailWidth !== void 0) {
|
|
722
|
+
validateRenderOptions({ trailWidth: options.trailWidth });
|
|
723
|
+
}
|
|
684
724
|
let headRadius = options.headRadius ?? getHeadDotRadius(logicalWidth, logicalHeight);
|
|
725
|
+
let trailWidth = options.trailWidth ?? 1;
|
|
685
726
|
let skeleton = [];
|
|
686
727
|
let skeletonCanvas = null;
|
|
687
728
|
let trail = [];
|
|
@@ -775,7 +816,9 @@ function createRenderer(options) {
|
|
|
775
816
|
i,
|
|
776
817
|
trailCount,
|
|
777
818
|
toX,
|
|
778
|
-
toY
|
|
819
|
+
toY,
|
|
820
|
+
TRAIL_MIN_WIDTH * trailWidth,
|
|
821
|
+
TRAIL_MAX_WIDTH * trailWidth
|
|
779
822
|
);
|
|
780
823
|
if (trailStyle === "default") {
|
|
781
824
|
ctx.fillStyle = `rgba(${trailSolidRgb},${opacity})`;
|
|
@@ -933,6 +976,9 @@ function createRenderer(options) {
|
|
|
933
976
|
if (partial.headRadius !== void 0) {
|
|
934
977
|
headRadius = partial.headRadius;
|
|
935
978
|
}
|
|
979
|
+
if (partial.trailWidth !== void 0) {
|
|
980
|
+
trailWidth = partial.trailWidth;
|
|
981
|
+
}
|
|
936
982
|
if (userHeadColor === null) {
|
|
937
983
|
headColor = resolveHeadColor(trailColor, trailStyle);
|
|
938
984
|
} else {
|
|
@@ -1031,7 +1077,14 @@ function createSVGRenderer(options) {
|
|
|
1031
1077
|
const svgTrailMinWidth = TRAIL_MIN_WIDTH * viewSize / containerPx;
|
|
1032
1078
|
const svgTrailMaxWidth = TRAIL_MAX_WIDTH * viewSize / containerPx;
|
|
1033
1079
|
const svgSkeletonStrokeWidth = String(SKELETON_STROKE_WIDTH_PX * viewSize / containerPx);
|
|
1080
|
+
if (options.headRadius !== void 0) {
|
|
1081
|
+
validateRenderOptions({ headRadius: options.headRadius });
|
|
1082
|
+
}
|
|
1083
|
+
if (options.trailWidth !== void 0) {
|
|
1084
|
+
validateRenderOptions({ trailWidth: options.trailWidth });
|
|
1085
|
+
}
|
|
1034
1086
|
headRadius = options.headRadius ?? SVG_DEFAULT_HEAD_RADIUS;
|
|
1087
|
+
let trailWidth = options.trailWidth ?? 1;
|
|
1035
1088
|
container.setAttribute("viewBox", `0 0 ${viewSize} ${viewSize}`);
|
|
1036
1089
|
container.setAttribute("role", "img");
|
|
1037
1090
|
container.setAttribute("aria-label", ariaLabel);
|
|
@@ -1126,8 +1179,8 @@ function createSVGRenderer(options) {
|
|
|
1126
1179
|
trailCount,
|
|
1127
1180
|
px,
|
|
1128
1181
|
py,
|
|
1129
|
-
svgTrailMinWidth,
|
|
1130
|
-
svgTrailMaxWidth
|
|
1182
|
+
svgTrailMinWidth * trailWidth,
|
|
1183
|
+
svgTrailMaxWidth * trailWidth
|
|
1131
1184
|
);
|
|
1132
1185
|
const d = `M${l0x.toFixed(2)} ${l0y.toFixed(2)} L${l1x.toFixed(2)} ${l1y.toFixed(2)} L${r1x.toFixed(2)} ${r1y.toFixed(2)} L${r0x.toFixed(2)} ${r0y.toFixed(2)} Z`;
|
|
1133
1186
|
trailPaths[i].setAttribute("d", d);
|
|
@@ -1319,6 +1372,9 @@ function createSVGRenderer(options) {
|
|
|
1319
1372
|
headRadius = partial.headRadius;
|
|
1320
1373
|
headCircle.setAttribute("r", String(headRadius));
|
|
1321
1374
|
}
|
|
1375
|
+
if (partial.trailWidth !== void 0) {
|
|
1376
|
+
trailWidth = partial.trailWidth;
|
|
1377
|
+
}
|
|
1322
1378
|
if (userHeadColor === null) {
|
|
1323
1379
|
headColor = resolveHeadColor(trailColor, trailStyle);
|
|
1324
1380
|
} else {
|
|
@@ -1361,6 +1417,317 @@ function createSarmalSVG(container, curveDef, options) {
|
|
|
1361
1417
|
return createSVGRenderer({ container, engine, ...rendererOpts });
|
|
1362
1418
|
}
|
|
1363
1419
|
|
|
1420
|
+
// src/renderer-dot-matrix.ts
|
|
1421
|
+
var NUM_BUCKETS = 8;
|
|
1422
|
+
function createSarmalDotMatrix(canvas, curveDef, options) {
|
|
1423
|
+
const {
|
|
1424
|
+
cols = 32,
|
|
1425
|
+
rows = 32,
|
|
1426
|
+
roundness = 1,
|
|
1427
|
+
trailLength: trailLengthOpt,
|
|
1428
|
+
trailColor: initialColor = "#ffffff",
|
|
1429
|
+
trailStyle: initialTrailStyle = "default",
|
|
1430
|
+
autoStart = true,
|
|
1431
|
+
pauseOnHidden: pauseOnHiddenOpt = true,
|
|
1432
|
+
initialPhase
|
|
1433
|
+
} = options ?? {};
|
|
1434
|
+
const trailLength = trailLengthOpt ?? cols * 3;
|
|
1435
|
+
const engine = createEngine(curveDef, trailLength);
|
|
1436
|
+
if (!canvas.getContext("2d")) {
|
|
1437
|
+
throw new Error("[sarmal] Could not get 2d context from canvas");
|
|
1438
|
+
}
|
|
1439
|
+
const ctx = canvas.getContext("2d");
|
|
1440
|
+
const W = canvas.width;
|
|
1441
|
+
const H = canvas.height;
|
|
1442
|
+
const cellW = W / cols;
|
|
1443
|
+
const cellH = H / rows;
|
|
1444
|
+
const dotR = Math.min(cellW, cellH) * 0.36;
|
|
1445
|
+
let gradientRgb;
|
|
1446
|
+
if (Array.isArray(initialColor)) {
|
|
1447
|
+
validateBaseRenderOptions({ trailColor: initialColor });
|
|
1448
|
+
gradientRgb = initialColor.map(colorToRgb);
|
|
1449
|
+
} else {
|
|
1450
|
+
gradientRgb = null;
|
|
1451
|
+
}
|
|
1452
|
+
let colorRgb = gradientRgb ? gradientRgb[0] : colorToRgb(initialColor);
|
|
1453
|
+
let currentTrailStyle = initialTrailStyle;
|
|
1454
|
+
let animTime = 0;
|
|
1455
|
+
const ANIM_PERIOD = 6;
|
|
1456
|
+
const grid = new Float32Array(cols * rows);
|
|
1457
|
+
let scale = 1;
|
|
1458
|
+
let offsetX = 0;
|
|
1459
|
+
let offsetY = 0;
|
|
1460
|
+
let bgCanvas = null;
|
|
1461
|
+
let animationId = null;
|
|
1462
|
+
let lastTime = 0;
|
|
1463
|
+
let pausedByVisibility = false;
|
|
1464
|
+
let morphResolve = null;
|
|
1465
|
+
let morphReject = null;
|
|
1466
|
+
let morphDurationMs = DEFAULT_MORPH_DURATION_MS;
|
|
1467
|
+
let morphProgress = 0;
|
|
1468
|
+
function buildBgCanvas() {
|
|
1469
|
+
bgCanvas = new OffscreenCanvas(W, H);
|
|
1470
|
+
const bgCtx = bgCanvas.getContext("2d");
|
|
1471
|
+
const bg = gradientRgb ? gradientRgb[0] : colorRgb;
|
|
1472
|
+
bgCtx.fillStyle = `rgba(${bg.r},${bg.g},${bg.b},0.05)`;
|
|
1473
|
+
bgCtx.beginPath();
|
|
1474
|
+
for (let row = 0; row < rows; row++) {
|
|
1475
|
+
for (let col = 0; col < cols; col++) {
|
|
1476
|
+
const cx = (col + 0.5) * cellW;
|
|
1477
|
+
const cy = (row + 0.5) * cellH;
|
|
1478
|
+
bgCtx.roundRect(cx - dotR, cy - dotR, dotR * 2, dotR * 2, roundness * dotR);
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
bgCtx.fill();
|
|
1482
|
+
}
|
|
1483
|
+
function sampleGradientRgb(stops, t) {
|
|
1484
|
+
const n = stops.length;
|
|
1485
|
+
const scaled = Math.max(0, Math.min(1, t)) * (n - 1);
|
|
1486
|
+
const i = Math.min(Math.floor(scaled), n - 2);
|
|
1487
|
+
const a = stops[i];
|
|
1488
|
+
const bStop = stops[i + 1];
|
|
1489
|
+
const mix = scaled - i;
|
|
1490
|
+
return {
|
|
1491
|
+
r: Math.round(a.r + (bStop.r - a.r) * mix),
|
|
1492
|
+
g: Math.round(a.g + (bStop.g - a.g) * mix),
|
|
1493
|
+
b: Math.round(a.b + (bStop.b - a.b) * mix)
|
|
1494
|
+
};
|
|
1495
|
+
}
|
|
1496
|
+
function calculateBoundaries(skel) {
|
|
1497
|
+
const b = computeBoundaries(skel, W, H);
|
|
1498
|
+
if (b) {
|
|
1499
|
+
scale = b.scale;
|
|
1500
|
+
offsetX = b.offsetX;
|
|
1501
|
+
offsetY = b.offsetY;
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
function mapPt(x, y) {
|
|
1505
|
+
const px = x * scale + offsetX;
|
|
1506
|
+
const py = y * scale + offsetY;
|
|
1507
|
+
return [
|
|
1508
|
+
Math.max(0, Math.min(cols - 1, Math.round(px / W * (cols - 1)))),
|
|
1509
|
+
Math.max(0, Math.min(rows - 1, Math.round(py / H * (rows - 1))))
|
|
1510
|
+
];
|
|
1511
|
+
}
|
|
1512
|
+
function stamp(c, r, intensity) {
|
|
1513
|
+
const idx = r * cols + c;
|
|
1514
|
+
if (intensity > grid[idx]) {
|
|
1515
|
+
grid[idx] = intensity;
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
function buildGrid(deltaTime) {
|
|
1519
|
+
const trail = engine.tick(deltaTime);
|
|
1520
|
+
const count = engine.trailCount;
|
|
1521
|
+
grid.fill(0);
|
|
1522
|
+
for (let i = 0; i < count; i++) {
|
|
1523
|
+
const pt = trail[i];
|
|
1524
|
+
const intensity = (i + 1) / count;
|
|
1525
|
+
const [c, r] = mapPt(pt.x, pt.y);
|
|
1526
|
+
stamp(c, r, intensity);
|
|
1527
|
+
if (i < count - 1) {
|
|
1528
|
+
const next = trail[i + 1];
|
|
1529
|
+
const [nc, nr] = mapPt(next.x, next.y);
|
|
1530
|
+
const steps = Math.ceil(Math.max(Math.abs(nc - c), Math.abs(nr - r))) * 2;
|
|
1531
|
+
for (let s = 1; s < steps; s++) {
|
|
1532
|
+
const t = s / steps;
|
|
1533
|
+
const ix = pt.x + (next.x - pt.x) * t;
|
|
1534
|
+
const iy = pt.y + (next.y - pt.y) * t;
|
|
1535
|
+
const ii = intensity + 1 / count * t;
|
|
1536
|
+
const [ic, ir] = mapPt(ix, iy);
|
|
1537
|
+
stamp(ic, ir, ii);
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
function draw() {
|
|
1543
|
+
ctx.clearRect(0, 0, W, H);
|
|
1544
|
+
if (bgCanvas) {
|
|
1545
|
+
ctx.drawImage(bgCanvas, 0, 0);
|
|
1546
|
+
}
|
|
1547
|
+
const animOffset = currentTrailStyle === "gradient-animated" ? Math.abs(animTime / ANIM_PERIOD % 2 - 1) * 0.35 : 0;
|
|
1548
|
+
for (let bucket = 0; bucket < NUM_BUCKETS; bucket++) {
|
|
1549
|
+
const lo = bucket / NUM_BUCKETS;
|
|
1550
|
+
const hi = (bucket + 1) / NUM_BUCKETS;
|
|
1551
|
+
const midpoint = (lo + hi) / 2;
|
|
1552
|
+
const alpha = 0.08 + midpoint * 0.92;
|
|
1553
|
+
let hasLit = false;
|
|
1554
|
+
ctx.beginPath();
|
|
1555
|
+
for (let row = 0; row < rows; row++) {
|
|
1556
|
+
for (let col = 0; col < cols; col++) {
|
|
1557
|
+
const intensity = grid[row * cols + col];
|
|
1558
|
+
if (intensity > lo && intensity <= hi) {
|
|
1559
|
+
const cx = (col + 0.5) * cellW;
|
|
1560
|
+
const cy = (row + 0.5) * cellH;
|
|
1561
|
+
ctx.roundRect(cx - dotR, cy - dotR, dotR * 2, dotR * 2, roundness * dotR);
|
|
1562
|
+
hasLit = true;
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
if (hasLit) {
|
|
1567
|
+
if (gradientRgb !== null) {
|
|
1568
|
+
const t = ((midpoint + animOffset) % 1 + 1) % 1;
|
|
1569
|
+
const { r, g, b } = sampleGradientRgb(gradientRgb, t);
|
|
1570
|
+
ctx.fillStyle = `rgb(${r},${g},${b})`;
|
|
1571
|
+
} else {
|
|
1572
|
+
const { r, g, b } = colorRgb;
|
|
1573
|
+
ctx.fillStyle = `rgb(${r},${g},${b})`;
|
|
1574
|
+
}
|
|
1575
|
+
ctx.globalAlpha = alpha;
|
|
1576
|
+
ctx.fill();
|
|
1577
|
+
}
|
|
1578
|
+
}
|
|
1579
|
+
ctx.globalAlpha = 1;
|
|
1580
|
+
}
|
|
1581
|
+
function renderFrame(deltaTime) {
|
|
1582
|
+
if (engine.morphAlpha !== null) {
|
|
1583
|
+
morphProgress = Math.min(1, morphProgress + deltaTime / (morphDurationMs / 1e3));
|
|
1584
|
+
engine.setMorphAlpha(morphProgress);
|
|
1585
|
+
calculateBoundaries(engine.getSarmalSkeleton());
|
|
1586
|
+
if (morphProgress >= 1) {
|
|
1587
|
+
engine.completeMorph();
|
|
1588
|
+
morphResolve?.();
|
|
1589
|
+
morphResolve = null;
|
|
1590
|
+
morphReject = null;
|
|
1591
|
+
morphProgress = 0;
|
|
1592
|
+
calculateBoundaries(engine.getSarmalSkeleton());
|
|
1593
|
+
}
|
|
1594
|
+
} else if (engine.isLiveSkeleton) {
|
|
1595
|
+
calculateBoundaries(engine.getSarmalSkeleton());
|
|
1596
|
+
}
|
|
1597
|
+
if (currentTrailStyle === "gradient-animated") {
|
|
1598
|
+
animTime += deltaTime;
|
|
1599
|
+
}
|
|
1600
|
+
buildGrid(deltaTime);
|
|
1601
|
+
draw();
|
|
1602
|
+
}
|
|
1603
|
+
function loop(timestamp = performance.now()) {
|
|
1604
|
+
const deltaTime = Math.min((timestamp - lastTime) / 1e3, 1 / 30);
|
|
1605
|
+
lastTime = timestamp;
|
|
1606
|
+
renderFrame(deltaTime);
|
|
1607
|
+
animationId = requestAnimationFrame(loop);
|
|
1608
|
+
}
|
|
1609
|
+
calculateBoundaries(engine.getSarmalSkeleton());
|
|
1610
|
+
buildBgCanvas();
|
|
1611
|
+
if (initialPhase !== void 0) {
|
|
1612
|
+
engine.seek(initialPhase);
|
|
1613
|
+
}
|
|
1614
|
+
renderFrame(0);
|
|
1615
|
+
const instance = {
|
|
1616
|
+
/** Starts the animation loop. Does nothing if already running. */
|
|
1617
|
+
play() {
|
|
1618
|
+
if (animationId !== null) return;
|
|
1619
|
+
lastTime = performance.now();
|
|
1620
|
+
loop();
|
|
1621
|
+
},
|
|
1622
|
+
/** Pauses the animation loop. Preserves current trail state. */
|
|
1623
|
+
pause() {
|
|
1624
|
+
if (animationId === null) return;
|
|
1625
|
+
cancelAnimationFrame(animationId);
|
|
1626
|
+
animationId = null;
|
|
1627
|
+
engine.cancelSpeedTransition();
|
|
1628
|
+
},
|
|
1629
|
+
/** Resets the animation to the start of the curve and clears the grid. */
|
|
1630
|
+
reset() {
|
|
1631
|
+
engine.reset();
|
|
1632
|
+
grid.fill(0);
|
|
1633
|
+
},
|
|
1634
|
+
/** Stops the animation and removes all event listeners. */
|
|
1635
|
+
destroy() {
|
|
1636
|
+
if (animationId !== null) {
|
|
1637
|
+
cancelAnimationFrame(animationId);
|
|
1638
|
+
animationId = null;
|
|
1639
|
+
}
|
|
1640
|
+
document.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
1641
|
+
if (morphReject !== null) {
|
|
1642
|
+
morphReject(new Error("[sarmal] Instance destroyed during morph"));
|
|
1643
|
+
morphResolve = null;
|
|
1644
|
+
morphReject = null;
|
|
1645
|
+
}
|
|
1646
|
+
},
|
|
1647
|
+
...enginePassthroughs(engine),
|
|
1648
|
+
/**
|
|
1649
|
+
* Smoothly transitions from the current curve to `target`.
|
|
1650
|
+
* If a morph is already in progress, it is snapped to completion before the new one starts.
|
|
1651
|
+
* @returns A Promise that resolves when the transition finishes.
|
|
1652
|
+
*/
|
|
1653
|
+
morphTo(target, opts) {
|
|
1654
|
+
if (morphResolve !== null) {
|
|
1655
|
+
engine.completeMorph();
|
|
1656
|
+
morphResolve();
|
|
1657
|
+
morphResolve = null;
|
|
1658
|
+
morphReject = null;
|
|
1659
|
+
morphProgress = 0;
|
|
1660
|
+
}
|
|
1661
|
+
morphDurationMs = opts?.duration ?? DEFAULT_MORPH_DURATION_MS;
|
|
1662
|
+
morphProgress = 0;
|
|
1663
|
+
engine.startMorph(target, opts?.morphStrategy);
|
|
1664
|
+
return new Promise((resolve, reject) => {
|
|
1665
|
+
morphResolve = resolve;
|
|
1666
|
+
morphReject = reject;
|
|
1667
|
+
});
|
|
1668
|
+
},
|
|
1669
|
+
/**
|
|
1670
|
+
* Updates render options on a live instance without stopping the animation.
|
|
1671
|
+
*
|
|
1672
|
+
* Supported: `trailColor` and `trailStyle`.
|
|
1673
|
+
* ! Unsupported fields (`headColor`, `skeletonColor`, `headRadius`, `trailWidth`) throw.
|
|
1674
|
+
* ! Validation fails the entire call if any field is invalid, leaving options unchanged.
|
|
1675
|
+
*/
|
|
1676
|
+
setRenderOptions(partial) {
|
|
1677
|
+
validateBaseRenderOptions(partial);
|
|
1678
|
+
let needsRebuildBg = false;
|
|
1679
|
+
if (partial.trailColor !== void 0) {
|
|
1680
|
+
if (Array.isArray(partial.trailColor)) {
|
|
1681
|
+
gradientRgb = partial.trailColor.map(colorToRgb);
|
|
1682
|
+
colorRgb = gradientRgb[0];
|
|
1683
|
+
} else {
|
|
1684
|
+
gradientRgb = null;
|
|
1685
|
+
colorRgb = colorToRgb(partial.trailColor);
|
|
1686
|
+
}
|
|
1687
|
+
needsRebuildBg = true;
|
|
1688
|
+
}
|
|
1689
|
+
if (partial.trailStyle !== void 0) {
|
|
1690
|
+
currentTrailStyle = partial.trailStyle;
|
|
1691
|
+
if (currentTrailStyle === "default") {
|
|
1692
|
+
animTime = 0;
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
if (needsRebuildBg) {
|
|
1696
|
+
buildBgCanvas();
|
|
1697
|
+
}
|
|
1698
|
+
if (currentTrailStyle !== "default" && gradientRgb === null) {
|
|
1699
|
+
console.warn(
|
|
1700
|
+
"[sarmal] dot matrix: gradient trailStyle has no effect without a trailColor array"
|
|
1701
|
+
);
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
};
|
|
1705
|
+
function handleVisibilityChange() {
|
|
1706
|
+
if (document.hidden) {
|
|
1707
|
+
if (animationId !== null) {
|
|
1708
|
+
instance.pause();
|
|
1709
|
+
pausedByVisibility = true;
|
|
1710
|
+
}
|
|
1711
|
+
} else {
|
|
1712
|
+
if (pausedByVisibility) {
|
|
1713
|
+
pausedByVisibility = false;
|
|
1714
|
+
instance.play();
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
if (pauseOnHiddenOpt) {
|
|
1719
|
+
document.addEventListener("visibilitychange", handleVisibilityChange);
|
|
1720
|
+
}
|
|
1721
|
+
const shouldAutoStart = autoStart !== false;
|
|
1722
|
+
const actuallyAutoStart = shouldAutoStart && !(pauseOnHiddenOpt && document.hidden);
|
|
1723
|
+
if (actuallyAutoStart) {
|
|
1724
|
+
instance.play();
|
|
1725
|
+
} else if (shouldAutoStart) {
|
|
1726
|
+
pausedByVisibility = true;
|
|
1727
|
+
}
|
|
1728
|
+
return instance;
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1364
1731
|
// src/catmull-rom.ts
|
|
1365
1732
|
var PERIOD = 2 * Math.PI;
|
|
1366
1733
|
function catmullRom1D(p0, p1, p2, p3, u) {
|
|
@@ -1697,6 +2064,7 @@ exports.createEngine = createEngine;
|
|
|
1697
2064
|
exports.createRenderer = createRenderer;
|
|
1698
2065
|
exports.createSVGRenderer = createSVGRenderer;
|
|
1699
2066
|
exports.createSarmal = createSarmal;
|
|
2067
|
+
exports.createSarmalDotMatrix = createSarmalDotMatrix;
|
|
1700
2068
|
exports.createSarmalSVG = createSarmalSVG;
|
|
1701
2069
|
exports.curves = curves;
|
|
1702
2070
|
exports.deltoid = deltoid;
|