reframe-video 0.6.13 → 0.6.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.js +28 -6
- package/dist/browserEntry.js +23 -3
- package/dist/cli.js +19 -3
- package/dist/diff.js +19 -3
- package/dist/index.js +24 -3
- package/dist/labels.js +19 -3
- package/dist/trace-cli.js +16 -1
- package/dist/types/ir.d.ts +19 -2
- package/guides/directing-guide.md +5 -0
- package/guides/edsl-guide.md +10 -2
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -10,13 +10,14 @@ var __export = (target, all) => {
|
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
// ../core/src/ir.ts
|
|
13
|
-
var DEFAULT_TO_DURATION, DEFAULT_TWEEN_DURATION, DEFAULT_MOTIONPATH_DURATION;
|
|
13
|
+
var DEFAULT_TO_DURATION, DEFAULT_TWEEN_DURATION, DEFAULT_MOTIONPATH_DURATION, DEFAULT_STILL_DURATION;
|
|
14
14
|
var init_ir = __esm({
|
|
15
15
|
"../core/src/ir.ts"() {
|
|
16
16
|
"use strict";
|
|
17
17
|
DEFAULT_TO_DURATION = 0.5;
|
|
18
18
|
DEFAULT_TWEEN_DURATION = 0.5;
|
|
19
19
|
DEFAULT_MOTIONPATH_DURATION = 1;
|
|
20
|
+
DEFAULT_STILL_DURATION = 1;
|
|
20
21
|
}
|
|
21
22
|
});
|
|
22
23
|
|
|
@@ -319,14 +320,14 @@ function compileScene(ir) {
|
|
|
319
320
|
}
|
|
320
321
|
}
|
|
321
322
|
};
|
|
322
|
-
const inferredEnd = ir.timeline ? walk(ir.timeline, 0) : 0;
|
|
323
|
+
const inferredEnd = (ir.timeline ? walk(ir.timeline, 0) : 0) || 0;
|
|
323
324
|
for (const list of segments.values()) list.sort((a, b) => a.t0 - b.t0);
|
|
324
325
|
for (const list of motionPaths.values()) list.sort((a, b) => a.t0 - b.t0);
|
|
325
326
|
const hasCamera = !cameraIsNode && (ir.camera !== void 0 || motionPaths.has("camera") || [...segments.keys()].some((k) => k.startsWith("camera.")));
|
|
326
327
|
const hasPerspective = !cameraIsNode && (ir.camera?.perspective !== void 0 || segments.has("camera.perspective"));
|
|
327
328
|
return {
|
|
328
329
|
ir,
|
|
329
|
-
duration: ir.duration ?? inferredEnd,
|
|
330
|
+
duration: ir.duration ?? (inferredEnd > 0 ? inferredEnd : DEFAULT_STILL_DURATION),
|
|
330
331
|
segments,
|
|
331
332
|
motionPaths,
|
|
332
333
|
initialValues,
|
|
@@ -1346,6 +1347,17 @@ var init_behaviors = __esm({
|
|
|
1346
1347
|
});
|
|
1347
1348
|
|
|
1348
1349
|
// ../core/src/interpolate.ts
|
|
1350
|
+
function springEase(stiffness, damping, velocity) {
|
|
1351
|
+
const K = 5;
|
|
1352
|
+
const zeta = Math.min(0.999, Math.max(0.05, damping / (2 * Math.sqrt(Math.max(1e-6, stiffness)))));
|
|
1353
|
+
const wd = K / zeta * Math.sqrt(1 - zeta * zeta);
|
|
1354
|
+
const coef = (K - velocity) / wd;
|
|
1355
|
+
return (u) => {
|
|
1356
|
+
if (u <= 0) return 0;
|
|
1357
|
+
if (u >= 1) return 1;
|
|
1358
|
+
return 1 - Math.exp(-K * u) * (Math.cos(wd * u) + coef * Math.sin(wd * u));
|
|
1359
|
+
};
|
|
1360
|
+
}
|
|
1349
1361
|
function easeOutBounce(u) {
|
|
1350
1362
|
const n1 = 7.5625;
|
|
1351
1363
|
const d1 = 2.75;
|
|
@@ -1389,7 +1401,11 @@ var init_interpolate = __esm({
|
|
|
1389
1401
|
// bounce: drops and bounces to rest (lands without overshoot)
|
|
1390
1402
|
easeInBounce: (u) => 1 - easeOutBounce(1 - u),
|
|
1391
1403
|
easeOutBounce,
|
|
1392
|
-
easeInOutBounce: (u) => u < 0.5 ? (1 - easeOutBounce(1 - 2 * u)) / 2 : (1 + easeOutBounce(2 * u - 1)) / 2
|
|
1404
|
+
easeInOutBounce: (u) => u < 0.5 ? (1 - easeOutBounce(1 - 2 * u)) / 2 : (1 + easeOutBounce(2 * u - 1)) / 2,
|
|
1405
|
+
// damped-spring presets (ζ from damping/(2√stiffness)): 0.5 / 0.30 / 0.90
|
|
1406
|
+
spring: springEase(100, 10, 0),
|
|
1407
|
+
springBouncy: springEase(180, 8, 0),
|
|
1408
|
+
springStiff: springEase(210, 26, 0)
|
|
1393
1409
|
};
|
|
1394
1410
|
EASE_NAMES = Object.keys(EASE_TABLE);
|
|
1395
1411
|
}
|
|
@@ -2843,10 +2859,16 @@ ${USAGE}`);
|
|
|
2843
2859
|
}
|
|
2844
2860
|
preflightFfmpeg();
|
|
2845
2861
|
const outBase = PACKAGED ? join9(USER_CWD, "out") : join9(ROOT2, "out");
|
|
2862
|
+
const stem = `${basename(input).replace(/\.[^.]+$/, "")}.mp4`;
|
|
2846
2863
|
let outArgs = args;
|
|
2847
|
-
|
|
2864
|
+
const oIdx = args.indexOf("-o");
|
|
2865
|
+
if (oIdx === -1) {
|
|
2848
2866
|
await mkdir4(outBase, { recursive: true });
|
|
2849
|
-
outArgs = [...args, "-o", join9(outBase,
|
|
2867
|
+
outArgs = [...args, "-o", join9(outBase, stem)];
|
|
2868
|
+
} else if (args[oIdx + 1] && !args[oIdx + 1].startsWith("-") && !/\.\w{2,4}$/.test(args[oIdx + 1])) {
|
|
2869
|
+
const dir = userPath(args[oIdx + 1]);
|
|
2870
|
+
await mkdir4(dir, { recursive: true });
|
|
2871
|
+
outArgs = args.map((a, i) => i === oIdx + 1 ? join9(dir, stem) : a);
|
|
2850
2872
|
}
|
|
2851
2873
|
outArgs = outArgs.map(
|
|
2852
2874
|
(a, i) => outArgs[i - 1] === "--overlay" || outArgs[i - 1] === "-o" ? userPath(a) : a
|
package/dist/browserEntry.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
var DEFAULT_TO_DURATION = 0.5;
|
|
5
5
|
var DEFAULT_TWEEN_DURATION = 0.5;
|
|
6
6
|
var DEFAULT_MOTIONPATH_DURATION = 1;
|
|
7
|
+
var DEFAULT_STILL_DURATION = 1;
|
|
7
8
|
|
|
8
9
|
// ../core/src/path.ts
|
|
9
10
|
function pathBBox(d) {
|
|
@@ -316,14 +317,14 @@
|
|
|
316
317
|
}
|
|
317
318
|
}
|
|
318
319
|
};
|
|
319
|
-
const inferredEnd = ir.timeline ? walk(ir.timeline, 0) : 0;
|
|
320
|
+
const inferredEnd = (ir.timeline ? walk(ir.timeline, 0) : 0) || 0;
|
|
320
321
|
for (const list of segments.values()) list.sort((a, b) => a.t0 - b.t0);
|
|
321
322
|
for (const list of motionPaths.values()) list.sort((a, b) => a.t0 - b.t0);
|
|
322
323
|
const hasCamera = !cameraIsNode && (ir.camera !== void 0 || motionPaths.has("camera") || [...segments.keys()].some((k) => k.startsWith("camera.")));
|
|
323
324
|
const hasPerspective = !cameraIsNode && (ir.camera?.perspective !== void 0 || segments.has("camera.perspective"));
|
|
324
325
|
return {
|
|
325
326
|
ir,
|
|
326
|
-
duration: ir.duration ?? inferredEnd,
|
|
327
|
+
duration: ir.duration ?? (inferredEnd > 0 ? inferredEnd : DEFAULT_STILL_DURATION),
|
|
327
328
|
segments,
|
|
328
329
|
motionPaths,
|
|
329
330
|
initialValues,
|
|
@@ -408,6 +409,17 @@
|
|
|
408
409
|
var BACK_C3 = BACK_C1 + 1;
|
|
409
410
|
var ELASTIC_C4 = 2 * Math.PI / 3;
|
|
410
411
|
var ELASTIC_C5 = 2 * Math.PI / 4.5;
|
|
412
|
+
function springEase(stiffness, damping, velocity) {
|
|
413
|
+
const K = 5;
|
|
414
|
+
const zeta = Math.min(0.999, Math.max(0.05, damping / (2 * Math.sqrt(Math.max(1e-6, stiffness)))));
|
|
415
|
+
const wd = K / zeta * Math.sqrt(1 - zeta * zeta);
|
|
416
|
+
const coef = (K - velocity) / wd;
|
|
417
|
+
return (u) => {
|
|
418
|
+
if (u <= 0) return 0;
|
|
419
|
+
if (u >= 1) return 1;
|
|
420
|
+
return 1 - Math.exp(-K * u) * (Math.cos(wd * u) + coef * Math.sin(wd * u));
|
|
421
|
+
};
|
|
422
|
+
}
|
|
411
423
|
function easeOutBounce(u) {
|
|
412
424
|
const n1 = 7.5625;
|
|
413
425
|
const d1 = 2.75;
|
|
@@ -442,7 +454,11 @@
|
|
|
442
454
|
// bounce: drops and bounces to rest (lands without overshoot)
|
|
443
455
|
easeInBounce: (u) => 1 - easeOutBounce(1 - u),
|
|
444
456
|
easeOutBounce,
|
|
445
|
-
easeInOutBounce: (u) => u < 0.5 ? (1 - easeOutBounce(1 - 2 * u)) / 2 : (1 + easeOutBounce(2 * u - 1)) / 2
|
|
457
|
+
easeInOutBounce: (u) => u < 0.5 ? (1 - easeOutBounce(1 - 2 * u)) / 2 : (1 + easeOutBounce(2 * u - 1)) / 2,
|
|
458
|
+
// damped-spring presets (ζ from damping/(2√stiffness)): 0.5 / 0.30 / 0.90
|
|
459
|
+
spring: springEase(100, 10, 0),
|
|
460
|
+
springBouncy: springEase(180, 8, 0),
|
|
461
|
+
springStiff: springEase(210, 26, 0)
|
|
446
462
|
};
|
|
447
463
|
var EASE_NAMES = Object.keys(EASE_TABLE);
|
|
448
464
|
function resolveEase(ease) {
|
|
@@ -452,6 +468,10 @@
|
|
|
452
468
|
if (!fn) throw new Error(`unknown ease "${ease}" \u2014 valid: ${Object.keys(EASE_TABLE).join(", ")}`);
|
|
453
469
|
return fn;
|
|
454
470
|
}
|
|
471
|
+
if ("spring" in ease) {
|
|
472
|
+
const { stiffness = 100, damping = 10, velocity = 0 } = ease.spring;
|
|
473
|
+
return springEase(stiffness, damping, velocity);
|
|
474
|
+
}
|
|
455
475
|
return cubicBezierEase(...ease.cubicBezier);
|
|
456
476
|
}
|
|
457
477
|
function cubicBezierEase(x1, y1, x2, y2) {
|
package/dist/cli.js
CHANGED
|
@@ -10,6 +10,7 @@ var DEFAULT_CROSSFADE = 0.5;
|
|
|
10
10
|
var DEFAULT_TO_DURATION = 0.5;
|
|
11
11
|
var DEFAULT_TWEEN_DURATION = 0.5;
|
|
12
12
|
var DEFAULT_MOTIONPATH_DURATION = 1;
|
|
13
|
+
var DEFAULT_STILL_DURATION = 1;
|
|
13
14
|
|
|
14
15
|
// ../core/src/path.ts
|
|
15
16
|
function locate(segCount, u) {
|
|
@@ -306,14 +307,14 @@ function compileScene(ir) {
|
|
|
306
307
|
}
|
|
307
308
|
}
|
|
308
309
|
};
|
|
309
|
-
const inferredEnd = ir.timeline ? walk(ir.timeline, 0) : 0;
|
|
310
|
+
const inferredEnd = (ir.timeline ? walk(ir.timeline, 0) : 0) || 0;
|
|
310
311
|
for (const list of segments.values()) list.sort((a, b) => a.t0 - b.t0);
|
|
311
312
|
for (const list of motionPaths.values()) list.sort((a, b) => a.t0 - b.t0);
|
|
312
313
|
const hasCamera = !cameraIsNode && (ir.camera !== void 0 || motionPaths.has("camera") || [...segments.keys()].some((k) => k.startsWith("camera.")));
|
|
313
314
|
const hasPerspective = !cameraIsNode && (ir.camera?.perspective !== void 0 || segments.has("camera.perspective"));
|
|
314
315
|
return {
|
|
315
316
|
ir,
|
|
316
|
-
duration: ir.duration ?? inferredEnd,
|
|
317
|
+
duration: ir.duration ?? (inferredEnd > 0 ? inferredEnd : DEFAULT_STILL_DURATION),
|
|
317
318
|
segments,
|
|
318
319
|
motionPaths,
|
|
319
320
|
initialValues,
|
|
@@ -1050,6 +1051,17 @@ var BACK_C2 = BACK_C1 * 1.525;
|
|
|
1050
1051
|
var BACK_C3 = BACK_C1 + 1;
|
|
1051
1052
|
var ELASTIC_C4 = 2 * Math.PI / 3;
|
|
1052
1053
|
var ELASTIC_C5 = 2 * Math.PI / 4.5;
|
|
1054
|
+
function springEase(stiffness, damping, velocity) {
|
|
1055
|
+
const K = 5;
|
|
1056
|
+
const zeta = Math.min(0.999, Math.max(0.05, damping / (2 * Math.sqrt(Math.max(1e-6, stiffness)))));
|
|
1057
|
+
const wd = K / zeta * Math.sqrt(1 - zeta * zeta);
|
|
1058
|
+
const coef = (K - velocity) / wd;
|
|
1059
|
+
return (u) => {
|
|
1060
|
+
if (u <= 0) return 0;
|
|
1061
|
+
if (u >= 1) return 1;
|
|
1062
|
+
return 1 - Math.exp(-K * u) * (Math.cos(wd * u) + coef * Math.sin(wd * u));
|
|
1063
|
+
};
|
|
1064
|
+
}
|
|
1053
1065
|
function easeOutBounce(u) {
|
|
1054
1066
|
const n1 = 7.5625;
|
|
1055
1067
|
const d1 = 2.75;
|
|
@@ -1084,7 +1096,11 @@ var EASE_TABLE = {
|
|
|
1084
1096
|
// bounce: drops and bounces to rest (lands without overshoot)
|
|
1085
1097
|
easeInBounce: (u) => 1 - easeOutBounce(1 - u),
|
|
1086
1098
|
easeOutBounce,
|
|
1087
|
-
easeInOutBounce: (u) => u < 0.5 ? (1 - easeOutBounce(1 - 2 * u)) / 2 : (1 + easeOutBounce(2 * u - 1)) / 2
|
|
1099
|
+
easeInOutBounce: (u) => u < 0.5 ? (1 - easeOutBounce(1 - 2 * u)) / 2 : (1 + easeOutBounce(2 * u - 1)) / 2,
|
|
1100
|
+
// damped-spring presets (ζ from damping/(2√stiffness)): 0.5 / 0.30 / 0.90
|
|
1101
|
+
spring: springEase(100, 10, 0),
|
|
1102
|
+
springBouncy: springEase(180, 8, 0),
|
|
1103
|
+
springStiff: springEase(210, 26, 0)
|
|
1088
1104
|
};
|
|
1089
1105
|
var EASE_NAMES = Object.keys(EASE_TABLE);
|
|
1090
1106
|
|
package/dist/diff.js
CHANGED
|
@@ -16,6 +16,7 @@ import { fileURLToPath } from "node:url";
|
|
|
16
16
|
var DEFAULT_TO_DURATION = 0.5;
|
|
17
17
|
var DEFAULT_TWEEN_DURATION = 0.5;
|
|
18
18
|
var DEFAULT_MOTIONPATH_DURATION = 1;
|
|
19
|
+
var DEFAULT_STILL_DURATION = 1;
|
|
19
20
|
|
|
20
21
|
// ../core/src/path.ts
|
|
21
22
|
function locate(segCount, u) {
|
|
@@ -312,14 +313,14 @@ function compileScene(ir) {
|
|
|
312
313
|
}
|
|
313
314
|
}
|
|
314
315
|
};
|
|
315
|
-
const inferredEnd = ir.timeline ? walk(ir.timeline, 0) : 0;
|
|
316
|
+
const inferredEnd = (ir.timeline ? walk(ir.timeline, 0) : 0) || 0;
|
|
316
317
|
for (const list of segments.values()) list.sort((a, b) => a.t0 - b.t0);
|
|
317
318
|
for (const list of motionPaths.values()) list.sort((a, b) => a.t0 - b.t0);
|
|
318
319
|
const hasCamera = !cameraIsNode && (ir.camera !== void 0 || motionPaths.has("camera") || [...segments.keys()].some((k) => k.startsWith("camera.")));
|
|
319
320
|
const hasPerspective = !cameraIsNode && (ir.camera?.perspective !== void 0 || segments.has("camera.perspective"));
|
|
320
321
|
return {
|
|
321
322
|
ir,
|
|
322
|
-
duration: ir.duration ?? inferredEnd,
|
|
323
|
+
duration: ir.duration ?? (inferredEnd > 0 ? inferredEnd : DEFAULT_STILL_DURATION),
|
|
323
324
|
segments,
|
|
324
325
|
motionPaths,
|
|
325
326
|
initialValues,
|
|
@@ -639,6 +640,17 @@ var BACK_C2 = BACK_C1 * 1.525;
|
|
|
639
640
|
var BACK_C3 = BACK_C1 + 1;
|
|
640
641
|
var ELASTIC_C4 = 2 * Math.PI / 3;
|
|
641
642
|
var ELASTIC_C5 = 2 * Math.PI / 4.5;
|
|
643
|
+
function springEase(stiffness, damping, velocity) {
|
|
644
|
+
const K = 5;
|
|
645
|
+
const zeta = Math.min(0.999, Math.max(0.05, damping / (2 * Math.sqrt(Math.max(1e-6, stiffness)))));
|
|
646
|
+
const wd = K / zeta * Math.sqrt(1 - zeta * zeta);
|
|
647
|
+
const coef = (K - velocity) / wd;
|
|
648
|
+
return (u) => {
|
|
649
|
+
if (u <= 0) return 0;
|
|
650
|
+
if (u >= 1) return 1;
|
|
651
|
+
return 1 - Math.exp(-K * u) * (Math.cos(wd * u) + coef * Math.sin(wd * u));
|
|
652
|
+
};
|
|
653
|
+
}
|
|
642
654
|
function easeOutBounce(u) {
|
|
643
655
|
const n1 = 7.5625;
|
|
644
656
|
const d1 = 2.75;
|
|
@@ -673,7 +685,11 @@ var EASE_TABLE = {
|
|
|
673
685
|
// bounce: drops and bounces to rest (lands without overshoot)
|
|
674
686
|
easeInBounce: (u) => 1 - easeOutBounce(1 - u),
|
|
675
687
|
easeOutBounce,
|
|
676
|
-
easeInOutBounce: (u) => u < 0.5 ? (1 - easeOutBounce(1 - 2 * u)) / 2 : (1 + easeOutBounce(2 * u - 1)) / 2
|
|
688
|
+
easeInOutBounce: (u) => u < 0.5 ? (1 - easeOutBounce(1 - 2 * u)) / 2 : (1 + easeOutBounce(2 * u - 1)) / 2,
|
|
689
|
+
// damped-spring presets (ζ from damping/(2√stiffness)): 0.5 / 0.30 / 0.90
|
|
690
|
+
spring: springEase(100, 10, 0),
|
|
691
|
+
springBouncy: springEase(180, 8, 0),
|
|
692
|
+
springStiff: springEase(210, 26, 0)
|
|
677
693
|
};
|
|
678
694
|
var EASE_NAMES = Object.keys(EASE_TABLE);
|
|
679
695
|
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@ var DEFAULT_TO_DURATION = 0.5;
|
|
|
4
4
|
var DEFAULT_TWEEN_DURATION = 0.5;
|
|
5
5
|
var DEFAULT_MOTIONPATH_DURATION = 1;
|
|
6
6
|
var DEFAULT_FPS = 30;
|
|
7
|
+
var DEFAULT_STILL_DURATION = 1;
|
|
7
8
|
|
|
8
9
|
// ../core/src/path.ts
|
|
9
10
|
function pathBBox(d) {
|
|
@@ -316,14 +317,14 @@ function compileScene(ir) {
|
|
|
316
317
|
}
|
|
317
318
|
}
|
|
318
319
|
};
|
|
319
|
-
const inferredEnd = ir.timeline ? walk(ir.timeline, 0) : 0;
|
|
320
|
+
const inferredEnd = (ir.timeline ? walk(ir.timeline, 0) : 0) || 0;
|
|
320
321
|
for (const list of segments.values()) list.sort((a, b) => a.t0 - b.t0);
|
|
321
322
|
for (const list of motionPaths.values()) list.sort((a, b) => a.t0 - b.t0);
|
|
322
323
|
const hasCamera = !cameraIsNode && (ir.camera !== void 0 || motionPaths.has("camera") || [...segments.keys()].some((k) => k.startsWith("camera.")));
|
|
323
324
|
const hasPerspective = !cameraIsNode && (ir.camera?.perspective !== void 0 || segments.has("camera.perspective"));
|
|
324
325
|
return {
|
|
325
326
|
ir,
|
|
326
|
-
duration: ir.duration ?? inferredEnd,
|
|
327
|
+
duration: ir.duration ?? (inferredEnd > 0 ? inferredEnd : DEFAULT_STILL_DURATION),
|
|
327
328
|
segments,
|
|
328
329
|
motionPaths,
|
|
329
330
|
initialValues,
|
|
@@ -2850,6 +2851,17 @@ var BACK_C2 = BACK_C1 * 1.525;
|
|
|
2850
2851
|
var BACK_C3 = BACK_C1 + 1;
|
|
2851
2852
|
var ELASTIC_C4 = 2 * Math.PI / 3;
|
|
2852
2853
|
var ELASTIC_C5 = 2 * Math.PI / 4.5;
|
|
2854
|
+
function springEase(stiffness, damping, velocity) {
|
|
2855
|
+
const K3 = 5;
|
|
2856
|
+
const zeta = Math.min(0.999, Math.max(0.05, damping / (2 * Math.sqrt(Math.max(1e-6, stiffness)))));
|
|
2857
|
+
const wd = K3 / zeta * Math.sqrt(1 - zeta * zeta);
|
|
2858
|
+
const coef = (K3 - velocity) / wd;
|
|
2859
|
+
return (u) => {
|
|
2860
|
+
if (u <= 0) return 0;
|
|
2861
|
+
if (u >= 1) return 1;
|
|
2862
|
+
return 1 - Math.exp(-K3 * u) * (Math.cos(wd * u) + coef * Math.sin(wd * u));
|
|
2863
|
+
};
|
|
2864
|
+
}
|
|
2853
2865
|
function easeOutBounce(u) {
|
|
2854
2866
|
const n1 = 7.5625;
|
|
2855
2867
|
const d1 = 2.75;
|
|
@@ -2884,7 +2896,11 @@ var EASE_TABLE = {
|
|
|
2884
2896
|
// bounce: drops and bounces to rest (lands without overshoot)
|
|
2885
2897
|
easeInBounce: (u) => 1 - easeOutBounce(1 - u),
|
|
2886
2898
|
easeOutBounce,
|
|
2887
|
-
easeInOutBounce: (u) => u < 0.5 ? (1 - easeOutBounce(1 - 2 * u)) / 2 : (1 + easeOutBounce(2 * u - 1)) / 2
|
|
2899
|
+
easeInOutBounce: (u) => u < 0.5 ? (1 - easeOutBounce(1 - 2 * u)) / 2 : (1 + easeOutBounce(2 * u - 1)) / 2,
|
|
2900
|
+
// damped-spring presets (ζ from damping/(2√stiffness)): 0.5 / 0.30 / 0.90
|
|
2901
|
+
spring: springEase(100, 10, 0),
|
|
2902
|
+
springBouncy: springEase(180, 8, 0),
|
|
2903
|
+
springStiff: springEase(210, 26, 0)
|
|
2888
2904
|
};
|
|
2889
2905
|
var EASE_NAMES = Object.keys(EASE_TABLE);
|
|
2890
2906
|
function resolveEase(ease) {
|
|
@@ -2894,6 +2910,10 @@ function resolveEase(ease) {
|
|
|
2894
2910
|
if (!fn) throw new Error(`unknown ease "${ease}" \u2014 valid: ${Object.keys(EASE_TABLE).join(", ")}`);
|
|
2895
2911
|
return fn;
|
|
2896
2912
|
}
|
|
2913
|
+
if ("spring" in ease) {
|
|
2914
|
+
const { stiffness = 100, damping = 10, velocity = 0 } = ease.spring;
|
|
2915
|
+
return springEase(stiffness, damping, velocity);
|
|
2916
|
+
}
|
|
2897
2917
|
return cubicBezierEase(...ease.cubicBezier);
|
|
2898
2918
|
}
|
|
2899
2919
|
function cubicBezierEase(x1, y1, x2, y2) {
|
|
@@ -3505,6 +3525,7 @@ export {
|
|
|
3505
3525
|
DEFAULT_CROSSFADE,
|
|
3506
3526
|
DEFAULT_FPS,
|
|
3507
3527
|
DEFAULT_MOTIONPATH_DURATION,
|
|
3528
|
+
DEFAULT_STILL_DURATION,
|
|
3508
3529
|
DEFAULT_TO_DURATION,
|
|
3509
3530
|
DEFAULT_TWEEN_DURATION,
|
|
3510
3531
|
DEVICE_PRESET_NAMES,
|
package/dist/labels.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
var DEFAULT_TO_DURATION = 0.5;
|
|
5
5
|
var DEFAULT_TWEEN_DURATION = 0.5;
|
|
6
6
|
var DEFAULT_MOTIONPATH_DURATION = 1;
|
|
7
|
+
var DEFAULT_STILL_DURATION = 1;
|
|
7
8
|
|
|
8
9
|
// ../core/src/path.ts
|
|
9
10
|
function locate(segCount, u) {
|
|
@@ -300,14 +301,14 @@ function compileScene(ir) {
|
|
|
300
301
|
}
|
|
301
302
|
}
|
|
302
303
|
};
|
|
303
|
-
const inferredEnd = ir.timeline ? walk(ir.timeline, 0) : 0;
|
|
304
|
+
const inferredEnd = (ir.timeline ? walk(ir.timeline, 0) : 0) || 0;
|
|
304
305
|
for (const list of segments.values()) list.sort((a, b) => a.t0 - b.t0);
|
|
305
306
|
for (const list of motionPaths.values()) list.sort((a, b) => a.t0 - b.t0);
|
|
306
307
|
const hasCamera = !cameraIsNode && (ir.camera !== void 0 || motionPaths.has("camera") || [...segments.keys()].some((k) => k.startsWith("camera.")));
|
|
307
308
|
const hasPerspective = !cameraIsNode && (ir.camera?.perspective !== void 0 || segments.has("camera.perspective"));
|
|
308
309
|
return {
|
|
309
310
|
ir,
|
|
310
|
-
duration: ir.duration ?? inferredEnd,
|
|
311
|
+
duration: ir.duration ?? (inferredEnd > 0 ? inferredEnd : DEFAULT_STILL_DURATION),
|
|
311
312
|
segments,
|
|
312
313
|
motionPaths,
|
|
313
314
|
initialValues,
|
|
@@ -597,6 +598,17 @@ var BACK_C2 = BACK_C1 * 1.525;
|
|
|
597
598
|
var BACK_C3 = BACK_C1 + 1;
|
|
598
599
|
var ELASTIC_C4 = 2 * Math.PI / 3;
|
|
599
600
|
var ELASTIC_C5 = 2 * Math.PI / 4.5;
|
|
601
|
+
function springEase(stiffness, damping, velocity) {
|
|
602
|
+
const K = 5;
|
|
603
|
+
const zeta = Math.min(0.999, Math.max(0.05, damping / (2 * Math.sqrt(Math.max(1e-6, stiffness)))));
|
|
604
|
+
const wd = K / zeta * Math.sqrt(1 - zeta * zeta);
|
|
605
|
+
const coef = (K - velocity) / wd;
|
|
606
|
+
return (u) => {
|
|
607
|
+
if (u <= 0) return 0;
|
|
608
|
+
if (u >= 1) return 1;
|
|
609
|
+
return 1 - Math.exp(-K * u) * (Math.cos(wd * u) + coef * Math.sin(wd * u));
|
|
610
|
+
};
|
|
611
|
+
}
|
|
600
612
|
function easeOutBounce(u) {
|
|
601
613
|
const n1 = 7.5625;
|
|
602
614
|
const d1 = 2.75;
|
|
@@ -631,7 +643,11 @@ var EASE_TABLE = {
|
|
|
631
643
|
// bounce: drops and bounces to rest (lands without overshoot)
|
|
632
644
|
easeInBounce: (u) => 1 - easeOutBounce(1 - u),
|
|
633
645
|
easeOutBounce,
|
|
634
|
-
easeInOutBounce: (u) => u < 0.5 ? (1 - easeOutBounce(1 - 2 * u)) / 2 : (1 + easeOutBounce(2 * u - 1)) / 2
|
|
646
|
+
easeInOutBounce: (u) => u < 0.5 ? (1 - easeOutBounce(1 - 2 * u)) / 2 : (1 + easeOutBounce(2 * u - 1)) / 2,
|
|
647
|
+
// damped-spring presets (ζ from damping/(2√stiffness)): 0.5 / 0.30 / 0.90
|
|
648
|
+
spring: springEase(100, 10, 0),
|
|
649
|
+
springBouncy: springEase(180, 8, 0),
|
|
650
|
+
springStiff: springEase(210, 26, 0)
|
|
635
651
|
};
|
|
636
652
|
var EASE_NAMES = Object.keys(EASE_TABLE);
|
|
637
653
|
|
package/dist/trace-cli.js
CHANGED
|
@@ -42,6 +42,17 @@ var BACK_C2 = BACK_C1 * 1.525;
|
|
|
42
42
|
var BACK_C3 = BACK_C1 + 1;
|
|
43
43
|
var ELASTIC_C4 = 2 * Math.PI / 3;
|
|
44
44
|
var ELASTIC_C5 = 2 * Math.PI / 4.5;
|
|
45
|
+
function springEase(stiffness, damping, velocity) {
|
|
46
|
+
const K = 5;
|
|
47
|
+
const zeta = Math.min(0.999, Math.max(0.05, damping / (2 * Math.sqrt(Math.max(1e-6, stiffness)))));
|
|
48
|
+
const wd = K / zeta * Math.sqrt(1 - zeta * zeta);
|
|
49
|
+
const coef = (K - velocity) / wd;
|
|
50
|
+
return (u) => {
|
|
51
|
+
if (u <= 0) return 0;
|
|
52
|
+
if (u >= 1) return 1;
|
|
53
|
+
return 1 - Math.exp(-K * u) * (Math.cos(wd * u) + coef * Math.sin(wd * u));
|
|
54
|
+
};
|
|
55
|
+
}
|
|
45
56
|
function easeOutBounce(u) {
|
|
46
57
|
const n1 = 7.5625;
|
|
47
58
|
const d1 = 2.75;
|
|
@@ -76,7 +87,11 @@ var EASE_TABLE = {
|
|
|
76
87
|
// bounce: drops and bounces to rest (lands without overshoot)
|
|
77
88
|
easeInBounce: (u) => 1 - easeOutBounce(1 - u),
|
|
78
89
|
easeOutBounce,
|
|
79
|
-
easeInOutBounce: (u) => u < 0.5 ? (1 - easeOutBounce(1 - 2 * u)) / 2 : (1 + easeOutBounce(2 * u - 1)) / 2
|
|
90
|
+
easeInOutBounce: (u) => u < 0.5 ? (1 - easeOutBounce(1 - 2 * u)) / 2 : (1 + easeOutBounce(2 * u - 1)) / 2,
|
|
91
|
+
// damped-spring presets (ζ from damping/(2√stiffness)): 0.5 / 0.30 / 0.90
|
|
92
|
+
spring: springEase(100, 10, 0),
|
|
93
|
+
springBouncy: springEase(180, 8, 0),
|
|
94
|
+
springStiff: springEase(210, 26, 0)
|
|
80
95
|
};
|
|
81
96
|
var EASE_NAMES = Object.keys(EASE_TABLE);
|
|
82
97
|
|
package/dist/types/ir.d.ts
CHANGED
|
@@ -8,10 +8,24 @@
|
|
|
8
8
|
* Semantics: a scene is evaluated as a pure function of continuous time
|
|
9
9
|
* `evaluate(scene, tSeconds) -> DisplayList`. `fps` is a render hint only.
|
|
10
10
|
*/
|
|
11
|
-
export type EaseName = "linear" | "easeInQuad" | "easeOutQuad" | "easeInOutQuad" | "easeInCubic" | "easeOutCubic" | "easeInOutCubic" | "easeInQuart" | "easeOutQuart" | "easeInOutQuart" | "easeInExpo" | "easeOutExpo" | "easeInOutExpo" | "easeInBack" | "easeOutBack" | "easeInOutBack" | "easeInElastic" | "easeOutElastic" | "easeInOutElastic" | "easeInBounce" | "easeOutBounce" | "easeInOutBounce";
|
|
11
|
+
export type EaseName = "linear" | "easeInQuad" | "easeOutQuad" | "easeInOutQuad" | "easeInCubic" | "easeOutCubic" | "easeInOutCubic" | "easeInQuart" | "easeOutQuart" | "easeInOutQuart" | "easeInExpo" | "easeOutExpo" | "easeInOutExpo" | "easeInBack" | "easeOutBack" | "easeInOutBack" | "easeInElastic" | "easeOutElastic" | "easeInOutElastic" | "easeInBounce" | "easeOutBounce" | "easeInOutBounce" | "spring" | "springBouncy" | "springStiff";
|
|
12
|
+
/**
|
|
13
|
+
* A custom spring: a damped harmonic oscillator sampled over the tween's normalized
|
|
14
|
+
* 0..1 window (mass = 1). `stiffness`/`damping` set the damping ratio
|
|
15
|
+
* ζ = damping / (2·√stiffness) — the SHAPE knob (low ζ ⇒ bouncy, high ζ ⇒ snappy);
|
|
16
|
+
* `velocity` is an initial launch slope. Defaults: stiffness 100, damping 10
|
|
17
|
+
* (ζ = 0.5), velocity 0.
|
|
18
|
+
*/
|
|
19
|
+
export interface SpringEase {
|
|
20
|
+
spring: {
|
|
21
|
+
stiffness?: number;
|
|
22
|
+
damping?: number;
|
|
23
|
+
velocity?: number;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
12
26
|
export type Ease = EaseName | {
|
|
13
27
|
cubicBezier: [number, number, number, number];
|
|
14
|
-
};
|
|
28
|
+
} | SpringEase;
|
|
15
29
|
export type Anchor = "top-left" | "top-center" | "top-right" | "center-left" | "center" | "center-right" | "bottom-left" | "bottom-center" | "bottom-right";
|
|
16
30
|
export interface Size {
|
|
17
31
|
width: number;
|
|
@@ -508,3 +522,6 @@ export declare const DEFAULT_TO_DURATION = 0.5;
|
|
|
508
522
|
export declare const DEFAULT_TWEEN_DURATION = 0.5;
|
|
509
523
|
export declare const DEFAULT_MOTIONPATH_DURATION = 1;
|
|
510
524
|
export declare const DEFAULT_FPS = 30;
|
|
525
|
+
/** Fallback length (seconds) for a scene with no animating timeline — a static
|
|
526
|
+
* frame still needs a positive duration to render. Override with scene `duration`. */
|
|
527
|
+
export declare const DEFAULT_STILL_DURATION = 1;
|
|
@@ -43,6 +43,11 @@ reframe diff ref.png scene.ts --mode blend # 50% overlay — spot drift
|
|
|
43
43
|
|
|
44
44
|
Loop: `--mode grid` to measure → write the node tree → `--mode side`/`diff` to compare →
|
|
45
45
|
fix coordinates/sizes/colors → repeat until faithful. Pick the frame with `--t <sec>`.
|
|
46
|
+
The grid is rendered at the reference's **full resolution** — the printed numbers are
|
|
47
|
+
exact scene pixels, so place nodes at the labelled coordinates directly (no scaling).
|
|
48
|
+
`diff`/`blend` are sharpest on hard edges (type, icons, frames); over large **soft
|
|
49
|
+
gradients/glows** they always light up "different" even when close, so tune those by
|
|
50
|
+
eye with `--mode side` rather than chasing the diff to black.
|
|
46
51
|
|
|
47
52
|
### 3. Apply the cinematic-craft checklist
|
|
48
53
|
|
package/guides/edsl-guide.md
CHANGED
|
@@ -68,6 +68,9 @@ Factories return plain data. Every node needs a unique `id`.
|
|
|
68
68
|
`"top-left"` (default) | `"top-center"` | `"top-right"` | `"center-left"` |
|
|
69
69
|
`"center"` | `"center-right"` | `"bottom-left"` | `"bottom-center"` | `"bottom-right"`.
|
|
70
70
|
Example: a bar that grows upward = `anchor: "bottom-left"` + animate `height`.
|
|
71
|
+
**Text alignment is `anchor`, not a separate `align` prop:** the anchor's horizontal
|
|
72
|
+
half sets the text align — `"…-left"` left-aligns, `"…-center"`/`"center"` centers,
|
|
73
|
+
`"…-right"` right-aligns (a right-aligned wordmark in a corner = `anchor: "bottom-right"`).
|
|
71
74
|
Font: use `fontFamily: "Inter"` (weights 400/700/800 are available).
|
|
72
75
|
|
|
73
76
|
### Layout helpers (evenly spacing things)
|
|
@@ -132,8 +135,13 @@ Expressive eases for a premium feel: `easeIn/Out/InOutBack` (overshoots past the
|
|
|
132
135
|
target then settles — a pop/snap), `easeIn/Out/InOutElastic` (rings around the
|
|
133
136
|
target — a playful spring), `easeIn/Out/InOutBounce` (drops and bounces to rest).
|
|
134
137
|
A logo or card "popping" in usually wants `easeOutBack`; a stamp landing,
|
|
135
|
-
`easeOutBounce`.
|
|
136
|
-
|
|
138
|
+
`easeOutBounce`. Physical springs settle to rest within the tween's duration:
|
|
139
|
+
`spring` (a natural settle), `springBouncy` (rings more), `springStiff` (snappy,
|
|
140
|
+
barely overshoots) — or tune your own with `{ spring: { stiffness, damping, velocity } }`
|
|
141
|
+
(damping ratio = `damping / (2·√stiffness)`; lower ⇒ bouncier).
|
|
142
|
+
Scene duration is inferred from the timeline. For a **static frame** you can omit
|
|
143
|
+
`timeline` entirely (or set scene `duration: <seconds>`) — a still defaults to a 1s
|
|
144
|
+
render; no throwaway `wait` is needed.
|
|
137
145
|
|
|
138
146
|
## Behaviors: continuous motion during holds
|
|
139
147
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reframe-video",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.15",
|
|
4
4
|
"description": "Declarative motion graphics that AI can write and humans can tweak — human edits survive AI regeneration. Deterministic mp4 renders from a plain-data scene format.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"motion-graphics",
|