@pixerate/editor 0.69.0 → 0.70.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/animation-editor/index.cjs +136 -58
- package/dist/animation-editor/index.cjs.map +1 -1
- package/dist/animation-editor/index.d.cts +54 -5
- package/dist/animation-editor/index.d.ts +54 -5
- package/dist/animation-editor/index.js +91 -13
- package/dist/animation-editor/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -223,10 +223,11 @@ var SCENE_KEYS = ["version", "name", "description", "artwork", "shots", "clips",
|
|
|
223
223
|
var SHOT_KEYS = ["id", "label", "camera", "at", "run", "hold", "speed"];
|
|
224
224
|
var CAMERA_KEYS = ["x", "y", "width", "height", "frame", "padding", "ease", "ms", "geometric"];
|
|
225
225
|
var BASE_KEYS = ["type", "targets", "duration", "delay", "stagger", "with", "ease", "note", "repeat", "repeatMode", "steps"];
|
|
226
|
+
var DRAW_ORDER_KEYS = ["order", "pace", "align", "sortBy", "origin"];
|
|
226
227
|
var COMMAND_KEYS = {
|
|
227
|
-
enter: [...BASE_KEYS, "style", "direction", "distance", "scale", "shape", "orientation"],
|
|
228
|
-
exit: [...BASE_KEYS, "style", "direction", "distance"],
|
|
229
|
-
draw: [...BASE_KEYS, "from", "to"],
|
|
228
|
+
enter: [...BASE_KEYS, ...DRAW_ORDER_KEYS, "style", "direction", "distance", "scale", "shape", "orientation"],
|
|
229
|
+
exit: [...BASE_KEYS, ...DRAW_ORDER_KEYS, "style", "direction", "distance"],
|
|
230
|
+
draw: [...BASE_KEYS, ...DRAW_ORDER_KEYS, "from", "to", "comet"],
|
|
230
231
|
move: [...BASE_KEYS, "by", "to", "via", "pacing", "blend"],
|
|
231
232
|
rotate: [...BASE_KEYS, "by", "to", "blend"],
|
|
232
233
|
scale: [...BASE_KEYS, "to", "geometric", "blend"],
|
|
@@ -270,6 +271,14 @@ function commandShapeProblems(command2, where) {
|
|
|
270
271
|
if (command2[key] !== void 0 && !isNum(command2[key])) p(`"${key}" must be a number.`);
|
|
271
272
|
};
|
|
272
273
|
if (command2.with !== void 0 && command2.with !== "after" && command2.with !== "previous") p(`"with" must be "after" or "previous".`);
|
|
274
|
+
if (DRAW_ORDER_KEYS.some((k) => command2[k] !== void 0)) {
|
|
275
|
+
if (type === "enter" && command2.style !== "draw" || type === "exit" && command2.style !== "undraw") p(`"order", "pace", "align", "sortBy" and "origin" go with drawing: style "${type === "enter" ? "draw" : "undraw"}".`);
|
|
276
|
+
if (command2.order !== void 0 && command2.order !== "together" && command2.order !== "sequence") p(`"order" must be "together" or "sequence" (one stroke after another).`);
|
|
277
|
+
if (command2.pace !== void 0 && command2.pace !== "time" && command2.pace !== "speed") p(`"pace" must be "time" (each stroke takes the whole time) or "speed" (all at one speed).`);
|
|
278
|
+
if (command2.align !== void 0 && command2.align !== "start" && command2.align !== "end") p(`"align" must be "start" or "end".`);
|
|
279
|
+
if (command2.sortBy !== void 0 && command2.sortBy !== "document" && command2.sortBy !== "distance") p(`"sortBy" must be "document" or "distance".`);
|
|
280
|
+
if (command2.origin !== void 0 && !isPoint(command2.origin)) p(`"origin" must be { "x": number, "y": number } (document units).`);
|
|
281
|
+
}
|
|
273
282
|
if (command2.note !== void 0 && typeof command2.note !== "string") p(`"note" must be a string.`);
|
|
274
283
|
if (type === "wait") {
|
|
275
284
|
if (!isNum(command2.duration) || command2.duration < 0) p(`"duration" must be a number of milliseconds \u2265 0.`);
|
|
@@ -303,6 +312,8 @@ function commandShapeProblems(command2, where) {
|
|
|
303
312
|
case "draw":
|
|
304
313
|
fraction("from");
|
|
305
314
|
fraction("to");
|
|
315
|
+
fraction("comet");
|
|
316
|
+
if (command2.comet !== void 0 && (command2.from !== void 0 || command2.to !== void 0)) p(`"comet" runs the whole path; leave out "from" and "to".`);
|
|
306
317
|
break;
|
|
307
318
|
case "move":
|
|
308
319
|
if (command2.by === void 0 && command2.to === void 0) p(`give "by" (an offset) or "to" (a point).`);
|
|
@@ -1003,19 +1014,58 @@ function compileMotionScene(scene) {
|
|
|
1003
1014
|
return typeof v === "number" ? v : 0;
|
|
1004
1015
|
};
|
|
1005
1016
|
const ease = command2.ease;
|
|
1017
|
+
const drawWindows = (s, e, o) => {
|
|
1018
|
+
const order2 = _nullishCoalesce(o.order, () => ( "together"));
|
|
1019
|
+
const pace = _nullishCoalesce(o.pace, () => ( "time"));
|
|
1020
|
+
if (order2 === "together" && pace === "time") return null;
|
|
1021
|
+
const paths = entry.node.type === "path" ? [entry.node] : _chunkYJLCBA7Vcjs.collectPathNodes.call(void 0, entry.node);
|
|
1022
|
+
let list = paths.map((p) => {
|
|
1023
|
+
const at = nodes.get(p.id);
|
|
1024
|
+
let first = { x: 0, y: 0 };
|
|
1025
|
+
try {
|
|
1026
|
+
const m = _chunkYJLCBA7Vcjs.vectorPathWorldBezier.call(void 0, p, _nullishCoalesce(_optionalChain([at, 'optionalAccess', _27 => _27.parentWorld]), () => ( _chunkYJLCBA7Vcjs.IDENTITY_MATRIX)), { effects: false })[0];
|
|
1027
|
+
if (m && m.type === "M") first = { x: m.x, y: m.y };
|
|
1028
|
+
} catch (e3) {
|
|
1029
|
+
}
|
|
1030
|
+
return { id: p.id, len: Math.max(1e-6, motionPathLength(p, _optionalChain([at, 'optionalAccess', _28 => _28.parentWorld]))), first };
|
|
1031
|
+
});
|
|
1032
|
+
if (!list.length) return null;
|
|
1033
|
+
const span = Math.max(0, e - s);
|
|
1034
|
+
if (order2 === "sequence") {
|
|
1035
|
+
if (o.sortBy === "distance") {
|
|
1036
|
+
const b = entry.bounds;
|
|
1037
|
+
const origin = _nullishCoalesce(o.origin, () => ( (b ? { x: b.x + b.width / 2, y: b.y + b.height / 2 } : { x: 0, y: 0 })));
|
|
1038
|
+
list = [...list].sort((a, c) => Math.hypot(a.first.x - origin.x, a.first.y - origin.y) - Math.hypot(c.first.x - origin.x, c.first.y - origin.y));
|
|
1039
|
+
}
|
|
1040
|
+
const total = list.reduce((sum, p) => sum + p.len, 0);
|
|
1041
|
+
let at = s;
|
|
1042
|
+
return list.map((p) => {
|
|
1043
|
+
const w = { id: p.id, start: at, end: at + span * p.len / total };
|
|
1044
|
+
at = w.end;
|
|
1045
|
+
return w;
|
|
1046
|
+
});
|
|
1047
|
+
}
|
|
1048
|
+
const longest = Math.max(...list.map((p) => p.len));
|
|
1049
|
+
return list.map((p) => {
|
|
1050
|
+
const d = span * p.len / longest;
|
|
1051
|
+
return o.align === "end" ? { id: p.id, start: e - d, end: e } : { id: p.id, start: s, end: s + d };
|
|
1052
|
+
});
|
|
1053
|
+
};
|
|
1006
1054
|
switch (command2.type) {
|
|
1007
1055
|
case "enter": {
|
|
1008
1056
|
const style = _nullishCoalesce(command2.style, () => ( "fade"));
|
|
1009
1057
|
const opacity = rest("opacity");
|
|
1010
1058
|
const reveal = style === "wipe" ? motionRevealClip(entry, { style, direction: command2.direction }) : style === "iris" ? motionRevealClip(entry, { style, shape: command2.shape }) : style === "split" ? motionRevealClip(entry, { style, orientation: command2.orientation }) : null;
|
|
1011
|
-
for (const p of ["clipRect", "clipCircle"]) if (p !== _optionalChain([reveal, 'optionalAccess',
|
|
1059
|
+
for (const p of ["clipRect", "clipCircle"]) if (p !== _optionalChain([reveal, 'optionalAccess', _29 => _29.property]) && tracks.has(motionTrackKey(node, p))) seg(p, rest(p), "linear", start, start);
|
|
1012
1060
|
if (reveal) {
|
|
1013
1061
|
seg("opacity", opacity, "linear", start, start);
|
|
1014
1062
|
seg(reveal.property, reveal.to, _nullishCoalesce(ease, () => ( MOTION_EASE_OUT)), start, end, { from: reveal.from });
|
|
1015
1063
|
break;
|
|
1016
1064
|
}
|
|
1017
1065
|
if (style === "draw") {
|
|
1018
|
-
|
|
1066
|
+
const windows = drawWindows(start, start + duration2 * 0.75, command2);
|
|
1067
|
+
if (windows) for (const w of windows) push({ node: w.id, property: "drawEnd", start: w.start, end: w.end, from: 0, to: 1, ease: _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)), ...where });
|
|
1068
|
+
else seg("drawEnd", 1, _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)), start, start + duration2 * 0.75, { from: 0 });
|
|
1019
1069
|
seg("fillOpacity", 1, MOTION_EASE_OUT, start + duration2 * 0.6, end, { from: 0 });
|
|
1020
1070
|
break;
|
|
1021
1071
|
}
|
|
@@ -1049,7 +1099,9 @@ function compileMotionScene(scene) {
|
|
|
1049
1099
|
if (wipe) seg(wipe.property, wipe.to, _nullishCoalesce(ease, () => ( "ease-in")), start, end, { from: wipe.from });
|
|
1050
1100
|
else if (style === "undraw") {
|
|
1051
1101
|
seg("fillOpacity", 0, "ease-in", start, start + duration2 * 0.4);
|
|
1052
|
-
|
|
1102
|
+
const windows = drawWindows(start, end, command2);
|
|
1103
|
+
if (windows) for (const w of windows) push({ node: w.id, property: "drawEnd", start: w.start, end: w.end, from: valueAt(w.id, "drawEnd", w.start), to: 0, ease: _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)), ...where });
|
|
1104
|
+
else seg("drawEnd", 0, _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)));
|
|
1053
1105
|
} else if (style === "shrink") {
|
|
1054
1106
|
seg("scaleX", 0, _nullishCoalesce(ease, () => ( "ease-in")));
|
|
1055
1107
|
seg("scaleY", 0, _nullishCoalesce(ease, () => ( "ease-in")));
|
|
@@ -1064,21 +1116,31 @@ function compileMotionScene(scene) {
|
|
|
1064
1116
|
break;
|
|
1065
1117
|
}
|
|
1066
1118
|
case "draw": {
|
|
1067
|
-
const
|
|
1068
|
-
|
|
1119
|
+
const e = _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT));
|
|
1120
|
+
const comet = command2.comet !== void 0 ? Math.min(1, Math.max(0, command2.comet)) : void 0;
|
|
1121
|
+
const windows = _nullishCoalesce(drawWindows(start, end, command2), () => ( [{ id: node, start, end }]));
|
|
1122
|
+
for (const w of windows) {
|
|
1123
|
+
if (comet !== void 0) {
|
|
1124
|
+
push({ node: w.id, property: "drawEnd", start: w.start, end: w.end, from: 0, to: 1 + comet, ease: e, ...where });
|
|
1125
|
+
push({ node: w.id, property: "drawStart", start: w.start, end: w.end, from: -comet, to: 1, ease: e, ...where });
|
|
1126
|
+
continue;
|
|
1127
|
+
}
|
|
1128
|
+
const drawn = tracks.has(motionTrackKey(w.id, "drawEnd"));
|
|
1129
|
+
push({ node: w.id, property: "drawEnd", start: w.start, end: w.end, from: _nullishCoalesce(command2.from, () => ( (drawn ? valueAt(w.id, "drawEnd", w.start) : 0))), to: _nullishCoalesce(command2.to, () => ( 1)), ease: e, ...where });
|
|
1130
|
+
}
|
|
1069
1131
|
break;
|
|
1070
1132
|
}
|
|
1071
1133
|
case "move": {
|
|
1072
1134
|
const e = _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT));
|
|
1073
|
-
let dx = _nullishCoalesce(_optionalChain([command2, 'access',
|
|
1074
|
-
let dy = _nullishCoalesce(_optionalChain([command2, 'access',
|
|
1135
|
+
let dx = _nullishCoalesce(_optionalChain([command2, 'access', _30 => _30.by, 'optionalAccess', _31 => _31.x]), () => ( 0));
|
|
1136
|
+
let dy = _nullishCoalesce(_optionalChain([command2, 'access', _32 => _32.by, 'optionalAccess', _33 => _33.y]), () => ( 0));
|
|
1075
1137
|
if (command2.to) {
|
|
1076
1138
|
const inv = _chunkYJLCBA7Vcjs.invertMatrix.call(void 0, entry.parentWorld);
|
|
1077
1139
|
const target = _chunkYJLCBA7Vcjs.applyMatrix.call(void 0, inv, command2.to);
|
|
1078
1140
|
dx = target.x - (entry.center.x + num("x"));
|
|
1079
1141
|
dy = target.y - (entry.center.y + num("y"));
|
|
1080
1142
|
}
|
|
1081
|
-
if (_optionalChain([command2, 'access',
|
|
1143
|
+
if (_optionalChain([command2, 'access', _34 => _34.via, 'optionalAccess', _35 => _35.length])) {
|
|
1082
1144
|
const inv = _chunkYJLCBA7Vcjs.invertMatrix.call(void 0, entry.parentWorld);
|
|
1083
1145
|
const x0 = num("x");
|
|
1084
1146
|
const y0 = num("y");
|
|
@@ -1189,7 +1251,7 @@ var MOTION_DRAW_EFFECT_ID = "~draw";
|
|
|
1189
1251
|
var TRANSFORM_PROPS = ["x", "y", "rotation", "scaleX", "scaleY", "follow"];
|
|
1190
1252
|
var tidy = (v) => Math.round(v * 1e6) / 1e6 || 0;
|
|
1191
1253
|
function applyMotionControlValues(scene, values) {
|
|
1192
|
-
if (!values || !_optionalChain([scene, 'access',
|
|
1254
|
+
if (!values || !_optionalChain([scene, 'access', _36 => _36.controls, 'optionalAccess', _37 => _37.length])) return scene;
|
|
1193
1255
|
const map = /* @__PURE__ */ new Map();
|
|
1194
1256
|
for (const control of scene.controls) {
|
|
1195
1257
|
const value = values[control.name];
|
|
@@ -1235,7 +1297,7 @@ function shapeIn(entry, shape) {
|
|
|
1235
1297
|
}
|
|
1236
1298
|
function shapeAt(entry, timeline, nodes, time) {
|
|
1237
1299
|
const track = timeline.tracks.get(motionTrackKey(entry.node.id, "shape"));
|
|
1238
|
-
if (!_optionalChain([track, 'optionalAccess',
|
|
1300
|
+
if (!_optionalChain([track, 'optionalAccess', _38 => _38.length])) return null;
|
|
1239
1301
|
const s = activeSegment(track, time);
|
|
1240
1302
|
const fromId = String(s ? s.from : track[0].from);
|
|
1241
1303
|
if (!s) return fromId === entry.node.id ? null : nodes.get(fromId) ? shapeIn(entry, nodes.get(fromId)) : null;
|
|
@@ -1252,14 +1314,14 @@ function motionFollowRoute(entry, path) {
|
|
|
1252
1314
|
if (!path || path.node.type !== "path") return null;
|
|
1253
1315
|
try {
|
|
1254
1316
|
return _chunkO2UH7LZEcjs.transformBezierPath.call(void 0, _chunkYJLCBA7Vcjs.vectorPathWorldBezier.call(void 0, path.node, path.parentWorld, { effects: false }), _chunkYJLCBA7Vcjs.invertMatrix.call(void 0, entry.parentWorld));
|
|
1255
|
-
} catch (
|
|
1317
|
+
} catch (e4) {
|
|
1256
1318
|
return null;
|
|
1257
1319
|
}
|
|
1258
1320
|
}
|
|
1259
1321
|
function followAt(entry, timeline, nodes, time) {
|
|
1260
1322
|
const track = timeline.tracks.get(motionTrackKey(entry.node.id, "follow"));
|
|
1261
|
-
const s = _nullishCoalesce(activeSegment(track, time), () => ( _optionalChain([track, 'optionalAccess',
|
|
1262
|
-
if (!_optionalChain([s, 'optionalAccess',
|
|
1323
|
+
const s = _nullishCoalesce(activeSegment(track, time), () => ( _optionalChain([track, 'optionalAccess', _39 => _39[0]])));
|
|
1324
|
+
if (!_optionalChain([s, 'optionalAccess', _40 => _40.path])) return null;
|
|
1263
1325
|
const route = motionFollowRoute(entry, nodes.get(s.path));
|
|
1264
1326
|
const progress = Number(trackValue(track, time));
|
|
1265
1327
|
if (!route || !(progress >= 0)) return null;
|
|
@@ -1267,7 +1329,7 @@ function followAt(entry, timeline, nodes, time) {
|
|
|
1267
1329
|
const at = _chunkO2UH7LZEcjs.bezierPathPointAtLength.call(void 0, route, Math.min(1, Math.max(0, progress)) * _chunkO2UH7LZEcjs.bezierPathLength.call(void 0, route));
|
|
1268
1330
|
if (!at) return null;
|
|
1269
1331
|
return { x: at.point[0], y: at.point[1], angle: s.rotate ? at.angle * 180 / Math.PI : 0 };
|
|
1270
|
-
} catch (
|
|
1332
|
+
} catch (e5) {
|
|
1271
1333
|
return null;
|
|
1272
1334
|
}
|
|
1273
1335
|
}
|
|
@@ -1319,7 +1381,7 @@ function motionFrameAt(scene, time, options = {}) {
|
|
|
1319
1381
|
if (out.visible === void 0) delete out.visible;
|
|
1320
1382
|
}
|
|
1321
1383
|
const effectTracks = [...timeline.tracks.keys()].filter((k) => k.startsWith(`${id}\0effect:`));
|
|
1322
|
-
if (effectTracks.length && _optionalChain([out, 'access',
|
|
1384
|
+
if (effectTracks.length && _optionalChain([out, 'access', _41 => _41.effects, 'optionalAccess', _42 => _42.length])) {
|
|
1323
1385
|
out = {
|
|
1324
1386
|
...out,
|
|
1325
1387
|
effects: out.effects.map((effect) => {
|
|
@@ -1432,7 +1494,7 @@ function motionPath(scene, id, options = {}) {
|
|
|
1432
1494
|
const r = (n) => Math.round(n * 100) / 100;
|
|
1433
1495
|
const centreAt = (t) => {
|
|
1434
1496
|
const node = indexMotionNodes(motionFrameAtCache(scene, t)).get(id);
|
|
1435
|
-
const b = _optionalChain([node, 'optionalAccess',
|
|
1497
|
+
const b = _optionalChain([node, 'optionalAccess', _43 => _43.bounds]);
|
|
1436
1498
|
return b ? { t: r(t), x: r(b.x + b.width / 2), y: r(b.y + b.height / 2) } : null;
|
|
1437
1499
|
};
|
|
1438
1500
|
const points = [];
|
|
@@ -1472,14 +1534,14 @@ function motionStageSvg(scene, time, options = {}) {
|
|
|
1472
1534
|
const open = current.indexOf(">") + 1;
|
|
1473
1535
|
const close = current.lastIndexOf("</svg>");
|
|
1474
1536
|
const body = current.slice(open, close);
|
|
1475
|
-
const bg = svgOptions.background !== false && scene.artwork.background ? _nullishCoalesce(_optionalChain([/^\s*<rect[^>]*\/>/, 'access',
|
|
1537
|
+
const bg = svgOptions.background !== false && scene.artwork.background ? _nullishCoalesce(_optionalChain([/^\s*<rect[^>]*\/>/, 'access', _44 => _44.exec, 'call', _45 => _45(body), 'optionalAccess', _46 => _46[0]]), () => ( "")) : "";
|
|
1476
1538
|
return `${current.slice(0, open)}${bg}${ghosts.join("")}${body.slice(bg.length)}${paths}</svg>`;
|
|
1477
1539
|
}
|
|
1478
1540
|
|
|
1479
1541
|
// src/animation-editor/check.ts
|
|
1480
1542
|
var MOTION_MIN_CHANGE_MS = 120;
|
|
1481
1543
|
var NUMERIC_PARAMS = (node, effectId) => {
|
|
1482
|
-
const effect = _optionalChain([node, 'access',
|
|
1544
|
+
const effect = _optionalChain([node, 'access', _47 => _47.effects, 'optionalAccess', _48 => _48.find, 'call', _49 => _49((e) => e.id === effectId)]);
|
|
1483
1545
|
if (!effect) return null;
|
|
1484
1546
|
return Object.entries(effect.params).filter(([, v]) => typeof v === "number").map(([k]) => k);
|
|
1485
1547
|
};
|
|
@@ -1510,7 +1572,11 @@ function visibleAt(timeline, id, time, node) {
|
|
|
1510
1572
|
const circle = clip("clipCircle");
|
|
1511
1573
|
if (Array.isArray(circle) && circle[2] <= 1e-4) return false;
|
|
1512
1574
|
const hasFill = node.type === "path" ? !!node.fill && node.fill !== "none" : _chunkYJLCBA7Vcjs.collectPathNodes.call(void 0, node).some((p) => p.fill && p.fill !== "none");
|
|
1513
|
-
|
|
1575
|
+
const drawn = num("drawEnd", 1) > 0.01 && (node.type === "path" || _chunkYJLCBA7Vcjs.collectPathNodes.call(void 0, node).length === 0 || _chunkYJLCBA7Vcjs.collectPathNodes.call(void 0, node).some((p) => {
|
|
1576
|
+
const v = trackValue(timeline.tracks.get(motionTrackKey(p.id, "drawEnd")), time);
|
|
1577
|
+
return typeof v !== "number" || v > 0.01;
|
|
1578
|
+
}));
|
|
1579
|
+
return drawn || hasFill && num("fillOpacity", 1) > 0.01;
|
|
1514
1580
|
}
|
|
1515
1581
|
function checkMotionScene(scene) {
|
|
1516
1582
|
const issues = [];
|
|
@@ -1556,7 +1622,7 @@ function checkMotionScene(scene) {
|
|
|
1556
1622
|
if (command2.type === "follow" && known(command2.path, "unknown-path", "path", where) && nodes.get(command2.path).node.type !== "path")
|
|
1557
1623
|
issues.push(motionIssue("not-a-path", `"${command2.path}" is a group; follow a single path node.`, { ...where, ids: [command2.path] }));
|
|
1558
1624
|
if (command2.type === "morph") known(command2.into, "unknown-shape", "shape", where);
|
|
1559
|
-
if (command2.type === "play" && !_optionalChain([scene, 'access',
|
|
1625
|
+
if (command2.type === "play" && !_optionalChain([scene, 'access', _50 => _50.clips, 'optionalAccess', _51 => _51[command2.clip]]))
|
|
1560
1626
|
issues.push(motionIssue("unknown-clip", `There is no clip ${namedWithSuggestion(command2.clip, Object.keys(_nullishCoalesce(scene.clips, () => ( {}))))}; define it with set_animation_clip.`, where));
|
|
1561
1627
|
if (command2.type === "match" && known(command2.into, "unknown-shape", "group", where)) {
|
|
1562
1628
|
const into = nodes.get(command2.into);
|
|
@@ -1649,7 +1715,7 @@ function checkMotionScene(scene) {
|
|
|
1649
1715
|
if (!shot.at) return;
|
|
1650
1716
|
const timing = timeline.shots[i];
|
|
1651
1717
|
if (timing.cue === void 0) {
|
|
1652
|
-
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',
|
|
1718
|
+
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', _52 => _52.narration, 'optionalAccess', _53 => _53.words, 'access', _54 => _54.length]), () => ( 0))} words (indexes from 0)`;
|
|
1653
1719
|
issues.push(motionIssue("unknown-cue", `Shot "${shot.id}" is cued to ${what}.`, { shot: shot.id }));
|
|
1654
1720
|
} else if (timing.missedBy !== void 0) {
|
|
1655
1721
|
const late = Math.round(timing.missedBy);
|
|
@@ -1682,7 +1748,7 @@ function checkMotionScene(scene) {
|
|
|
1682
1748
|
const cam = frame.camera;
|
|
1683
1749
|
const docIndex = indexMotionNodes(frame.document.nodes);
|
|
1684
1750
|
for (const id of command2.targets) {
|
|
1685
|
-
const b = _optionalChain([docIndex, 'access',
|
|
1751
|
+
const b = _optionalChain([docIndex, 'access', _55 => _55.get, 'call', _56 => _56(id), 'optionalAccess', _57 => _57.bounds]);
|
|
1686
1752
|
if (!b || reported.has(id) || !visibleAt(timeline, id, timing.end, nodes.get(id).node)) continue;
|
|
1687
1753
|
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;
|
|
1688
1754
|
if (outside) {
|
|
@@ -1906,9 +1972,9 @@ function applyMotionEdits(scene, edits) {
|
|
|
1906
1972
|
let next = scene;
|
|
1907
1973
|
for (let i = 0; i < list.length; i++) {
|
|
1908
1974
|
const edit = list[i];
|
|
1909
|
-
const prefix = list.length > 1 ? `op ${i + 1} (${_optionalChain([edit, 'optionalAccess',
|
|
1975
|
+
const prefix = list.length > 1 ? `op ${i + 1} (${_optionalChain([edit, 'optionalAccess', _58 => _58.op])}): ` : "";
|
|
1910
1976
|
if (!edit || !MOTION_EDIT_OPS.includes(edit.op))
|
|
1911
|
-
return { ok: false, error: `${prefix}Unknown op ${namedWithSuggestion(String(_optionalChain([edit, 'optionalAccess',
|
|
1977
|
+
return { ok: false, error: `${prefix}Unknown op ${namedWithSuggestion(String(_optionalChain([edit, 'optionalAccess', _59 => _59.op])), MOTION_EDIT_OPS)}. Nothing was changed.` };
|
|
1912
1978
|
try {
|
|
1913
1979
|
next = applyOne(next, edit);
|
|
1914
1980
|
} catch (error) {
|
|
@@ -2068,7 +2134,7 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
2068
2134
|
const wrapper = freshId(`${id}--follow`);
|
|
2069
2135
|
const centre = freshId(`${id}--follow-centre`);
|
|
2070
2136
|
const c = entry.center;
|
|
2071
|
-
const at = _optionalChain([motionNodeOffset, 'call',
|
|
2137
|
+
const at = _optionalChain([motionNodeOffset, 'call', _60 => _60(entry, timeline, 0, nodes), 'optionalAccess', _61 => _61.follow]);
|
|
2072
2138
|
const placed = at ? `translate(${f(at.x)} ${f(at.y)})${at.angle ? ` rotate(${f(at.angle)})` : ""}` : void 0;
|
|
2073
2139
|
(wrap[id] ??= []).push({ id: wrapper, ...placed ? { transform: placed } : {} }, { id: centre, transform: `translate(${f(-c.x)} ${f(-c.y)})` });
|
|
2074
2140
|
follows.push({ id, wrapper, routes: ids, segments: followSegments.filter((seg) => seg.path && ids.has(seg.path)) });
|
|
@@ -2128,7 +2194,7 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
2128
2194
|
exact: () => false,
|
|
2129
2195
|
value: (t) => {
|
|
2130
2196
|
const frame = motionFrameAt(scene, t, { draw: false, offsets: false });
|
|
2131
|
-
const now = _chunkYJLCBA7Vcjs.expandVectorAppearance.call(void 0, _optionalChain([indexMotionNodes, 'call',
|
|
2197
|
+
const now = _chunkYJLCBA7Vcjs.expandVectorAppearance.call(void 0, _optionalChain([indexMotionNodes, 'call', _62 => _62(frame.document.nodes), 'access', _63 => _63.get, 'call', _64 => _64(id), 'optionalAccess', _65 => _65.node]));
|
|
2132
2198
|
if (!now || now.type !== "path") return "";
|
|
2133
2199
|
const bezier = _chunkO2UH7LZEcjs.transformBezierPath.call(void 0, _chunkYJLCBA7Vcjs.vectorPathWorldBezier.call(void 0, now, _chunkYJLCBA7Vcjs.IDENTITY_MATRIX, { effects: false }), inverse);
|
|
2134
2200
|
return _chunkO2UH7LZEcjs.bezierPathToSvg.call(void 0, bezier, { precision: Math.min(precision, 2) });
|
|
@@ -2149,8 +2215,8 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
2149
2215
|
...delay > 0 ? { delay: round2(delay) } : {},
|
|
2150
2216
|
ms: round2(ms),
|
|
2151
2217
|
ease: written,
|
|
2152
|
-
..._optionalChain([segment, 'optionalAccess',
|
|
2153
|
-
..._optionalChain([segment, 'optionalAccess',
|
|
2218
|
+
..._optionalChain([segment, 'optionalAccess', _66 => _66.behavior]) ? { behavior: segment.behavior } : {},
|
|
2219
|
+
..._optionalChain([segment, 'optionalAccess', _67 => _67.steps]) ? { strobe: segment.steps } : {}
|
|
2154
2220
|
};
|
|
2155
2221
|
if (channel.kind === "view") animate.push({ ...base, view: to });
|
|
2156
2222
|
else if (channel.kind === "draw") animate.push({ ...base, draw: [from, to] });
|
|
@@ -2239,7 +2305,7 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
2239
2305
|
let svg = _chunkYJLCBA7Vcjs.vectorDocumentToSvg.call(void 0, { ...scene.artwork, nodes: _chunkYJLCBA7Vcjs.expandVectorAppearances.call(void 0, scene.artwork.nodes) }, { pretty: false, precision, wrap });
|
|
2240
2306
|
if (routes.length) svg = svg.replace(/^(<svg[^>]*>)/, `$1<defs>${routes.join("")}</defs>`);
|
|
2241
2307
|
let motion = { svg, animate };
|
|
2242
|
-
if (_optionalChain([scene, 'access',
|
|
2308
|
+
if (_optionalChain([scene, 'access', _68 => _68.controls, 'optionalAccess', _69 => _69.length])) {
|
|
2243
2309
|
const declarations = scene.controls.map((c) => `{{${c.name}: color default=${c.default.toUpperCase()}${c.label ? ` label='${c.label.replace(/'/g, "\u2019")}'` : ""}}}`).join(" ");
|
|
2244
2310
|
const swap = (text) => scene.controls.reduce((s, c) => s.replace(new RegExp(`${c.default}(?![0-9a-fA-F])`, "gi"), `{{${c.name}}}`), text);
|
|
2245
2311
|
svg = svg.replace(/^(<svg[^>]*>)/, `$1<!-- ${declarations} -->`);
|
|
@@ -2261,7 +2327,7 @@ function toFlairVectorEffect(scene, options = {}) {
|
|
|
2261
2327
|
tags: _nullishCoalesce(options.tags, () => ( [])),
|
|
2262
2328
|
motion: out.motion,
|
|
2263
2329
|
hold: out.hold,
|
|
2264
|
-
..._optionalChain([scene, 'access',
|
|
2330
|
+
..._optionalChain([scene, 'access', _70 => _70.presets, 'optionalAccess', _71 => _71.length]) ? { presets: scene.presets } : {},
|
|
2265
2331
|
...out.trigger ? { trigger: out.trigger } : {}
|
|
2266
2332
|
};
|
|
2267
2333
|
}
|
|
@@ -2305,7 +2371,7 @@ function resolvePlaceholders(motion, notes) {
|
|
|
2305
2371
|
const filled = text.replace(PLACEHOLDER, (_, name) => {
|
|
2306
2372
|
const d = declared.get(name);
|
|
2307
2373
|
if (!d) unknown.add(name);
|
|
2308
|
-
return (_nullishCoalesce(_optionalChain([d, 'optionalAccess',
|
|
2374
|
+
return (_nullishCoalesce(_optionalChain([d, 'optionalAccess', _72 => _72.value]), () => ( ""))).replace(/"/g, '\\"');
|
|
2309
2375
|
});
|
|
2310
2376
|
for (const name of unknown) notes.push(`Placeholder "{{${name}}}" has no declaration, so it was left empty.`);
|
|
2311
2377
|
return { motion: JSON.parse(filled), controls };
|
|
@@ -2359,7 +2425,7 @@ function commandsFor(anim, nodes, entered, notes) {
|
|
|
2359
2425
|
if (anim.along) {
|
|
2360
2426
|
const path = nodes.get(anim.along);
|
|
2361
2427
|
if (!path || path.node.type !== "path") notes.push(`${where} follows "${anim.along}", which is not a path in the artwork; the follow was skipped.`);
|
|
2362
|
-
else out.push({ type: "follow", targets, path: anim.along, from: _nullishCoalesce(_optionalChain([anim, 'access',
|
|
2428
|
+
else out.push({ type: "follow", targets, path: anim.along, from: _nullishCoalesce(_optionalChain([anim, 'access', _73 => _73.at, 'optionalAccess', _74 => _74[0]]), () => ( 0)), to: _nullishCoalesce(_optionalChain([anim, 'access', _75 => _75.at, 'optionalAccess', _76 => _76[1]]), () => ( 1)), ...anim.orient ? { rotate: true } : {}, ...timing, ...ease(MOTION_EASE_IN_OUT) });
|
|
2363
2429
|
}
|
|
2364
2430
|
for (const [attr, [a, b]] of Object.entries(_nullishCoalesce(anim.attrs, () => ( {})))) {
|
|
2365
2431
|
if (attr === "opacity") {
|
|
@@ -2370,7 +2436,7 @@ function commandsFor(anim, nodes, entered, notes) {
|
|
|
2370
2436
|
out.push({ type: "enter", targets, style: "fade", ...timing, ...ease(MOTION_EASE_OUT) });
|
|
2371
2437
|
} else out.push({ type: "fade", targets, to: Math.min(1, Math.max(0, to)), ...timing, ...ease(MOTION_EASE_IN_OUT) });
|
|
2372
2438
|
} else if (attr === "fill" || attr === "stroke") {
|
|
2373
|
-
if (_optionalChain([behavior, 'optionalAccess',
|
|
2439
|
+
if (_optionalChain([behavior, 'optionalAccess', _77 => _77.type]) === "pulse") out.push({ type: "color-pulse", targets, color: String(b), paint: attr, count: _nullishCoalesce(behavior.count, () => ( 1)), ...timing, ...ease("linear") });
|
|
2374
2440
|
else {
|
|
2375
2441
|
if (behavior) notes.push(`${where}: a ${attr} ${behavior.type} became a plain colour change.`);
|
|
2376
2442
|
out.push({ type: "style", targets, [attr]: String(b), ...timing, ...ease(MOTION_EASE_IN_OUT) });
|
|
@@ -2653,7 +2719,7 @@ function toLottie(scene, options = {}) {
|
|
|
2653
2719
|
const shapeSegments = [...timeline.tracks.entries()].filter(([k]) => k.startsWith(`${path.id}\0`) && (k.endsWith("\0shape") || k.includes("\0effect:"))).flatMap(([, v]) => v);
|
|
2654
2720
|
const geometryAt = (t) => {
|
|
2655
2721
|
if (!shapeSegments.length) return _chunkYJLCBA7Vcjs.vectorPathWorldBezier.call(void 0, path);
|
|
2656
|
-
const now = _optionalChain([indexMotionNodes, 'call',
|
|
2722
|
+
const now = _optionalChain([indexMotionNodes, 'call', _78 => _78(motionFrameAt(scene, t, { draw: false, offsets: false }).document.nodes), 'access', _79 => _79.get, 'call', _80 => _80(path.id), 'optionalAccess', _81 => _81.node]);
|
|
2657
2723
|
return now && now.type === "path" ? _chunkYJLCBA7Vcjs.vectorPathWorldBezier.call(void 0, _chunkYJLCBA7Vcjs.expandVectorAppearances.call(void 0, [now])[0]) : _chunkYJLCBA7Vcjs.vectorPathWorldBezier.call(void 0, path);
|
|
2658
2724
|
};
|
|
2659
2725
|
const rest = lottieContours(_chunkO2UH7LZEcjs.toBezierPath.call(void 0, geometryAt(0)), round2);
|
|
@@ -2681,11 +2747,13 @@ function toLottie(scene, options = {}) {
|
|
|
2681
2747
|
const widthFrom = owner(path, "strokeWidth");
|
|
2682
2748
|
const fadeFrom = owner(path, "fillOpacity");
|
|
2683
2749
|
const drawFrom = owner(path, "drawEnd");
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2750
|
+
const tailFrom = owner(path, "drawStart");
|
|
2751
|
+
if (drawFrom || tailFrom) {
|
|
2752
|
+
const pct = (track2, fallback) => (t) => [Math.min(1, Math.max(0, Number(_nullishCoalesce(trackValue(track2, t), () => ( fallback))))) * 100];
|
|
2753
|
+
const head = drawFrom ? track(drawFrom, "drawEnd") : [];
|
|
2754
|
+
const tail = tailFrom ? track(tailFrom, "drawStart") : [];
|
|
2755
|
+
items.push({ ty: "tm", nm: "Draw on", s: tail.length ? animate(tail, pct(tail, 0)) : { a: 0, k: 0 }, e: head.length ? animate(head, pct(head, 1)) : { a: 0, k: 100 }, o: { a: 0, k: 0 }, m: 1 });
|
|
2687
2756
|
}
|
|
2688
|
-
if (owner(path, "drawStart")) notes.push(`"${path.id}": drawing from a start other than 0 is not exported.`);
|
|
2689
2757
|
const stroke = paintOf(path.stroke, "stroke", path.id);
|
|
2690
2758
|
if (stroke || strokeFrom) {
|
|
2691
2759
|
const colour = strokeFrom ? track(strokeFrom, "stroke") : [];
|
|
@@ -2699,7 +2767,7 @@ function toLottie(scene, options = {}) {
|
|
|
2699
2767
|
lj: path.strokeLinejoin === "round" ? 2 : path.strokeLinejoin === "bevel" ? 3 : 1,
|
|
2700
2768
|
ml: 4
|
|
2701
2769
|
});
|
|
2702
|
-
if (_optionalChain([path, 'access',
|
|
2770
|
+
if (_optionalChain([path, 'access', _82 => _82.strokeDasharray, 'optionalAccess', _83 => _83.length])) notes.push(`"${path.id}": its dashes are not exported; the stroke is solid.`);
|
|
2703
2771
|
if (path.strokeAlign && path.strokeAlign !== "center") notes.push(`"${path.id}": its stroke is drawn centred (Lottie has no inside or outside strokes).`);
|
|
2704
2772
|
}
|
|
2705
2773
|
const fill = paintOf(path.fill, "fill", path.id);
|
|
@@ -2850,7 +2918,7 @@ var AnimationEditorController = (_class = class {
|
|
|
2850
2918
|
time: p.time,
|
|
2851
2919
|
playing: p.playing,
|
|
2852
2920
|
loop: p.loop,
|
|
2853
|
-
currentShot: _nullishCoalesce(_optionalChain([motionShotAt, 'call',
|
|
2921
|
+
currentShot: _nullishCoalesce(_optionalChain([motionShotAt, 'call', _84 => _84(timeline, p.time), 'optionalAccess', _85 => _85.id]), () => ( null)),
|
|
2854
2922
|
selection: this.selection,
|
|
2855
2923
|
readOnly: this.readOnly,
|
|
2856
2924
|
error: this.error,
|
|
@@ -2889,7 +2957,7 @@ var AnimationEditorController = (_class = class {
|
|
|
2889
2957
|
this.applying = false;
|
|
2890
2958
|
}
|
|
2891
2959
|
this.notify();
|
|
2892
|
-
_optionalChain([this, 'access',
|
|
2960
|
+
_optionalChain([this, 'access', _86 => _86.onChange, 'optionalCall', _87 => _87(scene)]);
|
|
2893
2961
|
}
|
|
2894
2962
|
commit(label, next) {
|
|
2895
2963
|
const before = this.scene;
|
|
@@ -3071,11 +3139,19 @@ var BLEND = (fallback) => ({
|
|
|
3071
3139
|
enum: [...MOTION_BLENDS],
|
|
3072
3140
|
description: `"add" makes it a layer over the pose that stacks with other changes (a shake on a slide); "replace" makes it a change the next one takes over from (default ${fallback}).`
|
|
3073
3141
|
});
|
|
3142
|
+
var DRAW_ORDER = {
|
|
3143
|
+
order: { type: "string", enum: ["together", "sequence"], description: "For drawing a group or several paths: together (default) or sequence (one stroke after another, each for a time in proportion to its length)." },
|
|
3144
|
+
pace: { type: "string", enum: ["time", "speed"], description: "With together: time (default, every stroke takes the whole time) or speed (all at one speed, so short strokes take less)." },
|
|
3145
|
+
align: { type: "string", enum: ["start", "end"], description: "With pace speed: start them together (default) or finish them together." },
|
|
3146
|
+
sortBy: { type: "string", enum: ["document", "distance"], description: "With sequence: stacking order (default) or distance from origin (a radial reveal)." },
|
|
3147
|
+
origin: object({ x: { type: "number", description: "Document units." }, y: { type: "number", description: "Document units." } }, ["x", "y"], "With sortBy distance: where strokes are drawn out from (default the target's centre).")
|
|
3148
|
+
};
|
|
3074
3149
|
var command = (type, description, extra = {}, required = []) => object({ type: { const: type, description }, ...BASE, ...extra }, ["type", "targets", ...required], description);
|
|
3075
3150
|
var MOTION_COMMAND_SCHEMA = {
|
|
3076
3151
|
description: `A command (one of ${MOTION_COMMAND_TYPES.join(", ")}). Commands in a shot play one after another unless "with": "previous".`,
|
|
3077
3152
|
oneOf: [
|
|
3078
3153
|
command("enter", "Brings hidden targets in; they are hidden before this shot.", {
|
|
3154
|
+
...DRAW_ORDER,
|
|
3079
3155
|
style: {
|
|
3080
3156
|
type: "string",
|
|
3081
3157
|
enum: [...MOTION_ENTER_STYLES],
|
|
@@ -3088,11 +3164,12 @@ var MOTION_COMMAND_SCHEMA = {
|
|
|
3088
3164
|
orientation: { type: "string", enum: [...MOTION_SPLIT_ORIENTATIONS], description: "For split: horizontal (default) opens up and down from a horizontal line through the middle; vertical opens left and right." }
|
|
3089
3165
|
}),
|
|
3090
3166
|
command("exit", "Takes targets out.", {
|
|
3167
|
+
...DRAW_ORDER,
|
|
3091
3168
|
style: { type: "string", enum: [...MOTION_EXIT_STYLES], description: "fade (default), shrink, slide, undraw, or wipe (hides behind a travelling edge through a clip over the target's box: a wipe in, reversed)." },
|
|
3092
3169
|
direction: { type: "string", enum: [...MOTION_DIRECTIONS], description: "For slide: direction of travel (default down). For wipe: the way the edge travels (default right, hiding from the left)." },
|
|
3093
3170
|
distance: { type: "number", description: "For slide: document units (default 40)." }
|
|
3094
3171
|
}),
|
|
3095
|
-
command("draw", "Draws strokes on (or off) to a fraction of their length.", { to: FRACTION("Fraction drawn at the end (default 1)."), from: FRACTION("Fraction drawn at the start (default: what is drawn now).") }),
|
|
3172
|
+
command("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).") }),
|
|
3096
3173
|
command("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.") }),
|
|
3097
3174
|
command("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." } }),
|
|
3098
3175
|
command("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"]),
|
|
@@ -3426,9 +3503,9 @@ function createAnimationEditorToolProvider(controller, options = {}) {
|
|
|
3426
3503
|
handler: ({ from = 0, to, limit = 200 }) => {
|
|
3427
3504
|
const s = scene();
|
|
3428
3505
|
const timeline = compileMotionScene(s);
|
|
3429
|
-
const words = _nullishCoalesce(_optionalChain([s, 'access',
|
|
3506
|
+
const words = _nullishCoalesce(_optionalChain([s, 'access', _88 => _88.narration, 'optionalAccess', _89 => _89.words]), () => ( []));
|
|
3430
3507
|
const end = _nullishCoalesce(to, () => ( Math.max(timeline.duration, words.length ? _nullishCoalesce(words[words.length - 1].end, () => ( words[words.length - 1].start)) : 0)));
|
|
3431
|
-
const shotAt = (ms) => _optionalChain([timeline, 'access',
|
|
3508
|
+
const shotAt = (ms) => _optionalChain([timeline, 'access', _90 => _90.shots, 'access', _91 => _91.find, 'call', _92 => _92((x) => ms >= x.start && ms < x.end), 'optionalAccess', _93 => _93.id]);
|
|
3432
3509
|
const beats = [];
|
|
3433
3510
|
if (s.beat && s.beat.bpm > 0) {
|
|
3434
3511
|
const first = Math.max(0, Math.ceil(motionBeatAt(s.beat, from).beat - 1e-9));
|
|
@@ -3678,7 +3755,7 @@ function createAnimationEditorToolProvider(controller, options = {}) {
|
|
|
3678
3755
|
inputSchema: object({ shot: SHOT_ID, commands: { type: "array", items: MOTION_COMMAND_SCHEMA, minItems: 1, description: "Commands to add." }, at: { type: "integer", minimum: 0, description: "Index in the run (default: the end)." } }, ["shot", "commands"]),
|
|
3679
3756
|
annotations: { title: "Add commands to a shot", ...WRITE },
|
|
3680
3757
|
handler: ({ shot, commands, at }) => {
|
|
3681
|
-
const before = _nullishCoalesce(_optionalChain([scene, 'call',
|
|
3758
|
+
const before = _nullishCoalesce(_optionalChain([scene, 'call', _94 => _94(), 'access', _95 => _95.shots, 'access', _96 => _96.find, 'call', _97 => _97((s) => s.id === shot), 'optionalAccess', _98 => _98.run, 'access', _99 => _99.length]), () => ( 0));
|
|
3682
3759
|
const first = _nullishCoalesce(at, () => ( before));
|
|
3683
3760
|
return apply({ op: "add_commands", shot, commands, at }, "Add commands", (s) => {
|
|
3684
3761
|
const shots = describeMotionShots(s);
|
|
@@ -3840,6 +3917,7 @@ Commands
|
|
|
3840
3917
|
- enter (fade, pop, grow, slide, draw) and exit (fade, shrink, slide, undraw) bring things in and out.
|
|
3841
3918
|
- enter also reveals through a clip over the target's box: wipe (behind an edge travelling "direction", default right), iris (from the centre; "shape": circle or box) and split (from a line through the middle; "orientation": horizontal or vertical). exit wipe hides the same way. Reveals need a width and a height: use draw / undraw for a straight line.
|
|
3842
3919
|
- draw draws strokes on to a fraction; move, rotate, scale and fade change the pose (about the centre); style blends solid colours and stroke width.
|
|
3920
|
+
- Drawing a group of strokes: "order": "sequence" draws them one after another (a signature, a diagram), "sortBy": "distance" from "origin" reveals outward, "pace": "speed" keeps one speed; draw with "comet": 0.2 sends a short stroke along a path and off the end.
|
|
3843
3921
|
- Reuse motion: set_animation_clip defines a clip (commands targeting "$"), and { "type": "play", "clip": name, "targets": [...] } plays it on any nodes (with stagger and speed). copy_animation gives other nodes one node's motion.
|
|
3844
3922
|
- move takes "via" waypoints for a curved, smooth path that passes exactly through each point (steady speed by default); use it instead of chaining moves, which stop at each point.
|
|
3845
3923
|
- enter also has zoom (settles from larger) and rise (rises a short way, overshooting a touch); pop and zoom take a starting "scale".
|
|
@@ -3867,7 +3945,7 @@ var TIMING = /* @__PURE__ */ new Set(["duration", "delay", "stagger", "with", "e
|
|
|
3867
3945
|
var COLORS = /* @__PURE__ */ new Set(["fill", "stroke", "color"]);
|
|
3868
3946
|
var NODES = /* @__PURE__ */ new Set(["into", "path"]);
|
|
3869
3947
|
function branch(type) {
|
|
3870
|
-
return (_nullishCoalesce(MOTION_COMMAND_SCHEMA.oneOf, () => ( []))).find((b) => _optionalChain([b, 'access',
|
|
3948
|
+
return (_nullishCoalesce(MOTION_COMMAND_SCHEMA.oneOf, () => ( []))).find((b) => _optionalChain([b, 'access', _100 => _100.properties, 'optionalAccess', _101 => _101.type, 'optionalAccess', _102 => _102.const]) === type);
|
|
3871
3949
|
}
|
|
3872
3950
|
function unitOf(type, name) {
|
|
3873
3951
|
if (name === "duration" || name === "delay" || name === "stagger") return "ms";
|
|
@@ -3902,7 +3980,7 @@ function kindOf(name, schema) {
|
|
|
3902
3980
|
}
|
|
3903
3981
|
function motionCommandFields(type) {
|
|
3904
3982
|
const schema = branch(type);
|
|
3905
|
-
if (!_optionalChain([schema, 'optionalAccess',
|
|
3983
|
+
if (!_optionalChain([schema, 'optionalAccess', _103 => _103.properties])) return [];
|
|
3906
3984
|
const required = new Set(_nullishCoalesce(schema.required, () => ( [])));
|
|
3907
3985
|
const fields = Object.entries(schema.properties).filter(([name]) => name !== "type").map(([name, s]) => {
|
|
3908
3986
|
const kind = kindOf(name, s);
|
|
@@ -3959,7 +4037,7 @@ function defaultMotionCommand(type, targets, scene) {
|
|
|
3959
4037
|
if (type === "wait") return { type: "wait", duration: 500 };
|
|
3960
4038
|
if (!targets.length) return { error: "Pick a target first: click an element on the stage." };
|
|
3961
4039
|
const nodes = [...indexMotionNodes(scene.artwork.nodes).values()];
|
|
3962
|
-
const other = (pred) => _optionalChain([nodes, 'access',
|
|
4040
|
+
const other = (pred) => _optionalChain([nodes, 'access', _104 => _104.find, 'call', _105 => _105((e) => !targets.includes(e.node.id) && pred(e)), 'optionalAccess', _106 => _106.node, 'access', _107 => _107.id]);
|
|
3963
4041
|
const base = { targets: [...targets] };
|
|
3964
4042
|
switch (type) {
|
|
3965
4043
|
case "scale":
|
|
@@ -3990,8 +4068,8 @@ function defaultMotionCommand(type, targets, scene) {
|
|
|
3990
4068
|
}
|
|
3991
4069
|
case "effect": {
|
|
3992
4070
|
for (const id of targets) {
|
|
3993
|
-
const node = _optionalChain([nodes, 'access',
|
|
3994
|
-
for (const effect of _nullishCoalesce(_optionalChain([node, 'optionalAccess',
|
|
4071
|
+
const node = _optionalChain([nodes, 'access', _108 => _108.find, 'call', _109 => _109((e) => e.node.id === id), 'optionalAccess', _110 => _110.node]);
|
|
4072
|
+
for (const effect of _nullishCoalesce(_optionalChain([node, 'optionalAccess', _111 => _111.effects]), () => ( []))) {
|
|
3995
4073
|
const param = Object.entries(effect.params).find(([, v]) => typeof v === "number");
|
|
3996
4074
|
if (param) return { type, ...base, effect: effect.id, param: param[0], to: param[1] + 1 };
|
|
3997
4075
|
}
|
|
@@ -4016,11 +4094,11 @@ function motionCommandRows(scene, shotId) {
|
|
|
4016
4094
|
return scene.shots[index].run.map((command2, i) => {
|
|
4017
4095
|
const t = timeline.commands.find((c) => c.shotIndex === index && c.command === i);
|
|
4018
4096
|
const primitive = motionFlairPrimitive(command2);
|
|
4019
|
-
return { index: i, command: command2, summary: motionCommandSummary(command2), start: _nullishCoalesce(_optionalChain([t, 'optionalAccess',
|
|
4097
|
+
return { index: i, command: command2, summary: motionCommandSummary(command2), start: _nullishCoalesce(_optionalChain([t, 'optionalAccess', _112 => _112.start]), () => ( 0)), end: _nullishCoalesce(_optionalChain([t, 'optionalAccess', _113 => _113.end]), () => ( 0)), ...primitive ? { flairPrimitive: primitive } : {} };
|
|
4020
4098
|
});
|
|
4021
4099
|
}
|
|
4022
4100
|
function duplicateMotionCommandEdit(scene, shot, command2) {
|
|
4023
|
-
const run = _nullishCoalesce(_optionalChain([scene, 'access',
|
|
4101
|
+
const run = _nullishCoalesce(_optionalChain([scene, 'access', _114 => _114.shots, 'access', _115 => _115.find, 'call', _116 => _116((s) => s.id === shot), 'optionalAccess', _117 => _117.run]), () => ( []));
|
|
4024
4102
|
return { op: "add_commands", shot, commands: run[command2] ? [structuredClone(run[command2])] : [], at: command2 + 1 };
|
|
4025
4103
|
}
|
|
4026
4104
|
var MOTION_EASE_CHOICES = [
|
|
@@ -4105,7 +4183,7 @@ function motionCueFromKey(key) {
|
|
|
4105
4183
|
function motionCueChoices(scene, limit = 256) {
|
|
4106
4184
|
const choices = [];
|
|
4107
4185
|
const s = (ms) => `${(ms / 1e3).toFixed(2)} s`;
|
|
4108
|
-
const end = Math.max(compileMotionScene(scene).duration, ..._nullishCoalesce(_optionalChain([scene, 'access',
|
|
4186
|
+
const end = Math.max(compileMotionScene(scene).duration, ..._nullishCoalesce(_optionalChain([scene, 'access', _118 => _118.narration, 'optionalAccess', _119 => _119.words, 'access', _120 => _120.map, 'call', _121 => _121((w) => _nullishCoalesce(w.end, () => ( w.start)))]), () => ( [0]))) + 2e3;
|
|
4109
4187
|
if (scene.beat && scene.beat.bpm > 0) {
|
|
4110
4188
|
for (let beat = 0; choices.length < limit; beat++) {
|
|
4111
4189
|
const time = motionBeatTime(scene.beat, beat);
|
|
@@ -4115,7 +4193,7 @@ function motionCueChoices(scene, limit = 256) {
|
|
|
4115
4193
|
choices.push({ value: `beat:${beat}`, label: `Beat ${beat} (bar ${at.bar}.${at.beatInBar}) \xB7 ${s(time)}`, time });
|
|
4116
4194
|
}
|
|
4117
4195
|
}
|
|
4118
|
-
_optionalChain([scene, 'access',
|
|
4196
|
+
_optionalChain([scene, 'access', _122 => _122.narration, 'optionalAccess', _123 => _123.words, 'access', _124 => _124.forEach, 'call', _125 => _125((w, i) => {
|
|
4119
4197
|
if (choices.length < limit * 2) choices.push({ value: `word:${i}`, label: `Word ${i} \u201C${w.text}\u201D \xB7 ${s(w.start)}`, time: w.start });
|
|
4120
4198
|
})]);
|
|
4121
4199
|
return choices;
|
|
@@ -4124,7 +4202,7 @@ function motionShotCueTime(scene, cue) {
|
|
|
4124
4202
|
return cue ? motionCueTime(scene, cue) : void 0;
|
|
4125
4203
|
}
|
|
4126
4204
|
function formatMotionNarration(narration) {
|
|
4127
|
-
return (_nullishCoalesce(_optionalChain([narration, 'optionalAccess',
|
|
4205
|
+
return (_nullishCoalesce(_optionalChain([narration, 'optionalAccess', _126 => _126.words]), () => ( []))).map((w) => `${w.start} ${w.text}`).join("\n");
|
|
4128
4206
|
}
|
|
4129
4207
|
function parseMotionNarration(text) {
|
|
4130
4208
|
const lines = text.split("\n").map((l) => l.trim()).filter(Boolean);
|