reframe-video 0.2.0 → 0.4.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/dist/index.js CHANGED
@@ -15,22 +15,22 @@ function locate(segCount, u) {
15
15
  return { i, t: scaled - i };
16
16
  }
17
17
  function controls(points, closed, i) {
18
- const n = points.length;
18
+ const n3 = points.length;
19
19
  const at = (k) => {
20
- if (closed) return points[(k % n + n) % n];
21
- return points[Math.max(0, Math.min(n - 1, k))];
20
+ if (closed) return points[(k % n3 + n3) % n3];
21
+ return points[Math.max(0, Math.min(n3 - 1, k))];
22
22
  };
23
23
  return [at(i - 1), at(i), at(i + 1), at(i + 2)];
24
24
  }
25
25
  function segCountOf(points, closed) {
26
- const n = points.length;
27
- if (n < 2) return 0;
28
- return closed ? n : n - 1;
26
+ const n3 = points.length;
27
+ if (n3 < 2) return 0;
28
+ return closed ? n3 : n3 - 1;
29
29
  }
30
30
  function pathPoint(points, closed, u, curviness = 1) {
31
- const n = points.length;
32
- if (n === 0) return [0, 0];
33
- if (n === 1) return [points[0][0], points[0][1]];
31
+ const n3 = points.length;
32
+ if (n3 === 0) return [0, 0];
33
+ if (n3 === 1) return [points[0][0], points[0][1]];
34
34
  const segs = segCountOf(points, closed);
35
35
  const { i, t } = locate(segs, u);
36
36
  const [p0, p1, p2, p3] = controls(points, closed, i);
@@ -49,8 +49,8 @@ function pathPoint(points, closed, u, curviness = 1) {
49
49
  return [H(p0[0], p1[0], p2[0], p3[0]), H(p0[1], p1[1], p2[1], p3[1])];
50
50
  }
51
51
  function pathTangentAngle(points, closed, u, curviness = 1) {
52
- const n = points.length;
53
- if (n < 2) return 0;
52
+ const n3 = points.length;
53
+ if (n3 < 2) return 0;
54
54
  const segs = segCountOf(points, closed);
55
55
  const { i, t } = locate(segs, u);
56
56
  const [p0, p1, p2, p3] = controls(points, closed, i);
@@ -816,7 +816,7 @@ function applyOverlay(ir, overlay, layer, report, baseNodeIds) {
816
816
  );
817
817
  continue;
818
818
  }
819
- const index = ir.nodes.findIndex((n) => n.id === id);
819
+ const index = ir.nodes.findIndex((n3) => n3.id === id);
820
820
  if (index < 0) {
821
821
  orphan(
822
822
  `removeNodes.${id}`,
@@ -884,14 +884,14 @@ function makeRng(seed) {
884
884
  var clamp01 = (x) => Math.max(0, Math.min(1, x));
885
885
  var SET = 1 / 120;
886
886
  function ctx(o) {
887
- const rand = makeRng((o.seed ?? 0) + 1);
887
+ const rand2 = makeRng((o.seed ?? 0) + 1);
888
888
  return {
889
889
  e: clamp01(o.energy ?? 0.5),
890
890
  sp: Math.max(0.25, o.speed ?? 1),
891
891
  it: clamp01(o.intensity ?? 0.5),
892
892
  from: o.from,
893
- rand,
894
- jit: (amp) => (rand() - 0.5) * 2 * amp,
893
+ rand: rand2,
894
+ jit: (amp) => (rand2() - 0.5) * 2 * amp,
895
895
  g: o.target.group,
896
896
  cx: o.target.center[0],
897
897
  cy: o.target.center[1],
@@ -1263,9 +1263,936 @@ function devicePreset(name, opts = {}) {
1263
1263
  );
1264
1264
  }
1265
1265
 
1266
+ // ../core/src/rig.ts
1267
+ var DEFAULT_LINE = "#FFE3D2";
1268
+ var DEFAULT_FILL = "#0E1424";
1269
+ var LINE_W = 5;
1270
+ var GLOW_W = 16;
1271
+ var K = 0.5523;
1272
+ var n = (v) => Number(v.toFixed(2));
1273
+ function capsulePath(hw, len) {
1274
+ const yT = hw;
1275
+ const yB = Math.max(hw, len - hw);
1276
+ const k = hw * K;
1277
+ return `M ${n(-hw)} ${n(yT)} C ${n(-hw)} ${n(yT - k)} ${n(-k)} ${n(yT - hw)} 0 ${n(yT - hw)} C ${n(k)} ${n(yT - hw)} ${n(hw)} ${n(yT - k)} ${n(hw)} ${n(yT)} L ${n(hw)} ${n(yB)} C ${n(hw)} ${n(yB + k)} ${n(k)} ${n(yB + hw)} 0 ${n(yB + hw)} C ${n(-k)} ${n(yB + hw)} ${n(-hw)} ${n(yB + k)} ${n(-hw)} ${n(yB)} Z`;
1278
+ }
1279
+ function ovalPath(a, b, cx = 0, cy = 0) {
1280
+ const ka = n(a * K), kb = n(b * K);
1281
+ const t = n(cy - b), bo = n(cy + b), c = n(cy), A = n(a), L = n(-a), X = n(cx);
1282
+ return `M ${X} ${t} C ${n(cx + ka)} ${t} ${n(cx + A)} ${n(cy - kb)} ${n(cx + A)} ${c} C ${n(cx + A)} ${n(cy + kb)} ${n(cx + ka)} ${bo} ${X} ${bo} C ${n(cx - ka)} ${bo} ${n(cx + L)} ${n(cy + kb)} ${n(cx + L)} ${c} C ${n(cx + L)} ${n(cy - kb)} ${n(cx - ka)} ${t} ${X} ${t} Z`;
1283
+ }
1284
+ function boneShape(jointId, bone, o) {
1285
+ if (bone.shape) return bone.shape;
1286
+ const len = bone.length ?? 0;
1287
+ if (len <= 0) return [];
1288
+ const d = capsulePath((bone.width ?? 20) / 2, len);
1289
+ const nodes = [];
1290
+ if (o.glow) nodes.push(path({ id: `${jointId}-glow`, d, x: 0, y: 0, fill: "none", stroke: o.glow, strokeWidth: GLOW_W, opacity: 0.18 }));
1291
+ nodes.push(path({ id: `${jointId}-shape`, d, x: 0, y: 0, fill: o.fill, stroke: o.color, strokeWidth: LINE_W }));
1292
+ return nodes;
1293
+ }
1294
+ function buildBone(bone, id, o) {
1295
+ const jointId = `${id}-${bone.name}`;
1296
+ return group(
1297
+ { id: jointId, x: bone.at[0], y: bone.at[1], rotation: bone.rotation ?? 0 },
1298
+ [...boneShape(jointId, bone, o), ...(bone.children ?? []).map((c) => buildBone(c, id, o))]
1299
+ );
1300
+ }
1301
+ function rig(root, opts = {}) {
1302
+ const id = opts.id ?? "rig";
1303
+ const o = { color: opts.color ?? DEFAULT_LINE, fill: opts.fill ?? DEFAULT_FILL, glow: opts.glow };
1304
+ return group(
1305
+ { id, x: opts.x ?? 0, y: opts.y ?? 0, scale: opts.scale ?? 1, opacity: opts.opacity ?? 1 },
1306
+ [buildBone(root, id, o)]
1307
+ );
1308
+ }
1309
+ function rigPose(id, pose) {
1310
+ const out = {};
1311
+ for (const [name, deg] of Object.entries(pose)) out[`${id}-${name}`] = { rotation: deg };
1312
+ return out;
1313
+ }
1314
+ function poseTo(id, pose, opts = {}) {
1315
+ const tweens = Object.entries(pose).map(
1316
+ ([name, deg]) => tween(`${id}-${name}`, { rotation: deg }, { duration: opts.duration ?? 0.5, ease: opts.ease ?? "easeInOutCubic" })
1317
+ );
1318
+ return opts.stagger ? stagger(opts.stagger, ...tweens) : par(...tweens);
1319
+ }
1320
+ function ikReach(upper, lower, dx, dy, flip = false) {
1321
+ const D = Math.hypot(dx, dy);
1322
+ const cos2 = Math.max(-1, Math.min(1, (D * D - upper * upper - lower * lower) / (2 * upper * lower)));
1323
+ const theta2 = (flip ? -1 : 1) * Math.acos(cos2);
1324
+ const vx = -lower * Math.sin(theta2);
1325
+ const vy = upper + lower * Math.cos(theta2);
1326
+ const theta1 = Math.atan2(dy, dx) - Math.atan2(vy, vx);
1327
+ const deg = (r) => r * 180 / Math.PI;
1328
+ return [deg(theta1), deg(theta2)];
1329
+ }
1330
+ function humanoid(opts = {}) {
1331
+ const line2 = opts.color ?? DEFAULT_LINE;
1332
+ const fill = opts.fill ?? DEFAULT_FILL;
1333
+ const glow = opts.glow;
1334
+ const blob = (jid, a, b, cy) => {
1335
+ const d = ovalPath(a, b, 0, cy);
1336
+ const nodes = [];
1337
+ if (glow) nodes.push(path({ id: `${jid}-glow`, d, x: 0, y: 0, fill: "none", stroke: glow, strokeWidth: GLOW_W, opacity: 0.18 }));
1338
+ nodes.push(path({ id: `${jid}-shape`, d, x: 0, y: 0, fill, stroke: line2, strokeWidth: LINE_W }));
1339
+ return nodes;
1340
+ };
1341
+ const id = opts.id ?? "rig";
1342
+ const root = {
1343
+ name: "chest",
1344
+ at: [0, 0],
1345
+ shape: blob(`${id}-chest`, 44, 62, 22),
1346
+ children: [
1347
+ { name: "head", at: [0, -42], rotation: 0, shape: blob(`${id}-head`, 40, 42, -34) },
1348
+ { name: "armUpperL", at: [-42, -20], length: 60, width: 20, rotation: 10, children: [
1349
+ { name: "armLowerL", at: [0, 60], length: 56, width: 16, rotation: 8 }
1350
+ ] },
1351
+ { name: "armUpperR", at: [42, -20], length: 60, width: 20, rotation: -10, children: [
1352
+ { name: "armLowerR", at: [0, 60], length: 56, width: 16, rotation: -8 }
1353
+ ] },
1354
+ { name: "legUpperL", at: [-20, 76], length: 76, width: 26, rotation: 3, children: [
1355
+ { name: "legLowerL", at: [0, 76], length: 72, width: 22, rotation: -2 }
1356
+ ] },
1357
+ { name: "legUpperR", at: [20, 76], length: 76, width: 26, rotation: -3, children: [
1358
+ { name: "legLowerR", at: [0, 76], length: 72, width: 22, rotation: 2 }
1359
+ ] }
1360
+ ]
1361
+ };
1362
+ return rig(root, opts);
1363
+ }
1364
+
1365
+ // ../core/src/characterPreset.ts
1366
+ var CHARACTER_PRESET_NAMES = ["walk", "run", "jump", "dance", "wave", "cheer"];
1367
+ var THIGH = 76;
1368
+ var SHIN = 72;
1369
+ var clamp012 = (x) => Math.max(0, Math.min(1, x));
1370
+ function makeRng2(seed) {
1371
+ let a = seed >>> 0 || 2654435769;
1372
+ return () => {
1373
+ a = a + 1831565813 | 0;
1374
+ let t = Math.imul(a ^ a >>> 15, 1 | a);
1375
+ t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
1376
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
1377
+ };
1378
+ }
1379
+ var dur2 = (base, sp) => base / sp;
1380
+ function ctx2(o) {
1381
+ const rand2 = makeRng2((o.seed ?? 0) + 1);
1382
+ return {
1383
+ g: o.target,
1384
+ label: o.label,
1385
+ e: clamp012(o.energy ?? 0.5),
1386
+ sp: Math.max(0.25, o.speed ?? 1),
1387
+ cycles: Math.max(1, Math.round(o.cycles ?? 4)),
1388
+ facing: o.facing ?? 1,
1389
+ at: o.at ?? [0, 0],
1390
+ travel: o.travel,
1391
+ rand: rand2,
1392
+ jit: (amp) => (rand2() - 0.5) * 2 * amp
1393
+ };
1394
+ }
1395
+ var round = (v) => Math.round(v * 1e3) / 1e3;
1396
+ function footPos(p, stride, lift) {
1397
+ p = (p % 1 + 1) % 1;
1398
+ if (p < 0.5) {
1399
+ const u2 = p / 0.5;
1400
+ return [stride * (1 - 2 * u2), 138];
1401
+ }
1402
+ const u = (p - 0.5) / 0.5;
1403
+ return [-stride + 2 * stride * u, 138 - Math.sin(Math.PI * u) * lift];
1404
+ }
1405
+ function gaitPose(ph, stride, lift, armSwing, facing) {
1406
+ const fl = footPos(ph, stride, lift);
1407
+ const fr = footPos(ph + 0.5, stride, lift);
1408
+ const [hipL, kneeL] = ikReach(THIGH, SHIN, facing * fl[0], fl[1], facing < 0);
1409
+ const [hipR, kneeR] = ikReach(THIGH, SHIN, facing * fr[0], fr[1], facing < 0);
1410
+ const swing = Math.cos(2 * Math.PI * ph);
1411
+ return {
1412
+ legUpperL: round(hipL),
1413
+ legLowerL: round(kneeL),
1414
+ legUpperR: round(hipR),
1415
+ legLowerR: round(kneeR),
1416
+ armUpperR: round(-10 - armSwing * swing),
1417
+ armLowerR: -16,
1418
+ armUpperL: round(10 + armSwing * swing),
1419
+ armLowerL: 16
1420
+ };
1421
+ }
1422
+ function gait(c, run) {
1423
+ const stride = (run ? 34 : 24) + (run ? 40 : 30) * c.e + c.jit(3);
1424
+ const lift = run ? 40 : 26;
1425
+ const armSwing = (run ? 26 : 16) + 20 * c.e;
1426
+ const halfDur = (run ? 0.26 : 0.42) + c.jit(0.02);
1427
+ const lean = run ? c.facing * -6 : 0;
1428
+ const steps = c.cycles * 2;
1429
+ const d = dur2(halfDur, c.sp);
1430
+ const intro = dur2(0.16, c.sp);
1431
+ const keys = [];
1432
+ for (let k = 0; k <= steps; k++) {
1433
+ const pose = { ...gaitPose(k / 2, stride, lift, armSwing, c.facing), chest: lean };
1434
+ keys.push(poseTo(c.g, pose, { duration: k === 0 ? intro : d, ease: k === 0 ? "easeOutQuad" : "linear" }));
1435
+ }
1436
+ const total = intro + steps * d;
1437
+ const travel = c.travel ?? stride * 2;
1438
+ const children = [seq(...keys)];
1439
+ if (travel !== 0) {
1440
+ children.push(tween(c.g, { x: c.at[0] + c.facing * travel * c.cycles }, { duration: total, ease: "linear", label: "travel" }));
1441
+ }
1442
+ return beat(run ? "run" : "walk", {}, [par(...children)]);
1443
+ }
1444
+ function jumpBeat(c) {
1445
+ const h = 120 + 150 * c.e;
1446
+ const [y0] = [c.at[1]];
1447
+ const CROUCH = { legUpperL: 18, legLowerL: 54, legUpperR: -18, legLowerR: 54, armUpperL: 28, armUpperR: -28 };
1448
+ const LAUNCH = { legUpperL: 0, legLowerL: 0, legUpperR: 0, legLowerR: 0, armUpperL: 150, armUpperR: -150 };
1449
+ const TUCK = { legUpperL: -28, legLowerL: 66, legUpperR: -28, legLowerR: 66, armUpperL: 124, armUpperR: -124 };
1450
+ const REST = { legUpperL: 3, legLowerL: -2, legUpperR: -3, legLowerR: 2, armUpperL: 10, armLowerL: 8, armUpperR: -10, armLowerR: -8 };
1451
+ const j = c.jit(0.03);
1452
+ return beat("jump", {}, [
1453
+ seq(
1454
+ par(poseTo(c.g, CROUCH, { duration: dur2(0.24, c.sp), ease: "easeOutQuad" }), tween(c.g, { y: y0 + 26 }, { duration: dur2(0.24, c.sp), ease: "easeOutQuad" })),
1455
+ par(poseTo(c.g, LAUNCH, { duration: dur2(0.22 + j, c.sp), ease: "easeOutCubic" }), tween(c.g, { y: y0 - h }, { duration: dur2(0.36, c.sp), ease: "easeOutCubic", label: "launch" })),
1456
+ poseTo(c.g, TUCK, { duration: dur2(0.22, c.sp) }),
1457
+ par(poseTo(c.g, CROUCH, { duration: dur2(0.28, c.sp), ease: "easeInQuad" }), tween(c.g, { y: y0 + 18 }, { duration: dur2(0.3, c.sp), ease: "easeInCubic", label: "land" })),
1458
+ par(poseTo(c.g, REST, { duration: dur2(0.45, c.sp), ease: "easeOutBack" }), tween(c.g, { y: y0 }, { duration: dur2(0.45, c.sp), ease: "easeOutBack" }))
1459
+ )
1460
+ ]);
1461
+ }
1462
+ function danceBeat(c) {
1463
+ const y0 = c.at[1];
1464
+ const sway = 8 + 6 * c.e;
1465
+ const armUp = 130 + 30 * c.e;
1466
+ const A = { chest: sway, head: -sway * 0.5, armUpperR: -armUp, armLowerR: -20, armUpperL: 40, armLowerL: 30, legUpperL: 8, legUpperR: -2 };
1467
+ const B = { chest: -sway, head: sway * 0.5, armUpperL: armUp, armLowerL: 20, armUpperR: -40, armLowerR: -30, legUpperL: 2, legUpperR: -8 };
1468
+ const d = dur2(0.34, c.sp);
1469
+ const keys = [];
1470
+ for (let k = 0; k < c.cycles * 2; k++) {
1471
+ const pose = k % 2 === 0 ? A : B;
1472
+ keys.push(par(
1473
+ poseTo(c.g, pose, { duration: d, ease: "easeInOutQuad" }),
1474
+ tween(c.g, { y: y0 - (k % 2 === 0 ? 14 : 0) }, { duration: d, ease: "easeInOutQuad" })
1475
+ ));
1476
+ }
1477
+ keys.push(tween(c.g, { y: y0 }, { duration: d }));
1478
+ return beat("dance", {}, [seq(...keys)]);
1479
+ }
1480
+ function waveBeat(c) {
1481
+ const n3 = 3 + Math.round(c.rand() * 2);
1482
+ const amp = 16 + 10 * c.e;
1483
+ const steps = [poseTo(c.g, { armUpperR: -150, armLowerR: -24 }, { duration: dur2(0.4, c.sp), ease: "easeOutBack" })];
1484
+ for (let k = 0; k < n3; k++) {
1485
+ steps.push(poseTo(c.g, { armLowerR: -24 + (k % 2 === 0 ? amp : -amp) }, { duration: dur2(0.22, c.sp), ease: "easeInOutQuad" }));
1486
+ }
1487
+ steps.push(poseTo(c.g, { armUpperR: -10, armLowerR: -8 }, { duration: dur2(0.4, c.sp), ease: "easeInOutCubic" }));
1488
+ return beat("wave", {}, [seq(...steps)]);
1489
+ }
1490
+ function cheerBeat(c) {
1491
+ const y0 = c.at[1];
1492
+ const UP = { armUpperL: 152, armLowerL: 8, armUpperR: -152, armLowerR: -8 };
1493
+ const d = dur2(0.3, c.sp);
1494
+ const keys = [poseTo(c.g, UP, { duration: dur2(0.35, c.sp), ease: "easeOutBack" })];
1495
+ for (let k = 0; k < c.cycles; k++) {
1496
+ keys.push(par(tween(c.g, { y: y0 - 28 }, { duration: d, ease: "easeOutQuad" }), poseTo(c.g, { armUpperL: 160, armUpperR: -160 }, { duration: d })));
1497
+ keys.push(par(tween(c.g, { y: y0 }, { duration: d, ease: "easeInQuad" }), poseTo(c.g, { armUpperL: 145, armUpperR: -145 }, { duration: d })));
1498
+ }
1499
+ return beat("cheer", {}, [seq(...keys)]);
1500
+ }
1501
+ function characterPreset(name, opts) {
1502
+ const c = ctx2(opts);
1503
+ let tl;
1504
+ switch (name) {
1505
+ case "walk":
1506
+ tl = gait(c, false);
1507
+ break;
1508
+ case "run":
1509
+ tl = gait(c, true);
1510
+ break;
1511
+ case "jump":
1512
+ tl = jumpBeat(c);
1513
+ break;
1514
+ case "dance":
1515
+ tl = danceBeat(c);
1516
+ break;
1517
+ case "wave":
1518
+ tl = waveBeat(c);
1519
+ break;
1520
+ case "cheer":
1521
+ tl = cheerBeat(c);
1522
+ break;
1523
+ default: {
1524
+ const _exhaustive = name;
1525
+ throw new Error(`unknown characterPreset "${_exhaustive}"`);
1526
+ }
1527
+ }
1528
+ return c.label && tl.kind === "beat" ? { ...tl, name: c.label } : tl;
1529
+ }
1530
+
1531
+ // ../core/src/figure.ts
1532
+ var K2 = 0.5523;
1533
+ var n2 = (v) => Number(v.toFixed(2));
1534
+ function limb(a, b, y0, y1) {
1535
+ const ka = n2(a * K2), kb = n2(b * K2);
1536
+ return `M ${-a} ${y0} C ${-a} ${n2(y0 - ka)} ${-ka} ${n2(y0 - a)} 0 ${n2(y0 - a)} C ${ka} ${n2(y0 - a)} ${a} ${n2(y0 - ka)} ${a} ${y0} L ${b} ${y1} C ${b} ${n2(y1 + kb)} ${kb} ${n2(y1 + b)} 0 ${n2(y1 + b)} C ${-kb} ${n2(y1 + b)} ${-b} ${n2(y1 + kb)} ${-b} ${y1} Z`;
1537
+ }
1538
+ function rrect(a, b, y0, y1, r) {
1539
+ return `M ${n2(-a + r)} ${y0} L ${n2(a - r)} ${y0} Q ${a} ${y0} ${a} ${n2(y0 + r)} L ${b} ${n2(y1 - r)} Q ${b} ${y1} ${n2(b - r)} ${y1} L ${n2(-b + r)} ${y1} Q ${-b} ${y1} ${-b} ${n2(y1 - r)} L ${-a} ${n2(y0 + r)} Q ${-a} ${y0} ${n2(-a + r)} ${y0} Z`;
1540
+ }
1541
+ function darken(hex, f) {
1542
+ const h = hex.replace("#", "");
1543
+ const v = parseInt(h.length === 3 ? [...h].map((c) => c + c).join("") : h, 16);
1544
+ const ch = (s) => Math.max(0, Math.min(255, Math.round((v >> s & 255) * (1 - f))));
1545
+ const hx = (x) => x.toString(16).padStart(2, "0");
1546
+ return `#${hx(ch(16))}${hx(ch(8))}${hx(ch(0))}`;
1547
+ }
1548
+ var DEF = {
1549
+ clean: { skin: "#E9B58E", hair: "#2B313F", top: "#E86C4A", pants: "#39425C", shoe: "#20242F", accent: "#E86C4A" },
1550
+ cute: { skin: "#FFD2A6", hair: "#5B4636", top: "#FF7E5F", pants: "#3E6F8E", shoe: "#272B38", accent: "#FF7E5F" }
1551
+ };
1552
+ function resolvePal(style, p = {}) {
1553
+ const d = DEF[style];
1554
+ const accent = p.accent ?? d.accent;
1555
+ const top = p.top ?? (style === "clean" ? accent : d.top);
1556
+ const skin = p.skin ?? d.skin;
1557
+ const hair = p.hair ?? d.hair;
1558
+ const pants = p.pants ?? d.pants;
1559
+ const shoe = p.shoe ?? d.shoe;
1560
+ return {
1561
+ skin,
1562
+ skinSh: darken(skin, 0.12),
1563
+ hair,
1564
+ hairSh: darken(hair, 0.14),
1565
+ top,
1566
+ topSh: darken(top, 0.12),
1567
+ pants,
1568
+ pantsSh: darken(pants, 0.14),
1569
+ shoe,
1570
+ shoeSh: darken(shoe, 0.22),
1571
+ eye: "#2B313F",
1572
+ cheek: "#FF9E7E",
1573
+ white: "#FFFFFF",
1574
+ mouth: "#8A4233"
1575
+ };
1576
+ }
1577
+ var fp = (id, d, fill, stroke, sw = 0, opacity = 1) => path({ id, d, x: 0, y: 0, fill, opacity, ...stroke && sw > 0 ? { stroke, strokeWidth: sw } : {} });
1578
+ function cleanParts(p, face) {
1579
+ const HC = -42;
1580
+ return {
1581
+ upperArm: (j) => [fp(`${j}-sleeve`, limb(12, 10, 2, 58), p.top)],
1582
+ forearm: (j) => [
1583
+ fp(`${j}-elbow`, ovalPath(10, 10, 0, 3), p.skin),
1584
+ fp(`${j}-fore`, limb(10, 8, 2, 48), p.skin),
1585
+ fp(`${j}-hand`, ovalPath(11, 12, 0, 50), p.skin)
1586
+ ],
1587
+ thigh: (j) => [fp(`${j}-thigh`, limb(15, 13, 2, 72), p.pants)],
1588
+ shin: (j) => [
1589
+ fp(`${j}-knee`, ovalPath(13, 13, 0, 2), p.pants),
1590
+ fp(`${j}-shin`, limb(13, 11, 2, 62), p.pants),
1591
+ fp(`${j}-shoe`, ovalPath(15, 9, 4, 67), p.shoe)
1592
+ ],
1593
+ torso: (j) => [
1594
+ fp(`${j}-shadow`, rrect(38, 26, -28, 52, 20), p.topSh),
1595
+ fp(`${j}-top`, rrect(40, 27, -30, 52, 22), p.top),
1596
+ fp(`${j}-pelvis`, rrect(29, 24, 46, 104, 14), p.pants)
1597
+ ],
1598
+ head: (j) => [
1599
+ fp(`${j}-neck`, rrect(9, 9, 2, 22, 5), p.skin),
1600
+ fp(`${j}-skin`, ovalPath(42, 46, 0, HC), p.skin),
1601
+ fp(`${j}-hair`, ovalPath(44, 27, 0, HC - 31), p.hair),
1602
+ fp(`${j}-hairL`, ovalPath(8, 14, -39, HC - 18), p.hair),
1603
+ fp(`${j}-hairR`, ovalPath(8, 14, 39, HC - 18), p.hair),
1604
+ ...face ? [fp(`${j}-eyeL`, ovalPath(5, 7, -14, HC + 2), p.eye), fp(`${j}-eyeR`, ovalPath(5, 7, 14, HC + 2), p.eye)] : []
1605
+ ]
1606
+ };
1607
+ }
1608
+ var CUTE_HAIR = "M -64 -54 C -78 -96 -50 -126 0 -126 C 50 -126 78 -96 64 -54 C 60 -34 48 -28 41 -33 C 35 -54 23 -60 9 -60 C 3 -60 -3 -60 -9 -60 C -23 -60 -35 -54 -41 -33 C -48 -28 -60 -34 -64 -54 Z";
1609
+ function cuteParts(p, face) {
1610
+ const HC = -50;
1611
+ return {
1612
+ upperArm: (j) => [fp(`${j}-sleeve`, limb(15, 13, 2, 58), p.top, p.topSh, 2.5)],
1613
+ forearm: (j) => [
1614
+ fp(`${j}-elbow`, ovalPath(12, 12, 0, 3), p.skin, p.skinSh, 2.5),
1615
+ fp(`${j}-fore`, limb(12, 10, 2, 46), p.skin, p.skinSh, 2.5),
1616
+ fp(`${j}-hand`, ovalPath(13, 14, 0, 50), p.skin, p.skinSh, 2.5)
1617
+ ],
1618
+ thigh: (j) => [fp(`${j}-thigh`, limb(19, 16, 2, 72), p.pants, p.pantsSh, 2.5)],
1619
+ shin: (j) => [
1620
+ fp(`${j}-knee`, ovalPath(16, 16, 0, 2), p.pants, p.pantsSh, 2.5),
1621
+ fp(`${j}-shin`, limb(15, 12, 2, 60), p.pants, p.pantsSh, 2.5),
1622
+ fp(`${j}-shoe`, ovalPath(18, 11, 5, 66), p.shoe, darken(p.shoe, 0.25), 2.5)
1623
+ ],
1624
+ torso: (j) => [
1625
+ fp(`${j}-shadow`, rrect(40, 34, -18, 52, 16), p.topSh),
1626
+ fp(`${j}-top`, rrect(42, 35, -20, 52, 18), p.top, p.topSh, 2.5),
1627
+ fp(`${j}-pelvis`, rrect(36, 30, 46, 104, 16), p.pants, p.pantsSh, 2.5)
1628
+ ],
1629
+ head: (j) => [
1630
+ fp(`${j}-neck`, ovalPath(15, 12, 0, 12), p.skinSh),
1631
+ fp(`${j}-skin`, ovalPath(42, 46, 0, HC + 4), p.skin, p.skinSh, 2.5),
1632
+ fp(`${j}-cheekL`, ovalPath(11, 8, -40, HC + 22), p.cheek, void 0, 0, 0.5),
1633
+ fp(`${j}-cheekR`, ovalPath(11, 8, 40, HC + 22), p.cheek, void 0, 0, 0.5),
1634
+ fp(`${j}-hair`, CUTE_HAIR, p.hair, p.hairSh, 2),
1635
+ ...face ? [
1636
+ fp(`${j}-eyeL`, ovalPath(10, 13, -25, HC + 8), p.eye),
1637
+ fp(`${j}-eyeR`, ovalPath(10, 13, 25, HC + 8), p.eye),
1638
+ fp(`${j}-glL`, ovalPath(3.4, 3.4, -28, HC + 3), p.white),
1639
+ fp(`${j}-glR`, ovalPath(3.4, 3.4, 22, HC + 3), p.white),
1640
+ path({ id: `${j}-mouth`, d: "M -15 0 Q 0 15 15 0", x: 0, y: HC + 28, fill: "none", stroke: p.mouth, strokeWidth: 5 })
1641
+ ] : []
1642
+ ]
1643
+ };
1644
+ }
1645
+ function buildSkeleton(id, S) {
1646
+ const arm = (side, x, r1, r2) => ({
1647
+ name: `armUpper${side}`,
1648
+ at: [x, -14],
1649
+ length: 60,
1650
+ width: 0,
1651
+ rotation: r1,
1652
+ shape: S.upperArm(`${id}-armUpper${side}`),
1653
+ children: [{ name: `armLower${side}`, at: [0, 60], length: 56, width: 0, rotation: r2, shape: S.forearm(`${id}-armLower${side}`) }]
1654
+ });
1655
+ const leg = (side, x, r1, r2) => ({
1656
+ name: `legUpper${side}`,
1657
+ at: [x, 76],
1658
+ length: 76,
1659
+ width: 0,
1660
+ rotation: r1,
1661
+ shape: S.thigh(`${id}-legUpper${side}`),
1662
+ children: [{ name: `legLower${side}`, at: [0, 76], length: 72, width: 0, rotation: r2, shape: S.shin(`${id}-legLower${side}`) }]
1663
+ });
1664
+ return {
1665
+ name: "chest",
1666
+ at: [0, 0],
1667
+ shape: S.torso(`${id}-chest`),
1668
+ children: [
1669
+ { name: "head", at: [0, -42], rotation: 0, shape: S.head(`${id}-head`) },
1670
+ arm("L", -40, 8, 6),
1671
+ arm("R", 40, -8, -6),
1672
+ leg("L", -20, 3, -2),
1673
+ leg("R", 20, -3, 2)
1674
+ ]
1675
+ };
1676
+ }
1677
+ function figure(opts = {}) {
1678
+ const style = opts.style ?? "clean";
1679
+ const pal = resolvePal(style, opts.palette);
1680
+ const face = opts.face ?? true;
1681
+ const id = opts.id ?? "figure";
1682
+ const parts = style === "clean" ? cleanParts(pal, face) : cuteParts(pal, face);
1683
+ const rigOpts = { id };
1684
+ if (opts.x !== void 0) rigOpts.x = opts.x;
1685
+ if (opts.y !== void 0) rigOpts.y = opts.y;
1686
+ if (opts.scale !== void 0) rigOpts.scale = opts.scale;
1687
+ if (opts.opacity !== void 0) rigOpts.opacity = opts.opacity;
1688
+ return rig(buildSkeleton(id, parts), rigOpts);
1689
+ }
1690
+
1691
+ // ../core/src/textMetrics.ts
1692
+ var INTER_ADVANCE = {
1693
+ "400": {
1694
+ "0": 63.09,
1695
+ "1": 40.67,
1696
+ "2": 60.99,
1697
+ "3": 61.77,
1698
+ "4": 64.6,
1699
+ "5": 59.33,
1700
+ "6": 62.01,
1701
+ "7": 56.59,
1702
+ "8": 61.87,
1703
+ "9": 62.01,
1704
+ " ": 28.13,
1705
+ "!": 28.76,
1706
+ '"': 46.58,
1707
+ "#": 63.33,
1708
+ "$": 64.16,
1709
+ "%": 98.19,
1710
+ "&": 64.4,
1711
+ "'": 29.98,
1712
+ "(": 36.47,
1713
+ ")": 36.47,
1714
+ "*": 50.1,
1715
+ "+": 66.16,
1716
+ ",": 28.81,
1717
+ "-": 46,
1718
+ ".": 28.81,
1719
+ "/": 36.04,
1720
+ ":": 28.81,
1721
+ ";": 30.18,
1722
+ "<": 66.16,
1723
+ "=": 66.16,
1724
+ ">": 66.16,
1725
+ "?": 51.12,
1726
+ "@": 96.58,
1727
+ "A": 68.99,
1728
+ "B": 65.43,
1729
+ "C": 73.05,
1730
+ "D": 72.17,
1731
+ "E": 60.11,
1732
+ "F": 59.03,
1733
+ "G": 74.61,
1734
+ "H": 74.32,
1735
+ "I": 26.86,
1736
+ "J": 57.08,
1737
+ "K": 67.19,
1738
+ "L": 56.54,
1739
+ "M": 90.33,
1740
+ "N": 75.34,
1741
+ "O": 76.46,
1742
+ "P": 63.87,
1743
+ "Q": 76.46,
1744
+ "R": 64.36,
1745
+ "S": 64.16,
1746
+ "T": 64.55,
1747
+ "U": 74.41,
1748
+ "V": 68.99,
1749
+ "W": 98.54,
1750
+ "X": 68.21,
1751
+ "Y": 67.87,
1752
+ "Z": 62.89,
1753
+ "[": 36.47,
1754
+ "\\": 36.04,
1755
+ "]": 36.47,
1756
+ "^": 47.12,
1757
+ "_": 45.61,
1758
+ "`": 32.28,
1759
+ "a": 56.15,
1760
+ "b": 61.23,
1761
+ "c": 57.13,
1762
+ "d": 61.23,
1763
+ "e": 58.3,
1764
+ "f": 37.01,
1765
+ "g": 61.33,
1766
+ "h": 59.13,
1767
+ "i": 24.22,
1768
+ "j": 24.22,
1769
+ "k": 54.88,
1770
+ "l": 24.22,
1771
+ "m": 87.6,
1772
+ "n": 59.08,
1773
+ "o": 59.96,
1774
+ "p": 61.23,
1775
+ "q": 61.23,
1776
+ "r": 37.65,
1777
+ "s": 52.78,
1778
+ "t": 32.71,
1779
+ "u": 59.13,
1780
+ "v": 56.2,
1781
+ "w": 81.84,
1782
+ "x": 54.59,
1783
+ "y": 56.2,
1784
+ "z": 55.22,
1785
+ "{": 42.63,
1786
+ "|": 33.25,
1787
+ "}": 42.63,
1788
+ "~": 66.16
1789
+ },
1790
+ "700": {
1791
+ "0": 67.43,
1792
+ "1": 43.12,
1793
+ "2": 62.94,
1794
+ "3": 64.55,
1795
+ "4": 67.63,
1796
+ "5": 62.21,
1797
+ "6": 64.94,
1798
+ "7": 58.15,
1799
+ "8": 65.09,
1800
+ "9": 64.94,
1801
+ " ": 23.68,
1802
+ "!": 33.79,
1803
+ '"': 55.13,
1804
+ "#": 64.89,
1805
+ "$": 65.48,
1806
+ "%": 101.56,
1807
+ "&": 67.19,
1808
+ "'": 33.89,
1809
+ "(": 37.7,
1810
+ ")": 37.7,
1811
+ "*": 55.91,
1812
+ "+": 67.87,
1813
+ ",": 33.4,
1814
+ "-": 46.78,
1815
+ ".": 33.4,
1816
+ "/": 38.82,
1817
+ ":": 33.4,
1818
+ ";": 34.28,
1819
+ "<": 67.87,
1820
+ "=": 67.87,
1821
+ ">": 67.87,
1822
+ "?": 55.96,
1823
+ "@": 101.61,
1824
+ "A": 74.66,
1825
+ "B": 66.16,
1826
+ "C": 73.97,
1827
+ "D": 72.22,
1828
+ "E": 60.74,
1829
+ "F": 58.69,
1830
+ "G": 75.05,
1831
+ "H": 74.71,
1832
+ "I": 28.08,
1833
+ "J": 58.45,
1834
+ "K": 71.92,
1835
+ "L": 56.54,
1836
+ "M": 93.16,
1837
+ "N": 76.22,
1838
+ "O": 77.05,
1839
+ "P": 64.79,
1840
+ "Q": 77.69,
1841
+ "R": 65.67,
1842
+ "S": 65.48,
1843
+ "T": 66.75,
1844
+ "U": 73.19,
1845
+ "V": 74.66,
1846
+ "W": 103.76,
1847
+ "X": 73.83,
1848
+ "Y": 73.1,
1849
+ "Z": 66.41,
1850
+ "[": 37.7,
1851
+ "\\": 38.82,
1852
+ "]": 37.7,
1853
+ "^": 48.68,
1854
+ "_": 47.61,
1855
+ "`": 36.52,
1856
+ "a": 58.06,
1857
+ "b": 63.04,
1858
+ "c": 58.84,
1859
+ "d": 63.04,
1860
+ "e": 59.57,
1861
+ "f": 39.79,
1862
+ "g": 63.18,
1863
+ "h": 62.26,
1864
+ "i": 27.1,
1865
+ "j": 27.1,
1866
+ "k": 58.01,
1867
+ "l": 27.1,
1868
+ "m": 91.26,
1869
+ "n": 62.26,
1870
+ "o": 61.33,
1871
+ "p": 63.04,
1872
+ "q": 63.04,
1873
+ "r": 40.72,
1874
+ "s": 56.01,
1875
+ "t": 36.62,
1876
+ "u": 62.26,
1877
+ "v": 59.96,
1878
+ "w": 85.01,
1879
+ "x": 58.01,
1880
+ "y": 60.21,
1881
+ "z": 57.28,
1882
+ "{": 46.88,
1883
+ "|": 37.16,
1884
+ "}": 46.88,
1885
+ "~": 67.87
1886
+ },
1887
+ "800": {
1888
+ "0": 69.19,
1889
+ "1": 44.14,
1890
+ "2": 63.77,
1891
+ "3": 65.67,
1892
+ "4": 68.85,
1893
+ "5": 63.38,
1894
+ "6": 66.16,
1895
+ "7": 58.79,
1896
+ "8": 66.41,
1897
+ "9": 66.16,
1898
+ " ": 21.88,
1899
+ "!": 35.84,
1900
+ '"': 58.64,
1901
+ "#": 65.53,
1902
+ "$": 66.02,
1903
+ "%": 102.93,
1904
+ "&": 68.31,
1905
+ "'": 35.45,
1906
+ "(": 38.23,
1907
+ ")": 38.23,
1908
+ "*": 58.25,
1909
+ "+": 68.55,
1910
+ ",": 35.25,
1911
+ "-": 47.12,
1912
+ ".": 35.25,
1913
+ "/": 39.99,
1914
+ ":": 35.25,
1915
+ ";": 35.99,
1916
+ "<": 68.55,
1917
+ "=": 68.55,
1918
+ ">": 68.55,
1919
+ "?": 57.91,
1920
+ "@": 103.61,
1921
+ "A": 76.95,
1922
+ "B": 66.46,
1923
+ "C": 74.37,
1924
+ "D": 72.27,
1925
+ "E": 60.99,
1926
+ "F": 58.54,
1927
+ "G": 75.24,
1928
+ "H": 74.85,
1929
+ "I": 28.56,
1930
+ "J": 58.98,
1931
+ "K": 73.83,
1932
+ "L": 56.54,
1933
+ "M": 94.34,
1934
+ "N": 76.56,
1935
+ "O": 77.29,
1936
+ "P": 65.19,
1937
+ "Q": 78.17,
1938
+ "R": 66.21,
1939
+ "S": 66.02,
1940
+ "T": 67.68,
1941
+ "U": 72.71,
1942
+ "V": 76.95,
1943
+ "W": 105.86,
1944
+ "X": 76.12,
1945
+ "Y": 75.2,
1946
+ "Z": 67.87,
1947
+ "[": 38.23,
1948
+ "\\": 39.99,
1949
+ "]": 38.23,
1950
+ "^": 49.32,
1951
+ "_": 48.44,
1952
+ "`": 38.23,
1953
+ "a": 58.84,
1954
+ "b": 63.77,
1955
+ "c": 59.52,
1956
+ "d": 63.77,
1957
+ "e": 60.06,
1958
+ "f": 40.97,
1959
+ "g": 63.92,
1960
+ "h": 63.53,
1961
+ "i": 28.32,
1962
+ "j": 28.32,
1963
+ "k": 59.28,
1964
+ "l": 28.32,
1965
+ "m": 92.72,
1966
+ "n": 63.53,
1967
+ "o": 61.91,
1968
+ "p": 63.77,
1969
+ "q": 63.77,
1970
+ "r": 41.99,
1971
+ "s": 57.32,
1972
+ "t": 38.18,
1973
+ "u": 63.53,
1974
+ "v": 61.52,
1975
+ "w": 86.28,
1976
+ "x": 59.42,
1977
+ "y": 61.82,
1978
+ "z": 58.11,
1979
+ "{": 48.63,
1980
+ "|": 38.77,
1981
+ "}": 48.63,
1982
+ "~": 68.55
1983
+ }
1984
+ };
1985
+ var INTER_FALLBACK = {
1986
+ "400": 56.16,
1987
+ "700": 58.74,
1988
+ "800": 59.79
1989
+ };
1990
+
1991
+ // ../core/src/textFx.ts
1992
+ var clamp013 = (v) => Math.max(0, Math.min(1, v));
1993
+ var fract = (v) => v - Math.floor(v);
1994
+ var rand = (i, salt) => fract(Math.sin(i * 127.1 + salt * 311.7) * 43758.5453);
1995
+ var dur3 = (base, sp) => base / sp;
1996
+ var SCRAMBLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789#%&@";
1997
+ var advance = (ch, weight, fontSize) => (INTER_ADVANCE[weight]?.[ch] ?? INTER_FALLBACK[weight]) * (fontSize / 100);
1998
+ function splitText(textStr, opts) {
1999
+ const { id, x, y, fontSize } = opts;
2000
+ const weight = opts.fontWeight ?? 800;
2001
+ const fill = opts.fill ?? "#FFFFFF";
2002
+ const ls = opts.letterSpacing ?? 0;
2003
+ const align = opts.align ?? "center";
2004
+ const unit = opts.unit ?? "glyph";
2005
+ const opacity = opts.opacity ?? 0;
2006
+ const chars = [...textStr];
2007
+ let total = 0;
2008
+ chars.forEach((ch, i) => {
2009
+ total += advance(ch, weight, fontSize) + (i < chars.length - 1 ? ls : 0);
2010
+ });
2011
+ let cursor = align === "center" ? x - total / 2 : x;
2012
+ const glyphs = [];
2013
+ const nodes = [];
2014
+ const mk = (ch, cx, adv, lsProp) => {
2015
+ const g = { id: `${id}-${glyphs.length}`, ch, x: cx, y, advance: adv, i: glyphs.length };
2016
+ glyphs.push(g);
2017
+ nodes.push(
2018
+ text({
2019
+ id: g.id,
2020
+ x: cx,
2021
+ y,
2022
+ content: ch,
2023
+ fontFamily: "Inter",
2024
+ fontSize,
2025
+ fontWeight: weight,
2026
+ fill,
2027
+ anchor: "center",
2028
+ opacity,
2029
+ ...lsProp ? { letterSpacing: lsProp } : {}
2030
+ })
2031
+ );
2032
+ };
2033
+ if (unit === "word") {
2034
+ let i = 0;
2035
+ while (i < chars.length) {
2036
+ if (chars[i] === " ") {
2037
+ cursor += advance(" ", weight, fontSize) + ls;
2038
+ i++;
2039
+ continue;
2040
+ }
2041
+ let word = "";
2042
+ let w = 0;
2043
+ const startCursor = cursor;
2044
+ while (i < chars.length && chars[i] !== " ") {
2045
+ const a = advance(chars[i], weight, fontSize);
2046
+ word += chars[i];
2047
+ w += a + (chars[i + 1] && chars[i + 1] !== " " ? ls : 0);
2048
+ i++;
2049
+ }
2050
+ mk(word, startCursor + w / 2, w, ls);
2051
+ cursor = startCursor + w + ls;
2052
+ }
2053
+ } else {
2054
+ chars.forEach((ch) => {
2055
+ const a = advance(ch, weight, fontSize);
2056
+ if (ch !== " ") mk(ch, cursor + a / 2, a);
2057
+ cursor += a + ls;
2058
+ });
2059
+ }
2060
+ return { nodes, glyphs, ids: glyphs.map((g) => g.id), width: total, x, y, fontSize };
2061
+ }
2062
+ var ctx3 = (o) => ({
2063
+ sp: Math.max(0.25, o.speed ?? 1),
2064
+ e: clamp013(o.energy ?? 0.5),
2065
+ seed: o.seed ?? 0,
2066
+ fs: 0,
2067
+ stag: o.stagger
2068
+ });
2069
+ var IN_STAGGER = { typewriter: 0.065, cascade: 0.04, rise: 0.03, bounce: 0.045, assemble: 0.05, decode: 0.05 };
2070
+ function glyphIn(name, g, c) {
2071
+ const set = (props) => tween(g.id, props, { duration: 1e-3 });
2072
+ const rs = (salt) => rand(g.i, salt + c.seed);
2073
+ switch (name) {
2074
+ case "typewriter":
2075
+ return tween(g.id, { opacity: 1 }, { duration: dur3(0.04, c.sp), ease: "linear" });
2076
+ case "cascade":
2077
+ return seq(
2078
+ set({ y: g.y + 56, opacity: 0 }),
2079
+ par(
2080
+ tween(g.id, { opacity: 1 }, { duration: dur3(0.22, c.sp), ease: "easeOutQuad" }),
2081
+ tween(g.id, { y: g.y }, { duration: dur3(0.34, c.sp), ease: "easeOutCubic" })
2082
+ )
2083
+ );
2084
+ case "rise":
2085
+ return seq(
2086
+ set({ y: g.y + 36, opacity: 0 }),
2087
+ par(
2088
+ tween(g.id, { opacity: 1 }, { duration: dur3(0.3, c.sp), ease: "easeOutQuad" }),
2089
+ tween(g.id, { y: g.y }, { duration: dur3(0.4, c.sp), ease: "easeOutQuad" })
2090
+ )
2091
+ );
2092
+ case "bounce":
2093
+ return seq(
2094
+ set({ y: g.y - 80 * (0.6 + c.e), opacity: 0, scale: 0.7 }),
2095
+ par(
2096
+ tween(g.id, { opacity: 1 }, { duration: dur3(0.2, c.sp), ease: "easeOutQuad" }),
2097
+ tween(g.id, { y: g.y, scale: 1 }, { duration: dur3(0.7, c.sp), ease: "easeOutBounce" })
2098
+ )
2099
+ );
2100
+ case "assemble":
2101
+ return seq(
2102
+ set({ x: g.x + (rs(11) - 0.5) * 1e3 * (0.5 + c.e), y: g.y + (rs(12) - 0.5) * 640, rotation: (rs(13) - 0.5) * 200, scale: 0.4, opacity: 0 }),
2103
+ par(
2104
+ tween(g.id, { opacity: 1 }, { duration: dur3(0.4, c.sp), ease: "easeOutQuad" }),
2105
+ tween(g.id, { x: g.x, y: g.y, rotation: 0, scale: 1 }, { duration: dur3(0.8, c.sp), ease: "easeOutExpo" })
2106
+ )
2107
+ );
2108
+ case "decode": {
2109
+ const steps = 4 + Math.floor(rs(7) * 3);
2110
+ const flicker = [set({ opacity: 1 })];
2111
+ for (let k = 0; k < steps; k++) {
2112
+ flicker.push(tween(g.id, { content: SCRAMBLE[Math.floor(rand(g.i, 20 + k + c.seed) * SCRAMBLE.length)] }, { duration: dur3(0.05, c.sp), ease: "linear" }));
2113
+ }
2114
+ flicker.push(tween(g.id, { content: g.ch }, { duration: dur3(0.05, c.sp), ease: "linear" }));
2115
+ return seq(...flicker);
2116
+ }
2117
+ }
2118
+ }
2119
+ function textIn(name, block, opts = {}) {
2120
+ const c = { ...ctx3(opts), fs: block.fontSize };
2121
+ const interval = (c.stag ?? IN_STAGGER[name]) / c.sp;
2122
+ return beat(opts.label ?? `text-in-${name}`, {}, [stagger(interval, ...block.glyphs.map((g) => glyphIn(name, g, c)))]);
2123
+ }
2124
+ function textLoop(name, block, opts = {}) {
2125
+ const win = { ...opts.from !== void 0 && { from: opts.from }, ...opts.until !== void 0 && { until: opts.until }, ...opts.ramp !== void 0 && { ramp: opts.ramp } };
2126
+ const f = opts.frequency ?? (name === "wave" ? 0.9 : name === "shimmer" ? 1.4 : 0.7);
2127
+ const ps = opts.phaseStep ?? 0.55;
2128
+ return block.glyphs.map((g, i) => {
2129
+ switch (name) {
2130
+ case "wave":
2131
+ return oscillate(g.id, "y", { amplitude: opts.amplitude ?? 9, frequency: f, phase: i * ps }, win);
2132
+ case "shimmer":
2133
+ return oscillate(g.id, "opacity", { amplitude: opts.amplitude ?? 0.25, frequency: f, phase: i * ps }, win);
2134
+ case "wobble":
2135
+ return oscillate(g.id, "rotation", { amplitude: opts.amplitude ?? 6, frequency: f, phase: i * ps }, win);
2136
+ case "float":
2137
+ return oscillate(g.id, "y", { amplitude: opts.amplitude ?? 5, frequency: f, phase: i * ps }, win);
2138
+ }
2139
+ });
2140
+ }
2141
+ var OUT_STAGGER = { shatter: 0.02, fly: 0.012, dissolve: 0, fall: 0.02, collapse: 0.02 };
2142
+ function glyphOut(name, g, c, block, dir) {
2143
+ const rs = (salt) => rand(g.i, salt + c.seed);
2144
+ switch (name) {
2145
+ case "shatter":
2146
+ return par(
2147
+ tween(g.id, { x: g.x + (rs(21) - 0.5) * 1100 * (0.6 + c.e), y: g.y + (rs(22) - 0.5) * 760 }, { duration: dur3(0.7, c.sp), ease: "easeInCubic" }),
2148
+ tween(g.id, { rotation: (rs(23) - 0.5) * 300, opacity: 0 }, { duration: dur3(0.7, c.sp), ease: "easeInQuad" })
2149
+ );
2150
+ case "fly":
2151
+ return par(
2152
+ tween(g.id, { x: g.x + dir[0] * 1200, y: g.y + dir[1] * 1200 }, { duration: dur3(0.6, c.sp), ease: "easeInCubic" }),
2153
+ tween(g.id, { opacity: 0 }, { duration: dur3(0.5, c.sp), ease: "easeInQuad" })
2154
+ );
2155
+ case "dissolve":
2156
+ return seq(wait(rs(31) * 0.5), par(
2157
+ tween(g.id, { opacity: 0 }, { duration: dur3(0.4, c.sp), ease: "easeInQuad" }),
2158
+ tween(g.id, { scale: 1.4 }, { duration: dur3(0.4, c.sp), ease: "easeOutQuad" })
2159
+ ));
2160
+ case "fall":
2161
+ return par(
2162
+ tween(g.id, { y: g.y + 700 + rs(41) * 200 }, { duration: dur3(0.8, c.sp), ease: "easeInQuad" }),
2163
+ tween(g.id, { rotation: (rs(42) - 0.5) * 120, opacity: 0 }, { duration: dur3(0.8, c.sp), ease: "easeInQuad" })
2164
+ );
2165
+ case "collapse":
2166
+ return par(
2167
+ tween(g.id, { x: block.x, y: block.y, scale: 0.2 }, { duration: dur3(0.5, c.sp), ease: "easeInBack" }),
2168
+ tween(g.id, { opacity: 0 }, { duration: dur3(0.5, c.sp), ease: "easeInQuad" })
2169
+ );
2170
+ }
2171
+ }
2172
+ function textOut(name, block, opts = {}) {
2173
+ const c = { ...ctx3(opts), fs: block.fontSize };
2174
+ const dir = opts.dir ?? [0, -1];
2175
+ const steps = block.glyphs.map((g) => glyphOut(name, g, c, block, dir));
2176
+ const interval = (c.stag ?? OUT_STAGGER[name]) / c.sp;
2177
+ const body = interval > 0 ? stagger(interval, ...steps) : par(...steps);
2178
+ return beat(opts.label ?? `text-out-${name}`, {}, [body]);
2179
+ }
2180
+ function textTypeCues(block, opts) {
2181
+ const interval = opts.interval ?? 0.065;
2182
+ const gain = opts.gain ?? 0.4;
2183
+ const off = opts.offset ?? 0;
2184
+ const KEYS = ["001", "004", "007", "010", "014"];
2185
+ return block.glyphs.map((g, i) => ({
2186
+ at: opts.at,
2187
+ offset: off + i * interval,
2188
+ file: `keypress-${KEYS[i % KEYS.length]}.wav`,
2189
+ gain: gain + 0.2 * rand(i, 31)
2190
+ }));
2191
+ }
2192
+
1266
2193
  // ../core/src/motionOps.ts
1267
2194
  var MOTION_OPS = ["rotate", "zoom", "ken-burns", "slide-in", "fade", "draw-on", "pulse"];
1268
- var clamp012 = (n) => Math.max(0, Math.min(1, n));
2195
+ var clamp014 = (n3) => Math.max(0, Math.min(1, n3));
1269
2196
  function settleEase2(e) {
1270
2197
  return e < 0.34 ? "easeOutCubic" : e < 0.67 ? "easeOutBack" : "easeOutElastic";
1271
2198
  }
@@ -1283,7 +2210,7 @@ function fromVec2(from, dist) {
1283
2210
  }
1284
2211
  var motionOpLabel = (name, target) => `op-${name}-${target}`;
1285
2212
  function motionOp(name, target, opts = {}) {
1286
- const e = clamp012(opts.energy ?? 0.5);
2213
+ const e = clamp014(opts.energy ?? 0.5);
1287
2214
  const sp = Math.max(0.25, opts.speed ?? 1);
1288
2215
  const amt = opts.amount ?? 1;
1289
2216
  const b = { scale: 1, x: 0, y: 0, rotation: 0, ...opts.base };
@@ -1493,8 +2420,8 @@ function valueNoise(x, seed) {
1493
2420
  const b = hash01(i + 1, seed) * 2 - 1;
1494
2421
  return a + (b - a) * u;
1495
2422
  }
1496
- function hash01(n, seed) {
1497
- let h = n * 374761393 + seed * 668265263 | 0;
2423
+ function hash01(n3, seed) {
2424
+ let h = n3 * 374761393 + seed * 668265263 | 0;
1498
2425
  h = h ^ h >>> 13 | 0;
1499
2426
  h = Math.imul(h, 1274126177);
1500
2427
  h = (h ^ h >>> 16) >>> 0;
@@ -1587,8 +2514,8 @@ function isColor(v) {
1587
2514
  function parseColor(hex) {
1588
2515
  let h = hex.slice(1);
1589
2516
  if (h.length <= 4) h = [...h].map((c) => c + c).join("");
1590
- const n = parseInt(h.padEnd(8, "f"), 16);
1591
- return [n >>> 24 & 255, n >>> 16 & 255, n >>> 8 & 255, n & 255];
2517
+ const n3 = parseInt(h.padEnd(8, "f"), 16);
2518
+ return [n3 >>> 24 & 255, n3 >>> 16 & 255, n3 >>> 8 & 255, n3 & 255];
1592
2519
  }
1593
2520
  function formatColor([r, g, b, a]) {
1594
2521
  const hex = (v) => Math.round(Math.max(0, Math.min(255, v))).toString(16).padStart(2, "0");
@@ -1608,19 +2535,68 @@ function lerpValue(from, to2, u) {
1608
2535
  a[3] + (b[3] - a[3]) * u
1609
2536
  ]);
1610
2537
  }
2538
+ if (looksLikePath(from) && looksLikePath(to2)) {
2539
+ const a = tokenizePath(from);
2540
+ const b = tokenizePath(to2);
2541
+ if (a && b && morphCompatible(a, b)) return morphPath(a, b, u);
2542
+ return u < 0.5 ? from : to2;
2543
+ }
1611
2544
  return to2;
1612
2545
  }
2546
+ var PATH_BODY = /^[\sMmLlHhVvCcSsQqTtAaZz0-9.,eE+-]+$/;
2547
+ function looksLikePath(v) {
2548
+ return typeof v === "string" && /^\s*[Mm]/.test(v) && PATH_BODY.test(v);
2549
+ }
2550
+ var PATH_TOKEN = /([MmLlHhVvCcSsQqTtAaZz])|(-?(?:\d+\.?\d*|\.\d+)(?:[eE][-+]?\d+)?)/g;
2551
+ function tokenizePath(d) {
2552
+ const out = [];
2553
+ let cur = null;
2554
+ let m;
2555
+ PATH_TOKEN.lastIndex = 0;
2556
+ while (m = PATH_TOKEN.exec(d)) {
2557
+ if (m[1]) out.push(cur = { cmd: m[1], nums: [] });
2558
+ else if (m[2]) {
2559
+ if (!cur) return null;
2560
+ cur.nums.push(parseFloat(m[2]));
2561
+ }
2562
+ }
2563
+ return out.length ? out : null;
2564
+ }
2565
+ function morphCompatible(a, b) {
2566
+ if (a.length !== b.length) return false;
2567
+ for (let i = 0; i < a.length; i++) {
2568
+ const ca = a[i];
2569
+ const cb = b[i];
2570
+ if (ca.cmd !== cb.cmd || ca.nums.length !== cb.nums.length) return false;
2571
+ if (ca.cmd === "A" || ca.cmd === "a") return false;
2572
+ }
2573
+ return true;
2574
+ }
2575
+ var fmtNum = (v) => {
2576
+ const r = Number(v.toFixed(3));
2577
+ return Object.is(r, -0) ? "0" : String(r);
2578
+ };
2579
+ function morphPath(a, b, u) {
2580
+ let s = "";
2581
+ for (let i = 0; i < a.length; i++) {
2582
+ const an = a[i].nums;
2583
+ const bn = b[i].nums;
2584
+ s += (i ? " " : "") + a[i].cmd;
2585
+ for (let j = 0; j < an.length; j++) s += " " + fmtNum(an[j] + (bn[j] - an[j]) * u);
2586
+ }
2587
+ return s;
2588
+ }
1613
2589
 
1614
2590
  // ../core/src/evaluate.ts
1615
2591
  var IDENTITY = [1, 0, 0, 1, 0, 0];
1616
- function multiply(m, n) {
2592
+ function multiply(m, n3) {
1617
2593
  return [
1618
- m[0] * n[0] + m[2] * n[1],
1619
- m[1] * n[0] + m[3] * n[1],
1620
- m[0] * n[2] + m[2] * n[3],
1621
- m[1] * n[2] + m[3] * n[3],
1622
- m[0] * n[4] + m[2] * n[5] + m[4],
1623
- m[1] * n[4] + m[3] * n[5] + m[5]
2594
+ m[0] * n3[0] + m[2] * n3[1],
2595
+ m[1] * n3[0] + m[3] * n3[1],
2596
+ m[0] * n3[2] + m[2] * n3[3],
2597
+ m[1] * n3[2] + m[3] * n3[3],
2598
+ m[0] * n3[4] + m[2] * n3[5] + m[4],
2599
+ m[1] * n3[4] + m[3] * n3[5] + m[5]
1624
2600
  ];
1625
2601
  }
1626
2602
  function localMatrix(x, y, rotationDeg, scale, scaleX = 1, scaleY = 1, skewXDeg = 0, skewYDeg = 0) {
@@ -1635,9 +2611,9 @@ function localMatrix(x, y, rotationDeg, scale, scaleX = 1, scaleY = 1, skewXDeg
1635
2611
  const tx = Math.tan(skewXDeg * Math.PI / 180);
1636
2612
  const ty = Math.tan(skewYDeg * Math.PI / 180);
1637
2613
  const R = [c, s, -s, c, 0, 0];
1638
- const K = [1, ty, tx, 1, 0, 0];
2614
+ const K3 = [1, ty, tx, 1, 0, 0];
1639
2615
  const S = [scale * scaleX, 0, 0, scale * scaleY, 0, 0];
1640
- const m = multiply(R, multiply(K, S));
2616
+ const m = multiply(R, multiply(K3, S));
1641
2617
  return [m[0], m[1], m[2], m[3], x, y];
1642
2618
  }
1643
2619
  var ANCHOR_FACTORS = {
@@ -1937,29 +2913,29 @@ function sketchToTimeline(sketch, nodeIds) {
1937
2913
  const steps = [];
1938
2914
  events.forEach((ev, i) => {
1939
2915
  const node = nodeIds[i % nodeIds.length];
1940
- const dur2 = Math.max(0.05, ev.t1 - ev.t0);
2916
+ const dur4 = Math.max(0.05, ev.t1 - ev.t0);
1941
2917
  const ease = easeFor(ev.easing);
1942
2918
  let motion;
1943
2919
  switch (ev.kind) {
1944
2920
  case "enter":
1945
- motion = tween(node, { opacity: 1 }, { duration: dur2, ease });
2921
+ motion = tween(node, { opacity: 1 }, { duration: dur4, ease });
1946
2922
  break;
1947
2923
  case "exit":
1948
- motion = tween(node, { opacity: 0 }, { duration: dur2, ease });
2924
+ motion = tween(node, { opacity: 0 }, { duration: dur4, ease });
1949
2925
  break;
1950
2926
  case "emphasis": {
1951
2927
  const peak = 1 + Math.max(0.08, Math.min(0.5, ev.magnitude));
1952
2928
  motion = seq(
1953
- tween(node, { scale: peak }, { duration: dur2 / 2, ease: "easeOutCubic" }),
1954
- tween(node, { scale: 1 }, { duration: dur2 / 2, ease: "easeInOutQuad" })
2929
+ tween(node, { scale: peak }, { duration: dur4 / 2, ease: "easeOutCubic" }),
2930
+ tween(node, { scale: 1 }, { duration: dur4 / 2, ease: "easeInOutQuad" })
1955
2931
  );
1956
2932
  break;
1957
2933
  }
1958
2934
  case "scale":
1959
- motion = tween(node, { scale: 1 + Math.max(-0.5, Math.min(0.5, ev.magnitude)) }, { duration: dur2, ease });
2935
+ motion = tween(node, { scale: 1 + Math.max(-0.5, Math.min(0.5, ev.magnitude)) }, { duration: dur4, ease });
1960
2936
  break;
1961
2937
  case "move":
1962
- motion = tween(node, { opacity: 1 }, { duration: dur2, ease });
2938
+ motion = tween(node, { opacity: 1 }, { duration: dur4, ease });
1963
2939
  break;
1964
2940
  }
1965
2941
  steps.push(ev.t0 > 0 ? seq(wait(ev.t0), motion) : motion);
@@ -1967,6 +2943,7 @@ function sketchToTimeline(sketch, nodeIds) {
1967
2943
  return par(...steps);
1968
2944
  }
1969
2945
  export {
2946
+ CHARACTER_PRESET_NAMES,
1970
2947
  DEFAULT_CROSSFADE,
1971
2948
  DEFAULT_FPS,
1972
2949
  DEFAULT_MOTIONPATH_DURATION,
@@ -1980,6 +2957,7 @@ export {
1980
2957
  SFX_DURATION,
1981
2958
  SceneValidationError,
1982
2959
  beat,
2960
+ characterPreset,
1983
2961
  collectImageSrcs,
1984
2962
  compileComposition,
1985
2963
  compileScene,
@@ -1991,8 +2969,11 @@ export {
1991
2969
  deviceScreenCenter,
1992
2970
  ellipse,
1993
2971
  evaluate,
2972
+ figure,
1994
2973
  formatComposeReport,
1995
2974
  group,
2975
+ humanoid,
2976
+ ikReach,
1996
2977
  image,
1997
2978
  isColor,
1998
2979
  lerpValue,
@@ -2003,21 +2984,30 @@ export {
2003
2984
  motionPreset,
2004
2985
  nodeParentMatrix,
2005
2986
  oscillate,
2987
+ ovalPath,
2006
2988
  par,
2007
2989
  path,
2008
2990
  pathPoint,
2009
2991
  pathTangentAngle,
2992
+ poseTo,
2010
2993
  rect,
2011
2994
  resolveAudioPlan,
2012
2995
  resolveCompositionAudioPlan,
2013
2996
  resolveEase,
2997
+ rig,
2998
+ rigPose,
2014
2999
  sampleBehavior,
2015
3000
  sampleProp,
2016
3001
  scene,
2017
3002
  seq,
2018
3003
  sketchToTimeline,
3004
+ splitText,
2019
3005
  stagger,
2020
3006
  text,
3007
+ textIn,
3008
+ textLoop,
3009
+ textOut,
3010
+ textTypeCues,
2021
3011
  to,
2022
3012
  tween,
2023
3013
  validateComposition,