@pixerate/editor 0.143.0 → 0.145.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/agent/index.cjs +6 -4
- package/dist/agent/index.js +4 -2
- package/dist/animation/index.cjs +2 -2
- package/dist/animation/index.js +1 -1
- package/dist/animation/player.d.cts +5 -0
- package/dist/animation/player.d.ts +5 -0
- package/dist/animation-editor/controller.d.cts +101 -1
- package/dist/animation-editor/controller.d.ts +101 -1
- package/dist/animation-editor/index.cjs +594 -147
- package/dist/animation-editor/index.d.cts +1 -0
- package/dist/animation-editor/index.d.ts +1 -0
- package/dist/animation-editor/index.js +525 -78
- package/dist/animation-editor/record.d.cts +114 -0
- package/dist/animation-editor/record.d.ts +114 -0
- package/dist/animation-editor/tools.d.cts +1 -0
- package/dist/animation-editor/tools.d.ts +1 -0
- package/dist/animation-editor/types.d.cts +6 -0
- package/dist/animation-editor/types.d.ts +6 -0
- package/dist/canvas/index.cjs +24 -24
- package/dist/canvas/index.js +1 -1
- package/dist/{chunk-XH32BATE.cjs → chunk-AG2EXVCR.cjs} +2 -2
- package/dist/{chunk-X2HQP7DF.cjs → chunk-D2ENR3LB.cjs} +43 -4
- package/dist/{chunk-54GVEVXS.js → chunk-HUHBJWTD.js} +43 -4
- package/dist/{chunk-I4T4J27S.js → chunk-QX25HWFL.js} +9 -0
- package/dist/{chunk-Y6SGC6KO.js → chunk-SA37RU2O.js} +1 -1
- package/dist/{chunk-SVR5FR7B.cjs → chunk-UUTTJK77.cjs} +9 -0
- package/dist/commands/index.cjs +2 -2
- package/dist/commands/index.js +1 -1
- package/dist/tools/index.cjs +5 -3
- package/dist/tools/index.js +4 -2
- package/dist/tools/playback.d.cts +18 -4
- package/dist/tools/playback.d.ts +18 -4
- package/dist/video-editor/index.cjs +2 -2
- package/dist/video-editor/index.js +1 -1
- package/package.json +1 -1
- package/skills/pixerate-agent-tools/SKILL.md +7 -1
- package/skills/pixerate-animation-editor/SKILL.md +10 -0
- package/skills/pixerate-animation-editor/references/commands.md +3 -1
|
@@ -68,7 +68,7 @@ var _chunkFJ2MSI43cjs = require('../chunk-FJ2MSI43.cjs');
|
|
|
68
68
|
var _chunk7B5GTJXBcjs = require('../chunk-7B5GTJXB.cjs');
|
|
69
69
|
|
|
70
70
|
|
|
71
|
-
var
|
|
71
|
+
var _chunkUUTTJK77cjs = require('../chunk-UUTTJK77.cjs');
|
|
72
72
|
|
|
73
73
|
|
|
74
74
|
|
|
@@ -188,7 +188,7 @@ var COMMAND_KEYS = {
|
|
|
188
188
|
enter: [...BASE_KEYS, ...DRAW_ORDER_KEYS, "style", "direction", "distance", "scale", "shape", "orientation"],
|
|
189
189
|
exit: [...BASE_KEYS, ...DRAW_ORDER_KEYS, "style", "direction", "distance"],
|
|
190
190
|
draw: [...BASE_KEYS, ...DRAW_ORDER_KEYS, "from", "to", "comet"],
|
|
191
|
-
move: [...BASE_KEYS, "by", "to", "via", "pacing", "blend"],
|
|
191
|
+
move: [...BASE_KEYS, "by", "to", "via", "pacing", "times", "blend"],
|
|
192
192
|
rotate: [...BASE_KEYS, "by", "to", "blend"],
|
|
193
193
|
scale: [...BASE_KEYS, "to", "geometric", "blend"],
|
|
194
194
|
fade: [...BASE_KEYS, "to"],
|
|
@@ -292,6 +292,13 @@ function commandShapeProblems(command3, where) {
|
|
|
292
292
|
if (command3.via !== void 0 && !(Array.isArray(command3.via) && command3.via.length > 0 && command3.via.length <= 64 && command3.via.every(isPoint))) p(`"via" must be a list of 1 to 64 points { "x": number, "y": number } (document units).`);
|
|
293
293
|
if (command3.pacing !== void 0 && command3.pacing !== "even" && command3.pacing !== "equal") p(`"pacing" must be "even" (steady speed) or "equal" (each leg the same time).`);
|
|
294
294
|
if (command3.pacing !== void 0 && command3.via === void 0) p(`"pacing" goes with "via".`);
|
|
295
|
+
if (command3.times !== void 0) {
|
|
296
|
+
const times = command3.times;
|
|
297
|
+
const via = Array.isArray(command3.via) ? command3.via.length : null;
|
|
298
|
+
if (command3.via === void 0) p(`"times" goes with "via".`);
|
|
299
|
+
else if (!(Array.isArray(times) && times.length === via && times.every((t, i) => isNum(t) && t > 0 && t < 1 && (i === 0 || t > times[i - 1]))))
|
|
300
|
+
p(`"times" must be ${_nullishCoalesce(via, () => ( "one per waypoint"))} rising fractions of the duration between 0 and 1 (one per "via" point).`);
|
|
301
|
+
}
|
|
295
302
|
break;
|
|
296
303
|
case "rotate":
|
|
297
304
|
if (command3.by === void 0 && command3.to === void 0) p(`give "by" or "to" (degrees).`);
|
|
@@ -887,8 +894,8 @@ function motionShotTimeScale(shot) {
|
|
|
887
894
|
function motionBeatTime(grid, beat) {
|
|
888
895
|
return (_nullishCoalesce(grid.offset, () => ( 0))) + beat * 6e4 / grid.bpm;
|
|
889
896
|
}
|
|
890
|
-
function motionBeatAt(grid,
|
|
891
|
-
const beat = (
|
|
897
|
+
function motionBeatAt(grid, ms2) {
|
|
898
|
+
const beat = (ms2 - (_nullishCoalesce(grid.offset, () => ( 0)))) * grid.bpm / 6e4;
|
|
892
899
|
const meter = Math.max(1, Math.round(_nullishCoalesce(grid.meter, () => ( 4))));
|
|
893
900
|
const whole = Math.floor(beat + 1e-9);
|
|
894
901
|
return { beat, bar: Math.floor(whole / meter) + 1, beatInBar: (whole % meter + meter) % meter + 1 };
|
|
@@ -982,8 +989,8 @@ function motionCycleDuration(command3, scene, target) {
|
|
|
982
989
|
if (command3.type === "draw" || command3.type === "enter" && command3.style === "draw" || command3.type === "exit" && command3.style === "undraw") {
|
|
983
990
|
const entry = target ? indexMotionNodes(scene.artwork.nodes).get(target) : void 0;
|
|
984
991
|
const length = entry ? motionPathLength(entry.node) : 0;
|
|
985
|
-
const
|
|
986
|
-
return Math.round(Math.min(MOTION_DRAW_RANGE[1], Math.max(MOTION_DRAW_RANGE[0],
|
|
992
|
+
const ms2 = length * MOTION_DRAW_MS_PER_UNIT;
|
|
993
|
+
return Math.round(Math.min(MOTION_DRAW_RANGE[1], Math.max(MOTION_DRAW_RANGE[0], ms2)));
|
|
987
994
|
}
|
|
988
995
|
if (command3.type === "move" && _optionalChain([command3, 'access', _35 => _35.via, 'optionalAccess', _36 => _36.length])) return MOTION_DEFAULT_DURATIONS.move + 400 * command3.via.length;
|
|
989
996
|
return MOTION_DEFAULT_DURATIONS[command3.type];
|
|
@@ -1014,9 +1021,9 @@ function compileMotionScene(scene) {
|
|
|
1014
1021
|
if (shot.camera) {
|
|
1015
1022
|
const rect = resolveMotionCamera(shot.camera, scene);
|
|
1016
1023
|
if (rect && rect.width > 0 && rect.height > 0) {
|
|
1017
|
-
const
|
|
1018
|
-
cameraMoves.push({ shot: shot.id, start, end: start +
|
|
1019
|
-
cursor = start +
|
|
1024
|
+
const ms2 = index === 0 ? 0 : (_nullishCoalesce(shot.camera.ms, () => ( MOTION_CAMERA_MS))) * f;
|
|
1025
|
+
cameraMoves.push({ shot: shot.id, start, end: start + ms2, to: rect, ease: _nullishCoalesce(shot.camera.ease, () => ( MOTION_EASE_IN_OUT)), geometric: shot.camera.geometric === true });
|
|
1026
|
+
cursor = start + ms2;
|
|
1020
1027
|
}
|
|
1021
1028
|
}
|
|
1022
1029
|
const commandsStart = cursor;
|
|
@@ -1256,7 +1263,7 @@ function compileMotionScene(scene) {
|
|
|
1256
1263
|
const y0 = num2("y");
|
|
1257
1264
|
const add = command3.blend === "add";
|
|
1258
1265
|
const offsets = [{ x: 0, y: 0 }, ...command3.via.map((p) => _chunkHRI3ZMDMcjs.applyMatrix.call(void 0, inv, p)).map((p) => ({ x: p.x - entry.center.x - x0, y: p.y - entry.center.y - y0 })), { x: dx, y: dy }];
|
|
1259
|
-
const times = smoothMotionTimes(offsets, start, end - start, _nullishCoalesce(command3.pacing, () => ( "even")));
|
|
1266
|
+
const times = _optionalChain([command3, 'access', _50 => _50.times, 'optionalAccess', _51 => _51.length]) === command3.via.length ? [start, ...command3.times.map((f) => start + f * (end - start)), end] : smoothMotionTimes(offsets, start, end - start, _nullishCoalesce(command3.pacing, () => ( "even")));
|
|
1260
1267
|
for (const axis of ["x", "y"]) {
|
|
1261
1268
|
const base = add ? 0 : axis === "x" ? x0 : y0;
|
|
1262
1269
|
for (const leg of smoothMotionLegs(times, offsets.map((o) => o[axis])))
|
|
@@ -1348,7 +1355,7 @@ function compileMotionScene(scene) {
|
|
|
1348
1355
|
for (const track of tracks.values()) track.sort((a, b) => a.start - b.start);
|
|
1349
1356
|
segments.sort((a, b) => a.start - b.start);
|
|
1350
1357
|
const duration = shots.length ? shots[shots.length - 1].end : 0;
|
|
1351
|
-
const timeline = { duration, shots, commands, segments, tracks, ..._optionalChain([scene, 'access',
|
|
1358
|
+
const timeline = { duration, shots, commands, segments, tracks, ..._optionalChain([scene, 'access', _52 => _52.drivers, 'optionalAccess', _53 => _53.length]) ? { drivers: scene.drivers, restOpacity: new Map([...nodes].map(([id, e]) => [id, _nullishCoalesce(e.node.opacity, () => ( 1))])) } : {} };
|
|
1352
1359
|
timelineCache.set(scene, timeline);
|
|
1353
1360
|
return timeline;
|
|
1354
1361
|
}
|
|
@@ -1361,7 +1368,7 @@ var MOTION_DRAW_EFFECT_ID = "~draw";
|
|
|
1361
1368
|
var TRANSFORM_PROPS = ["x", "y", "rotation", "scaleX", "scaleY", "follow"];
|
|
1362
1369
|
var tidy = (v) => Math.round(v * 1e6) / 1e6 || 0;
|
|
1363
1370
|
function applyMotionControlValues(scene, values) {
|
|
1364
|
-
if (!values || !_optionalChain([scene, 'access',
|
|
1371
|
+
if (!values || !_optionalChain([scene, 'access', _54 => _54.controls, 'optionalAccess', _55 => _55.length])) return scene;
|
|
1365
1372
|
const map = /* @__PURE__ */ new Map();
|
|
1366
1373
|
for (const control of scene.controls) {
|
|
1367
1374
|
const value = values[control.name];
|
|
@@ -1408,7 +1415,7 @@ function shapeIn(entry, shape) {
|
|
|
1408
1415
|
}
|
|
1409
1416
|
function shapeAt(entry, timeline, nodes, time) {
|
|
1410
1417
|
const track = timeline.tracks.get(motionTrackKey(entry.node.id, "shape"));
|
|
1411
|
-
if (!_optionalChain([track, 'optionalAccess',
|
|
1418
|
+
if (!_optionalChain([track, 'optionalAccess', _56 => _56.length])) return null;
|
|
1412
1419
|
const s = activeSegment(track, time);
|
|
1413
1420
|
const fromId = String(s ? s.from : track[0].from);
|
|
1414
1421
|
if (!s) return fromId === entry.node.id ? null : nodes.get(fromId) ? shapeIn(entry, nodes.get(fromId)) : null;
|
|
@@ -1431,8 +1438,8 @@ function motionFollowRoute(entry, path) {
|
|
|
1431
1438
|
}
|
|
1432
1439
|
function followAt(entry, timeline, nodes, time) {
|
|
1433
1440
|
const track = timeline.tracks.get(motionTrackKey(entry.node.id, "follow"));
|
|
1434
|
-
const s = _nullishCoalesce(activeSegment(track, time), () => ( _optionalChain([track, 'optionalAccess',
|
|
1435
|
-
if (!_optionalChain([s, 'optionalAccess',
|
|
1441
|
+
const s = _nullishCoalesce(activeSegment(track, time), () => ( _optionalChain([track, 'optionalAccess', _57 => _57[0]])));
|
|
1442
|
+
if (!_optionalChain([s, 'optionalAccess', _58 => _58.path])) return null;
|
|
1436
1443
|
const route = motionFollowRoute(entry, nodes.get(s.path));
|
|
1437
1444
|
const progress = Number(trackValue(track, time));
|
|
1438
1445
|
if (!route || !(progress >= 0)) return null;
|
|
@@ -1450,7 +1457,7 @@ function motionNodeOffset(entry, timeline, time, nodes = indexMotionNodes([entry
|
|
|
1450
1457
|
const layered = withMotionLayers(timeline, entry.node.id, p, typeof v === "number" ? v : rest, time, raw);
|
|
1451
1458
|
return p === "follow" ? layered : applyMotionDrivers(timeline, entry.node.id, p, layered, time, raw);
|
|
1452
1459
|
};
|
|
1453
|
-
const driven = _optionalChain([timeline, 'access',
|
|
1460
|
+
const driven = _optionalChain([timeline, 'access', _59 => _59.drivers, 'optionalAccess', _60 => _60.some, 'call', _61 => _61((d) => d.target === entry.node.id && d.property !== "opacity")]);
|
|
1454
1461
|
if (!driven && !TRANSFORM_PROPS.some((p) => (follow || p !== "follow") && (timeline.tracks.has(motionTrackKey(entry.node.id, p)) || timeline.tracks.has(motionAddTrackKey(entry.node.id, p))))) return null;
|
|
1455
1462
|
const dx = read("x", 0);
|
|
1456
1463
|
const dy = read("y", 0);
|
|
@@ -1494,7 +1501,7 @@ function motionFrameAt(scene, time, options = {}) {
|
|
|
1494
1501
|
if (out.visible === void 0) delete out.visible;
|
|
1495
1502
|
}
|
|
1496
1503
|
const effectTracks = [...timeline.tracks.keys()].filter((k) => k.startsWith(`${id}\0effect:`));
|
|
1497
|
-
if (effectTracks.length && _optionalChain([out, 'access',
|
|
1504
|
+
if (effectTracks.length && _optionalChain([out, 'access', _62 => _62.effects, 'optionalAccess', _63 => _63.length])) {
|
|
1498
1505
|
out = {
|
|
1499
1506
|
...out,
|
|
1500
1507
|
effects: out.effects.map((effect) => {
|
|
@@ -1534,7 +1541,7 @@ function motionFrameAt(scene, time, options = {}) {
|
|
|
1534
1541
|
return p;
|
|
1535
1542
|
});
|
|
1536
1543
|
}
|
|
1537
|
-
const opacity = _optionalChain([timeline, 'access',
|
|
1544
|
+
const opacity = _optionalChain([timeline, 'access', _64 => _64.drivers, 'optionalAccess', _65 => _65.some, 'call', _66 => _66((d) => d.target === id && d.property === "opacity")]) ? motionPropertyValue(timeline, id, "opacity", time) : value(id, "opacity");
|
|
1538
1545
|
if (typeof opacity === "number") {
|
|
1539
1546
|
const o = Math.min(1, Math.max(0, tidy(opacity)));
|
|
1540
1547
|
out = { ...out, opacity: o };
|
|
@@ -1607,7 +1614,7 @@ function motionPath(scene, id, options = {}) {
|
|
|
1607
1614
|
const r = (n) => Math.round(n * 100) / 100;
|
|
1608
1615
|
const centreAt = (t) => {
|
|
1609
1616
|
const node = indexMotionNodes(motionFrameAtCache(scene, t)).get(id);
|
|
1610
|
-
const b = _optionalChain([node, 'optionalAccess',
|
|
1617
|
+
const b = _optionalChain([node, 'optionalAccess', _67 => _67.bounds]);
|
|
1611
1618
|
return b ? { t: r(t), x: r(b.x + b.width / 2), y: r(b.y + b.height / 2) } : null;
|
|
1612
1619
|
};
|
|
1613
1620
|
const points = [];
|
|
@@ -1641,8 +1648,8 @@ function motionBlurTimes(scene, time, blur = {}) {
|
|
|
1641
1648
|
const b = indexMotionNodes(motionFrameAt(scene, time + span / 2).document.nodes);
|
|
1642
1649
|
let travel = 0;
|
|
1643
1650
|
for (const id of ids) {
|
|
1644
|
-
const p = _optionalChain([a, 'access',
|
|
1645
|
-
const q = _optionalChain([b, 'access',
|
|
1651
|
+
const p = _optionalChain([a, 'access', _68 => _68.get, 'call', _69 => _69(id), 'optionalAccess', _70 => _70.bounds]);
|
|
1652
|
+
const q = _optionalChain([b, 'access', _71 => _71.get, 'call', _72 => _72(id), 'optionalAccess', _73 => _73.bounds]);
|
|
1646
1653
|
if (p && q) travel = Math.max(travel, Math.abs(p.x - q.x), Math.abs(p.y - q.y), Math.abs(p.x + p.width - q.x - q.width), Math.abs(p.y + p.height - q.y - q.height));
|
|
1647
1654
|
}
|
|
1648
1655
|
const n = Math.min(most, Math.ceil(travel / 1.5));
|
|
@@ -1654,7 +1661,7 @@ function averagedFrames(scene, times, options, prefix) {
|
|
|
1654
1661
|
return `<g data-motion-blur="${n}" style="isolation:isolate">${times.map((t, k) => {
|
|
1655
1662
|
const own = `${prefix}blur${k}-`;
|
|
1656
1663
|
const frame = motionFrameToSvg(scene, t, { ...options, idPrefix: own, background: false });
|
|
1657
|
-
const view = _nullishCoalesce(_optionalChain([/viewBox="([^"]*)"/, 'access',
|
|
1664
|
+
const view = _nullishCoalesce(_optionalChain([/viewBox="([^"]*)"/, 'access', _74 => _74.exec, 'call', _75 => _75(frame), 'optionalAccess', _76 => _76[1]]), () => ( ""));
|
|
1658
1665
|
const inner = k === n - 1 ? innerSvg(frame) : innerSvg(frame).replace(new RegExp(` id="(?!${own.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")})[^"]*"`, "g"), "");
|
|
1659
1666
|
return `<svg viewBox="${view}" preserveAspectRatio="none" style="mix-blend-mode:plus-lighter" opacity="${Math.round(1e4 / n) / 1e4}">${inner}</svg>`;
|
|
1660
1667
|
}).join("")}</g>`;
|
|
@@ -1669,7 +1676,7 @@ function motionStageSvg(scene, time, options = {}) {
|
|
|
1669
1676
|
if (blurTimes.length > 1) {
|
|
1670
1677
|
const open2 = current.indexOf(">") + 1;
|
|
1671
1678
|
const body2 = current.slice(open2, current.lastIndexOf("</svg>"));
|
|
1672
|
-
const bg2 = svgOptions.background !== false && scene.artwork.background ? _nullishCoalesce(_optionalChain([/^\s*<rect[^>]*\/>/, 'access',
|
|
1679
|
+
const bg2 = svgOptions.background !== false && scene.artwork.background ? _nullishCoalesce(_optionalChain([/^\s*<rect[^>]*\/>/, 'access', _77 => _77.exec, 'call', _78 => _78(body2), 'optionalAccess', _79 => _79[0]]), () => ( "")) : "";
|
|
1673
1680
|
current = `${current.slice(0, open2)}${bg2}${averagedFrames(scene, blurTimes, svgOptions, prefix)}</svg>`;
|
|
1674
1681
|
}
|
|
1675
1682
|
if (!onionSkin && !motionPaths) return current;
|
|
@@ -1684,7 +1691,7 @@ function motionStageSvg(scene, time, options = {}) {
|
|
|
1684
1691
|
const open = current.indexOf(">") + 1;
|
|
1685
1692
|
const close = current.lastIndexOf("</svg>");
|
|
1686
1693
|
const body = current.slice(open, close);
|
|
1687
|
-
const bg = svgOptions.background !== false && scene.artwork.background ? _nullishCoalesce(_optionalChain([/^\s*<rect[^>]*\/>/, 'access',
|
|
1694
|
+
const bg = svgOptions.background !== false && scene.artwork.background ? _nullishCoalesce(_optionalChain([/^\s*<rect[^>]*\/>/, 'access', _80 => _80.exec, 'call', _81 => _81(body), 'optionalAccess', _82 => _82[0]]), () => ( "")) : "";
|
|
1688
1695
|
return `${current.slice(0, open)}${bg}${ghosts.join("")}${body.slice(bg.length)}${paths}</svg>`;
|
|
1689
1696
|
}
|
|
1690
1697
|
function motionContactSheetSvg(scene, times, options = {}) {
|
|
@@ -1703,7 +1710,7 @@ function motionContactSheetSvg(scene, times, options = {}) {
|
|
|
1703
1710
|
const x = gap + i % cols * (w + gap);
|
|
1704
1711
|
const y = gap + Math.floor(i / cols) * (h + label + gap);
|
|
1705
1712
|
const frame = motionStageSvg(scene, t, { ...frameOptions, idPrefix: `${prefix}f${i}-` });
|
|
1706
|
-
const view = _nullishCoalesce(_optionalChain([/viewBox="([^"]*)"/, 'access',
|
|
1713
|
+
const view = _nullishCoalesce(_optionalChain([/viewBox="([^"]*)"/, 'access', _83 => _83.exec, 'call', _84 => _84(frame), 'optionalAccess', _85 => _85[1]]), () => ( `0 0 ${w} ${h}`));
|
|
1707
1714
|
const inner = innerSvg(frame).replace(new RegExp(` id="(?!${`${prefix}f${i}-`.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")})[^"]*"`, "g"), "");
|
|
1708
1715
|
const shot = _nullishCoalesce(timeline.shots.find((s) => t >= s.start && t < s.end), () => ( timeline.shots[timeline.shots.length - 1]));
|
|
1709
1716
|
const caption = labels ? `<text x="${x + w / 2}" y="${y + h + label * 0.8}" font-family="system-ui, sans-serif" font-size="${Math.round(label * 0.7)}" text-anchor="middle" fill="#64748b">${(t / 1e3).toFixed(2)} s${shot ? ` \xB7 ${shot.id.replace(/[<&]/g, "")}` : ""}</text>` : "";
|
|
@@ -1715,7 +1722,7 @@ function motionContactSheetSvg(scene, times, options = {}) {
|
|
|
1715
1722
|
// src/animation-editor/check.ts
|
|
1716
1723
|
var MOTION_MIN_CHANGE_MS = 120;
|
|
1717
1724
|
var NUMERIC_PARAMS = (node, effectId) => {
|
|
1718
|
-
const effect = _optionalChain([node, 'access',
|
|
1725
|
+
const effect = _optionalChain([node, 'access', _86 => _86.effects, 'optionalAccess', _87 => _87.find, 'call', _88 => _88((e) => e.id === effectId)]);
|
|
1719
1726
|
if (!effect) return null;
|
|
1720
1727
|
return Object.entries(effect.params).filter(([, v]) => typeof v === "number").map(([k]) => k);
|
|
1721
1728
|
};
|
|
@@ -1801,7 +1808,7 @@ function checkMotionScene(scene) {
|
|
|
1801
1808
|
if (!motionTextLayout(nodes, id))
|
|
1802
1809
|
issues.push(_chunkEBDZWIOPcjs.motionIssue.call(void 0, "split-not-text", `"${id}" is not a group of letter paths, so "split": "${command3.split}" animates it as a whole; convert the text to outlines and group the letters (get_text_layout shows how they read).`, { ...where, ids: [id] }));
|
|
1803
1810
|
}
|
|
1804
|
-
if (command3.type === "play" && !_optionalChain([scene, 'access',
|
|
1811
|
+
if (command3.type === "play" && !_optionalChain([scene, 'access', _89 => _89.clips, 'optionalAccess', _90 => _90[command3.clip]]))
|
|
1805
1812
|
issues.push(_chunkEBDZWIOPcjs.motionIssue.call(void 0, "unknown-clip", `There is no clip ${_chunkEBDZWIOPcjs.namedWithSuggestion.call(void 0, command3.clip, Object.keys(_nullishCoalesce(scene.clips, () => ( {}))))}; define it with set_animation_clip.`, where));
|
|
1806
1813
|
if (command3.type === "match" && known(command3.into, "unknown-shape", "group", where)) {
|
|
1807
1814
|
const into = nodes.get(command3.into);
|
|
@@ -1916,7 +1923,7 @@ function checkMotionScene(scene) {
|
|
|
1916
1923
|
if (!shot.at) return;
|
|
1917
1924
|
const timing = timeline.shots[i];
|
|
1918
1925
|
if (timing.cue === void 0) {
|
|
1919
|
-
const what = shot.at.beat !== void 0 ? `beat ${shot.at.beat}, but the scene has no beat grid; set one with set_animation_settings ({ "beat": { "bpm": \u2026 } })` : `word ${shot.at.word}, but the narration has ${_nullishCoalesce(_optionalChain([scene, 'access',
|
|
1926
|
+
const what = shot.at.beat !== void 0 ? `beat ${shot.at.beat}, but the scene has no beat grid; set one with set_animation_settings ({ "beat": { "bpm": \u2026 } })` : `word ${shot.at.word}, but the narration has ${_nullishCoalesce(_optionalChain([scene, 'access', _91 => _91.narration, 'optionalAccess', _92 => _92.words, 'access', _93 => _93.length]), () => ( 0))} words (indexes from 0)`;
|
|
1920
1927
|
issues.push(_chunkEBDZWIOPcjs.motionIssue.call(void 0, "unknown-cue", `Shot "${shot.id}" is cued to ${what}.`, { shot: shot.id }));
|
|
1921
1928
|
} else if (timing.missedBy !== void 0) {
|
|
1922
1929
|
const late = Math.round(timing.missedBy);
|
|
@@ -1949,7 +1956,7 @@ function checkMotionScene(scene) {
|
|
|
1949
1956
|
const cam = frame.camera;
|
|
1950
1957
|
const docIndex = indexMotionNodes(frame.document.nodes);
|
|
1951
1958
|
for (const id of command3.targets) {
|
|
1952
|
-
const b = _optionalChain([docIndex, 'access',
|
|
1959
|
+
const b = _optionalChain([docIndex, 'access', _94 => _94.get, 'call', _95 => _95(id), 'optionalAccess', _96 => _96.bounds]);
|
|
1953
1960
|
if (!b || reported.has(id) || !visibleAt(timeline, id, timing.end, nodes.get(id).node)) continue;
|
|
1954
1961
|
const outside = b.x + b.width < cam.x || b.y + b.height < cam.y || b.x > cam.x + cam.width || b.y > cam.y + cam.height;
|
|
1955
1962
|
if (outside) {
|
|
@@ -2181,9 +2188,9 @@ function applyMotionEdits(scene, edits) {
|
|
|
2181
2188
|
let next = scene;
|
|
2182
2189
|
for (let i = 0; i < list.length; i++) {
|
|
2183
2190
|
const edit = list[i];
|
|
2184
|
-
const prefix = list.length > 1 ? `op ${i + 1} (${_optionalChain([edit, 'optionalAccess',
|
|
2191
|
+
const prefix = list.length > 1 ? `op ${i + 1} (${_optionalChain([edit, 'optionalAccess', _97 => _97.op])}): ` : "";
|
|
2185
2192
|
if (!edit || !MOTION_EDIT_OPS.includes(edit.op))
|
|
2186
|
-
return { ok: false, error: `${prefix}Unknown op ${_chunkEBDZWIOPcjs.namedWithSuggestion.call(void 0, String(_optionalChain([edit, 'optionalAccess',
|
|
2193
|
+
return { ok: false, error: `${prefix}Unknown op ${_chunkEBDZWIOPcjs.namedWithSuggestion.call(void 0, String(_optionalChain([edit, 'optionalAccess', _98 => _98.op])), MOTION_EDIT_OPS)}. Nothing was changed.` };
|
|
2187
2194
|
try {
|
|
2188
2195
|
next = applyOne(next, edit);
|
|
2189
2196
|
} catch (error) {
|
|
@@ -2205,7 +2212,7 @@ function resolveMotionEditRefs(scene, edits) {
|
|
|
2205
2212
|
const resolved = [];
|
|
2206
2213
|
let next = scene;
|
|
2207
2214
|
for (let i = 0; i < edits.length; i++) {
|
|
2208
|
-
const where = `op ${i + 1} (${_optionalChain([edits, 'access',
|
|
2215
|
+
const where = `op ${i + 1} (${_optionalChain([edits, 'access', _99 => _99[i], 'optionalAccess', _100 => _100.op])})`;
|
|
2209
2216
|
let problem = null;
|
|
2210
2217
|
const walk = (v) => {
|
|
2211
2218
|
if (typeof v === "string") {
|
|
@@ -2238,9 +2245,9 @@ function resolveMotionEditRefs(scene, edits) {
|
|
|
2238
2245
|
resolved.push(edit);
|
|
2239
2246
|
const out = { op: edit.op };
|
|
2240
2247
|
if (edit.op === "insert_shots") out.shots = edit.shots.map((s) => s.id);
|
|
2241
|
-
if ("shot" in edit && typeof edit.shot === "string") out.shot = edit.op === "update_shot" && _optionalChain([edit, 'access',
|
|
2248
|
+
if ("shot" in edit && typeof edit.shot === "string") out.shot = edit.op === "update_shot" && _optionalChain([edit, 'access', _101 => _101.set, 'optionalAccess', _102 => _102.id]) ? edit.set.id : edit.shot;
|
|
2242
2249
|
if (edit.op === "add_commands") {
|
|
2243
|
-
const at = _nullishCoalesce(_nullishCoalesce(edit.at, () => ( _optionalChain([before, 'access',
|
|
2250
|
+
const at = _nullishCoalesce(_nullishCoalesce(edit.at, () => ( _optionalChain([before, 'access', _103 => _103.shots, 'access', _104 => _104.find, 'call', _105 => _105((s) => s.id === edit.shot), 'optionalAccess', _106 => _106.run, 'access', _107 => _107.length]))), () => ( 0));
|
|
2244
2251
|
out.indexes = edit.commands.map((_, k) => at + k);
|
|
2245
2252
|
}
|
|
2246
2253
|
if (edit.op === "move_command") out.indexes = [edit.to];
|
|
@@ -2255,8 +2262,8 @@ function createMotionScene(artwork, init = {}) {
|
|
|
2255
2262
|
|
|
2256
2263
|
// src/animation-editor/fit.ts
|
|
2257
2264
|
var MOTION_SPEED_RANGE = [0.05, 20];
|
|
2258
|
-
function fitMotionScene(scene,
|
|
2259
|
-
if (!(Number.isFinite(
|
|
2265
|
+
function fitMotionScene(scene, ms2, options = {}) {
|
|
2266
|
+
if (!(Number.isFinite(ms2) && ms2 > 0)) throw new Error("Give the length to fit as a number of milliseconds above 0.");
|
|
2260
2267
|
const ids = scene.shots.map((s) => s.id);
|
|
2261
2268
|
const keep = new Set(_nullishCoalesce(options.keep, () => ( [])));
|
|
2262
2269
|
for (const id of keep) if (!ids.includes(id)) throw new Error(`There is no shot ${_chunkEBDZWIOPcjs.namedWithSuggestion.call(void 0, id, ids)} to keep.`);
|
|
@@ -2265,14 +2272,14 @@ function fitMotionScene(scene, ms, options = {}) {
|
|
|
2265
2272
|
let rest = 0;
|
|
2266
2273
|
timeline.shots.forEach((t) => keep.has(t.id) ? kept += t.end - t.start : rest += t.end - t.start);
|
|
2267
2274
|
if (rest <= 0) throw new Error(keep.size ? "Every shot is kept, so nothing can change pace; keep fewer shots." : "The scene has no length to fit; add shots first.");
|
|
2268
|
-
if (
|
|
2269
|
-
const k = (
|
|
2275
|
+
if (ms2 <= kept) throw new Error(`The kept shots alone last ${Math.round(kept)} ms, so the scene cannot fit ${Math.round(ms2)} ms; keep fewer shots or give at least ${Math.ceil(kept) + 1} ms.`);
|
|
2276
|
+
const k = (ms2 - kept) / rest;
|
|
2270
2277
|
const speeds = {};
|
|
2271
2278
|
const shots = scene.shots.map((shot) => {
|
|
2272
2279
|
if (keep.has(shot.id)) return shot;
|
|
2273
2280
|
const speed = Math.round((_nullishCoalesce(shot.speed, () => ( 1))) / k * 1e4) / 1e4;
|
|
2274
2281
|
if (speed < MOTION_SPEED_RANGE[0] || speed > MOTION_SPEED_RANGE[1])
|
|
2275
|
-
throw new Error(`Fitting ${Math.round(
|
|
2282
|
+
throw new Error(`Fitting ${Math.round(ms2)} ms would play shot "${shot.id}" at ${speed}\xD7 (speeds run ${MOTION_SPEED_RANGE[0]}\u2013${MOTION_SPEED_RANGE[1]}); aim nearer the current ${Math.round(timeline.duration)} ms.`);
|
|
2276
2283
|
speeds[shot.id] = speed;
|
|
2277
2284
|
const { speed: _old, ...rest2 } = shot;
|
|
2278
2285
|
return speed === 1 ? rest2 : { ...rest2, speed };
|
|
@@ -2281,6 +2288,214 @@ function fitMotionScene(scene, ms, options = {}) {
|
|
|
2281
2288
|
return { scene: fitted, duration: compileMotionScene(fitted).duration, speeds };
|
|
2282
2289
|
}
|
|
2283
2290
|
|
|
2291
|
+
// src/animation-editor/record.ts
|
|
2292
|
+
var TOLERANCE = { move: 1, scale: 0.01, rotation: 0.5, opacity: 0.01, strokeWidth: 0.25 };
|
|
2293
|
+
var COLOR_GAP = 80;
|
|
2294
|
+
var MAX_VIA = 64;
|
|
2295
|
+
function motionNodePose(scene, id, time) {
|
|
2296
|
+
const entry = indexMotionNodes(scene.artwork.nodes).get(id);
|
|
2297
|
+
if (!entry) throw new Error(`There is no element ${_chunkEBDZWIOPcjs.namedWithSuggestion.call(void 0, id, [...indexMotionNodes(scene.artwork.nodes).keys()])} in the artwork.`);
|
|
2298
|
+
const timeline = compileMotionScene(scene);
|
|
2299
|
+
const drawn = indexMotionNodes(motionFrameAt(scene, time).document.nodes).get(id);
|
|
2300
|
+
const centre = _chunkHRI3ZMDMcjs.applyMatrix.call(void 0, entry.parentWorld, { x: entry.center.x + motionPropertyValue(timeline, id, "x", time), y: entry.center.y + motionPropertyValue(timeline, id, "y", time) });
|
|
2301
|
+
const paint = (property) => _nullishCoalesce(trackValue(timeline.tracks.get(motionTrackKey(id, property)), time), () => ( motionRestValue(entry, property, scene.artwork)));
|
|
2302
|
+
const fill = paint("fill");
|
|
2303
|
+
const stroke = paint("stroke");
|
|
2304
|
+
const width = paint("strokeWidth");
|
|
2305
|
+
return {
|
|
2306
|
+
x: centre.x,
|
|
2307
|
+
y: centre.y,
|
|
2308
|
+
scaleX: motionPropertyValue(timeline, id, "scaleX", time),
|
|
2309
|
+
scaleY: motionPropertyValue(timeline, id, "scaleY", time),
|
|
2310
|
+
rotation: motionPropertyValue(timeline, id, "rotation", time),
|
|
2311
|
+
opacity: motionPropertyValue(timeline, id, "opacity", time),
|
|
2312
|
+
...typeof fill === "string" && fill !== "transparent" ? { fill } : {},
|
|
2313
|
+
...typeof stroke === "string" && stroke !== "transparent" ? { stroke } : {},
|
|
2314
|
+
...typeof width === "number" ? { strokeWidth: width } : {},
|
|
2315
|
+
bounds: _nullishCoalesce(_optionalChain([drawn, 'optionalAccess', _108 => _108.bounds]), () => ( null))
|
|
2316
|
+
};
|
|
2317
|
+
}
|
|
2318
|
+
function simplifyTimedSamples(times, values, tol) {
|
|
2319
|
+
const n = times.length;
|
|
2320
|
+
if (n <= 2) return [...Array(n).keys()];
|
|
2321
|
+
const keep = new Uint8Array(n);
|
|
2322
|
+
keep[0] = keep[n - 1] = 1;
|
|
2323
|
+
const stack = [[0, n - 1]];
|
|
2324
|
+
while (stack.length) {
|
|
2325
|
+
const [a, b] = stack.pop();
|
|
2326
|
+
let worst = -1;
|
|
2327
|
+
let at = -1;
|
|
2328
|
+
const span = times[b] - times[a];
|
|
2329
|
+
for (let i = a + 1; i < b; i++) {
|
|
2330
|
+
const u = span > 0 ? (times[i] - times[a]) / span : 0;
|
|
2331
|
+
let e = 0;
|
|
2332
|
+
for (let d = 0; d < values[i].length; d++) {
|
|
2333
|
+
const v = values[a][d] + (values[b][d] - values[a][d]) * u;
|
|
2334
|
+
e += (values[i][d] - v) ** 2;
|
|
2335
|
+
}
|
|
2336
|
+
if (e > worst) {
|
|
2337
|
+
worst = e;
|
|
2338
|
+
at = i;
|
|
2339
|
+
}
|
|
2340
|
+
}
|
|
2341
|
+
if (at >= 0 && Math.sqrt(worst) > tol) {
|
|
2342
|
+
keep[at] = 1;
|
|
2343
|
+
stack.push([a, at], [at, b]);
|
|
2344
|
+
}
|
|
2345
|
+
}
|
|
2346
|
+
const out = [];
|
|
2347
|
+
keep.forEach((k, i) => k && out.push(i));
|
|
2348
|
+
return out;
|
|
2349
|
+
}
|
|
2350
|
+
var round = (n, places = 2) => Math.round(n * 10 ** places) / 10 ** places || 0;
|
|
2351
|
+
var ms = (n) => Math.max(0, Math.round(n));
|
|
2352
|
+
function motionTakeProblems(scene, take) {
|
|
2353
|
+
const problems = [];
|
|
2354
|
+
const nodes = indexMotionNodes(scene.artwork.nodes);
|
|
2355
|
+
if (!Array.isArray(take.samples) || !take.samples.length) return ["Give at least one sample."];
|
|
2356
|
+
const num2 = (v) => typeof v === "number" && Number.isFinite(v);
|
|
2357
|
+
take.samples.forEach((s, i) => {
|
|
2358
|
+
const where = `Sample ${i + 1}`;
|
|
2359
|
+
if (!s || typeof s !== "object") return problems.push(`${where} must be an object.`);
|
|
2360
|
+
if (typeof s.node !== "string" || !nodes.has(s.node)) problems.push(`${where}: the artwork has no element ${_chunkEBDZWIOPcjs.namedWithSuggestion.call(void 0, String(s.node), [...nodes.keys()])}; get_animation lists the element ids.`);
|
|
2361
|
+
if (!num2(s.t) || s.t < 0) problems.push(`${where}: "t" must be ms from the start of the take, \u2265 0.`);
|
|
2362
|
+
if (s.x === void 0 !== (s.y === void 0)) problems.push(`${where}: give "x" and "y" together (the centre, document units).`);
|
|
2363
|
+
for (const k of ["x", "y", "scaleX", "scaleY", "rotation", "opacity", "strokeWidth"]) if (s[k] !== void 0 && !num2(s[k])) problems.push(`${where}: "${k}" must be a number.`);
|
|
2364
|
+
if (s.opacity !== void 0 && num2(s.opacity) && (s.opacity < 0 || s.opacity > 1)) problems.push(`${where}: "opacity" must be from 0 to 1.`);
|
|
2365
|
+
if (s.strokeWidth !== void 0 && num2(s.strokeWidth) && s.strokeWidth < 0) problems.push(`${where}: "strokeWidth" must be \u2265 0.`);
|
|
2366
|
+
for (const k of ["fill", "stroke"]) if (s[k] !== void 0 && (typeof s[k] !== "string" || _chunkA6QWKBJPcjs.normalizeTokenColor.call(void 0, s[k]) === null)) problems.push(`${where}: "${k}" must be a colour such as "#0ea5e9".`);
|
|
2367
|
+
if (["x", "scaleX", "scaleY", "rotation", "opacity", "fill", "stroke", "strokeWidth"].every((k) => s[k] === void 0)) problems.push(`${where} records nothing; give at least one of x and y, scaleX, scaleY, rotation, opacity, fill, stroke, strokeWidth.`);
|
|
2368
|
+
});
|
|
2369
|
+
if (take.length !== void 0 && !(num2(take.length) && take.length >= 0)) problems.push('"length" must be ms \u2265 0.');
|
|
2370
|
+
return problems;
|
|
2371
|
+
}
|
|
2372
|
+
function channelSamples(samples, pick) {
|
|
2373
|
+
const out = [];
|
|
2374
|
+
for (const s of samples) {
|
|
2375
|
+
const v = pick(s);
|
|
2376
|
+
if (v === void 0) continue;
|
|
2377
|
+
if (out.length && Math.abs(out[out.length - 1].t - s.t) < 0.5) out[out.length - 1] = { t: s.t, v };
|
|
2378
|
+
else out.push({ t: s.t, v });
|
|
2379
|
+
}
|
|
2380
|
+
return out;
|
|
2381
|
+
}
|
|
2382
|
+
function channelCommands(node, channel, pose, samples, tol, kept) {
|
|
2383
|
+
const targets = [node];
|
|
2384
|
+
const out = [];
|
|
2385
|
+
if (channel === "fill" || channel === "stroke") {
|
|
2386
|
+
const list2 = channelSamples(samples, (s) => s[channel] !== void 0 ? _nullishCoalesce(_chunkA6QWKBJPcjs.normalizeTokenColor.call(void 0, s[channel]), () => ( void 0)) : void 0);
|
|
2387
|
+
let last = pose[channel] ? _chunkA6QWKBJPcjs.normalizeTokenColor.call(void 0, pose[channel]) : null;
|
|
2388
|
+
let lastT = -Infinity;
|
|
2389
|
+
list2.forEach((s, i) => {
|
|
2390
|
+
const final = i === list2.length - 1;
|
|
2391
|
+
if (s.v === last || !final && s.t - lastT < COLOR_GAP) return;
|
|
2392
|
+
const duration = lastT === -Infinity ? 0 : s.t - lastT;
|
|
2393
|
+
out.push({ start: s.t - duration, command: { type: "style", targets, [channel]: s.v, duration: ms(duration), ease: "linear" } });
|
|
2394
|
+
kept.n++;
|
|
2395
|
+
last = s.v;
|
|
2396
|
+
lastT = s.t;
|
|
2397
|
+
});
|
|
2398
|
+
return out;
|
|
2399
|
+
}
|
|
2400
|
+
const pick = channel === "move" ? (s) => s.x !== void 0 && s.y !== void 0 ? [s.x, s.y] : void 0 : channel === "scale" ? (s) => s.scaleX !== void 0 || s.scaleY !== void 0 ? [_nullishCoalesce(s.scaleX, () => ( s.scaleY)), _nullishCoalesce(s.scaleY, () => ( s.scaleX))] : void 0 : (s) => s[channel] !== void 0 ? [s[channel]] : void 0;
|
|
2401
|
+
const list = channelSamples(samples, pick);
|
|
2402
|
+
if (!list.length) return out;
|
|
2403
|
+
const rest = channel === "move" ? [pose.x, pose.y] : channel === "scale" ? [pose.scaleX, pose.scaleY] : [_nullishCoalesce(pose[channel], () => ( (channel === "strokeWidth" ? 1 : 0)))];
|
|
2404
|
+
const limit = TOLERANCE[channel] * tol;
|
|
2405
|
+
const differs = (a, b) => Math.hypot(...a.map((v, d) => v - b[d])) > limit;
|
|
2406
|
+
const make = (to, duration, ease, extra = {}) => {
|
|
2407
|
+
const base = { targets, duration: ms(duration), ease, ...extra };
|
|
2408
|
+
switch (channel) {
|
|
2409
|
+
case "move":
|
|
2410
|
+
return { type: "move", ...base, to: { x: round(to[0]), y: round(to[1]) } };
|
|
2411
|
+
case "scale":
|
|
2412
|
+
return { type: "scale", ...base, to: Math.abs(to[0] - to[1]) < 1e-4 ? round(to[0], 4) : { x: round(to[0], 4), y: round(to[1], 4) } };
|
|
2413
|
+
case "rotation":
|
|
2414
|
+
return { type: "rotate", ...base, to: round(to[0]) };
|
|
2415
|
+
case "opacity":
|
|
2416
|
+
return { type: "fade", ...base, to: round(to[0], 4) };
|
|
2417
|
+
default:
|
|
2418
|
+
return { type: "style", ...base, strokeWidth: round(to[0]) };
|
|
2419
|
+
}
|
|
2420
|
+
};
|
|
2421
|
+
if (differs(list[0].v, rest)) {
|
|
2422
|
+
out.push({ start: list[0].t, command: make(list[0].v, 0, "linear") });
|
|
2423
|
+
kept.n++;
|
|
2424
|
+
}
|
|
2425
|
+
if (list.length < 2) return out;
|
|
2426
|
+
const times = list.map((s) => s.t);
|
|
2427
|
+
const values = list.map((s) => s.v);
|
|
2428
|
+
let factor = 1;
|
|
2429
|
+
let keep = simplifyTimedSamples(times, values, limit);
|
|
2430
|
+
if (channel === "move") {
|
|
2431
|
+
while (keep.length - 2 > MAX_VIA) keep = simplifyTimedSamples(times, values, limit * (factor *= 1.5));
|
|
2432
|
+
const first = list[keep[0]];
|
|
2433
|
+
const end = list[keep[keep.length - 1]];
|
|
2434
|
+
if (!keep.some((k) => differs(values[k], first.v))) return out;
|
|
2435
|
+
const span = end.t - first.t;
|
|
2436
|
+
const via = [];
|
|
2437
|
+
for (const k of keep.slice(1, -1)) {
|
|
2438
|
+
const f = round((list[k].t - first.t) / span, 6);
|
|
2439
|
+
if (f > (_nullishCoalesce(_optionalChain([via, 'access', _109 => _109.at, 'call', _110 => _110(-1), 'optionalAccess', _111 => _111.f]), () => ( 0))) && f < 1) via.push({ s: list[k], f });
|
|
2440
|
+
}
|
|
2441
|
+
const extra = via.length ? { via: via.map(({ s }) => ({ x: round(s.v[0]), y: round(s.v[1]) })), times: via.map(({ f }) => f) } : {};
|
|
2442
|
+
out.push({ start: first.t, command: make(end.v, span, "linear", extra) });
|
|
2443
|
+
kept.n += via.length + 2;
|
|
2444
|
+
return out;
|
|
2445
|
+
}
|
|
2446
|
+
const kt = keep.map((k) => times[k]);
|
|
2447
|
+
const legs = values[0].map((_, d) => smoothMotionLegs(kt, keep.map((k) => values[k][d])));
|
|
2448
|
+
for (let i = 0; i + 1 < keep.length; i++) {
|
|
2449
|
+
const a = values[keep[i]];
|
|
2450
|
+
const b = values[keep[i + 1]];
|
|
2451
|
+
if (!differs(a, b)) continue;
|
|
2452
|
+
const lead = a.length > 1 && Math.abs(b[1] - a[1]) > Math.abs(b[0] - a[0]) ? 1 : 0;
|
|
2453
|
+
const leg = legs[lead][i];
|
|
2454
|
+
out.push({ start: leg.start, command: make(b, leg.end - leg.start, leg.ease) });
|
|
2455
|
+
kept.n++;
|
|
2456
|
+
}
|
|
2457
|
+
return out;
|
|
2458
|
+
}
|
|
2459
|
+
function motionTakeShot(scene, take, options = {}) {
|
|
2460
|
+
const problems = motionTakeProblems(scene, take);
|
|
2461
|
+
if (problems.length) throw new Error(problems.join(" "));
|
|
2462
|
+
const tol = options.tolerance !== void 0 && options.tolerance > 0 ? options.tolerance : 1;
|
|
2463
|
+
const at = _nullishCoalesce(options.at, () => ( scene.shots.length));
|
|
2464
|
+
if (!Number.isInteger(at) || at < 0 || at > scene.shots.length) throw new Error(`"at" must be a whole number from 0 to ${scene.shots.length} (got ${at}).`);
|
|
2465
|
+
const timeline = compileMotionScene(scene);
|
|
2466
|
+
const start = at < timeline.shots.length ? timeline.shots[at].start : timeline.duration;
|
|
2467
|
+
const samples = [...take.samples].sort((a, b) => a.t - b.t);
|
|
2468
|
+
const placed = [];
|
|
2469
|
+
const kept = { n: 0 };
|
|
2470
|
+
const byNode = /* @__PURE__ */ new Map();
|
|
2471
|
+
for (const s of samples) byNode.set(s.node, [..._nullishCoalesce(byNode.get(s.node), () => ( [])), s]);
|
|
2472
|
+
for (const [node, list] of byNode) {
|
|
2473
|
+
const pose = motionNodePose(scene, node, start);
|
|
2474
|
+
for (const channel of ["move", "scale", "rotation", "opacity", "strokeWidth", "fill", "stroke"]) placed.push(...channelCommands(node, channel, pose, list, tol, kept));
|
|
2475
|
+
}
|
|
2476
|
+
placed.sort((a, b) => a.start - b.start);
|
|
2477
|
+
const run = [];
|
|
2478
|
+
let prev = 0;
|
|
2479
|
+
let end = 0;
|
|
2480
|
+
placed.forEach((p, i) => {
|
|
2481
|
+
const s = ms(p.start);
|
|
2482
|
+
const delay = i === 0 ? s : s - prev;
|
|
2483
|
+
run.push({ ...p.command, ...i > 0 ? { with: "previous" } : {}, ...delay > 0 ? { delay } : {} });
|
|
2484
|
+
prev = s;
|
|
2485
|
+
end = Math.max(end, s + (_nullishCoalesce(p.command.duration, () => ( 0))));
|
|
2486
|
+
});
|
|
2487
|
+
const length = _nullishCoalesce(take.length, () => ( samples[samples.length - 1].t));
|
|
2488
|
+
const shot = { id: _nullishCoalesce(options.id, () => ( freshShotId(scene, "take"))), label: _nullishCoalesce(options.label, () => ( "Recorded take")), run, hold: ms(Math.max(0, length - end)) };
|
|
2489
|
+
if (!run.length) Object.assign(shot, { run: [{ type: "wait", duration: ms(Math.max(1, length)) }], hold: 0 });
|
|
2490
|
+
return { shot, at, start, samples: { given: take.samples.length, kept: kept.n } };
|
|
2491
|
+
}
|
|
2492
|
+
function motionClientToDocument(camera, box, clientX, clientY) {
|
|
2493
|
+
const k = Math.min(box.width / camera.width, box.height / camera.height) || 1;
|
|
2494
|
+
const ox = box.left + (box.width - camera.width * k) / 2;
|
|
2495
|
+
const oy = box.top + (box.height - camera.height * k) / 2;
|
|
2496
|
+
return { x: camera.x + (clientX - ox) / k, y: camera.y + (clientY - oy) / k };
|
|
2497
|
+
}
|
|
2498
|
+
|
|
2284
2499
|
// src/animation-editor/flair.ts
|
|
2285
2500
|
var FLAIR_EASES = {
|
|
2286
2501
|
linear: (t) => t,
|
|
@@ -2327,7 +2542,7 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
2327
2542
|
const behaviors = _nullishCoalesce(options.behaviors, () => ( flairAtLeast(flair, "0.38")));
|
|
2328
2543
|
const springs = flairAtLeast(flair, "0.39");
|
|
2329
2544
|
const strobes = flairAtLeast(flair, "0.39");
|
|
2330
|
-
const writeEase = (ease) => typeof ease === "string" || "spring" in ease ? ease : ease.map(
|
|
2545
|
+
const writeEase = (ease) => typeof ease === "string" || "spring" in ease ? ease : ease.map(round3);
|
|
2331
2546
|
const easeOf2 = (segment) => {
|
|
2332
2547
|
const resolved = _chunkDWJJI6RTcjs.resolveEasing.call(void 0, segment.ease);
|
|
2333
2548
|
if (resolved !== "hold" && resolved.type === "spring") {
|
|
@@ -2340,8 +2555,8 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
2340
2555
|
return back && curves ? flairCurveFor(back, curves) : void 0;
|
|
2341
2556
|
};
|
|
2342
2557
|
const plays = (s) => (!s.behavior || behaviors) && (!s.steps || strobes) && !s.geometric;
|
|
2343
|
-
const
|
|
2344
|
-
const f = (n) => String(
|
|
2558
|
+
const round3 = (n) => Math.round(n * 10 ** precision) / 10 ** precision;
|
|
2559
|
+
const f = (n) => String(round3(n));
|
|
2345
2560
|
const timeline = compileMotionScene(scene);
|
|
2346
2561
|
const nodes = indexMotionNodes(scene.artwork.nodes);
|
|
2347
2562
|
const notes = [];
|
|
@@ -2399,7 +2614,7 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
2399
2614
|
const wrapper = freshId(`${id}--follow`);
|
|
2400
2615
|
const centre = freshId(`${id}--follow-centre`);
|
|
2401
2616
|
const c = entry.center;
|
|
2402
|
-
const at = _optionalChain([motionNodeOffset, 'call',
|
|
2617
|
+
const at = _optionalChain([motionNodeOffset, 'call', _112 => _112(entry, timeline, 0, nodes), 'optionalAccess', _113 => _113.follow]);
|
|
2403
2618
|
const placed = at ? `translate(${f(at.x)} ${f(at.y)})${at.angle ? ` rotate(${f(at.angle)})` : ""}` : void 0;
|
|
2404
2619
|
(wrap[id] ??= []).push({ id: wrapper, ...placed ? { transform: placed } : {} }, { id: centre, transform: `translate(${f(-c.x)} ${f(-c.y)})` });
|
|
2405
2620
|
follows.push({ id, wrapper, routes: ids, segments: followSegments.filter((seg) => seg.path && ids.has(seg.path)) });
|
|
@@ -2407,7 +2622,7 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
2407
2622
|
}
|
|
2408
2623
|
const opacitySources = sourcesOf(id, ["opacity"]);
|
|
2409
2624
|
if (timeline.tracks.has(motionTrackKey(id, "opacity")) || opacitySources.length)
|
|
2410
|
-
channels.push({ id, kind: "attr", attr: "opacity", segments: [...track(id, "opacity"), ...opacitySources], exact: (s) => plays(s) && !drivenBy.has(s), value: (t) =>
|
|
2625
|
+
channels.push({ id, kind: "attr", attr: "opacity", segments: [...track(id, "opacity"), ...opacitySources], exact: (s) => plays(s) && !drivenBy.has(s), value: (t) => round3(motionPropertyValue(timeline, id, "opacity", t)) });
|
|
2411
2626
|
const followed = follows.some((x) => x.id === id);
|
|
2412
2627
|
const transformSegments = [...TRANSFORM_PROPS2.flatMap((p) => followed && p === "follow" ? [] : [...track(id, p), ..._nullishCoalesce(timeline.tracks.get(motionAddTrackKey(id, p)), () => ( []))]), ...sourcesOf(id, ["x", "y", "rotation", "scaleX", "scaleY"])];
|
|
2413
2628
|
if (transformSegments.length) {
|
|
@@ -2440,11 +2655,11 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
2440
2655
|
paint("fill", "fill");
|
|
2441
2656
|
paint("stroke", "stroke");
|
|
2442
2657
|
const width = owner(path, "strokeWidth");
|
|
2443
|
-
if (width) channels.push({ id: path.id, kind: "attr", attr: "stroke-width", segments: [...track(width, "strokeWidth")], exact: () => true, value: (t) =>
|
|
2658
|
+
if (width) channels.push({ id: path.id, kind: "attr", attr: "stroke-width", segments: [...track(width, "strokeWidth")], exact: () => true, value: (t) => round3(Number(trackValue(track(width, "strokeWidth"), t))) });
|
|
2444
2659
|
const fade = owner(path, "fillOpacity");
|
|
2445
|
-
if (fade) channels.push({ id: path.id, kind: "attr", attr: "fill-opacity", segments: [...track(fade, "fillOpacity")], exact: () => true, value: (t) =>
|
|
2660
|
+
if (fade) channels.push({ id: path.id, kind: "attr", attr: "fill-opacity", segments: [...track(fade, "fillOpacity")], exact: () => true, value: (t) => round3(Number(trackValue(track(fade, "fillOpacity"), t))) });
|
|
2446
2661
|
const draw = owner(path, "drawEnd");
|
|
2447
|
-
if (draw) channels.push({ id: path.id, kind: "draw", segments: [...track(draw, "drawEnd")], exact: () => true, value: (t) =>
|
|
2662
|
+
if (draw) channels.push({ id: path.id, kind: "draw", segments: [...track(draw, "drawEnd")], exact: () => true, value: (t) => round3(Math.min(1, Math.max(0, Number(trackValue(track(draw, "drawEnd"), t))))) });
|
|
2448
2663
|
if (owner(path, "drawStart")) notes.push(`"${path.id}": drawing from a start other than 0 is not exported; Flair draws from the start of the path.`);
|
|
2449
2664
|
}
|
|
2450
2665
|
const shapeSegments = [...timeline.tracks.entries()].filter(([k]) => k.startsWith(`${id}\0`) && (k.endsWith("\0shape") || k.includes("\0effect:"))).flatMap(([, v]) => v);
|
|
@@ -2460,7 +2675,7 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
2460
2675
|
exact: () => false,
|
|
2461
2676
|
value: (t) => {
|
|
2462
2677
|
const frame = motionFrameAt(scene, t, { draw: false, offsets: false });
|
|
2463
|
-
const now = _chunkHRI3ZMDMcjs.expandVectorAppearance.call(void 0, _optionalChain([indexMotionNodes, 'call',
|
|
2678
|
+
const now = _chunkHRI3ZMDMcjs.expandVectorAppearance.call(void 0, _optionalChain([indexMotionNodes, 'call', _114 => _114(frame.document.nodes), 'access', _115 => _115.get, 'call', _116 => _116(id), 'optionalAccess', _117 => _117.node]));
|
|
2464
2679
|
if (!now || now.type !== "path") return "";
|
|
2465
2680
|
const bezier = _chunkGF2RBSDMcjs.transformBezierPath.call(void 0, _chunkHRI3ZMDMcjs.vectorPathWorldBezier.call(void 0, now, _chunkHRI3ZMDMcjs.IDENTITY_MATRIX, { effects: false }), inverse);
|
|
2466
2681
|
return _chunkGF2RBSDMcjs.bezierPathToSvg.call(void 0, bezier, { precision: Math.min(precision, 2) });
|
|
@@ -2470,19 +2685,19 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
2470
2685
|
}
|
|
2471
2686
|
}
|
|
2472
2687
|
const camera = track("", "camera");
|
|
2473
|
-
if (camera.length) channels.push({ id: "", kind: "view", segments: [...camera], exact: () => true, overshoot: true, value: (t) => trackValue(camera, t).map(
|
|
2688
|
+
if (camera.length) channels.push({ id: "", kind: "view", segments: [...camera], exact: () => true, overshoot: true, value: (t) => trackValue(camera, t).map(round3) });
|
|
2474
2689
|
const animate = [];
|
|
2475
2690
|
let sampled = 0;
|
|
2476
2691
|
const same = (a, b) => JSON.stringify(a) === JSON.stringify(b);
|
|
2477
|
-
const emit = (channel, delay,
|
|
2692
|
+
const emit = (channel, delay, ms2, from, to, ease, segment) => {
|
|
2478
2693
|
const written = writeEase(ease);
|
|
2479
2694
|
const base = {
|
|
2480
2695
|
...channel.id ? { id: channel.id } : {},
|
|
2481
|
-
...delay > 0 ? { delay:
|
|
2482
|
-
ms:
|
|
2696
|
+
...delay > 0 ? { delay: round3(delay) } : {},
|
|
2697
|
+
ms: round3(ms2),
|
|
2483
2698
|
ease: written,
|
|
2484
|
-
..._optionalChain([segment, 'optionalAccess',
|
|
2485
|
-
..._optionalChain([segment, 'optionalAccess',
|
|
2699
|
+
..._optionalChain([segment, 'optionalAccess', _118 => _118.behavior]) ? { behavior: segment.behavior } : {},
|
|
2700
|
+
..._optionalChain([segment, 'optionalAccess', _119 => _119.steps]) ? { strobe: segment.steps } : {}
|
|
2486
2701
|
};
|
|
2487
2702
|
if (channel.kind === "view") animate.push({ ...base, view: to });
|
|
2488
2703
|
else if (channel.kind === "draw") animate.push({ ...base, draw: [from, to] });
|
|
@@ -2533,11 +2748,11 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
2533
2748
|
const starts = follow.segments.map((seg) => seg.start);
|
|
2534
2749
|
for (const seg of follow.segments) {
|
|
2535
2750
|
const along = { id: follow.wrapper, along: follow.routes.get(seg.path), ...seg.rotate ? { orient: true } : {} };
|
|
2536
|
-
const progress = (t) =>
|
|
2751
|
+
const progress = (t) => round3(Math.min(1, Math.max(0, Number(segmentValue(seg, t)))));
|
|
2537
2752
|
const curve = seg.end > seg.start ? plays(seg) ? easeOf2(seg) : void 0 : "linear";
|
|
2538
2753
|
if (curve && !(overshoots(curve) && seg.rotate)) {
|
|
2539
2754
|
const ends = seg.reverse ? [Number(seg.to), Number(seg.from)] : [Number(seg.from), Number(seg.to)];
|
|
2540
|
-
animate.push({ ...along, ...seg.start > 0 ? { delay:
|
|
2755
|
+
animate.push({ ...along, ...seg.start > 0 ? { delay: round3(seg.start) } : {}, ms: round3(seg.end - seg.start), ease: writeEase(curve), ...seg.steps ? { strobe: seg.steps } : {}, at: [round3(ends[0]), round3(ends[1])] });
|
|
2541
2756
|
continue;
|
|
2542
2757
|
}
|
|
2543
2758
|
const until = Math.min(seg.end, ...starts.filter((t) => t > seg.start));
|
|
@@ -2545,7 +2760,7 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
2545
2760
|
for (let k = 1; k <= n; k++) {
|
|
2546
2761
|
const t0 = seg.start + (until - seg.start) * (k - 1) / n;
|
|
2547
2762
|
const t1 = seg.start + (until - seg.start) * k / n;
|
|
2548
|
-
animate.push({ ...along, ...t0 > 0 ? { delay:
|
|
2763
|
+
animate.push({ ...along, ...t0 > 0 ? { delay: round3(t0) } : {}, ms: round3(t1 - t0), ease: "linear", at: [progress(t0), progress(t1)] });
|
|
2549
2764
|
sampled++;
|
|
2550
2765
|
}
|
|
2551
2766
|
}
|
|
@@ -2560,7 +2775,7 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
2560
2775
|
timeline.shots.forEach((shot, i) => {
|
|
2561
2776
|
if (shot.start <= at + 1e-6) step = i;
|
|
2562
2777
|
});
|
|
2563
|
-
const delay =
|
|
2778
|
+
const delay = round3(at - timeline.shots[step].start);
|
|
2564
2779
|
delete a.delay;
|
|
2565
2780
|
if (delay > 0) a.delay = delay;
|
|
2566
2781
|
a.step = step + 1;
|
|
@@ -2572,7 +2787,7 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
2572
2787
|
let svg = _chunkHRI3ZMDMcjs.vectorDocumentToSvg.call(void 0, { ...scene.artwork, nodes: _chunkHRI3ZMDMcjs.expandVectorAppearances.call(void 0, scene.artwork.nodes) }, { pretty: false, precision, wrap });
|
|
2573
2788
|
if (routes.length) svg = svg.replace(/^(<svg[^>]*>)/, `$1<defs>${routes.join("")}</defs>`);
|
|
2574
2789
|
let motion = { svg, animate };
|
|
2575
|
-
if (_optionalChain([scene, 'access',
|
|
2790
|
+
if (_optionalChain([scene, 'access', _120 => _120.controls, 'optionalAccess', _121 => _121.length])) {
|
|
2576
2791
|
const declarations = scene.controls.map((c) => `{{${c.name}: color default=${c.default.toUpperCase()}${c.label ? ` label='${c.label.replace(/'/g, "\u2019")}'` : ""}}}`).join(" ");
|
|
2577
2792
|
const swap = (text) => scene.controls.reduce((s, c) => s.replace(new RegExp(`${c.default}(?![0-9a-fA-F])`, "gi"), `{{${c.name}}}`), text);
|
|
2578
2793
|
svg = svg.replace(/^(<svg[^>]*>)/, `$1<!-- ${declarations} -->`);
|
|
@@ -2580,7 +2795,7 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
2580
2795
|
svg = svg.slice(0, header) + swap(svg.slice(header));
|
|
2581
2796
|
motion = { svg, animate: animate.map((a) => a.attrs ? { ...a, attrs: Object.fromEntries(Object.entries(a.attrs).map(([k, [x, y]]) => [k, [typeof x === "string" ? swap(x) : x, typeof y === "string" ? swap(y) : y]])) } : a) };
|
|
2582
2797
|
}
|
|
2583
|
-
return { motion, duration: timeline.duration, hold: Math.max(0,
|
|
2798
|
+
return { motion, duration: timeline.duration, hold: Math.max(0, round3(timeline.duration - lastChange)), sampled, notes: [...new Set(notes)], ...trigger ? { trigger } : {} };
|
|
2584
2799
|
}
|
|
2585
2800
|
var kebab = (text) => text.trim().replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/[^A-Za-z0-9]+/g, "-").replace(/^-+|-+$/g, "").toLowerCase() || "animation";
|
|
2586
2801
|
function toFlairVectorEffect(scene, options = {}) {
|
|
@@ -2594,7 +2809,7 @@ function toFlairVectorEffect(scene, options = {}) {
|
|
|
2594
2809
|
tags: _nullishCoalesce(options.tags, () => ( [])),
|
|
2595
2810
|
motion: out.motion,
|
|
2596
2811
|
hold: out.hold,
|
|
2597
|
-
..._optionalChain([scene, 'access',
|
|
2812
|
+
..._optionalChain([scene, 'access', _122 => _122.presets, 'optionalAccess', _123 => _123.length]) ? { presets: scene.presets } : {},
|
|
2598
2813
|
...out.trigger ? { trigger: out.trigger } : {}
|
|
2599
2814
|
};
|
|
2600
2815
|
}
|
|
@@ -2638,7 +2853,7 @@ function resolvePlaceholders(motion, notes) {
|
|
|
2638
2853
|
const filled = text.replace(PLACEHOLDER, (_, name) => {
|
|
2639
2854
|
const d = declared.get(name);
|
|
2640
2855
|
if (!d) unknown.add(name);
|
|
2641
|
-
return (_nullishCoalesce(_optionalChain([d, 'optionalAccess',
|
|
2856
|
+
return (_nullishCoalesce(_optionalChain([d, 'optionalAccess', _124 => _124.value]), () => ( ""))).replace(/"/g, '\\"');
|
|
2642
2857
|
});
|
|
2643
2858
|
for (const name of unknown) notes.push(`Placeholder "{{${name}}}" has no declaration, so it was left empty.`);
|
|
2644
2859
|
return { motion: JSON.parse(filled), controls };
|
|
@@ -2692,7 +2907,7 @@ function commandsFor(anim, nodes, entered, notes) {
|
|
|
2692
2907
|
if (anim.along) {
|
|
2693
2908
|
const path = nodes.get(anim.along);
|
|
2694
2909
|
if (!path || path.node.type !== "path") notes.push(`${where} follows "${anim.along}", which is not a path in the artwork; the follow was skipped.`);
|
|
2695
|
-
else out.push({ type: "follow", targets, path: anim.along, from: _nullishCoalesce(_optionalChain([anim, 'access',
|
|
2910
|
+
else out.push({ type: "follow", targets, path: anim.along, from: _nullishCoalesce(_optionalChain([anim, 'access', _125 => _125.at, 'optionalAccess', _126 => _126[0]]), () => ( 0)), to: _nullishCoalesce(_optionalChain([anim, 'access', _127 => _127.at, 'optionalAccess', _128 => _128[1]]), () => ( 1)), ...anim.orient ? { rotate: true } : {}, ...timing, ...ease(MOTION_EASE_IN_OUT) });
|
|
2696
2911
|
}
|
|
2697
2912
|
for (const [attr, [a, b]] of Object.entries(_nullishCoalesce(anim.attrs, () => ( {})))) {
|
|
2698
2913
|
if (attr === "opacity") {
|
|
@@ -2703,7 +2918,7 @@ function commandsFor(anim, nodes, entered, notes) {
|
|
|
2703
2918
|
out.push({ type: "enter", targets, style: "fade", ...timing, ...ease(MOTION_EASE_OUT) });
|
|
2704
2919
|
} else out.push({ type: "fade", targets, to: Math.min(1, Math.max(0, to)), ...timing, ...ease(MOTION_EASE_IN_OUT) });
|
|
2705
2920
|
} else if (attr === "fill" || attr === "stroke") {
|
|
2706
|
-
if (_optionalChain([behavior, 'optionalAccess',
|
|
2921
|
+
if (_optionalChain([behavior, 'optionalAccess', _129 => _129.type]) === "pulse") out.push({ type: "color-pulse", targets, color: String(b), paint: attr, count: _nullishCoalesce(behavior.count, () => ( 1)), ...timing, ...ease("linear") });
|
|
2707
2922
|
else {
|
|
2708
2923
|
if (behavior) notes.push(`${where}: a ${attr} ${behavior.type} became a plain colour change.`);
|
|
2709
2924
|
out.push({ type: "style", targets, [attr]: String(b), ...timing, ...ease(MOTION_EASE_IN_OUT) });
|
|
@@ -2769,10 +2984,10 @@ function fromFlairSvgMotion(input, options = {}) {
|
|
|
2769
2984
|
notes.push(`Step ${step}: a camera move ${camera ? "after the first" : `${anim.delay} ms in`} was dropped; a shot frames the camera once, at its start.`);
|
|
2770
2985
|
continue;
|
|
2771
2986
|
}
|
|
2772
|
-
const
|
|
2987
|
+
const ms2 = _nullishCoalesce(anim.ms, () => ( 800));
|
|
2773
2988
|
const ease = easingOf(anim.ease, MOTION_EASE_IN_OUT);
|
|
2774
|
-
camera = typeof anim.view === "string" ? { frame: anim.view, padding: 0, ms, ease } : { x: anim.view[0], y: anim.view[1], width: anim.view[2], height: anim.view[3], ms, ease };
|
|
2775
|
-
cameraMs = index === 0 ? 0 :
|
|
2989
|
+
camera = typeof anim.view === "string" ? { frame: anim.view, padding: 0, ms: ms2, ease } : { x: anim.view[0], y: anim.view[1], width: anim.view[2], height: anim.view[3], ms: ms2, ease };
|
|
2990
|
+
cameraMs = index === 0 ? 0 : ms2;
|
|
2776
2991
|
continue;
|
|
2777
2992
|
}
|
|
2778
2993
|
if (!anim.id) continue;
|
|
@@ -2826,7 +3041,7 @@ function rgba(color) {
|
|
|
2826
3041
|
const n = (i) => parseInt(hex.slice(i, i + 2), 16) / 255;
|
|
2827
3042
|
return [n(1), n(3), n(5), hex.length >= 9 ? parseInt(hex.slice(7, 9), 16) / 255 : 1];
|
|
2828
3043
|
}
|
|
2829
|
-
function lottieContours(path,
|
|
3044
|
+
function lottieContours(path, round3) {
|
|
2830
3045
|
const out = [];
|
|
2831
3046
|
let cur = null;
|
|
2832
3047
|
let px = 0;
|
|
@@ -2867,7 +3082,7 @@ function lottieContours(path, round2) {
|
|
|
2867
3082
|
}
|
|
2868
3083
|
}
|
|
2869
3084
|
if (cur) out.push(cur);
|
|
2870
|
-
return out.map((c) => ({ c: c.c, v: c.v.map((p) => p.map(
|
|
3085
|
+
return out.map((c) => ({ c: c.c, v: c.v.map((p) => p.map(round3)), i: c.i.map((p) => p.map(round3)), o: c.o.map((p) => p.map(round3)) }));
|
|
2871
3086
|
}
|
|
2872
3087
|
function staticParts(m) {
|
|
2873
3088
|
const sx = Math.hypot(m[0], m[1]);
|
|
@@ -2879,8 +3094,8 @@ function staticParts(m) {
|
|
|
2879
3094
|
function toLottie(scene, options = {}) {
|
|
2880
3095
|
const fps = _nullishCoalesce(_nullishCoalesce(options.fps, () => ( scene.fps)), () => ( 30));
|
|
2881
3096
|
const precision = _nullishCoalesce(options.precision, () => ( 3));
|
|
2882
|
-
const
|
|
2883
|
-
const frame = (
|
|
3097
|
+
const round3 = (n) => Math.round(n * 10 ** precision) / 10 ** precision;
|
|
3098
|
+
const frame = (ms2) => round3(ms2 * fps / 1e3);
|
|
2884
3099
|
const timeline = compileMotionScene(scene);
|
|
2885
3100
|
const nodes = indexMotionNodes(scene.artwork.nodes);
|
|
2886
3101
|
const notes = [];
|
|
@@ -2897,16 +3112,16 @@ function toLottie(scene, options = {}) {
|
|
|
2897
3112
|
return [e.x1, e.y1, e.x2, e.y2];
|
|
2898
3113
|
};
|
|
2899
3114
|
const animate = (segments, value, exact = true) => {
|
|
2900
|
-
const rest = value(0).map(
|
|
3115
|
+
const rest = value(0).map(round3);
|
|
2901
3116
|
if (!segments.length) return { a: 0, k: rest.length === 1 ? rest[0] : rest };
|
|
2902
3117
|
const keys = [];
|
|
2903
3118
|
const key = (t, v, ease) => {
|
|
2904
|
-
const k = { t: frame(t), s: v.map(
|
|
3119
|
+
const k = { t: frame(t), s: v.map(round3) };
|
|
2905
3120
|
if (ease === "hold") k.h = 1;
|
|
2906
3121
|
else {
|
|
2907
3122
|
const [x1, y1, x2, y2] = ease === "linear" || !ease ? [0, 0, 1, 1] : ease;
|
|
2908
|
-
k.o = { x: [
|
|
2909
|
-
k.i = { x: [
|
|
3123
|
+
k.o = { x: [round3(x1)], y: [round3(y1)] };
|
|
3124
|
+
k.i = { x: [round3(x2)], y: [round3(y2)] };
|
|
2910
3125
|
}
|
|
2911
3126
|
if (keys.length && keys[keys.length - 1].t === k.t) keys[keys.length - 1] = k;
|
|
2912
3127
|
else keys.push(k);
|
|
@@ -2954,7 +3169,7 @@ function toLottie(scene, options = {}) {
|
|
|
2954
3169
|
const poseTransform = (entry) => {
|
|
2955
3170
|
const id = entry.node.id;
|
|
2956
3171
|
const tr = STATIC_TR();
|
|
2957
|
-
const c = [entry.center.x, entry.center.y].map(
|
|
3172
|
+
const c = [entry.center.x, entry.center.y].map(round3);
|
|
2958
3173
|
const own = (p) => track(id, p);
|
|
2959
3174
|
const sources = motionDriverSources(timeline, id, ["x", "y", "rotation", "scaleX", "scaleY"]);
|
|
2960
3175
|
const layered = ["x", "y", "rotation", "scaleX", "scaleY"].some((p) => layers(id, p).length) || sources.length > 0;
|
|
@@ -2965,7 +3180,7 @@ function toLottie(scene, options = {}) {
|
|
|
2965
3180
|
tr.p = animate(mixed ? [...own("x"), ...own("y"), ...own("follow"), ...layers(id, "x"), ...layers(id, "y"), ...sources] : [...own("x"), ...own("y")], (t) => poseAt(entry, t).p, !mixed);
|
|
2966
3181
|
tr.r = animate(mixed ? [...own("rotation"), ...own("follow"), ...layers(id, "rotation"), ...sources] : own("rotation"), (t) => [poseAt(entry, t).r], !mixed);
|
|
2967
3182
|
tr.s = animate(mixed ? [...own("scaleX"), ...own("scaleY"), ...layers(id, "scaleX"), ...layers(id, "scaleY"), ...sources] : [...own("scaleX"), ...own("scaleY")], (t) => poseAt(entry, t).s, !mixed);
|
|
2968
|
-
if (tr.p.a === 0) tr.p = { a: 0, k: poseAt(entry, 0).p.map(
|
|
3183
|
+
if (tr.p.a === 0) tr.p = { a: 0, k: poseAt(entry, 0).p.map(round3) };
|
|
2969
3184
|
}
|
|
2970
3185
|
const opacitySources = motionDriverSources(timeline, id, ["opacity"]);
|
|
2971
3186
|
if (opacitySources.length) {
|
|
@@ -2974,7 +3189,7 @@ function toLottie(scene, options = {}) {
|
|
|
2974
3189
|
}
|
|
2975
3190
|
const opacity = own("opacity");
|
|
2976
3191
|
const rest = _nullishCoalesce(entry.node.opacity, () => ( 1));
|
|
2977
|
-
tr.o = opacity.length ? animate(opacity, (t) => [Math.min(1, Math.max(0, Number(_nullishCoalesce(trackValue(opacity, t), () => ( rest))))) * 100]) : { a: 0, k:
|
|
3192
|
+
tr.o = opacity.length ? animate(opacity, (t) => [Math.min(1, Math.max(0, Number(_nullishCoalesce(trackValue(opacity, t), () => ( rest))))) * 100]) : { a: 0, k: round3(rest * 100) };
|
|
2978
3193
|
return tr;
|
|
2979
3194
|
};
|
|
2980
3195
|
const owner = (path, p) => {
|
|
@@ -2993,10 +3208,10 @@ function toLottie(scene, options = {}) {
|
|
|
2993
3208
|
const shapeSegments = [...timeline.tracks.entries()].filter(([k]) => k.startsWith(`${path.id}\0`) && (k.endsWith("\0shape") || k.includes("\0effect:"))).flatMap(([, v]) => v);
|
|
2994
3209
|
const geometryAt = (t) => {
|
|
2995
3210
|
if (!shapeSegments.length) return _chunkHRI3ZMDMcjs.vectorPathWorldBezier.call(void 0, path);
|
|
2996
|
-
const now = _optionalChain([indexMotionNodes, 'call',
|
|
3211
|
+
const now = _optionalChain([indexMotionNodes, 'call', _130 => _130(motionFrameAt(scene, t, { draw: false, offsets: false }).document.nodes), 'access', _131 => _131.get, 'call', _132 => _132(path.id), 'optionalAccess', _133 => _133.node]);
|
|
2997
3212
|
return now && now.type === "path" ? _chunkHRI3ZMDMcjs.vectorPathWorldBezier.call(void 0, _chunkHRI3ZMDMcjs.expandVectorAppearances.call(void 0, [now])[0]) : _chunkHRI3ZMDMcjs.vectorPathWorldBezier.call(void 0, path);
|
|
2998
3213
|
};
|
|
2999
|
-
const rest = lottieContours(_chunkGF2RBSDMcjs.toBezierPath.call(void 0, geometryAt(0)),
|
|
3214
|
+
const rest = lottieContours(_chunkGF2RBSDMcjs.toBezierPath.call(void 0, geometryAt(0)), round3);
|
|
3000
3215
|
rest.forEach((contour, n) => {
|
|
3001
3216
|
let ks = { a: 0, k: contour };
|
|
3002
3217
|
if (shapeSegments.length) {
|
|
@@ -3005,7 +3220,7 @@ function toLottie(scene, options = {}) {
|
|
|
3005
3220
|
const count = Math.max(1, Math.ceil(end * fps / 1e3));
|
|
3006
3221
|
for (let k = 0; k <= count && fits; k++) {
|
|
3007
3222
|
const t = end * k / count;
|
|
3008
|
-
const c = lottieContours(_chunkGF2RBSDMcjs.toBezierPath.call(void 0, geometryAt(t)),
|
|
3223
|
+
const c = lottieContours(_chunkGF2RBSDMcjs.toBezierPath.call(void 0, geometryAt(t)), round3)[n];
|
|
3009
3224
|
if (!c || c.v.length !== contour.v.length || c.c !== contour.c) fits = false;
|
|
3010
3225
|
else keys.push({ t: frame(t), s: [c], ...k < count ? { o: { x: [0], y: [0] }, i: { x: [1], y: [1] } } : {} });
|
|
3011
3226
|
}
|
|
@@ -3034,14 +3249,14 @@ function toLottie(scene, options = {}) {
|
|
|
3034
3249
|
items.push({
|
|
3035
3250
|
ty: "st",
|
|
3036
3251
|
nm: "Stroke",
|
|
3037
|
-
c: colour2.length ? animate(colour2, (t) => _nullishCoalesce(_nullishCoalesce(rgba(trackValue(colour2, t)), () => ( stroke)), () => ( [0, 0, 0, 1]))) : { a: 0, k: (_nullishCoalesce(stroke, () => ( [0, 0, 0, 1]))).map(
|
|
3252
|
+
c: colour2.length ? animate(colour2, (t) => _nullishCoalesce(_nullishCoalesce(rgba(trackValue(colour2, t)), () => ( stroke)), () => ( [0, 0, 0, 1]))) : { a: 0, k: (_nullishCoalesce(stroke, () => ( [0, 0, 0, 1]))).map(round3) },
|
|
3038
3253
|
o: { a: 0, k: 100 },
|
|
3039
|
-
w: widthFrom ? animate(track(widthFrom, "strokeWidth"), (t) => [Math.max(0, Number(_nullishCoalesce(_nullishCoalesce(trackValue(track(widthFrom, "strokeWidth"), t), () => ( path.strokeWidth)), () => ( 1))))]) : { a: 0, k:
|
|
3254
|
+
w: widthFrom ? animate(track(widthFrom, "strokeWidth"), (t) => [Math.max(0, Number(_nullishCoalesce(_nullishCoalesce(trackValue(track(widthFrom, "strokeWidth"), t), () => ( path.strokeWidth)), () => ( 1))))]) : { a: 0, k: round3(_nullishCoalesce(path.strokeWidth, () => ( 1))) },
|
|
3040
3255
|
lc: path.strokeLinecap === "round" ? 2 : path.strokeLinecap === "square" ? 3 : 1,
|
|
3041
3256
|
lj: path.strokeLinejoin === "round" ? 2 : path.strokeLinejoin === "bevel" ? 3 : 1,
|
|
3042
3257
|
ml: 4
|
|
3043
3258
|
});
|
|
3044
|
-
if (_optionalChain([path, 'access',
|
|
3259
|
+
if (_optionalChain([path, 'access', _134 => _134.strokeDasharray, 'optionalAccess', _135 => _135.length])) notes.push(`"${path.id}": its dashes are not exported; the stroke is solid.`);
|
|
3045
3260
|
if (path.strokeAlign && path.strokeAlign !== "center") notes.push(`"${path.id}": its stroke is drawn centred (Lottie has no inside or outside strokes).`);
|
|
3046
3261
|
}
|
|
3047
3262
|
const fill = paintOf(path.fill, "fill", path.id);
|
|
@@ -3051,7 +3266,7 @@ function toLottie(scene, options = {}) {
|
|
|
3051
3266
|
items.push({
|
|
3052
3267
|
ty: "fl",
|
|
3053
3268
|
nm: "Fill",
|
|
3054
|
-
c: colour2.length ? animate(colour2, (t) => _nullishCoalesce(_nullishCoalesce(rgba(trackValue(colour2, t)), () => ( fill)), () => ( [0, 0, 0, 1]))) : { a: 0, k: (_nullishCoalesce(fill, () => ( [0, 0, 0, 1]))).map(
|
|
3269
|
+
c: colour2.length ? animate(colour2, (t) => _nullishCoalesce(_nullishCoalesce(rgba(trackValue(colour2, t)), () => ( fill)), () => ( [0, 0, 0, 1]))) : { a: 0, k: (_nullishCoalesce(fill, () => ( [0, 0, 0, 1]))).map(round3) },
|
|
3055
3270
|
o: fade.length ? animate(fade, (t) => [Math.min(1, Math.max(0, Number(_nullishCoalesce(trackValue(fade, t), () => ( 1))))) * 100]) : { a: 0, k: 100 },
|
|
3056
3271
|
r: path.fillRule === "evenodd" ? 2 : 1
|
|
3057
3272
|
});
|
|
@@ -3077,9 +3292,9 @@ function toLottie(scene, options = {}) {
|
|
|
3077
3292
|
const parts = staticParts(m);
|
|
3078
3293
|
if (!parts) notes.push(`"${node.id}": its transform skews, which is not exported; it is drawn without the skew.`);
|
|
3079
3294
|
else {
|
|
3080
|
-
innerTr.p = { a: 0, k: parts.p.map(
|
|
3081
|
-
innerTr.s = { a: 0, k: parts.s.map(
|
|
3082
|
-
innerTr.r = { a: 0, k:
|
|
3295
|
+
innerTr.p = { a: 0, k: parts.p.map(round3) };
|
|
3296
|
+
innerTr.s = { a: 0, k: parts.s.map(round3) };
|
|
3297
|
+
innerTr.r = { a: 0, k: round3(parts.r) };
|
|
3083
3298
|
}
|
|
3084
3299
|
}
|
|
3085
3300
|
}
|
|
@@ -3089,7 +3304,7 @@ function toLottie(scene, options = {}) {
|
|
|
3089
3304
|
const artwork = { ...scene.artwork, nodes: _chunkHRI3ZMDMcjs.expandVectorAppearances.call(void 0, scene.artwork.nodes) };
|
|
3090
3305
|
const content = shown(artwork.nodes).reverse().map(nodeGroup);
|
|
3091
3306
|
if (artwork.background && rgba(artwork.background))
|
|
3092
|
-
content.push({ ty: "gr", nm: "Background", it: [{ ty: "rc", nm: "Background", p: { a: 0, k: [artwork.width / 2, artwork.height / 2] }, s: { a: 0, k: [artwork.width, artwork.height] }, r: { a: 0, k: 0 } }, { ty: "fl", c: { a: 0, k: rgba(artwork.background).map(
|
|
3307
|
+
content.push({ ty: "gr", nm: "Background", it: [{ ty: "rc", nm: "Background", p: { a: 0, k: [artwork.width / 2, artwork.height / 2] }, s: { a: 0, k: [artwork.width, artwork.height] }, r: { a: 0, k: 0 } }, { ty: "fl", c: { a: 0, k: rgba(artwork.background).map(round3) }, o: { a: 0, k: 100 }, r: 1 }, STATIC_TR()] });
|
|
3093
3308
|
const camera = track("", "camera");
|
|
3094
3309
|
const root = STATIC_TR();
|
|
3095
3310
|
if (camera.length) {
|
|
@@ -3135,7 +3350,7 @@ var kids = (el, name) => el ? Array.from(el.children).filter((c) => c.localName
|
|
|
3135
3350
|
var kid = (el, name) => _nullishCoalesce(kids(el, name)[0], () => ( null));
|
|
3136
3351
|
var deep = (el, name) => el ? Array.from(el.getElementsByTagNameNS("*", name)) : [];
|
|
3137
3352
|
var num = (el, attr, fallback = 0) => {
|
|
3138
|
-
const v = _optionalChain([el, 'optionalAccess',
|
|
3353
|
+
const v = _optionalChain([el, 'optionalAccess', _136 => _136.getAttribute, 'call', _137 => _137(attr)]);
|
|
3139
3354
|
const n = v === null || v === void 0 ? NaN : Number(v);
|
|
3140
3355
|
return Number.isFinite(n) ? n : fallback;
|
|
3141
3356
|
};
|
|
@@ -3243,8 +3458,8 @@ function placement(xfrm, map) {
|
|
|
3243
3458
|
const ext = kid(xfrm, "ext");
|
|
3244
3459
|
const [x, y, w, h] = map(num(off, "x"), num(off, "y"), num(ext, "cx"), num(ext, "cy"));
|
|
3245
3460
|
const rot = num(xfrm, "rot") / 6e4 * (Math.PI / 180);
|
|
3246
|
-
const fx = _optionalChain([xfrm, 'optionalAccess',
|
|
3247
|
-
const fy = _optionalChain([xfrm, 'optionalAccess',
|
|
3461
|
+
const fx = _optionalChain([xfrm, 'optionalAccess', _138 => _138.getAttribute, 'call', _139 => _139("flipH")]) === "1" ? -1 : 1;
|
|
3462
|
+
const fy = _optionalChain([xfrm, 'optionalAccess', _140 => _140.getAttribute, 'call', _141 => _141("flipV")]) === "1" ? -1 : 1;
|
|
3248
3463
|
const cx = x + w / 2;
|
|
3249
3464
|
const cy = y + h / 2;
|
|
3250
3465
|
const cos = Math.cos(rot);
|
|
@@ -3271,7 +3486,7 @@ function shapes(ctx, tree, map) {
|
|
|
3271
3486
|
const nv = el.firstElementChild;
|
|
3272
3487
|
const cNvPr = kid(nv, "cNvPr");
|
|
3273
3488
|
const spid = num(cNvPr, "id", -1);
|
|
3274
|
-
const name = _nullishCoalesce(_optionalChain([cNvPr, 'optionalAccess',
|
|
3489
|
+
const name = _nullishCoalesce(_optionalChain([cNvPr, 'optionalAccess', _142 => _142.getAttribute, 'call', _143 => _143("name")]), () => ( ""));
|
|
3275
3490
|
if (el.localName === "grpSp") {
|
|
3276
3491
|
const xfrm = kid(kid(el, "grpSpPr"), "xfrm");
|
|
3277
3492
|
const off = kid(xfrm, "off");
|
|
@@ -3292,7 +3507,7 @@ function shapes(ctx, tree, map) {
|
|
|
3292
3507
|
if (el.localName !== "sp" && el.localName !== "cxnSp") continue;
|
|
3293
3508
|
const spPr = kid(el, "spPr");
|
|
3294
3509
|
const { w, h, transform } = placement(kid(spPr, "xfrm"), map);
|
|
3295
|
-
const prst = _optionalChain([kid, 'call',
|
|
3510
|
+
const prst = _optionalChain([kid, 'call', _144 => _144(spPr, "prstGeom"), 'optionalAccess', _145 => _145.getAttribute, 'call', _146 => _146("prst")]);
|
|
3296
3511
|
const cust = kid(spPr, "custGeom");
|
|
3297
3512
|
let subpaths = cust ? custom(cust, w, h) : prst ? preset(prst, w, h) : null;
|
|
3298
3513
|
if (!subpaths) {
|
|
@@ -3406,13 +3621,13 @@ function command(ctx, ctn, scene, artwork, target, hidden) {
|
|
|
3406
3621
|
}
|
|
3407
3622
|
if (cls === "path") {
|
|
3408
3623
|
const motion = deep(ctn, "animMotion")[0];
|
|
3409
|
-
const d = _optionalChain([motion, 'optionalAccess',
|
|
3624
|
+
const d = _optionalChain([motion, 'optionalAccess', _147 => _147.getAttribute, 'call', _148 => _148("path")]);
|
|
3410
3625
|
if (!d) return null;
|
|
3411
3626
|
const entry = artwork.flatMap(function all(n) {
|
|
3412
3627
|
return n.type === "group" ? [n, ...n.children.flatMap(all)] : [n];
|
|
3413
3628
|
}).find((n) => n.id === target);
|
|
3414
|
-
const t = _nullishCoalesce(_optionalChain([entry, 'optionalAccess',
|
|
3415
|
-
const p = _optionalChain([entry, 'optionalAccess',
|
|
3629
|
+
const t = _nullishCoalesce(_optionalChain([entry, 'optionalAccess', _149 => _149.transform]), () => ( [1, 0, 0, 1, 0, 0]));
|
|
3630
|
+
const p = _optionalChain([entry, 'optionalAccess', _150 => _150.type]) === "path" ? _optionalChain([entry, 'access', _151 => _151.subpaths, 'access', _152 => _152[0], 'optionalAccess', _153 => _153.anchors]) : void 0;
|
|
3416
3631
|
const xs = (_nullishCoalesce(p, () => ( []))).map((a) => a.x);
|
|
3417
3632
|
const ys = (_nullishCoalesce(p, () => ( []))).map((a) => a.y);
|
|
3418
3633
|
const lx = xs.length ? (Math.min(...xs) + Math.max(...xs)) / 2 : 0;
|
|
@@ -3464,9 +3679,9 @@ async function fromPptxAnimation(data, options = {}) {
|
|
|
3464
3679
|
if (!path.includes("theme/")) continue;
|
|
3465
3680
|
const theme = await xml(ctx, path);
|
|
3466
3681
|
const scheme = deep(theme, "clrScheme")[0];
|
|
3467
|
-
for (const c of Array.from(_nullishCoalesce(_optionalChain([scheme, 'optionalAccess',
|
|
3682
|
+
for (const c of Array.from(_nullishCoalesce(_optionalChain([scheme, 'optionalAccess', _154 => _154.children]), () => ( [])))) {
|
|
3468
3683
|
const v = c.firstElementChild;
|
|
3469
|
-
const hex = _optionalChain([v, 'optionalAccess',
|
|
3684
|
+
const hex = _optionalChain([v, 'optionalAccess', _155 => _155.localName]) === "srgbClr" ? v.getAttribute("val") : _optionalChain([v, 'optionalAccess', _156 => _156.getAttribute, 'call', _157 => _157("lastClr")]);
|
|
3470
3685
|
if (hex) ctx.theme.set(c.localName, `#${hex.toLowerCase()}`);
|
|
3471
3686
|
}
|
|
3472
3687
|
break;
|
|
@@ -3584,7 +3799,7 @@ function withoutMotionEffect(scene, instance) {
|
|
|
3584
3799
|
const shots = [];
|
|
3585
3800
|
for (const shot of scene.shots) {
|
|
3586
3801
|
const run = shot.run.filter((c, i) => {
|
|
3587
|
-
const mine = _optionalChain([sourceOf, 'call',
|
|
3802
|
+
const mine = _optionalChain([sourceOf, 'call', _158 => _158(c), 'optionalAccess', _159 => _159.instance]) === instance;
|
|
3588
3803
|
if (mine && !place) place = { shot: shot.id, index: i };
|
|
3589
3804
|
return !mine;
|
|
3590
3805
|
});
|
|
@@ -3597,6 +3812,8 @@ function withoutMotionEffect(scene, instance) {
|
|
|
3597
3812
|
}
|
|
3598
3813
|
|
|
3599
3814
|
// src/animation-editor/controller.ts
|
|
3815
|
+
var MOTION_RECORD_GESTURE_MODES = ["move", "scale", "rotate"];
|
|
3816
|
+
var MOTION_MAX_TAKE_MS = 10 * 6e4;
|
|
3600
3817
|
var BLANK = { width: 800, height: 450, background: "#ffffff", nodes: [] };
|
|
3601
3818
|
var AnimationEditorController = (_class = class {
|
|
3602
3819
|
|
|
@@ -3614,14 +3831,15 @@ var AnimationEditorController = (_class = class {
|
|
|
3614
3831
|
__init6() {this.listeners = /* @__PURE__ */ new Set()}
|
|
3615
3832
|
__init7() {this.unsubscribers = []}
|
|
3616
3833
|
|
|
3617
|
-
|
|
3834
|
+
__init8() {this.recording = null}
|
|
3835
|
+
constructor(options = {}) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);_class.prototype.__init4.call(this);_class.prototype.__init5.call(this);_class.prototype.__init6.call(this);_class.prototype.__init7.call(this);_class.prototype.__init8.call(this);_class.prototype.__init9.call(this);_class.prototype.__init10.call(this);
|
|
3618
3836
|
this.history = _nullishCoalesce(options.history, () => ( new (0, _chunkFJ2MSI43cjs.HistoryManager)()));
|
|
3619
3837
|
this.scene = options.scene ? parseMotionScene(options.scene) : createMotionScene(BLANK);
|
|
3620
3838
|
this.issues = checkMotionScene(this.scene);
|
|
3621
3839
|
this.readOnly = _nullishCoalesce(options.readOnly, () => ( false));
|
|
3622
3840
|
this.onChange = options.onChange;
|
|
3623
3841
|
if (options.effects) this.effects.set(options.effects);
|
|
3624
|
-
this.player = new (0,
|
|
3842
|
+
this.player = new (0, _chunkUUTTJK77cjs.AnimationPlayer)({ clock: options.clock, duration: () => compileMotionScene(this.scene).duration, loop: _nullishCoalesce(this.scene.loop, () => ( "none")) });
|
|
3625
3843
|
this.state = this.computeState();
|
|
3626
3844
|
this.connect();
|
|
3627
3845
|
}
|
|
@@ -3640,11 +3858,12 @@ var AnimationEditorController = (_class = class {
|
|
|
3640
3858
|
* controller survives React StrictMode's unmount / remount.
|
|
3641
3859
|
*/
|
|
3642
3860
|
destroy() {
|
|
3861
|
+
this.cancelRecording();
|
|
3643
3862
|
this.player.pause();
|
|
3644
3863
|
this.unsubscribers.splice(0).forEach((u) => u());
|
|
3645
3864
|
}
|
|
3646
|
-
|
|
3647
|
-
|
|
3865
|
+
__init9() {this.getState = () => this.state}
|
|
3866
|
+
__init10() {this.subscribe = (listener) => {
|
|
3648
3867
|
this.connect();
|
|
3649
3868
|
this.listeners.add(listener);
|
|
3650
3869
|
return () => {
|
|
@@ -3667,7 +3886,7 @@ var AnimationEditorController = (_class = class {
|
|
|
3667
3886
|
time: p.time,
|
|
3668
3887
|
playing: p.playing,
|
|
3669
3888
|
loop: p.loop,
|
|
3670
|
-
currentShot: _nullishCoalesce(_optionalChain([motionShotAt, 'call',
|
|
3889
|
+
currentShot: _nullishCoalesce(_optionalChain([motionShotAt, 'call', _160 => _160(timeline, p.time), 'optionalAccess', _161 => _161.id]), () => ( null)),
|
|
3671
3890
|
selection: this.selection,
|
|
3672
3891
|
readOnly: this.readOnly,
|
|
3673
3892
|
error: this.error,
|
|
@@ -3676,7 +3895,15 @@ var AnimationEditorController = (_class = class {
|
|
|
3676
3895
|
undoLabel: h.undoName,
|
|
3677
3896
|
redoLabel: h.redoName,
|
|
3678
3897
|
revision: this.revision,
|
|
3679
|
-
effectsProvider: this.effects.provider
|
|
3898
|
+
effectsProvider: this.effects.provider,
|
|
3899
|
+
recording: this.recording ? {
|
|
3900
|
+
shot: this.recording.options.id,
|
|
3901
|
+
at: this.recording.options.at,
|
|
3902
|
+
start: this.recording.start,
|
|
3903
|
+
elapsed: Math.max(0, p.time - this.recording.start),
|
|
3904
|
+
samples: this.recording.samples.length,
|
|
3905
|
+
gesture: this.recording.gesture ? { node: this.recording.gesture.node, mode: this.recording.gesture.mode } : null
|
|
3906
|
+
} : null
|
|
3680
3907
|
};
|
|
3681
3908
|
}
|
|
3682
3909
|
notify() {
|
|
@@ -3707,7 +3934,7 @@ var AnimationEditorController = (_class = class {
|
|
|
3707
3934
|
this.applying = false;
|
|
3708
3935
|
}
|
|
3709
3936
|
this.notify();
|
|
3710
|
-
_optionalChain([this, 'access',
|
|
3937
|
+
_optionalChain([this, 'access', _162 => _162.onChange, 'optionalCall', _163 => _163(scene)]);
|
|
3711
3938
|
}
|
|
3712
3939
|
commit(label, next, merge) {
|
|
3713
3940
|
const before = this.scene;
|
|
@@ -3740,7 +3967,7 @@ var AnimationEditorController = (_class = class {
|
|
|
3740
3967
|
* `dryRun` it reports what would happen and changes nothing.
|
|
3741
3968
|
*/
|
|
3742
3969
|
apply(edits, label = "Edit animation", options = {}) {
|
|
3743
|
-
const reason = this.readOnlyReason();
|
|
3970
|
+
const reason = _nullishCoalesce(this.readOnlyReason(), () => ( (this.recording ? "A take is being recorded; stop or cancel the recording first." : null)));
|
|
3744
3971
|
if (reason) return this.refuse(reason);
|
|
3745
3972
|
const result = applyMotionEdits(this.scene, edits);
|
|
3746
3973
|
if (!result.ok) return options.dryRun ? result : this.refuse(result.error);
|
|
@@ -3869,7 +4096,7 @@ var AnimationEditorController = (_class = class {
|
|
|
3869
4096
|
const { group, shots: shots2, notes: notes2 } = motionPartsFromVectorEffect(bare.artwork, motion2, source);
|
|
3870
4097
|
const index = this.scene.artwork.nodes.findIndex((n) => n.id === instance);
|
|
3871
4098
|
const nodes = [...bare.artwork.nodes];
|
|
3872
|
-
nodes.splice(Math.max(0, index), 0, _optionalChain([old, 'optionalAccess',
|
|
4099
|
+
nodes.splice(Math.max(0, index), 0, _optionalChain([old, 'optionalAccess', _164 => _164.transform]) ? { ...group, transform: old.transform } : group);
|
|
3873
4100
|
const at = this.scene.shots.findIndex((s) => was.shots.includes(s.id));
|
|
3874
4101
|
const kept = bare.shots;
|
|
3875
4102
|
const nextShots = [...kept.slice(0, Math.max(0, at)), ...shots2, ...kept.slice(Math.max(0, at))];
|
|
@@ -3880,7 +4107,7 @@ var AnimationEditorController = (_class = class {
|
|
|
3880
4107
|
let shots = bare.shots;
|
|
3881
4108
|
if (place && shots.some((s) => s.id === place.shot)) shots = shots.map((s) => s.id === place.shot ? { ...s, run: [...s.run.slice(0, place.index), ...commands, ...s.run.slice(place.index)] } : s);
|
|
3882
4109
|
else {
|
|
3883
|
-
const at = this.scene.shots.findIndex((s) => s.id === _optionalChain([place, 'optionalAccess',
|
|
4110
|
+
const at = this.scene.shots.findIndex((s) => s.id === _optionalChain([place, 'optionalAccess', _165 => _165.shot]));
|
|
3884
4111
|
shots = [...shots.slice(0, Math.max(0, at)), { id: instance, label: d.name, run: commands }, ...shots.slice(Math.max(0, at))];
|
|
3885
4112
|
}
|
|
3886
4113
|
return { scene: { ...bare, shots }, label: `Change ${d.name}`, instance, notes };
|
|
@@ -3895,6 +4122,7 @@ var AnimationEditorController = (_class = class {
|
|
|
3895
4122
|
}
|
|
3896
4123
|
/** Opens a scene (JSON or object). `clearHistory` drops the undo stack (opening a file); otherwise it is one undo step. */
|
|
3897
4124
|
load(scene, options = {}) {
|
|
4125
|
+
if (options.clearHistory) this.cancelRecording();
|
|
3898
4126
|
let parsed;
|
|
3899
4127
|
try {
|
|
3900
4128
|
parsed = parseMotionScene(scene);
|
|
@@ -3914,6 +4142,7 @@ var AnimationEditorController = (_class = class {
|
|
|
3914
4142
|
setReadOnly(readOnly) {
|
|
3915
4143
|
if (this.readOnly === readOnly) return;
|
|
3916
4144
|
this.readOnly = readOnly;
|
|
4145
|
+
if (readOnly) this.cancelRecording();
|
|
3917
4146
|
this.notify();
|
|
3918
4147
|
}
|
|
3919
4148
|
/** Selects a shot (and optionally a command in it); `null` clears. Unknown shots clear. */
|
|
@@ -3933,30 +4162,186 @@ var AnimationEditorController = (_class = class {
|
|
|
3933
4162
|
toggle() {
|
|
3934
4163
|
this.player.toggle();
|
|
3935
4164
|
}
|
|
3936
|
-
/** Moves the playhead (ms). */
|
|
4165
|
+
/** Moves the playhead (ms). Ignored while recording: a take runs on from where it started. */
|
|
3937
4166
|
seek(time) {
|
|
4167
|
+
if (this.recording) return;
|
|
3938
4168
|
this.player.seek(time);
|
|
3939
4169
|
}
|
|
3940
4170
|
/** Moves the playhead to the start or end of a shot. Returns false for an unknown shot. */
|
|
3941
4171
|
seekShot(id, at = "start") {
|
|
3942
4172
|
const shot = compileMotionScene(this.scene).shots.find((s) => s.id === id);
|
|
3943
|
-
if (!shot) return false;
|
|
4173
|
+
if (!shot || this.recording) return false;
|
|
3944
4174
|
this.player.seek(at === "start" ? shot.start : shot.end);
|
|
3945
4175
|
return true;
|
|
3946
4176
|
}
|
|
3947
4177
|
undo() {
|
|
3948
|
-
return this.history.undo();
|
|
4178
|
+
return this.recording ? false : this.history.undo();
|
|
3949
4179
|
}
|
|
3950
4180
|
redo() {
|
|
3951
|
-
return this.history.redo();
|
|
4181
|
+
return this.recording ? false : this.history.redo();
|
|
4182
|
+
}
|
|
4183
|
+
/** The scene as shown: while recording, with the take so far as its shot. */
|
|
4184
|
+
viewScene() {
|
|
4185
|
+
const r = this.recording;
|
|
4186
|
+
if (!r) return this.scene;
|
|
4187
|
+
if (_optionalChain([r, 'access', _166 => _166.preview, 'optionalAccess', _167 => _167.count]) === r.samples.length) return r.preview.scene;
|
|
4188
|
+
let scene = this.scene;
|
|
4189
|
+
if (r.samples.length) {
|
|
4190
|
+
try {
|
|
4191
|
+
const { shot, at } = motionTakeShot(this.scene, { samples: r.samples, length: Math.max(0, this.player.currentTime - r.start) }, r.options);
|
|
4192
|
+
scene = { ...this.scene, shots: [...this.scene.shots.slice(0, at), shot, ...this.scene.shots.slice(at)] };
|
|
4193
|
+
} catch (e6) {
|
|
4194
|
+
}
|
|
4195
|
+
}
|
|
4196
|
+
r.preview = { count: r.samples.length, scene };
|
|
4197
|
+
return scene;
|
|
3952
4198
|
}
|
|
3953
4199
|
/** The scene at `time` (default: the playhead). */
|
|
3954
4200
|
frame(time = this.player.currentTime, options = {}) {
|
|
3955
|
-
return motionFrameAt(this.
|
|
4201
|
+
return motionFrameAt(this.viewScene(), time, options);
|
|
3956
4202
|
}
|
|
3957
4203
|
/** The scene at `time` (default: the playhead) as SVG. */
|
|
3958
4204
|
toSvg(time = this.player.currentTime, options = {}) {
|
|
3959
|
-
return motionStageSvg(this.
|
|
4205
|
+
return motionStageSvg(this.viewScene(), time, options);
|
|
4206
|
+
}
|
|
4207
|
+
// ---------------------------------------------------------------------------------------
|
|
4208
|
+
// Recording
|
|
4209
|
+
// ---------------------------------------------------------------------------------------
|
|
4210
|
+
/**
|
|
4211
|
+
* Starts recording a take: the playhead goes to where the take's shot will start (default:
|
|
4212
|
+
* the end of the scene) and plays on, and every `recordSample` (or stage gesture) is a value
|
|
4213
|
+
* at that moment. Frames show the take so far. `stopRecording` turns it into one shot, one
|
|
4214
|
+
* undo step. Edits, seeks and undo wait until the recording stops; `pause` pauses the take.
|
|
4215
|
+
*/
|
|
4216
|
+
startRecording(options = {}) {
|
|
4217
|
+
const reason = _nullishCoalesce(this.readOnlyReason(), () => ( (this.recording ? "A take is already being recorded." : null)));
|
|
4218
|
+
if (reason) return this.refuse(reason);
|
|
4219
|
+
const at = _nullishCoalesce(options.at, () => ( this.scene.shots.length));
|
|
4220
|
+
if (!Number.isInteger(at) || at < 0 || at > this.scene.shots.length) return this.refuse(`"at" must be a whole number from 0 to ${this.scene.shots.length} (got ${at}).`);
|
|
4221
|
+
const id = _nullishCoalesce(options.id, () => ( freshShotId(this.scene, "take")));
|
|
4222
|
+
if (this.scene.shots.some((s) => s.id === id)) return this.refuse(`Shot id "${id}" is already used; choose another (e.g. "${freshShotId(this.scene, id)}").`);
|
|
4223
|
+
const timeline = compileMotionScene(this.scene);
|
|
4224
|
+
const start = at < timeline.shots.length ? timeline.shots[at].start : timeline.duration;
|
|
4225
|
+
this.error = null;
|
|
4226
|
+
this.recording = { options: { ...options, id, at }, start, samples: [], gesture: null, preview: null, restore: { loop: this.player.getState().loop } };
|
|
4227
|
+
this.player.pause();
|
|
4228
|
+
this.player.setPlaybackRate(1);
|
|
4229
|
+
this.player.setLoop("none");
|
|
4230
|
+
this.player.setDuration(start + MOTION_MAX_TAKE_MS);
|
|
4231
|
+
this.player.seek(start);
|
|
4232
|
+
this.player.play();
|
|
4233
|
+
return { ok: true, recording: this.state.recording };
|
|
4234
|
+
}
|
|
4235
|
+
/**
|
|
4236
|
+
* Records `values` for `node` at `t` (ms into the take; default now). Returns why a sample was
|
|
4237
|
+
* refused (an unknown node, a value out of range), or null.
|
|
4238
|
+
*/
|
|
4239
|
+
recordSample(node, values, t) {
|
|
4240
|
+
const r = this.recording;
|
|
4241
|
+
if (!r) return "Nothing is being recorded; startRecording first.";
|
|
4242
|
+
const sample = { ...values, node, t: Math.max(0, _nullishCoalesce(t, () => ( this.player.liveTime - r.start))) };
|
|
4243
|
+
const problems = motionTakeProblems(this.scene, { samples: [sample] });
|
|
4244
|
+
if (problems.length) return problems.join(" ").replace("Sample 1", "The sample");
|
|
4245
|
+
r.samples.push(sample);
|
|
4246
|
+
this.notify();
|
|
4247
|
+
return null;
|
|
4248
|
+
}
|
|
4249
|
+
/** Where `node` is and how it looks now (while recording: in the take so far), or at `time`. */
|
|
4250
|
+
pose(node, time = this.player.currentTime) {
|
|
4251
|
+
return motionNodePose(this.viewScene(), node, time);
|
|
4252
|
+
}
|
|
4253
|
+
/** A point on a stage element (client pixels, e.g. a pointer event) in document units, through the camera now. */
|
|
4254
|
+
clientToDocument(box, clientX, clientY) {
|
|
4255
|
+
return motionClientToDocument(this.frame().camera, box, clientX, clientY);
|
|
4256
|
+
}
|
|
4257
|
+
/**
|
|
4258
|
+
* Starts a stage drag while recording: from `point` (document units), `move` drags the node,
|
|
4259
|
+
* `scale` scales it about its centre by how much nearer or farther the pointer is, and
|
|
4260
|
+
* `rotate` turns it with the pointer. Returns false when not recording or `node` is unknown.
|
|
4261
|
+
*/
|
|
4262
|
+
beginRecordGesture(node, mode, point) {
|
|
4263
|
+
const r = this.recording;
|
|
4264
|
+
if (!r || !indexMotionNodes(this.scene.artwork.nodes).has(node)) return false;
|
|
4265
|
+
const pose = this.pose(node);
|
|
4266
|
+
r.gesture = { node, mode, from: point, pose };
|
|
4267
|
+
this.recordGesture(pose, point);
|
|
4268
|
+
return true;
|
|
4269
|
+
}
|
|
4270
|
+
/** Records the drag's pose with the pointer at `point` (document units). */
|
|
4271
|
+
updateRecordGesture(point) {
|
|
4272
|
+
const g = _optionalChain([this, 'access', _168 => _168.recording, 'optionalAccess', _169 => _169.gesture]);
|
|
4273
|
+
if (g) this.recordGesture(g.pose, point);
|
|
4274
|
+
}
|
|
4275
|
+
/** Ends the stage drag (the take keeps recording). */
|
|
4276
|
+
endRecordGesture() {
|
|
4277
|
+
const r = this.recording;
|
|
4278
|
+
if (!_optionalChain([r, 'optionalAccess', _170 => _170.gesture])) return;
|
|
4279
|
+
r.gesture = null;
|
|
4280
|
+
this.notify();
|
|
4281
|
+
}
|
|
4282
|
+
recordGesture(pose, point) {
|
|
4283
|
+
const g = this.recording.gesture;
|
|
4284
|
+
const c = { x: pose.x, y: pose.y };
|
|
4285
|
+
if (g.mode === "move") {
|
|
4286
|
+
this.recordSample(g.node, { x: pose.x + point.x - g.from.x, y: pose.y + point.y - g.from.y });
|
|
4287
|
+
} else if (g.mode === "scale") {
|
|
4288
|
+
const f = Math.hypot(point.x - c.x, point.y - c.y) / Math.max(1e-6, Math.hypot(g.from.x - c.x, g.from.y - c.y));
|
|
4289
|
+
this.recordSample(g.node, { scaleX: Math.max(0.01, pose.scaleX * f), scaleY: Math.max(0.01, pose.scaleY * f) });
|
|
4290
|
+
} else {
|
|
4291
|
+
const turn = (Math.atan2(point.y - c.y, point.x - c.x) - Math.atan2(g.from.y - c.y, g.from.x - c.x)) * (180 / Math.PI);
|
|
4292
|
+
const was = _nullishCoalesce(_optionalChain([this, 'access', _171 => _171.recording, 'access', _172 => _172.samples, 'access', _173 => _173.filter, 'call', _174 => _174((s) => s.node === g.node && s.rotation !== void 0), 'access', _175 => _175.at, 'call', _176 => _176(-1), 'optionalAccess', _177 => _177.rotation]), () => ( pose.rotation));
|
|
4293
|
+
let next = pose.rotation + turn;
|
|
4294
|
+
next += 360 * Math.round((was - next) / 360);
|
|
4295
|
+
this.recordSample(g.node, { rotation: next });
|
|
4296
|
+
}
|
|
4297
|
+
}
|
|
4298
|
+
endRecording() {
|
|
4299
|
+
const r = this.recording;
|
|
4300
|
+
if (!r) return null;
|
|
4301
|
+
this.player.pause();
|
|
4302
|
+
this.recording = null;
|
|
4303
|
+
this.player.setLoop(r.restore.loop);
|
|
4304
|
+
this.player.setDuration(() => compileMotionScene(this.scene).duration);
|
|
4305
|
+
return r;
|
|
4306
|
+
}
|
|
4307
|
+
/**
|
|
4308
|
+
* Stops recording and adds the take as a shot (one undo step), selected, with the playhead at
|
|
4309
|
+
* its start. Returns the shot, or why nothing was added (nothing recorded).
|
|
4310
|
+
*/
|
|
4311
|
+
stopRecording() {
|
|
4312
|
+
const length = this.recording ? this.player.liveTime - this.recording.start : 0;
|
|
4313
|
+
const r = this.endRecording();
|
|
4314
|
+
if (!r) return this.refuse("Nothing is being recorded.");
|
|
4315
|
+
if (!r.samples.length) {
|
|
4316
|
+
this.player.seek(r.start);
|
|
4317
|
+
return this.refuse("Nothing was recorded, so no shot was added: drag an element on the stage (or change its paint) while recording.");
|
|
4318
|
+
}
|
|
4319
|
+
const result = this.recordTake({ samples: r.samples, length }, r.options);
|
|
4320
|
+
this.player.seek(r.start);
|
|
4321
|
+
return result;
|
|
4322
|
+
}
|
|
4323
|
+
/** Stops recording and throws the take away. */
|
|
4324
|
+
cancelRecording() {
|
|
4325
|
+
const r = this.endRecording();
|
|
4326
|
+
if (!r) return;
|
|
4327
|
+
this.player.seek(r.start);
|
|
4328
|
+
this.notify();
|
|
4329
|
+
}
|
|
4330
|
+
/**
|
|
4331
|
+
* Adds a take given as samples (an agent's performance, or one recorded elsewhere) as a shot,
|
|
4332
|
+
* one undo step, selected. With `dryRun` it returns the shot and changes nothing.
|
|
4333
|
+
*/
|
|
4334
|
+
recordTake(take, options = {}) {
|
|
4335
|
+
let made;
|
|
4336
|
+
try {
|
|
4337
|
+
made = motionTakeShot(this.scene, take, options);
|
|
4338
|
+
} catch (error) {
|
|
4339
|
+
return options.dryRun ? { ok: false, error: error.message } : this.refuse(error.message);
|
|
4340
|
+
}
|
|
4341
|
+
const result = this.apply({ op: "insert_shots", shots: [made.shot], at: made.at }, "Record take", { dryRun: options.dryRun });
|
|
4342
|
+
if (!result.ok) return result;
|
|
4343
|
+
if (!options.dryRun) this.select({ shot: made.shot.id });
|
|
4344
|
+
return { ok: true, ...made, issues: result.issues };
|
|
3960
4345
|
}
|
|
3961
4346
|
/** The scene as a Flair `SvgMotion`. */
|
|
3962
4347
|
toFlairMotion(options = {}) {
|
|
@@ -3998,7 +4383,7 @@ function elementsOf(args) {
|
|
|
3998
4383
|
if (Array.isArray(args.ops)) {
|
|
3999
4384
|
for (const op of args.ops) {
|
|
4000
4385
|
fromShot(op);
|
|
4001
|
-
if (Array.isArray(_optionalChain([op, 'optionalAccess',
|
|
4386
|
+
if (Array.isArray(_optionalChain([op, 'optionalAccess', _178 => _178.shots]))) op.shots.forEach(fromShot);
|
|
4002
4387
|
}
|
|
4003
4388
|
}
|
|
4004
4389
|
return out.slice(0, 4);
|
|
@@ -4111,7 +4496,7 @@ var MOTION_COMMAND_TYPE_SCHEMAS = {
|
|
|
4111
4496
|
distance: { type: "number", description: "For slide: document units (default 40)." }
|
|
4112
4497
|
}),
|
|
4113
4498
|
draw: command2("draw", "Draws strokes on (or off) to a fraction of their length.", { ...DRAW_ORDER, comet: FRACTION("A travelling stroke this fraction of the length long: it runs along the path and off the end (leave out from and to)."), to: FRACTION("Fraction drawn at the end (default 1)."), from: FRACTION("Fraction drawn at the start (default: what is drawn now).") }),
|
|
4114
|
-
move: command2("move", "Moves targets by an offset or so their centre lands on a point.", { via: { type: "array", items: POINT("A point the centre passes through."), minItems: 1, maxItems: 64, description: 'Waypoints (document units) the centre passes through on the way to "to" or "by", smoothly (velocity carries through each) and without overshooting any. Default length 700 ms + 400 per waypoint.' }, pacing: { type: "string", enum: ["even", "equal"], description: "With via: even (default) keeps the speed steady along the path; equal gives each stretch the same time." }, blend: BLEND("replace"), by: POINT("Offset in document units."), to: POINT("Point (document coordinates) for the centre.") }),
|
|
4499
|
+
move: command2("move", "Moves targets by an offset or so their centre lands on a point.", { via: { type: "array", items: POINT("A point the centre passes through."), minItems: 1, maxItems: 64, description: 'Waypoints (document units) the centre passes through on the way to "to" or "by", smoothly (velocity carries through each) and without overshooting any. Default length 700 ms + 400 per waypoint.' }, pacing: { type: "string", enum: ["even", "equal"], description: "With via: even (default) keeps the speed steady along the path; equal gives each stretch the same time." }, times: { type: "array", items: { type: "number", exclusiveMinimum: 0, exclusiveMaximum: 1 }, minItems: 1, maxItems: 64, description: "With via: when the centre passes each waypoint, as rising fractions of the duration (0\u20131), one per waypoint; overrides pacing. Recorded takes set it." }, blend: BLEND("replace"), by: POINT("Offset in document units."), to: POINT("Point (document coordinates) for the centre.") }),
|
|
4115
4500
|
rotate: command2("rotate", "Turns targets about their centre (degrees, clockwise).", { blend: BLEND("replace"), by: { type: "number", description: "Degrees to turn by." }, to: { type: "number", description: "Angle to turn to, in degrees from rest." } }),
|
|
4116
4501
|
scale: command2("scale", "Scales targets about their centre (1 = rest size).", { blend: BLEND("replace"), to: { description: "A factor, or { x, y } factors.", anyOf: [{ type: "number" }, POINT("Factors per axis.")] }, geometric: { type: "boolean", description: "Grow by a steady factor, which reads as an even zoom (default false)." } }, ["to"]),
|
|
4117
4502
|
fade: command2("fade", "Changes opacity.", { to: FRACTION("Opacity at the end (0\u20131).") }, ["to"]),
|
|
@@ -4180,7 +4565,7 @@ var MOTION_COMMAND_SCHEMA = {
|
|
|
4180
4565
|
oneOf: MOTION_COMMAND_TYPES.map((type) => MOTION_COMMAND_TYPE_SCHEMAS[type])
|
|
4181
4566
|
};
|
|
4182
4567
|
var REF2 = (name, description) => ({ $ref: `#/$defs/${name}`, ...description ? { description } : {} });
|
|
4183
|
-
var firstSentence = (text) => _nullishCoalesce(_optionalChain([/^.*?[.;](?=\s|$)/, 'access',
|
|
4568
|
+
var firstSentence = (text) => _nullishCoalesce(_optionalChain([/^.*?[.;](?=\s|$)/, 'access', _179 => _179.exec, 'call', _180 => _180(text), 'optionalAccess', _181 => _181[0]]), () => ( text));
|
|
4184
4569
|
var COMMAND_TYPE_LINES = MOTION_COMMAND_TYPES.map((type) => `${type}: ${firstSentence(_nullishCoalesce(MOTION_COMMAND_TYPE_SCHEMAS[type].description, () => ( "")))}`);
|
|
4185
4570
|
var EASE_COMPACT = {
|
|
4186
4571
|
description: `Easing: a preset, or a curve { "type": "cubic-bezier", x1, y1, x2, y2 }, { "type": "spring", stiffness, damping, mass, velocity } (spring presets are Figma's; with no "duration" it lasts until it settles) or { "type": "elastic", amplitude, period, mode }. Default: ease-out for entrances, ease-in-out for moves.`,
|
|
@@ -4242,6 +4627,7 @@ var COMMAND = object(
|
|
|
4242
4627
|
comet: FRACTION("draw: a travelling stroke this fraction of the length long (leave out from and to)."),
|
|
4243
4628
|
via: { type: "array", items: REF2("point"), minItems: 1, maxItems: 64, description: 'move: waypoints the centre passes through smoothly on the way to "to" or "by".' },
|
|
4244
4629
|
pacing: { type: "string", enum: ["even", "equal"], description: "move with via: steady speed (even, default) or the same time per stretch (equal)." },
|
|
4630
|
+
times: { type: "array", items: { type: "number", exclusiveMinimum: 0, exclusiveMaximum: 1 }, description: "move with via: rising fractions of the duration (0\u20131) at which each waypoint is passed; overrides pacing." },
|
|
4245
4631
|
geometric: { type: "boolean", description: "scale: grow by a steady factor, an even zoom (default false)." },
|
|
4246
4632
|
fill: { type: "string", description: "style: fill colour, CSS (hex, rgb(), rgba())." },
|
|
4247
4633
|
stroke: { type: "string", description: "style: stroke colour, CSS." },
|
|
@@ -4381,11 +4767,11 @@ function describeElement(node, ancestors) {
|
|
|
4381
4767
|
...effects.length ? { effects } : {}
|
|
4382
4768
|
};
|
|
4383
4769
|
}
|
|
4384
|
-
var
|
|
4770
|
+
var round2 = (n) => Math.round(n * 10) / 10;
|
|
4385
4771
|
function describeMotionElements(scene) {
|
|
4386
4772
|
return [...indexMotionNodes(scene.artwork.nodes).values()].map((e) => ({
|
|
4387
4773
|
...describeElement(e.node, e.ancestors),
|
|
4388
|
-
...e.bounds ? { bounds: { x:
|
|
4774
|
+
...e.bounds ? { bounds: { x: round2(e.bounds.x), y: round2(e.bounds.y), width: round2(e.bounds.width), height: round2(e.bounds.height) } } : {}
|
|
4389
4775
|
}));
|
|
4390
4776
|
}
|
|
4391
4777
|
function describeMotionShots(scene) {
|
|
@@ -4588,18 +4974,18 @@ function createAnimationEditorToolProvider(controller, options = {}) {
|
|
|
4588
4974
|
handler: ({ from = 0, to, limit = 200 }) => {
|
|
4589
4975
|
const s = scene();
|
|
4590
4976
|
const timeline = compileMotionScene(s);
|
|
4591
|
-
const words = _nullishCoalesce(_optionalChain([s, 'access',
|
|
4977
|
+
const words = _nullishCoalesce(_optionalChain([s, 'access', _182 => _182.narration, 'optionalAccess', _183 => _183.words]), () => ( []));
|
|
4592
4978
|
const end = _nullishCoalesce(to, () => ( Math.max(timeline.duration, words.length ? _nullishCoalesce(words[words.length - 1].end, () => ( words[words.length - 1].start)) : 0)));
|
|
4593
|
-
const shotAt = (
|
|
4979
|
+
const shotAt = (ms2) => _optionalChain([timeline, 'access', _184 => _184.shots, 'access', _185 => _185.find, 'call', _186 => _186((x) => ms2 >= x.start && ms2 < x.end), 'optionalAccess', _187 => _187.id]);
|
|
4594
4980
|
const beats = [];
|
|
4595
4981
|
if (s.beat && s.beat.bpm > 0) {
|
|
4596
4982
|
const first = Math.max(0, Math.ceil(motionBeatAt(s.beat, from).beat - 1e-9));
|
|
4597
4983
|
for (let b = first; beats.length < limit; b++) {
|
|
4598
|
-
const
|
|
4599
|
-
if (
|
|
4600
|
-
const at = motionBeatAt(s.beat,
|
|
4601
|
-
const shot = shotAt(
|
|
4602
|
-
beats.push({ beat: b, ms: Math.round(
|
|
4984
|
+
const ms2 = motionBeatTime(s.beat, b);
|
|
4985
|
+
if (ms2 > end) break;
|
|
4986
|
+
const at = motionBeatAt(s.beat, ms2);
|
|
4987
|
+
const shot = shotAt(ms2);
|
|
4988
|
+
beats.push({ beat: b, ms: Math.round(ms2), bar: at.bar, beatInBar: at.beatInBar, ...shot ? { shot } : {} });
|
|
4603
4989
|
}
|
|
4604
4990
|
}
|
|
4605
4991
|
const listed = words.map((w, index) => ({ index, text: w.text, start: w.start, ...w.end !== void 0 ? { end: w.end } : {}, ...shotAt(w.start) ? { shot: shotAt(w.start) } : {} })).filter((w) => w.start >= from && w.start <= end).slice(0, limit);
|
|
@@ -4774,6 +5160,60 @@ function createAnimationEditorToolProvider(controller, options = {}) {
|
|
|
4774
5160
|
return apply(edits, "Fit to length", () => report);
|
|
4775
5161
|
}
|
|
4776
5162
|
};
|
|
5163
|
+
const recordTake = {
|
|
5164
|
+
name: "record_animation_take",
|
|
5165
|
+
title: "Record a performed take",
|
|
5166
|
+
description: "Performs changes live and keeps them as a new shot (one undo step), the way a person records by dragging, resizing, turning and recolouring on the stage: give timed samples of where elements are and how they look, and they become smooth commands that play the performance back (a path is one move through waypoints at the recorded times; scales, turns, fades and stroke widths a few eased commands; paints change as picked). Returns the shot id, where it starts, its commands with their start and end times, and how many samples were kept. With dry_run it returns the shot without adding it.",
|
|
5167
|
+
inputSchema: object(
|
|
5168
|
+
{
|
|
5169
|
+
samples: {
|
|
5170
|
+
type: "array",
|
|
5171
|
+
minItems: 1,
|
|
5172
|
+
maxItems: 5e3,
|
|
5173
|
+
description: "Values at moments of the take, in any order. Before an element's first sample a property keeps its pose and the change into the first sample is instant, so start a movement with a sample at the element's current pose (get_motion_path's last point) to have it travel from there.",
|
|
5174
|
+
items: object(
|
|
5175
|
+
{
|
|
5176
|
+
node: { type: "string", minLength: 1, description: "Element id from get_animation." },
|
|
5177
|
+
t: MS("Time from the start of the take"),
|
|
5178
|
+
x: { type: "number", description: "Centre x, document units (with y)." },
|
|
5179
|
+
y: { type: "number", description: "Centre y, document units (with x)." },
|
|
5180
|
+
scaleX: { type: "number", exclusiveMinimum: 0, description: "Horizontal scale from rest (1 = rest size; give scaleX alone for a uniform scale)." },
|
|
5181
|
+
scaleY: { type: "number", exclusiveMinimum: 0, description: "Vertical scale from rest (1 = rest size)." },
|
|
5182
|
+
rotation: { type: "number", description: "Turn from rest, degrees clockwise (keep counting past 360 for more turns)." },
|
|
5183
|
+
opacity: FRACTION("Opacity, 0\u20131."),
|
|
5184
|
+
fill: { type: "string", description: 'Fill colour, e.g. "#0ea5e9".' },
|
|
5185
|
+
stroke: { type: "string", description: 'Stroke colour, e.g. "#0f172a".' },
|
|
5186
|
+
strokeWidth: { type: "number", minimum: 0, description: "Stroke width, document units." }
|
|
5187
|
+
},
|
|
5188
|
+
["node", "t"]
|
|
5189
|
+
)
|
|
5190
|
+
},
|
|
5191
|
+
length: MS("How long the take ran (default: the last sample); the shot holds after its last change until then"),
|
|
5192
|
+
id: { ...SHOT_ID, description: 'Id for the new shot (default "take", "take-2"\u2026).' },
|
|
5193
|
+
label: { type: "string", description: 'Label for the new shot (default "Recorded take").' },
|
|
5194
|
+
at: { type: "integer", minimum: 0, description: "Position among the shots, 0 = first (default: the end); the take starts where that shot starts." },
|
|
5195
|
+
tolerance: { type: "number", exclusiveMinimum: 0, description: "How closely commands follow the samples, as a multiple of 1 document unit, 0.01 of scale or opacity, 0.5\xB0 and 0.25 of stroke width (default 1; lower keeps more points)." },
|
|
5196
|
+
dry_run: { type: "boolean", description: "Return the shot without adding it (default false)." }
|
|
5197
|
+
},
|
|
5198
|
+
["samples"]
|
|
5199
|
+
),
|
|
5200
|
+
annotations: { title: "Record a performed take", ...WRITE },
|
|
5201
|
+
handler: ({ samples, length, id, label, at, tolerance, dry_run = false }) => {
|
|
5202
|
+
const result = controller.recordTake({ samples, length }, { id, label, at, tolerance, dryRun: dry_run });
|
|
5203
|
+
if (!result.ok) throw new Error(result.error);
|
|
5204
|
+
const s = dry_run ? scene() : controller.getScene();
|
|
5205
|
+
const report = { shot: result.shot.id, at: result.at, start: Math.round(result.start), samples: result.samples };
|
|
5206
|
+
if (dry_run) return { dry_run: true, ...report, run: result.shot.run, hold: result.shot.hold, hint: "Nothing was changed; call again without dry_run to add the shot." };
|
|
5207
|
+
const timeline = compileMotionScene(s);
|
|
5208
|
+
const described = describeMotionShots(s).find((x) => x.id === result.shot.id);
|
|
5209
|
+
return {
|
|
5210
|
+
...report,
|
|
5211
|
+
commands: described.commands.map((c) => ({ index: c.index, type: c.type, targets: "targets" in c ? c.targets : [], start: c.start, end: c.end })),
|
|
5212
|
+
duration: timeline.duration,
|
|
5213
|
+
issues: issueSummary(result.issues)
|
|
5214
|
+
};
|
|
5215
|
+
}
|
|
5216
|
+
};
|
|
4777
5217
|
const PROP = { type: "string", enum: ["x", "y", "rotation", "scaleX", "scaleY", "opacity"], description: "x / y (offset, document units), rotation (degrees), scaleX / scaleY or opacity (1 = rest)." };
|
|
4778
5218
|
const setDrivers = {
|
|
4779
5219
|
name: "set_animation_drivers",
|
|
@@ -4912,7 +5352,7 @@ function createAnimationEditorToolProvider(controller, options = {}) {
|
|
|
4912
5352
|
inputSchema: object({ shot: SHOT_ID, commands: { type: "array", items: REF2("command"), minItems: 1, description: "Commands to add." }, at: { type: "integer", minimum: 0, description: "Index in the run (default: the end)." } }, ["shot", "commands"]),
|
|
4913
5353
|
annotations: { title: "Add commands to a shot", ...WRITE },
|
|
4914
5354
|
handler: ({ shot, commands, at }) => {
|
|
4915
|
-
const before = _nullishCoalesce(_optionalChain([scene, 'call',
|
|
5355
|
+
const before = _nullishCoalesce(_optionalChain([scene, 'call', _188 => _188(), 'access', _189 => _189.shots, 'access', _190 => _190.find, 'call', _191 => _191((s) => s.id === shot), 'optionalAccess', _192 => _192.run, 'access', _193 => _193.length]), () => ( 0));
|
|
4916
5356
|
const first = _nullishCoalesce(at, () => ( before));
|
|
4917
5357
|
return apply({ op: "add_commands", shot, commands, at }, "Add commands", (s) => {
|
|
4918
5358
|
const shots = describeMotionShots(s);
|
|
@@ -5045,7 +5485,7 @@ function createAnimationEditorToolProvider(controller, options = {}) {
|
|
|
5045
5485
|
};
|
|
5046
5486
|
const finish = (tools) => tools.map((tool) => ({ ...tool, inputSchema: withDefs(tool.inputSchema) }));
|
|
5047
5487
|
const reads = finish([getAnimation, getShot, describeCommand, findElements, getTextLayout, getMotionPath, getTimingGrid, checkAnimation, renderFrame, exportFlairMotion, exportFlairEffect, exportLottie, seekAnimation]);
|
|
5048
|
-
const writes = finish([setArtworkSvg, importPptx, setDrivers, setClip, copyAnimation, fitAnimation, importFlairEffect, insertShots, updateShot, deleteShots, moveShot, addCommands, updateCommand, deleteCommands, moveCommand, setSettings, setControls, editAnimation, writeAnimation, undo, redo]);
|
|
5488
|
+
const writes = finish([setArtworkSvg, importPptx, setDrivers, setClip, copyAnimation, fitAnimation, recordTake, importFlairEffect, insertShots, updateShot, deleteShots, moveShot, addCommands, updateCommand, deleteCommands, moveCommand, setSettings, setControls, editAnimation, writeAnimation, undo, redo]);
|
|
5049
5489
|
return withAnimationPlayback({
|
|
5050
5490
|
id: _nullishCoalesce(options.id, () => ( "animation")),
|
|
5051
5491
|
namespace: options.namespace,
|
|
@@ -5113,7 +5553,7 @@ var TIMING = /* @__PURE__ */ new Set(["duration", "delay", "stagger", "with", "e
|
|
|
5113
5553
|
var COLORS = /* @__PURE__ */ new Set(["fill", "stroke", "color"]);
|
|
5114
5554
|
var NODES = /* @__PURE__ */ new Set(["into", "path"]);
|
|
5115
5555
|
function branch(type) {
|
|
5116
|
-
return (_nullishCoalesce(MOTION_COMMAND_SCHEMA.oneOf, () => ( []))).find((b) => _optionalChain([b, 'access',
|
|
5556
|
+
return (_nullishCoalesce(MOTION_COMMAND_SCHEMA.oneOf, () => ( []))).find((b) => _optionalChain([b, 'access', _194 => _194.properties, 'optionalAccess', _195 => _195.type, 'optionalAccess', _196 => _196.const]) === type);
|
|
5117
5557
|
}
|
|
5118
5558
|
function unitOf(type, name) {
|
|
5119
5559
|
if (name === "duration" || name === "delay" || name === "stagger") return "ms";
|
|
@@ -5148,7 +5588,7 @@ function kindOf(name, schema) {
|
|
|
5148
5588
|
}
|
|
5149
5589
|
function motionCommandFields(type) {
|
|
5150
5590
|
const schema = branch(type);
|
|
5151
|
-
if (!_optionalChain([schema, 'optionalAccess',
|
|
5591
|
+
if (!_optionalChain([schema, 'optionalAccess', _197 => _197.properties])) return [];
|
|
5152
5592
|
const required = new Set(_nullishCoalesce(schema.required, () => ( [])));
|
|
5153
5593
|
const fields = Object.entries(schema.properties).filter(([name]) => name !== "type").map(([name, s]) => {
|
|
5154
5594
|
const kind = kindOf(name, s);
|
|
@@ -5205,7 +5645,7 @@ function defaultMotionCommand(type, targets, scene) {
|
|
|
5205
5645
|
if (type === "wait") return { type: "wait", duration: 500 };
|
|
5206
5646
|
if (!targets.length) return { error: "Pick a target first: click an element on the stage." };
|
|
5207
5647
|
const nodes = [...indexMotionNodes(scene.artwork.nodes).values()];
|
|
5208
|
-
const other = (pred) => _optionalChain([nodes, 'access',
|
|
5648
|
+
const other = (pred) => _optionalChain([nodes, 'access', _198 => _198.find, 'call', _199 => _199((e) => !targets.includes(e.node.id) && pred(e)), 'optionalAccess', _200 => _200.node, 'access', _201 => _201.id]);
|
|
5209
5649
|
const base = { targets: [...targets] };
|
|
5210
5650
|
switch (type) {
|
|
5211
5651
|
case "scale":
|
|
@@ -5236,8 +5676,8 @@ function defaultMotionCommand(type, targets, scene) {
|
|
|
5236
5676
|
}
|
|
5237
5677
|
case "effect": {
|
|
5238
5678
|
for (const id of targets) {
|
|
5239
|
-
const node = _optionalChain([nodes, 'access',
|
|
5240
|
-
for (const effect of _nullishCoalesce(_optionalChain([node, 'optionalAccess',
|
|
5679
|
+
const node = _optionalChain([nodes, 'access', _202 => _202.find, 'call', _203 => _203((e) => e.node.id === id), 'optionalAccess', _204 => _204.node]);
|
|
5680
|
+
for (const effect of _nullishCoalesce(_optionalChain([node, 'optionalAccess', _205 => _205.effects]), () => ( []))) {
|
|
5241
5681
|
const param = Object.entries(effect.params).find(([, v]) => typeof v === "number");
|
|
5242
5682
|
if (param) return { type, ...base, effect: effect.id, param: param[0], to: param[1] + 1 };
|
|
5243
5683
|
}
|
|
@@ -5262,11 +5702,11 @@ function motionCommandRows(scene, shotId) {
|
|
|
5262
5702
|
return scene.shots[index].run.map((command3, i) => {
|
|
5263
5703
|
const t = timeline.commands.find((c) => c.shotIndex === index && c.command === i);
|
|
5264
5704
|
const primitive = motionFlairPrimitive(command3);
|
|
5265
|
-
return { index: i, command: command3, summary: motionCommandSummary(command3), start: _nullishCoalesce(_optionalChain([t, 'optionalAccess',
|
|
5705
|
+
return { index: i, command: command3, summary: motionCommandSummary(command3), start: _nullishCoalesce(_optionalChain([t, 'optionalAccess', _206 => _206.start]), () => ( 0)), end: _nullishCoalesce(_optionalChain([t, 'optionalAccess', _207 => _207.end]), () => ( 0)), ...primitive ? { flairPrimitive: primitive } : {} };
|
|
5266
5706
|
});
|
|
5267
5707
|
}
|
|
5268
5708
|
function duplicateMotionCommandEdit(scene, shot, command3) {
|
|
5269
|
-
const run = _nullishCoalesce(_optionalChain([scene, 'access',
|
|
5709
|
+
const run = _nullishCoalesce(_optionalChain([scene, 'access', _208 => _208.shots, 'access', _209 => _209.find, 'call', _210 => _210((s) => s.id === shot), 'optionalAccess', _211 => _211.run]), () => ( []));
|
|
5270
5710
|
return { op: "add_commands", shot, commands: run[command3] ? [structuredClone(run[command3])] : [], at: command3 + 1 };
|
|
5271
5711
|
}
|
|
5272
5712
|
var MOTION_EASE_CHOICES = [
|
|
@@ -5279,15 +5719,15 @@ function motionEaseKey(ease) {
|
|
|
5279
5719
|
}
|
|
5280
5720
|
function motionEasingCurve(ease, samples = 48, durationMs = 1e3) {
|
|
5281
5721
|
const resolved = _chunkDWJJI6RTcjs.resolveEasing.call(void 0, ease);
|
|
5282
|
-
const
|
|
5722
|
+
const ms2 = resolved !== "hold" && resolved.type === "spring" ? _chunkDWJJI6RTcjs.springSettleMs.call(void 0, resolved) : durationMs;
|
|
5283
5723
|
const n = Math.max(2, Math.floor(samples));
|
|
5284
5724
|
const points = [];
|
|
5285
5725
|
for (let i = 0; i <= n; i++) {
|
|
5286
5726
|
const u = i / n;
|
|
5287
|
-
points.push({ u, v: resolved === "hold" ? i === n ? 1 : 0 : _chunkDWJJI6RTcjs.evaluateEasing.call(void 0, ease, u,
|
|
5727
|
+
points.push({ u, v: resolved === "hold" ? i === n ? 1 : 0 : _chunkDWJJI6RTcjs.evaluateEasing.call(void 0, ease, u, ms2) });
|
|
5288
5728
|
}
|
|
5289
5729
|
const values = points.map((p) => p.v);
|
|
5290
|
-
return { points, min: Math.min(0, ...values), max: Math.max(1, ...values), durationMs:
|
|
5730
|
+
return { points, min: Math.min(0, ...values), max: Math.max(1, ...values), durationMs: ms2 };
|
|
5291
5731
|
}
|
|
5292
5732
|
function motionEasingPath(curve, width, height, pad = 4) {
|
|
5293
5733
|
const span = curve.max - curve.min || 1;
|
|
@@ -5328,8 +5768,8 @@ function motionRulerTicks(duration, width, minGap = 56) {
|
|
|
5328
5768
|
}
|
|
5329
5769
|
return ticks;
|
|
5330
5770
|
}
|
|
5331
|
-
function fitMotionSceneEdits(scene,
|
|
5332
|
-
const fit = fitMotionScene(scene,
|
|
5771
|
+
function fitMotionSceneEdits(scene, ms2, options = {}) {
|
|
5772
|
+
const fit = fitMotionScene(scene, ms2, options);
|
|
5333
5773
|
return fit.scene.shots.flatMap((shot, i) => {
|
|
5334
5774
|
const before = scene.shots[i].speed;
|
|
5335
5775
|
if (before === shot.speed) return [];
|
|
@@ -5350,8 +5790,8 @@ function motionCueFromKey(key) {
|
|
|
5350
5790
|
}
|
|
5351
5791
|
function motionCueChoices(scene, limit = 256) {
|
|
5352
5792
|
const choices = [];
|
|
5353
|
-
const s = (
|
|
5354
|
-
const end = Math.max(compileMotionScene(scene).duration, ..._nullishCoalesce(_optionalChain([scene, 'access',
|
|
5793
|
+
const s = (ms2) => `${(ms2 / 1e3).toFixed(2)} s`;
|
|
5794
|
+
const end = Math.max(compileMotionScene(scene).duration, ..._nullishCoalesce(_optionalChain([scene, 'access', _212 => _212.narration, 'optionalAccess', _213 => _213.words, 'access', _214 => _214.map, 'call', _215 => _215((w) => _nullishCoalesce(w.end, () => ( w.start)))]), () => ( [0]))) + 2e3;
|
|
5355
5795
|
if (scene.beat && scene.beat.bpm > 0) {
|
|
5356
5796
|
for (let beat = 0; choices.length < limit; beat++) {
|
|
5357
5797
|
const time = motionBeatTime(scene.beat, beat);
|
|
@@ -5361,7 +5801,7 @@ function motionCueChoices(scene, limit = 256) {
|
|
|
5361
5801
|
choices.push({ value: `beat:${beat}`, label: `Beat ${beat} (bar ${at.bar}.${at.beatInBar}) \xB7 ${s(time)}`, time });
|
|
5362
5802
|
}
|
|
5363
5803
|
}
|
|
5364
|
-
_optionalChain([scene, 'access',
|
|
5804
|
+
_optionalChain([scene, 'access', _216 => _216.narration, 'optionalAccess', _217 => _217.words, 'access', _218 => _218.forEach, 'call', _219 => _219((w, i) => {
|
|
5365
5805
|
if (choices.length < limit * 2) choices.push({ value: `word:${i}`, label: `Word ${i} \u201C${w.text}\u201D \xB7 ${s(w.start)}`, time: w.start });
|
|
5366
5806
|
})]);
|
|
5367
5807
|
return choices;
|
|
@@ -5370,7 +5810,7 @@ function motionShotCueTime(scene, cue) {
|
|
|
5370
5810
|
return cue ? motionCueTime(scene, cue) : void 0;
|
|
5371
5811
|
}
|
|
5372
5812
|
function formatMotionNarration(narration) {
|
|
5373
|
-
return (_nullishCoalesce(_optionalChain([narration, 'optionalAccess',
|
|
5813
|
+
return (_nullishCoalesce(_optionalChain([narration, 'optionalAccess', _220 => _220.words]), () => ( []))).map((w) => `${w.start} ${w.text}`).join("\n");
|
|
5374
5814
|
}
|
|
5375
5815
|
function parseMotionNarration(text) {
|
|
5376
5816
|
const lines = text.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
@@ -5643,4 +6083,11 @@ function createAnimationEffectsToolProvider(controller, options = {}) {
|
|
|
5643
6083
|
|
|
5644
6084
|
|
|
5645
6085
|
|
|
5646
|
-
|
|
6086
|
+
|
|
6087
|
+
|
|
6088
|
+
|
|
6089
|
+
|
|
6090
|
+
|
|
6091
|
+
|
|
6092
|
+
|
|
6093
|
+
exports.ANIMATION_EDITOR_AGENT_INSTRUCTIONS = ANIMATION_EDITOR_AGENT_INSTRUCTIONS; exports.AnimationEditorController = AnimationEditorController; exports.FLAIR_EASES = FLAIR_EASES; exports.FLAIR_EXPORT_VERSION = FLAIR_EXPORT_VERSION; exports.MOTION_ADDITIVE_PROPERTIES = MOTION_ADDITIVE_PROPERTIES; exports.MOTION_BLENDS = MOTION_BLENDS; exports.MOTION_CAMERA_MS = MOTION_CAMERA_MS; exports.MOTION_CASCADES = MOTION_CASCADES; exports.MOTION_CLIP_TARGET = MOTION_CLIP_TARGET; exports.MOTION_COMMAND_FIELDS = MOTION_COMMAND_FIELDS; exports.MOTION_COMMAND_SCHEMA = MOTION_COMMAND_SCHEMA; exports.MOTION_COMMAND_TYPES = MOTION_COMMAND_TYPES; exports.MOTION_COMMAND_TYPE_SCHEMAS = MOTION_COMMAND_TYPE_SCHEMAS; exports.MOTION_DEFAULT_DURATIONS = MOTION_DEFAULT_DURATIONS; exports.MOTION_DEFAULT_HOLD = MOTION_DEFAULT_HOLD; exports.MOTION_DIRECTIONS = MOTION_DIRECTIONS; exports.MOTION_DRAW_EFFECT_ID = MOTION_DRAW_EFFECT_ID; exports.MOTION_DRAW_MS_PER_UNIT = MOTION_DRAW_MS_PER_UNIT; exports.MOTION_DRAW_RANGE = MOTION_DRAW_RANGE; exports.MOTION_DRIVEN_PROPERTIES = MOTION_DRIVEN_PROPERTIES; exports.MOTION_EASE_CHOICES = MOTION_EASE_CHOICES; exports.MOTION_EASE_IN_OUT = MOTION_EASE_IN_OUT; exports.MOTION_EASE_OUT = MOTION_EASE_OUT; exports.MOTION_EDIT_OPS = MOTION_EDIT_OPS; exports.MOTION_ENTER_STYLES = MOTION_ENTER_STYLES; exports.MOTION_EXIT_STYLES = MOTION_EXIT_STYLES; exports.MOTION_IRIS_SHAPES = MOTION_IRIS_SHAPES; exports.MOTION_ISSUE_CODES = _chunkEBDZWIOPcjs.MOTION_ISSUE_CODES; exports.MOTION_MAX_TAKE_MS = MOTION_MAX_TAKE_MS; exports.MOTION_MIN_CHANGE_MS = MOTION_MIN_CHANGE_MS; exports.MOTION_RECORD_GESTURE_MODES = MOTION_RECORD_GESTURE_MODES; exports.MOTION_REPEAT_MODES = MOTION_REPEAT_MODES; exports.MOTION_SPEED_RANGE = MOTION_SPEED_RANGE; exports.MOTION_SPLIT_ORIENTATIONS = MOTION_SPLIT_ORIENTATIONS; exports.MOTION_TEXT_STAGGER = MOTION_TEXT_STAGGER; exports.MOTION_TEXT_UNITS = MOTION_TEXT_UNITS; exports.MOTION_TRIGGERS = MOTION_TRIGGERS; exports.MotionSceneFormatError = MotionSceneFormatError; exports.activeSegment = activeSegment; exports.applyMotionControlValues = applyMotionControlValues; exports.applyMotionDrivers = applyMotionDrivers; exports.applyMotionEdits = applyMotionEdits; exports.checkMotionScene = checkMotionScene; exports.clipsShapeProblems = clipsShapeProblems; exports.commandShapeProblems = commandShapeProblems; exports.compileMotionScene = compileMotionScene; exports.createAnimationEditorToolProvider = createAnimationEditorToolProvider; exports.createAnimationEffectsToolProvider = createAnimationEffectsToolProvider; exports.createMotionScene = createMotionScene; exports.defaultMotionCommand = defaultMotionCommand; exports.describeMotionElements = describeMotionElements; exports.describeMotionShots = describeMotionShots; exports.driversShapeProblems = driversShapeProblems; exports.duplicateMotionCommandEdit = duplicateMotionCommandEdit; exports.fitMotionRect = fitMotionRect; exports.fitMotionScene = fitMotionScene; exports.fitMotionSceneEdits = fitMotionSceneEdits; exports.flairAtLeast = flairAtLeast; exports.flairCurveFor = flairCurveFor; exports.flairEaseFor = flairEaseFor; exports.formatMotionDrivers = formatMotionDrivers; exports.formatMotionNarration = formatMotionNarration; exports.freshShotId = freshShotId; exports.fromFlairSvgMotion = fromFlairSvgMotion; exports.fromFlairVectorEffect = fromFlairVectorEffect; exports.fromPptxAnimation = fromPptxAnimation; exports.highlightMotionSvg = highlightMotionSvg; exports.indexMotionNodes = indexMotionNodes; exports.interpolateMotionValue = interpolateMotionValue; exports.isMotionCommand = isMotionCommand; exports.isMotionShot = isMotionShot; exports.matchMotionNodes = matchMotionNodes; exports.motionAddTrackKey = motionAddTrackKey; exports.motionArtworkSvg = motionArtworkSvg; exports.motionBeatAt = motionBeatAt; exports.motionBeatTime = motionBeatTime; exports.motionBlurTimes = motionBlurTimes; exports.motionCascade = motionCascade; exports.motionClientToDocument = motionClientToDocument; exports.motionClipAttrs = motionClipAttrs; exports.motionClipLayout = motionClipLayout; exports.motionCommandDuration = motionCommandDuration; exports.motionCommandFields = motionCommandFields; exports.motionCommandRepeat = motionCommandRepeat; exports.motionCommandRows = motionCommandRows; exports.motionCommandSlots = motionCommandSlots; exports.motionCommandSummary = motionCommandSummary; exports.motionCommandsFromEffect = motionCommandsFromEffect; exports.motionContactSheetSvg = motionContactSheetSvg; exports.motionCueChoices = motionCueChoices; exports.motionCueFromKey = motionCueFromKey; exports.motionCueKey = motionCueKey; exports.motionCueTime = motionCueTime; exports.motionCycleDuration = motionCycleDuration; exports.motionDrivenNodes = motionDrivenNodes; exports.motionDriverSources = motionDriverSources; exports.motionEaseKey = motionEaseKey; exports.motionEasingCurve = motionEasingCurve; exports.motionEasingGuides = motionEasingGuides; exports.motionEasingPath = motionEasingPath; exports.motionFieldEdit = motionFieldEdit; exports.motionFieldLabel = motionFieldLabel; exports.motionFlairPrimitive = motionFlairPrimitive; exports.motionFollowRoute = motionFollowRoute; exports.motionFrameAt = motionFrameAt; exports.motionFrameToSvg = motionFrameToSvg; exports.motionGhosts = motionGhosts; exports.motionIssue = _chunkEBDZWIOPcjs.motionIssue; exports.motionIssueKey = _chunkEBDZWIOPcjs.motionIssueKey; exports.motionIssueLine = _chunkEBDZWIOPcjs.motionIssueLine; exports.motionMovingIds = motionMovingIds; exports.motionNodeAtElement = motionNodeAtElement; exports.motionNodeClips = motionNodeClips; exports.motionNodeOffset = motionNodeOffset; exports.motionNodePose = motionNodePose; exports.motionPartsFromVectorEffect = motionPartsFromVectorEffect; exports.motionPath = motionPath; exports.motionPathLength = motionPathLength; exports.motionPathsSvg = motionPathsSvg; exports.motionPropertyValue = motionPropertyValue; exports.motionRestValue = motionRestValue; exports.motionRevealClip = motionRevealClip; exports.motionRevealPadding = motionRevealPadding; exports.motionRulerTicks = motionRulerTicks; exports.motionSceneEffects = motionSceneEffects; exports.motionSceneShapeProblems = motionSceneShapeProblems; exports.motionSceneToJson = motionSceneToJson; exports.motionShotAt = motionShotAt; exports.motionShotCueTime = motionShotCueTime; exports.motionShotTimeScale = motionShotTimeScale; exports.motionStageSvg = motionStageSvg; exports.motionTakeProblems = motionTakeProblems; exports.motionTakeShot = motionTakeShot; exports.motionTextLayout = motionTextLayout; exports.motionTextUnits = motionTextUnits; exports.motionTrackKey = motionTrackKey; exports.namedWithSuggestion = _chunkEBDZWIOPcjs.namedWithSuggestion; exports.nextMotionEffectInstance = nextMotionEffectInstance; exports.parseMotionDrivers = parseMotionDrivers; exports.parseMotionFieldInput = parseMotionFieldInput; exports.parseMotionNarration = parseMotionNarration; exports.parseMotionScene = parseMotionScene; exports.planAnimationToolPlayback = planAnimationToolPlayback; exports.resolveMotionCamera = resolveMotionCamera; exports.resolveMotionEditRefs = resolveMotionEditRefs; exports.segmentProgress = segmentProgress; exports.segmentValue = segmentValue; exports.shotShapeProblems = shotShapeProblems; exports.simplifyTimedSamples = simplifyTimedSamples; exports.smoothMotionLegs = smoothMotionLegs; exports.smoothMotionTimes = smoothMotionTimes; exports.suggestName = _chunkEBDZWIOPcjs.suggestName; exports.toFlairEffectDocument = toFlairEffectDocument; exports.toFlairSvgMotion = toFlairSvgMotion; exports.toFlairVectorEffect = toFlairVectorEffect; exports.toLottie = toLottie; exports.trackValue = trackValue; exports.withAnimationPlayback = withAnimationPlayback; exports.withMotionLayers = withMotionLayers; exports.withViewBox = withViewBox; exports.withoutMotionEffect = withoutMotionEffect;
|