reframe-video 0.6.16 → 0.6.17
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 +7 -3
- package/dist/browserEntry.js +14 -3
- package/dist/cli.js +7 -3
- package/dist/diff.js +7 -3
- package/dist/index.js +18 -5
- package/dist/labels.js +7 -3
- package/dist/types/compile.d.ts +2 -0
- package/dist/types/ir.d.ts +8 -0
- package/guides/edsl-guide.md +8 -2
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -327,6 +327,7 @@ function compileScene(ir) {
|
|
|
327
327
|
for (const list of motionPaths.values()) list.sort((a, b) => a.t0 - b.t0);
|
|
328
328
|
const hasCamera = !cameraIsNode && (ir.camera !== void 0 || motionPaths.has("camera") || [...segments.keys()].some((k) => k.startsWith("camera.")));
|
|
329
329
|
const hasPerspective = !cameraIsNode && (ir.camera?.perspective !== void 0 || segments.has("camera.perspective"));
|
|
330
|
+
const zSort = !cameraIsNode && ir.camera?.zSort === true && hasPerspective;
|
|
330
331
|
return {
|
|
331
332
|
ir,
|
|
332
333
|
duration: ir.duration ?? (inferredEnd > 0 ? inferredEnd : DEFAULT_STILL_DURATION),
|
|
@@ -338,7 +339,8 @@ function compileScene(ir) {
|
|
|
338
339
|
labelTimes,
|
|
339
340
|
beatTimes,
|
|
340
341
|
hasCamera,
|
|
341
|
-
hasPerspective
|
|
342
|
+
hasPerspective,
|
|
343
|
+
zSort
|
|
342
344
|
};
|
|
343
345
|
}
|
|
344
346
|
var key;
|
|
@@ -544,8 +546,10 @@ function validateScene(ir) {
|
|
|
544
546
|
problems.push(`camera: a node is already named "camera" \u2014 rename that node or drop the scene camera (the id "camera" can't be both)`);
|
|
545
547
|
}
|
|
546
548
|
for (const [key2, value] of Object.entries(ir.camera)) {
|
|
547
|
-
if (
|
|
548
|
-
problems.push(`camera
|
|
549
|
+
if (key2 === "zSort") {
|
|
550
|
+
if (typeof value !== "boolean") problems.push(`camera.zSort must be a boolean`);
|
|
551
|
+
} else if (!CAMERA_PROPS.includes(key2)) {
|
|
552
|
+
problems.push(`camera: "${key2}" is not a camera prop \u2014 valid props: ${CAMERA_PROPS.join(", ")}, zSort`);
|
|
549
553
|
} else if (typeof value !== "number") {
|
|
550
554
|
problems.push(`camera.${key2} must be a number`);
|
|
551
555
|
} else if (key2 === "perspective" && value <= 0) {
|
package/dist/browserEntry.js
CHANGED
|
@@ -324,6 +324,7 @@
|
|
|
324
324
|
for (const list of motionPaths.values()) list.sort((a, b) => a.t0 - b.t0);
|
|
325
325
|
const hasCamera = !cameraIsNode && (ir.camera !== void 0 || motionPaths.has("camera") || [...segments.keys()].some((k) => k.startsWith("camera.")));
|
|
326
326
|
const hasPerspective = !cameraIsNode && (ir.camera?.perspective !== void 0 || segments.has("camera.perspective"));
|
|
327
|
+
const zSort = !cameraIsNode && ir.camera?.zSort === true && hasPerspective;
|
|
327
328
|
return {
|
|
328
329
|
ir,
|
|
329
330
|
duration: ir.duration ?? (inferredEnd > 0 ? inferredEnd : DEFAULT_STILL_DURATION),
|
|
@@ -335,7 +336,8 @@
|
|
|
335
336
|
labelTimes,
|
|
336
337
|
beatTimes,
|
|
337
338
|
hasCamera,
|
|
338
|
-
hasPerspective
|
|
339
|
+
hasPerspective,
|
|
340
|
+
zSort
|
|
339
341
|
};
|
|
340
342
|
}
|
|
341
343
|
|
|
@@ -750,6 +752,9 @@
|
|
|
750
752
|
if (extra <= 0) return fx;
|
|
751
753
|
return { ...fx, blur: z0((fx.blur ?? 0) + extra) };
|
|
752
754
|
};
|
|
755
|
+
const zSort = compiled2.zSort;
|
|
756
|
+
const depthOf = (node, zAcc) => zAcc + num(node.id, "z", node.props.z ?? 0);
|
|
757
|
+
const depthOrder = (children, zAcc) => [...children].sort((a, b) => depthOf(b, zAcc) - depthOf(a, zAcc));
|
|
753
758
|
const walk = (node, parent, parentOpacity, clips, zAcc, project) => {
|
|
754
759
|
const id = node.id;
|
|
755
760
|
const clipSpread = clips.length > 0 ? { clips } : void 0;
|
|
@@ -824,7 +829,8 @@
|
|
|
824
829
|
for (let i = 1; i < node.children.length; i++) walk(node.children[i], matrix, opacity, childClips, depth, project);
|
|
825
830
|
ops.push({ type: "matte-pop", id, transform: matrix, opacity });
|
|
826
831
|
} else {
|
|
827
|
-
|
|
832
|
+
const kids = zSort && project ? depthOrder(node.children, depth) : node.children;
|
|
833
|
+
for (const child of kids) walk(child, matrix, opacity, childClips, depth, project);
|
|
828
834
|
}
|
|
829
835
|
if (hasFx) ops.push({ type: "group-fx-pop", id, transform: matrix, opacity });
|
|
830
836
|
return;
|
|
@@ -966,7 +972,12 @@
|
|
|
966
972
|
},
|
|
967
973
|
compiled2.ir.size
|
|
968
974
|
) : IDENTITY;
|
|
969
|
-
|
|
975
|
+
let roots = compiled2.ir.nodes;
|
|
976
|
+
if (zSort) {
|
|
977
|
+
const isHud = (n) => !!(n.props.fixed && compiled2.hasCamera);
|
|
978
|
+
roots = [...depthOrder(compiled2.ir.nodes.filter((n) => !isHud(n)), 0), ...compiled2.ir.nodes.filter(isHud)];
|
|
979
|
+
}
|
|
980
|
+
for (const node of roots) {
|
|
970
981
|
const root = compiled2.hasCamera && node.props.fixed ? IDENTITY : cameraRoot;
|
|
971
982
|
const project = persp && !(node.props.fixed && compiled2.hasCamera);
|
|
972
983
|
walk(node, root, 1, [], 0, project);
|
package/dist/cli.js
CHANGED
|
@@ -314,6 +314,7 @@ function compileScene(ir) {
|
|
|
314
314
|
for (const list of motionPaths.values()) list.sort((a, b) => a.t0 - b.t0);
|
|
315
315
|
const hasCamera = !cameraIsNode && (ir.camera !== void 0 || motionPaths.has("camera") || [...segments.keys()].some((k) => k.startsWith("camera.")));
|
|
316
316
|
const hasPerspective = !cameraIsNode && (ir.camera?.perspective !== void 0 || segments.has("camera.perspective"));
|
|
317
|
+
const zSort = !cameraIsNode && ir.camera?.zSort === true && hasPerspective;
|
|
317
318
|
return {
|
|
318
319
|
ir,
|
|
319
320
|
duration: ir.duration ?? (inferredEnd > 0 ? inferredEnd : DEFAULT_STILL_DURATION),
|
|
@@ -325,7 +326,8 @@ function compileScene(ir) {
|
|
|
325
326
|
labelTimes,
|
|
326
327
|
beatTimes,
|
|
327
328
|
hasCamera,
|
|
328
|
-
hasPerspective
|
|
329
|
+
hasPerspective,
|
|
330
|
+
zSort
|
|
329
331
|
};
|
|
330
332
|
}
|
|
331
333
|
|
|
@@ -558,8 +560,10 @@ function validateScene(ir) {
|
|
|
558
560
|
problems.push(`camera: a node is already named "camera" \u2014 rename that node or drop the scene camera (the id "camera" can't be both)`);
|
|
559
561
|
}
|
|
560
562
|
for (const [key2, value] of Object.entries(ir.camera)) {
|
|
561
|
-
if (
|
|
562
|
-
problems.push(`camera
|
|
563
|
+
if (key2 === "zSort") {
|
|
564
|
+
if (typeof value !== "boolean") problems.push(`camera.zSort must be a boolean`);
|
|
565
|
+
} else if (!CAMERA_PROPS.includes(key2)) {
|
|
566
|
+
problems.push(`camera: "${key2}" is not a camera prop \u2014 valid props: ${CAMERA_PROPS.join(", ")}, zSort`);
|
|
563
567
|
} else if (typeof value !== "number") {
|
|
564
568
|
problems.push(`camera.${key2} must be a number`);
|
|
565
569
|
} else if (key2 === "perspective" && value <= 0) {
|
package/dist/diff.js
CHANGED
|
@@ -320,6 +320,7 @@ function compileScene(ir) {
|
|
|
320
320
|
for (const list of motionPaths.values()) list.sort((a, b) => a.t0 - b.t0);
|
|
321
321
|
const hasCamera = !cameraIsNode && (ir.camera !== void 0 || motionPaths.has("camera") || [...segments.keys()].some((k) => k.startsWith("camera.")));
|
|
322
322
|
const hasPerspective = !cameraIsNode && (ir.camera?.perspective !== void 0 || segments.has("camera.perspective"));
|
|
323
|
+
const zSort = !cameraIsNode && ir.camera?.zSort === true && hasPerspective;
|
|
323
324
|
return {
|
|
324
325
|
ir,
|
|
325
326
|
duration: ir.duration ?? (inferredEnd > 0 ? inferredEnd : DEFAULT_STILL_DURATION),
|
|
@@ -331,7 +332,8 @@ function compileScene(ir) {
|
|
|
331
332
|
labelTimes,
|
|
332
333
|
beatTimes,
|
|
333
334
|
hasCamera,
|
|
334
|
-
hasPerspective
|
|
335
|
+
hasPerspective,
|
|
336
|
+
zSort
|
|
335
337
|
};
|
|
336
338
|
}
|
|
337
339
|
|
|
@@ -564,8 +566,10 @@ function validateScene(ir) {
|
|
|
564
566
|
problems.push(`camera: a node is already named "camera" \u2014 rename that node or drop the scene camera (the id "camera" can't be both)`);
|
|
565
567
|
}
|
|
566
568
|
for (const [key2, value] of Object.entries(ir.camera)) {
|
|
567
|
-
if (
|
|
568
|
-
problems.push(`camera
|
|
569
|
+
if (key2 === "zSort") {
|
|
570
|
+
if (typeof value !== "boolean") problems.push(`camera.zSort must be a boolean`);
|
|
571
|
+
} else if (!CAMERA_PROPS.includes(key2)) {
|
|
572
|
+
problems.push(`camera: "${key2}" is not a camera prop \u2014 valid props: ${CAMERA_PROPS.join(", ")}, zSort`);
|
|
569
573
|
} else if (typeof value !== "number") {
|
|
570
574
|
problems.push(`camera.${key2} must be a number`);
|
|
571
575
|
} else if (key2 === "perspective" && value <= 0) {
|
package/dist/index.js
CHANGED
|
@@ -324,6 +324,7 @@ function compileScene(ir) {
|
|
|
324
324
|
for (const list of motionPaths.values()) list.sort((a, b) => a.t0 - b.t0);
|
|
325
325
|
const hasCamera = !cameraIsNode && (ir.camera !== void 0 || motionPaths.has("camera") || [...segments.keys()].some((k) => k.startsWith("camera.")));
|
|
326
326
|
const hasPerspective = !cameraIsNode && (ir.camera?.perspective !== void 0 || segments.has("camera.perspective"));
|
|
327
|
+
const zSort = !cameraIsNode && ir.camera?.zSort === true && hasPerspective;
|
|
327
328
|
return {
|
|
328
329
|
ir,
|
|
329
330
|
duration: ir.duration ?? (inferredEnd > 0 ? inferredEnd : DEFAULT_STILL_DURATION),
|
|
@@ -335,7 +336,8 @@ function compileScene(ir) {
|
|
|
335
336
|
labelTimes,
|
|
336
337
|
beatTimes,
|
|
337
338
|
hasCamera,
|
|
338
|
-
hasPerspective
|
|
339
|
+
hasPerspective,
|
|
340
|
+
zSort
|
|
339
341
|
};
|
|
340
342
|
}
|
|
341
343
|
|
|
@@ -568,8 +570,10 @@ function validateScene(ir) {
|
|
|
568
570
|
problems.push(`camera: a node is already named "camera" \u2014 rename that node or drop the scene camera (the id "camera" can't be both)`);
|
|
569
571
|
}
|
|
570
572
|
for (const [key2, value] of Object.entries(ir.camera)) {
|
|
571
|
-
if (
|
|
572
|
-
problems.push(`camera
|
|
573
|
+
if (key2 === "zSort") {
|
|
574
|
+
if (typeof value !== "boolean") problems.push(`camera.zSort must be a boolean`);
|
|
575
|
+
} else if (!CAMERA_PROPS.includes(key2)) {
|
|
576
|
+
problems.push(`camera: "${key2}" is not a camera prop \u2014 valid props: ${CAMERA_PROPS.join(", ")}, zSort`);
|
|
573
577
|
} else if (typeof value !== "number") {
|
|
574
578
|
problems.push(`camera.${key2} must be a number`);
|
|
575
579
|
} else if (key2 === "perspective" && value <= 0) {
|
|
@@ -3226,6 +3230,9 @@ function evaluate(compiled, t) {
|
|
|
3226
3230
|
if (extra <= 0) return fx;
|
|
3227
3231
|
return { ...fx, blur: z0((fx.blur ?? 0) + extra) };
|
|
3228
3232
|
};
|
|
3233
|
+
const zSort = compiled.zSort;
|
|
3234
|
+
const depthOf = (node, zAcc) => zAcc + num(node.id, "z", node.props.z ?? 0);
|
|
3235
|
+
const depthOrder = (children, zAcc) => [...children].sort((a, b) => depthOf(b, zAcc) - depthOf(a, zAcc));
|
|
3229
3236
|
const walk = (node, parent, parentOpacity, clips, zAcc, project) => {
|
|
3230
3237
|
const id = node.id;
|
|
3231
3238
|
const clipSpread = clips.length > 0 ? { clips } : void 0;
|
|
@@ -3300,7 +3307,8 @@ function evaluate(compiled, t) {
|
|
|
3300
3307
|
for (let i = 1; i < node.children.length; i++) walk(node.children[i], matrix, opacity, childClips, depth, project);
|
|
3301
3308
|
ops.push({ type: "matte-pop", id, transform: matrix, opacity });
|
|
3302
3309
|
} else {
|
|
3303
|
-
|
|
3310
|
+
const kids = zSort && project ? depthOrder(node.children, depth) : node.children;
|
|
3311
|
+
for (const child of kids) walk(child, matrix, opacity, childClips, depth, project);
|
|
3304
3312
|
}
|
|
3305
3313
|
if (hasFx) ops.push({ type: "group-fx-pop", id, transform: matrix, opacity });
|
|
3306
3314
|
return;
|
|
@@ -3442,7 +3450,12 @@ function evaluate(compiled, t) {
|
|
|
3442
3450
|
},
|
|
3443
3451
|
compiled.ir.size
|
|
3444
3452
|
) : IDENTITY;
|
|
3445
|
-
|
|
3453
|
+
let roots = compiled.ir.nodes;
|
|
3454
|
+
if (zSort) {
|
|
3455
|
+
const isHud = (n3) => !!(n3.props.fixed && compiled.hasCamera);
|
|
3456
|
+
roots = [...depthOrder(compiled.ir.nodes.filter((n3) => !isHud(n3)), 0), ...compiled.ir.nodes.filter(isHud)];
|
|
3457
|
+
}
|
|
3458
|
+
for (const node of roots) {
|
|
3446
3459
|
const root = compiled.hasCamera && node.props.fixed ? IDENTITY : cameraRoot;
|
|
3447
3460
|
const project = persp && !(node.props.fixed && compiled.hasCamera);
|
|
3448
3461
|
walk(node, root, 1, [], 0, project);
|
package/dist/labels.js
CHANGED
|
@@ -308,6 +308,7 @@ function compileScene(ir) {
|
|
|
308
308
|
for (const list of motionPaths.values()) list.sort((a, b) => a.t0 - b.t0);
|
|
309
309
|
const hasCamera = !cameraIsNode && (ir.camera !== void 0 || motionPaths.has("camera") || [...segments.keys()].some((k) => k.startsWith("camera.")));
|
|
310
310
|
const hasPerspective = !cameraIsNode && (ir.camera?.perspective !== void 0 || segments.has("camera.perspective"));
|
|
311
|
+
const zSort = !cameraIsNode && ir.camera?.zSort === true && hasPerspective;
|
|
311
312
|
return {
|
|
312
313
|
ir,
|
|
313
314
|
duration: ir.duration ?? (inferredEnd > 0 ? inferredEnd : DEFAULT_STILL_DURATION),
|
|
@@ -319,7 +320,8 @@ function compileScene(ir) {
|
|
|
319
320
|
labelTimes,
|
|
320
321
|
beatTimes,
|
|
321
322
|
hasCamera,
|
|
322
|
-
hasPerspective
|
|
323
|
+
hasPerspective,
|
|
324
|
+
zSort
|
|
323
325
|
};
|
|
324
326
|
}
|
|
325
327
|
|
|
@@ -552,8 +554,10 @@ function validateScene(ir) {
|
|
|
552
554
|
problems.push(`camera: a node is already named "camera" \u2014 rename that node or drop the scene camera (the id "camera" can't be both)`);
|
|
553
555
|
}
|
|
554
556
|
for (const [key2, value] of Object.entries(ir.camera)) {
|
|
555
|
-
if (
|
|
556
|
-
problems.push(`camera
|
|
557
|
+
if (key2 === "zSort") {
|
|
558
|
+
if (typeof value !== "boolean") problems.push(`camera.zSort must be a boolean`);
|
|
559
|
+
} else if (!CAMERA_PROPS.includes(key2)) {
|
|
560
|
+
problems.push(`camera: "${key2}" is not a camera prop \u2014 valid props: ${CAMERA_PROPS.join(", ")}, zSort`);
|
|
557
561
|
} else if (typeof value !== "number") {
|
|
558
562
|
problems.push(`camera.${key2} must be a number`);
|
|
559
563
|
} else if (key2 === "perspective" && value <= 0) {
|
package/dist/types/compile.d.ts
CHANGED
|
@@ -53,5 +53,7 @@ export interface CompiledScene {
|
|
|
53
53
|
hasCamera: boolean;
|
|
54
54
|
/** True iff the scene sets/animates `camera.perspective` (gates depth projection). */
|
|
55
55
|
hasPerspective: boolean;
|
|
56
|
+
/** True iff `camera.zSort` is on (gates depth-ordered paint; needs perspective). */
|
|
57
|
+
zSort: boolean;
|
|
56
58
|
}
|
|
57
59
|
export declare function compileScene(ir: SceneIR): CompiledScene;
|
package/dist/types/ir.d.ts
CHANGED
|
@@ -472,6 +472,14 @@ export interface CameraIR {
|
|
|
472
472
|
*/
|
|
473
473
|
focus?: number;
|
|
474
474
|
aperture?: number;
|
|
475
|
+
/**
|
|
476
|
+
* Paint order by depth (requires `perspective`). Off by default — drawing stays
|
|
477
|
+
* array order. When `true`, siblings at each level are drawn far-to-near (larger
|
|
478
|
+
* world `z` first) so nearer nodes occlude farther ones without hand-ordering the
|
|
479
|
+
* tree. A `fixed` HUD stays on top; a track-matte group keeps its child order (the
|
|
480
|
+
* first child is the mask). Discrete flag, not animatable.
|
|
481
|
+
*/
|
|
482
|
+
zSort?: boolean;
|
|
475
483
|
}
|
|
476
484
|
export interface SceneIR {
|
|
477
485
|
version: 1;
|
package/guides/edsl-guide.md
CHANGED
|
@@ -221,11 +221,17 @@ scene({
|
|
|
221
221
|
`aperture` for an iris pull. Absent/`0` ⇒ no blur. HUD/UI text should be `fixed` so it stays
|
|
222
222
|
crisp (a `fixed` node opts out of DOF too). It feeds the same `blur` op, so it composes with an
|
|
223
223
|
authored `blur`.
|
|
224
|
+
- **Occlusion by depth** is opt-in: set `camera.zSort: true` and siblings paint far→near
|
|
225
|
+
(larger `z` first) so nearer nodes cover farther ones without hand-ordering the tree (a
|
|
226
|
+
`fixed` HUD stays on top; a track-matte group keeps its child order). Off by default — paint
|
|
227
|
+
stays array order. Gotcha: with `zSort`, a full-screen background rect at `z: 0` is the
|
|
228
|
+
NEAREST plane and paints on top — use the scene `background` color instead, or give the
|
|
229
|
+
backdrop a large `z`.
|
|
224
230
|
- **Limits (honest):** `rotateX`/`rotateY` are an affine approximation (cos-foreshorten +
|
|
225
231
|
keystone skew) — a single rotated quad is really a trapezoid Canvas 2D can't draw, so it
|
|
226
232
|
reads as a flip/tilt, not a pixel-true 3D face (that needs WebGL). Depth positioning
|
|
227
|
-
(parallax, convergence, dolly) IS exact.
|
|
228
|
-
|
|
233
|
+
(parallax, convergence, dolly) IS exact. No GPU 3D, no z-buffer (per-pixel) — `zSort` orders
|
|
234
|
+
whole nodes, so two INTERSECTING planes can't visually cross.
|
|
229
235
|
|
|
230
236
|
See `examples/scenes/perspective-cards.ts`.
|
|
231
237
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reframe-video",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.17",
|
|
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",
|