@pixerate/editor 0.70.0 → 0.72.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 +327 -74
- package/dist/animation-editor/index.cjs.map +1 -1
- package/dist/animation-editor/index.d.cts +104 -2
- package/dist/animation-editor/index.d.ts +104 -2
- package/dist/animation-editor/index.js +279 -26
- package/dist/animation-editor/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -56,6 +56,7 @@ var _chunk2FHLHZALcjs = require('../chunk-2FHLHZAL.cjs');
|
|
|
56
56
|
|
|
57
57
|
// src/animation-editor/types.ts
|
|
58
58
|
var MOTION_TRIGGERS = ["click", "hover", "press", "scroll"];
|
|
59
|
+
var MOTION_DRIVEN_PROPERTIES = ["x", "y", "rotation", "scaleX", "scaleY", "opacity"];
|
|
59
60
|
var MOTION_CLIP_TARGET = "$";
|
|
60
61
|
var MOTION_COMMAND_TYPES = [
|
|
61
62
|
"enter",
|
|
@@ -141,6 +142,7 @@ var MOTION_ISSUE_CODES = {
|
|
|
141
142
|
"unknown-path": { severity: "error", wording: "A follow command names a path the artwork does not have." },
|
|
142
143
|
"not-a-path": { severity: "error", wording: "A follow path must be a path node, not a group." },
|
|
143
144
|
"reveal-no-box": { severity: "warning", wording: "A wipe, iris or split cannot clip a node with no width or height (a lone straight line), so it fades instead." },
|
|
145
|
+
"driver-cycle": { severity: "error", wording: "Drivers follow each other in a loop, so none of them has a value." },
|
|
144
146
|
"unknown-clip": { severity: "error", wording: "A play command names a clip the scene does not have." },
|
|
145
147
|
"unknown-shape": { severity: "error", wording: "A morph names a shape the artwork does not have." },
|
|
146
148
|
"match-self": { severity: "error", wording: "A match turns a group into itself or into a group it holds (or that holds it)." },
|
|
@@ -219,7 +221,7 @@ ${problems.map((p) => `- ${p}`).join("\n")}`);
|
|
|
219
221
|
this.problems = problems;
|
|
220
222
|
}
|
|
221
223
|
};
|
|
222
|
-
var SCENE_KEYS = ["version", "name", "description", "artwork", "shots", "clips", "loop", "trigger", "fps", "steps", "beat", "narration", "target", "controls", "presets"];
|
|
224
|
+
var SCENE_KEYS = ["version", "name", "description", "artwork", "shots", "clips", "drivers", "loop", "trigger", "fps", "steps", "beat", "narration", "target", "controls", "presets"];
|
|
223
225
|
var SHOT_KEYS = ["id", "label", "camera", "at", "run", "hold", "speed"];
|
|
224
226
|
var CAMERA_KEYS = ["x", "y", "width", "height", "frame", "padding", "ease", "ms", "geometric"];
|
|
225
227
|
var BASE_KEYS = ["type", "targets", "duration", "delay", "stagger", "with", "ease", "note", "repeat", "repeatMode", "steps"];
|
|
@@ -403,6 +405,23 @@ function clipsShapeProblems(clips) {
|
|
|
403
405
|
}
|
|
404
406
|
return problems;
|
|
405
407
|
}
|
|
408
|
+
function driversShapeProblems(drivers) {
|
|
409
|
+
if (!Array.isArray(drivers)) return ['Scene: "drivers" must be a list of { target, property, source: { node, property }, factor?, offset?, clamp? }.'];
|
|
410
|
+
const problems = [];
|
|
411
|
+
const props = MOTION_DRIVEN_PROPERTIES;
|
|
412
|
+
drivers.forEach((d, i) => {
|
|
413
|
+
const where = `Driver ${i + 1}`;
|
|
414
|
+
if (!isObject(d)) return void problems.push(`${where} must be an object.`);
|
|
415
|
+
unknownKeys(d, ["target", "property", "source", "factor", "offset", "clamp"], where, problems);
|
|
416
|
+
if (typeof d.target !== "string" || !d.target) problems.push(`${where}: "target" must be the id of the node that follows.`);
|
|
417
|
+
if (!props.includes(d.property)) problems.push(`${where}: "property" must be one of ${MOTION_DRIVEN_PROPERTIES.join(", ")}.`);
|
|
418
|
+
if (!isObject(d.source) || typeof d.source.node !== "string" || !d.source.node || !props.includes(d.source.property)) problems.push(`${where}: "source" must be { "node": id, "property": one of ${MOTION_DRIVEN_PROPERTIES.join(", ")} }.`);
|
|
419
|
+
if (d.factor !== void 0 && !isNum(d.factor)) problems.push(`${where}: "factor" must be a number.`);
|
|
420
|
+
if (d.offset !== void 0 && !isNum(d.offset)) problems.push(`${where}: "offset" must be a number.`);
|
|
421
|
+
if (d.clamp !== void 0 && !(Array.isArray(d.clamp) && d.clamp.length === 2 && d.clamp.every(isNum) && d.clamp[0] <= d.clamp[1])) problems.push(`${where}: "clamp" must be [min, max].`);
|
|
422
|
+
});
|
|
423
|
+
return problems;
|
|
424
|
+
}
|
|
406
425
|
function shotShapeProblems(shot, where) {
|
|
407
426
|
if (!isObject(shot)) return [`${where} must be an object.`];
|
|
408
427
|
const problems = [];
|
|
@@ -451,6 +470,7 @@ function motionSceneShapeProblems(input) {
|
|
|
451
470
|
if (!isObject(art) || !isNum(art.width) || !isNum(art.height) || !(art.width > 0) || !(art.height > 0) || !Array.isArray(art.nodes))
|
|
452
471
|
problems.push('Scene: "artwork" must be a vector document { width, height, nodes }.');
|
|
453
472
|
if (input.clips !== void 0) problems.push(...clipsShapeProblems(input.clips));
|
|
473
|
+
if (input.drivers !== void 0) problems.push(...driversShapeProblems(input.drivers));
|
|
454
474
|
if (input.trigger !== void 0 && !MOTION_TRIGGERS.includes(input.trigger)) problems.push(`Scene: "trigger" must be one of ${MOTION_TRIGGERS.join(", ")}.`);
|
|
455
475
|
if (input.loop !== void 0 && !["none", "loop", "ping-pong"].includes(input.loop)) problems.push('Scene: "loop" must be "none", "loop" or "ping-pong".');
|
|
456
476
|
if (input.fps !== void 0 && !(isNum(input.fps) && input.fps > 0 && input.fps <= 120)) problems.push('Scene: "fps" must be a number from 1 to 120.');
|
|
@@ -826,6 +846,41 @@ function motionCueTime(scene, cue) {
|
|
|
826
846
|
if (typeof cue.word === "number") return _optionalChain([scene, 'access', _18 => _18.narration, 'optionalAccess', _19 => _19.words, 'access', _20 => _20[cue.word], 'optionalAccess', _21 => _21.start]);
|
|
827
847
|
return void 0;
|
|
828
848
|
}
|
|
849
|
+
var SCALE_LIKE = /* @__PURE__ */ new Set(["scaleX", "scaleY", "opacity"]);
|
|
850
|
+
function motionPropertyValue(timeline, node, property, time, raw = false, depth = 0) {
|
|
851
|
+
const rest = property === "opacity" ? _nullishCoalesce(_optionalChain([timeline, 'access', _22 => _22.restOpacity, 'optionalAccess', _23 => _23.get, 'call', _24 => _24(node)]), () => ( 1)) : SCALE_LIKE.has(property) ? 1 : 0;
|
|
852
|
+
const v = trackValue(timeline.tracks.get(motionTrackKey(node, property)), time, raw);
|
|
853
|
+
const base = property === "opacity" ? typeof v === "number" ? v : rest : withMotionLayers(timeline, node, property, typeof v === "number" ? v : rest, time, raw);
|
|
854
|
+
return applyMotionDrivers(timeline, node, property, base, time, raw, depth);
|
|
855
|
+
}
|
|
856
|
+
function applyMotionDrivers(timeline, node, property, value, time, raw = false, depth = 0) {
|
|
857
|
+
if (!timeline.drivers || depth > 8) return value;
|
|
858
|
+
let v = value;
|
|
859
|
+
for (const d of timeline.drivers) {
|
|
860
|
+
if (d.target !== node || d.property !== property) continue;
|
|
861
|
+
const src = motionPropertyValue(timeline, d.source.node, d.source.property, time, raw, depth + 1);
|
|
862
|
+
const srcRest = d.source.property === "opacity" ? _nullishCoalesce(_optionalChain([timeline, 'access', _25 => _25.restOpacity, 'optionalAccess', _26 => _26.get, 'call', _27 => _27(d.source.node)]), () => ( 1)) : SCALE_LIKE.has(d.source.property) ? 1 : 0;
|
|
863
|
+
let change = (src - srcRest) * (_nullishCoalesce(d.factor, () => ( 1))) + (_nullishCoalesce(d.offset, () => ( 0)));
|
|
864
|
+
if (d.clamp) change = Math.min(d.clamp[1], Math.max(d.clamp[0], change));
|
|
865
|
+
v = SCALE_LIKE.has(property) ? v * (1 + change) : v + change;
|
|
866
|
+
}
|
|
867
|
+
return property === "opacity" ? Math.min(1, Math.max(0, v)) : v;
|
|
868
|
+
}
|
|
869
|
+
function motionDrivenNodes(timeline) {
|
|
870
|
+
return new Set((_nullishCoalesce(timeline.drivers, () => ( []))).map((d) => d.target));
|
|
871
|
+
}
|
|
872
|
+
function motionDriverSources(timeline, node, properties, seen = /* @__PURE__ */ new Set()) {
|
|
873
|
+
const out = [];
|
|
874
|
+
for (const d of _nullishCoalesce(timeline.drivers, () => ( []))) {
|
|
875
|
+
if (d.target !== node || !properties.includes(d.property)) continue;
|
|
876
|
+
const key = `${d.source.node}\0${d.source.property}`;
|
|
877
|
+
if (seen.has(key)) continue;
|
|
878
|
+
seen.add(key);
|
|
879
|
+
out.push(..._nullishCoalesce(timeline.tracks.get(motionTrackKey(d.source.node, d.source.property)), () => ( [])), ..._nullishCoalesce(timeline.tracks.get(`${motionTrackKey(d.source.node, d.source.property)}\0add`), () => ( [])));
|
|
880
|
+
out.push(...motionDriverSources(timeline, d.source.node, [d.source.property], seen));
|
|
881
|
+
}
|
|
882
|
+
return out;
|
|
883
|
+
}
|
|
829
884
|
function motionCommandRepeat(command2) {
|
|
830
885
|
if (command2.type === "wait" || command2.type === "enter" || command2.type === "exit") return 1;
|
|
831
886
|
const n = _nullishCoalesce(command2.repeat, () => ( 1));
|
|
@@ -835,7 +890,7 @@ function motionCommandDuration(command2, scene, target) {
|
|
|
835
890
|
return motionCycleDuration(command2, scene, target) * motionCommandRepeat(command2);
|
|
836
891
|
}
|
|
837
892
|
function motionClipLayout(scene, clip, target, speed = 1) {
|
|
838
|
-
const run = _optionalChain([scene, 'access',
|
|
893
|
+
const run = _optionalChain([scene, 'access', _28 => _28.clips, 'optionalAccess', _29 => _29[clip], 'optionalAccess', _30 => _30.run]);
|
|
839
894
|
if (!run) return { items: [], length: 0 };
|
|
840
895
|
const f = speed > 0 && Number.isFinite(speed) ? 1 / speed : 1;
|
|
841
896
|
const items = [];
|
|
@@ -877,7 +932,7 @@ function motionCycleDuration(command2, scene, target) {
|
|
|
877
932
|
const ms = length * MOTION_DRAW_MS_PER_UNIT;
|
|
878
933
|
return Math.round(Math.min(MOTION_DRAW_RANGE[1], Math.max(MOTION_DRAW_RANGE[0], ms)));
|
|
879
934
|
}
|
|
880
|
-
if (command2.type === "move" && _optionalChain([command2, 'access',
|
|
935
|
+
if (command2.type === "move" && _optionalChain([command2, 'access', _31 => _31.via, 'optionalAccess', _32 => _32.length])) return MOTION_DEFAULT_DURATIONS.move + 400 * command2.via.length;
|
|
881
936
|
return MOTION_DEFAULT_DURATIONS[command2.type];
|
|
882
937
|
}
|
|
883
938
|
var MOTION_EASE_OUT = { type: "cubic-bezier", x1: 1 / 3, y1: 1, x2: 2 / 3, y2: 1 };
|
|
@@ -1023,11 +1078,11 @@ function compileMotionScene(scene) {
|
|
|
1023
1078
|
const at = nodes.get(p.id);
|
|
1024
1079
|
let first = { x: 0, y: 0 };
|
|
1025
1080
|
try {
|
|
1026
|
-
const m = _chunkYJLCBA7Vcjs.vectorPathWorldBezier.call(void 0, p, _nullishCoalesce(_optionalChain([at, 'optionalAccess',
|
|
1081
|
+
const m = _chunkYJLCBA7Vcjs.vectorPathWorldBezier.call(void 0, p, _nullishCoalesce(_optionalChain([at, 'optionalAccess', _33 => _33.parentWorld]), () => ( _chunkYJLCBA7Vcjs.IDENTITY_MATRIX)), { effects: false })[0];
|
|
1027
1082
|
if (m && m.type === "M") first = { x: m.x, y: m.y };
|
|
1028
1083
|
} catch (e3) {
|
|
1029
1084
|
}
|
|
1030
|
-
return { id: p.id, len: Math.max(1e-6, motionPathLength(p, _optionalChain([at, 'optionalAccess',
|
|
1085
|
+
return { id: p.id, len: Math.max(1e-6, motionPathLength(p, _optionalChain([at, 'optionalAccess', _34 => _34.parentWorld]))), first };
|
|
1031
1086
|
});
|
|
1032
1087
|
if (!list.length) return null;
|
|
1033
1088
|
const span = Math.max(0, e - s);
|
|
@@ -1056,7 +1111,7 @@ function compileMotionScene(scene) {
|
|
|
1056
1111
|
const style = _nullishCoalesce(command2.style, () => ( "fade"));
|
|
1057
1112
|
const opacity = rest("opacity");
|
|
1058
1113
|
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;
|
|
1059
|
-
for (const p of ["clipRect", "clipCircle"]) if (p !== _optionalChain([reveal, 'optionalAccess',
|
|
1114
|
+
for (const p of ["clipRect", "clipCircle"]) if (p !== _optionalChain([reveal, 'optionalAccess', _35 => _35.property]) && tracks.has(motionTrackKey(node, p))) seg(p, rest(p), "linear", start, start);
|
|
1060
1115
|
if (reveal) {
|
|
1061
1116
|
seg("opacity", opacity, "linear", start, start);
|
|
1062
1117
|
seg(reveal.property, reveal.to, _nullishCoalesce(ease, () => ( MOTION_EASE_OUT)), start, end, { from: reveal.from });
|
|
@@ -1132,15 +1187,15 @@ function compileMotionScene(scene) {
|
|
|
1132
1187
|
}
|
|
1133
1188
|
case "move": {
|
|
1134
1189
|
const e = _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT));
|
|
1135
|
-
let dx = _nullishCoalesce(_optionalChain([command2, 'access',
|
|
1136
|
-
let dy = _nullishCoalesce(_optionalChain([command2, 'access',
|
|
1190
|
+
let dx = _nullishCoalesce(_optionalChain([command2, 'access', _36 => _36.by, 'optionalAccess', _37 => _37.x]), () => ( 0));
|
|
1191
|
+
let dy = _nullishCoalesce(_optionalChain([command2, 'access', _38 => _38.by, 'optionalAccess', _39 => _39.y]), () => ( 0));
|
|
1137
1192
|
if (command2.to) {
|
|
1138
1193
|
const inv = _chunkYJLCBA7Vcjs.invertMatrix.call(void 0, entry.parentWorld);
|
|
1139
1194
|
const target = _chunkYJLCBA7Vcjs.applyMatrix.call(void 0, inv, command2.to);
|
|
1140
1195
|
dx = target.x - (entry.center.x + num("x"));
|
|
1141
1196
|
dy = target.y - (entry.center.y + num("y"));
|
|
1142
1197
|
}
|
|
1143
|
-
if (_optionalChain([command2, 'access',
|
|
1198
|
+
if (_optionalChain([command2, 'access', _40 => _40.via, 'optionalAccess', _41 => _41.length])) {
|
|
1144
1199
|
const inv = _chunkYJLCBA7Vcjs.invertMatrix.call(void 0, entry.parentWorld);
|
|
1145
1200
|
const x0 = num("x");
|
|
1146
1201
|
const y0 = num("y");
|
|
@@ -1238,7 +1293,7 @@ function compileMotionScene(scene) {
|
|
|
1238
1293
|
for (const track of tracks.values()) track.sort((a, b) => a.start - b.start);
|
|
1239
1294
|
segments.sort((a, b) => a.start - b.start);
|
|
1240
1295
|
const duration = shots.length ? shots[shots.length - 1].end : 0;
|
|
1241
|
-
const timeline = { duration, shots, commands, segments, tracks };
|
|
1296
|
+
const timeline = { duration, shots, commands, segments, tracks, ..._optionalChain([scene, 'access', _42 => _42.drivers, 'optionalAccess', _43 => _43.length]) ? { drivers: scene.drivers, restOpacity: new Map([...nodes].map(([id, e]) => [id, _nullishCoalesce(e.node.opacity, () => ( 1))])) } : {} };
|
|
1242
1297
|
timelineCache.set(scene, timeline);
|
|
1243
1298
|
return timeline;
|
|
1244
1299
|
}
|
|
@@ -1251,7 +1306,7 @@ var MOTION_DRAW_EFFECT_ID = "~draw";
|
|
|
1251
1306
|
var TRANSFORM_PROPS = ["x", "y", "rotation", "scaleX", "scaleY", "follow"];
|
|
1252
1307
|
var tidy = (v) => Math.round(v * 1e6) / 1e6 || 0;
|
|
1253
1308
|
function applyMotionControlValues(scene, values) {
|
|
1254
|
-
if (!values || !_optionalChain([scene, 'access',
|
|
1309
|
+
if (!values || !_optionalChain([scene, 'access', _44 => _44.controls, 'optionalAccess', _45 => _45.length])) return scene;
|
|
1255
1310
|
const map = /* @__PURE__ */ new Map();
|
|
1256
1311
|
for (const control of scene.controls) {
|
|
1257
1312
|
const value = values[control.name];
|
|
@@ -1297,7 +1352,7 @@ function shapeIn(entry, shape) {
|
|
|
1297
1352
|
}
|
|
1298
1353
|
function shapeAt(entry, timeline, nodes, time) {
|
|
1299
1354
|
const track = timeline.tracks.get(motionTrackKey(entry.node.id, "shape"));
|
|
1300
|
-
if (!_optionalChain([track, 'optionalAccess',
|
|
1355
|
+
if (!_optionalChain([track, 'optionalAccess', _46 => _46.length])) return null;
|
|
1301
1356
|
const s = activeSegment(track, time);
|
|
1302
1357
|
const fromId = String(s ? s.from : track[0].from);
|
|
1303
1358
|
if (!s) return fromId === entry.node.id ? null : nodes.get(fromId) ? shapeIn(entry, nodes.get(fromId)) : null;
|
|
@@ -1320,8 +1375,8 @@ function motionFollowRoute(entry, path) {
|
|
|
1320
1375
|
}
|
|
1321
1376
|
function followAt(entry, timeline, nodes, time) {
|
|
1322
1377
|
const track = timeline.tracks.get(motionTrackKey(entry.node.id, "follow"));
|
|
1323
|
-
const s = _nullishCoalesce(activeSegment(track, time), () => ( _optionalChain([track, 'optionalAccess',
|
|
1324
|
-
if (!_optionalChain([s, 'optionalAccess',
|
|
1378
|
+
const s = _nullishCoalesce(activeSegment(track, time), () => ( _optionalChain([track, 'optionalAccess', _47 => _47[0]])));
|
|
1379
|
+
if (!_optionalChain([s, 'optionalAccess', _48 => _48.path])) return null;
|
|
1325
1380
|
const route = motionFollowRoute(entry, nodes.get(s.path));
|
|
1326
1381
|
const progress = Number(trackValue(track, time));
|
|
1327
1382
|
if (!route || !(progress >= 0)) return null;
|
|
@@ -1336,9 +1391,11 @@ function followAt(entry, timeline, nodes, time) {
|
|
|
1336
1391
|
function motionNodeOffset(entry, timeline, time, nodes = indexMotionNodes([entry.node]), raw = false, follow = true) {
|
|
1337
1392
|
const read = (p, rest) => {
|
|
1338
1393
|
const v = trackValue(timeline.tracks.get(motionTrackKey(entry.node.id, p)), time, raw);
|
|
1339
|
-
|
|
1394
|
+
const layered = withMotionLayers(timeline, entry.node.id, p, typeof v === "number" ? v : rest, time, raw);
|
|
1395
|
+
return p === "follow" ? layered : applyMotionDrivers(timeline, entry.node.id, p, layered, time, raw);
|
|
1340
1396
|
};
|
|
1341
|
-
|
|
1397
|
+
const driven = _optionalChain([timeline, 'access', _49 => _49.drivers, 'optionalAccess', _50 => _50.some, 'call', _51 => _51((d) => d.target === entry.node.id && d.property !== "opacity")]);
|
|
1398
|
+
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;
|
|
1342
1399
|
const dx = read("x", 0);
|
|
1343
1400
|
const dy = read("y", 0);
|
|
1344
1401
|
const rotation = read("rotation", 0);
|
|
@@ -1364,7 +1421,7 @@ function motionFrameAt(scene, time, options = {}) {
|
|
|
1364
1421
|
const nodes = indexMotionNodes(s.artwork.nodes);
|
|
1365
1422
|
const drawOn = options.draw !== false;
|
|
1366
1423
|
const value = (id, p) => trackValue(timeline.tracks.get(motionTrackKey(id, p)), time);
|
|
1367
|
-
const animated = new Set(timeline.segments.map((seg) => seg.node));
|
|
1424
|
+
const animated = /* @__PURE__ */ new Set([...timeline.segments.map((seg) => seg.node), ...motionDrivenNodes(timeline)]);
|
|
1368
1425
|
const clips = {};
|
|
1369
1426
|
const visit = (node) => {
|
|
1370
1427
|
let out = node.type === "group" ? { ...node, children: node.children.map(visit) } : node;
|
|
@@ -1381,7 +1438,7 @@ function motionFrameAt(scene, time, options = {}) {
|
|
|
1381
1438
|
if (out.visible === void 0) delete out.visible;
|
|
1382
1439
|
}
|
|
1383
1440
|
const effectTracks = [...timeline.tracks.keys()].filter((k) => k.startsWith(`${id}\0effect:`));
|
|
1384
|
-
if (effectTracks.length && _optionalChain([out, 'access',
|
|
1441
|
+
if (effectTracks.length && _optionalChain([out, 'access', _52 => _52.effects, 'optionalAccess', _53 => _53.length])) {
|
|
1385
1442
|
out = {
|
|
1386
1443
|
...out,
|
|
1387
1444
|
effects: out.effects.map((effect) => {
|
|
@@ -1421,7 +1478,7 @@ function motionFrameAt(scene, time, options = {}) {
|
|
|
1421
1478
|
return p;
|
|
1422
1479
|
});
|
|
1423
1480
|
}
|
|
1424
|
-
const opacity = value(id, "opacity");
|
|
1481
|
+
const opacity = _optionalChain([timeline, 'access', _54 => _54.drivers, 'optionalAccess', _55 => _55.some, 'call', _56 => _56((d) => d.target === id && d.property === "opacity")]) ? motionPropertyValue(timeline, id, "opacity", time) : value(id, "opacity");
|
|
1425
1482
|
if (typeof opacity === "number") {
|
|
1426
1483
|
const o = Math.min(1, Math.max(0, tidy(opacity)));
|
|
1427
1484
|
out = { ...out, opacity: o };
|
|
@@ -1494,7 +1551,7 @@ function motionPath(scene, id, options = {}) {
|
|
|
1494
1551
|
const r = (n) => Math.round(n * 100) / 100;
|
|
1495
1552
|
const centreAt = (t) => {
|
|
1496
1553
|
const node = indexMotionNodes(motionFrameAtCache(scene, t)).get(id);
|
|
1497
|
-
const b = _optionalChain([node, 'optionalAccess',
|
|
1554
|
+
const b = _optionalChain([node, 'optionalAccess', _57 => _57.bounds]);
|
|
1498
1555
|
return b ? { t: r(t), x: r(b.x + b.width / 2), y: r(b.y + b.height / 2) } : null;
|
|
1499
1556
|
};
|
|
1500
1557
|
const points = [];
|
|
@@ -1534,14 +1591,38 @@ function motionStageSvg(scene, time, options = {}) {
|
|
|
1534
1591
|
const open = current.indexOf(">") + 1;
|
|
1535
1592
|
const close = current.lastIndexOf("</svg>");
|
|
1536
1593
|
const body = current.slice(open, close);
|
|
1537
|
-
const bg = svgOptions.background !== false && scene.artwork.background ? _nullishCoalesce(_optionalChain([/^\s*<rect[^>]*\/>/, 'access',
|
|
1594
|
+
const bg = svgOptions.background !== false && scene.artwork.background ? _nullishCoalesce(_optionalChain([/^\s*<rect[^>]*\/>/, 'access', _58 => _58.exec, 'call', _59 => _59(body), 'optionalAccess', _60 => _60[0]]), () => ( "")) : "";
|
|
1538
1595
|
return `${current.slice(0, open)}${bg}${ghosts.join("")}${body.slice(bg.length)}${paths}</svg>`;
|
|
1539
1596
|
}
|
|
1597
|
+
function motionContactSheetSvg(scene, times, options = {}) {
|
|
1598
|
+
const { columns, labels = true, ...frameOptions } = options;
|
|
1599
|
+
const n = Math.max(1, times.length);
|
|
1600
|
+
const cols = Math.max(1, Math.min(n, Math.floor(_nullishCoalesce(columns, () => ( Math.ceil(Math.sqrt(n)))))));
|
|
1601
|
+
const rows = Math.ceil(n / cols);
|
|
1602
|
+
const { width: w, height: h } = scene.artwork;
|
|
1603
|
+
const gap = Math.round(Math.max(w, h) * 0.04);
|
|
1604
|
+
const label = labels ? Math.round(Math.max(12, h * 0.08)) : 0;
|
|
1605
|
+
const timeline = compileMotionScene(scene);
|
|
1606
|
+
const W = cols * w + (cols + 1) * gap;
|
|
1607
|
+
const H = rows * (h + label) + (rows + 1) * gap;
|
|
1608
|
+
const prefix = _nullishCoalesce(frameOptions.idPrefix, () => ( "vg-"));
|
|
1609
|
+
const cells = times.map((t, i) => {
|
|
1610
|
+
const x = gap + i % cols * (w + gap);
|
|
1611
|
+
const y = gap + Math.floor(i / cols) * (h + label + gap);
|
|
1612
|
+
const frame = motionStageSvg(scene, t, { ...frameOptions, idPrefix: `${prefix}f${i}-` });
|
|
1613
|
+
const view = _nullishCoalesce(_optionalChain([/viewBox="([^"]*)"/, 'access', _61 => _61.exec, 'call', _62 => _62(frame), 'optionalAccess', _63 => _63[1]]), () => ( `0 0 ${w} ${h}`));
|
|
1614
|
+
const inner = innerSvg(frame).replace(new RegExp(` id="(?!${`${prefix}f${i}-`.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")})[^"]*"`, "g"), "");
|
|
1615
|
+
const shot = _nullishCoalesce(timeline.shots.find((s) => t >= s.start && t < s.end), () => ( timeline.shots[timeline.shots.length - 1]));
|
|
1616
|
+
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>` : "";
|
|
1617
|
+
return `<svg x="${x}" y="${y}" width="${w}" height="${h}" viewBox="${view}" overflow="hidden">${inner}</svg><rect x="${x}" y="${y}" width="${w}" height="${h}" fill="none" stroke="#cbd5e1"/>${caption}`;
|
|
1618
|
+
});
|
|
1619
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${W} ${H}" width="${W}" height="${H}"><rect width="${W}" height="${H}" fill="#f8fafc"/>${cells.join("")}</svg>`;
|
|
1620
|
+
}
|
|
1540
1621
|
|
|
1541
1622
|
// src/animation-editor/check.ts
|
|
1542
1623
|
var MOTION_MIN_CHANGE_MS = 120;
|
|
1543
1624
|
var NUMERIC_PARAMS = (node, effectId) => {
|
|
1544
|
-
const effect = _optionalChain([node, 'access',
|
|
1625
|
+
const effect = _optionalChain([node, 'access', _64 => _64.effects, 'optionalAccess', _65 => _65.find, 'call', _66 => _66((e) => e.id === effectId)]);
|
|
1545
1626
|
if (!effect) return null;
|
|
1546
1627
|
return Object.entries(effect.params).filter(([, v]) => typeof v === "number").map(([k]) => k);
|
|
1547
1628
|
};
|
|
@@ -1622,7 +1703,7 @@ function checkMotionScene(scene) {
|
|
|
1622
1703
|
if (command2.type === "follow" && known(command2.path, "unknown-path", "path", where) && nodes.get(command2.path).node.type !== "path")
|
|
1623
1704
|
issues.push(motionIssue("not-a-path", `"${command2.path}" is a group; follow a single path node.`, { ...where, ids: [command2.path] }));
|
|
1624
1705
|
if (command2.type === "morph") known(command2.into, "unknown-shape", "shape", where);
|
|
1625
|
-
if (command2.type === "play" && !_optionalChain([scene, 'access',
|
|
1706
|
+
if (command2.type === "play" && !_optionalChain([scene, 'access', _67 => _67.clips, 'optionalAccess', _68 => _68[command2.clip]]))
|
|
1626
1707
|
issues.push(motionIssue("unknown-clip", `There is no clip ${namedWithSuggestion(command2.clip, Object.keys(_nullishCoalesce(scene.clips, () => ( {}))))}; define it with set_animation_clip.`, where));
|
|
1627
1708
|
if (command2.type === "match" && known(command2.into, "unknown-shape", "group", where)) {
|
|
1628
1709
|
const into = nodes.get(command2.into);
|
|
@@ -1696,6 +1777,28 @@ function checkMotionScene(scene) {
|
|
|
1696
1777
|
}
|
|
1697
1778
|
});
|
|
1698
1779
|
});
|
|
1780
|
+
const drivers = _nullishCoalesce(scene.drivers, () => ( []));
|
|
1781
|
+
drivers.forEach((d, i) => {
|
|
1782
|
+
for (const id of [d.target, d.source.node])
|
|
1783
|
+
if (!nodes.has(id)) issues.push(motionIssue("unknown-target", `Driver ${i + 1} names ${namedWithSuggestion(id, [...nodes.keys()])}, which the artwork does not have.`, { ids: [id] }));
|
|
1784
|
+
});
|
|
1785
|
+
{
|
|
1786
|
+
const key = (n, p) => `${n}.${p}`;
|
|
1787
|
+
const edges = /* @__PURE__ */ new Map();
|
|
1788
|
+
for (const d of drivers) edges.set(key(d.target, d.property), [..._nullishCoalesce(edges.get(key(d.target, d.property)), () => ( [])), key(d.source.node, d.source.property)]);
|
|
1789
|
+
const state = /* @__PURE__ */ new Map();
|
|
1790
|
+
const loop = [];
|
|
1791
|
+
const visit = (k, trail) => {
|
|
1792
|
+
if (state.get(k) === 2) return false;
|
|
1793
|
+
if (state.get(k) === 1) return loop.push(...trail.slice(trail.indexOf(k)), k), true;
|
|
1794
|
+
state.set(k, 1);
|
|
1795
|
+
for (const n of _nullishCoalesce(edges.get(k), () => ( []))) if (visit(n, [...trail, k])) return true;
|
|
1796
|
+
state.set(k, 2);
|
|
1797
|
+
return false;
|
|
1798
|
+
};
|
|
1799
|
+
for (const k of edges.keys()) if (visit(k, [])) break;
|
|
1800
|
+
if (loop.length) issues.push(motionIssue("driver-cycle", `Drivers follow each other round in a loop (${loop.join(" \u2190 ")}); remove one of them.`, { ids: [...new Set(loop.map((k) => k.split(".")[0]))] }));
|
|
1801
|
+
}
|
|
1699
1802
|
for (const [name, clip] of Object.entries(_nullishCoalesce(scene.clips, () => ( {}))))
|
|
1700
1803
|
for (const command2 of clip.run)
|
|
1701
1804
|
if (command2.type !== "wait") {
|
|
@@ -1715,7 +1818,7 @@ function checkMotionScene(scene) {
|
|
|
1715
1818
|
if (!shot.at) return;
|
|
1716
1819
|
const timing = timeline.shots[i];
|
|
1717
1820
|
if (timing.cue === void 0) {
|
|
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',
|
|
1821
|
+
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', _69 => _69.narration, 'optionalAccess', _70 => _70.words, 'access', _71 => _71.length]), () => ( 0))} words (indexes from 0)`;
|
|
1719
1822
|
issues.push(motionIssue("unknown-cue", `Shot "${shot.id}" is cued to ${what}.`, { shot: shot.id }));
|
|
1720
1823
|
} else if (timing.missedBy !== void 0) {
|
|
1721
1824
|
const late = Math.round(timing.missedBy);
|
|
@@ -1748,7 +1851,7 @@ function checkMotionScene(scene) {
|
|
|
1748
1851
|
const cam = frame.camera;
|
|
1749
1852
|
const docIndex = indexMotionNodes(frame.document.nodes);
|
|
1750
1853
|
for (const id of command2.targets) {
|
|
1751
|
-
const b = _optionalChain([docIndex, 'access',
|
|
1854
|
+
const b = _optionalChain([docIndex, 'access', _72 => _72.get, 'call', _73 => _73(id), 'optionalAccess', _74 => _74.bounds]);
|
|
1752
1855
|
if (!b || reported.has(id) || !visibleAt(timeline, id, timing.end, nodes.get(id).node)) continue;
|
|
1753
1856
|
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;
|
|
1754
1857
|
if (outside) {
|
|
@@ -1786,6 +1889,7 @@ var MOTION_EDIT_OPS = [
|
|
|
1786
1889
|
"set_artwork",
|
|
1787
1890
|
"set_controls",
|
|
1788
1891
|
"set_clip",
|
|
1892
|
+
"set_drivers",
|
|
1789
1893
|
"replace_scene"
|
|
1790
1894
|
];
|
|
1791
1895
|
var EditError = class extends Error {
|
|
@@ -1842,6 +1946,13 @@ function applyOne(scene, edit) {
|
|
|
1842
1946
|
else if (edit.target !== void 0) next.target = edit.target;
|
|
1843
1947
|
return next;
|
|
1844
1948
|
}
|
|
1949
|
+
case "set_drivers": {
|
|
1950
|
+
const problems = driversShapeProblems(edit.drivers);
|
|
1951
|
+
if (problems.length) fail(problems.join(" "));
|
|
1952
|
+
const next = { ...scene, drivers: edit.drivers };
|
|
1953
|
+
if (!edit.drivers.length) delete next.drivers;
|
|
1954
|
+
return next;
|
|
1955
|
+
}
|
|
1845
1956
|
case "set_clip": {
|
|
1846
1957
|
if (typeof edit.name !== "string" || !edit.name) fail('Give the clip a "name".');
|
|
1847
1958
|
const clips = { ..._nullishCoalesce(scene.clips, () => ( {})) };
|
|
@@ -1972,9 +2083,9 @@ function applyMotionEdits(scene, edits) {
|
|
|
1972
2083
|
let next = scene;
|
|
1973
2084
|
for (let i = 0; i < list.length; i++) {
|
|
1974
2085
|
const edit = list[i];
|
|
1975
|
-
const prefix = list.length > 1 ? `op ${i + 1} (${_optionalChain([edit, 'optionalAccess',
|
|
2086
|
+
const prefix = list.length > 1 ? `op ${i + 1} (${_optionalChain([edit, 'optionalAccess', _75 => _75.op])}): ` : "";
|
|
1976
2087
|
if (!edit || !MOTION_EDIT_OPS.includes(edit.op))
|
|
1977
|
-
return { ok: false, error: `${prefix}Unknown op ${namedWithSuggestion(String(_optionalChain([edit, 'optionalAccess',
|
|
2088
|
+
return { ok: false, error: `${prefix}Unknown op ${namedWithSuggestion(String(_optionalChain([edit, 'optionalAccess', _76 => _76.op])), MOTION_EDIT_OPS)}. Nothing was changed.` };
|
|
1978
2089
|
try {
|
|
1979
2090
|
next = applyOne(next, edit);
|
|
1980
2091
|
} catch (error) {
|
|
@@ -1990,6 +2101,56 @@ function applyMotionEdits(scene, edits) {
|
|
|
1990
2101
|
if (added.length) return { ok: false, error: `Nothing was changed: ${added.map(motionIssueLine).join(" ")}` };
|
|
1991
2102
|
return { ok: true, scene: next, issues, hints: editHints(before, issues) };
|
|
1992
2103
|
}
|
|
2104
|
+
var REF = /^\$(\d+|last)((?:\.[A-Za-z_]\w*|\[\d+\])+)$/;
|
|
2105
|
+
function resolveMotionEditRefs(scene, edits) {
|
|
2106
|
+
const outcomes = [];
|
|
2107
|
+
const resolved = [];
|
|
2108
|
+
let next = scene;
|
|
2109
|
+
for (let i = 0; i < edits.length; i++) {
|
|
2110
|
+
const where = `op ${i + 1} (${_optionalChain([edits, 'access', _77 => _77[i], 'optionalAccess', _78 => _78.op])})`;
|
|
2111
|
+
let problem = null;
|
|
2112
|
+
const walk = (v) => {
|
|
2113
|
+
if (typeof v === "string") {
|
|
2114
|
+
if (v.startsWith("$$")) return v.slice(1);
|
|
2115
|
+
const m = REF.exec(v);
|
|
2116
|
+
if (!m) return v;
|
|
2117
|
+
const n = m[1] === "last" ? i - 1 : Number(m[1]);
|
|
2118
|
+
if (!(n >= 0 && n < i)) {
|
|
2119
|
+
problem = `${where}: "${v}" refers to op ${n + 1}, but only ops before this one (1 to ${i}) can be referred to.`;
|
|
2120
|
+
return v;
|
|
2121
|
+
}
|
|
2122
|
+
let value = outcomes[n];
|
|
2123
|
+
for (const part of _nullishCoalesce(m[2].match(/\.[A-Za-z_]\w*|\[\d+\]/g), () => ( []))) value = value == null ? void 0 : value[part.startsWith(".") ? part.slice(1) : part.slice(1, -1)];
|
|
2124
|
+
if (value === void 0) problem = `${where}: "${v}" has no value; op ${n + 1} made ${JSON.stringify(outcomes[n])}.`;
|
|
2125
|
+
return value;
|
|
2126
|
+
}
|
|
2127
|
+
if (Array.isArray(v)) return v.map(walk);
|
|
2128
|
+
if (v && typeof v === "object") return Object.fromEntries(Object.entries(v).map(([k, x]) => [k, walk(x)]));
|
|
2129
|
+
return v;
|
|
2130
|
+
};
|
|
2131
|
+
const edit = walk(edits[i]);
|
|
2132
|
+
if (problem) return { ok: false, error: `${problem} Nothing was changed.` };
|
|
2133
|
+
const before = next;
|
|
2134
|
+
try {
|
|
2135
|
+
next = applyOne(next, edit);
|
|
2136
|
+
} catch (error) {
|
|
2137
|
+
if (error instanceof EditError) return { ok: false, error: `${where}: ${error.message} Nothing was changed.` };
|
|
2138
|
+
throw error;
|
|
2139
|
+
}
|
|
2140
|
+
resolved.push(edit);
|
|
2141
|
+
const out = { op: edit.op };
|
|
2142
|
+
if (edit.op === "insert_shots") out.shots = edit.shots.map((s) => s.id);
|
|
2143
|
+
if ("shot" in edit && typeof edit.shot === "string") out.shot = edit.op === "update_shot" && _optionalChain([edit, 'access', _79 => _79.set, 'optionalAccess', _80 => _80.id]) ? edit.set.id : edit.shot;
|
|
2144
|
+
if (edit.op === "add_commands") {
|
|
2145
|
+
const at = _nullishCoalesce(_nullishCoalesce(edit.at, () => ( _optionalChain([before, 'access', _81 => _81.shots, 'access', _82 => _82.find, 'call', _83 => _83((s) => s.id === edit.shot), 'optionalAccess', _84 => _84.run, 'access', _85 => _85.length]))), () => ( 0));
|
|
2146
|
+
out.indexes = edit.commands.map((_, k) => at + k);
|
|
2147
|
+
}
|
|
2148
|
+
if (edit.op === "move_command") out.indexes = [edit.to];
|
|
2149
|
+
if (edit.op === "set_clip") out.clip = edit.name;
|
|
2150
|
+
outcomes.push(out);
|
|
2151
|
+
}
|
|
2152
|
+
return { ok: true, edits: resolved, outcomes };
|
|
2153
|
+
}
|
|
1993
2154
|
function createMotionScene(artwork, init = {}) {
|
|
1994
2155
|
return { version: 1, name: _nullishCoalesce(init.name, () => ( "animation")), ...init, artwork, shots: _nullishCoalesce(init.shots, () => ( [])) };
|
|
1995
2156
|
}
|
|
@@ -2093,7 +2254,13 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
2093
2254
|
if (!entry) return void 0;
|
|
2094
2255
|
return [...entry.ancestors, path.id].find((id) => timeline.tracks.has(motionTrackKey(id, p)));
|
|
2095
2256
|
};
|
|
2096
|
-
const animatedNodes = new Set(timeline.segments.map((s) => s.node).filter(Boolean));
|
|
2257
|
+
const animatedNodes = /* @__PURE__ */ new Set([...timeline.segments.map((s) => s.node).filter(Boolean), ...motionDrivenNodes(timeline)]);
|
|
2258
|
+
const drivenBy = /* @__PURE__ */ new WeakSet();
|
|
2259
|
+
const sourcesOf = (id, props) => motionDriverSources(timeline, id, props).map((s) => {
|
|
2260
|
+
const copy = { ...s };
|
|
2261
|
+
drivenBy.add(copy);
|
|
2262
|
+
return copy;
|
|
2263
|
+
});
|
|
2097
2264
|
const pathsSeen = /* @__PURE__ */ new Set();
|
|
2098
2265
|
const wrap = {};
|
|
2099
2266
|
const routes = [];
|
|
@@ -2134,16 +2301,17 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
2134
2301
|
const wrapper = freshId(`${id}--follow`);
|
|
2135
2302
|
const centre = freshId(`${id}--follow-centre`);
|
|
2136
2303
|
const c = entry.center;
|
|
2137
|
-
const at = _optionalChain([motionNodeOffset, 'call',
|
|
2304
|
+
const at = _optionalChain([motionNodeOffset, 'call', _86 => _86(entry, timeline, 0, nodes), 'optionalAccess', _87 => _87.follow]);
|
|
2138
2305
|
const placed = at ? `translate(${f(at.x)} ${f(at.y)})${at.angle ? ` rotate(${f(at.angle)})` : ""}` : void 0;
|
|
2139
2306
|
(wrap[id] ??= []).push({ id: wrapper, ...placed ? { transform: placed } : {} }, { id: centre, transform: `translate(${f(-c.x)} ${f(-c.y)})` });
|
|
2140
2307
|
follows.push({ id, wrapper, routes: ids, segments: followSegments.filter((seg) => seg.path && ids.has(seg.path)) });
|
|
2141
2308
|
}
|
|
2142
2309
|
}
|
|
2143
|
-
|
|
2144
|
-
|
|
2310
|
+
const opacitySources = sourcesOf(id, ["opacity"]);
|
|
2311
|
+
if (timeline.tracks.has(motionTrackKey(id, "opacity")) || opacitySources.length)
|
|
2312
|
+
channels.push({ id, kind: "attr", attr: "opacity", segments: [...track(id, "opacity"), ...opacitySources], exact: (s) => plays(s) && !drivenBy.has(s), value: (t) => round2(motionPropertyValue(timeline, id, "opacity", t)) });
|
|
2145
2313
|
const followed = follows.some((x) => x.id === id);
|
|
2146
|
-
const transformSegments = TRANSFORM_PROPS2.flatMap((p) => followed && p === "follow" ? [] : [...track(id, p), ..._nullishCoalesce(timeline.tracks.get(motionAddTrackKey(id, p)), () => ( []))]);
|
|
2314
|
+
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"])];
|
|
2147
2315
|
if (transformSegments.length) {
|
|
2148
2316
|
const base = node.transform ? ` matrix(${node.transform.map(f).join(" ")})` : "";
|
|
2149
2317
|
const c = entry.center;
|
|
@@ -2156,7 +2324,7 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
2156
2324
|
kind: "attr",
|
|
2157
2325
|
attr: "transform",
|
|
2158
2326
|
segments: transformSegments,
|
|
2159
|
-
exact: (s) => LINEAR_TRANSFORM.has(s.property) && plays(s),
|
|
2327
|
+
exact: (s) => LINEAR_TRANSFORM.has(s.property) && plays(s) && !drivenBy.has(s),
|
|
2160
2328
|
overshoot: true,
|
|
2161
2329
|
// x, y and the angle are the transform's numbers 0, 1 and 2 (`MotionSegment.index`).
|
|
2162
2330
|
value: (t) => transformAt(t, false),
|
|
@@ -2194,7 +2362,7 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
2194
2362
|
exact: () => false,
|
|
2195
2363
|
value: (t) => {
|
|
2196
2364
|
const frame = motionFrameAt(scene, t, { draw: false, offsets: false });
|
|
2197
|
-
const now = _chunkYJLCBA7Vcjs.expandVectorAppearance.call(void 0, _optionalChain([indexMotionNodes, 'call',
|
|
2365
|
+
const now = _chunkYJLCBA7Vcjs.expandVectorAppearance.call(void 0, _optionalChain([indexMotionNodes, 'call', _88 => _88(frame.document.nodes), 'access', _89 => _89.get, 'call', _90 => _90(id), 'optionalAccess', _91 => _91.node]));
|
|
2198
2366
|
if (!now || now.type !== "path") return "";
|
|
2199
2367
|
const bezier = _chunkO2UH7LZEcjs.transformBezierPath.call(void 0, _chunkYJLCBA7Vcjs.vectorPathWorldBezier.call(void 0, now, _chunkYJLCBA7Vcjs.IDENTITY_MATRIX, { effects: false }), inverse);
|
|
2200
2368
|
return _chunkO2UH7LZEcjs.bezierPathToSvg.call(void 0, bezier, { precision: Math.min(precision, 2) });
|
|
@@ -2215,8 +2383,8 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
2215
2383
|
...delay > 0 ? { delay: round2(delay) } : {},
|
|
2216
2384
|
ms: round2(ms),
|
|
2217
2385
|
ease: written,
|
|
2218
|
-
..._optionalChain([segment, 'optionalAccess',
|
|
2219
|
-
..._optionalChain([segment, 'optionalAccess',
|
|
2386
|
+
..._optionalChain([segment, 'optionalAccess', _92 => _92.behavior]) ? { behavior: segment.behavior } : {},
|
|
2387
|
+
..._optionalChain([segment, 'optionalAccess', _93 => _93.steps]) ? { strobe: segment.steps } : {}
|
|
2220
2388
|
};
|
|
2221
2389
|
if (channel.kind === "view") animate.push({ ...base, view: to });
|
|
2222
2390
|
else if (channel.kind === "draw") animate.push({ ...base, draw: [from, to] });
|
|
@@ -2305,7 +2473,7 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
2305
2473
|
let svg = _chunkYJLCBA7Vcjs.vectorDocumentToSvg.call(void 0, { ...scene.artwork, nodes: _chunkYJLCBA7Vcjs.expandVectorAppearances.call(void 0, scene.artwork.nodes) }, { pretty: false, precision, wrap });
|
|
2306
2474
|
if (routes.length) svg = svg.replace(/^(<svg[^>]*>)/, `$1<defs>${routes.join("")}</defs>`);
|
|
2307
2475
|
let motion = { svg, animate };
|
|
2308
|
-
if (_optionalChain([scene, 'access',
|
|
2476
|
+
if (_optionalChain([scene, 'access', _94 => _94.controls, 'optionalAccess', _95 => _95.length])) {
|
|
2309
2477
|
const declarations = scene.controls.map((c) => `{{${c.name}: color default=${c.default.toUpperCase()}${c.label ? ` label='${c.label.replace(/'/g, "\u2019")}'` : ""}}}`).join(" ");
|
|
2310
2478
|
const swap = (text) => scene.controls.reduce((s, c) => s.replace(new RegExp(`${c.default}(?![0-9a-fA-F])`, "gi"), `{{${c.name}}}`), text);
|
|
2311
2479
|
svg = svg.replace(/^(<svg[^>]*>)/, `$1<!-- ${declarations} -->`);
|
|
@@ -2327,7 +2495,7 @@ function toFlairVectorEffect(scene, options = {}) {
|
|
|
2327
2495
|
tags: _nullishCoalesce(options.tags, () => ( [])),
|
|
2328
2496
|
motion: out.motion,
|
|
2329
2497
|
hold: out.hold,
|
|
2330
|
-
..._optionalChain([scene, 'access',
|
|
2498
|
+
..._optionalChain([scene, 'access', _96 => _96.presets, 'optionalAccess', _97 => _97.length]) ? { presets: scene.presets } : {},
|
|
2331
2499
|
...out.trigger ? { trigger: out.trigger } : {}
|
|
2332
2500
|
};
|
|
2333
2501
|
}
|
|
@@ -2371,7 +2539,7 @@ function resolvePlaceholders(motion, notes) {
|
|
|
2371
2539
|
const filled = text.replace(PLACEHOLDER, (_, name) => {
|
|
2372
2540
|
const d = declared.get(name);
|
|
2373
2541
|
if (!d) unknown.add(name);
|
|
2374
|
-
return (_nullishCoalesce(_optionalChain([d, 'optionalAccess',
|
|
2542
|
+
return (_nullishCoalesce(_optionalChain([d, 'optionalAccess', _98 => _98.value]), () => ( ""))).replace(/"/g, '\\"');
|
|
2375
2543
|
});
|
|
2376
2544
|
for (const name of unknown) notes.push(`Placeholder "{{${name}}}" has no declaration, so it was left empty.`);
|
|
2377
2545
|
return { motion: JSON.parse(filled), controls };
|
|
@@ -2425,7 +2593,7 @@ function commandsFor(anim, nodes, entered, notes) {
|
|
|
2425
2593
|
if (anim.along) {
|
|
2426
2594
|
const path = nodes.get(anim.along);
|
|
2427
2595
|
if (!path || path.node.type !== "path") notes.push(`${where} follows "${anim.along}", which is not a path in the artwork; the follow was skipped.`);
|
|
2428
|
-
else out.push({ type: "follow", targets, path: anim.along, from: _nullishCoalesce(_optionalChain([anim, 'access',
|
|
2596
|
+
else out.push({ type: "follow", targets, path: anim.along, from: _nullishCoalesce(_optionalChain([anim, 'access', _99 => _99.at, 'optionalAccess', _100 => _100[0]]), () => ( 0)), to: _nullishCoalesce(_optionalChain([anim, 'access', _101 => _101.at, 'optionalAccess', _102 => _102[1]]), () => ( 1)), ...anim.orient ? { rotate: true } : {}, ...timing, ...ease(MOTION_EASE_IN_OUT) });
|
|
2429
2597
|
}
|
|
2430
2598
|
for (const [attr, [a, b]] of Object.entries(_nullishCoalesce(anim.attrs, () => ( {})))) {
|
|
2431
2599
|
if (attr === "opacity") {
|
|
@@ -2436,7 +2604,7 @@ function commandsFor(anim, nodes, entered, notes) {
|
|
|
2436
2604
|
out.push({ type: "enter", targets, style: "fade", ...timing, ...ease(MOTION_EASE_OUT) });
|
|
2437
2605
|
} else out.push({ type: "fade", targets, to: Math.min(1, Math.max(0, to)), ...timing, ...ease(MOTION_EASE_IN_OUT) });
|
|
2438
2606
|
} else if (attr === "fill" || attr === "stroke") {
|
|
2439
|
-
if (_optionalChain([behavior, 'optionalAccess',
|
|
2607
|
+
if (_optionalChain([behavior, 'optionalAccess', _103 => _103.type]) === "pulse") out.push({ type: "color-pulse", targets, color: String(b), paint: attr, count: _nullishCoalesce(behavior.count, () => ( 1)), ...timing, ...ease("linear") });
|
|
2440
2608
|
else {
|
|
2441
2609
|
if (behavior) notes.push(`${where}: a ${attr} ${behavior.type} became a plain colour change.`);
|
|
2442
2610
|
out.push({ type: "style", targets, [attr]: String(b), ...timing, ...ease(MOTION_EASE_IN_OUT) });
|
|
@@ -2688,16 +2856,22 @@ function toLottie(scene, options = {}) {
|
|
|
2688
2856
|
const tr = STATIC_TR();
|
|
2689
2857
|
const c = [entry.center.x, entry.center.y].map(round2);
|
|
2690
2858
|
const own = (p) => track(id, p);
|
|
2691
|
-
const
|
|
2859
|
+
const sources = motionDriverSources(timeline, id, ["x", "y", "rotation", "scaleX", "scaleY"]);
|
|
2860
|
+
const layered = ["x", "y", "rotation", "scaleX", "scaleY"].some((p) => layers(id, p).length) || sources.length > 0;
|
|
2692
2861
|
const moving = ["x", "y", "rotation", "scaleX", "scaleY", "follow"].some((p) => own(p).length) || layered;
|
|
2693
2862
|
if (moving) {
|
|
2694
2863
|
const mixed = own("follow").length > 0 || layered;
|
|
2695
2864
|
tr.a = { a: 0, k: c };
|
|
2696
|
-
tr.p = animate(mixed ? [...own("x"), ...own("y"), ...own("follow"), ...layers(id, "x"), ...layers(id, "y")] : [...own("x"), ...own("y")], (t) => poseAt(entry, t).p, !mixed);
|
|
2697
|
-
tr.r = animate(mixed ? [...own("rotation"), ...own("follow"), ...layers(id, "rotation")] : own("rotation"), (t) => [poseAt(entry, t).r], !mixed);
|
|
2698
|
-
tr.s = animate(mixed ? [...own("scaleX"), ...own("scaleY"), ...layers(id, "scaleX"), ...layers(id, "scaleY")] : [...own("scaleX"), ...own("scaleY")], (t) => poseAt(entry, t).s, !mixed);
|
|
2865
|
+
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);
|
|
2866
|
+
tr.r = animate(mixed ? [...own("rotation"), ...own("follow"), ...layers(id, "rotation"), ...sources] : own("rotation"), (t) => [poseAt(entry, t).r], !mixed);
|
|
2867
|
+
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);
|
|
2699
2868
|
if (tr.p.a === 0) tr.p = { a: 0, k: poseAt(entry, 0).p.map(round2) };
|
|
2700
2869
|
}
|
|
2870
|
+
const opacitySources = motionDriverSources(timeline, id, ["opacity"]);
|
|
2871
|
+
if (opacitySources.length) {
|
|
2872
|
+
tr.o = animate([...own("opacity"), ...opacitySources], (t) => [motionPropertyValue(timeline, id, "opacity", t) * 100], false);
|
|
2873
|
+
return tr;
|
|
2874
|
+
}
|
|
2701
2875
|
const opacity = own("opacity");
|
|
2702
2876
|
const rest = _nullishCoalesce(entry.node.opacity, () => ( 1));
|
|
2703
2877
|
tr.o = opacity.length ? animate(opacity, (t) => [Math.min(1, Math.max(0, Number(_nullishCoalesce(trackValue(opacity, t), () => ( rest))))) * 100]) : { a: 0, k: round2(rest * 100) };
|
|
@@ -2719,7 +2893,7 @@ function toLottie(scene, options = {}) {
|
|
|
2719
2893
|
const shapeSegments = [...timeline.tracks.entries()].filter(([k]) => k.startsWith(`${path.id}\0`) && (k.endsWith("\0shape") || k.includes("\0effect:"))).flatMap(([, v]) => v);
|
|
2720
2894
|
const geometryAt = (t) => {
|
|
2721
2895
|
if (!shapeSegments.length) return _chunkYJLCBA7Vcjs.vectorPathWorldBezier.call(void 0, path);
|
|
2722
|
-
const now = _optionalChain([indexMotionNodes, 'call',
|
|
2896
|
+
const now = _optionalChain([indexMotionNodes, 'call', _104 => _104(motionFrameAt(scene, t, { draw: false, offsets: false }).document.nodes), 'access', _105 => _105.get, 'call', _106 => _106(path.id), 'optionalAccess', _107 => _107.node]);
|
|
2723
2897
|
return now && now.type === "path" ? _chunkYJLCBA7Vcjs.vectorPathWorldBezier.call(void 0, _chunkYJLCBA7Vcjs.expandVectorAppearances.call(void 0, [now])[0]) : _chunkYJLCBA7Vcjs.vectorPathWorldBezier.call(void 0, path);
|
|
2724
2898
|
};
|
|
2725
2899
|
const rest = lottieContours(_chunkO2UH7LZEcjs.toBezierPath.call(void 0, geometryAt(0)), round2);
|
|
@@ -2767,7 +2941,7 @@ function toLottie(scene, options = {}) {
|
|
|
2767
2941
|
lj: path.strokeLinejoin === "round" ? 2 : path.strokeLinejoin === "bevel" ? 3 : 1,
|
|
2768
2942
|
ml: 4
|
|
2769
2943
|
});
|
|
2770
|
-
if (_optionalChain([path, 'access',
|
|
2944
|
+
if (_optionalChain([path, 'access', _108 => _108.strokeDasharray, 'optionalAccess', _109 => _109.length])) notes.push(`"${path.id}": its dashes are not exported; the stroke is solid.`);
|
|
2771
2945
|
if (path.strokeAlign && path.strokeAlign !== "center") notes.push(`"${path.id}": its stroke is drawn centred (Lottie has no inside or outside strokes).`);
|
|
2772
2946
|
}
|
|
2773
2947
|
const fill = paintOf(path.fill, "fill", path.id);
|
|
@@ -2918,7 +3092,7 @@ var AnimationEditorController = (_class = class {
|
|
|
2918
3092
|
time: p.time,
|
|
2919
3093
|
playing: p.playing,
|
|
2920
3094
|
loop: p.loop,
|
|
2921
|
-
currentShot: _nullishCoalesce(_optionalChain([motionShotAt, 'call',
|
|
3095
|
+
currentShot: _nullishCoalesce(_optionalChain([motionShotAt, 'call', _110 => _110(timeline, p.time), 'optionalAccess', _111 => _111.id]), () => ( null)),
|
|
2922
3096
|
selection: this.selection,
|
|
2923
3097
|
readOnly: this.readOnly,
|
|
2924
3098
|
error: this.error,
|
|
@@ -2957,7 +3131,7 @@ var AnimationEditorController = (_class = class {
|
|
|
2957
3131
|
this.applying = false;
|
|
2958
3132
|
}
|
|
2959
3133
|
this.notify();
|
|
2960
|
-
_optionalChain([this, 'access',
|
|
3134
|
+
_optionalChain([this, 'access', _112 => _112.onChange, 'optionalCall', _113 => _113(scene)]);
|
|
2961
3135
|
}
|
|
2962
3136
|
commit(label, next) {
|
|
2963
3137
|
const before = this.scene;
|
|
@@ -3302,9 +3476,9 @@ var EDIT_OP = {
|
|
|
3302
3476
|
object({ op: { const: "delete_shots", description: "Like delete_shots." }, shots: { type: "array", items: SHOT_ID, minItems: 1, description: "Shot ids." } }, ["op", "shots"]),
|
|
3303
3477
|
object({ op: { const: "move_shot", description: "Like move_shot." }, shot: SHOT_ID, to: { type: "integer", minimum: 0, description: "New position (0 = first)." } }, ["op", "shot", "to"]),
|
|
3304
3478
|
object({ op: { const: "add_commands", description: "Like add_commands." }, 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 shot's run (default: the end)." } }, ["op", "shot", "commands"]),
|
|
3305
|
-
object({ op: { const: "update_command", description: "Like update_command." }, shot: SHOT_ID, command: { type: "integer", minimum: 0, description:
|
|
3306
|
-
object({ op: { const: "move_command", description: "Like move_command." }, shot: SHOT_ID, command: { type: "integer", minimum: 0, description:
|
|
3307
|
-
object({ op: { const: "delete_commands", description: "Like delete_commands." }, shot: SHOT_ID, commands: { type: "array", items: { type: "integer", minimum: 0 }, minItems: 1, description: "Indexes in the shot's run." } }, ["op", "shot", "commands"]),
|
|
3479
|
+
object({ op: { const: "update_command", description: "Like update_command." }, shot: SHOT_ID, command: { anyOf: [{ type: "integer", minimum: 0 }, { type: "string", pattern: "^\\$(\\d+|last)[.\\[]" }], description: `Index in the shot's run, or a reference to an earlier op's outcome such as "$0.indexes[0]".` }, set: { type: "object", description: "Command fields to replace." }, remove: { type: "array", items: { type: "string" }, description: "Command fields to clear." } }, ["op", "shot", "command"]),
|
|
3480
|
+
object({ op: { const: "move_command", description: "Like move_command." }, shot: SHOT_ID, command: { anyOf: [{ type: "integer", minimum: 0 }, { type: "string", pattern: "^\\$(\\d+|last)[.\\[]" }], description: `Index in the shot's run, or a reference to an earlier op's outcome such as "$0.indexes[0]".` }, to: { type: "integer", minimum: 0, description: "New index in the run (0 = first)." } }, ["op", "shot", "command", "to"]),
|
|
3481
|
+
object({ op: { const: "delete_commands", description: "Like delete_commands." }, shot: SHOT_ID, commands: { type: "array", items: { anyOf: [{ type: "integer", minimum: 0 }, { type: "string", pattern: "^\\$(\\d+|last)[.\\[]" }], description: `An index, or a reference to an earlier op's outcome such as "$0.indexes[0]".` }, minItems: 1, description: "Indexes in the shot's run." } }, ["op", "shot", "commands"]),
|
|
3308
3482
|
object({ op: { const: "set_controls", description: "Like set_controls." }, controls: { type: "array", items: CONTROL, description: "Controls (empty clears)." }, presets: { type: "array", items: PRESET, description: "Presets (empty clears)." } }, ["op", "controls"])
|
|
3309
3483
|
]
|
|
3310
3484
|
};
|
|
@@ -3402,6 +3576,7 @@ function createAnimationEditorToolProvider(controller, options = {}) {
|
|
|
3402
3576
|
...includeElements ? { elements: describeMotionElements(s) } : {},
|
|
3403
3577
|
shots: describeMotionShots(s),
|
|
3404
3578
|
...s.clips ? { clips: Object.fromEntries(Object.entries(s.clips).map(([name, c]) => [name, { ...c.description ? { description: c.description } : {}, commands: c.run.length, length: Math.round(motionClipLayout(s, name, "$").length) }])) } : {},
|
|
3579
|
+
...s.drivers ? { drivers: s.drivers } : {},
|
|
3405
3580
|
...s.controls ? { controls: s.controls } : {},
|
|
3406
3581
|
...s.presets ? { presets: s.presets } : {},
|
|
3407
3582
|
issues: issueSummary(controller.getState().issues),
|
|
@@ -3448,7 +3623,7 @@ function createAnimationEditorToolProvider(controller, options = {}) {
|
|
|
3448
3623
|
const renderFrame = {
|
|
3449
3624
|
name: "render_animation_frame",
|
|
3450
3625
|
title: "Render a frame",
|
|
3451
|
-
description: 'Draws the animation at one moment without changing it, to check it: standalone SVG by default, or a PNG image (format "png") when the host can rasterize. Give a time in ms, or a shot and where in it.',
|
|
3626
|
+
description: 'Draws the animation at one moment (or, with times, several moments as one labelled contact sheet) without changing it, to check it: standalone SVG by default, or a PNG image (format "png") when the host can rasterize. Give a time in ms, or a shot and where in it.',
|
|
3452
3627
|
inputSchema: object({
|
|
3453
3628
|
time: MS("Time from the start"),
|
|
3454
3629
|
shot: { ...SHOT_ID, description: 'Render a moment of this shot instead of "time".' },
|
|
@@ -3463,10 +3638,12 @@ function createAnimationEditorToolProvider(controller, options = {}) {
|
|
|
3463
3638
|
[],
|
|
3464
3639
|
"Draw faded frames either side of the moment, to see motion in one picture."
|
|
3465
3640
|
),
|
|
3641
|
+
times: { type: "array", items: { type: "number", minimum: 0 }, minItems: 2, maxItems: 16, description: "Several moments (ms) to draw as one contact sheet, each labelled with its time and shot, instead of one frame. Overrides time and shot." },
|
|
3642
|
+
columns: { type: "integer", minimum: 1, maximum: 8, description: "With times: frames a row (default about square)." },
|
|
3466
3643
|
motion_paths: { anyOf: [{ type: "boolean" }, { type: "array", items: { type: "string", minLength: 1 } }], description: "Draw where nodes travel (dashed, with a dot where each change starts or ends): true for every node whose place changes, or a list of ids." }
|
|
3467
3644
|
}),
|
|
3468
3645
|
annotations: { title: "Render a frame", ...READ },
|
|
3469
|
-
handler: async ({ time, shot, at = "end", format = "svg", onion_skin, motion_paths }) => {
|
|
3646
|
+
handler: async ({ time, shot, at = "end", format = "svg", onion_skin, motion_paths, times, columns }) => {
|
|
3470
3647
|
const s = scene();
|
|
3471
3648
|
let t = _nullishCoalesce(time, () => ( controller.getState().time));
|
|
3472
3649
|
if (shot !== void 0) {
|
|
@@ -3478,7 +3655,18 @@ function createAnimationEditorToolProvider(controller, options = {}) {
|
|
|
3478
3655
|
const known = new Set(describeMotionElements(s).map((e) => e.id));
|
|
3479
3656
|
for (const id of motion_paths) if (!known.has(id)) throw new Error(`There is no element ${namedWithSuggestion(id, known)} to draw a motion path for.`);
|
|
3480
3657
|
}
|
|
3481
|
-
const
|
|
3658
|
+
const overlays = { ...onion_skin ? { onionSkin: onion_skin } : {}, ...motion_paths ? { motionPaths: motion_paths } : {} };
|
|
3659
|
+
if (times) {
|
|
3660
|
+
const sheet = motionContactSheetSvg(s, times, { pretty: false, ...overlays, ...columns ? { columns } : {} });
|
|
3661
|
+
if (format === "png") {
|
|
3662
|
+
if (!options.rasterize) throw new Error('This host cannot make PNG images; call render_animation_frame with format "svg".');
|
|
3663
|
+
const size = /width="(\d+)" height="(\d+)"/.exec(sheet);
|
|
3664
|
+
const image = await options.rasterize(sheet, { width: Number(size[1]), height: Number(size[2]) });
|
|
3665
|
+
return { content: [{ type: "image", data: image.data, mimeType: image.mimeType }] };
|
|
3666
|
+
}
|
|
3667
|
+
return { times, svg: sheet };
|
|
3668
|
+
}
|
|
3669
|
+
const svg = controller.toSvg(t, { pretty: false, ...overlays });
|
|
3482
3670
|
if (format === "png") {
|
|
3483
3671
|
if (!options.rasterize) throw new Error('This host cannot make PNG images; call render_animation_frame with format "svg".');
|
|
3484
3672
|
const image = await options.rasterize(svg, { width: s.artwork.width, height: s.artwork.height });
|
|
@@ -3503,9 +3691,9 @@ function createAnimationEditorToolProvider(controller, options = {}) {
|
|
|
3503
3691
|
handler: ({ from = 0, to, limit = 200 }) => {
|
|
3504
3692
|
const s = scene();
|
|
3505
3693
|
const timeline = compileMotionScene(s);
|
|
3506
|
-
const words = _nullishCoalesce(_optionalChain([s, 'access',
|
|
3694
|
+
const words = _nullishCoalesce(_optionalChain([s, 'access', _114 => _114.narration, 'optionalAccess', _115 => _115.words]), () => ( []));
|
|
3507
3695
|
const end = _nullishCoalesce(to, () => ( Math.max(timeline.duration, words.length ? _nullishCoalesce(words[words.length - 1].end, () => ( words[words.length - 1].start)) : 0)));
|
|
3508
|
-
const shotAt = (ms) => _optionalChain([timeline, 'access',
|
|
3696
|
+
const shotAt = (ms) => _optionalChain([timeline, 'access', _116 => _116.shots, 'access', _117 => _117.find, 'call', _118 => _118((x) => ms >= x.start && ms < x.end), 'optionalAccess', _119 => _119.id]);
|
|
3509
3697
|
const beats = [];
|
|
3510
3698
|
if (s.beat && s.beat.bpm > 0) {
|
|
3511
3699
|
const first = Math.max(0, Math.ceil(motionBeatAt(s.beat, from).beat - 1e-9));
|
|
@@ -3647,6 +3835,36 @@ function createAnimationEditorToolProvider(controller, options = {}) {
|
|
|
3647
3835
|
return apply(edits, "Fit to length", () => report);
|
|
3648
3836
|
}
|
|
3649
3837
|
};
|
|
3838
|
+
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)." };
|
|
3839
|
+
const setDrivers = {
|
|
3840
|
+
name: "set_animation_drivers",
|
|
3841
|
+
title: "Make properties follow others",
|
|
3842
|
+
description: "Replaces the drivers (empty clears them): each makes a target's property follow a source node's property, as a change from rest \u2014 target change = source change \xB7 factor + offset, clamped \u2014 added to offsets and turns, multiplied into scales and opacity, on top of the target's own commands. For parallax (x follows x \xB7 0.5), a wheel turning as it rolls (rotation follows x \xB7 360 / (2\u03C0\xB7radius)), a shadow fading as a ball rises (opacity follows y \xB7 0.005). One undo step; loops between drivers are refused by check_animation. Returns the timing and issues.",
|
|
3843
|
+
inputSchema: object(
|
|
3844
|
+
{
|
|
3845
|
+
drivers: {
|
|
3846
|
+
type: "array",
|
|
3847
|
+
maxItems: 64,
|
|
3848
|
+
items: object(
|
|
3849
|
+
{
|
|
3850
|
+
target: { type: "string", minLength: 1, description: "Element id that follows." },
|
|
3851
|
+
property: PROP,
|
|
3852
|
+
source: object({ node: { type: "string", minLength: 1, description: "Element id it follows." }, property: PROP }, ["node", "property"], "What it follows."),
|
|
3853
|
+
factor: { type: "number", description: "Multiplies the source's change (default 1)." },
|
|
3854
|
+
offset: { type: "number", description: "Added to the change (default 0)." },
|
|
3855
|
+
clamp: { type: "array", items: { type: "number" }, minItems: 2, maxItems: 2, description: "Limits the change to [min, max]." }
|
|
3856
|
+
},
|
|
3857
|
+
["target", "property", "source"],
|
|
3858
|
+
"One property following another."
|
|
3859
|
+
),
|
|
3860
|
+
description: "Every driver (the list replaces the current one)."
|
|
3861
|
+
}
|
|
3862
|
+
},
|
|
3863
|
+
["drivers"]
|
|
3864
|
+
),
|
|
3865
|
+
annotations: { title: "Make properties follow others", ...WRITE_IDEMPOTENT },
|
|
3866
|
+
handler: ({ drivers }) => apply({ op: "set_drivers", drivers }, "Set drivers")
|
|
3867
|
+
};
|
|
3650
3868
|
const setClip = {
|
|
3651
3869
|
name: "set_animation_clip",
|
|
3652
3870
|
title: "Define a reusable clip",
|
|
@@ -3755,7 +3973,7 @@ function createAnimationEditorToolProvider(controller, options = {}) {
|
|
|
3755
3973
|
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"]),
|
|
3756
3974
|
annotations: { title: "Add commands to a shot", ...WRITE },
|
|
3757
3975
|
handler: ({ shot, commands, at }) => {
|
|
3758
|
-
const before = _nullishCoalesce(_optionalChain([scene, 'call',
|
|
3976
|
+
const before = _nullishCoalesce(_optionalChain([scene, 'call', _120 => _120(), 'access', _121 => _121.shots, 'access', _122 => _122.find, 'call', _123 => _123((s) => s.id === shot), 'optionalAccess', _124 => _124.run, 'access', _125 => _125.length]), () => ( 0));
|
|
3759
3977
|
const first = _nullishCoalesce(at, () => ( before));
|
|
3760
3978
|
return apply({ op: "add_commands", shot, commands, at }, "Add commands", (s) => {
|
|
3761
3979
|
const shots = describeMotionShots(s);
|
|
@@ -3822,12 +4040,14 @@ function createAnimationEditorToolProvider(controller, options = {}) {
|
|
|
3822
4040
|
const editAnimation = {
|
|
3823
4041
|
name: "edit_animation",
|
|
3824
4042
|
title: "Make several edits at once",
|
|
3825
|
-
description:
|
|
4043
|
+
description: `Applies several edits in order as one undo step, all or nothing: if one fails, nothing changes and the error names the op ("op 2 (add_commands): \u2026"). A later op can use what an earlier one made: "$0.indexes[0]" is the index of the first command op 1 added, "$last.shot" the shot the op before changed. Returns each op's outcome (shot, shots, indexes, clip), the timing and issues. With dry_run it only reports them.`,
|
|
3826
4044
|
inputSchema: object({ ops: { type: "array", items: EDIT_OP, minItems: 1, description: "Edits, applied in order." }, dry_run: { type: "boolean", description: "Check without applying (default false)." } }, ["ops"]),
|
|
3827
4045
|
annotations: { title: "Make several edits at once", ...WRITE },
|
|
3828
4046
|
handler: ({ ops, dry_run = false }) => {
|
|
3829
|
-
const
|
|
3830
|
-
|
|
4047
|
+
const refs = resolveMotionEditRefs(controller.getScene(), ops);
|
|
4048
|
+
if (!refs.ok) throw new Error(refs.error);
|
|
4049
|
+
const result = controller.apply(refs.edits, "Edit animation", { dryRun: dry_run });
|
|
4050
|
+
return outcome(result, { outcomes: refs.outcomes, ...dry_run ? { dry_run: true, hint: "Nothing was applied; call again without dry_run to apply." } : {} });
|
|
3831
4051
|
}
|
|
3832
4052
|
};
|
|
3833
4053
|
const writeAnimation = {
|
|
@@ -3885,7 +4105,7 @@ function createAnimationEditorToolProvider(controller, options = {}) {
|
|
|
3885
4105
|
}
|
|
3886
4106
|
};
|
|
3887
4107
|
const reads = [getAnimation, getShot, findElements, getMotionPath, getTimingGrid, checkAnimation, renderFrame, exportFlairMotion, exportFlairEffect, exportLottie, seekAnimation];
|
|
3888
|
-
const writes = [setArtworkSvg, setClip, copyAnimation, fitAnimation, importFlairEffect, insertShots, updateShot, deleteShots, moveShot, addCommands, updateCommand, deleteCommands, moveCommand, setSettings, setControls, editAnimation, writeAnimation, undo, redo];
|
|
4108
|
+
const writes = [setArtworkSvg, setDrivers, setClip, copyAnimation, fitAnimation, importFlairEffect, insertShots, updateShot, deleteShots, moveShot, addCommands, updateCommand, deleteCommands, moveCommand, setSettings, setControls, editAnimation, writeAnimation, undo, redo];
|
|
3889
4109
|
return {
|
|
3890
4110
|
id: _nullishCoalesce(options.id, () => ( "animation")),
|
|
3891
4111
|
namespace: options.namespace,
|
|
@@ -3918,6 +4138,7 @@ Commands
|
|
|
3918
4138
|
- 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.
|
|
3919
4139
|
- 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
4140
|
- 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.
|
|
4141
|
+
- Tie properties together with set_animation_drivers: parallax layers, a wheel that turns as it rolls, a shadow that fades as something rises. Drivers add to the target's own commands.
|
|
3921
4142
|
- 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.
|
|
3922
4143
|
- 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.
|
|
3923
4144
|
- enter also has zoom (settles from larger) and rise (rises a short way, overshooting a touch); pop and zoom take a starting "scale".
|
|
@@ -3934,7 +4155,8 @@ Commands
|
|
|
3934
4155
|
Working method
|
|
3935
4156
|
1. get_animation to learn the element ids (set_animation_artwork_svg first if there is no artwork: give every element you will animate an id; or import_flair_effect to start from a Flair effect and edit it).
|
|
3936
4157
|
2. Plan a few shots, each one idea. Add them with insert_shots (add_commands, update_command, move_command and delete_commands refine a shot), or edit_animation to make several edits as one step (dry_run first when unsure).
|
|
3937
|
-
|
|
4158
|
+
- In edit_animation, a later op can use what an earlier one made: "$0.indexes[0]" (the first command op 1 added), "$last.shot".
|
|
4159
|
+
3. To hit a length, fit_animation (keep the shots whose pace matters). check_animation and fix every error and warning; render_animation_frame (shot + "end") to look at the result, or with "times" for a contact sheet of several moments; add onion_skin or motion_paths to see the motion in one picture, or get_motion_path for the numbers.
|
|
3938
4160
|
- For something the viewer plays (a button, a toggle, a step-by-step explainer), set "trigger" with set_animation_settings: click plays the next shot on each click and then starts over (make the last shot put things back for a toggle), hover or press plays and rewinds, scroll scrubs.
|
|
3939
4161
|
4. To make a reusable Flair effect, set_animation_controls (colour controls stand for every use of their default colour) and export_flair_effect.
|
|
3940
4162
|
|
|
@@ -3945,7 +4167,7 @@ var TIMING = /* @__PURE__ */ new Set(["duration", "delay", "stagger", "with", "e
|
|
|
3945
4167
|
var COLORS = /* @__PURE__ */ new Set(["fill", "stroke", "color"]);
|
|
3946
4168
|
var NODES = /* @__PURE__ */ new Set(["into", "path"]);
|
|
3947
4169
|
function branch(type) {
|
|
3948
|
-
return (_nullishCoalesce(MOTION_COMMAND_SCHEMA.oneOf, () => ( []))).find((b) => _optionalChain([b, 'access',
|
|
4170
|
+
return (_nullishCoalesce(MOTION_COMMAND_SCHEMA.oneOf, () => ( []))).find((b) => _optionalChain([b, 'access', _126 => _126.properties, 'optionalAccess', _127 => _127.type, 'optionalAccess', _128 => _128.const]) === type);
|
|
3949
4171
|
}
|
|
3950
4172
|
function unitOf(type, name) {
|
|
3951
4173
|
if (name === "duration" || name === "delay" || name === "stagger") return "ms";
|
|
@@ -3980,7 +4202,7 @@ function kindOf(name, schema) {
|
|
|
3980
4202
|
}
|
|
3981
4203
|
function motionCommandFields(type) {
|
|
3982
4204
|
const schema = branch(type);
|
|
3983
|
-
if (!_optionalChain([schema, 'optionalAccess',
|
|
4205
|
+
if (!_optionalChain([schema, 'optionalAccess', _129 => _129.properties])) return [];
|
|
3984
4206
|
const required = new Set(_nullishCoalesce(schema.required, () => ( [])));
|
|
3985
4207
|
const fields = Object.entries(schema.properties).filter(([name]) => name !== "type").map(([name, s]) => {
|
|
3986
4208
|
const kind = kindOf(name, s);
|
|
@@ -4037,7 +4259,7 @@ function defaultMotionCommand(type, targets, scene) {
|
|
|
4037
4259
|
if (type === "wait") return { type: "wait", duration: 500 };
|
|
4038
4260
|
if (!targets.length) return { error: "Pick a target first: click an element on the stage." };
|
|
4039
4261
|
const nodes = [...indexMotionNodes(scene.artwork.nodes).values()];
|
|
4040
|
-
const other = (pred) => _optionalChain([nodes, 'access',
|
|
4262
|
+
const other = (pred) => _optionalChain([nodes, 'access', _130 => _130.find, 'call', _131 => _131((e) => !targets.includes(e.node.id) && pred(e)), 'optionalAccess', _132 => _132.node, 'access', _133 => _133.id]);
|
|
4041
4263
|
const base = { targets: [...targets] };
|
|
4042
4264
|
switch (type) {
|
|
4043
4265
|
case "scale":
|
|
@@ -4068,8 +4290,8 @@ function defaultMotionCommand(type, targets, scene) {
|
|
|
4068
4290
|
}
|
|
4069
4291
|
case "effect": {
|
|
4070
4292
|
for (const id of targets) {
|
|
4071
|
-
const node = _optionalChain([nodes, 'access',
|
|
4072
|
-
for (const effect of _nullishCoalesce(_optionalChain([node, 'optionalAccess',
|
|
4293
|
+
const node = _optionalChain([nodes, 'access', _134 => _134.find, 'call', _135 => _135((e) => e.node.id === id), 'optionalAccess', _136 => _136.node]);
|
|
4294
|
+
for (const effect of _nullishCoalesce(_optionalChain([node, 'optionalAccess', _137 => _137.effects]), () => ( []))) {
|
|
4073
4295
|
const param = Object.entries(effect.params).find(([, v]) => typeof v === "number");
|
|
4074
4296
|
if (param) return { type, ...base, effect: effect.id, param: param[0], to: param[1] + 1 };
|
|
4075
4297
|
}
|
|
@@ -4094,11 +4316,11 @@ function motionCommandRows(scene, shotId) {
|
|
|
4094
4316
|
return scene.shots[index].run.map((command2, i) => {
|
|
4095
4317
|
const t = timeline.commands.find((c) => c.shotIndex === index && c.command === i);
|
|
4096
4318
|
const primitive = motionFlairPrimitive(command2);
|
|
4097
|
-
return { index: i, command: command2, summary: motionCommandSummary(command2), start: _nullishCoalesce(_optionalChain([t, 'optionalAccess',
|
|
4319
|
+
return { index: i, command: command2, summary: motionCommandSummary(command2), start: _nullishCoalesce(_optionalChain([t, 'optionalAccess', _138 => _138.start]), () => ( 0)), end: _nullishCoalesce(_optionalChain([t, 'optionalAccess', _139 => _139.end]), () => ( 0)), ...primitive ? { flairPrimitive: primitive } : {} };
|
|
4098
4320
|
});
|
|
4099
4321
|
}
|
|
4100
4322
|
function duplicateMotionCommandEdit(scene, shot, command2) {
|
|
4101
|
-
const run = _nullishCoalesce(_optionalChain([scene, 'access',
|
|
4323
|
+
const run = _nullishCoalesce(_optionalChain([scene, 'access', _140 => _140.shots, 'access', _141 => _141.find, 'call', _142 => _142((s) => s.id === shot), 'optionalAccess', _143 => _143.run]), () => ( []));
|
|
4102
4324
|
return { op: "add_commands", shot, commands: run[command2] ? [structuredClone(run[command2])] : [], at: command2 + 1 };
|
|
4103
4325
|
}
|
|
4104
4326
|
var MOTION_EASE_CHOICES = [
|
|
@@ -4183,7 +4405,7 @@ function motionCueFromKey(key) {
|
|
|
4183
4405
|
function motionCueChoices(scene, limit = 256) {
|
|
4184
4406
|
const choices = [];
|
|
4185
4407
|
const s = (ms) => `${(ms / 1e3).toFixed(2)} s`;
|
|
4186
|
-
const end = Math.max(compileMotionScene(scene).duration, ..._nullishCoalesce(_optionalChain([scene, 'access',
|
|
4408
|
+
const end = Math.max(compileMotionScene(scene).duration, ..._nullishCoalesce(_optionalChain([scene, 'access', _144 => _144.narration, 'optionalAccess', _145 => _145.words, 'access', _146 => _146.map, 'call', _147 => _147((w) => _nullishCoalesce(w.end, () => ( w.start)))]), () => ( [0]))) + 2e3;
|
|
4187
4409
|
if (scene.beat && scene.beat.bpm > 0) {
|
|
4188
4410
|
for (let beat = 0; choices.length < limit; beat++) {
|
|
4189
4411
|
const time = motionBeatTime(scene.beat, beat);
|
|
@@ -4193,7 +4415,7 @@ function motionCueChoices(scene, limit = 256) {
|
|
|
4193
4415
|
choices.push({ value: `beat:${beat}`, label: `Beat ${beat} (bar ${at.bar}.${at.beatInBar}) \xB7 ${s(time)}`, time });
|
|
4194
4416
|
}
|
|
4195
4417
|
}
|
|
4196
|
-
_optionalChain([scene, 'access',
|
|
4418
|
+
_optionalChain([scene, 'access', _148 => _148.narration, 'optionalAccess', _149 => _149.words, 'access', _150 => _150.forEach, 'call', _151 => _151((w, i) => {
|
|
4197
4419
|
if (choices.length < limit * 2) choices.push({ value: `word:${i}`, label: `Word ${i} \u201C${w.text}\u201D \xB7 ${s(w.start)}`, time: w.start });
|
|
4198
4420
|
})]);
|
|
4199
4421
|
return choices;
|
|
@@ -4202,7 +4424,7 @@ function motionShotCueTime(scene, cue) {
|
|
|
4202
4424
|
return cue ? motionCueTime(scene, cue) : void 0;
|
|
4203
4425
|
}
|
|
4204
4426
|
function formatMotionNarration(narration) {
|
|
4205
|
-
return (_nullishCoalesce(_optionalChain([narration, 'optionalAccess',
|
|
4427
|
+
return (_nullishCoalesce(_optionalChain([narration, 'optionalAccess', _152 => _152.words]), () => ( []))).map((w) => `${w.start} ${w.text}`).join("\n");
|
|
4206
4428
|
}
|
|
4207
4429
|
function parseMotionNarration(text) {
|
|
4208
4430
|
const lines = text.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
@@ -4217,6 +4439,37 @@ function parseMotionNarration(text) {
|
|
|
4217
4439
|
}
|
|
4218
4440
|
return { narration: { words } };
|
|
4219
4441
|
}
|
|
4442
|
+
function formatMotionDrivers(drivers) {
|
|
4443
|
+
return (_nullishCoalesce(drivers, () => ( []))).map((d) => `${d.target}.${d.property} <- ${d.source.node}.${d.source.property}${d.factor !== void 0 && d.factor !== 1 ? ` * ${d.factor}` : ""}${d.offset ? ` ${d.offset < 0 ? "-" : "+"} ${Math.abs(d.offset)}` : ""}${d.clamp ? ` [${d.clamp[0]}, ${d.clamp[1]}]` : ""}`).join("\n");
|
|
4444
|
+
}
|
|
4445
|
+
function parseMotionDrivers(text) {
|
|
4446
|
+
const props = MOTION_DRIVEN_PROPERTIES;
|
|
4447
|
+
const drivers = [];
|
|
4448
|
+
const lines = text.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
4449
|
+
for (const [i, line] of lines.entries()) {
|
|
4450
|
+
const m = /^([\w.-]+)\.(\w+)\s*<-\s*([\w.-]+)\.(\w+)\s*(?:\*\s*(-?[\d.]+(?:e-?\d+)?))?\s*(?:([+-])\s*([\d.]+(?:e-?\d+)?))?\s*(?:\[\s*(-?[\d.]+)\s*,\s*(-?[\d.]+)\s*\])?$/.exec(line);
|
|
4451
|
+
if (!m) return { error: `Line ${i + 1} ("${line}") should read "target.property <- source.property", optionally "* factor", "+ offset" and "[min, max]", e.g. "dot.x <- box.x * 0.5".` };
|
|
4452
|
+
const [, target, property, node, sourceProperty, factor, sign, offset, min, max] = m;
|
|
4453
|
+
for (const p of [property, sourceProperty]) if (!props.includes(p)) return { error: `Line ${i + 1}: "${p}" is not a property drivers use; use ${MOTION_DRIVEN_PROPERTIES.join(", ")}.` };
|
|
4454
|
+
const d = { target, property, source: { node, property: sourceProperty } };
|
|
4455
|
+
if (factor !== void 0) d.factor = Number(factor);
|
|
4456
|
+
if (offset !== void 0) d.offset = (sign === "-" ? -1 : 1) * Number(offset);
|
|
4457
|
+
if (min !== void 0) d.clamp = [Number(min), Number(max)];
|
|
4458
|
+
if ([d.factor, d.offset, ..._nullishCoalesce(d.clamp, () => ( []))].some((n) => n !== void 0 && !Number.isFinite(n))) return { error: `Line ${i + 1} has a number that cannot be read.` };
|
|
4459
|
+
drivers.push(d);
|
|
4460
|
+
}
|
|
4461
|
+
return { drivers };
|
|
4462
|
+
}
|
|
4463
|
+
|
|
4464
|
+
|
|
4465
|
+
|
|
4466
|
+
|
|
4467
|
+
|
|
4468
|
+
|
|
4469
|
+
|
|
4470
|
+
|
|
4471
|
+
|
|
4472
|
+
|
|
4220
4473
|
|
|
4221
4474
|
|
|
4222
4475
|
|
|
@@ -4342,5 +4595,5 @@ function parseMotionNarration(text) {
|
|
|
4342
4595
|
|
|
4343
4596
|
|
|
4344
4597
|
|
|
4345
|
-
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_CLIP_TARGET = MOTION_CLIP_TARGET; exports.MOTION_COMMAND_SCHEMA = MOTION_COMMAND_SCHEMA; exports.MOTION_COMMAND_TYPES = MOTION_COMMAND_TYPES; 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_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 = MOTION_ISSUE_CODES; exports.MOTION_MIN_CHANGE_MS = MOTION_MIN_CHANGE_MS; exports.MOTION_REPEAT_MODES = MOTION_REPEAT_MODES; exports.MOTION_SPEED_RANGE = MOTION_SPEED_RANGE; exports.MOTION_SPLIT_ORIENTATIONS = MOTION_SPLIT_ORIENTATIONS; exports.MOTION_TRIGGERS = MOTION_TRIGGERS; exports.MotionSceneFormatError = MotionSceneFormatError; exports.activeSegment = activeSegment; exports.applyMotionControlValues = applyMotionControlValues; exports.applyMotionEdits = applyMotionEdits; exports.checkMotionScene = checkMotionScene; exports.clipsShapeProblems = clipsShapeProblems; exports.commandShapeProblems = commandShapeProblems; exports.compileMotionScene = compileMotionScene; exports.createAnimationEditorToolProvider = createAnimationEditorToolProvider; exports.createMotionScene = createMotionScene; exports.defaultMotionCommand = defaultMotionCommand; exports.describeMotionElements = describeMotionElements; exports.describeMotionShots = describeMotionShots; exports.duplicateMotionCommandEdit = duplicateMotionCommandEdit; exports.fitMotionRect = fitMotionRect; exports.fitMotionScene = fitMotionScene; exports.fitMotionSceneEdits = fitMotionSceneEdits; exports.flairAtLeast = flairAtLeast; exports.flairCurveFor = flairCurveFor; exports.flairEaseFor = flairEaseFor; exports.formatMotionNarration = formatMotionNarration; exports.freshShotId = freshShotId; exports.fromFlairSvgMotion = fromFlairSvgMotion; exports.fromFlairVectorEffect = fromFlairVectorEffect; exports.highlightMotionSvg = highlightMotionSvg; exports.indexMotionNodes = indexMotionNodes; exports.interpolateMotionValue = interpolateMotionValue; exports.isMotionCommand = isMotionCommand; exports.isMotionShot = isMotionShot; exports.matchMotionNodes = matchMotionNodes; exports.motionAddTrackKey = motionAddTrackKey; exports.motionBeatAt = motionBeatAt; exports.motionBeatTime = motionBeatTime; exports.motionClipAttrs = motionClipAttrs; exports.motionClipLayout = motionClipLayout; exports.motionCommandDuration = motionCommandDuration; exports.motionCommandFields = motionCommandFields; exports.motionCommandRepeat = motionCommandRepeat; exports.motionCommandRows = motionCommandRows; exports.motionCommandSummary = motionCommandSummary; exports.motionCueChoices = motionCueChoices; exports.motionCueFromKey = motionCueFromKey; exports.motionCueKey = motionCueKey; exports.motionCueTime = motionCueTime; exports.motionCycleDuration = motionCycleDuration; 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 = motionIssue; exports.motionIssueKey = motionIssueKey; exports.motionIssueLine = motionIssueLine; exports.motionMovingIds = motionMovingIds; exports.motionNodeAtElement = motionNodeAtElement; exports.motionNodeClips = motionNodeClips; exports.motionNodeOffset = motionNodeOffset; exports.motionPath = motionPath; exports.motionPathLength = motionPathLength; exports.motionPathsSvg = motionPathsSvg; exports.motionRestValue = motionRestValue; exports.motionRevealClip = motionRevealClip; exports.motionRevealPadding = motionRevealPadding; exports.motionRulerTicks = motionRulerTicks; exports.motionSceneShapeProblems = motionSceneShapeProblems; exports.motionSceneToJson = motionSceneToJson; exports.motionShotAt = motionShotAt; exports.motionShotCueTime = motionShotCueTime; exports.motionShotTimeScale = motionShotTimeScale; exports.motionStageSvg = motionStageSvg; exports.motionTrackKey = motionTrackKey; exports.namedWithSuggestion = namedWithSuggestion; exports.parseMotionFieldInput = parseMotionFieldInput; exports.parseMotionNarration = parseMotionNarration; exports.parseMotionScene = parseMotionScene; exports.resolveMotionCamera = resolveMotionCamera; exports.segmentProgress = segmentProgress; exports.segmentValue = segmentValue; exports.shotShapeProblems = shotShapeProblems; exports.smoothMotionLegs = smoothMotionLegs; exports.smoothMotionTimes = smoothMotionTimes; exports.suggestName = suggestName; exports.toFlairEffectDocument = toFlairEffectDocument; exports.toFlairSvgMotion = toFlairSvgMotion; exports.toFlairVectorEffect = toFlairVectorEffect; exports.toLottie = toLottie; exports.trackValue = trackValue; exports.withMotionLayers = withMotionLayers; exports.withViewBox = withViewBox;
|
|
4598
|
+
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_CLIP_TARGET = MOTION_CLIP_TARGET; exports.MOTION_COMMAND_SCHEMA = MOTION_COMMAND_SCHEMA; exports.MOTION_COMMAND_TYPES = MOTION_COMMAND_TYPES; 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 = MOTION_ISSUE_CODES; exports.MOTION_MIN_CHANGE_MS = MOTION_MIN_CHANGE_MS; exports.MOTION_REPEAT_MODES = MOTION_REPEAT_MODES; exports.MOTION_SPEED_RANGE = MOTION_SPEED_RANGE; exports.MOTION_SPLIT_ORIENTATIONS = MOTION_SPLIT_ORIENTATIONS; 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.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.highlightMotionSvg = highlightMotionSvg; exports.indexMotionNodes = indexMotionNodes; exports.interpolateMotionValue = interpolateMotionValue; exports.isMotionCommand = isMotionCommand; exports.isMotionShot = isMotionShot; exports.matchMotionNodes = matchMotionNodes; exports.motionAddTrackKey = motionAddTrackKey; exports.motionBeatAt = motionBeatAt; exports.motionBeatTime = motionBeatTime; exports.motionClipAttrs = motionClipAttrs; exports.motionClipLayout = motionClipLayout; exports.motionCommandDuration = motionCommandDuration; exports.motionCommandFields = motionCommandFields; exports.motionCommandRepeat = motionCommandRepeat; exports.motionCommandRows = motionCommandRows; exports.motionCommandSummary = motionCommandSummary; 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 = motionIssue; exports.motionIssueKey = motionIssueKey; exports.motionIssueLine = motionIssueLine; exports.motionMovingIds = motionMovingIds; exports.motionNodeAtElement = motionNodeAtElement; exports.motionNodeClips = motionNodeClips; exports.motionNodeOffset = motionNodeOffset; 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.motionSceneShapeProblems = motionSceneShapeProblems; exports.motionSceneToJson = motionSceneToJson; exports.motionShotAt = motionShotAt; exports.motionShotCueTime = motionShotCueTime; exports.motionShotTimeScale = motionShotTimeScale; exports.motionStageSvg = motionStageSvg; exports.motionTrackKey = motionTrackKey; exports.namedWithSuggestion = namedWithSuggestion; exports.parseMotionDrivers = parseMotionDrivers; exports.parseMotionFieldInput = parseMotionFieldInput; exports.parseMotionNarration = parseMotionNarration; exports.parseMotionScene = parseMotionScene; exports.resolveMotionCamera = resolveMotionCamera; exports.resolveMotionEditRefs = resolveMotionEditRefs; exports.segmentProgress = segmentProgress; exports.segmentValue = segmentValue; exports.shotShapeProblems = shotShapeProblems; exports.smoothMotionLegs = smoothMotionLegs; exports.smoothMotionTimes = smoothMotionTimes; exports.suggestName = suggestName; exports.toFlairEffectDocument = toFlairEffectDocument; exports.toFlairSvgMotion = toFlairSvgMotion; exports.toFlairVectorEffect = toFlairVectorEffect; exports.toLottie = toLottie; exports.trackValue = trackValue; exports.withMotionLayers = withMotionLayers; exports.withViewBox = withViewBox;
|
|
4346
4599
|
//# sourceMappingURL=index.cjs.map
|