@pixerate/editor 0.72.0 → 0.74.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/THIRD_PARTY_NOTICES.md +1 -0
- package/dist/animation-editor/index.cjs +786 -325
- package/dist/animation-editor/index.cjs.map +1 -1
- package/dist/animation-editor/index.d.cts +57 -2
- package/dist/animation-editor/index.d.ts +57 -2
- package/dist/animation-editor/index.js +750 -289
- package/dist/animation-editor/index.js.map +1 -1
- package/dist/chart/index.cjs +11 -9
- package/dist/chart/index.cjs.map +1 -1
- package/dist/chart/index.js +5 -3
- package/dist/chart/index.js.map +1 -1
- package/dist/{chunk-3S72F46R.js → chunk-2GFUA4X5.js} +2 -322
- package/dist/chunk-2GFUA4X5.js.map +1 -0
- package/dist/chunk-33AHNSEB.js +323 -0
- package/dist/chunk-33AHNSEB.js.map +1 -0
- package/dist/{chunk-NN3JGUSX.cjs → chunk-5PTR7HTL.cjs} +3 -323
- package/dist/chunk-5PTR7HTL.cjs.map +1 -0
- package/dist/{chunk-4YFMSPBU.js → chunk-EIXXBKUB.js} +6 -4
- package/dist/{chunk-4YFMSPBU.js.map → chunk-EIXXBKUB.js.map} +1 -1
- package/dist/chunk-EMQWJA3G.cjs +323 -0
- package/dist/chunk-EMQWJA3G.cjs.map +1 -0
- package/dist/{chunk-BQQZ6MOI.js → chunk-FGW5OWJ4.js} +6 -4
- package/dist/{chunk-BQQZ6MOI.js.map → chunk-FGW5OWJ4.js.map} +1 -1
- package/dist/{chunk-YZZMX6V7.cjs → chunk-FV5AXTSF.cjs} +11 -9
- package/dist/chunk-FV5AXTSF.cjs.map +1 -0
- package/dist/{chunk-7ZSU4MQZ.cjs → chunk-PL3MPFPA.cjs} +10 -8
- package/dist/chunk-PL3MPFPA.cjs.map +1 -0
- package/dist/export/index.cjs +4 -3
- package/dist/export/index.cjs.map +1 -1
- package/dist/export/index.js +3 -2
- package/dist/extensions/index.cjs +4 -3
- package/dist/extensions/index.cjs.map +1 -1
- package/dist/extensions/index.js +3 -2
- package/dist/index.cjs +19 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +12 -11
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-3S72F46R.js.map +0 -1
- package/dist/chunk-7ZSU4MQZ.cjs.map +0 -1
- package/dist/chunk-NN3JGUSX.cjs.map +0 -1
- package/dist/chunk-YZZMX6V7.cjs.map +0 -1
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
|
|
20
|
+
|
|
21
|
+
|
|
20
22
|
|
|
21
23
|
var _chunkYJLCBA7Vcjs = require('../chunk-YJLCBA7V.cjs');
|
|
22
24
|
|
|
@@ -28,6 +30,9 @@ var _chunkYJLCBA7Vcjs = require('../chunk-YJLCBA7V.cjs');
|
|
|
28
30
|
var _chunkO2UH7LZEcjs = require('../chunk-O2UH7LZE.cjs');
|
|
29
31
|
|
|
30
32
|
|
|
33
|
+
var _chunkEMQWJA3Gcjs = require('../chunk-EMQWJA3G.cjs');
|
|
34
|
+
|
|
35
|
+
|
|
31
36
|
var _chunkQKLRAA4Tcjs = require('../chunk-QKLRAA4T.cjs');
|
|
32
37
|
|
|
33
38
|
|
|
@@ -112,21 +117,21 @@ var MOTION_DEFAULT_HOLD = 600;
|
|
|
112
117
|
var MOTION_CAMERA_MS = 1e3;
|
|
113
118
|
var MOTION_DRAW_MS_PER_UNIT = 1.5;
|
|
114
119
|
var MOTION_DRAW_RANGE = [500, 2500];
|
|
115
|
-
function motionFlairPrimitive(
|
|
116
|
-
switch (
|
|
120
|
+
function motionFlairPrimitive(command3) {
|
|
121
|
+
switch (command3.type) {
|
|
117
122
|
case "enter":
|
|
118
|
-
return `entrance.${_nullishCoalesce(
|
|
123
|
+
return `entrance.${_nullishCoalesce(command3.style, () => ( "fade"))}`;
|
|
119
124
|
case "exit":
|
|
120
|
-
return `exit.${_nullishCoalesce(
|
|
125
|
+
return `exit.${_nullishCoalesce(command3.style, () => ( "fade"))}`;
|
|
121
126
|
case "pulse":
|
|
122
127
|
case "shake":
|
|
123
128
|
case "teeter":
|
|
124
129
|
case "wave":
|
|
125
130
|
case "wiggle":
|
|
126
131
|
case "color-pulse":
|
|
127
|
-
return `emphasis.${
|
|
132
|
+
return `emphasis.${command3.type}`;
|
|
128
133
|
case "rotate":
|
|
129
|
-
return
|
|
134
|
+
return command3.by !== void 0 ? "emphasis.spin" : void 0;
|
|
130
135
|
case "follow":
|
|
131
136
|
return "path.follow";
|
|
132
137
|
default:
|
|
@@ -221,7 +226,7 @@ ${problems.map((p) => `- ${p}`).join("\n")}`);
|
|
|
221
226
|
this.problems = problems;
|
|
222
227
|
}
|
|
223
228
|
};
|
|
224
|
-
var SCENE_KEYS = ["version", "name", "description", "artwork", "shots", "clips", "drivers", "loop", "trigger", "fps", "steps", "beat", "narration", "target", "controls", "presets"];
|
|
229
|
+
var SCENE_KEYS = ["version", "name", "description", "artwork", "shots", "clips", "drivers", "motionBlur", "loop", "trigger", "fps", "steps", "beat", "narration", "target", "controls", "presets"];
|
|
225
230
|
var SHOT_KEYS = ["id", "label", "camera", "at", "run", "hold", "speed"];
|
|
226
231
|
var CAMERA_KEYS = ["x", "y", "width", "height", "frame", "padding", "ease", "ms", "geometric"];
|
|
227
232
|
var BASE_KEYS = ["type", "targets", "duration", "delay", "stagger", "with", "ease", "note", "repeat", "repeatMode", "steps"];
|
|
@@ -254,108 +259,108 @@ var isPoint = (v) => isObject(v) && isNum(v.x) && isNum(v.y);
|
|
|
254
259
|
function unknownKeys(value, known, where, problems) {
|
|
255
260
|
for (const key of Object.keys(value)) if (!known.includes(key)) problems.push(`${where}: unknown field ${namedWithSuggestion(key, known)}.`);
|
|
256
261
|
}
|
|
257
|
-
function commandShapeProblems(
|
|
262
|
+
function commandShapeProblems(command3, where) {
|
|
258
263
|
const problems = [];
|
|
259
|
-
if (!isObject(
|
|
260
|
-
const type =
|
|
264
|
+
if (!isObject(command3)) return [`${where} must be an object.`];
|
|
265
|
+
const type = command3.type;
|
|
261
266
|
if (typeof type !== "string" || !MOTION_COMMAND_TYPES.includes(type)) {
|
|
262
267
|
return [`${where}: unknown type ${typeof type === "string" ? namedWithSuggestion(type, MOTION_COMMAND_TYPES) : String(type)}; use one of ${MOTION_COMMAND_TYPES.join(", ")}.`];
|
|
263
268
|
}
|
|
264
|
-
unknownKeys(
|
|
269
|
+
unknownKeys(command3, COMMAND_KEYS[type], `${where} (${type})`, problems);
|
|
265
270
|
const p = (msg) => problems.push(`${where} (${type}): ${msg}`);
|
|
266
271
|
const nonNegative = (key) => {
|
|
267
|
-
if (
|
|
272
|
+
if (command3[key] !== void 0 && !(isNum(command3[key]) && command3[key] >= 0)) p(`"${key}" must be a number of milliseconds \u2265 0.`);
|
|
268
273
|
};
|
|
269
274
|
const fraction = (key) => {
|
|
270
|
-
if (
|
|
275
|
+
if (command3[key] !== void 0 && !(isNum(command3[key]) && command3[key] >= 0 && command3[key] <= 1)) p(`"${key}" must be a fraction from 0 to 1.`);
|
|
271
276
|
};
|
|
272
277
|
const optionalNumber = (key) => {
|
|
273
|
-
if (
|
|
278
|
+
if (command3[key] !== void 0 && !isNum(command3[key])) p(`"${key}" must be a number.`);
|
|
274
279
|
};
|
|
275
|
-
if (
|
|
276
|
-
if (DRAW_ORDER_KEYS.some((k) =>
|
|
277
|
-
if (type === "enter" &&
|
|
278
|
-
if (
|
|
279
|
-
if (
|
|
280
|
-
if (
|
|
281
|
-
if (
|
|
282
|
-
if (
|
|
280
|
+
if (command3.with !== void 0 && command3.with !== "after" && command3.with !== "previous") p(`"with" must be "after" or "previous".`);
|
|
281
|
+
if (DRAW_ORDER_KEYS.some((k) => command3[k] !== void 0)) {
|
|
282
|
+
if (type === "enter" && command3.style !== "draw" || type === "exit" && command3.style !== "undraw") p(`"order", "pace", "align", "sortBy" and "origin" go with drawing: style "${type === "enter" ? "draw" : "undraw"}".`);
|
|
283
|
+
if (command3.order !== void 0 && command3.order !== "together" && command3.order !== "sequence") p(`"order" must be "together" or "sequence" (one stroke after another).`);
|
|
284
|
+
if (command3.pace !== void 0 && command3.pace !== "time" && command3.pace !== "speed") p(`"pace" must be "time" (each stroke takes the whole time) or "speed" (all at one speed).`);
|
|
285
|
+
if (command3.align !== void 0 && command3.align !== "start" && command3.align !== "end") p(`"align" must be "start" or "end".`);
|
|
286
|
+
if (command3.sortBy !== void 0 && command3.sortBy !== "document" && command3.sortBy !== "distance") p(`"sortBy" must be "document" or "distance".`);
|
|
287
|
+
if (command3.origin !== void 0 && !isPoint(command3.origin)) p(`"origin" must be { "x": number, "y": number } (document units).`);
|
|
283
288
|
}
|
|
284
|
-
if (
|
|
289
|
+
if (command3.note !== void 0 && typeof command3.note !== "string") p(`"note" must be a string.`);
|
|
285
290
|
if (type === "wait") {
|
|
286
|
-
if (!isNum(
|
|
291
|
+
if (!isNum(command3.duration) || command3.duration < 0) p(`"duration" must be a number of milliseconds \u2265 0.`);
|
|
287
292
|
return problems;
|
|
288
293
|
}
|
|
289
|
-
if (!Array.isArray(
|
|
294
|
+
if (!Array.isArray(command3.targets) || command3.targets.length === 0 || !command3.targets.every((t) => typeof t === "string" && t.length > 0)) p(`"targets" must be a non-empty list of node ids.`);
|
|
290
295
|
nonNegative("duration");
|
|
291
296
|
nonNegative("delay");
|
|
292
297
|
nonNegative("stagger");
|
|
293
|
-
if (
|
|
294
|
-
if (
|
|
298
|
+
if (command3.ease !== void 0 && !_chunk2FHLHZALcjs.isAnimationEasing.call(void 0, command3.ease)) p(`"ease" is not an easing; use a preset such as "ease-in-out" or "spring-bouncy", or { "type": "cubic-bezier", \u2026 } or { "type": "spring", \u2026 }.`);
|
|
299
|
+
if (command3.repeat !== void 0) {
|
|
295
300
|
if (type === "enter" || type === "exit") p(`"repeat" is not for ${type} (it brings things in or out once); repeat an emphasis or a move instead.`);
|
|
296
|
-
else if (!(Number.isInteger(
|
|
301
|
+
else if (!(Number.isInteger(command3.repeat) && command3.repeat >= 1 && command3.repeat <= 1e3)) p(`"repeat" must be a whole number from 1 to 1000.`);
|
|
297
302
|
}
|
|
298
|
-
if (
|
|
299
|
-
if (
|
|
300
|
-
if (
|
|
303
|
+
if (command3.blend !== void 0 && !MOTION_BLENDS.includes(command3.blend)) p(`"blend" must be "replace" (the change takes over) or "add" (a layer on top).`);
|
|
304
|
+
if (command3.repeatMode !== void 0 && !MOTION_REPEAT_MODES.includes(command3.repeatMode)) p(`"repeatMode" must be one of ${MOTION_REPEAT_MODES.join(", ")}.`);
|
|
305
|
+
if (command3.steps !== void 0 && !(isNum(command3.steps) && command3.steps > 0 && command3.steps <= 120)) p(`"steps" must be frames per second from above 0 to 120 (12 holds each pose for two frames at 24 fps).`);
|
|
301
306
|
switch (type) {
|
|
302
307
|
case "enter":
|
|
303
308
|
case "exit": {
|
|
304
309
|
const styles = type === "enter" ? MOTION_ENTER_STYLES : MOTION_EXIT_STYLES;
|
|
305
|
-
if (
|
|
306
|
-
p(`"style" must be one of ${styles.join(", ")}${typeof
|
|
307
|
-
if (
|
|
310
|
+
if (command3.style !== void 0 && !(typeof command3.style === "string" && styles.includes(command3.style)))
|
|
311
|
+
p(`"style" must be one of ${styles.join(", ")}${typeof command3.style === "string" ? ` (got ${namedWithSuggestion(command3.style, styles)})` : ""}.`);
|
|
312
|
+
if (command3.direction !== void 0 && !MOTION_DIRECTIONS.includes(command3.direction)) p(`"direction" must be one of ${MOTION_DIRECTIONS.join(", ")}.`);
|
|
308
313
|
optionalNumber("distance");
|
|
309
|
-
if (
|
|
310
|
-
if (
|
|
311
|
-
if (
|
|
314
|
+
if (command3.scale !== void 0 && !(isNum(command3.scale) && command3.scale >= 0)) p(`"scale" must be a number \u2265 0 (the starting scale for pop and zoom).`);
|
|
315
|
+
if (command3.shape !== void 0 && !MOTION_IRIS_SHAPES.includes(command3.shape)) p(`"shape" must be one of ${MOTION_IRIS_SHAPES.join(", ")} (the opening of an iris).`);
|
|
316
|
+
if (command3.orientation !== void 0 && !MOTION_SPLIT_ORIENTATIONS.includes(command3.orientation)) p(`"orientation" must be one of ${MOTION_SPLIT_ORIENTATIONS.join(", ")} (the line a split opens from).`);
|
|
312
317
|
break;
|
|
313
318
|
}
|
|
314
319
|
case "draw":
|
|
315
320
|
fraction("from");
|
|
316
321
|
fraction("to");
|
|
317
322
|
fraction("comet");
|
|
318
|
-
if (
|
|
323
|
+
if (command3.comet !== void 0 && (command3.from !== void 0 || command3.to !== void 0)) p(`"comet" runs the whole path; leave out "from" and "to".`);
|
|
319
324
|
break;
|
|
320
325
|
case "move":
|
|
321
|
-
if (
|
|
322
|
-
if (
|
|
323
|
-
if (
|
|
324
|
-
if (
|
|
325
|
-
if (
|
|
326
|
-
if (
|
|
327
|
-
if (
|
|
326
|
+
if (command3.by === void 0 && command3.to === void 0) p(`give "by" (an offset) or "to" (a point).`);
|
|
327
|
+
if (command3.by !== void 0 && command3.to !== void 0) p(`give "by" or "to", not both.`);
|
|
328
|
+
if (command3.by !== void 0 && !isPoint(command3.by)) p(`"by" must be { "x": number, "y": number }.`);
|
|
329
|
+
if (command3.to !== void 0 && !isPoint(command3.to)) p(`"to" must be { "x": number, "y": number }.`);
|
|
330
|
+
if (command3.via !== void 0 && !(Array.isArray(command3.via) && command3.via.length > 0 && command3.via.length <= 64 && command3.via.every(isPoint))) p(`"via" must be a list of 1 to 64 points { "x": number, "y": number } (document units).`);
|
|
331
|
+
if (command3.pacing !== void 0 && command3.pacing !== "even" && command3.pacing !== "equal") p(`"pacing" must be "even" (steady speed) or "equal" (each leg the same time).`);
|
|
332
|
+
if (command3.pacing !== void 0 && command3.via === void 0) p(`"pacing" goes with "via".`);
|
|
328
333
|
break;
|
|
329
334
|
case "rotate":
|
|
330
|
-
if (
|
|
331
|
-
if (
|
|
335
|
+
if (command3.by === void 0 && command3.to === void 0) p(`give "by" or "to" (degrees).`);
|
|
336
|
+
if (command3.by !== void 0 && command3.to !== void 0) p(`give "by" or "to", not both.`);
|
|
332
337
|
optionalNumber("by");
|
|
333
338
|
optionalNumber("to");
|
|
334
339
|
break;
|
|
335
340
|
case "scale":
|
|
336
|
-
if (!(isNum(
|
|
337
|
-
if (
|
|
341
|
+
if (!(isNum(command3.to) || isPoint(command3.to))) p(`"to" must be a number or { "x": number, "y": number }.`);
|
|
342
|
+
if (command3.geometric !== void 0 && typeof command3.geometric !== "boolean") p(`"geometric" must be true or false.`);
|
|
338
343
|
break;
|
|
339
344
|
case "fade":
|
|
340
|
-
if (!(isNum(
|
|
345
|
+
if (!(isNum(command3.to) && command3.to >= 0 && command3.to <= 1)) p(`"to" must be an opacity from 0 to 1.`);
|
|
341
346
|
break;
|
|
342
347
|
case "style":
|
|
343
|
-
if (
|
|
344
|
-
if (
|
|
345
|
-
if (
|
|
346
|
-
if (
|
|
348
|
+
if (command3.fill === void 0 && command3.stroke === void 0 && command3.strokeWidth === void 0) p(`give at least one of "fill", "stroke" or "strokeWidth".`);
|
|
349
|
+
if (command3.fill !== void 0 && typeof command3.fill !== "string") p(`"fill" must be a CSS colour.`);
|
|
350
|
+
if (command3.stroke !== void 0 && typeof command3.stroke !== "string") p(`"stroke" must be a CSS colour.`);
|
|
351
|
+
if (command3.strokeWidth !== void 0 && !(isNum(command3.strokeWidth) && command3.strokeWidth >= 0)) p(`"strokeWidth" must be a number \u2265 0.`);
|
|
347
352
|
break;
|
|
348
353
|
case "play":
|
|
349
|
-
if (typeof
|
|
350
|
-
if (
|
|
354
|
+
if (typeof command3.clip !== "string" || !command3.clip) p(`"clip" must be the name of one of the scene's clips.`);
|
|
355
|
+
if (command3.speed !== void 0 && !(isNum(command3.speed) && command3.speed >= 0.05 && command3.speed <= 20)) p(`"speed" must be a number from 0.05 to 20 (2 plays the clip twice as fast).`);
|
|
351
356
|
break;
|
|
352
357
|
case "morph":
|
|
353
358
|
case "match":
|
|
354
|
-
if (typeof
|
|
359
|
+
if (typeof command3.into !== "string" || !command3.into) p(`"into" must be the id of a ${type === "match" ? "group to become" : "node whose shape to take"}.`);
|
|
355
360
|
break;
|
|
356
361
|
case "follow":
|
|
357
|
-
if (typeof
|
|
358
|
-
if (
|
|
362
|
+
if (typeof command3.path !== "string" || !command3.path) p(`"path" must be the id of a path node.`);
|
|
363
|
+
if (command3.rotate !== void 0 && typeof command3.rotate !== "boolean") p(`"rotate" must be true or false.`);
|
|
359
364
|
fraction("from");
|
|
360
365
|
fraction("to");
|
|
361
366
|
break;
|
|
@@ -366,22 +371,22 @@ function commandShapeProblems(command2, where) {
|
|
|
366
371
|
case "color-pulse":
|
|
367
372
|
optionalNumber("amount");
|
|
368
373
|
optionalNumber("angle");
|
|
369
|
-
if (
|
|
370
|
-
if (
|
|
371
|
-
if (
|
|
372
|
-
if (
|
|
374
|
+
if (command3.count !== void 0 && !(Number.isInteger(command3.count) && command3.count >= 1)) p(`"count" must be a whole number \u2265 1.`);
|
|
375
|
+
if (command3.axis !== void 0 && command3.axis !== "horizontal" && command3.axis !== "vertical") p(`"axis" must be "horizontal" or "vertical".`);
|
|
376
|
+
if (command3.color !== void 0 && typeof command3.color !== "string") p(`"color" must be a CSS colour.`);
|
|
377
|
+
if (command3.paint !== void 0 && command3.paint !== "fill" && command3.paint !== "stroke") p(`"paint" must be "fill" or "stroke".`);
|
|
373
378
|
break;
|
|
374
379
|
case "wiggle":
|
|
375
380
|
optionalNumber("amount");
|
|
376
381
|
optionalNumber("rotate");
|
|
377
|
-
if (
|
|
378
|
-
if (
|
|
379
|
-
if (
|
|
382
|
+
if (command3.frequency !== void 0 && !(isNum(command3.frequency) && command3.frequency > 0)) p(`"frequency" must be a number of Hz > 0.`);
|
|
383
|
+
if (command3.octaves !== void 0 && !(Number.isInteger(command3.octaves) && command3.octaves >= 1 && command3.octaves <= 8)) p(`"octaves" must be a whole number from 1 to 8.`);
|
|
384
|
+
if (command3.seed !== void 0 && !(Number.isInteger(command3.seed) && command3.seed >= 0)) p(`"seed" must be a whole number \u2265 0.`);
|
|
380
385
|
break;
|
|
381
386
|
case "effect":
|
|
382
|
-
if (typeof
|
|
383
|
-
if (typeof
|
|
384
|
-
if (!isNum(
|
|
387
|
+
if (typeof command3.effect !== "string" || !command3.effect) p(`"effect" must be the id of one of the node's effects.`);
|
|
388
|
+
if (typeof command3.param !== "string" || !command3.param) p(`"param" must name a numeric parameter of the effect.`);
|
|
389
|
+
if (!isNum(command3.to)) p(`"to" must be a number.`);
|
|
385
390
|
break;
|
|
386
391
|
}
|
|
387
392
|
return problems;
|
|
@@ -398,9 +403,9 @@ function clipsShapeProblems(clips) {
|
|
|
398
403
|
}
|
|
399
404
|
unknownKeys(clip, ["description", "run"], where, problems);
|
|
400
405
|
if (clip.description !== void 0 && typeof clip.description !== "string") problems.push(`${where}: "description" must be a string.`);
|
|
401
|
-
clip.run.forEach((
|
|
402
|
-
if (isObject(
|
|
403
|
-
else problems.push(...commandShapeProblems(
|
|
406
|
+
clip.run.forEach((command3, i) => {
|
|
407
|
+
if (isObject(command3) && command3.type === "play") problems.push(`${where}, command ${i + 1}: a clip cannot play another clip; copy its commands in instead.`);
|
|
408
|
+
else problems.push(...commandShapeProblems(command3, `${where}, command ${i + 1}`));
|
|
404
409
|
});
|
|
405
410
|
}
|
|
406
411
|
return problems;
|
|
@@ -456,7 +461,7 @@ function shotShapeProblems(shot, where) {
|
|
|
456
461
|
}
|
|
457
462
|
}
|
|
458
463
|
if (!Array.isArray(shot.run)) problems.push(`${name}: "run" must be a list of commands (it may be empty).`);
|
|
459
|
-
else shot.run.forEach((
|
|
464
|
+
else shot.run.forEach((command3, i) => problems.push(...commandShapeProblems(command3, `${name}, command ${i + 1}`)));
|
|
460
465
|
return problems;
|
|
461
466
|
}
|
|
462
467
|
function motionSceneShapeProblems(input) {
|
|
@@ -471,6 +476,11 @@ function motionSceneShapeProblems(input) {
|
|
|
471
476
|
problems.push('Scene: "artwork" must be a vector document { width, height, nodes }.');
|
|
472
477
|
if (input.clips !== void 0) problems.push(...clipsShapeProblems(input.clips));
|
|
473
478
|
if (input.drivers !== void 0) problems.push(...driversShapeProblems(input.drivers));
|
|
479
|
+
if (input.motionBlur !== void 0) {
|
|
480
|
+
const b = input.motionBlur;
|
|
481
|
+
if (!isObject(b) || Object.keys(b).some((k) => k !== "shutter" && k !== "samples") || b.shutter !== void 0 && !(isNum(b.shutter) && b.shutter > 0 && b.shutter <= 720) || b.samples !== void 0 && !(Number.isInteger(b.samples) && b.samples >= 2 && b.samples <= 32))
|
|
482
|
+
problems.push('Scene: "motionBlur" must be { "shutter"?: degrees above 0 up to 720 (default 180), "samples"?: 2 to 32 (default 8) }.');
|
|
483
|
+
}
|
|
474
484
|
if (input.trigger !== void 0 && !MOTION_TRIGGERS.includes(input.trigger)) problems.push(`Scene: "trigger" must be one of ${MOTION_TRIGGERS.join(", ")}.`);
|
|
475
485
|
if (input.loop !== void 0 && !["none", "loop", "ping-pong"].includes(input.loop)) problems.push('Scene: "loop" must be "none", "loop" or "ping-pong".');
|
|
476
486
|
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.');
|
|
@@ -518,7 +528,7 @@ function motionSceneToJson(scene, space = 2) {
|
|
|
518
528
|
return JSON.stringify(scene, null, space);
|
|
519
529
|
}
|
|
520
530
|
var isMotionShot = (shot) => shotShapeProblems(shot, "Shot").length === 0;
|
|
521
|
-
var isMotionCommand = (
|
|
531
|
+
var isMotionCommand = (command3) => commandShapeProblems(command3, "Command").length === 0;
|
|
522
532
|
|
|
523
533
|
// src/animation-editor/match.ts
|
|
524
534
|
var SEPARATOR = /[-_/.:]/;
|
|
@@ -881,13 +891,13 @@ function motionDriverSources(timeline, node, properties, seen = /* @__PURE__ */
|
|
|
881
891
|
}
|
|
882
892
|
return out;
|
|
883
893
|
}
|
|
884
|
-
function motionCommandRepeat(
|
|
885
|
-
if (
|
|
886
|
-
const n = _nullishCoalesce(
|
|
894
|
+
function motionCommandRepeat(command3) {
|
|
895
|
+
if (command3.type === "wait" || command3.type === "enter" || command3.type === "exit") return 1;
|
|
896
|
+
const n = _nullishCoalesce(command3.repeat, () => ( 1));
|
|
887
897
|
return Number.isInteger(n) && n >= 1 ? n : 1;
|
|
888
898
|
}
|
|
889
|
-
function motionCommandDuration(
|
|
890
|
-
return motionCycleDuration(
|
|
899
|
+
function motionCommandDuration(command3, scene, target) {
|
|
900
|
+
return motionCycleDuration(command3, scene, target) * motionCommandRepeat(command3);
|
|
891
901
|
}
|
|
892
902
|
function motionClipLayout(scene, clip, target, speed = 1) {
|
|
893
903
|
const run = _optionalChain([scene, 'access', _28 => _28.clips, 'optionalAccess', _29 => _29[clip], 'optionalAccess', _30 => _30.run]);
|
|
@@ -904,36 +914,36 @@ function motionClipLayout(scene, clip, target, speed = 1) {
|
|
|
904
914
|
return;
|
|
905
915
|
}
|
|
906
916
|
if (raw.type === "play") return;
|
|
907
|
-
const
|
|
908
|
-
const begin = base + (_nullishCoalesce(
|
|
917
|
+
const command3 = { ...raw, targets: raw.targets.map((t) => t === MOTION_CLIP_TARGET ? target : t) };
|
|
918
|
+
const begin = base + (_nullishCoalesce(command3.delay, () => ( 0))) * f;
|
|
909
919
|
let end = begin;
|
|
910
|
-
|
|
911
|
-
const cycle = motionCycleDuration(
|
|
912
|
-
const s = begin + k * (_nullishCoalesce(
|
|
913
|
-
items.push({ command:
|
|
914
|
-
end = Math.max(end, s + cycle * motionCommandRepeat(
|
|
920
|
+
command3.targets.forEach((t, k) => {
|
|
921
|
+
const cycle = motionCycleDuration(command3, scene, t) * f;
|
|
922
|
+
const s = begin + k * (_nullishCoalesce(command3.stagger, () => ( 0))) * f;
|
|
923
|
+
items.push({ command: command3, target: t, start: s, cycle });
|
|
924
|
+
end = Math.max(end, s + cycle * motionCommandRepeat(command3));
|
|
915
925
|
});
|
|
916
926
|
prevStart = begin;
|
|
917
927
|
runEnd = Math.max(runEnd, end);
|
|
918
928
|
});
|
|
919
929
|
return { items, length: runEnd };
|
|
920
930
|
}
|
|
921
|
-
function motionCycleDuration(
|
|
922
|
-
if (
|
|
923
|
-
if (
|
|
924
|
-
if (
|
|
925
|
-
if (
|
|
926
|
-
const ease = _chunk2FHLHZALcjs.resolveEasing.call(void 0,
|
|
931
|
+
function motionCycleDuration(command3, scene, target) {
|
|
932
|
+
if (command3.type === "wait") return command3.duration;
|
|
933
|
+
if (command3.type === "play") return motionClipLayout(scene, command3.clip, _nullishCoalesce(target, () => ( MOTION_CLIP_TARGET)), command3.speed).length;
|
|
934
|
+
if (command3.duration !== void 0) return command3.duration;
|
|
935
|
+
if (command3.ease !== void 0) {
|
|
936
|
+
const ease = _chunk2FHLHZALcjs.resolveEasing.call(void 0, command3.ease);
|
|
927
937
|
if (ease !== "hold" && ease.type === "spring") return _chunk2FHLHZALcjs.springSettleMs.call(void 0, ease);
|
|
928
938
|
}
|
|
929
|
-
if (
|
|
939
|
+
if (command3.type === "draw" || command3.type === "enter" && command3.style === "draw" || command3.type === "exit" && command3.style === "undraw") {
|
|
930
940
|
const entry = target ? indexMotionNodes(scene.artwork.nodes).get(target) : void 0;
|
|
931
941
|
const length = entry ? motionPathLength(entry.node) : 0;
|
|
932
942
|
const ms = length * MOTION_DRAW_MS_PER_UNIT;
|
|
933
943
|
return Math.round(Math.min(MOTION_DRAW_RANGE[1], Math.max(MOTION_DRAW_RANGE[0], ms)));
|
|
934
944
|
}
|
|
935
|
-
if (
|
|
936
|
-
return MOTION_DEFAULT_DURATIONS[
|
|
945
|
+
if (command3.type === "move" && _optionalChain([command3, 'access', _31 => _31.via, 'optionalAccess', _32 => _32.length])) return MOTION_DEFAULT_DURATIONS.move + 400 * command3.via.length;
|
|
946
|
+
return MOTION_DEFAULT_DURATIONS[command3.type];
|
|
937
947
|
}
|
|
938
948
|
var MOTION_EASE_OUT = { type: "cubic-bezier", x1: 1 / 3, y1: 1, x2: 2 / 3, y2: 1 };
|
|
939
949
|
var MOTION_EASE_IN_OUT = { type: "cubic-bezier", x1: 0.65, y1: 0, x2: 0.35, y2: 1 };
|
|
@@ -969,14 +979,14 @@ function compileMotionScene(scene) {
|
|
|
969
979
|
const commandsStart = cursor;
|
|
970
980
|
let runEnd = cursor;
|
|
971
981
|
let prevStart = cursor;
|
|
972
|
-
shot.run.forEach((
|
|
973
|
-
const base =
|
|
974
|
-
const begin = base + (
|
|
975
|
-
const targets =
|
|
976
|
-
const durations =
|
|
977
|
-
const stagger =
|
|
982
|
+
shot.run.forEach((command3, i) => {
|
|
983
|
+
const base = command3.with === "previous" && i > 0 ? prevStart : runEnd;
|
|
984
|
+
const begin = base + (command3.type === "wait" ? 0 : (_nullishCoalesce(command3.delay, () => ( 0))) * f);
|
|
985
|
+
const targets = command3.type === "wait" ? [] : command3.targets;
|
|
986
|
+
const durations = command3.type === "wait" ? [command3.duration * f] : targets.map((id) => motionCommandDuration(command3, scene, id) * f);
|
|
987
|
+
const stagger = command3.type === "wait" ? 0 : (_nullishCoalesce(command3.stagger, () => ( 0))) * f;
|
|
978
988
|
const end2 = durations.reduce((m, d, k) => Math.max(m, begin + k * stagger + d), begin);
|
|
979
|
-
commands.push({ shot: shot.id, shotIndex: index, command: i, type:
|
|
989
|
+
commands.push({ shot: shot.id, shotIndex: index, command: i, type: command3.type, start: begin, end: end2, duration: _nullishCoalesce(durations[0], () => ( 0)) });
|
|
980
990
|
prevStart = begin;
|
|
981
991
|
runEnd = Math.max(runEnd, end2);
|
|
982
992
|
});
|
|
@@ -1008,14 +1018,14 @@ function compileMotionScene(scene) {
|
|
|
1008
1018
|
}
|
|
1009
1019
|
for (const timing of commands) {
|
|
1010
1020
|
const shot = scene.shots[timing.shotIndex];
|
|
1011
|
-
const
|
|
1012
|
-
if (
|
|
1013
|
-
if (
|
|
1021
|
+
const command3 = shot.run[timing.command];
|
|
1022
|
+
if (command3.type === "wait") continue;
|
|
1023
|
+
if (command3.type === "play") {
|
|
1014
1024
|
const f = motionShotTimeScale(shot);
|
|
1015
|
-
|
|
1025
|
+
command3.targets.forEach((target, k) => {
|
|
1016
1026
|
if (!nodes.has(target)) return;
|
|
1017
|
-
const at = timing.start + k * (_nullishCoalesce(
|
|
1018
|
-
for (const item of motionClipLayout(scene,
|
|
1027
|
+
const at = timing.start + k * (_nullishCoalesce(command3.stagger, () => ( 0))) * f;
|
|
1028
|
+
for (const item of motionClipLayout(scene, command3.clip, target, (_nullishCoalesce(command3.speed, () => ( 1))) / f).items) {
|
|
1019
1029
|
const entry = nodes.get(item.target);
|
|
1020
1030
|
if (!entry) continue;
|
|
1021
1031
|
jobs.push({ start: at + item.start, order: order++, run: () => compileRepeated(item.command, item.target, entry, at + item.start, item.cycle, timing) });
|
|
@@ -1023,24 +1033,24 @@ function compileMotionScene(scene) {
|
|
|
1023
1033
|
});
|
|
1024
1034
|
continue;
|
|
1025
1035
|
}
|
|
1026
|
-
|
|
1036
|
+
command3.targets.forEach((target, k) => {
|
|
1027
1037
|
const entry = nodes.get(target);
|
|
1028
1038
|
if (!entry) return;
|
|
1029
1039
|
const f = motionShotTimeScale(shot);
|
|
1030
|
-
const start = timing.start + k * (_nullishCoalesce(
|
|
1031
|
-
const cycle = motionCycleDuration(
|
|
1032
|
-
jobs.push({ start, order: order++, run: () => compileRepeated(
|
|
1040
|
+
const start = timing.start + k * (_nullishCoalesce(command3.stagger, () => ( 0))) * f;
|
|
1041
|
+
const cycle = motionCycleDuration(command3, scene, target) * f;
|
|
1042
|
+
jobs.push({ start, order: order++, run: () => compileRepeated(command3, target, entry, start, cycle, timing) });
|
|
1033
1043
|
});
|
|
1034
1044
|
}
|
|
1035
1045
|
jobs.sort((a, b) => a.start - b.start || a.order - b.order).forEach((job) => job.run());
|
|
1036
|
-
function compileRepeated(
|
|
1046
|
+
function compileRepeated(command3, node, entry, start, cycle, timing) {
|
|
1037
1047
|
const first = segments.length;
|
|
1038
|
-
compileCommand(
|
|
1048
|
+
compileCommand(command3, node, entry, start, cycle, timing);
|
|
1039
1049
|
const made = segments.slice(first);
|
|
1040
|
-
const steps = _nullishCoalesce(
|
|
1050
|
+
const steps = _nullishCoalesce(command3.steps, () => ( scene.steps));
|
|
1041
1051
|
if (steps && steps > 0) for (const s of made) s.steps = steps;
|
|
1042
|
-
const n = motionCommandRepeat(
|
|
1043
|
-
const mode = _nullishCoalesce(
|
|
1052
|
+
const n = motionCommandRepeat(command3);
|
|
1053
|
+
const mode = _nullishCoalesce(command3.repeatMode, () => ( "loop"));
|
|
1044
1054
|
const shift = (v, by, k) => {
|
|
1045
1055
|
if (typeof v === "number" && typeof by === "number") return v + by * k;
|
|
1046
1056
|
if (Array.isArray(v) && Array.isArray(by) && v.length === by.length) return v.map((x, i) => x + by[i] * k);
|
|
@@ -1059,16 +1069,16 @@ function compileMotionScene(scene) {
|
|
|
1059
1069
|
}
|
|
1060
1070
|
}
|
|
1061
1071
|
}
|
|
1062
|
-
function compileCommand(
|
|
1072
|
+
function compileCommand(command3, node, entry, start, duration2, timing) {
|
|
1063
1073
|
const end = start + duration2;
|
|
1064
1074
|
const where = { shot: timing.shot, command: timing.command };
|
|
1065
1075
|
const seg = (property, to, ease2, s = start, e = end, extra = {}) => push({ node, property, start: s, end: e, from: _nullishCoalesce(extra.from, () => ( valueAt(node, property, s))), to, ease: ease2, ...where, ...extra });
|
|
1066
1076
|
const rest = (p) => motionRestValue(entry, p, artwork);
|
|
1067
|
-
const
|
|
1077
|
+
const num2 = (p, time = start) => {
|
|
1068
1078
|
const v = valueAt(node, p, time);
|
|
1069
1079
|
return typeof v === "number" ? v : 0;
|
|
1070
1080
|
};
|
|
1071
|
-
const ease =
|
|
1081
|
+
const ease = command3.ease;
|
|
1072
1082
|
const drawWindows = (s, e, o) => {
|
|
1073
1083
|
const order2 = _nullishCoalesce(o.order, () => ( "together"));
|
|
1074
1084
|
const pace = _nullishCoalesce(o.pace, () => ( "time"));
|
|
@@ -1106,11 +1116,11 @@ function compileMotionScene(scene) {
|
|
|
1106
1116
|
return o.align === "end" ? { id: p.id, start: e - d, end: e } : { id: p.id, start: s, end: s + d };
|
|
1107
1117
|
});
|
|
1108
1118
|
};
|
|
1109
|
-
switch (
|
|
1119
|
+
switch (command3.type) {
|
|
1110
1120
|
case "enter": {
|
|
1111
|
-
const style = _nullishCoalesce(
|
|
1121
|
+
const style = _nullishCoalesce(command3.style, () => ( "fade"));
|
|
1112
1122
|
const opacity = rest("opacity");
|
|
1113
|
-
const reveal = style === "wipe" ? motionRevealClip(entry, { style, direction:
|
|
1123
|
+
const reveal = style === "wipe" ? motionRevealClip(entry, { style, direction: command3.direction }) : style === "iris" ? motionRevealClip(entry, { style, shape: command3.shape }) : style === "split" ? motionRevealClip(entry, { style, orientation: command3.orientation }) : null;
|
|
1114
1124
|
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);
|
|
1115
1125
|
if (reveal) {
|
|
1116
1126
|
seg("opacity", opacity, "linear", start, start);
|
|
@@ -1118,30 +1128,30 @@ function compileMotionScene(scene) {
|
|
|
1118
1128
|
break;
|
|
1119
1129
|
}
|
|
1120
1130
|
if (style === "draw") {
|
|
1121
|
-
const windows = drawWindows(start, start + duration2 * 0.75,
|
|
1131
|
+
const windows = drawWindows(start, start + duration2 * 0.75, command3);
|
|
1122
1132
|
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 });
|
|
1123
1133
|
else seg("drawEnd", 1, _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)), start, start + duration2 * 0.75, { from: 0 });
|
|
1124
1134
|
seg("fillOpacity", 1, MOTION_EASE_OUT, start + duration2 * 0.6, end, { from: 0 });
|
|
1125
1135
|
break;
|
|
1126
1136
|
}
|
|
1127
1137
|
if (style === "pop" || style === "zoom") {
|
|
1128
|
-
const from = _nullishCoalesce(
|
|
1138
|
+
const from = _nullishCoalesce(command3.scale, () => ( (style === "pop" ? 0.5 : 1.6)));
|
|
1129
1139
|
seg("opacity", opacity, MOTION_EASE_OUT, start, start + duration2 * (style === "pop" ? 0.4 : 0.5), { from: 0 });
|
|
1130
1140
|
seg("scaleX", 1, _nullishCoalesce(ease, () => ( (style === "pop" ? "ease-out-back" : MOTION_EASE_OUT))), start, end, { from });
|
|
1131
1141
|
seg("scaleY", 1, _nullishCoalesce(ease, () => ( (style === "pop" ? "ease-out-back" : MOTION_EASE_OUT))), start, end, { from });
|
|
1132
1142
|
} else if (style === "rise") {
|
|
1133
|
-
const y =
|
|
1143
|
+
const y = num2("y");
|
|
1134
1144
|
seg("opacity", opacity, MOTION_EASE_OUT, start, start + duration2 * 0.5, { from: 0 });
|
|
1135
|
-
seg("y", y, _nullishCoalesce(ease, () => ( "ease-out-back")), start, end, { from: y + (_nullishCoalesce(
|
|
1145
|
+
seg("y", y, _nullishCoalesce(ease, () => ( "ease-out-back")), start, end, { from: y + (_nullishCoalesce(command3.distance, () => ( 20))) });
|
|
1136
1146
|
} else if (style === "grow") {
|
|
1137
1147
|
seg("opacity", opacity, MOTION_EASE_OUT, start, start + duration2 * 0.3, { from: 0 });
|
|
1138
1148
|
seg("scaleX", 1, _nullishCoalesce(ease, () => ( MOTION_EASE_OUT)), start, end, { from: 0 });
|
|
1139
1149
|
seg("scaleY", 1, _nullishCoalesce(ease, () => ( MOTION_EASE_OUT)), start, end, { from: 0 });
|
|
1140
1150
|
} else if (style === "slide") {
|
|
1141
|
-
const dir = DIRECTION[_nullishCoalesce(
|
|
1142
|
-
const distance = _nullishCoalesce(
|
|
1143
|
-
const x =
|
|
1144
|
-
const y =
|
|
1151
|
+
const dir = DIRECTION[_nullishCoalesce(command3.direction, () => ( "up"))];
|
|
1152
|
+
const distance = _nullishCoalesce(command3.distance, () => ( 40));
|
|
1153
|
+
const x = num2("x");
|
|
1154
|
+
const y = num2("y");
|
|
1145
1155
|
seg("opacity", opacity, MOTION_EASE_OUT, start, end, { from: 0 });
|
|
1146
1156
|
seg("x", x, _nullishCoalesce(ease, () => ( MOTION_EASE_OUT)), start, end, { from: x - dir.x * distance });
|
|
1147
1157
|
seg("y", y, _nullishCoalesce(ease, () => ( MOTION_EASE_OUT)), start, end, { from: y - dir.y * distance });
|
|
@@ -1149,12 +1159,12 @@ function compileMotionScene(scene) {
|
|
|
1149
1159
|
break;
|
|
1150
1160
|
}
|
|
1151
1161
|
case "exit": {
|
|
1152
|
-
const style = _nullishCoalesce(
|
|
1153
|
-
const wipe = style === "wipe" ? motionRevealClip(entry, { style, direction:
|
|
1162
|
+
const style = _nullishCoalesce(command3.style, () => ( "fade"));
|
|
1163
|
+
const wipe = style === "wipe" ? motionRevealClip(entry, { style, direction: command3.direction, hide: true }) : null;
|
|
1154
1164
|
if (wipe) seg(wipe.property, wipe.to, _nullishCoalesce(ease, () => ( "ease-in")), start, end, { from: wipe.from });
|
|
1155
1165
|
else if (style === "undraw") {
|
|
1156
1166
|
seg("fillOpacity", 0, "ease-in", start, start + duration2 * 0.4);
|
|
1157
|
-
const windows = drawWindows(start, end,
|
|
1167
|
+
const windows = drawWindows(start, end, command3);
|
|
1158
1168
|
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 });
|
|
1159
1169
|
else seg("drawEnd", 0, _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)));
|
|
1160
1170
|
} else if (style === "shrink") {
|
|
@@ -1162,18 +1172,18 @@ function compileMotionScene(scene) {
|
|
|
1162
1172
|
seg("scaleY", 0, _nullishCoalesce(ease, () => ( "ease-in")));
|
|
1163
1173
|
seg("opacity", 0, "ease-in", start + duration2 * 0.6, end);
|
|
1164
1174
|
} else if (style === "slide") {
|
|
1165
|
-
const dir = DIRECTION[_nullishCoalesce(
|
|
1166
|
-
const distance = _nullishCoalesce(
|
|
1167
|
-
seg("x",
|
|
1168
|
-
seg("y",
|
|
1175
|
+
const dir = DIRECTION[_nullishCoalesce(command3.direction, () => ( "down"))];
|
|
1176
|
+
const distance = _nullishCoalesce(command3.distance, () => ( 40));
|
|
1177
|
+
seg("x", num2("x") + dir.x * distance, _nullishCoalesce(ease, () => ( "ease-in")));
|
|
1178
|
+
seg("y", num2("y") + dir.y * distance, _nullishCoalesce(ease, () => ( "ease-in")));
|
|
1169
1179
|
seg("opacity", 0, _nullishCoalesce(ease, () => ( "ease-in")));
|
|
1170
1180
|
} else seg("opacity", 0, _nullishCoalesce(ease, () => ( "ease-in")));
|
|
1171
1181
|
break;
|
|
1172
1182
|
}
|
|
1173
1183
|
case "draw": {
|
|
1174
1184
|
const e = _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT));
|
|
1175
|
-
const comet =
|
|
1176
|
-
const windows = _nullishCoalesce(drawWindows(start, end,
|
|
1185
|
+
const comet = command3.comet !== void 0 ? Math.min(1, Math.max(0, command3.comet)) : void 0;
|
|
1186
|
+
const windows = _nullishCoalesce(drawWindows(start, end, command3), () => ( [{ id: node, start, end }]));
|
|
1177
1187
|
for (const w of windows) {
|
|
1178
1188
|
if (comet !== void 0) {
|
|
1179
1189
|
push({ node: w.id, property: "drawEnd", start: w.start, end: w.end, from: 0, to: 1 + comet, ease: e, ...where });
|
|
@@ -1181,27 +1191,27 @@ function compileMotionScene(scene) {
|
|
|
1181
1191
|
continue;
|
|
1182
1192
|
}
|
|
1183
1193
|
const drawn = tracks.has(motionTrackKey(w.id, "drawEnd"));
|
|
1184
|
-
push({ node: w.id, property: "drawEnd", start: w.start, end: w.end, from: _nullishCoalesce(
|
|
1194
|
+
push({ node: w.id, property: "drawEnd", start: w.start, end: w.end, from: _nullishCoalesce(command3.from, () => ( (drawn ? valueAt(w.id, "drawEnd", w.start) : 0))), to: _nullishCoalesce(command3.to, () => ( 1)), ease: e, ...where });
|
|
1185
1195
|
}
|
|
1186
1196
|
break;
|
|
1187
1197
|
}
|
|
1188
1198
|
case "move": {
|
|
1189
1199
|
const e = _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT));
|
|
1190
|
-
let dx = _nullishCoalesce(_optionalChain([
|
|
1191
|
-
let dy = _nullishCoalesce(_optionalChain([
|
|
1192
|
-
if (
|
|
1200
|
+
let dx = _nullishCoalesce(_optionalChain([command3, 'access', _36 => _36.by, 'optionalAccess', _37 => _37.x]), () => ( 0));
|
|
1201
|
+
let dy = _nullishCoalesce(_optionalChain([command3, 'access', _38 => _38.by, 'optionalAccess', _39 => _39.y]), () => ( 0));
|
|
1202
|
+
if (command3.to) {
|
|
1193
1203
|
const inv = _chunkYJLCBA7Vcjs.invertMatrix.call(void 0, entry.parentWorld);
|
|
1194
|
-
const target = _chunkYJLCBA7Vcjs.applyMatrix.call(void 0, inv,
|
|
1195
|
-
dx = target.x - (entry.center.x +
|
|
1196
|
-
dy = target.y - (entry.center.y +
|
|
1204
|
+
const target = _chunkYJLCBA7Vcjs.applyMatrix.call(void 0, inv, command3.to);
|
|
1205
|
+
dx = target.x - (entry.center.x + num2("x"));
|
|
1206
|
+
dy = target.y - (entry.center.y + num2("y"));
|
|
1197
1207
|
}
|
|
1198
|
-
if (_optionalChain([
|
|
1208
|
+
if (_optionalChain([command3, 'access', _40 => _40.via, 'optionalAccess', _41 => _41.length])) {
|
|
1199
1209
|
const inv = _chunkYJLCBA7Vcjs.invertMatrix.call(void 0, entry.parentWorld);
|
|
1200
|
-
const x0 =
|
|
1201
|
-
const y0 =
|
|
1202
|
-
const add =
|
|
1203
|
-
const offsets = [{ x: 0, y: 0 }, ...
|
|
1204
|
-
const times = smoothMotionTimes(offsets, start, end - start, _nullishCoalesce(
|
|
1210
|
+
const x0 = num2("x");
|
|
1211
|
+
const y0 = num2("y");
|
|
1212
|
+
const add = command3.blend === "add";
|
|
1213
|
+
const offsets = [{ x: 0, y: 0 }, ...command3.via.map((p) => _chunkYJLCBA7Vcjs.applyMatrix.call(void 0, inv, p)).map((p) => ({ x: p.x - entry.center.x - x0, y: p.y - entry.center.y - y0 })), { x: dx, y: dy }];
|
|
1214
|
+
const times = smoothMotionTimes(offsets, start, end - start, _nullishCoalesce(command3.pacing, () => ( "even")));
|
|
1205
1215
|
for (const axis of ["x", "y"]) {
|
|
1206
1216
|
const base = add ? 0 : axis === "x" ? x0 : y0;
|
|
1207
1217
|
for (const leg of smoothMotionLegs(times, offsets.map((o) => o[axis])))
|
|
@@ -1209,42 +1219,42 @@ function compileMotionScene(scene) {
|
|
|
1209
1219
|
}
|
|
1210
1220
|
break;
|
|
1211
1221
|
}
|
|
1212
|
-
if (
|
|
1222
|
+
if (command3.blend === "add") {
|
|
1213
1223
|
seg("x", dx, e, start, end, { from: 0, blend: "add" });
|
|
1214
1224
|
seg("y", dy, e, start, end, { from: 0, blend: "add" });
|
|
1215
1225
|
} else {
|
|
1216
|
-
seg("x",
|
|
1217
|
-
seg("y",
|
|
1226
|
+
seg("x", num2("x") + dx, e);
|
|
1227
|
+
seg("y", num2("y") + dy, e);
|
|
1218
1228
|
}
|
|
1219
1229
|
break;
|
|
1220
1230
|
}
|
|
1221
1231
|
case "rotate": {
|
|
1222
1232
|
const e = _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT));
|
|
1223
|
-
if (
|
|
1224
|
-
else seg("rotation", _nullishCoalesce(
|
|
1233
|
+
if (command3.blend === "add") seg("rotation", command3.to !== void 0 ? command3.to - num2("rotation") : _nullishCoalesce(command3.by, () => ( 0)), e, start, end, { from: 0, blend: "add" });
|
|
1234
|
+
else seg("rotation", _nullishCoalesce(command3.to, () => ( num2("rotation") + (_nullishCoalesce(command3.by, () => ( 0))))), e);
|
|
1225
1235
|
break;
|
|
1226
1236
|
}
|
|
1227
1237
|
case "scale": {
|
|
1228
|
-
const to = typeof
|
|
1229
|
-
const extra = { ...
|
|
1238
|
+
const to = typeof command3.to === "number" ? { x: command3.to, y: command3.to } : command3.to;
|
|
1239
|
+
const extra = { ...command3.geometric ? { geometric: true } : {}, ...command3.blend === "add" ? { from: 1, blend: "add" } : {} };
|
|
1230
1240
|
seg("scaleX", to.x, _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)), start, end, extra);
|
|
1231
1241
|
seg("scaleY", to.y, _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)), start, end, extra);
|
|
1232
1242
|
break;
|
|
1233
1243
|
}
|
|
1234
1244
|
case "fade":
|
|
1235
|
-
seg("opacity",
|
|
1245
|
+
seg("opacity", command3.to, _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)));
|
|
1236
1246
|
break;
|
|
1237
1247
|
case "style":
|
|
1238
|
-
if (
|
|
1239
|
-
if (
|
|
1240
|
-
if (
|
|
1248
|
+
if (command3.fill !== void 0) seg("fill", command3.fill, _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)));
|
|
1249
|
+
if (command3.stroke !== void 0) seg("stroke", command3.stroke, _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)));
|
|
1250
|
+
if (command3.strokeWidth !== void 0) seg("strokeWidth", command3.strokeWidth, _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)));
|
|
1241
1251
|
break;
|
|
1242
1252
|
case "morph":
|
|
1243
|
-
seg("shape",
|
|
1253
|
+
seg("shape", command3.into, _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)));
|
|
1244
1254
|
break;
|
|
1245
1255
|
case "match": {
|
|
1246
|
-
const into = nodes.get(
|
|
1247
|
-
if (!into ||
|
|
1256
|
+
const into = nodes.get(command3.into);
|
|
1257
|
+
if (!into || command3.into === node) break;
|
|
1248
1258
|
const match = matchMotionNodes(entry.node, into.node);
|
|
1249
1259
|
const e = _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT));
|
|
1250
1260
|
for (const [a, b] of match.pairs) push({ node: a, property: "shape", start, end, from: valueAt(a, "shape", start), to: b, ease: e, ...where });
|
|
@@ -1252,7 +1262,7 @@ function compileMotionScene(scene) {
|
|
|
1252
1262
|
break;
|
|
1253
1263
|
}
|
|
1254
1264
|
case "follow":
|
|
1255
|
-
seg("follow", _nullishCoalesce(
|
|
1265
|
+
seg("follow", _nullishCoalesce(command3.to, () => ( 1)), _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)), start, end, { from: _nullishCoalesce(command3.from, () => ( 0)), path: command3.path, rotate: _nullishCoalesce(command3.rotate, () => ( false)) });
|
|
1256
1266
|
break;
|
|
1257
1267
|
// Emphasis: a change toward a peak that a behaviour swings round (as Flair's primitives do).
|
|
1258
1268
|
case "pulse":
|
|
@@ -1260,33 +1270,33 @@ function compileMotionScene(scene) {
|
|
|
1260
1270
|
case "teeter":
|
|
1261
1271
|
case "wave":
|
|
1262
1272
|
case "wiggle": {
|
|
1263
|
-
const add = (_nullishCoalesce(
|
|
1273
|
+
const add = (_nullishCoalesce(command3.blend, () => ( "add"))) === "add";
|
|
1264
1274
|
const swing = (p, by, extra) => {
|
|
1265
1275
|
const scale = p === "scaleX" || p === "scaleY";
|
|
1266
1276
|
if (add) seg(p, by, _nullishCoalesce(ease, () => ( "linear")), start, end, { from: scale ? 1 : 0, blend: "add", ...extra });
|
|
1267
|
-
else seg(p, scale ?
|
|
1277
|
+
else seg(p, scale ? num2(p) * by : num2(p) + by, _nullishCoalesce(ease, () => ( "linear")), start, end, extra);
|
|
1268
1278
|
};
|
|
1269
|
-
if (
|
|
1270
|
-
const behavior = { type: "pulse", count: _nullishCoalesce(
|
|
1271
|
-
swing("scaleX", 1 + (_nullishCoalesce(
|
|
1272
|
-
swing("scaleY", 1 + (_nullishCoalesce(
|
|
1273
|
-
} else if (
|
|
1274
|
-
else if (
|
|
1275
|
-
else if (
|
|
1279
|
+
if (command3.type === "pulse") {
|
|
1280
|
+
const behavior = { type: "pulse", count: _nullishCoalesce(command3.count, () => ( 1)) };
|
|
1281
|
+
swing("scaleX", 1 + (_nullishCoalesce(command3.amount, () => ( 0.15))), { behavior });
|
|
1282
|
+
swing("scaleY", 1 + (_nullishCoalesce(command3.amount, () => ( 0.15))), { behavior });
|
|
1283
|
+
} else if (command3.type === "shake") swing(command3.axis === "vertical" ? "y" : "x", _nullishCoalesce(command3.amount, () => ( 8)), { behavior: { type: "oscillate", count: _nullishCoalesce(command3.count, () => ( 3)), decay: 1 } });
|
|
1284
|
+
else if (command3.type === "teeter") swing("rotation", _nullishCoalesce(command3.angle, () => ( 8)), { behavior: { type: "oscillate", count: _nullishCoalesce(command3.count, () => ( 3)), decay: 1 } });
|
|
1285
|
+
else if (command3.type === "wave") swing("y", -(_nullishCoalesce(command3.amount, () => ( 6))), { behavior: { type: "oscillate", count: _nullishCoalesce(command3.count, () => ( 2)) } });
|
|
1276
1286
|
else {
|
|
1277
|
-
const behavior = { type: "wriggle", frequency: _nullishCoalesce(
|
|
1278
|
-
const amount = _nullishCoalesce(
|
|
1287
|
+
const behavior = { type: "wriggle", frequency: _nullishCoalesce(command3.frequency, () => ( 3)), octaves: _nullishCoalesce(command3.octaves, () => ( 2)), seed: command3.seed || _chunkZCNLBRT6cjs.motionSeedFromId.call(void 0, node) };
|
|
1288
|
+
const amount = _nullishCoalesce(command3.amount, () => ( 6));
|
|
1279
1289
|
swing("x", amount, { behavior, index: 0 });
|
|
1280
1290
|
swing("y", amount, { behavior, index: 1 });
|
|
1281
|
-
if (
|
|
1291
|
+
if (command3.rotate) swing("rotation", command3.rotate, { behavior, index: 2 });
|
|
1282
1292
|
}
|
|
1283
1293
|
break;
|
|
1284
1294
|
}
|
|
1285
1295
|
case "color-pulse":
|
|
1286
|
-
seg(_nullishCoalesce(
|
|
1296
|
+
seg(_nullishCoalesce(command3.paint, () => ( "fill")), _nullishCoalesce(command3.color, () => ( "#f59e0b")), _nullishCoalesce(ease, () => ( "linear")), start, end, { behavior: { type: "pulse", count: _nullishCoalesce(command3.count, () => ( 1)) } });
|
|
1287
1297
|
break;
|
|
1288
1298
|
case "effect":
|
|
1289
|
-
seg(`effect:${
|
|
1299
|
+
seg(`effect:${command3.effect}:${command3.param}`, command3.to, _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)));
|
|
1290
1300
|
break;
|
|
1291
1301
|
}
|
|
1292
1302
|
}
|
|
@@ -1574,12 +1584,49 @@ function motionPathsSvg(paths, camera, color = "#f43f5e") {
|
|
|
1574
1584
|
(p) => `<g data-motion-path="${p.id.replace(/"/g, """)}" fill="none" stroke="${color}" stroke-width="${f(w)}" stroke-linecap="round" stroke-linejoin="round" opacity="0.8"><polyline points="${p.points.map((q) => `${f(q.x)},${f(q.y)}`).join(" ")}" stroke-dasharray="${f(w * 3)} ${f(w * 2)}"/>${p.keys.map((k) => `<circle cx="${f(k.x)}" cy="${f(k.y)}" r="${f(w * 2.5)}" fill="${color}" stroke="none"/>`).join("")}</g>`
|
|
1575
1585
|
).join("");
|
|
1576
1586
|
}
|
|
1587
|
+
function motionBlurTimes(scene, time, blur = {}) {
|
|
1588
|
+
const shutter = Math.min(720, Math.max(0, _nullishCoalesce(blur.shutter, () => ( 180))));
|
|
1589
|
+
const most = Math.max(1, Math.min(32, Math.floor(_nullishCoalesce(blur.samples, () => ( 8)))));
|
|
1590
|
+
const span = 1e3 / (_nullishCoalesce(scene.fps, () => ( 30))) * shutter / 360;
|
|
1591
|
+
if (span <= 0 || most <= 1) return [time];
|
|
1592
|
+
const ids = motionMovingIds(scene);
|
|
1593
|
+
if (!ids.length) return [time];
|
|
1594
|
+
const a = indexMotionNodes(motionFrameAt(scene, time - span / 2).document.nodes);
|
|
1595
|
+
const b = indexMotionNodes(motionFrameAt(scene, time + span / 2).document.nodes);
|
|
1596
|
+
let travel = 0;
|
|
1597
|
+
for (const id of ids) {
|
|
1598
|
+
const p = _optionalChain([a, 'access', _58 => _58.get, 'call', _59 => _59(id), 'optionalAccess', _60 => _60.bounds]);
|
|
1599
|
+
const q = _optionalChain([b, 'access', _61 => _61.get, 'call', _62 => _62(id), 'optionalAccess', _63 => _63.bounds]);
|
|
1600
|
+
if (p && q) travel = Math.max(travel, Math.abs(p.x - q.x), Math.abs(p.y - q.y), Math.abs(p.x + p.width - q.x - q.width), Math.abs(p.y + p.height - q.y - q.height));
|
|
1601
|
+
}
|
|
1602
|
+
const n = Math.min(most, Math.ceil(travel / 1.5));
|
|
1603
|
+
if (n <= 1) return [time];
|
|
1604
|
+
return Array.from({ length: n }, (_, k) => time - span / 2 + span * (k + 0.5) / n);
|
|
1605
|
+
}
|
|
1606
|
+
function averagedFrames(scene, times, options, prefix) {
|
|
1607
|
+
const n = times.length;
|
|
1608
|
+
return `<g data-motion-blur="${n}" style="isolation:isolate">${times.map((t, k) => {
|
|
1609
|
+
const own = `${prefix}blur${k}-`;
|
|
1610
|
+
const frame = motionFrameToSvg(scene, t, { ...options, idPrefix: own, background: false });
|
|
1611
|
+
const view = _nullishCoalesce(_optionalChain([/viewBox="([^"]*)"/, 'access', _64 => _64.exec, 'call', _65 => _65(frame), 'optionalAccess', _66 => _66[1]]), () => ( ""));
|
|
1612
|
+
const inner = k === n - 1 ? innerSvg(frame) : innerSvg(frame).replace(new RegExp(` id="(?!${own.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")})[^"]*"`, "g"), "");
|
|
1613
|
+
return `<svg viewBox="${view}" preserveAspectRatio="none" style="mix-blend-mode:plus-lighter" opacity="${Math.round(1e4 / n) / 1e4}">${inner}</svg>`;
|
|
1614
|
+
}).join("")}</g>`;
|
|
1615
|
+
}
|
|
1577
1616
|
var innerSvg = (svg) => svg.slice(svg.indexOf(">") + 1, svg.lastIndexOf("</svg>"));
|
|
1578
1617
|
function motionStageSvg(scene, time, options = {}) {
|
|
1579
|
-
const { onionSkin, motionPaths, motionPathColor, ...svgOptions } = options;
|
|
1580
|
-
const current = motionFrameToSvg(scene, time, svgOptions);
|
|
1581
|
-
if (!onionSkin && !motionPaths) return current;
|
|
1618
|
+
const { onionSkin, motionPaths, motionPathColor, motionBlur, ...svgOptions } = options;
|
|
1582
1619
|
const prefix = _nullishCoalesce(svgOptions.idPrefix, () => ( "vg-"));
|
|
1620
|
+
let current = motionFrameToSvg(scene, time, svgOptions);
|
|
1621
|
+
const blur = motionBlur === false ? void 0 : _nullishCoalesce(motionBlur, () => ( scene.motionBlur));
|
|
1622
|
+
const blurTimes = blur ? motionBlurTimes(scene, time, blur) : [time];
|
|
1623
|
+
if (blurTimes.length > 1) {
|
|
1624
|
+
const open2 = current.indexOf(">") + 1;
|
|
1625
|
+
const body2 = current.slice(open2, current.lastIndexOf("</svg>"));
|
|
1626
|
+
const bg2 = svgOptions.background !== false && scene.artwork.background ? _nullishCoalesce(_optionalChain([/^\s*<rect[^>]*\/>/, 'access', _67 => _67.exec, 'call', _68 => _68(body2), 'optionalAccess', _69 => _69[0]]), () => ( "")) : "";
|
|
1627
|
+
current = `${current.slice(0, open2)}${bg2}${averagedFrames(scene, blurTimes, svgOptions, prefix)}</svg>`;
|
|
1628
|
+
}
|
|
1629
|
+
if (!onionSkin && !motionPaths) return current;
|
|
1583
1630
|
const ghosts = onionSkin ? motionGhosts(scene, time, onionSkin).map((g, i) => {
|
|
1584
1631
|
const own = `${prefix}ghost${i}-`;
|
|
1585
1632
|
const inner = innerSvg(motionFrameToSvg(scene, g.time, { ...svgOptions, idPrefix: own, background: false })).replace(new RegExp(` id="(?!${own.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")})[^"]*"`, "g"), "");
|
|
@@ -1591,7 +1638,7 @@ function motionStageSvg(scene, time, options = {}) {
|
|
|
1591
1638
|
const open = current.indexOf(">") + 1;
|
|
1592
1639
|
const close = current.lastIndexOf("</svg>");
|
|
1593
1640
|
const body = current.slice(open, close);
|
|
1594
|
-
const bg = svgOptions.background !== false && scene.artwork.background ? _nullishCoalesce(_optionalChain([/^\s*<rect[^>]*\/>/, 'access',
|
|
1641
|
+
const bg = svgOptions.background !== false && scene.artwork.background ? _nullishCoalesce(_optionalChain([/^\s*<rect[^>]*\/>/, 'access', _70 => _70.exec, 'call', _71 => _71(body), 'optionalAccess', _72 => _72[0]]), () => ( "")) : "";
|
|
1595
1642
|
return `${current.slice(0, open)}${bg}${ghosts.join("")}${body.slice(bg.length)}${paths}</svg>`;
|
|
1596
1643
|
}
|
|
1597
1644
|
function motionContactSheetSvg(scene, times, options = {}) {
|
|
@@ -1610,7 +1657,7 @@ function motionContactSheetSvg(scene, times, options = {}) {
|
|
|
1610
1657
|
const x = gap + i % cols * (w + gap);
|
|
1611
1658
|
const y = gap + Math.floor(i / cols) * (h + label + gap);
|
|
1612
1659
|
const frame = motionStageSvg(scene, t, { ...frameOptions, idPrefix: `${prefix}f${i}-` });
|
|
1613
|
-
const view = _nullishCoalesce(_optionalChain([/viewBox="([^"]*)"/, 'access',
|
|
1660
|
+
const view = _nullishCoalesce(_optionalChain([/viewBox="([^"]*)"/, 'access', _73 => _73.exec, 'call', _74 => _74(frame), 'optionalAccess', _75 => _75[1]]), () => ( `0 0 ${w} ${h}`));
|
|
1614
1661
|
const inner = innerSvg(frame).replace(new RegExp(` id="(?!${`${prefix}f${i}-`.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")})[^"]*"`, "g"), "");
|
|
1615
1662
|
const shot = _nullishCoalesce(timeline.shots.find((s) => t >= s.start && t < s.end), () => ( timeline.shots[timeline.shots.length - 1]));
|
|
1616
1663
|
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>` : "";
|
|
@@ -1622,7 +1669,7 @@ function motionContactSheetSvg(scene, times, options = {}) {
|
|
|
1622
1669
|
// src/animation-editor/check.ts
|
|
1623
1670
|
var MOTION_MIN_CHANGE_MS = 120;
|
|
1624
1671
|
var NUMERIC_PARAMS = (node, effectId) => {
|
|
1625
|
-
const effect = _optionalChain([node, 'access',
|
|
1672
|
+
const effect = _optionalChain([node, 'access', _76 => _76.effects, 'optionalAccess', _77 => _77.find, 'call', _78 => _78((e) => e.id === effectId)]);
|
|
1626
1673
|
if (!effect) return null;
|
|
1627
1674
|
return Object.entries(effect.params).filter(([, v]) => typeof v === "number").map(([k]) => k);
|
|
1628
1675
|
};
|
|
@@ -1640,24 +1687,24 @@ function usesColor(scene, color) {
|
|
|
1640
1687
|
return found;
|
|
1641
1688
|
}
|
|
1642
1689
|
function visibleAt(timeline, id, time, node) {
|
|
1643
|
-
const
|
|
1690
|
+
const num2 = (p, rest) => {
|
|
1644
1691
|
const v = trackValue(timeline.tracks.get(motionTrackKey(id, p)), time);
|
|
1645
1692
|
return typeof v === "number" ? v : rest;
|
|
1646
1693
|
};
|
|
1647
1694
|
if (node.visible === false) return false;
|
|
1648
|
-
if (
|
|
1649
|
-
if (Math.abs(
|
|
1695
|
+
if (num2("opacity", _nullishCoalesce(node.opacity, () => ( 1))) <= 0.01) return false;
|
|
1696
|
+
if (Math.abs(num2("scaleX", 1) * num2("scaleY", 1)) <= 1e-4) return false;
|
|
1650
1697
|
const clip = (p) => trackValue(timeline.tracks.get(motionTrackKey(id, p)), time);
|
|
1651
1698
|
const rect = clip("clipRect");
|
|
1652
1699
|
if (Array.isArray(rect) && (rect[2] <= 1e-4 || rect[3] <= 1e-4)) return false;
|
|
1653
1700
|
const circle = clip("clipCircle");
|
|
1654
1701
|
if (Array.isArray(circle) && circle[2] <= 1e-4) return false;
|
|
1655
1702
|
const hasFill = node.type === "path" ? !!node.fill && node.fill !== "none" : _chunkYJLCBA7Vcjs.collectPathNodes.call(void 0, node).some((p) => p.fill && p.fill !== "none");
|
|
1656
|
-
const drawn =
|
|
1703
|
+
const drawn = num2("drawEnd", 1) > 0.01 && (node.type === "path" || _chunkYJLCBA7Vcjs.collectPathNodes.call(void 0, node).length === 0 || _chunkYJLCBA7Vcjs.collectPathNodes.call(void 0, node).some((p) => {
|
|
1657
1704
|
const v = trackValue(timeline.tracks.get(motionTrackKey(p.id, "drawEnd")), time);
|
|
1658
1705
|
return typeof v !== "number" || v > 0.01;
|
|
1659
1706
|
}));
|
|
1660
|
-
return drawn || hasFill &&
|
|
1707
|
+
return drawn || hasFill && num2("fillOpacity", 1) > 0.01;
|
|
1661
1708
|
}
|
|
1662
1709
|
function checkMotionScene(scene) {
|
|
1663
1710
|
const issues = [];
|
|
@@ -1685,48 +1732,48 @@ function checkMotionScene(scene) {
|
|
|
1685
1732
|
issues.push(motionIssue("camera-aspect", `The camera is ${c.width} \xD7 ${c.height}, a different shape from the ${scene.artwork.width} \xD7 ${scene.artwork.height} artwork, so it shows bands at the sides; frame a node instead to match.`, { shot: shot.id }));
|
|
1686
1733
|
}
|
|
1687
1734
|
if (!shot.run.length && !shot.camera && shotIndex2 > 0) issues.push(motionIssue("empty-shot", "This shot only holds; add commands or a camera move, or remove it.", { shot: shot.id }));
|
|
1688
|
-
shot.run.forEach((
|
|
1735
|
+
shot.run.forEach((command3, i) => {
|
|
1689
1736
|
const where = { shot: shot.id, command: i };
|
|
1690
|
-
if (
|
|
1691
|
-
const targets =
|
|
1737
|
+
if (command3.type === "wait") return;
|
|
1738
|
+
const targets = command3.targets.filter((id) => known(id, "unknown-target", "node", where));
|
|
1692
1739
|
const timing = timeline.commands.find((c) => c.shot === shot.id && c.command === i);
|
|
1693
1740
|
if (timing && timing.duration > 0 && timing.duration < MOTION_MIN_CHANGE_MS)
|
|
1694
|
-
issues.push(motionIssue("too-fast", `This ${
|
|
1695
|
-
if (
|
|
1696
|
-
const ease = _chunk2FHLHZALcjs.resolveEasing.call(void 0,
|
|
1741
|
+
issues.push(motionIssue("too-fast", `This ${command3.type} lasts ${timing.duration} ms, too quick to follow; make it at least ${MOTION_MIN_CHANGE_MS} ms.`, { ...where, measured: timing.duration, needed: MOTION_MIN_CHANGE_MS, unit: "ms", ids: targets }));
|
|
1742
|
+
if (command3.duration !== void 0 && command3.ease !== void 0) {
|
|
1743
|
+
const ease = _chunk2FHLHZALcjs.resolveEasing.call(void 0, command3.ease);
|
|
1697
1744
|
if (ease !== "hold" && ease.type === "spring") {
|
|
1698
1745
|
const settle = _chunk2FHLHZALcjs.springSettleMs.call(void 0, ease);
|
|
1699
|
-
if (
|
|
1700
|
-
issues.push(motionIssue("spring-cut-short", `This ${
|
|
1746
|
+
if (command3.duration < settle * 0.9)
|
|
1747
|
+
issues.push(motionIssue("spring-cut-short", `This ${command3.type}'s spring takes ${settle} ms to settle but lasts ${command3.duration} ms, so it jumps to its end; leave "duration" out (the spring sets it) or make it at least ${settle} ms.`, { ...where, measured: command3.duration, needed: settle, unit: "ms", ids: targets }));
|
|
1701
1748
|
}
|
|
1702
1749
|
}
|
|
1703
|
-
if (
|
|
1704
|
-
issues.push(motionIssue("not-a-path", `"${
|
|
1705
|
-
if (
|
|
1706
|
-
if (
|
|
1707
|
-
issues.push(motionIssue("unknown-clip", `There is no clip ${namedWithSuggestion(
|
|
1708
|
-
if (
|
|
1709
|
-
const into = nodes.get(
|
|
1750
|
+
if (command3.type === "follow" && known(command3.path, "unknown-path", "path", where) && nodes.get(command3.path).node.type !== "path")
|
|
1751
|
+
issues.push(motionIssue("not-a-path", `"${command3.path}" is a group; follow a single path node.`, { ...where, ids: [command3.path] }));
|
|
1752
|
+
if (command3.type === "morph") known(command3.into, "unknown-shape", "shape", where);
|
|
1753
|
+
if (command3.type === "play" && !_optionalChain([scene, 'access', _79 => _79.clips, 'optionalAccess', _80 => _80[command3.clip]]))
|
|
1754
|
+
issues.push(motionIssue("unknown-clip", `There is no clip ${namedWithSuggestion(command3.clip, Object.keys(_nullishCoalesce(scene.clips, () => ( {}))))}; define it with set_animation_clip.`, where));
|
|
1755
|
+
if (command3.type === "match" && known(command3.into, "unknown-shape", "group", where)) {
|
|
1756
|
+
const into = nodes.get(command3.into);
|
|
1710
1757
|
for (const id of targets) {
|
|
1711
1758
|
const entry = nodes.get(id);
|
|
1712
|
-
if (id ===
|
|
1713
|
-
issues.push(motionIssue("match-self", `"${id}" cannot match "${
|
|
1759
|
+
if (id === command3.into || into.ancestors.includes(id) || entry.ancestors.includes(command3.into)) {
|
|
1760
|
+
issues.push(motionIssue("match-self", `"${id}" cannot match "${command3.into}": one is the other or holds it. Match two separate groups (the second usually hidden).`, { ...where, ids: [id, command3.into] }));
|
|
1714
1761
|
continue;
|
|
1715
1762
|
}
|
|
1716
1763
|
const m = matchMotionNodes(entry.node, into.node);
|
|
1717
1764
|
if (m.unpairedInto.length)
|
|
1718
|
-
issues.push(motionIssue("match-unpaired", `${m.unpairedInto.map((x) => `"${x}"`).join(", ")} in "${
|
|
1765
|
+
issues.push(motionIssue("match-unpaired", `${m.unpairedInto.map((x) => `"${x}"`).join(", ")} in "${command3.into}" ${m.unpairedInto.length > 1 ? "have" : "has"} no partner in "${id}", so ${m.unpairedInto.length > 1 ? "they do" : "it does"} not appear; add a part with the same name or id to "${id}" (it can start hidden or tiny).`, { ...where, ids: m.unpairedInto }));
|
|
1719
1766
|
}
|
|
1720
1767
|
}
|
|
1721
|
-
const style =
|
|
1768
|
+
const style = command3.type === "enter" || command3.type === "exit" ? command3.style : void 0;
|
|
1722
1769
|
if (style === "wipe" || style === "iris" || style === "split") {
|
|
1723
1770
|
for (const id of targets)
|
|
1724
1771
|
if (!motionRevealPadding(nodes.get(id)))
|
|
1725
|
-
issues.push(motionIssue("reveal-no-box", `"${id}" has no width or height (a straight line), so a ${style} cannot clip it and it fades instead; use style "${
|
|
1772
|
+
issues.push(motionIssue("reveal-no-box", `"${id}" has no width or height (a straight line), so a ${style} cannot clip it and it fades instead; use style "${command3.type === "enter" ? "draw" : "undraw"}" for a line.`, { ...where, ids: [id] }));
|
|
1726
1773
|
}
|
|
1727
|
-
if (
|
|
1774
|
+
if (command3.type === "style") {
|
|
1728
1775
|
for (const key of ["fill", "stroke"]) {
|
|
1729
|
-
const color =
|
|
1776
|
+
const color = command3[key];
|
|
1730
1777
|
if (color === void 0) continue;
|
|
1731
1778
|
if (_chunkA6PHCAGXcjs.normalizeTokenColor.call(void 0, color) === null) {
|
|
1732
1779
|
issues.push(motionIssue("invalid-color", `${key} "${color}" is not a colour this editor reads; use hex ("#0ea5e9"), rgb() or rgba().`, where));
|
|
@@ -1740,9 +1787,9 @@ function checkMotionScene(scene) {
|
|
|
1740
1787
|
}
|
|
1741
1788
|
}
|
|
1742
1789
|
}
|
|
1743
|
-
if (
|
|
1744
|
-
const key = _nullishCoalesce(
|
|
1745
|
-
const color = _nullishCoalesce(
|
|
1790
|
+
if (command3.type === "color-pulse") {
|
|
1791
|
+
const key = _nullishCoalesce(command3.paint, () => ( "fill"));
|
|
1792
|
+
const color = _nullishCoalesce(command3.color, () => ( "#f59e0b"));
|
|
1746
1793
|
if (_chunkA6PHCAGXcjs.normalizeTokenColor.call(void 0, color) === null) issues.push(motionIssue("invalid-color", `color "${color}" is not a colour this editor reads; use hex ("#0ea5e9"), rgb() or rgba().`, where));
|
|
1747
1794
|
else
|
|
1748
1795
|
for (const id of targets) {
|
|
@@ -1752,15 +1799,15 @@ function checkMotionScene(scene) {
|
|
|
1752
1799
|
issues.push(motionIssue("paint-not-color", `"${id}" has a gradient or no ${key}, so a colour pulse cannot tint it; give it a solid ${key} or pulse the ${key === "fill" ? "stroke" : "fill"}.`, { ...where, ids: [id] }));
|
|
1753
1800
|
}
|
|
1754
1801
|
}
|
|
1755
|
-
if (
|
|
1802
|
+
if (command3.type === "effect") {
|
|
1756
1803
|
for (const id of targets) {
|
|
1757
1804
|
const node = nodes.get(id).node;
|
|
1758
|
-
const params = NUMERIC_PARAMS(node,
|
|
1805
|
+
const params = NUMERIC_PARAMS(node, command3.effect);
|
|
1759
1806
|
const effects = (_nullishCoalesce(node.effects, () => ( []))).map((e) => e.id);
|
|
1760
1807
|
if (!params)
|
|
1761
|
-
issues.push(motionIssue("unknown-effect", `"${id}" has no effect ${namedWithSuggestion(
|
|
1762
|
-
else if (!params.includes(
|
|
1763
|
-
issues.push(motionIssue("unknown-effect-param", `Effect "${
|
|
1808
|
+
issues.push(motionIssue("unknown-effect", `"${id}" has no effect ${namedWithSuggestion(command3.effect, effects)}${effects.length ? `; its effects are ${effects.map((e) => `"${e}"`).join(", ")}` : "; it has no live effects"}.`, { ...where, ids: [id] }));
|
|
1809
|
+
else if (!params.includes(command3.param))
|
|
1810
|
+
issues.push(motionIssue("unknown-effect-param", `Effect "${command3.effect}" has no numeric parameter ${namedWithSuggestion(command3.param, params)}; use one of ${params.join(", ")}.`, { ...where, ids: [id] }));
|
|
1764
1811
|
}
|
|
1765
1812
|
}
|
|
1766
1813
|
if (timing) {
|
|
@@ -1768,11 +1815,11 @@ function checkMotionScene(scene) {
|
|
|
1768
1815
|
const node = nodes.get(id).node;
|
|
1769
1816
|
const before = Math.max(0, timing.start - 0.5);
|
|
1770
1817
|
const visible = timing.start > 0 ? visibleAt(timeline, id, before, node) : node.visible !== false;
|
|
1771
|
-
if (
|
|
1818
|
+
if (command3.type === "enter" && visible && timing.start > 0)
|
|
1772
1819
|
issues.push(motionIssue("enter-visible", `"${id}" is already showing when it enters, so it blinks out first; remove the enter or exit it earlier.`, { ...where, ids: [id] }));
|
|
1773
|
-
const probe =
|
|
1774
|
-
if (
|
|
1775
|
-
issues.push(motionIssue("exit-hidden", `"${id}" is hidden when this ${
|
|
1820
|
+
const probe = command3.type === "exit" ? timing.start + 0.5 : (timing.start + timing.end) / 2;
|
|
1821
|
+
if (command3.type !== "enter" && command3.type !== "draw" && !visibleAt(timeline, id, probe, node))
|
|
1822
|
+
issues.push(motionIssue("exit-hidden", `"${id}" is hidden when this ${command3.type} starts, so it is not seen; enter it first.`, { ...where, ids: [id] }));
|
|
1776
1823
|
}
|
|
1777
1824
|
}
|
|
1778
1825
|
});
|
|
@@ -1800,9 +1847,9 @@ function checkMotionScene(scene) {
|
|
|
1800
1847
|
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
1848
|
}
|
|
1802
1849
|
for (const [name, clip] of Object.entries(_nullishCoalesce(scene.clips, () => ( {}))))
|
|
1803
|
-
for (const
|
|
1804
|
-
if (
|
|
1805
|
-
for (const id of
|
|
1850
|
+
for (const command3 of clip.run)
|
|
1851
|
+
if (command3.type !== "wait") {
|
|
1852
|
+
for (const id of command3.targets)
|
|
1806
1853
|
if (id !== MOTION_CLIP_TARGET && !nodes.has(id)) issues.push(motionIssue("unknown-target", `Clip "${name}" names ${namedWithSuggestion(id, [...nodes.keys()])}, which the artwork does not have; use "$" for the node the clip is played on.`, { ids: [id] }));
|
|
1807
1854
|
}
|
|
1808
1855
|
if (scene.trigger) {
|
|
@@ -1818,7 +1865,7 @@ function checkMotionScene(scene) {
|
|
|
1818
1865
|
if (!shot.at) return;
|
|
1819
1866
|
const timing = timeline.shots[i];
|
|
1820
1867
|
if (timing.cue === void 0) {
|
|
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',
|
|
1868
|
+
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', _81 => _81.narration, 'optionalAccess', _82 => _82.words, 'access', _83 => _83.length]), () => ( 0))} words (indexes from 0)`;
|
|
1822
1869
|
issues.push(motionIssue("unknown-cue", `Shot "${shot.id}" is cued to ${what}.`, { shot: shot.id }));
|
|
1823
1870
|
} else if (timing.missedBy !== void 0) {
|
|
1824
1871
|
const late = Math.round(timing.missedBy);
|
|
@@ -1845,18 +1892,18 @@ function checkMotionScene(scene) {
|
|
|
1845
1892
|
}
|
|
1846
1893
|
const reported = /* @__PURE__ */ new Set();
|
|
1847
1894
|
for (const timing of timeline.commands) {
|
|
1848
|
-
const
|
|
1849
|
-
if (
|
|
1895
|
+
const command3 = scene.shots[timing.shotIndex].run[timing.command];
|
|
1896
|
+
if (command3.type === "wait" || command3.type === "exit") continue;
|
|
1850
1897
|
const frame = motionFrameAt(scene, timing.end);
|
|
1851
1898
|
const cam = frame.camera;
|
|
1852
1899
|
const docIndex = indexMotionNodes(frame.document.nodes);
|
|
1853
|
-
for (const id of
|
|
1854
|
-
const b = _optionalChain([docIndex, 'access',
|
|
1900
|
+
for (const id of command3.targets) {
|
|
1901
|
+
const b = _optionalChain([docIndex, 'access', _84 => _84.get, 'call', _85 => _85(id), 'optionalAccess', _86 => _86.bounds]);
|
|
1855
1902
|
if (!b || reported.has(id) || !visibleAt(timeline, id, timing.end, nodes.get(id).node)) continue;
|
|
1856
1903
|
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;
|
|
1857
1904
|
if (outside) {
|
|
1858
1905
|
reported.add(id);
|
|
1859
|
-
issues.push(motionIssue("out-of-frame", `"${id}" is outside the camera when this ${
|
|
1906
|
+
issues.push(motionIssue("out-of-frame", `"${id}" is outside the camera when this ${command3.type} ends, so it is not seen; move it into frame or move the camera.`, { shot: timing.shot, command: timing.command, ids: [id] }));
|
|
1860
1907
|
}
|
|
1861
1908
|
}
|
|
1862
1909
|
}
|
|
@@ -1934,12 +1981,12 @@ function applyOne(scene, edit) {
|
|
|
1934
1981
|
if (!(typeof edit.steps === "number" && edit.steps > 0 && edit.steps <= 120)) fail('"steps" must be frames per second from above 0 to 120 (12 is "on twos" at 24 fps), or null for smooth.');
|
|
1935
1982
|
next.steps = edit.steps;
|
|
1936
1983
|
}
|
|
1937
|
-
for (const key of ["beat", "narration"]) {
|
|
1984
|
+
for (const key of ["beat", "narration", "motionBlur"]) {
|
|
1938
1985
|
if (edit[key] === null) delete next[key];
|
|
1939
1986
|
else if (edit[key] !== void 0) next[key] = edit[key];
|
|
1940
1987
|
}
|
|
1941
1988
|
{
|
|
1942
|
-
const problems = motionSceneShapeProblems(next).filter((p) => /"beat"|"narration"/.test(p));
|
|
1989
|
+
const problems = motionSceneShapeProblems(next).filter((p) => /"beat"|"narration"|"motionBlur"/.test(p));
|
|
1943
1990
|
if (problems.length) fail(problems.join(" "));
|
|
1944
1991
|
}
|
|
1945
1992
|
if (edit.target === null) delete next.target;
|
|
@@ -2010,8 +2057,8 @@ function applyOne(scene, edit) {
|
|
|
2010
2057
|
case "add_commands": {
|
|
2011
2058
|
const i = shotIndex(scene, edit.shot);
|
|
2012
2059
|
if (!edit.commands.length) fail("Give at least one command.");
|
|
2013
|
-
edit.commands.forEach((
|
|
2014
|
-
const problems = commandShapeProblems(
|
|
2060
|
+
edit.commands.forEach((command3, k) => {
|
|
2061
|
+
const problems = commandShapeProblems(command3, `Command ${k + 1}`);
|
|
2015
2062
|
if (problems.length) fail(problems.join(" "));
|
|
2016
2063
|
});
|
|
2017
2064
|
const run = scene.shots[i].run;
|
|
@@ -2023,14 +2070,14 @@ function applyOne(scene, edit) {
|
|
|
2023
2070
|
const run = scene.shots[i].run;
|
|
2024
2071
|
if (!Number.isInteger(edit.command) || edit.command < 0 || edit.command >= run.length)
|
|
2025
2072
|
fail(`Shot "${edit.shot}" has ${run.length} command${run.length === 1 ? "" : "s"}; "command" must be an index from 0 to ${run.length - 1} (got ${edit.command}).`);
|
|
2026
|
-
const
|
|
2073
|
+
const command3 = { ...run[edit.command], ..._nullishCoalesce(edit.set, () => ( {})) };
|
|
2027
2074
|
for (const key of _nullishCoalesce(edit.remove, () => ( []))) {
|
|
2028
2075
|
if (key === "type") fail('"type" cannot be removed; replace the command instead.');
|
|
2029
|
-
delete
|
|
2076
|
+
delete command3[key];
|
|
2030
2077
|
}
|
|
2031
|
-
const problems = commandShapeProblems(
|
|
2078
|
+
const problems = commandShapeProblems(command3, `Command ${edit.command}`);
|
|
2032
2079
|
if (problems.length) fail(problems.join(" "));
|
|
2033
|
-
return replaceShot(scene, i, { ...scene.shots[i], run: run.map((c, k) => k === edit.command ?
|
|
2080
|
+
return replaceShot(scene, i, { ...scene.shots[i], run: run.map((c, k) => k === edit.command ? command3 : c) });
|
|
2034
2081
|
}
|
|
2035
2082
|
case "delete_commands": {
|
|
2036
2083
|
const i = shotIndex(scene, edit.shot);
|
|
@@ -2061,9 +2108,9 @@ function applyOne(scene, edit) {
|
|
|
2061
2108
|
else delete next.presets;
|
|
2062
2109
|
}
|
|
2063
2110
|
const names = new Set(edit.controls.map((c) => c.name));
|
|
2064
|
-
for (const
|
|
2065
|
-
for (const key of Object.keys(
|
|
2066
|
-
if (!names.has(key)) fail(`Preset "${
|
|
2111
|
+
for (const preset2 of _nullishCoalesce(next.presets, () => ( [])))
|
|
2112
|
+
for (const key of Object.keys(preset2.values))
|
|
2113
|
+
if (!names.has(key)) fail(`Preset "${preset2.name}" sets ${namedWithSuggestion(key, names)}, which is not a control.`);
|
|
2067
2114
|
return next;
|
|
2068
2115
|
}
|
|
2069
2116
|
case "replace_scene":
|
|
@@ -2083,9 +2130,9 @@ function applyMotionEdits(scene, edits) {
|
|
|
2083
2130
|
let next = scene;
|
|
2084
2131
|
for (let i = 0; i < list.length; i++) {
|
|
2085
2132
|
const edit = list[i];
|
|
2086
|
-
const prefix = list.length > 1 ? `op ${i + 1} (${_optionalChain([edit, 'optionalAccess',
|
|
2133
|
+
const prefix = list.length > 1 ? `op ${i + 1} (${_optionalChain([edit, 'optionalAccess', _87 => _87.op])}): ` : "";
|
|
2087
2134
|
if (!edit || !MOTION_EDIT_OPS.includes(edit.op))
|
|
2088
|
-
return { ok: false, error: `${prefix}Unknown op ${namedWithSuggestion(String(_optionalChain([edit, 'optionalAccess',
|
|
2135
|
+
return { ok: false, error: `${prefix}Unknown op ${namedWithSuggestion(String(_optionalChain([edit, 'optionalAccess', _88 => _88.op])), MOTION_EDIT_OPS)}. Nothing was changed.` };
|
|
2089
2136
|
try {
|
|
2090
2137
|
next = applyOne(next, edit);
|
|
2091
2138
|
} catch (error) {
|
|
@@ -2107,7 +2154,7 @@ function resolveMotionEditRefs(scene, edits) {
|
|
|
2107
2154
|
const resolved = [];
|
|
2108
2155
|
let next = scene;
|
|
2109
2156
|
for (let i = 0; i < edits.length; i++) {
|
|
2110
|
-
const where = `op ${i + 1} (${_optionalChain([edits, 'access',
|
|
2157
|
+
const where = `op ${i + 1} (${_optionalChain([edits, 'access', _89 => _89[i], 'optionalAccess', _90 => _90.op])})`;
|
|
2111
2158
|
let problem = null;
|
|
2112
2159
|
const walk = (v) => {
|
|
2113
2160
|
if (typeof v === "string") {
|
|
@@ -2140,9 +2187,9 @@ function resolveMotionEditRefs(scene, edits) {
|
|
|
2140
2187
|
resolved.push(edit);
|
|
2141
2188
|
const out = { op: edit.op };
|
|
2142
2189
|
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',
|
|
2190
|
+
if ("shot" in edit && typeof edit.shot === "string") out.shot = edit.op === "update_shot" && _optionalChain([edit, 'access', _91 => _91.set, 'optionalAccess', _92 => _92.id]) ? edit.set.id : edit.shot;
|
|
2144
2191
|
if (edit.op === "add_commands") {
|
|
2145
|
-
const at = _nullishCoalesce(_nullishCoalesce(edit.at, () => ( _optionalChain([before, 'access',
|
|
2192
|
+
const at = _nullishCoalesce(_nullishCoalesce(edit.at, () => ( _optionalChain([before, 'access', _93 => _93.shots, 'access', _94 => _94.find, 'call', _95 => _95((s) => s.id === edit.shot), 'optionalAccess', _96 => _96.run, 'access', _97 => _97.length]))), () => ( 0));
|
|
2146
2193
|
out.indexes = edit.commands.map((_, k) => at + k);
|
|
2147
2194
|
}
|
|
2148
2195
|
if (edit.op === "move_command") out.indexes = [edit.to];
|
|
@@ -2230,7 +2277,7 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
2230
2277
|
const springs = flairAtLeast(flair, "0.39");
|
|
2231
2278
|
const strobes = flairAtLeast(flair, "0.39");
|
|
2232
2279
|
const writeEase = (ease) => typeof ease === "string" || "spring" in ease ? ease : ease.map(round2);
|
|
2233
|
-
const
|
|
2280
|
+
const easeOf2 = (segment) => {
|
|
2234
2281
|
const resolved = _chunk2FHLHZALcjs.resolveEasing.call(void 0, segment.ease);
|
|
2235
2282
|
if (resolved !== "hold" && resolved.type === "spring") {
|
|
2236
2283
|
if (!springs || segment.reverse) return void 0;
|
|
@@ -2301,7 +2348,7 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
2301
2348
|
const wrapper = freshId(`${id}--follow`);
|
|
2302
2349
|
const centre = freshId(`${id}--follow-centre`);
|
|
2303
2350
|
const c = entry.center;
|
|
2304
|
-
const at = _optionalChain([motionNodeOffset, 'call',
|
|
2351
|
+
const at = _optionalChain([motionNodeOffset, 'call', _98 => _98(entry, timeline, 0, nodes), 'optionalAccess', _99 => _99.follow]);
|
|
2305
2352
|
const placed = at ? `translate(${f(at.x)} ${f(at.y)})${at.angle ? ` rotate(${f(at.angle)})` : ""}` : void 0;
|
|
2306
2353
|
(wrap[id] ??= []).push({ id: wrapper, ...placed ? { transform: placed } : {} }, { id: centre, transform: `translate(${f(-c.x)} ${f(-c.y)})` });
|
|
2307
2354
|
follows.push({ id, wrapper, routes: ids, segments: followSegments.filter((seg) => seg.path && ids.has(seg.path)) });
|
|
@@ -2362,7 +2409,7 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
2362
2409
|
exact: () => false,
|
|
2363
2410
|
value: (t) => {
|
|
2364
2411
|
const frame = motionFrameAt(scene, t, { draw: false, offsets: false });
|
|
2365
|
-
const now = _chunkYJLCBA7Vcjs.expandVectorAppearance.call(void 0, _optionalChain([indexMotionNodes, 'call',
|
|
2412
|
+
const now = _chunkYJLCBA7Vcjs.expandVectorAppearance.call(void 0, _optionalChain([indexMotionNodes, 'call', _100 => _100(frame.document.nodes), 'access', _101 => _101.get, 'call', _102 => _102(id), 'optionalAccess', _103 => _103.node]));
|
|
2366
2413
|
if (!now || now.type !== "path") return "";
|
|
2367
2414
|
const bezier = _chunkO2UH7LZEcjs.transformBezierPath.call(void 0, _chunkYJLCBA7Vcjs.vectorPathWorldBezier.call(void 0, now, _chunkYJLCBA7Vcjs.IDENTITY_MATRIX, { effects: false }), inverse);
|
|
2368
2415
|
return _chunkO2UH7LZEcjs.bezierPathToSvg.call(void 0, bezier, { precision: Math.min(precision, 2) });
|
|
@@ -2383,8 +2430,8 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
2383
2430
|
...delay > 0 ? { delay: round2(delay) } : {},
|
|
2384
2431
|
ms: round2(ms),
|
|
2385
2432
|
ease: written,
|
|
2386
|
-
..._optionalChain([segment, 'optionalAccess',
|
|
2387
|
-
..._optionalChain([segment, 'optionalAccess',
|
|
2433
|
+
..._optionalChain([segment, 'optionalAccess', _104 => _104.behavior]) ? { behavior: segment.behavior } : {},
|
|
2434
|
+
..._optionalChain([segment, 'optionalAccess', _105 => _105.steps]) ? { strobe: segment.steps } : {}
|
|
2388
2435
|
};
|
|
2389
2436
|
if (channel.kind === "view") animate.push({ ...base, view: to });
|
|
2390
2437
|
else if (channel.kind === "draw") animate.push({ ...base, draw: [from, to] });
|
|
@@ -2409,7 +2456,7 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
2409
2456
|
const first = active[0];
|
|
2410
2457
|
const key = (x) => JSON.stringify([x.ease, x.behavior, _nullishCoalesce(x.reverse, () => ( false)), _nullishCoalesce(x.steps, () => ( 0))]);
|
|
2411
2458
|
const together = active.every((x) => x.start === a && x.end === b && channel.exact(x) && key(x) === key(first));
|
|
2412
|
-
const curve = together ?
|
|
2459
|
+
const curve = together ? easeOf2(first) : void 0;
|
|
2413
2460
|
const ease = curve && (!overshoots(curve) || channel.overshoot) ? curve : void 0;
|
|
2414
2461
|
if (ease) {
|
|
2415
2462
|
const from = channel.value(a);
|
|
@@ -2436,7 +2483,7 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
2436
2483
|
for (const seg of follow.segments) {
|
|
2437
2484
|
const along = { id: follow.wrapper, along: follow.routes.get(seg.path), ...seg.rotate ? { orient: true } : {} };
|
|
2438
2485
|
const progress = (t) => round2(Math.min(1, Math.max(0, Number(segmentValue(seg, t)))));
|
|
2439
|
-
const curve = seg.end > seg.start ? plays(seg) ?
|
|
2486
|
+
const curve = seg.end > seg.start ? plays(seg) ? easeOf2(seg) : void 0 : "linear";
|
|
2440
2487
|
if (curve && !(overshoots(curve) && seg.rotate)) {
|
|
2441
2488
|
const ends = seg.reverse ? [Number(seg.to), Number(seg.from)] : [Number(seg.from), Number(seg.to)];
|
|
2442
2489
|
animate.push({ ...along, ...seg.start > 0 ? { delay: round2(seg.start) } : {}, ms: round2(seg.end - seg.start), ease: writeEase(curve), ...seg.steps ? { strobe: seg.steps } : {}, at: [round2(ends[0]), round2(ends[1])] });
|
|
@@ -2469,11 +2516,12 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
2469
2516
|
}
|
|
2470
2517
|
animate.sort((x, y) => (_nullishCoalesce(x.step, () => ( 0))) - (_nullishCoalesce(y.step, () => ( 0))) || (_nullishCoalesce(x.delay, () => ( 0))) - (_nullishCoalesce(y.delay, () => ( 0))));
|
|
2471
2518
|
}
|
|
2519
|
+
if (scene.motionBlur) notes.push("Motion blur is drawn on the stage only; Flair plays the scene sharp.");
|
|
2472
2520
|
if (trigger && (_nullishCoalesce(scene.loop, () => ( "none"))) !== "none") notes.push(`The scene plays on ${trigger}, so its loop does not apply in Flair.`);
|
|
2473
2521
|
let svg = _chunkYJLCBA7Vcjs.vectorDocumentToSvg.call(void 0, { ...scene.artwork, nodes: _chunkYJLCBA7Vcjs.expandVectorAppearances.call(void 0, scene.artwork.nodes) }, { pretty: false, precision, wrap });
|
|
2474
2522
|
if (routes.length) svg = svg.replace(/^(<svg[^>]*>)/, `$1<defs>${routes.join("")}</defs>`);
|
|
2475
2523
|
let motion = { svg, animate };
|
|
2476
|
-
if (_optionalChain([scene, 'access',
|
|
2524
|
+
if (_optionalChain([scene, 'access', _106 => _106.controls, 'optionalAccess', _107 => _107.length])) {
|
|
2477
2525
|
const declarations = scene.controls.map((c) => `{{${c.name}: color default=${c.default.toUpperCase()}${c.label ? ` label='${c.label.replace(/'/g, "\u2019")}'` : ""}}}`).join(" ");
|
|
2478
2526
|
const swap = (text) => scene.controls.reduce((s, c) => s.replace(new RegExp(`${c.default}(?![0-9a-fA-F])`, "gi"), `{{${c.name}}}`), text);
|
|
2479
2527
|
svg = svg.replace(/^(<svg[^>]*>)/, `$1<!-- ${declarations} -->`);
|
|
@@ -2495,7 +2543,7 @@ function toFlairVectorEffect(scene, options = {}) {
|
|
|
2495
2543
|
tags: _nullishCoalesce(options.tags, () => ( [])),
|
|
2496
2544
|
motion: out.motion,
|
|
2497
2545
|
hold: out.hold,
|
|
2498
|
-
..._optionalChain([scene, 'access',
|
|
2546
|
+
..._optionalChain([scene, 'access', _108 => _108.presets, 'optionalAccess', _109 => _109.length]) ? { presets: scene.presets } : {},
|
|
2499
2547
|
...out.trigger ? { trigger: out.trigger } : {}
|
|
2500
2548
|
};
|
|
2501
2549
|
}
|
|
@@ -2539,7 +2587,7 @@ function resolvePlaceholders(motion, notes) {
|
|
|
2539
2587
|
const filled = text.replace(PLACEHOLDER, (_, name) => {
|
|
2540
2588
|
const d = declared.get(name);
|
|
2541
2589
|
if (!d) unknown.add(name);
|
|
2542
|
-
return (_nullishCoalesce(_optionalChain([d, 'optionalAccess',
|
|
2590
|
+
return (_nullishCoalesce(_optionalChain([d, 'optionalAccess', _110 => _110.value]), () => ( ""))).replace(/"/g, '\\"');
|
|
2543
2591
|
});
|
|
2544
2592
|
for (const name of unknown) notes.push(`Placeholder "{{${name}}}" has no declaration, so it was left empty.`);
|
|
2545
2593
|
return { motion: JSON.parse(filled), controls };
|
|
@@ -2593,7 +2641,7 @@ function commandsFor(anim, nodes, entered, notes) {
|
|
|
2593
2641
|
if (anim.along) {
|
|
2594
2642
|
const path = nodes.get(anim.along);
|
|
2595
2643
|
if (!path || path.node.type !== "path") notes.push(`${where} follows "${anim.along}", which is not a path in the artwork; the follow was skipped.`);
|
|
2596
|
-
else out.push({ type: "follow", targets, path: anim.along, from: _nullishCoalesce(_optionalChain([anim, 'access',
|
|
2644
|
+
else out.push({ type: "follow", targets, path: anim.along, from: _nullishCoalesce(_optionalChain([anim, 'access', _111 => _111.at, 'optionalAccess', _112 => _112[0]]), () => ( 0)), to: _nullishCoalesce(_optionalChain([anim, 'access', _113 => _113.at, 'optionalAccess', _114 => _114[1]]), () => ( 1)), ...anim.orient ? { rotate: true } : {}, ...timing, ...ease(MOTION_EASE_IN_OUT) });
|
|
2597
2645
|
}
|
|
2598
2646
|
for (const [attr, [a, b]] of Object.entries(_nullishCoalesce(anim.attrs, () => ( {})))) {
|
|
2599
2647
|
if (attr === "opacity") {
|
|
@@ -2604,7 +2652,7 @@ function commandsFor(anim, nodes, entered, notes) {
|
|
|
2604
2652
|
out.push({ type: "enter", targets, style: "fade", ...timing, ...ease(MOTION_EASE_OUT) });
|
|
2605
2653
|
} else out.push({ type: "fade", targets, to: Math.min(1, Math.max(0, to)), ...timing, ...ease(MOTION_EASE_IN_OUT) });
|
|
2606
2654
|
} else if (attr === "fill" || attr === "stroke") {
|
|
2607
|
-
if (_optionalChain([behavior, 'optionalAccess',
|
|
2655
|
+
if (_optionalChain([behavior, 'optionalAccess', _115 => _115.type]) === "pulse") out.push({ type: "color-pulse", targets, color: String(b), paint: attr, count: _nullishCoalesce(behavior.count, () => ( 1)), ...timing, ...ease("linear") });
|
|
2608
2656
|
else {
|
|
2609
2657
|
if (behavior) notes.push(`${where}: a ${attr} ${behavior.type} became a plain colour change.`);
|
|
2610
2658
|
out.push({ type: "style", targets, [attr]: String(b), ...timing, ...ease(MOTION_EASE_IN_OUT) });
|
|
@@ -2679,9 +2727,9 @@ function fromFlairSvgMotion(input, options = {}) {
|
|
|
2679
2727
|
if (!anim.id) continue;
|
|
2680
2728
|
const at = Math.max(0, (_nullishCoalesce(anim.delay, () => ( 0))) - cameraMs);
|
|
2681
2729
|
if (cameraMs && (_nullishCoalesce(anim.delay, () => ( 0))) < cameraMs) notes.push(`Step ${step}: a change to "${anim.id}" during the camera move now starts after it.`);
|
|
2682
|
-
for (const
|
|
2730
|
+
for (const command3 of commandsFor(anim, nodes, entered, notes)) {
|
|
2683
2731
|
const first = run.length === 0;
|
|
2684
|
-
run.push({ ...
|
|
2732
|
+
run.push({ ...command3, ...first ? {} : { with: "previous" }, ...at - last > 0 || first ? { delay: first ? at : at - last } : {} });
|
|
2685
2733
|
if (first && at === 0) delete run[run.length - 1].delay;
|
|
2686
2734
|
last = at;
|
|
2687
2735
|
}
|
|
@@ -2785,6 +2833,7 @@ function toLottie(scene, options = {}) {
|
|
|
2785
2833
|
const timeline = compileMotionScene(scene);
|
|
2786
2834
|
const nodes = indexMotionNodes(scene.artwork.nodes);
|
|
2787
2835
|
const notes = [];
|
|
2836
|
+
if (scene.motionBlur) notes.push("Motion blur is drawn on the stage only; the Lottie plays sharp.");
|
|
2788
2837
|
if (scene.trigger) notes.push(`The scene plays on ${scene.trigger}; Lottie has no trigger, so it plays through (markers show where each shot starts, for a player to stop at).`);
|
|
2789
2838
|
let sampled = 0;
|
|
2790
2839
|
const track = (id, p) => _nullishCoalesce(timeline.tracks.get(motionTrackKey(id, p)), () => ( []));
|
|
@@ -2893,7 +2942,7 @@ function toLottie(scene, options = {}) {
|
|
|
2893
2942
|
const shapeSegments = [...timeline.tracks.entries()].filter(([k]) => k.startsWith(`${path.id}\0`) && (k.endsWith("\0shape") || k.includes("\0effect:"))).flatMap(([, v]) => v);
|
|
2894
2943
|
const geometryAt = (t) => {
|
|
2895
2944
|
if (!shapeSegments.length) return _chunkYJLCBA7Vcjs.vectorPathWorldBezier.call(void 0, path);
|
|
2896
|
-
const now = _optionalChain([indexMotionNodes, 'call',
|
|
2945
|
+
const now = _optionalChain([indexMotionNodes, 'call', _116 => _116(motionFrameAt(scene, t, { draw: false, offsets: false }).document.nodes), 'access', _117 => _117.get, 'call', _118 => _118(path.id), 'optionalAccess', _119 => _119.node]);
|
|
2897
2946
|
return now && now.type === "path" ? _chunkYJLCBA7Vcjs.vectorPathWorldBezier.call(void 0, _chunkYJLCBA7Vcjs.expandVectorAppearances.call(void 0, [now])[0]) : _chunkYJLCBA7Vcjs.vectorPathWorldBezier.call(void 0, path);
|
|
2898
2947
|
};
|
|
2899
2948
|
const rest = lottieContours(_chunkO2UH7LZEcjs.toBezierPath.call(void 0, geometryAt(0)), round2);
|
|
@@ -2930,28 +2979,28 @@ function toLottie(scene, options = {}) {
|
|
|
2930
2979
|
}
|
|
2931
2980
|
const stroke = paintOf(path.stroke, "stroke", path.id);
|
|
2932
2981
|
if (stroke || strokeFrom) {
|
|
2933
|
-
const
|
|
2982
|
+
const colour2 = strokeFrom ? track(strokeFrom, "stroke") : [];
|
|
2934
2983
|
items.push({
|
|
2935
2984
|
ty: "st",
|
|
2936
2985
|
nm: "Stroke",
|
|
2937
|
-
c:
|
|
2986
|
+
c: colour2.length ? animate(colour2, (t) => _nullishCoalesce(_nullishCoalesce(rgba(trackValue(colour2, t)), () => ( stroke)), () => ( [0, 0, 0, 1]))) : { a: 0, k: (_nullishCoalesce(stroke, () => ( [0, 0, 0, 1]))).map(round2) },
|
|
2938
2987
|
o: { a: 0, k: 100 },
|
|
2939
2988
|
w: widthFrom ? animate(track(widthFrom, "strokeWidth"), (t) => [Math.max(0, Number(_nullishCoalesce(_nullishCoalesce(trackValue(track(widthFrom, "strokeWidth"), t), () => ( path.strokeWidth)), () => ( 1))))]) : { a: 0, k: round2(_nullishCoalesce(path.strokeWidth, () => ( 1))) },
|
|
2940
2989
|
lc: path.strokeLinecap === "round" ? 2 : path.strokeLinecap === "square" ? 3 : 1,
|
|
2941
2990
|
lj: path.strokeLinejoin === "round" ? 2 : path.strokeLinejoin === "bevel" ? 3 : 1,
|
|
2942
2991
|
ml: 4
|
|
2943
2992
|
});
|
|
2944
|
-
if (_optionalChain([path, 'access',
|
|
2993
|
+
if (_optionalChain([path, 'access', _120 => _120.strokeDasharray, 'optionalAccess', _121 => _121.length])) notes.push(`"${path.id}": its dashes are not exported; the stroke is solid.`);
|
|
2945
2994
|
if (path.strokeAlign && path.strokeAlign !== "center") notes.push(`"${path.id}": its stroke is drawn centred (Lottie has no inside or outside strokes).`);
|
|
2946
2995
|
}
|
|
2947
2996
|
const fill = paintOf(path.fill, "fill", path.id);
|
|
2948
2997
|
if (fill || fillFrom) {
|
|
2949
|
-
const
|
|
2998
|
+
const colour2 = fillFrom ? track(fillFrom, "fill") : [];
|
|
2950
2999
|
const fade = fadeFrom ? track(fadeFrom, "fillOpacity") : [];
|
|
2951
3000
|
items.push({
|
|
2952
3001
|
ty: "fl",
|
|
2953
3002
|
nm: "Fill",
|
|
2954
|
-
c:
|
|
3003
|
+
c: colour2.length ? animate(colour2, (t) => _nullishCoalesce(_nullishCoalesce(rgba(trackValue(colour2, t)), () => ( fill)), () => ( [0, 0, 0, 1]))) : { a: 0, k: (_nullishCoalesce(fill, () => ( [0, 0, 0, 1]))).map(round2) },
|
|
2955
3004
|
o: fade.length ? animate(fade, (t) => [Math.min(1, Math.max(0, Number(_nullishCoalesce(trackValue(fade, t), () => ( 1))))) * 100]) : { a: 0, k: 100 },
|
|
2956
3005
|
r: path.fillRule === "evenodd" ? 2 : 1
|
|
2957
3006
|
});
|
|
@@ -3025,6 +3074,383 @@ function toLottie(scene, options = {}) {
|
|
|
3025
3074
|
return { lottie, sampled, notes: [...new Set(notes)] };
|
|
3026
3075
|
}
|
|
3027
3076
|
|
|
3077
|
+
// src/animation-editor/pptx.ts
|
|
3078
|
+
var EMU = 9525;
|
|
3079
|
+
var kids = (el, name) => el ? Array.from(el.children).filter((c) => c.localName === name) : [];
|
|
3080
|
+
var kid = (el, name) => _nullishCoalesce(kids(el, name)[0], () => ( null));
|
|
3081
|
+
var deep = (el, name) => el ? Array.from(el.getElementsByTagNameNS("*", name)) : [];
|
|
3082
|
+
var num = (el, attr, fallback = 0) => {
|
|
3083
|
+
const v = _optionalChain([el, 'optionalAccess', _122 => _122.getAttribute, 'call', _123 => _123(attr)]);
|
|
3084
|
+
const n = v === null || v === void 0 ? NaN : Number(v);
|
|
3085
|
+
return Number.isFinite(n) ? n : fallback;
|
|
3086
|
+
};
|
|
3087
|
+
var dirname = (p) => p.slice(0, p.lastIndexOf("/") + 1);
|
|
3088
|
+
function resolve(base, target) {
|
|
3089
|
+
const out = [];
|
|
3090
|
+
for (const seg of (target.startsWith("/") ? target.slice(1) : base + target).split("/")) {
|
|
3091
|
+
if (seg === "..") out.pop();
|
|
3092
|
+
else if (seg && seg !== ".") out.push(seg);
|
|
3093
|
+
}
|
|
3094
|
+
return out.join("/");
|
|
3095
|
+
}
|
|
3096
|
+
async function xml(ctx, path) {
|
|
3097
|
+
if (!ctx.zip.has(path)) return null;
|
|
3098
|
+
const text = new TextDecoder().decode(await ctx.zip.read(path));
|
|
3099
|
+
const doc = new ctx.Parser().parseFromString(text, "application/xml");
|
|
3100
|
+
return doc.getElementsByTagName("parsererror").length ? null : doc;
|
|
3101
|
+
}
|
|
3102
|
+
async function rels(ctx, part) {
|
|
3103
|
+
const doc = await xml(ctx, `${dirname(part)}_rels/${part.slice(part.lastIndexOf("/") + 1)}.rels`);
|
|
3104
|
+
const out = /* @__PURE__ */ new Map();
|
|
3105
|
+
for (const r of deep(doc, "Relationship")) if (r.getAttribute("TargetMode") !== "External") out.set(_nullishCoalesce(r.getAttribute("Id"), () => ( "")), resolve(dirname(part), _nullishCoalesce(r.getAttribute("Target"), () => ( ""))));
|
|
3106
|
+
return out;
|
|
3107
|
+
}
|
|
3108
|
+
function colour(ctx, holder) {
|
|
3109
|
+
if (!holder) return null;
|
|
3110
|
+
const c = holder.firstElementChild;
|
|
3111
|
+
if (!c) return null;
|
|
3112
|
+
let hex = null;
|
|
3113
|
+
if (c.localName === "srgbClr") hex = `#${(_nullishCoalesce(c.getAttribute("val"), () => ( "000000"))).toLowerCase()}`;
|
|
3114
|
+
else if (c.localName === "sysClr") hex = `#${(_nullishCoalesce(c.getAttribute("lastClr"), () => ( "000000"))).toLowerCase()}`;
|
|
3115
|
+
else if (c.localName === "schemeClr") {
|
|
3116
|
+
const name = _nullishCoalesce(c.getAttribute("val"), () => ( ""));
|
|
3117
|
+
const alias = { tx1: "dk1", bg1: "lt1", tx2: "dk2", bg2: "lt2" };
|
|
3118
|
+
hex = _nullishCoalesce(ctx.theme.get(_nullishCoalesce(alias[name], () => ( name))), () => ( null));
|
|
3119
|
+
} else if (c.localName === "prstClr") hex = _nullishCoalesce({ black: "#000000", white: "#ffffff", red: "#ff0000", green: "#008000", blue: "#0000ff" }[_nullishCoalesce(c.getAttribute("val"), () => ( ""))], () => ( null));
|
|
3120
|
+
if (!hex || !/^#[0-9a-f]{6}$/.test(hex)) return null;
|
|
3121
|
+
const lum = kid(c, "lumMod");
|
|
3122
|
+
if (lum) {
|
|
3123
|
+
const k = num(lum, "val", 1e5) / 1e5;
|
|
3124
|
+
const off = num(kid(c, "lumOff"), "val", 0) / 1e5;
|
|
3125
|
+
hex = `#${[1, 3, 5].map((i) => Math.round(Math.min(255, Math.max(0, parseInt(hex.slice(i, i + 2), 16) * k + 255 * off))).toString(16).padStart(2, "0")).join("")}`;
|
|
3126
|
+
}
|
|
3127
|
+
return hex;
|
|
3128
|
+
}
|
|
3129
|
+
function preset(prst, w, h) {
|
|
3130
|
+
const poly = (pts) => [{ closed: true, anchors: pts.map(([x, y]) => ({ x, y })) }];
|
|
3131
|
+
switch (prst) {
|
|
3132
|
+
case "rect":
|
|
3133
|
+
return [_chunkYJLCBA7Vcjs.rectSubpath.call(void 0, { x: 0, y: 0, width: w, height: h })];
|
|
3134
|
+
case "roundRect":
|
|
3135
|
+
return [_chunkYJLCBA7Vcjs.rectSubpath.call(void 0, { x: 0, y: 0, width: w, height: h }, Math.min(w, h) * 0.16667)];
|
|
3136
|
+
case "ellipse":
|
|
3137
|
+
return [_chunkYJLCBA7Vcjs.ellipseSubpath.call(void 0, { x: 0, y: 0, width: w, height: h })];
|
|
3138
|
+
case "triangle":
|
|
3139
|
+
return poly([[w / 2, 0], [w, h], [0, h]]);
|
|
3140
|
+
case "rtTriangle":
|
|
3141
|
+
return poly([[0, 0], [w, h], [0, h]]);
|
|
3142
|
+
case "diamond":
|
|
3143
|
+
return poly([[w / 2, 0], [w, h / 2], [w / 2, h], [0, h / 2]]);
|
|
3144
|
+
case "parallelogram":
|
|
3145
|
+
return poly([[w * 0.25, 0], [w, 0], [w * 0.75, h], [0, h]]);
|
|
3146
|
+
case "hexagon":
|
|
3147
|
+
return poly([[w * 0.25, 0], [w * 0.75, 0], [w, h / 2], [w * 0.75, h], [w * 0.25, h], [0, h / 2]]);
|
|
3148
|
+
case "line":
|
|
3149
|
+
case "straightConnector1":
|
|
3150
|
+
return [{ closed: false, anchors: [{ x: 0, y: 0 }, { x: w, y: h }] }];
|
|
3151
|
+
default:
|
|
3152
|
+
return null;
|
|
3153
|
+
}
|
|
3154
|
+
}
|
|
3155
|
+
function custom(geom, w, h) {
|
|
3156
|
+
const out = [];
|
|
3157
|
+
for (const path of deep(geom, "path")) {
|
|
3158
|
+
const sx = w / (num(path, "w") || w || 1);
|
|
3159
|
+
const sy = h / (num(path, "h") || h || 1);
|
|
3160
|
+
const pt = (el) => ({ x: num(el, "x") * sx, y: num(el, "y") * sy });
|
|
3161
|
+
let cur = null;
|
|
3162
|
+
for (const cmd of Array.from(path.children)) {
|
|
3163
|
+
const pts = kids(cmd, "pt").map(pt);
|
|
3164
|
+
if (cmd.localName === "moveTo" && pts[0]) {
|
|
3165
|
+
if (cur) out.push(cur);
|
|
3166
|
+
cur = { closed: false, anchors: [{ x: pts[0].x, y: pts[0].y }] };
|
|
3167
|
+
} else if (!cur) continue;
|
|
3168
|
+
else if (cmd.localName === "lnTo" && pts[0]) cur.anchors.push({ x: pts[0].x, y: pts[0].y });
|
|
3169
|
+
else if (cmd.localName === "cubicBezTo" && pts.length === 3) {
|
|
3170
|
+
cur.anchors[cur.anchors.length - 1].out = pts[0];
|
|
3171
|
+
cur.anchors.push({ x: pts[2].x, y: pts[2].y, in: pts[1] });
|
|
3172
|
+
} else if (cmd.localName === "quadBezTo" && pts.length === 2) {
|
|
3173
|
+
const p0 = cur.anchors[cur.anchors.length - 1];
|
|
3174
|
+
cur.anchors[cur.anchors.length - 1].out = { x: p0.x + 2 / 3 * (pts[0].x - p0.x), y: p0.y + 2 / 3 * (pts[0].y - p0.y) };
|
|
3175
|
+
cur.anchors.push({ x: pts[1].x, y: pts[1].y, in: { x: pts[1].x + 2 / 3 * (pts[0].x - pts[1].x), y: pts[1].y + 2 / 3 * (pts[0].y - pts[1].y) } });
|
|
3176
|
+
} else if (cmd.localName === "close") {
|
|
3177
|
+
cur.closed = true;
|
|
3178
|
+
out.push(cur);
|
|
3179
|
+
cur = null;
|
|
3180
|
+
}
|
|
3181
|
+
}
|
|
3182
|
+
if (cur) out.push(cur);
|
|
3183
|
+
}
|
|
3184
|
+
return out;
|
|
3185
|
+
}
|
|
3186
|
+
function placement(xfrm, map) {
|
|
3187
|
+
const off = kid(xfrm, "off");
|
|
3188
|
+
const ext = kid(xfrm, "ext");
|
|
3189
|
+
const [x, y, w, h] = map(num(off, "x"), num(off, "y"), num(ext, "cx"), num(ext, "cy"));
|
|
3190
|
+
const rot = num(xfrm, "rot") / 6e4 * (Math.PI / 180);
|
|
3191
|
+
const fx = _optionalChain([xfrm, 'optionalAccess', _124 => _124.getAttribute, 'call', _125 => _125("flipH")]) === "1" ? -1 : 1;
|
|
3192
|
+
const fy = _optionalChain([xfrm, 'optionalAccess', _126 => _126.getAttribute, 'call', _127 => _127("flipV")]) === "1" ? -1 : 1;
|
|
3193
|
+
const cx = x + w / 2;
|
|
3194
|
+
const cy = y + h / 2;
|
|
3195
|
+
const cos = Math.cos(rot);
|
|
3196
|
+
const sin = Math.sin(rot);
|
|
3197
|
+
const a = cos * fx;
|
|
3198
|
+
const b = sin * fx;
|
|
3199
|
+
const c = -sin * fy;
|
|
3200
|
+
const d = cos * fy;
|
|
3201
|
+
const e = cx - a * (w / 2) - c * (h / 2);
|
|
3202
|
+
const f = cy - b * (w / 2) - d * (h / 2);
|
|
3203
|
+
return { x, y, w, h, transform: [a, b, c, d, e, f].map((v) => v + 0) };
|
|
3204
|
+
}
|
|
3205
|
+
var idFor = (ctx, spid, name) => {
|
|
3206
|
+
const base = `${name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "shape"}-${spid}`;
|
|
3207
|
+
let id = base;
|
|
3208
|
+
for (let k = 2; ctx.used.has(id); k++) id = `${base}-${k}`;
|
|
3209
|
+
ctx.used.add(id);
|
|
3210
|
+
ctx.nodes.set(spid, id);
|
|
3211
|
+
return id;
|
|
3212
|
+
};
|
|
3213
|
+
function shapes(ctx, tree, map) {
|
|
3214
|
+
const out = [];
|
|
3215
|
+
for (const el of Array.from(tree.children)) {
|
|
3216
|
+
const nv = el.firstElementChild;
|
|
3217
|
+
const cNvPr = kid(nv, "cNvPr");
|
|
3218
|
+
const spid = num(cNvPr, "id", -1);
|
|
3219
|
+
const name = _nullishCoalesce(_optionalChain([cNvPr, 'optionalAccess', _128 => _128.getAttribute, 'call', _129 => _129("name")]), () => ( ""));
|
|
3220
|
+
if (el.localName === "grpSp") {
|
|
3221
|
+
const xfrm = kid(kid(el, "grpSpPr"), "xfrm");
|
|
3222
|
+
const off = kid(xfrm, "off");
|
|
3223
|
+
const ext = kid(xfrm, "ext");
|
|
3224
|
+
const chOff = kid(xfrm, "chOff");
|
|
3225
|
+
const chExt = kid(xfrm, "chExt");
|
|
3226
|
+
const sx = num(ext, "cx") / (num(chExt, "cx") || num(ext, "cx") || 1);
|
|
3227
|
+
const sy = num(ext, "cy") / (num(chExt, "cy") || num(ext, "cy") || 1);
|
|
3228
|
+
const inner = (x, y, w2, h2) => map(num(off, "x") + (x - num(chOff, "x")) * sx, num(off, "y") + (y - num(chOff, "y")) * sy, w2 * sx, h2 * sy);
|
|
3229
|
+
const id = idFor(ctx, spid, name || "group");
|
|
3230
|
+
out.push({ id, type: "group", ...name ? { name } : {}, children: shapes(ctx, el, inner) });
|
|
3231
|
+
continue;
|
|
3232
|
+
}
|
|
3233
|
+
if (el.localName === "pic" || el.localName === "graphicFrame") {
|
|
3234
|
+
ctx.notes.push(`"${name || `shape ${spid}`}" is a ${el.localName === "pic" ? "picture" : "chart, table or diagram"}, which the artwork cannot hold; it was left out.`);
|
|
3235
|
+
continue;
|
|
3236
|
+
}
|
|
3237
|
+
if (el.localName !== "sp" && el.localName !== "cxnSp") continue;
|
|
3238
|
+
const spPr = kid(el, "spPr");
|
|
3239
|
+
const { w, h, transform } = placement(kid(spPr, "xfrm"), map);
|
|
3240
|
+
const prst = _optionalChain([kid, 'call', _130 => _130(spPr, "prstGeom"), 'optionalAccess', _131 => _131.getAttribute, 'call', _132 => _132("prst")]);
|
|
3241
|
+
const cust = kid(spPr, "custGeom");
|
|
3242
|
+
let subpaths = cust ? custom(cust, w, h) : prst ? preset(prst, w, h) : null;
|
|
3243
|
+
if (!subpaths) {
|
|
3244
|
+
if (prst) ctx.notes.push(`"${name}" is a ${prst}, drawn as its bounding rectangle.`);
|
|
3245
|
+
subpaths = [_chunkYJLCBA7Vcjs.rectSubpath.call(void 0, { x: 0, y: 0, width: w, height: h })];
|
|
3246
|
+
}
|
|
3247
|
+
const style = kid(el, "style");
|
|
3248
|
+
const fill = kid(spPr, "noFill") ? null : _nullishCoalesce(colour(ctx, kid(spPr, "solidFill")), () => ( (el.localName === "cxnSp" ? null : colour(ctx, kid(style, "fillRef")))));
|
|
3249
|
+
const ln = kid(spPr, "ln");
|
|
3250
|
+
const stroke = kid(ln, "noFill") ? null : _nullishCoalesce(colour(ctx, kid(ln, "solidFill")), () => ( colour(ctx, kid(style, "lnRef"))));
|
|
3251
|
+
if (kid(spPr, "gradFill")) ctx.notes.push(`"${name}" has a gradient fill, imported as its first colour or none.`);
|
|
3252
|
+
const text = deep(kid(el, "txBody"), "t").map((t) => _nullishCoalesce(t.textContent, () => ( ""))).join("").trim();
|
|
3253
|
+
if (text) ctx.notes.push(`"${name}" has text ("${text.slice(0, 40)}${text.length > 40 ? "\u2026" : ""}"), which the artwork cannot hold; only its shape was imported.`);
|
|
3254
|
+
const node = {
|
|
3255
|
+
id: idFor(ctx, spid, name),
|
|
3256
|
+
type: "path",
|
|
3257
|
+
...name ? { name } : {},
|
|
3258
|
+
transform,
|
|
3259
|
+
fill: _nullishCoalesce(fill, () => ( (kid(spPr, "gradFill") ? colour(ctx, kid(deep(spPr, "gs")[0], "srgbClr") ? deep(spPr, "gs")[0] : null) : null))),
|
|
3260
|
+
stroke,
|
|
3261
|
+
strokeWidth: Math.max(0.5, (num(ln, "w", 12700) || 12700) / EMU),
|
|
3262
|
+
subpaths
|
|
3263
|
+
};
|
|
3264
|
+
out.push(node);
|
|
3265
|
+
}
|
|
3266
|
+
return out;
|
|
3267
|
+
}
|
|
3268
|
+
var DIRECTION_FROM = { 1: "down", 2: "left", 4: "up", 8: "right" };
|
|
3269
|
+
var DIRECTION_TO = { 1: "up", 2: "right", 4: "down", 8: "left" };
|
|
3270
|
+
function easeOf(ctn) {
|
|
3271
|
+
const accel = num(ctn, "accel") / 1e5;
|
|
3272
|
+
const decel = num(ctn, "decel") / 1e5;
|
|
3273
|
+
if (!accel && !decel) return void 0;
|
|
3274
|
+
return { type: "cubic-bezier", x1: Math.min(1, accel), y1: 0, x2: Math.max(0, 1 - decel), y2: 1 };
|
|
3275
|
+
}
|
|
3276
|
+
function command(ctx, ctn, scene, artwork, target, hidden) {
|
|
3277
|
+
const cls = ctn.getAttribute("presetClass");
|
|
3278
|
+
const id = num(ctn, "presetID");
|
|
3279
|
+
const sub = num(ctn, "presetSubtype");
|
|
3280
|
+
const durs = [ctn, ...deep(ctn, "cTn")].map((c) => num(c, "dur", NaN)).filter(Number.isFinite);
|
|
3281
|
+
const duration = durs.length ? Math.max(...durs) : void 0;
|
|
3282
|
+
const ease = easeOf(ctn);
|
|
3283
|
+
const base = { targets: [target], ...duration !== void 0 ? { duration } : {}, ...ease ? { ease } : {} };
|
|
3284
|
+
const repeat = num(ctn, "repeatCount", 1e3) / 1e3;
|
|
3285
|
+
const auto = ctn.getAttribute("autoRev") === "1";
|
|
3286
|
+
const timing = auto || repeat > 1 ? { repeat: Math.max(1, Math.round(repeat)) * (auto ? 2 : 1), ...auto ? { repeatMode: "mirror" } : {} } : {};
|
|
3287
|
+
const label = `${cls} effect ${id} on "${target}"`;
|
|
3288
|
+
if (cls === "entr") {
|
|
3289
|
+
const by = {
|
|
3290
|
+
1: { type: "enter", ...base, duration: 0 },
|
|
3291
|
+
2: { type: "enter", style: "slide", ...base, direction: _nullishCoalesce(DIRECTION_FROM[sub], () => ( "up")), distance: Math.round(Math.max(scene.width, scene.height) / 3) },
|
|
3292
|
+
10: { type: "enter", style: "fade", ...base },
|
|
3293
|
+
9: { type: "enter", style: "fade", ...base },
|
|
3294
|
+
22: { type: "enter", style: "wipe", ...base, direction: _nullishCoalesce(DIRECTION_FROM[sub], () => ( "right")) },
|
|
3295
|
+
16: { type: "enter", style: "split", ...base, orientation: sub === 26 || sub === 42 ? "vertical" : "horizontal" },
|
|
3296
|
+
6: { type: "enter", style: "iris", ...base, shape: "circle" },
|
|
3297
|
+
4: { type: "enter", style: "iris", ...base, shape: "box" },
|
|
3298
|
+
53: { type: "enter", style: "zoom", ...base },
|
|
3299
|
+
23: { type: "enter", style: "zoom", ...base },
|
|
3300
|
+
31: { type: "enter", style: "pop", ...base },
|
|
3301
|
+
26: { type: "enter", style: "pop", ...base },
|
|
3302
|
+
55: { type: "enter", style: "grow", ...base },
|
|
3303
|
+
37: { type: "enter", style: "rise", ...base },
|
|
3304
|
+
42: { type: "enter", style: "rise", ...base }
|
|
3305
|
+
};
|
|
3306
|
+
if (by[id]) return by[id];
|
|
3307
|
+
ctx.notes.push(`${label} has no match here; it fades in instead.`);
|
|
3308
|
+
return { type: "enter", style: "fade", ...base };
|
|
3309
|
+
}
|
|
3310
|
+
if (cls === "exit") {
|
|
3311
|
+
const by = {
|
|
3312
|
+
1: { type: "exit", ...base, duration: 0 },
|
|
3313
|
+
10: { type: "exit", style: "fade", ...base },
|
|
3314
|
+
9: { type: "exit", style: "fade", ...base },
|
|
3315
|
+
2: { type: "exit", style: "slide", ...base, direction: _nullishCoalesce(DIRECTION_TO[sub], () => ( "down")), distance: Math.round(Math.max(scene.width, scene.height) / 3) },
|
|
3316
|
+
22: { type: "exit", style: "wipe", ...base, direction: _nullishCoalesce(DIRECTION_TO[sub], () => ( "right")) },
|
|
3317
|
+
53: { type: "exit", style: "shrink", ...base },
|
|
3318
|
+
31: { type: "exit", style: "shrink", ...base }
|
|
3319
|
+
};
|
|
3320
|
+
if (by[id]) return by[id];
|
|
3321
|
+
ctx.notes.push(`${label} has no match here; it fades out instead.`);
|
|
3322
|
+
return { type: "exit", style: "fade", ...base };
|
|
3323
|
+
}
|
|
3324
|
+
if (cls === "emph") {
|
|
3325
|
+
const rot = deep(ctn, "animRot")[0];
|
|
3326
|
+
const scale = kid(deep(ctn, "animScale")[0], "by");
|
|
3327
|
+
const clr = colour(ctx, kid(deep(ctn, "animClr")[0], "to"));
|
|
3328
|
+
switch (id) {
|
|
3329
|
+
case 26:
|
|
3330
|
+
return { type: "pulse", ...base, ...timing };
|
|
3331
|
+
case 27:
|
|
3332
|
+
return { type: "color-pulse", ...base, ...clr ? { color: clr } : {}, ...timing };
|
|
3333
|
+
case 32:
|
|
3334
|
+
return { type: "teeter", ...base, ...timing };
|
|
3335
|
+
case 34:
|
|
3336
|
+
return { type: "wave", ...base, ...timing };
|
|
3337
|
+
case 8:
|
|
3338
|
+
return { type: "rotate", ...base, by: rot ? num(rot, "by") / 6e4 : 360, ...timing };
|
|
3339
|
+
case 6:
|
|
3340
|
+
return { type: "scale", ...base, to: scale ? { x: num(scale, "x", 1e5) / 1e5, y: num(scale, "y", 1e5) / 1e5 } : 1.5, ...timing };
|
|
3341
|
+
case 9:
|
|
3342
|
+
return { type: "fade", ...base, to: 0.5, ...timing };
|
|
3343
|
+
case 1:
|
|
3344
|
+
case 19:
|
|
3345
|
+
return clr ? { type: "style", ...base, fill: clr, ...timing } : null;
|
|
3346
|
+
case 7:
|
|
3347
|
+
return clr ? { type: "style", ...base, stroke: clr, ...timing } : null;
|
|
3348
|
+
}
|
|
3349
|
+
ctx.notes.push(`${label} has no match here and was skipped.`);
|
|
3350
|
+
return null;
|
|
3351
|
+
}
|
|
3352
|
+
if (cls === "path") {
|
|
3353
|
+
const motion = deep(ctn, "animMotion")[0];
|
|
3354
|
+
const d = _optionalChain([motion, 'optionalAccess', _133 => _133.getAttribute, 'call', _134 => _134("path")]);
|
|
3355
|
+
if (!d) return null;
|
|
3356
|
+
const entry = artwork.flatMap(function all(n) {
|
|
3357
|
+
return n.type === "group" ? [n, ...n.children.flatMap(all)] : [n];
|
|
3358
|
+
}).find((n) => n.id === target);
|
|
3359
|
+
const t = _nullishCoalesce(_optionalChain([entry, 'optionalAccess', _135 => _135.transform]), () => ( [1, 0, 0, 1, 0, 0]));
|
|
3360
|
+
const p = _optionalChain([entry, 'optionalAccess', _136 => _136.type]) === "path" ? _optionalChain([entry, 'access', _137 => _137.subpaths, 'access', _138 => _138[0], 'optionalAccess', _139 => _139.anchors]) : void 0;
|
|
3361
|
+
const xs = (_nullishCoalesce(p, () => ( []))).map((a) => a.x);
|
|
3362
|
+
const ys = (_nullishCoalesce(p, () => ( []))).map((a) => a.y);
|
|
3363
|
+
const lx = xs.length ? (Math.min(...xs) + Math.max(...xs)) / 2 : 0;
|
|
3364
|
+
const ly = ys.length ? (Math.min(...ys) + Math.max(...ys)) / 2 : 0;
|
|
3365
|
+
const cx = t[0] * lx + t[2] * ly + t[4];
|
|
3366
|
+
const cy = t[1] * lx + t[3] * ly + t[5];
|
|
3367
|
+
const nums = _nullishCoalesce(d.match(/[MLCZE]|-?[\d.]+(?:e-?\d+)?/gi), () => ( []));
|
|
3368
|
+
const sp = { closed: false, anchors: [] };
|
|
3369
|
+
let i = 0;
|
|
3370
|
+
const read = () => ({ x: cx + Number(nums[i++]) * scene.width, y: cy + Number(nums[i++]) * scene.height });
|
|
3371
|
+
while (i < nums.length) {
|
|
3372
|
+
const c = nums[i++].toUpperCase();
|
|
3373
|
+
if (c === "M" || c === "L") sp.anchors.push(read());
|
|
3374
|
+
else if (c === "C") {
|
|
3375
|
+
const c1 = read();
|
|
3376
|
+
const c2 = read();
|
|
3377
|
+
const e = read();
|
|
3378
|
+
if (sp.anchors.length) sp.anchors[sp.anchors.length - 1].out = c1;
|
|
3379
|
+
sp.anchors.push({ ...e, in: c2 });
|
|
3380
|
+
} else if (c === "Z") sp.closed = true;
|
|
3381
|
+
}
|
|
3382
|
+
if (sp.anchors.length < 2) return null;
|
|
3383
|
+
const pathId = `${target}-path-${hidden.length + 1}`;
|
|
3384
|
+
hidden.push({ id: pathId, type: "path", name: "Motion path", visible: false, fill: null, stroke: "#94a3b8", subpaths: [sp] });
|
|
3385
|
+
return { type: "follow", ...base, path: pathId, ...timing };
|
|
3386
|
+
}
|
|
3387
|
+
return null;
|
|
3388
|
+
}
|
|
3389
|
+
async function fromPptxAnimation(data, options = {}) {
|
|
3390
|
+
const Parser = _nullishCoalesce(options.DOMParser, () => ( globalThis.DOMParser));
|
|
3391
|
+
if (!Parser) throw new Error("No DOMParser: pass one in options (jsdom's or xmldom's in Node).");
|
|
3392
|
+
let zip;
|
|
3393
|
+
try {
|
|
3394
|
+
zip = await _chunkEMQWJA3Gcjs.readZip.call(void 0, data, options);
|
|
3395
|
+
} catch (error) {
|
|
3396
|
+
throw new Error(`This is not a PowerPoint file (.pptx): ${error.message}`);
|
|
3397
|
+
}
|
|
3398
|
+
const ctx = { zip, Parser, notes: [], theme: /* @__PURE__ */ new Map(), nodes: /* @__PURE__ */ new Map(), used: /* @__PURE__ */ new Set() };
|
|
3399
|
+
const pres = await xml(ctx, "ppt/presentation.xml");
|
|
3400
|
+
if (!pres) throw new Error("This is not a PowerPoint file (.pptx): it has no ppt/presentation.xml.");
|
|
3401
|
+
const presRels = await rels(ctx, "ppt/presentation.xml");
|
|
3402
|
+
const slidePaths = deep(pres, "sldId").map((s) => _nullishCoalesce(presRels.get(_nullishCoalesce(_nullishCoalesce(s.getAttributeNS("http://schemas.openxmlformats.org/officeDocument/2006/relationships", "id"), () => ( s.getAttribute("r:id"))), () => ( ""))), () => ( ""))).filter(Boolean);
|
|
3403
|
+
const n = _nullishCoalesce(options.slide, () => ( 1));
|
|
3404
|
+
if (!(Number.isInteger(n) && n >= 1 && n <= slidePaths.length)) throw new Error(`There is no slide ${n}; the file has ${slidePaths.length} slide${slidePaths.length === 1 ? "" : "s"}.`);
|
|
3405
|
+
const size = deep(pres, "sldSz")[0];
|
|
3406
|
+
const width = Math.round(num(size, "cx", 12192e3) / EMU);
|
|
3407
|
+
const height = Math.round(num(size, "cy", 6858e3) / EMU);
|
|
3408
|
+
for (const [, path] of presRels) {
|
|
3409
|
+
if (!path.includes("theme/")) continue;
|
|
3410
|
+
const theme = await xml(ctx, path);
|
|
3411
|
+
const scheme = deep(theme, "clrScheme")[0];
|
|
3412
|
+
for (const c of Array.from(_nullishCoalesce(_optionalChain([scheme, 'optionalAccess', _140 => _140.children]), () => ( [])))) {
|
|
3413
|
+
const v = c.firstElementChild;
|
|
3414
|
+
const hex = _optionalChain([v, 'optionalAccess', _141 => _141.localName]) === "srgbClr" ? v.getAttribute("val") : _optionalChain([v, 'optionalAccess', _142 => _142.getAttribute, 'call', _143 => _143("lastClr")]);
|
|
3415
|
+
if (hex) ctx.theme.set(c.localName, `#${hex.toLowerCase()}`);
|
|
3416
|
+
}
|
|
3417
|
+
break;
|
|
3418
|
+
}
|
|
3419
|
+
const slide = await xml(ctx, slidePaths[n - 1]);
|
|
3420
|
+
if (!slide) throw new Error(`Slide ${n} cannot be read.`);
|
|
3421
|
+
const tree = deep(slide, "spTree")[0];
|
|
3422
|
+
const toPx = (x, y, w, h) => [x / EMU, y / EMU, w / EMU, h / EMU];
|
|
3423
|
+
const nodes = tree ? shapes(ctx, tree, toPx) : [];
|
|
3424
|
+
const bg = _nullishCoalesce(colour(ctx, _nullishCoalesce(deep(deep(slide, "bg")[0], "solidFill")[0], () => ( null))), () => ( "#ffffff"));
|
|
3425
|
+
const hidden = [];
|
|
3426
|
+
const shots = [];
|
|
3427
|
+
const effects = deep(deep(slide, "timing")[0], "cTn").filter((c) => c.hasAttribute("presetClass"));
|
|
3428
|
+
for (const ctn of effects) {
|
|
3429
|
+
const spid = num(deep(ctn, "spTgt")[0], "spid", -1);
|
|
3430
|
+
const target = ctx.nodes.get(spid);
|
|
3431
|
+
if (!target) {
|
|
3432
|
+
ctx.notes.push(`An effect targets shape ${spid}, which was not imported; it was skipped.`);
|
|
3433
|
+
continue;
|
|
3434
|
+
}
|
|
3435
|
+
if (deep(ctn, "txEl").length) ctx.notes.push(`An effect on "${target}" animates part of its text, which was not imported; it animates the whole shape.`);
|
|
3436
|
+
const cmd = command(ctx, ctn, { width, height }, nodes, target, hidden);
|
|
3437
|
+
if (!cmd) continue;
|
|
3438
|
+
const kind = ctn.getAttribute("nodeType");
|
|
3439
|
+
const delay = num(kid(kid(ctn, "stCondLst"), "cond"), "delay", 0);
|
|
3440
|
+
if (kind === "clickEffect" || !shots.length) shots.push({ id: `click-${shots.length + 1}`, run: [] });
|
|
3441
|
+
const run = shots[shots.length - 1].run;
|
|
3442
|
+
const placed = { ...cmd, ...delay > 0 ? { delay } : {}, ...kind === "withEffect" && run.length ? { with: "previous" } : {} };
|
|
3443
|
+
run.push(placed);
|
|
3444
|
+
}
|
|
3445
|
+
if (effects.length && !shots.length) ctx.notes.push("The slide's effects could not be read.");
|
|
3446
|
+
const artwork = { width, height, background: bg, nodes: [...nodes, ...hidden] };
|
|
3447
|
+
const scene = { version: 1, name: `slide-${n}`, artwork, shots, ...shots.length > 1 ? { trigger: "click" } : {} };
|
|
3448
|
+
const problems = motionSceneShapeProblems(scene);
|
|
3449
|
+
if (problems.length) throw new Error(`The imported scene is not well formed:
|
|
3450
|
+
${problems.join("\n")}`);
|
|
3451
|
+
return { scene, notes: [...new Set(ctx.notes)], issues: checkMotionScene(scene), slides: slidePaths.length };
|
|
3452
|
+
}
|
|
3453
|
+
|
|
3028
3454
|
// src/animation-editor/controller.ts
|
|
3029
3455
|
var BLANK = { width: 800, height: 450, background: "#ffffff", nodes: [] };
|
|
3030
3456
|
var AnimationEditorController = (_class = class {
|
|
@@ -3092,7 +3518,7 @@ var AnimationEditorController = (_class = class {
|
|
|
3092
3518
|
time: p.time,
|
|
3093
3519
|
playing: p.playing,
|
|
3094
3520
|
loop: p.loop,
|
|
3095
|
-
currentShot: _nullishCoalesce(_optionalChain([motionShotAt, 'call',
|
|
3521
|
+
currentShot: _nullishCoalesce(_optionalChain([motionShotAt, 'call', _144 => _144(timeline, p.time), 'optionalAccess', _145 => _145.id]), () => ( null)),
|
|
3096
3522
|
selection: this.selection,
|
|
3097
3523
|
readOnly: this.readOnly,
|
|
3098
3524
|
error: this.error,
|
|
@@ -3131,7 +3557,7 @@ var AnimationEditorController = (_class = class {
|
|
|
3131
3557
|
this.applying = false;
|
|
3132
3558
|
}
|
|
3133
3559
|
this.notify();
|
|
3134
|
-
_optionalChain([this, 'access',
|
|
3560
|
+
_optionalChain([this, 'access', _146 => _146.onChange, 'optionalCall', _147 => _147(scene)]);
|
|
3135
3561
|
}
|
|
3136
3562
|
commit(label, next) {
|
|
3137
3563
|
const before = this.scene;
|
|
@@ -3140,14 +3566,14 @@ var AnimationEditorController = (_class = class {
|
|
|
3140
3566
|
this.notify();
|
|
3141
3567
|
return;
|
|
3142
3568
|
}
|
|
3143
|
-
const
|
|
3569
|
+
const command3 = {
|
|
3144
3570
|
name: label,
|
|
3145
3571
|
tag: "animation-editor",
|
|
3146
3572
|
execute: () => this.setScene(next),
|
|
3147
3573
|
undo: () => this.setScene(before),
|
|
3148
3574
|
redo: () => this.setScene(next)
|
|
3149
3575
|
};
|
|
3150
|
-
this.history.execute(
|
|
3576
|
+
this.history.execute(command3);
|
|
3151
3577
|
}
|
|
3152
3578
|
refuse(message) {
|
|
3153
3579
|
this.error = message;
|
|
@@ -3320,11 +3746,11 @@ var DRAW_ORDER = {
|
|
|
3320
3746
|
sortBy: { type: "string", enum: ["document", "distance"], description: "With sequence: stacking order (default) or distance from origin (a radial reveal)." },
|
|
3321
3747
|
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).")
|
|
3322
3748
|
};
|
|
3323
|
-
var
|
|
3749
|
+
var command2 = (type, description, extra = {}, required = []) => object({ type: { const: type, description }, ...BASE, ...extra }, ["type", "targets", ...required], description);
|
|
3324
3750
|
var MOTION_COMMAND_SCHEMA = {
|
|
3325
3751
|
description: `A command (one of ${MOTION_COMMAND_TYPES.join(", ")}). Commands in a shot play one after another unless "with": "previous".`,
|
|
3326
3752
|
oneOf: [
|
|
3327
|
-
|
|
3753
|
+
command2("enter", "Brings hidden targets in; they are hidden before this shot.", {
|
|
3328
3754
|
...DRAW_ORDER,
|
|
3329
3755
|
style: {
|
|
3330
3756
|
type: "string",
|
|
@@ -3337,18 +3763,18 @@ var MOTION_COMMAND_SCHEMA = {
|
|
|
3337
3763
|
shape: { type: "string", enum: [...MOTION_IRIS_SHAPES], description: "For iris: open through a circle (default; an ellipse on a box that is not square) or a box." },
|
|
3338
3764
|
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." }
|
|
3339
3765
|
}),
|
|
3340
|
-
|
|
3766
|
+
command2("exit", "Takes targets out.", {
|
|
3341
3767
|
...DRAW_ORDER,
|
|
3342
3768
|
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)." },
|
|
3343
3769
|
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)." },
|
|
3344
3770
|
distance: { type: "number", description: "For slide: document units (default 40)." }
|
|
3345
3771
|
}),
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3772
|
+
command2("draw", "Draws strokes on (or off) to a fraction of their length.", { ...DRAW_ORDER, comet: FRACTION("A travelling stroke this fraction of the length long: it runs along the path and off the end (leave out from and to)."), to: FRACTION("Fraction drawn at the end (default 1)."), from: FRACTION("Fraction drawn at the start (default: what is drawn now).") }),
|
|
3773
|
+
command2("move", "Moves targets by an offset or so their centre lands on a point.", { via: { type: "array", items: POINT("A point the centre passes through."), minItems: 1, maxItems: 64, description: 'Waypoints (document units) the centre passes through on the way to "to" or "by", smoothly (velocity carries through each) and without overshooting any. Default length 700 ms + 400 per waypoint.' }, pacing: { type: "string", enum: ["even", "equal"], description: "With via: even (default) keeps the speed steady along the path; equal gives each stretch the same time." }, blend: BLEND("replace"), by: POINT("Offset in document units."), to: POINT("Point (document coordinates) for the centre.") }),
|
|
3774
|
+
command2("rotate", "Turns targets about their centre (degrees, clockwise).", { blend: BLEND("replace"), by: { type: "number", description: "Degrees to turn by." }, to: { type: "number", description: "Angle to turn to, in degrees from rest." } }),
|
|
3775
|
+
command2("scale", "Scales targets about their centre (1 = rest size).", { blend: BLEND("replace"), to: { description: "A factor, or { x, y } factors.", anyOf: [{ type: "number" }, POINT("Factors per axis.")] }, geometric: { type: "boolean", description: "Grow by a steady factor, which reads as an even zoom (default false)." } }, ["to"]),
|
|
3776
|
+
command2("fade", "Changes opacity.", { to: FRACTION("Opacity at the end (0\u20131).") }, ["to"]),
|
|
3777
|
+
command2("style", "Blends solid fill / stroke colours (in OKLab) and stroke width. On a group it changes every path in it.", {
|
|
3352
3778
|
fill: { type: "string", description: "CSS colour (hex, rgb(), rgba())." },
|
|
3353
3779
|
stroke: { type: "string", description: "CSS colour (hex, rgb(), rgba())." },
|
|
3354
3780
|
strokeWidth: { type: "number", minimum: 0, description: "Document units." }
|
|
@@ -3367,24 +3793,24 @@ var MOTION_COMMAND_SCHEMA = {
|
|
|
3367
3793
|
["type", "targets", "clip"],
|
|
3368
3794
|
"Plays a clip on the targets."
|
|
3369
3795
|
),
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3796
|
+
command2("match", `Smart Animate: tweens a group into another group (usually hidden), part by part. Parts pair by name, then by id with the group ids taken out (card-a-title \u2194 card-b-title), then by kind in order; each takes its partner's shape, paint and place. Unpaired parts fade out; parts of "into" with no partner do not appear (check_animation lists them).`, { into: { type: "string", minLength: 1, description: "Id of the group to become." } }, ["into"]),
|
|
3797
|
+
command2("morph", "Morphs each target into the shape (and paint) of another node, which usually stays hidden.", { into: { type: "string", minLength: 1, description: "Id of the node whose shape to take." } }, ["into"]),
|
|
3798
|
+
command2("follow", "Moves targets along a path node, centre on the path.", {
|
|
3373
3799
|
path: { type: "string", minLength: 1, description: "Id of a path node (it can be hidden)." },
|
|
3374
3800
|
rotate: { type: "boolean", description: "Turn with the path (default false); moves and shakes made meanwhile turn with it too." },
|
|
3375
3801
|
from: FRACTION("Where to start along the path (default 0)."),
|
|
3376
3802
|
to: FRACTION("Where to end along the path (default 1).")
|
|
3377
3803
|
}, ["path"]),
|
|
3378
|
-
|
|
3379
|
-
|
|
3804
|
+
command2("pulse", "Scales up and back to draw the eye.", { blend: BLEND("add"), amount: { type: "number", description: "Extra scale at the peak (default 0.15)." }, count: { type: "integer", minimum: 1, description: "Pulses (default 1)." } }),
|
|
3805
|
+
command2("shake", "Shakes side to side (or up and down), settling.", {
|
|
3380
3806
|
blend: BLEND("add"),
|
|
3381
3807
|
amount: { type: "number", description: "Document units (default 8)." },
|
|
3382
3808
|
count: { type: "integer", minimum: 1, description: "Shakes (default 3)." },
|
|
3383
3809
|
axis: { type: "string", enum: ["horizontal", "vertical"], description: "Default horizontal." }
|
|
3384
3810
|
}),
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3811
|
+
command2("teeter", "Rocks about the centre either way, settling.", { blend: BLEND("add"), angle: { type: "number", description: "Degrees either way (default 8)." }, count: { type: "integer", minimum: 1, description: "Rocks (default 3)." } }),
|
|
3812
|
+
command2("wave", "Bobs up and down without settling, like something floating (default 1000 ms).", { blend: BLEND("add"), amount: { type: "number", description: "Height in document units (default 6)." }, count: { type: "integer", minimum: 1, description: "Bobs (default 2)." } }),
|
|
3813
|
+
command2("wiggle", "Wanders about its place as smooth seeded noise, easing out of and back to rest (default 1500 ms). The same seed always wanders the same way.", {
|
|
3388
3814
|
blend: BLEND("add"),
|
|
3389
3815
|
amount: { type: "number", description: "Up to about this far, in document units (default 6)." },
|
|
3390
3816
|
frequency: { type: "number", exclusiveMinimum: 0, description: "Wanders per second, in Hz (default 3)." },
|
|
@@ -3392,12 +3818,12 @@ var MOTION_COMMAND_SCHEMA = {
|
|
|
3392
3818
|
rotate: { type: "number", description: "Also turns about the centre by up to about this many degrees (default 0)." },
|
|
3393
3819
|
seed: { type: "integer", minimum: 0, description: "Default: one picked from the target id, so each target wanders its own way." }
|
|
3394
3820
|
}),
|
|
3395
|
-
|
|
3821
|
+
command2("color-pulse", "Tints solid paint toward a colour and back (mixed in OKLab). On a group it tints every path in it.", {
|
|
3396
3822
|
color: { type: "string", description: 'The tint, a CSS colour (default "#f59e0b").' },
|
|
3397
3823
|
paint: { type: "string", enum: ["fill", "stroke"], description: "Which paint (default fill)." },
|
|
3398
3824
|
count: { type: "integer", minimum: 1, description: "Pulses (default 1)." }
|
|
3399
3825
|
}),
|
|
3400
|
-
|
|
3826
|
+
command2("effect", "Tweens a numeric parameter of a live path effect on the targets (twist angle, wiggle time, zig zag size, trim offset\u2026).", {
|
|
3401
3827
|
effect: { type: "string", minLength: 1, description: `Effect id from get_animation's element "effects".` },
|
|
3402
3828
|
param: { type: "string", minLength: 1, description: "A numeric parameter of that effect (listed with it)." },
|
|
3403
3829
|
to: { type: "number", description: "Value at the end." }
|
|
@@ -3451,6 +3877,10 @@ var SETTINGS = {
|
|
|
3451
3877
|
},
|
|
3452
3878
|
fps: { type: "number", minimum: 1, maximum: 120, description: "Sampling rate for exports (default 30)." },
|
|
3453
3879
|
steps: { anyOf: [{ type: "number", exclusiveMinimum: 0, maximum: 120 }, { type: "null" }], description: 'Stepped time for every command without its own: changes hold on a grid of this many frames per second (12 = "on twos" at 24 fps) for a hand-drawn look; null makes it smooth again (default).' },
|
|
3880
|
+
motionBlur: {
|
|
3881
|
+
anyOf: [object({ shutter: { type: "number", exclusiveMinimum: 0, maximum: 720, description: "Shutter angle in degrees of a frame (default 180; 360 is a whole frame, more blur)." }, samples: { type: "integer", minimum: 2, maximum: 32, description: "Most moments a frame averages (default 8)." } }), { type: "null" }],
|
|
3882
|
+
description: "Motion blur on the stage and in rendered frames (not exported to Flair or Lottie); null turns it off."
|
|
3883
|
+
},
|
|
3454
3884
|
beat: {
|
|
3455
3885
|
anyOf: [
|
|
3456
3886
|
object({ bpm: { type: "number", minimum: 1, maximum: 1e3, description: "Beats a minute." }, offset: { type: "number", description: "When beat 0 falls, ms (default 0)." }, meter: { type: "integer", minimum: 1, description: "Beats a bar (default 4)." } }, ["bpm"]),
|
|
@@ -3577,6 +4007,7 @@ function createAnimationEditorToolProvider(controller, options = {}) {
|
|
|
3577
4007
|
shots: describeMotionShots(s),
|
|
3578
4008
|
...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
4009
|
...s.drivers ? { drivers: s.drivers } : {},
|
|
4010
|
+
...s.motionBlur ? { motionBlur: s.motionBlur } : {},
|
|
3580
4011
|
...s.controls ? { controls: s.controls } : {},
|
|
3581
4012
|
...s.presets ? { presets: s.presets } : {},
|
|
3582
4013
|
issues: issueSummary(controller.getState().issues),
|
|
@@ -3691,9 +4122,9 @@ function createAnimationEditorToolProvider(controller, options = {}) {
|
|
|
3691
4122
|
handler: ({ from = 0, to, limit = 200 }) => {
|
|
3692
4123
|
const s = scene();
|
|
3693
4124
|
const timeline = compileMotionScene(s);
|
|
3694
|
-
const words = _nullishCoalesce(_optionalChain([s, 'access',
|
|
4125
|
+
const words = _nullishCoalesce(_optionalChain([s, 'access', _148 => _148.narration, 'optionalAccess', _149 => _149.words]), () => ( []));
|
|
3695
4126
|
const end = _nullishCoalesce(to, () => ( Math.max(timeline.duration, words.length ? _nullishCoalesce(words[words.length - 1].end, () => ( words[words.length - 1].start)) : 0)));
|
|
3696
|
-
const shotAt = (ms) => _optionalChain([timeline, 'access',
|
|
4127
|
+
const shotAt = (ms) => _optionalChain([timeline, 'access', _150 => _150.shots, 'access', _151 => _151.find, 'call', _152 => _152((x) => ms >= x.start && ms < x.end), 'optionalAccess', _153 => _153.id]);
|
|
3697
4128
|
const beats = [];
|
|
3698
4129
|
if (s.beat && s.beat.bpm > 0) {
|
|
3699
4130
|
const first = Math.max(0, Math.ceil(motionBeatAt(s.beat, from).beat - 1e-9));
|
|
@@ -3786,6 +4217,33 @@ function createAnimationEditorToolProvider(controller, options = {}) {
|
|
|
3786
4217
|
return apply({ op: "set_artwork", artwork }, "Replace artwork", (s) => ({ elements: describeMotionElements(s) }));
|
|
3787
4218
|
}
|
|
3788
4219
|
};
|
|
4220
|
+
const importPptx = {
|
|
4221
|
+
name: "import_pptx_animation",
|
|
4222
|
+
title: "Import a PowerPoint slide's animation",
|
|
4223
|
+
description: "Replaces the whole animation with a PowerPoint slide (one undo step): its shapes become the artwork and its animation pane becomes shots, a shot per click (with previous / after previous, delays, repeats and acceleration kept; the scene plays on click). Text, pictures and charts are left out and effects with no match fade, with notes. Returns the shots, notes and issues. With dry_run it only reports them.",
|
|
4224
|
+
inputSchema: object(
|
|
4225
|
+
{
|
|
4226
|
+
data: { type: "string", minLength: 1, description: "The .pptx file, base64-encoded." },
|
|
4227
|
+
slide: { type: "integer", minimum: 1, description: "Which slide (1 = first, default 1)." },
|
|
4228
|
+
dry_run: { type: "boolean", description: "Report without replacing anything (default false)." }
|
|
4229
|
+
},
|
|
4230
|
+
["data"]
|
|
4231
|
+
),
|
|
4232
|
+
annotations: { title: "Import a PowerPoint slide's animation", ...DESTRUCTIVE },
|
|
4233
|
+
handler: async ({ data, slide, dry_run = false }) => {
|
|
4234
|
+
let result;
|
|
4235
|
+
try {
|
|
4236
|
+
const bytes = Uint8Array.from(atob(data.replace(/\s+/g, "")), (c) => c.charCodeAt(0));
|
|
4237
|
+
result = await fromPptxAnimation(bytes, slide ? { slide } : {});
|
|
4238
|
+
} catch (error) {
|
|
4239
|
+
throw new Error(`The slide could not be imported: ${error.message} Nothing was changed.`);
|
|
4240
|
+
}
|
|
4241
|
+
const report = { slides: result.slides, notes: result.notes, issues: issueSummary(result.issues), shots: describeMotionShots(result.scene).map(({ id, start, end, commands }) => ({ id, start, end, commands: commands.length })) };
|
|
4242
|
+
if (dry_run) return { dry_run: true, ...report, hint: "Nothing was replaced; call again without dry_run to import." };
|
|
4243
|
+
const applied = controller.apply({ op: "replace_scene", scene: result.scene }, "Import PowerPoint slide");
|
|
4244
|
+
return outcome(applied, applied.ok ? { ...report, elements: describeMotionElements(result.scene) } : {});
|
|
4245
|
+
}
|
|
4246
|
+
};
|
|
3789
4247
|
const importFlairEffect = {
|
|
3790
4248
|
name: "import_flair_effect",
|
|
3791
4249
|
title: "Import a Flair effect",
|
|
@@ -3973,7 +4431,7 @@ function createAnimationEditorToolProvider(controller, options = {}) {
|
|
|
3973
4431
|
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"]),
|
|
3974
4432
|
annotations: { title: "Add commands to a shot", ...WRITE },
|
|
3975
4433
|
handler: ({ shot, commands, at }) => {
|
|
3976
|
-
const before = _nullishCoalesce(_optionalChain([scene, 'call',
|
|
4434
|
+
const before = _nullishCoalesce(_optionalChain([scene, 'call', _154 => _154(), 'access', _155 => _155.shots, 'access', _156 => _156.find, 'call', _157 => _157((s) => s.id === shot), 'optionalAccess', _158 => _158.run, 'access', _159 => _159.length]), () => ( 0));
|
|
3977
4435
|
const first = _nullishCoalesce(at, () => ( before));
|
|
3978
4436
|
return apply({ op: "add_commands", shot, commands, at }, "Add commands", (s) => {
|
|
3979
4437
|
const shots = describeMotionShots(s);
|
|
@@ -3996,7 +4454,7 @@ function createAnimationEditorToolProvider(controller, options = {}) {
|
|
|
3996
4454
|
["shot", "command"]
|
|
3997
4455
|
),
|
|
3998
4456
|
annotations: { title: "Change a command", ...WRITE_IDEMPOTENT },
|
|
3999
|
-
handler: ({ shot, command:
|
|
4457
|
+
handler: ({ shot, command: command3, set, remove }) => apply({ op: "update_command", shot, command: command3, set, remove }, "Change command", (s) => ({ command: describeMotionShots(s).find((x) => x.id === shot).commands[command3] }))
|
|
4000
4458
|
};
|
|
4001
4459
|
const deleteCommands = {
|
|
4002
4460
|
name: "delete_commands",
|
|
@@ -4019,7 +4477,7 @@ function createAnimationEditorToolProvider(controller, options = {}) {
|
|
|
4019
4477
|
["shot", "command", "to"]
|
|
4020
4478
|
),
|
|
4021
4479
|
annotations: { title: "Reorder a command", ...WRITE },
|
|
4022
|
-
handler: ({ shot, command:
|
|
4480
|
+
handler: ({ shot, command: command3, to }) => apply({ op: "move_command", shot, command: command3, to }, "Move command", (s) => ({ shot, command: to, from: command3, commands: describeMotionShots(s).find((x) => x.id === shot).commands.map((c) => ({ index: c.index, type: c.type, start: c.start, end: c.end })) }))
|
|
4023
4481
|
};
|
|
4024
4482
|
const setSettings = {
|
|
4025
4483
|
name: "set_animation_settings",
|
|
@@ -4105,7 +4563,7 @@ function createAnimationEditorToolProvider(controller, options = {}) {
|
|
|
4105
4563
|
}
|
|
4106
4564
|
};
|
|
4107
4565
|
const reads = [getAnimation, getShot, findElements, getMotionPath, getTimingGrid, checkAnimation, renderFrame, exportFlairMotion, exportFlairEffect, exportLottie, seekAnimation];
|
|
4108
|
-
const writes = [setArtworkSvg, setDrivers, setClip, copyAnimation, fitAnimation, importFlairEffect, insertShots, updateShot, deleteShots, moveShot, addCommands, updateCommand, deleteCommands, moveCommand, setSettings, setControls, editAnimation, writeAnimation, undo, redo];
|
|
4566
|
+
const writes = [setArtworkSvg, importPptx, setDrivers, setClip, copyAnimation, fitAnimation, importFlairEffect, insertShots, updateShot, deleteShots, moveShot, addCommands, updateCommand, deleteCommands, moveCommand, setSettings, setControls, editAnimation, writeAnimation, undo, redo];
|
|
4109
4567
|
return {
|
|
4110
4568
|
id: _nullishCoalesce(options.id, () => ( "animation")),
|
|
4111
4569
|
namespace: options.namespace,
|
|
@@ -4138,6 +4596,7 @@ Commands
|
|
|
4138
4596
|
- 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.
|
|
4139
4597
|
- 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.
|
|
4140
4598
|
- 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.
|
|
4599
|
+
- Fast motion reads better with blur: set_animation_settings { "motionBlur": { "shutter": 180 } } (stage and rendered frames only).
|
|
4141
4600
|
- 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.
|
|
4142
4601
|
- 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.
|
|
4143
4602
|
- 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.
|
|
@@ -4153,7 +4612,7 @@ Commands
|
|
|
4153
4612
|
- effect tweens a numeric parameter of a live path effect already on the node (twist angle, wiggle time, zig zag size).
|
|
4154
4613
|
|
|
4155
4614
|
Working method
|
|
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
|
|
4615
|
+
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 / import_pptx_animation to start from a Flair effect or a PowerPoint slide).
|
|
4157
4616
|
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).
|
|
4158
4617
|
- 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
4618
|
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.
|
|
@@ -4167,7 +4626,7 @@ var TIMING = /* @__PURE__ */ new Set(["duration", "delay", "stagger", "with", "e
|
|
|
4167
4626
|
var COLORS = /* @__PURE__ */ new Set(["fill", "stroke", "color"]);
|
|
4168
4627
|
var NODES = /* @__PURE__ */ new Set(["into", "path"]);
|
|
4169
4628
|
function branch(type) {
|
|
4170
|
-
return (_nullishCoalesce(MOTION_COMMAND_SCHEMA.oneOf, () => ( []))).find((b) => _optionalChain([b, 'access',
|
|
4629
|
+
return (_nullishCoalesce(MOTION_COMMAND_SCHEMA.oneOf, () => ( []))).find((b) => _optionalChain([b, 'access', _160 => _160.properties, 'optionalAccess', _161 => _161.type, 'optionalAccess', _162 => _162.const]) === type);
|
|
4171
4630
|
}
|
|
4172
4631
|
function unitOf(type, name) {
|
|
4173
4632
|
if (name === "duration" || name === "delay" || name === "stagger") return "ms";
|
|
@@ -4202,7 +4661,7 @@ function kindOf(name, schema) {
|
|
|
4202
4661
|
}
|
|
4203
4662
|
function motionCommandFields(type) {
|
|
4204
4663
|
const schema = branch(type);
|
|
4205
|
-
if (!_optionalChain([schema, 'optionalAccess',
|
|
4664
|
+
if (!_optionalChain([schema, 'optionalAccess', _163 => _163.properties])) return [];
|
|
4206
4665
|
const required = new Set(_nullishCoalesce(schema.required, () => ( [])));
|
|
4207
4666
|
const fields = Object.entries(schema.properties).filter(([name]) => name !== "type").map(([name, s]) => {
|
|
4208
4667
|
const kind = kindOf(name, s);
|
|
@@ -4249,17 +4708,17 @@ function parseMotionFieldInput(field, raw) {
|
|
|
4249
4708
|
if (field.kind === "enum" && field.options && !field.options.includes(text)) return { error: `${field.label} must be one of ${field.options.join(", ")}.` };
|
|
4250
4709
|
return { set: text };
|
|
4251
4710
|
}
|
|
4252
|
-
function motionFieldEdit(shot,
|
|
4711
|
+
function motionFieldEdit(shot, command3, field, raw) {
|
|
4253
4712
|
const input = parseMotionFieldInput(field, raw);
|
|
4254
4713
|
if ("error" in input) return input;
|
|
4255
|
-
if ("remove" in input) return { op: "update_command", shot, command:
|
|
4256
|
-
return { op: "update_command", shot, command:
|
|
4714
|
+
if ("remove" in input) return { op: "update_command", shot, command: command3, remove: [field.name] };
|
|
4715
|
+
return { op: "update_command", shot, command: command3, set: { [field.name]: input.set } };
|
|
4257
4716
|
}
|
|
4258
4717
|
function defaultMotionCommand(type, targets, scene) {
|
|
4259
4718
|
if (type === "wait") return { type: "wait", duration: 500 };
|
|
4260
4719
|
if (!targets.length) return { error: "Pick a target first: click an element on the stage." };
|
|
4261
4720
|
const nodes = [...indexMotionNodes(scene.artwork.nodes).values()];
|
|
4262
|
-
const other = (pred) => _optionalChain([nodes, 'access',
|
|
4721
|
+
const other = (pred) => _optionalChain([nodes, 'access', _164 => _164.find, 'call', _165 => _165((e) => !targets.includes(e.node.id) && pred(e)), 'optionalAccess', _166 => _166.node, 'access', _167 => _167.id]);
|
|
4263
4722
|
const base = { targets: [...targets] };
|
|
4264
4723
|
switch (type) {
|
|
4265
4724
|
case "scale":
|
|
@@ -4290,8 +4749,8 @@ function defaultMotionCommand(type, targets, scene) {
|
|
|
4290
4749
|
}
|
|
4291
4750
|
case "effect": {
|
|
4292
4751
|
for (const id of targets) {
|
|
4293
|
-
const node = _optionalChain([nodes, 'access',
|
|
4294
|
-
for (const effect of _nullishCoalesce(_optionalChain([node, 'optionalAccess',
|
|
4752
|
+
const node = _optionalChain([nodes, 'access', _168 => _168.find, 'call', _169 => _169((e) => e.node.id === id), 'optionalAccess', _170 => _170.node]);
|
|
4753
|
+
for (const effect of _nullishCoalesce(_optionalChain([node, 'optionalAccess', _171 => _171.effects]), () => ( []))) {
|
|
4295
4754
|
const param = Object.entries(effect.params).find(([, v]) => typeof v === "number");
|
|
4296
4755
|
if (param) return { type, ...base, effect: effect.id, param: param[0], to: param[1] + 1 };
|
|
4297
4756
|
}
|
|
@@ -4302,26 +4761,26 @@ function defaultMotionCommand(type, targets, scene) {
|
|
|
4302
4761
|
return { type, ...base };
|
|
4303
4762
|
}
|
|
4304
4763
|
}
|
|
4305
|
-
function motionCommandSummary(
|
|
4306
|
-
const c =
|
|
4307
|
-
const what = [
|
|
4308
|
-
if (
|
|
4764
|
+
function motionCommandSummary(command3) {
|
|
4765
|
+
const c = command3;
|
|
4766
|
+
const what = [command3.type, typeof c.style === "string" ? c.style : ""].filter(Boolean).join(" ");
|
|
4767
|
+
if (command3.type === "wait") return `wait ${command3.duration} ms`;
|
|
4309
4768
|
const extra = typeof c.into === "string" ? ` \u2192 ${c.into}` : typeof c.path === "string" ? ` along ${c.path}` : "";
|
|
4310
|
-
return `${what} \xB7 ${(_nullishCoalesce(
|
|
4769
|
+
return `${what} \xB7 ${(_nullishCoalesce(command3.targets, () => ( []))).join(", ")}${extra}`;
|
|
4311
4770
|
}
|
|
4312
4771
|
function motionCommandRows(scene, shotId) {
|
|
4313
4772
|
const index = scene.shots.findIndex((s) => s.id === shotId);
|
|
4314
4773
|
if (index < 0) return [];
|
|
4315
4774
|
const timeline = compileMotionScene(scene);
|
|
4316
|
-
return scene.shots[index].run.map((
|
|
4775
|
+
return scene.shots[index].run.map((command3, i) => {
|
|
4317
4776
|
const t = timeline.commands.find((c) => c.shotIndex === index && c.command === i);
|
|
4318
|
-
const primitive = motionFlairPrimitive(
|
|
4319
|
-
return { index: i, command:
|
|
4777
|
+
const primitive = motionFlairPrimitive(command3);
|
|
4778
|
+
return { index: i, command: command3, summary: motionCommandSummary(command3), start: _nullishCoalesce(_optionalChain([t, 'optionalAccess', _172 => _172.start]), () => ( 0)), end: _nullishCoalesce(_optionalChain([t, 'optionalAccess', _173 => _173.end]), () => ( 0)), ...primitive ? { flairPrimitive: primitive } : {} };
|
|
4320
4779
|
});
|
|
4321
4780
|
}
|
|
4322
|
-
function duplicateMotionCommandEdit(scene, shot,
|
|
4323
|
-
const run = _nullishCoalesce(_optionalChain([scene, 'access',
|
|
4324
|
-
return { op: "add_commands", shot, commands: run[
|
|
4781
|
+
function duplicateMotionCommandEdit(scene, shot, command3) {
|
|
4782
|
+
const run = _nullishCoalesce(_optionalChain([scene, 'access', _174 => _174.shots, 'access', _175 => _175.find, 'call', _176 => _176((s) => s.id === shot), 'optionalAccess', _177 => _177.run]), () => ( []));
|
|
4783
|
+
return { op: "add_commands", shot, commands: run[command3] ? [structuredClone(run[command3])] : [], at: command3 + 1 };
|
|
4325
4784
|
}
|
|
4326
4785
|
var MOTION_EASE_CHOICES = [
|
|
4327
4786
|
{ value: "", label: "Default" },
|
|
@@ -4405,7 +4864,7 @@ function motionCueFromKey(key) {
|
|
|
4405
4864
|
function motionCueChoices(scene, limit = 256) {
|
|
4406
4865
|
const choices = [];
|
|
4407
4866
|
const s = (ms) => `${(ms / 1e3).toFixed(2)} s`;
|
|
4408
|
-
const end = Math.max(compileMotionScene(scene).duration, ..._nullishCoalesce(_optionalChain([scene, 'access',
|
|
4867
|
+
const end = Math.max(compileMotionScene(scene).duration, ..._nullishCoalesce(_optionalChain([scene, 'access', _178 => _178.narration, 'optionalAccess', _179 => _179.words, 'access', _180 => _180.map, 'call', _181 => _181((w) => _nullishCoalesce(w.end, () => ( w.start)))]), () => ( [0]))) + 2e3;
|
|
4409
4868
|
if (scene.beat && scene.beat.bpm > 0) {
|
|
4410
4869
|
for (let beat = 0; choices.length < limit; beat++) {
|
|
4411
4870
|
const time = motionBeatTime(scene.beat, beat);
|
|
@@ -4415,7 +4874,7 @@ function motionCueChoices(scene, limit = 256) {
|
|
|
4415
4874
|
choices.push({ value: `beat:${beat}`, label: `Beat ${beat} (bar ${at.bar}.${at.beatInBar}) \xB7 ${s(time)}`, time });
|
|
4416
4875
|
}
|
|
4417
4876
|
}
|
|
4418
|
-
_optionalChain([scene, 'access',
|
|
4877
|
+
_optionalChain([scene, 'access', _182 => _182.narration, 'optionalAccess', _183 => _183.words, 'access', _184 => _184.forEach, 'call', _185 => _185((w, i) => {
|
|
4419
4878
|
if (choices.length < limit * 2) choices.push({ value: `word:${i}`, label: `Word ${i} \u201C${w.text}\u201D \xB7 ${s(w.start)}`, time: w.start });
|
|
4420
4879
|
})]);
|
|
4421
4880
|
return choices;
|
|
@@ -4424,7 +4883,7 @@ function motionShotCueTime(scene, cue) {
|
|
|
4424
4883
|
return cue ? motionCueTime(scene, cue) : void 0;
|
|
4425
4884
|
}
|
|
4426
4885
|
function formatMotionNarration(narration) {
|
|
4427
|
-
return (_nullishCoalesce(_optionalChain([narration, 'optionalAccess',
|
|
4886
|
+
return (_nullishCoalesce(_optionalChain([narration, 'optionalAccess', _186 => _186.words]), () => ( []))).map((w) => `${w.start} ${w.text}`).join("\n");
|
|
4428
4887
|
}
|
|
4429
4888
|
function parseMotionNarration(text) {
|
|
4430
4889
|
const lines = text.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
@@ -4595,5 +5054,7 @@ function parseMotionDrivers(text) {
|
|
|
4595
5054
|
|
|
4596
5055
|
|
|
4597
5056
|
|
|
4598
|
-
|
|
5057
|
+
|
|
5058
|
+
|
|
5059
|
+
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.fromPptxAnimation = fromPptxAnimation; 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.motionBlurTimes = motionBlurTimes; 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;
|
|
4599
5060
|
//# sourceMappingURL=index.cjs.map
|