@pixerate/flair 0.25.0 → 0.26.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/CHANGELOG.md +15 -0
- package/README.md +1 -0
- package/dist/catalog/index.js +1 -1
- package/dist/{chunk-WAMA45G3.js → chunk-5USSDEQL.js} +77 -21
- package/dist/chunk-5USSDEQL.js.map +1 -0
- package/dist/{chunk-GTIAOYOK.js → chunk-74ZLCZOG.js} +13 -10
- package/dist/chunk-74ZLCZOG.js.map +1 -0
- package/dist/{chunk-O44ZJTGG.js → chunk-CK6EV37I.js} +15 -11
- package/dist/chunk-CK6EV37I.js.map +1 -0
- package/dist/{chunk-OKYMKSXK.js → chunk-G2B5L234.js} +3 -3
- package/dist/{chunk-PYDN5G2P.js → chunk-HVXWNGTW.js} +79 -2
- package/dist/{chunk-PYDN5G2P.js.map → chunk-HVXWNGTW.js.map} +1 -1
- package/dist/{chunk-S2RGHHKZ.js → chunk-UCZ43ARE.js} +2 -2
- package/dist/cli.js +9 -9
- package/dist/editor/index.js +1 -1
- package/dist/{effect-26CPF5HS.js → effect-GGH3OSOT.js} +4 -4
- package/dist/embed/index.js +2 -2
- package/dist/{export-DH2HMACO.js → export-QVW474KM.js} +3 -3
- package/dist/index.d.ts +42 -2
- package/dist/index.js +18 -4
- package/dist/index.js.map +1 -1
- package/dist/node.js +3 -3
- package/dist/react/index.js +2 -2
- package/dist/svelte/index.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-GTIAOYOK.js.map +0 -1
- package/dist/chunk-O44ZJTGG.js.map +0 -1
- package/dist/chunk-WAMA45G3.js.map +0 -1
- /package/dist/{chunk-OKYMKSXK.js.map → chunk-G2B5L234.js.map} +0 -0
- /package/dist/{chunk-S2RGHHKZ.js.map → chunk-UCZ43ARE.js.map} +0 -0
- /package/dist/{effect-26CPF5HS.js.map → effect-GGH3OSOT.js.map} +0 -0
- /package/dist/{export-DH2HMACO.js.map → export-QVW474KM.js.map} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# @pixerate/flair
|
|
2
2
|
|
|
3
|
+
## 0.26.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 9c2c741: Scene shots can choose how the camera gets into them with `camera.transition`:
|
|
8
|
+
|
|
9
|
+
- `"move"` (the default) eases across as before.
|
|
10
|
+
- `"whip"` whip-pans off the last framing and onto the new one in a streak of motion blur.
|
|
11
|
+
- `"zoom"` punches in and cuts to the new framing settling out.
|
|
12
|
+
- `"cut"` jumps straight there and takes no time.
|
|
13
|
+
|
|
14
|
+
The player, the standalone HTML player and the Lottie, Apple Motion, FCP7 and EDL exporters all play them. `transitionView` gives the camera part-way through any of them.
|
|
15
|
+
|
|
16
|
+
New catalog shader **Whip pan** (`transition-whip-pan`): a two-image whip pan or zoom punch. The motion blur peaks at the cut and there's a slight scale punch. It has presets for whip left, whip up, zoom punch and hard whip.
|
|
17
|
+
|
|
3
18
|
## 0.25.0
|
|
4
19
|
|
|
5
20
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -434,6 +434,7 @@ A shot's `camera` frames a box (`{ x, y, width, height }`) or elements (`{ frame
|
|
|
434
434
|
- `tilt` leans the board back (top away, like a desk seen from above), `turn` swings it round its vertical axis (left side nearer when positive), `roll` rolls it clockwise. Tilt and turn go up to ±75°.
|
|
435
435
|
- `perspective` is the lens distance as a multiple of the stage width (1.2 by default). Lower values look more dramatic.
|
|
436
436
|
- `motionBlur` (0 to 3, with 1 as natural) blurs the move in proportion to its speed, so the blur peaks mid-move and clears when the camera settles. A pan streaks along its direction (`View.blurAngle`), while a zoom or turn blurs in every direction.
|
|
437
|
+
- `transition` sets how the camera gets into the shot. `"move"` (the default) eases across. `"whip"` whip-pans off the last framing toward this one in a streak of blur and whips in onto it, cutting at the blurriest moment. `"zoom"` punches into the last framing and cuts to this one settling out, blurring every way. `"cut"` jumps straight there and takes no time. A whip or zoom blurs at `motionBlur` 2 unless the shot sets its own, and a zoom punches in to (1 + `motionBlur`)×: `{ "camera": { "frame": ["pricing"], "transition": "whip" } }`. `transitionView` gives the camera part-way through any of them.
|
|
437
438
|
- Angles carry over to later shots until a shot changes them. `{ "camera": { "tilt": 0, "turn": 0 } }` flattens the board in place.
|
|
438
439
|
|
|
439
440
|
`playScene` hands boards views with these fields (`View.tilt`, `View.blur`, `View.blurAngle`, …). For renderers:
|
package/dist/catalog/index.js
CHANGED
|
@@ -311,6 +311,45 @@ function moveBlur(from, to, stage, strength) {
|
|
|
311
311
|
const blurAngle = motionBlurAngle(from, to, stage);
|
|
312
312
|
return blurAngle === void 0 ? { blur } : { blur, blurAngle };
|
|
313
313
|
}
|
|
314
|
+
var CAMERA_TRANSITIONS = ["move", "whip", "zoom", "cut"];
|
|
315
|
+
var TRANSITION_BLUR = 2;
|
|
316
|
+
function transitionStrength(kind, motionBlur2) {
|
|
317
|
+
return motionBlur2 ?? (kind === "whip" || kind === "zoom" ? TRANSITION_BLUR : 0);
|
|
318
|
+
}
|
|
319
|
+
var CAMERA_STEPS = 15;
|
|
320
|
+
var easeInCubic = (t) => t * t * t;
|
|
321
|
+
var easeOutCubic = (t) => 1 - Math.pow(1 - t, 3);
|
|
322
|
+
function transitionHalf(kind, from, to, out, t, stage, strength) {
|
|
323
|
+
const at = out ? from : to;
|
|
324
|
+
const off = out ? easeInCubic(t) : 1 - easeOutCubic(t);
|
|
325
|
+
const [cx, cy] = centreOf(at, stage);
|
|
326
|
+
const angle = cameraAngle(at);
|
|
327
|
+
if (kind === "zoom") {
|
|
328
|
+
return { ...viewAt(cx, cy, at.zoom * Math.pow(1 + strength, off), stage), ...angle };
|
|
329
|
+
}
|
|
330
|
+
const [x0, y0] = centreOf(from, stage);
|
|
331
|
+
const [x1, y1] = centreOf(to, stage);
|
|
332
|
+
const dist = Math.hypot(x1 - x0, y1 - y0);
|
|
333
|
+
const [dx, dy] = dist > 1e-6 ? [(x1 - x0) / dist, (y1 - y0) / dist] : [1, 0];
|
|
334
|
+
const reach = stage.width / at.zoom * off * (out ? 1 : -1);
|
|
335
|
+
return { ...viewAt(cx + dx * reach, cy + dy * reach, at.zoom, stage), ...angle };
|
|
336
|
+
}
|
|
337
|
+
function transitionView(kind, from, to, u, stage, strength) {
|
|
338
|
+
if (kind === "cut" || u >= 1) return to;
|
|
339
|
+
const step = 1 / CAMERA_STEPS;
|
|
340
|
+
if (kind === "move") {
|
|
341
|
+
const view2 = between(from, to, easeInOutCubic(Math.max(0, u)), stage);
|
|
342
|
+
if (!(strength > 0)) return view2;
|
|
343
|
+
const before2 = between(from, to, easeInOutCubic(Math.max(0, u - step)), stage);
|
|
344
|
+
return { ...view2, ...moveBlur(before2, view2, stage, strength) };
|
|
345
|
+
}
|
|
346
|
+
const out = u < 0.5;
|
|
347
|
+
const t = out ? Math.max(0, u) * 2 : (u - 0.5) * 2;
|
|
348
|
+
const view = transitionHalf(kind, from, to, out, t, stage, strength);
|
|
349
|
+
if (!(strength > 0)) return view;
|
|
350
|
+
const before = transitionHalf(kind, from, to, out, out ? Math.max(0, t - 2 * step) : t - 2 * step, stage, strength);
|
|
351
|
+
return { ...view, ...moveBlur(before, view, stage, strength * 0.25) };
|
|
352
|
+
}
|
|
314
353
|
function visibleRect(view, stage) {
|
|
315
354
|
return {
|
|
316
355
|
x: -view.scrollX,
|
|
@@ -753,17 +792,17 @@ async function playScene(scene, board, clock, options = {}) {
|
|
|
753
792
|
if (idx === 0) {
|
|
754
793
|
board.setView(targetView);
|
|
755
794
|
} else {
|
|
756
|
-
const
|
|
757
|
-
const strength = shot.camera?.motionBlur
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
const
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
795
|
+
const kind = shot.camera?.transition ?? "move";
|
|
796
|
+
const strength = transitionStrength(kind, shot.camera?.motionBlur);
|
|
797
|
+
if (kind === "cut") {
|
|
798
|
+
board.setView(targetView);
|
|
799
|
+
} else {
|
|
800
|
+
const fromView = board.getView();
|
|
801
|
+
const stepMs = CAMERA_MS / CAMERA_STEPS;
|
|
802
|
+
for (let s = 1; s <= CAMERA_STEPS; s++) {
|
|
803
|
+
board.setView(transitionView(kind, fromView, targetView, s / CAMERA_STEPS, stage, strength));
|
|
804
|
+
await clock.sleep(stepMs);
|
|
805
|
+
}
|
|
767
806
|
}
|
|
768
807
|
}
|
|
769
808
|
}
|
|
@@ -876,6 +915,9 @@ async function simulateScene(scene, options = {}) {
|
|
|
876
915
|
}
|
|
877
916
|
|
|
878
917
|
// src/scene/cameratrack.ts
|
|
918
|
+
function shotMoveMs(shot, index) {
|
|
919
|
+
return index > 0 && shot.camera && shot.camera.transition !== "cut" ? CAMERA_MS : 0;
|
|
920
|
+
}
|
|
879
921
|
function sceneStage(scene) {
|
|
880
922
|
return {
|
|
881
923
|
width: scene.stage?.width ?? DEFAULT_STAGE.width,
|
|
@@ -890,7 +932,8 @@ function cameraTrack(scene) {
|
|
|
890
932
|
let startMs = 0;
|
|
891
933
|
return scene.shots.map((shot, index) => {
|
|
892
934
|
const target = shotView(shot, board.elements(), stage, view);
|
|
893
|
-
const
|
|
935
|
+
const transition = shot.camera?.transition ?? "move";
|
|
936
|
+
const moveMs = shotMoveMs(shot, index);
|
|
894
937
|
const from = index === 0 && target ? target : view;
|
|
895
938
|
const to = target ?? view;
|
|
896
939
|
const holdMs = shot.hold ?? 800;
|
|
@@ -903,7 +946,8 @@ function cameraTrack(scene) {
|
|
|
903
946
|
holdMs,
|
|
904
947
|
from,
|
|
905
948
|
to,
|
|
906
|
-
|
|
949
|
+
transition,
|
|
950
|
+
motionBlur: transitionStrength(transition, shot.camera?.motionBlur),
|
|
907
951
|
elements: board.elements().map((e) => ({ ...e }))
|
|
908
952
|
};
|
|
909
953
|
view = to;
|
|
@@ -911,15 +955,12 @@ function cameraTrack(scene) {
|
|
|
911
955
|
return entry;
|
|
912
956
|
});
|
|
913
957
|
}
|
|
914
|
-
var CAMERA_STEP_MS = CAMERA_MS /
|
|
958
|
+
var CAMERA_STEP_MS = CAMERA_MS / CAMERA_STEPS;
|
|
915
959
|
function cameraAt(entry, ms, stage) {
|
|
916
960
|
if (!(entry.moveMs > 0) || ms >= entry.moveMs) return entry.to;
|
|
917
|
-
|
|
918
|
-
const view = between(entry.from, entry.to, easeInOutCubic(u), stage);
|
|
919
|
-
if (!(entry.motionBlur > 0)) return view;
|
|
920
|
-
const before = between(entry.from, entry.to, easeInOutCubic(Math.max(0, u - CAMERA_STEP_MS / entry.moveMs)), stage);
|
|
921
|
-
return { ...view, ...moveBlur(before, view, stage, entry.motionBlur) };
|
|
961
|
+
return transitionView(entry.transition, entry.from, entry.to, Math.max(0, ms) / entry.moveMs, stage, entry.motionBlur);
|
|
922
962
|
}
|
|
963
|
+
var cutsHalfway = (entry) => entry.moveMs > 0 && (entry.transition === "whip" || entry.transition === "zoom");
|
|
923
964
|
function cameraSamples(scene, frameRate, every = 2) {
|
|
924
965
|
const stage = sceneStage(scene);
|
|
925
966
|
const samples = [];
|
|
@@ -930,8 +971,16 @@ function cameraSamples(scene, frameRate, every = 2) {
|
|
|
930
971
|
for (const entry of cameraTrack(scene)) {
|
|
931
972
|
const start = Math.round(entry.startMs / 1e3 * frameRate);
|
|
932
973
|
const moveFrames = Math.round(entry.moveMs / 1e3 * frameRate);
|
|
974
|
+
const at = (f) => add({ frame: start + f, view: cameraAt(entry, f / frameRate * 1e3, stage), shot: entry.index });
|
|
975
|
+
const cut = cutsHalfway(entry) ? Math.ceil(entry.moveMs / 2e3 * frameRate) : -1;
|
|
933
976
|
for (let f = 0; f < moveFrames; f += every) {
|
|
934
|
-
|
|
977
|
+
if (cut > 0 && f < cut && f + every >= cut) {
|
|
978
|
+
at(f);
|
|
979
|
+
if (cut - 1 > f) at(cut - 1);
|
|
980
|
+
at(cut);
|
|
981
|
+
continue;
|
|
982
|
+
}
|
|
983
|
+
at(f);
|
|
935
984
|
}
|
|
936
985
|
add({ frame: start + moveFrames, view: entry.to, shot: entry.index });
|
|
937
986
|
}
|
|
@@ -971,6 +1020,11 @@ export {
|
|
|
971
1020
|
motionBlur,
|
|
972
1021
|
motionBlurAngle,
|
|
973
1022
|
moveBlur,
|
|
1023
|
+
CAMERA_TRANSITIONS,
|
|
1024
|
+
TRANSITION_BLUR,
|
|
1025
|
+
transitionStrength,
|
|
1026
|
+
CAMERA_STEPS,
|
|
1027
|
+
transitionView,
|
|
974
1028
|
visibleRect,
|
|
975
1029
|
easeInOutCubic,
|
|
976
1030
|
NO_CHECK,
|
|
@@ -989,10 +1043,12 @@ export {
|
|
|
989
1043
|
shotView,
|
|
990
1044
|
playScene,
|
|
991
1045
|
simulateScene,
|
|
1046
|
+
shotMoveMs,
|
|
992
1047
|
sceneStage,
|
|
993
1048
|
cameraTrack,
|
|
994
1049
|
CAMERA_STEP_MS,
|
|
995
1050
|
cameraAt,
|
|
1051
|
+
cutsHalfway,
|
|
996
1052
|
cameraSamples
|
|
997
1053
|
};
|
|
998
|
-
//# sourceMappingURL=chunk-
|
|
1054
|
+
//# sourceMappingURL=chunk-5USSDEQL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/time/noise.ts","../src/scene/types.ts","../src/scene/geometry.ts","../src/scene/camera.ts","../src/scene/check.ts","../src/scene/effects.ts","../src/scene/behavior.ts","../src/scene/report.ts","../src/scene/player.ts","../src/scene/cameratrack.ts"],"sourcesContent":["/**\n * Deterministic pseudo-random number generator, hash mixing, and C2-continuous smooth value noise.\n * Based on 64-bit SplitMix64 mixing and quintic Hermite interpolation.\n *\n * Produces deterministic, platform-independent outputs for wiggle, organic turbulence, and camera shake.\n */\n\n// BigInt constants for SplitMix64\nconst GOLDEN_GAMMA = 0x9e3779b97f4a7c15n;\nconst MULT1 = 0xbf58476d1ce4e5b9n;\nconst MULT2 = 0x94d049bb133111ebn;\nconst MASK64 = 0xffffffffffffffffn;\nconst BASE_MIX = 0x2545f4914f6cdd1dn;\nconst POW2_53 = 9007199254740992; // 2^53\n\n/**\n * SplitMix64 mixing finalizer: maps any 64-bit integer to a high-quality pseudorandom 64-bit integer.\n */\nexport function hash64(x: bigint): bigint {\n let v = (x + GOLDEN_GAMMA) & MASK64;\n v = ((v ^ (v >> 30n)) * MULT1) & MASK64;\n v = ((v ^ (v >> 27n)) * MULT2) & MASK64;\n return (v ^ (v >> 31n)) & MASK64;\n}\n\n/**\n * Combines multiple seed numbers/bigints into a single mixed 64-bit seed.\n */\nexport function mixSeeds(parts: (bigint | number)[]): bigint {\n let h = BASE_MIX;\n for (const p of parts) {\n const val = typeof p === 'bigint' ? p : BigInt(Math.floor(p));\n h = hash64(h ^ hash64(val & MASK64));\n }\n return h;\n}\n\n/**\n * Maps a 64-bit seed to a uniform float in [0, 1).\n */\nexport function unitFloat(seed: bigint | number): number {\n const s = typeof seed === 'bigint' ? seed : BigInt(Math.floor(seed));\n const hashed = hash64(s);\n const shifted = Number(hashed >> 11n);\n return shifted / POW2_53;\n}\n\n/**\n * Quintic Hermite interpolant fade function: 6t^5 - 15t^4 + 10t^3.\n * First and second derivatives are zero at t = 0 and t = 1, ensuring C2 continuity.\n */\nexport function quinticFade(t: number): number {\n return t * t * t * (t * (t * 6.0 - 15.0) + 10.0);\n}\n\n/**\n * Returns a deterministic pseudo-random lattice value in [-1, 1].\n */\nfunction lattice(seed: bigint, i: bigint): number {\n const mixed = seed ^ hash64(i & MASK64);\n return unitFloat(mixed) * 2.0 - 1.0;\n}\n\n/**\n * 1D C2-continuous smooth value noise in [-1, 1].\n * Guaranteed bounded by lattice values, smooth without sudden velocity spikes.\n */\nexport function noise1d(seed: bigint | number, x: number): number {\n if (!Number.isFinite(x)) return 0.0;\n const s = typeof seed === 'bigint' ? seed : BigInt(Math.floor(seed));\n const floorX = Math.floor(x);\n const u = quinticFade(x - floorX);\n const i = BigInt(Math.floor(floorX));\n const a = lattice(s, i);\n const b = lattice(s, i + 1n);\n return a + (b - a) * u;\n}\n\n/**\n * 3D C2-continuous smooth value noise in [-1, 1].\n */\nexport function noise3d(x: number, y: number, z: number, seed: bigint | number = 0): number {\n if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(z)) {\n return 0.0;\n }\n const s = typeof seed === 'bigint' ? seed : BigInt(Math.floor(seed));\n const fx = Math.floor(x);\n const fy = Math.floor(y);\n const fz = Math.floor(z);\n\n const u = quinticFade(x - fx);\n const v = quinticFade(y - fy);\n const w = quinticFade(z - fz);\n\n const ix = BigInt(fx);\n const iy = BigInt(fy);\n const iz = BigInt(fz);\n\n const g = (dx: bigint, dy: bigint, dz: bigint): number => {\n const combined = mixSeeds([s, ix + dx, iy + dy, iz + dz]);\n return unitFloat(combined) * 2.0 - 1.0;\n };\n\n const lerp = (a: number, b: number, t: number) => a + (b - a) * t;\n\n const x00 = lerp(g(0n, 0n, 0n), g(1n, 0n, 0n), u);\n const x10 = lerp(g(0n, 1n, 0n), g(1n, 1n, 0n), u);\n const x01 = lerp(g(0n, 0n, 1n), g(1n, 0n, 1n), u);\n const x11 = lerp(g(0n, 1n, 1n), g(1n, 1n, 1n), u);\n\n const y0 = lerp(x00, x10, v);\n const y1 = lerp(x01, x11, v);\n\n return lerp(y0, y1, w);\n}\n\n/**\n * Evaluates multi-octave fractal noise wiggle:\n * sum_{k=0}^{octaves-1} amp * (ampMult^k) * noise1d(freq * (2^k) * t)\n *\n * Matches the mathematical formulation of After Effects / EffectCraft wiggle().\n *\n * @param freq Oscillation frequency in Hz (cycles per second).\n * @param amp Maximum deviation amplitude.\n * @param octaves Number of noise detail octaves (default: 3).\n * @param ampMult Amplitude multiplier per octave (default: 0.5).\n * @param t Elapsed time in seconds.\n * @param seed Random seed (default: 42).\n * @param dimension Dimension index (0, 1, 2) to ensure decorrelated multi-axis wiggle.\n */\nexport function fractalWiggle(\n freq: number,\n amp: number,\n octaves = 3,\n ampMult = 0.5,\n t = 0,\n seed = 42,\n dimension = 0,\n): number {\n const oct = Math.max(1, Math.min(16, Math.floor(octaves)));\n let currentAmp = amp;\n let currentFreq = freq;\n let sum = 0.0;\n const baseSeed = BigInt(seed);\n\n for (let k = 0; k < oct; k++) {\n const s = mixSeeds([baseSeed, BigInt(dimension), BigInt(k)]);\n const phaseOffset = unitFloat(s) * 1000.0;\n sum += currentAmp * noise1d(s, currentFreq * t + phaseOffset);\n currentAmp *= ampMult;\n currentFreq *= 2.0;\n }\n\n return sum;\n}\n","import type { Rect, SceneElement } from './geometry.js';\nimport type { CameraAngle, CameraTransition, Stage } from './camera.js';\nimport type { Effect } from './effects.js';\nimport type { Behavior } from './behavior.js';\nimport type { SceneRig } from './rig.js';\n\n/** Shader or external visual stream in the scene. */\nexport interface SceneMedia {\n type: 'shader' | string;\n source?: string;\n values?: Record<string, unknown>;\n [field: string]: unknown;\n}\n\nexport interface ElementCommand {\n type: 'element';\n element: SceneElement;\n}\n\nexport interface UpdateCommand {\n type: 'update';\n id: string;\n props: Partial<SceneElement>;\n}\n\nexport interface DeleteCommand {\n type: 'delete';\n id: string;\n}\n\nexport interface ClearCommand {\n type: 'clear';\n}\n\nexport interface CustomCommand {\n type: string;\n [key: string]: unknown;\n}\n\n/** A command to add, move, or style elements in a shot. */\nexport type SceneCommand =\n | ElementCommand\n | UpdateCommand\n | DeleteCommand\n | ClearCommand\n | CustomCommand;\n\nexport function isElementCommand(cmd: SceneCommand): cmd is ElementCommand {\n return cmd.type === 'element' && 'element' in cmd && !!cmd.element;\n}\n\nexport function isUpdateCommand(cmd: SceneCommand): cmd is UpdateCommand {\n return cmd.type === 'update' && 'id' in cmd && typeof cmd.id === 'string';\n}\n\nexport function isDeleteCommand(cmd: SceneCommand): cmd is DeleteCommand {\n return cmd.type === 'delete' && 'id' in cmd && typeof cmd.id === 'string';\n}\n\nexport interface DynamicFraming {\n /** Element IDs to frame. */\n frame: string[];\n /** Proportional padding (default 0.15 for 15%). */\n padding?: number;\n /** Minimum width in scene units. */\n minWidth?: number;\n /** Minimum height in scene units. */\n minHeight?: number;\n}\n\n/**\n * A shot's camera beyond its framing: its 3D angle (see `CameraAngle`) and\n * how much it blurs while moving into the shot. A part left out keeps the\n * value the camera already has, so set `tilt: 0` to come back flat.\n */\nexport interface CameraLens extends CameraAngle {\n /**\n * Motion blur while the camera moves into this shot: 0 (the default) for\n * none, 1 for natural, up to 3 for a whip. The blur follows the move's\n * speed, so it peaks mid-move and is gone when the camera settles.\n */\n motionBlur?: number;\n /**\n * How the camera gets into this shot: `move` (the default) eases across,\n * `whip` whip-pans off the last framing and onto this one, `zoom` punches\n * in and cuts to this one settling out, and `cut` jumps straight here\n * (see `CameraTransition`). A whip or zoom blurs at `motionBlur` 2 unless\n * the shot sets one; a zoom punches in to (1 + motionBlur) ×.\n */\n transition?: CameraTransition;\n}\n\n/**\n * Where the camera goes for a shot: a framing box, a dynamic framing, or\n * neither (only a new angle or blur, keeping the framing), with any\n * {@link CameraLens} fields alongside: `{ \"frame\": [\"chart\"], \"tilt\": 30 }`.\n */\nexport type ShotCamera = (Rect | DynamicFraming | Record<never, never>) & CameraLens;\n\n/** One beat of a scene: camera framing, title, narration/caption, commands, and procedural effects. */\nexport interface Shot {\n /** Narration / caption spoken or displayed during this beat. */\n caption?: string;\n /** High-level title or callout displayed on screen. */\n title?: string;\n /** Camera framing box in scene units or dynamic auto-framing target, with an optional 3D angle and motion blur. */\n camera?: ShotCamera;\n /** Pause/dwell duration after this shot's actions run (in milliseconds). Default: 800ms. */\n hold?: number;\n /** Drawing or state change commands played during this shot. */\n run?: SceneCommand[];\n /** Procedural visual effects started in this shot. */\n effects?: Effect[];\n /** IDs of active effects to stop/fade at the start of this shot. */\n stopEffects?: string[];\n /** Continuous procedural parameter and physics behaviors played during this shot. */\n behaviors?: Behavior[];\n}\n\n/** Complete animated scene specification. */\nexport interface Scene {\n name: string;\n title?: string;\n description?: string;\n stage?: Partial<Stage>;\n /** Embedded media (e.g. bespoke WebGL fragment shaders). */\n media?: Record<string, SceneMedia>;\n /** Target duration [minSeconds, maxSeconds]. */\n target?: [number, number];\n /** Interactive macro rigs binding high-level controls to uniforms or element properties. */\n rig?: SceneRig;\n shots: Shot[];\n}\n","export type Point = [number, number];\n\nexport interface Rect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\n/** An element placed or animated in a scene. */\nexport interface SceneElement {\n id: string;\n type: string;\n x: number;\n y: number;\n width: number;\n height: number;\n points?: readonly (readonly [number, number])[];\n fontSize?: number;\n text?: string;\n containerId?: string | null;\n isDeleted?: boolean;\n opacity?: number;\n}\n\n/** Bounds in scene units, ignoring rotation. */\nexport function boundsOf(e: SceneElement): Rect {\n if (e.points?.length) {\n const xs = e.points.map((p) => e.x + p[0]);\n const ys = e.points.map((p) => e.y + p[1]);\n const x = Math.min(...xs);\n const y = Math.min(...ys);\n return {\n x,\n y,\n width: Math.max(1, Math.max(...xs) - x),\n height: Math.max(1, Math.max(...ys) - y),\n };\n }\n return { x: e.x, y: e.y, width: Math.max(1, e.width), height: Math.max(1, e.height) };\n}\n\n/** Area of the overlap of two rectangles (0 when they do not meet). */\nexport function overlapArea(a: Rect, b: Rect): number {\n const w = Math.min(a.x + a.width, b.x + b.width) - Math.max(a.x, b.x);\n const h = Math.min(a.y + a.height, b.y + b.height) - Math.max(a.y, b.y);\n return w > 0 && h > 0 ? w * h : 0;\n}\n\nexport const areaOf = (r: Rect): number => r.width * r.height;\n","import { type Rect, type SceneElement, boundsOf } from './geometry.js';\n\nexport interface Stage {\n width: number;\n height: number;\n caption?: number;\n}\n\nexport const DEFAULT_STAGE: Stage = {\n width: 1920,\n height: 1080,\n caption: 0,\n};\n\nexport const CAMERA_MS = 600;\n\n/**\n * How the camera is turned in 3D, in degrees: the board is drawn flat (see\n * {@link View}), then turned round the middle of the stage and seen in\n * perspective. Each is optional and 0 (flat) by default.\n */\nexport interface CameraAngle {\n /** Tips the board back round its horizontal axis (CSS `rotateX`): positive leans the top away, like a desk seen from above. −75 to 75. */\n tilt?: number;\n /** Swings the board round its vertical axis (CSS `rotateY`): positive brings the left side nearer. −75 to 75. */\n turn?: number;\n /** Rolls the board round the line of sight (CSS `rotateZ`), clockwise. −180 to 180. */\n roll?: number;\n /** How far the lens sits from the board, as a multiple of the stage width (default {@link DEFAULT_PERSPECTIVE}); smaller is more dramatic. */\n perspective?: number;\n}\n\n/** The camera's default distance from the board, as a multiple of the stage width. */\nexport const DEFAULT_PERSPECTIVE = 1.2;\n\n/** The largest tilt or turn, in degrees: past this the board is seen edge-on. */\nexport const MAX_TILT = 75;\n\n/**\n * Camera view into the scene: drawn at (point + scroll) × zoom, then turned\n * by its {@link CameraAngle} (see {@link cameraTransform}) and blurred by\n * `blur` while the camera moves.\n */\nexport interface View extends CameraAngle {\n zoom: number;\n scrollX: number;\n scrollY: number;\n /** Motion blur in stage pixels, set while the camera moves (see `ShotCamera.motionBlur`); 0 or absent when still. */\n blur?: number;\n /**\n * The direction the picture streaks in, in degrees on screen (0 is\n * horizontal, 90 vertical), when the move is mostly a pan; absent when it\n * blurs every way (a zoom or a turn).\n */\n blurAngle?: number;\n}\n\nexport const boardHeight = (stage: Stage): number => stage.height - (stage.caption ?? 0);\n\n/** View with `(cx, cy)` in the middle of the board at `zoom`. */\nexport function viewAt(cx: number, cy: number, zoom: number, stage: Stage): View {\n return {\n zoom,\n scrollX: stage.width / (2 * zoom) - cx,\n scrollY: boardHeight(stage) / (2 * zoom) - cy,\n };\n}\n\n/** Scene point in the middle of the board. */\nexport function centreOf(view: View, stage: Stage): [number, number] {\n return [\n stage.width / (2 * view.zoom) - view.scrollX,\n boardHeight(stage) / (2 * view.zoom) - view.scrollY,\n ];\n}\n\n/** The view that fits `rect` on the board, centred and with its aspect kept. */\nexport function frame(rect: Rect, stage: Stage): View {\n if (!(rect.width > 0 && rect.height > 0)) {\n throw new Error('A camera rectangle needs a positive width and height');\n }\n const zoom = Math.min(stage.width / rect.width, boardHeight(stage) / rect.height);\n return viewAt(rect.x + rect.width / 2, rect.y + rect.height / 2, zoom, stage);\n}\n\nexport interface FramingOptions {\n /** Proportional padding added to all sides (e.g., 0.15 for 15%). Default: 0.15 */\n padding?: number;\n /** Minimum width in scene units. */\n minWidth?: number;\n /** Minimum height in scene units. */\n minHeight?: number;\n}\n\n/**\n * Calculates a camera framing rectangle that encloses target elements\n * while preserving the stage aspect ratio and applying optional margin padding.\n */\nexport function calculateFramingRect(\n elements: readonly SceneElement[],\n stage: Stage,\n options: FramingOptions = {},\n): Rect {\n const activeElements = elements.filter((e) => !e.isDeleted);\n const targetH = boardHeight(stage);\n const stageAspect = stage.width / Math.max(1, targetH);\n\n if (activeElements.length === 0) {\n return {\n x: 0,\n y: 0,\n width: stage.width,\n height: targetH,\n };\n }\n\n let minX = Infinity;\n let minY = Infinity;\n let maxX = -Infinity;\n let maxY = -Infinity;\n\n for (const el of activeElements) {\n const b = boundsOf(el);\n if (b.x < minX) minX = b.x;\n if (b.y < minY) minY = b.y;\n if (b.x + b.width > maxX) maxX = b.x + b.width;\n if (b.y + b.height > maxY) maxY = b.y + b.height;\n }\n\n let width = maxX - minX;\n let height = maxY - minY;\n\n // Apply padding (default 15%)\n const padding = options.padding ?? 0.15;\n if (padding > 0) {\n const padX = width * padding;\n const padY = height * padding;\n minX -= padX;\n minY -= padY;\n width += padX * 2;\n height += padY * 2;\n }\n\n // Apply min dimensions\n if (options.minWidth && width < options.minWidth) {\n const diff = options.minWidth - width;\n minX -= diff / 2;\n width = options.minWidth;\n }\n if (options.minHeight && height < options.minHeight) {\n const diff = options.minHeight - height;\n minY -= diff / 2;\n height = options.minHeight;\n }\n\n // Preserve stage aspect ratio centered around midpoint\n const currentAspect = width / Math.max(1, height);\n if (currentAspect < stageAspect) {\n // Taller than stage: expand width\n const targetWidth = height * stageAspect;\n const diff = targetWidth - width;\n minX -= diff / 2;\n width = targetWidth;\n } else if (currentAspect > stageAspect) {\n // Wider than stage: expand height\n const targetHeight = width / stageAspect;\n const diff = targetHeight - height;\n minY -= diff / 2;\n height = targetHeight;\n }\n\n return {\n x: minX,\n y: minY,\n width,\n height,\n };\n}\n\nconst ANGLES = ['tilt', 'turn', 'roll'] as const;\n\n/** Whether a view is turned in 3D at all. */\nexport function isAngled(view: CameraAngle): boolean {\n return ANGLES.some((k) => (view[k] ?? 0) !== 0);\n}\n\n/** The view's angle with each part clamped to its range (see {@link CameraAngle}); flat parts are left out. */\nexport function cameraAngle(view: CameraAngle): CameraAngle {\n const out: CameraAngle = {};\n const clamp = (v: number, max: number) => Math.max(-max, Math.min(max, v));\n if (view.tilt) out.tilt = clamp(view.tilt, MAX_TILT);\n if (view.turn) out.turn = clamp(view.turn, MAX_TILT);\n if (view.roll) out.roll = clamp(view.roll, 180);\n if (view.perspective !== undefined && view.perspective !== DEFAULT_PERSPECTIVE) out.perspective = Math.max(0.3, view.perspective);\n return out;\n}\n\n/**\n * The view part-way (`t` from 0 to 1) from one view to another.\n * Centre moves linearly and zoom scales exponentially for smooth motion;\n * angles and perspective move linearly.\n */\nexport function between(from: View, to: View, t: number, stage: Stage): View {\n const [x0, y0] = centreOf(from, stage);\n const [x1, y1] = centreOf(to, stage);\n const zoom = from.zoom * Math.pow(to.zoom / from.zoom, t);\n const view: View = viewAt(x0 + (x1 - x0) * t, y0 + (y1 - y0) * t, zoom, stage);\n if (!isAngled(from) && !isAngled(to) && from.perspective === to.perspective) return view;\n const lerp = (a = 0, b = 0) => a + (b - a) * t;\n return {\n ...view,\n ...cameraAngle({\n tilt: lerp(from.tilt, to.tilt),\n turn: lerp(from.turn, to.turn),\n roll: lerp(from.roll, to.roll),\n perspective: lerp(from.perspective ?? DEFAULT_PERSPECTIVE, to.perspective ?? DEFAULT_PERSPECTIVE),\n }),\n };\n}\n\n/**\n * The CSS `transform` that turns a board layer (a box the stage's size,\n * with `transform-origin` in its middle) to the view's angle, or \"none\" when\n * the view is flat. Zoom and scroll are not in it: draw them inside the\n * layer, as for a flat view. `width` is the layer's width on screen in CSS\n * pixels (default: the stage width).\n */\nexport function cameraTransform(view: View, stage: Stage, width = stage.width): string {\n if (!isAngled(view)) return 'none';\n const a = cameraAngle(view);\n const distance = (a.perspective ?? DEFAULT_PERSPECTIVE) * width;\n return `perspective(${round(distance)}px) rotateX(${round(a.tilt ?? 0)}deg) rotateY(${round(a.turn ?? 0)}deg) rotateZ(${round(a.roll ?? 0)}deg)`;\n}\n\n/**\n * The view's motion blur as standard deviations along the board layer's own\n * x and y axes, in CSS pixels at a layer `width` wide: for an SVG\n * `<feGaussianBlur stdDeviation=\"x y\">`, so a pan streaks along its\n * direction (CSS `blur()` can only blur every way). The layer is turned\n * after it is blurred, so the direction is taken back through its roll.\n * [0, 0] when still.\n */\nexport function cameraBlur(view: View, stage: Stage, width = stage.width): [number, number] {\n const blur = (view.blur ?? 0) * (width / stage.width);\n if (!(blur >= 0.05)) return [0, 0];\n if (view.blurAngle === undefined) return [round(blur), round(blur)];\n const a = ((view.blurAngle - (cameraAngle(view).roll ?? 0)) * Math.PI) / 180;\n // A little blur across the streak keeps it from looking like a scanline.\n const across = blur * 0.15;\n return [round(Math.max(across, blur * Math.abs(Math.cos(a)))), round(Math.max(across, blur * Math.abs(Math.sin(a))))];\n}\n\n/** The CSS `filter` for the view's motion blur at a layer `width` CSS pixels wide (the same every way; see {@link cameraBlur} for a streak), or \"none\". */\nexport function cameraFilter(view: View, stage: Stage, width = stage.width): string {\n const blur = (view.blur ?? 0) * (width / stage.width);\n return blur >= 0.05 ? `blur(${round(blur)}px)` : 'none';\n}\n\nconst round = (n: number) => Math.round(n * 1000) / 1000;\n\n/**\n * Where a scene point lands on screen, in stage pixels from the top left of\n * the board: drawn by the view's zoom and scroll, then turned and seen in\n * perspective as {@link cameraTransform} does. Null when the point is\n * behind the camera.\n */\nexport function projectPoint(view: View, stage: Stage, [x, y]: readonly [number, number]): [number, number] | null {\n const cx = stage.width / 2;\n const cy = boardHeight(stage) / 2;\n let px = (x + view.scrollX) * view.zoom - cx;\n let py = (y + view.scrollY) * view.zoom - cy;\n if (!isAngled(view)) return [px + cx, py + cy];\n const a = cameraAngle(view);\n const rad = Math.PI / 180;\n // CSS applies rotateX(tilt) rotateY(turn) rotateZ(roll) right to left: roll first.\n const [sr, cr] = [Math.sin((a.roll ?? 0) * rad), Math.cos((a.roll ?? 0) * rad)];\n [px, py] = [px * cr - py * sr, px * sr + py * cr];\n const [st, ct] = [Math.sin((a.turn ?? 0) * rad), Math.cos((a.turn ?? 0) * rad)];\n let pz = -px * st;\n px = px * ct;\n const [sx, cx2] = [Math.sin((a.tilt ?? 0) * rad), Math.cos((a.tilt ?? 0) * rad)];\n [py, pz] = [py * cx2 - pz * sx, py * sx + pz * cx2];\n const distance = (a.perspective ?? DEFAULT_PERSPECTIVE) * stage.width;\n if (pz >= distance) return null;\n const k = distance / (distance - pz);\n return [cx + px * k, cy + py * k];\n}\n\n/** How far the picture moves on screen between two views: as a pan (x, y), and as zooming and turning. */\nfunction screenMotion(from: View, to: View, stage: Stage): { dx: number; dy: number; other: number } {\n const [x0, y0] = centreOf(from, stage);\n const [x1, y1] = centreOf(to, stage);\n const zoom = Math.sqrt(from.zoom * to.zoom);\n const scale = Math.abs(Math.log(to.zoom / from.zoom)) * stage.width * 0.5;\n const turn = ANGLES.reduce((sum, k) => sum + Math.abs((to[k] ?? 0) - (from[k] ?? 0)), 0) * (stage.width / 180);\n return { dx: (x1 - x0) * zoom, dy: (y1 - y0) * zoom, other: scale + turn };\n}\n\n/**\n * Motion blur for one step of a camera move, in stage pixels: how far the\n * picture moves on screen between two views (centre, zoom and angle), times\n * `strength` (`ShotCamera.motionBlur`), capped at 40.\n */\nexport function motionBlur(from: View, to: View, stage: Stage, strength: number): number {\n if (!(strength > 0)) return 0;\n const { dx, dy, other } = screenMotion(from, to, stage);\n return round(Math.min(40, (Math.hypot(dx, dy) + other) * 0.35 * strength));\n}\n\n/**\n * The direction a step of a camera move streaks in, in degrees on screen\n * (0 to 180, 0 horizontal), when most of it is a pan; undefined when it is\n * mostly zooming or turning, which blurs every way.\n */\nexport function motionBlurAngle(from: View, to: View, stage: Stage): number | undefined {\n const { dx, dy, other } = screenMotion(from, to, stage);\n const pan = Math.hypot(dx, dy);\n if (!(pan > 0) || pan < 0.6 * (pan + other)) return undefined;\n const a = (Math.atan2(dy, dx) * 180) / Math.PI;\n return round(((a % 180) + 180) % 180);\n}\n\n/** The blur fields of a step of a camera move with this strength: `{ blur, blurAngle }`, or nothing when still. */\nexport function moveBlur(from: View, to: View, stage: Stage, strength: number): Pick<View, 'blur' | 'blurAngle'> {\n const blur = motionBlur(from, to, stage, strength);\n if (!(blur > 0)) return {};\n const blurAngle = motionBlurAngle(from, to, stage);\n return blurAngle === undefined ? { blur } : { blur, blurAngle };\n}\n\n/**\n * How the camera gets into a shot (`ShotCamera.transition`):\n * - `move` (the default) eases from the last framing to the new one.\n * - `whip` whips off the last framing toward the new one in a streak of\n * motion blur and whips in onto it, cutting at the blurriest moment.\n * - `zoom` punches into the last framing, blurring every way, and settles\n * out of a punched-in new one, cutting halfway.\n * - `cut` jumps straight to the new framing, taking no time.\n */\nexport type CameraTransition = 'move' | 'whip' | 'zoom' | 'cut';\n\nexport const CAMERA_TRANSITIONS: readonly CameraTransition[] = ['move', 'whip', 'zoom', 'cut'];\n\n/** The motion blur a whip or zoom uses when the shot does not set `motionBlur`. */\nexport const TRANSITION_BLUR = 2;\n\n/** The blur strength a transition plays with: the shot's `motionBlur`, or {@link TRANSITION_BLUR} for a whip or zoom without one. */\nexport function transitionStrength(kind: CameraTransition, motionBlur: number | undefined): number {\n return motionBlur ?? (kind === 'whip' || kind === 'zoom' ? TRANSITION_BLUR : 0);\n}\n\n/** The steps a camera move is played in; its blur is measured over one. */\nexport const CAMERA_STEPS = 15;\n\nconst easeInCubic = (t: number): number => t * t * t;\nconst easeOutCubic = (t: number): number => 1 - Math.pow(1 - t, 3);\n\n/**\n * One half of a whip or zoom at `t` (0 to 1 through the half; the formula\n * carries on smoothly before 0, which the blur step relies on arriving):\n * leaving `from` (`out`) or arriving at `to`.\n */\nfunction transitionHalf(kind: 'whip' | 'zoom', from: View, to: View, out: boolean, t: number, stage: Stage, strength: number): View {\n const at = out ? from : to;\n // How far off its framing the camera is: 0 settled, 1 at the cut.\n const off = out ? easeInCubic(t) : 1 - easeOutCubic(t);\n const [cx, cy] = centreOf(at, stage);\n const angle = cameraAngle(at);\n if (kind === 'zoom') {\n return { ...viewAt(cx, cy, at.zoom * Math.pow(1 + strength, off), stage), ...angle };\n }\n const [x0, y0] = centreOf(from, stage);\n const [x1, y1] = centreOf(to, stage);\n const dist = Math.hypot(x1 - x0, y1 - y0);\n const [dx, dy] = dist > 1e-6 ? [(x1 - x0) / dist, (y1 - y0) / dist] : [1, 0];\n // A board's width at this framing's zoom: out ahead of `from`, behind `to`.\n const reach = (stage.width / at.zoom) * off * (out ? 1 : -1);\n return { ...viewAt(cx + dx * reach, cy + dy * reach, at.zoom, stage), ...angle };\n}\n\n/**\n * The camera `u` (0 to 1) of the way through a transition from one view to\n * another (see {@link CameraTransition}), with its motion blur measured over\n * the step before (1/{@link CAMERA_STEPS} of the move). `strength` is the\n * blur (`ShotCamera.motionBlur`; see {@link transitionStrength}) and, for a\n * zoom, how far it punches in: to (1 + strength) × at the cut. Settled on\n * `to`, with no blur, from `u` = 1 (and at once for a cut).\n */\nexport function transitionView(\n kind: CameraTransition,\n from: View,\n to: View,\n u: number,\n stage: Stage,\n strength: number,\n): View {\n if (kind === 'cut' || u >= 1) return to;\n const step = 1 / CAMERA_STEPS;\n if (kind === 'move') {\n const view = between(from, to, easeInOutCubic(Math.max(0, u)), stage);\n if (!(strength > 0)) return view;\n const before = between(from, to, easeInOutCubic(Math.max(0, u - step)), stage);\n return { ...view, ...moveBlur(before, view, stage, strength) };\n }\n // Each half takes half the move, so a step is twice as far through it; the\n // step before is taken on the same side of the cut, never across it.\n const out = u < 0.5;\n const t = out ? Math.max(0, u) * 2 : (u - 0.5) * 2;\n const view = transitionHalf(kind, from, to, out, t, stage, strength);\n if (!(strength > 0)) return view;\n // Leaving, the camera was still before it set off; arriving, the step before carries on from behind the cut.\n const before = transitionHalf(kind, from, to, out, out ? Math.max(0, t - 2 * step) : t - 2 * step, stage, strength);\n // A whip or zoom covers a board's width in a few steps, so it blurs at a quarter of a move's strength to build up to the cut rather than smear throughout.\n return { ...view, ...moveBlur(before, view, stage, strength * 0.25) };\n}\n\n/** The part of the scene the camera currently shows. */\nexport function visibleRect(view: View, stage: Stage): Rect {\n return {\n x: -view.scrollX,\n y: -view.scrollY,\n width: stage.width / view.zoom,\n height: boardHeight(stage) / view.zoom,\n };\n}\n\n/** Smooth cubic ease in / ease out. */\nexport const easeInOutCubic = (t: number): number =>\n t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;\n","import { boardHeight, projectPoint, type View, type Stage } from './camera.js';\nimport { boundsOf, overlapArea, areaOf, type Rect, type SceneElement } from './geometry.js';\nimport { isElementCommand, type Shot } from './types.js';\n\nexport interface ShotCheck {\n /** Elements this shot drew or changed that the camera frame leaves out (\"outside\") or cuts (\"cut\"). */\n framing: { id: string; how: 'outside' | 'cut' }[];\n /** Elements this shot drew that overlap each other significantly. */\n overlaps: [string, string][];\n /** Smallest text size on screen in CSS px. */\n smallestText?: { id: string; text: string; px: number };\n /** Any text smaller than min readable threshold (e.g. 18px). */\n smallTexts?: { id: string; text: string; px: number }[];\n /** Elements clipped by the screen edges. */\n edges: string[];\n /** Elements covered by the title bar. */\n underTitle: string[];\n}\n\nexport const NO_CHECK: ShotCheck = { framing: [], overlaps: [], edges: [], underTitle: [] };\n\nexport const SMALL_TEXT_PX = 24;\n\nexport function titleBox(title: string, stage: Stage): Rect {\n const width = Math.min(stage.width, title.length * 20 + 64);\n return { x: (stage.width - width) / 2, y: 24, width, height: 60 };\n}\n\nexport function touchedIds(shot: Shot): string[] {\n const ids = new Set<string>();\n for (const cmd of shot.run ?? []) {\n if (isElementCommand(cmd)) {\n ids.add(cmd.element.id);\n } else if ('id' in cmd && typeof (cmd as any).id === 'string') {\n ids.add((cmd as any).id);\n }\n }\n return [...ids];\n}\n\nconst IN_FRAME = 0.95;\n\n/**\n * Where a scene box lands on screen (stage pixels from the board's top\n * left), turned and in perspective as the view draws it: the bounds of its\n * four corners. Null when part of it is behind the camera.\n */\nexport function screenBox(box: Rect, view: View, stage: Stage): Rect | null {\n const corners = [\n [box.x, box.y],\n [box.x + box.width, box.y],\n [box.x, box.y + box.height],\n [box.x + box.width, box.y + box.height],\n ].map(([x, y]) => projectPoint(view, stage, [x!, y!]));\n if (corners.some((c) => !c)) return null;\n const xs = corners.map((c) => c![0]);\n const ys = corners.map((c) => c![1]);\n const x = Math.min(...xs);\n const y = Math.min(...ys);\n return { x, y, width: Math.max(...xs) - x, height: Math.max(...ys) - y };\n}\n\n/**\n * Evaluates the elements on stage against the current camera view and shot\n * settings. Framing, edges, text size and the title are judged on screen,\n * where the view (its angle included) puts each element.\n */\nexport function checkShot(\n shot: Shot,\n elements: readonly SceneElement[],\n view: View,\n stage: Stage,\n title?: string,\n): ShotCheck {\n const screen: Rect = { x: 0, y: 0, width: stage.width, height: boardHeight(stage) };\n const touched = new Set(touchedIds(shot));\n const framing: { id: string; how: 'outside' | 'cut' }[] = [];\n const edges: string[] = [];\n const overlaps: [string, string][] = [];\n const activeElements = elements.filter((e) => !e.isDeleted && (e.opacity === undefined || e.opacity > 0));\n\n for (const el of activeElements) {\n const box = screenBox(boundsOf(el), view, stage);\n const overlap = box ? overlapArea(box, screen) : 0;\n const boxArea = box ? areaOf(box) : 0;\n\n if (touched.has(el.id)) {\n if (overlap === 0) {\n framing.push({ id: el.id, how: 'outside' });\n } else if (boxArea > 0 && overlap / boxArea < IN_FRAME) {\n framing.push({ id: el.id, how: 'cut' });\n }\n } else if (overlap > 0 && boxArea > 0 && overlap / boxArea < IN_FRAME) {\n edges.push(el.id);\n }\n }\n\n // Check overlaps among touched elements\n const touchedList = activeElements.filter((e) => touched.has(e.id));\n for (let i = 0; i < touchedList.length; i++) {\n for (let j = i + 1; j < touchedList.length; j++) {\n const a = touchedList[i]!;\n const b = touchedList[j]!;\n const aBox = boundsOf(a);\n const bBox = boundsOf(b);\n const ol = overlapArea(aBox, bBox);\n const minArea = Math.min(areaOf(aBox), areaOf(bBox));\n if (minArea > 0 && ol / minArea > 0.3) {\n overlaps.push([a.id, b.id]);\n }\n }\n }\n\n // Check text readability under current zoom\n const smallTexts: { id: string; text: string; px: number }[] = [];\n let smallestText: { id: string; text: string; px: number } | undefined;\n\n for (const el of activeElements) {\n if (el.text && el.fontSize) {\n // The text's height on screen where it sits: zoom, and perspective when the camera is angled.\n const b = boundsOf(el);\n const top = projectPoint(view, stage, [b.x + b.width / 2, b.y + b.height / 2]);\n const bottom = projectPoint(view, stage, [b.x + b.width / 2, b.y + b.height / 2 + el.fontSize]);\n if (!top || !bottom) continue;\n const px = Math.round(Math.hypot(bottom[0] - top[0], bottom[1] - top[1]));\n const item = { id: el.id, text: el.text, px };\n if (!smallestText || px < smallestText.px) {\n smallestText = item;\n }\n if (px < SMALL_TEXT_PX) {\n smallTexts.push(item);\n }\n }\n }\n\n // Check title obstruction\n const underTitle: string[] = [];\n if (title) {\n const tBox = titleBox(title, stage);\n for (const el of activeElements) {\n const box = screenBox(boundsOf(el), view, stage);\n if (box && overlapArea(box, tBox) > 0) {\n underTitle.push(el.id);\n }\n }\n }\n\n return {\n framing,\n overlaps,\n smallestText,\n smallTexts: smallTexts.length ? smallTexts : undefined,\n edges,\n underTitle,\n };\n}\n","import type { Rect } from './geometry.js';\n\nexport interface EffectBase {\n /** Unique identifier for the effect so it can be stopped or updated. */\n id: string;\n /** End the effect when this many subsequent shots begin. */\n shots?: number;\n /** Duration in milliseconds after which the effect stops automatically. */\n duration?: number;\n /** When to start: 'start' of shot or after commands are 'drawn'. Default: 'drawn'. */\n at?: 'start' | 'drawn';\n}\n\nexport interface GradientEffect extends EffectBase {\n type: 'gradient';\n colors: string[];\n rect?: Rect;\n ids?: string[];\n angle?: number;\n period?: number;\n opacity?: number;\n}\n\nexport interface FlowEffect extends EffectBase {\n type: 'flow';\n along: string;\n color?: string;\n dots?: number;\n period?: number;\n size?: number;\n direction?: 'forward' | 'back' | 'both';\n label?: string;\n}\n\nexport interface GlowEffect extends EffectBase {\n type: 'glow';\n ids: string[];\n color?: string;\n size?: number;\n pulse?: boolean;\n}\n\nexport interface SpotlightEffect extends EffectBase {\n type: 'spotlight';\n ids: string[];\n dim?: number;\n padding?: number;\n}\n\nexport interface ShimmerEffect extends EffectBase {\n type: 'shimmer';\n ids: string[];\n color?: string;\n period?: number;\n}\n\nexport interface RippleEffect extends EffectBase {\n type: 'ripple';\n ids: string[];\n color?: string;\n period?: number;\n size?: number;\n times?: number;\n}\n\nexport interface TimerEffect extends EffectBase {\n type: 'timer';\n ids: string[];\n time?: number;\n fill?: boolean;\n repeat?: boolean;\n color?: string;\n size?: number;\n}\n\nexport interface CounterEffect extends EffectBase {\n type: 'counter';\n to: number;\n from?: number;\n time?: number;\n text?: string;\n decimals?: number;\n near?: string;\n x?: number;\n y?: number;\n color?: string;\n size?: number;\n}\n\nexport interface HighlightEffect extends EffectBase {\n type: 'highlight';\n ids: string[];\n style?: 'marker' | 'underline';\n color?: string;\n}\n\nexport interface BlurEffect extends EffectBase {\n type: 'blur';\n ids: string[];\n amount?: number;\n padding?: number;\n}\n\nexport interface BurstEffect extends EffectBase {\n type: 'burst';\n ids: string[];\n color?: string;\n sparks?: number;\n size?: number;\n}\n\n/** Media layer effect driving a WebGL shader (or other media stream) under or over the scene. */\nexport interface MediaEffect extends EffectBase {\n type: 'media';\n /** ID in Scene.media. */\n media: string;\n rect?: Rect;\n ids?: string[];\n padding?: number;\n layer?: 'under' | 'over';\n opacity?: number;\n}\n\nexport type Effect =\n | GradientEffect\n | FlowEffect\n | GlowEffect\n | SpotlightEffect\n | ShimmerEffect\n | RippleEffect\n | TimerEffect\n | CounterEffect\n | HighlightEffect\n | BlurEffect\n | BurstEffect\n | MediaEffect;\n\nexport const EFFECT_TYPES = [\n 'gradient',\n 'flow',\n 'glow',\n 'spotlight',\n 'shimmer',\n 'ripple',\n 'timer',\n 'counter',\n 'highlight',\n 'blur',\n 'burst',\n 'media',\n] as const;\n\nexport const EFFECT_FADE_MS = 300;\n\nexport interface RunningEffect {\n effect: Effect;\n /** Timestamp when started (ms). */\n from: number;\n /** Timestamp when stopping/fading begins (ms). */\n until?: number;\n}\n\n/** Returns true if the effect has completely expired and finished fading out. */\nexport function effectGone(running: RunningEffect, now: number): boolean {\n if (running.until !== undefined) {\n return now >= running.until + EFFECT_FADE_MS;\n }\n if (running.effect.duration !== undefined) {\n return now >= running.from + running.effect.duration + EFFECT_FADE_MS;\n }\n return false;\n}\n\n/** Returns current opacity multiplier (0 to 1) accounting for fade in / fade out. */\nexport function effectOpacity(running: RunningEffect, now: number): number {\n if (now < running.from) return 0;\n // Fade in over first 150ms\n const inFade = Math.min(1, (now - running.from) / 150);\n\n if (running.until !== undefined) {\n const out = Math.max(0, 1 - (now - running.until) / EFFECT_FADE_MS);\n return inFade * out;\n }\n if (running.effect.duration !== undefined) {\n const remaining = running.from + running.effect.duration - now;\n if (remaining < 0) {\n const out = Math.max(0, 1 + remaining / EFFECT_FADE_MS);\n return inFade * out;\n }\n }\n return inFade;\n}\n","import type { EasingCurve } from '../time/retiming.js';\n\nexport interface BehaviorBase {\n id?: string;\n /** Target element ID or media ID. */\n target: string;\n /** Property on element (e.g. 'x', 'y', 'scale', 'rotation', 'opacity') or uniform on media. */\n property: string;\n}\n\nexport interface OscillateBehavior extends BehaviorBase {\n type: 'oscillate';\n wave?: 'sine' | 'square' | 'triangle' | 'sawtooth';\n amplitude: number;\n /** Frequency in Hz (cycles per second). */\n frequency: number;\n /** Phase offset in radians (default 0). */\n phase?: number;\n /** Base center value. If undefined, treated as relative offset added to current base value. */\n center?: number;\n}\n\nexport interface RampBehavior extends BehaviorBase {\n type: 'ramp';\n from?: number;\n to: number;\n ease?: EasingCurve;\n}\n\nexport interface RateBehavior extends BehaviorBase {\n type: 'rate';\n /** Rate of change per second (e.g. degrees/sec or units/sec). */\n rate: number;\n}\n\nexport interface WriggleBehavior extends BehaviorBase {\n type: 'wriggle';\n /** Maximum amplitude of deviation. */\n amount: number;\n /** Speed / frequency of variation in Hz. */\n frequency: number;\n /** Number of noise octaves for organic complexity (default: 3). */\n octaves?: number;\n /** Deterministic random seed. */\n seed?: number;\n}\n\nexport interface OvershootBehavior extends BehaviorBase {\n type: 'overshoot';\n targetValue: number;\n startValue?: number;\n /** Oscillation frequency / cycles (default: 3). */\n cycles?: number;\n /** Damping factor (higher settles faster, default: 4.0). */\n damping?: number;\n}\n\nexport interface ThrowBehavior extends BehaviorBase {\n type: 'throw';\n /** Initial speed in units per second. */\n initialVelocity: number;\n /** Friction / deceleration factor (default: 2.0). */\n friction?: number;\n}\n\nexport type Behavior =\n | OscillateBehavior\n | RampBehavior\n | RateBehavior\n | WriggleBehavior\n | OvershootBehavior\n | ThrowBehavior;\n\nimport { fractalWiggle } from '../time/noise.js';\n\n/**\n * Computes smooth organic 1D multi-octave C2 noise wander matching AE wiggle.\n */\nfunction evaluateWriggle(\n t: number,\n freq: number,\n amount: number,\n octaves = 3,\n seed = 42,\n): number {\n return fractalWiggle(freq, amount, octaves, 0.5, t, seed, 0);\n}\n\n/**\n * Evaluates the new numeric value for a behavior at a given elapsed time.\n * @param behavior The behavior specification.\n * @param elapsedSec Elapsed time within the shot/behavior duration (in seconds).\n * @param durationSec Total duration of the shot/behavior (in seconds).\n * @param baseValue Initial or fallback value before behavior was applied.\n */\nexport function evaluateBehavior(\n behavior: Behavior,\n elapsedSec: number,\n durationSec: number,\n baseValue = 0,\n): number {\n const t = Math.max(0, elapsedSec);\n\n switch (behavior.type) {\n case 'oscillate': {\n const freq = behavior.frequency;\n const phase = behavior.phase ?? 0;\n const angle = 2 * Math.PI * freq * t + phase;\n let waveVal = 0;\n\n switch (behavior.wave) {\n case 'square':\n waveVal = Math.sin(angle) >= 0 ? 1 : -1;\n break;\n case 'triangle':\n waveVal = (2 / Math.PI) * Math.asin(Math.sin(angle));\n break;\n case 'sawtooth': {\n const norm = (angle / (2 * Math.PI)) % 1;\n waveVal = 2 * (norm < 0 ? norm + 1 : norm) - 1;\n break;\n }\n case 'sine':\n default:\n waveVal = Math.sin(angle);\n break;\n }\n\n const offset = waveVal * behavior.amplitude;\n return behavior.center !== undefined ? behavior.center + offset : baseValue + offset;\n }\n\n case 'ramp': {\n const from = behavior.from ?? baseValue;\n const to = behavior.to;\n const p = durationSec > 0 ? Math.min(1, Math.max(0, t / durationSec)) : 1;\n let eased = p;\n\n switch (behavior.ease) {\n case 'easeIn':\n eased = p * p;\n break;\n case 'easeOut':\n eased = p * (2 - p);\n break;\n case 'easeInOut':\n eased = p < 0.5 ? 2 * p * p : -1 + (4 - 2 * p) * p;\n break;\n case 'linear':\n default:\n eased = p;\n break;\n }\n\n return from + (to - from) * eased;\n }\n\n case 'rate': {\n return baseValue + behavior.rate * t;\n }\n\n case 'wriggle': {\n const wander = evaluateWriggle(\n t,\n behavior.frequency,\n behavior.amount,\n behavior.octaves ?? 3,\n behavior.seed ?? 42,\n );\n return baseValue + wander;\n }\n\n case 'overshoot': {\n const from = behavior.startValue ?? baseValue;\n const target = behavior.targetValue;\n const cycles = behavior.cycles ?? 3;\n const damping = behavior.damping ?? 4.0;\n\n if (durationSec <= 0) return target;\n const p = Math.min(1, t / durationSec);\n if (p >= 1) return target;\n\n // Damped harmonic oscillation: 1 - e^(-damping * p) * cos(cycles * pi * p)\n const envelope = Math.exp(-damping * p);\n const oscillation = Math.cos(cycles * Math.PI * p);\n const progress = 1 - envelope * oscillation;\n\n return from + (target - from) * progress;\n }\n\n case 'throw': {\n const v0 = behavior.initialVelocity;\n const k = Math.max(0.001, behavior.friction ?? 2.0);\n // Continuous displacement under linear drag: x(t) = (v0 / k) * (1 - e^(-k * t))\n const displacement = (v0 / k) * (1 - Math.exp(-k * t));\n return baseValue + displacement;\n }\n }\n}\n","import type { ShotCheck } from './check.js';\nimport type { Scene } from './types.js';\n\nexport interface ShotLog extends ShotCheck {\n index: number;\n start: number;\n drawn: number;\n end: number;\n caption?: string;\n title?: string;\n failed: string[];\n effects?: string[];\n}\n\nexport interface ReviewOptions {\n target?: [number, number];\n minTextPx?: number;\n}\n\nexport interface Review {\n totalMs: number;\n warnings: string[];\n notes: string[];\n markdown: string;\n}\n\n/** Formats a structured diagnostic report of scene playback. */\nexport function reviewScene(scene: Scene, logs: readonly ShotLog[], options: ReviewOptions = {}): Review {\n const warnings: string[] = [];\n const notes: string[] = [];\n const minTextPx = options.minTextPx ?? 18;\n const totalMs = logs.length > 0 ? logs[logs.length - 1]!.end : 0;\n const totalSec = (totalMs / 1000).toFixed(1);\n\n if (scene.target) {\n const [minS, maxS] = scene.target;\n const durS = totalMs / 1000;\n if (durS < minS) {\n warnings.push(`Scene is ${totalSec}s, shorter than target minimum (${minS}s).`);\n } else if (durS > maxS) {\n warnings.push(`Scene is ${totalSec}s, longer than target maximum (${maxS}s).`);\n }\n }\n\n for (const log of logs) {\n const shotPrefix = `Shot ${log.index}:`;\n\n if (log.failed.length > 0) {\n warnings.push(`${shotPrefix} ${log.failed.length} command(s) failed: ${log.failed.join(', ')}`);\n }\n\n for (const f of log.framing) {\n warnings.push(`${shotPrefix} element \"${f.id}\" is ${f.how} of the camera frame.`);\n }\n\n for (const [a, b] of log.overlaps) {\n notes.push(`${shotPrefix} elements \"${a}\" and \"${b}\" overlap significantly.`);\n }\n\n if (log.smallTexts) {\n for (const t of log.smallTexts) {\n if (t.px < minTextPx) {\n warnings.push(`${shotPrefix} text \"${t.text}\" is only ${t.px}px on screen (less than ${minTextPx}px minimum).`);\n }\n }\n }\n\n for (const id of log.underTitle) {\n warnings.push(`${shotPrefix} element \"${id}\" is partially covered by the title bar.`);\n }\n }\n\n // Generate markdown\n const lines: string[] = [\n `# Scene Preview Review: ${scene.title || scene.name}`,\n `**Total Duration:** ${totalSec}s (${logs.length} shots)`,\n '',\n ];\n\n if (warnings.length > 0) {\n lines.push('### ⚠️ Warnings to Address:');\n for (const w of warnings) lines.push(`- ${w}`);\n lines.push('');\n } else {\n lines.push('✅ No critical layout or framing warnings.');\n lines.push('');\n }\n\n if (notes.length > 0) {\n lines.push('### ℹ️ Layout Notes:');\n for (const n of notes) lines.push(`- ${n}`);\n lines.push('');\n }\n\n lines.push('### Shot Breakdown');\n lines.push('| Shot | Start | End | Duration | Title / Caption |');\n lines.push('|:---:|:---:|:---:|:---:|:---|');\n for (const log of logs) {\n const dur = ((log.end - log.start) / 1000).toFixed(1);\n const text = log.title || log.caption || '—';\n lines.push(`| ${log.index} | ${(log.start / 1000).toFixed(1)}s | ${(log.end / 1000).toFixed(1)}s | ${dur}s | ${text.slice(0, 40)} |`);\n }\n\n return {\n totalMs,\n warnings,\n notes,\n markdown: lines.join('\\n'),\n };\n}\n","import type { Clock } from '../time/clock.js';\nimport { ManualClock } from '../time/clock.js';\nimport {\n boardHeight,\n cameraAngle,\n centreOf,\n CAMERA_MS,\n CAMERA_STEPS,\n DEFAULT_STAGE,\n frame,\n calculateFramingRect,\n isAngled,\n projectPoint,\n transitionStrength,\n transitionView,\n viewAt,\n type Stage,\n type View,\n} from './camera.js';\nimport { checkShot } from './check.js';\nimport { effectGone, type RunningEffect } from './effects.js';\nimport { evaluateBehavior } from './behavior.js';\nimport type { Rect, SceneElement } from './geometry.js';\nimport { reviewScene, type Review, type ShotLog } from './report.js';\nimport {\n isDeleteCommand,\n isElementCommand,\n isUpdateCommand,\n type Scene,\n type SceneCommand,\n type Shot,\n} from './types.js';\n\nexport interface SceneBoard {\n getView(): View;\n setView(view: View): void;\n elements(): readonly SceneElement[];\n runCommand(cmd: SceneCommand): void | Promise<void>;\n setTitle?(text: string, opacity: number): void;\n setCaption?(text: string): void;\n setEffects?(effects: readonly RunningEffect[]): void;\n setMediaTime?(mediaId: string, timeSeconds: number): void;\n}\n\n/** Pure in-memory board implementation suitable for headless simulation, testing, and SSR. */\nexport class MemorySceneBoard implements SceneBoard {\n private view: View;\n private elMap = new Map<string, SceneElement>();\n private currentTitle = '';\n private currentCaption = '';\n private runningEffects: readonly RunningEffect[] = [];\n\n constructor(stage: Stage = DEFAULT_STAGE) {\n this.view = viewAt(stage.width / 2, stage.height / 2, 1, stage);\n }\n\n getView(): View {\n return this.view;\n }\n\n setView(view: View): void {\n this.view = view;\n }\n\n elements(): readonly SceneElement[] {\n return Array.from(this.elMap.values());\n }\n\n runCommand(cmd: SceneCommand): void {\n if (isElementCommand(cmd)) {\n this.elMap.set(cmd.element.id, { ...cmd.element });\n } else if (isUpdateCommand(cmd)) {\n const existing = this.elMap.get(cmd.id);\n if (existing) {\n this.elMap.set(cmd.id, { ...existing, ...cmd.props });\n }\n } else if (isDeleteCommand(cmd)) {\n this.elMap.delete(cmd.id);\n } else if (cmd.type === 'clear') {\n this.elMap.clear();\n }\n }\n\n setTitle(text: string): void {\n this.currentTitle = text;\n }\n\n setCaption(text: string): void {\n this.currentCaption = text;\n }\n\n setEffects(effects: readonly RunningEffect[]): void {\n this.runningEffects = effects;\n }\n}\n\n/**\n * The view a shot's camera moves to: its framing (a box, or the elements it\n * frames, from the board or the shot's own commands) at its angle, zoomed\n * out when angled so the framed box still fits. Framing\n * or angle parts the camera leaves out keep `current`'s. Undefined when the\n * shot has no camera.\n */\nexport function shotView(\n shot: Shot,\n elements: readonly SceneElement[],\n stage: Stage,\n current: View,\n): View | undefined {\n const camera = shot.camera;\n if (!camera) return undefined;\n let view: View = { zoom: current.zoom, scrollX: current.scrollX, scrollY: current.scrollY };\n let framed: Rect | undefined;\n if ('frame' in camera) {\n const frameIds = new Set(camera.frame);\n const targetElements = elements.filter((el) => frameIds.has(el.id));\n if (targetElements.length === 0) {\n for (const cmd of shot.run ?? []) {\n if (isElementCommand(cmd) && frameIds.has(cmd.element.id)) targetElements.push(cmd.element);\n }\n }\n framed = calculateFramingRect(targetElements, stage, {\n padding: camera.padding,\n minWidth: camera.minWidth,\n minHeight: camera.minHeight,\n });\n } else if ('width' in camera) {\n framed = camera as Rect;\n }\n if (framed) view = frame(framed, stage);\n const angle = cameraAngle({\n tilt: camera.tilt ?? current.tilt,\n turn: camera.turn ?? current.turn,\n roll: camera.roll ?? current.roll,\n perspective: camera.perspective ?? current.perspective,\n });\n const angled: View = { ...view, ...angle };\n return framed && isAngled(angled) ? fitAngled(angled, framed, stage) : angled;\n}\n\n/**\n * An angled view zoomed out (round the same centre) until the framed box,\n * turned and in perspective, fits the board again: perspective makes the\n * near side bigger, so a flat framing would cut it.\n */\nfunction fitAngled(view: View, box: Rect, stage: Stage): View {\n const [cx, cy] = centreOf(view, stage);\n const halfW = stage.width / 2;\n const halfH = boardHeight(stage) / 2;\n let fitted = view;\n for (let i = 0; i < 4; i++) {\n let spread = 0;\n for (const corner of [[box.x, box.y], [box.x + box.width, box.y], [box.x, box.y + box.height], [box.x + box.width, box.y + box.height]] as const) {\n const p = projectPoint(fitted, stage, corner);\n spread = p ? Math.max(spread, Math.abs(p[0] - halfW) / halfW, Math.abs(p[1] - halfH) / halfH) : Infinity;\n if (!p) break;\n }\n if (spread <= 1.001) break;\n const zoom = fitted.zoom / (Number.isFinite(spread) ? spread : 2);\n fitted = { ...fitted, ...viewAt(cx, cy, zoom, stage) };\n }\n return fitted;\n}\n\nexport interface PlayOptions {\n check?: boolean;\n afterShot?: (log: ShotLog) => void | Promise<void>;\n}\n\nexport interface PlayResult {\n ok: boolean;\n duration: number;\n shots: ShotLog[];\n}\n\n/**\n * Plays a scene's shots sequentially on `board` driven by `clock`.\n * When using a `ManualClock`, the execution is completely deterministic and stepped.\n */\nexport async function playScene(\n scene: Scene,\n board: SceneBoard,\n clock: Clock,\n options: PlayOptions = {},\n): Promise<PlayResult> {\n const stage: Stage = {\n width: scene.stage?.width ?? DEFAULT_STAGE.width,\n height: scene.stage?.height ?? DEFAULT_STAGE.height,\n caption: scene.stage?.caption ?? DEFAULT_STAGE.caption,\n };\n\n const shots: ShotLog[] = [];\n const begin = clock.now();\n let runningEffects: RunningEffect[] = [];\n const endsAt = new Map<RunningEffect, number>();\n\n const updateEffects = (next: RunningEffect[]) => {\n runningEffects = next.filter((r) => !effectGone(r, clock.now()));\n board.setEffects?.(runningEffects);\n };\n\n for (const [idx, shot] of scene.shots.entries()) {\n const shotStart = clock.now() - begin;\n const failedCmds: string[] = [];\n\n // Stop requested effects\n const stopIds = new Set(shot.stopEffects ?? []);\n if (stopIds.size > 0 || runningEffects.some((r) => endsAt.get(r) === idx)) {\n const now = clock.now();\n updateEffects(\n runningEffects.map((r) =>\n r.until === undefined && (stopIds.has(r.effect.id) || endsAt.get(r) === idx)\n ? { ...r, until: now }\n : r,\n ),\n );\n }\n\n // Camera move\n const targetView = shotView(shot, board.elements(), stage, board.getView());\n if (targetView) {\n if (idx === 0) {\n board.setView(targetView);\n } else {\n const kind = shot.camera?.transition ?? 'move';\n const strength = transitionStrength(kind, shot.camera?.motionBlur);\n if (kind === 'cut') {\n board.setView(targetView);\n } else {\n const fromView = board.getView();\n const stepMs = CAMERA_MS / CAMERA_STEPS;\n for (let s = 1; s <= CAMERA_STEPS; s++) {\n board.setView(transitionView(kind, fromView, targetView, s / CAMERA_STEPS, stage, strength));\n await clock.sleep(stepMs);\n }\n }\n }\n }\n\n // Set title and caption\n if (shot.title !== undefined) {\n board.setTitle?.(shot.title, 1);\n }\n if (shot.caption !== undefined) {\n board.setCaption?.(shot.caption);\n }\n\n // Start effects scheduled at 'start'\n const startEffects = (shot.effects ?? []).filter((e) => e.at === 'start');\n if (startEffects.length > 0) {\n const now = clock.now();\n const added = startEffects.map((effect): RunningEffect => ({ effect, from: now }));\n for (const r of added) if (r.effect.shots) endsAt.set(r, idx + r.effect.shots);\n updateEffects([...runningEffects, ...added]);\n }\n\n // Run commands\n for (const cmd of shot.run ?? []) {\n try {\n await board.runCommand(cmd);\n } catch (err: any) {\n failedCmds.push(err.message || String(err));\n }\n }\n\n // Start effects scheduled after 'drawn' (default)\n const drawnEffects = (shot.effects ?? []).filter((e) => e.at !== 'start');\n if (drawnEffects.length > 0) {\n const now = clock.now();\n const added = drawnEffects.map((effect): RunningEffect => ({ effect, from: now }));\n for (const r of added) if (r.effect.shots) endsAt.set(r, idx + r.effect.shots);\n updateEffects([...runningEffects, ...added]);\n }\n\n const drawnTime = clock.now() - begin;\n\n // Check shot layout & framing\n const check = options.check\n ? checkShot(shot, board.elements(), board.getView(), stage, shot.title)\n : { framing: [], overlaps: [], edges: [], underTitle: [] };\n\n // Hold dwell time and active behaviors\n const holdMs = shot.hold ?? 800;\n const behaviors = shot.behaviors ?? [];\n if (behaviors.length > 0) {\n const initialProps = new Map<string, number>();\n for (const b of behaviors) {\n const key = `${b.target}:${b.property}`;\n const el = board.elements().find((e) => e.id === b.target);\n if (el && typeof (el as any)[b.property] === 'number') {\n initialProps.set(key, (el as any)[b.property]);\n } else {\n initialProps.set(key, 0);\n }\n }\n\n const stepMs = 50;\n const steps = Math.max(1, Math.round(holdMs / stepMs));\n const actualStepMs = holdMs / steps;\n const durationSec = holdMs / 1000;\n\n for (let s = 1; s <= steps; s++) {\n await clock.sleep(actualStepMs);\n const elapsedSec = (s * actualStepMs) / 1000;\n for (const b of behaviors) {\n const baseVal = initialProps.get(`${b.target}:${b.property}`) ?? 0;\n const nextVal = evaluateBehavior(b, elapsedSec, durationSec, baseVal);\n board.runCommand({\n type: 'update',\n id: b.target,\n props: { [b.property]: nextVal },\n });\n }\n }\n } else {\n await clock.sleep(holdMs);\n }\n\n const shotEnd = clock.now() - begin;\n\n const log: ShotLog = {\n ...check,\n index: idx + 1,\n start: shotStart,\n drawn: drawnTime,\n end: shotEnd,\n caption: shot.caption,\n title: shot.title,\n failed: failedCmds,\n effects: runningEffects.map((r) => r.effect.id),\n };\n\n shots.push(log);\n if (options.afterShot) {\n await options.afterShot(log);\n }\n }\n\n const duration = clock.now() - begin;\n const ok = shots.every((s) => s.failed.length === 0 && s.framing.every((f) => f.how !== 'outside'));\n\n return { ok, duration, shots };\n}\n\n/**\n * Simulates a scene instantaneously using a ManualClock and MemorySceneBoard,\n * returning full timing details, layout checks, and diagnostic report.\n */\nexport async function simulateScene(\n scene: Scene,\n options: { minTextPx?: number } = {},\n): Promise<{ result: PlayResult; review: Review }> {\n const clock = new ManualClock();\n const board = new MemorySceneBoard();\n\n let done = false;\n let playError: any = null;\n const playPromise = playScene(scene, board, clock, { check: true })\n .then((res) => {\n done = true;\n return res;\n })\n .catch((err) => {\n done = true;\n playError = err;\n throw err;\n });\n\n // Advance manual clock until simulation finishes\n for (let i = 0; i < 5000 && !done; i++) {\n await clock.advance(50);\n }\n\n if (playError) throw playError;\n const result = await playPromise;\n const review = reviewScene(scene, result.shots, {\n target: scene.target,\n minTextPx: options.minTextPx,\n });\n\n return { result, review };\n}\n","import {\n CAMERA_MS,\n CAMERA_STEPS,\n DEFAULT_STAGE,\n transitionStrength,\n transitionView,\n viewAt,\n type CameraTransition,\n type Stage,\n type View,\n} from './camera.js';\nimport type { SceneElement } from './geometry.js';\nimport { MemorySceneBoard, shotView } from './player.js';\nimport type { Scene, Shot } from './types.js';\n\n/** The camera through one shot, timed as `playScene` plays it: a move from `from` to `to`, then a hold. */\nexport interface CameraShot {\n shot: Shot;\n index: number;\n /** When the shot starts on the scene's timeline, in ms. */\n startMs: number;\n /** How long the camera moves at the start of the shot (0 when it cuts or stays). */\n moveMs: number;\n /** How long the shot holds after the move. */\n holdMs: number;\n from: View;\n to: View;\n /** How the camera gets into the shot (`camera.transition`, `move` by default). */\n transition: CameraTransition;\n /** The blur strength the move plays with: `camera.motionBlur`, or 2 for a whip or zoom without one (0 for none). */\n motionBlur: number;\n /** The elements on stage once the shot's commands have run. */\n elements: SceneElement[];\n}\n\n/** How long the camera moves at the start of a shot, in ms: none for the first shot, a shot without a camera, or a cut. */\nexport function shotMoveMs(shot: Shot, index: number): number {\n return index > 0 && shot.camera && shot.camera.transition !== 'cut' ? CAMERA_MS : 0;\n}\n\n/** The scene's stage with defaults filled in. */\nexport function sceneStage(scene: Scene): Stage {\n return {\n width: scene.stage?.width ?? DEFAULT_STAGE.width,\n height: scene.stage?.height ?? DEFAULT_STAGE.height,\n caption: scene.stage?.caption ?? DEFAULT_STAGE.caption,\n };\n}\n\n/**\n * The camera through every shot of a scene, worked out the way `playScene`\n * moves it, for exporters and players that lay a scene out on a timeline:\n * each shot's move (framing, angle and blur strength) and hold.\n */\nexport function cameraTrack(scene: Scene): CameraShot[] {\n const stage = sceneStage(scene);\n const board = new MemorySceneBoard(stage);\n let view: View = viewAt(stage.width / 2, (stage.height - (stage.caption ?? 0)) / 2, 1, stage);\n let startMs = 0;\n return scene.shots.map((shot, index) => {\n const target = shotView(shot, board.elements(), stage, view);\n const transition = shot.camera?.transition ?? 'move';\n const moveMs = shotMoveMs(shot, index);\n const from = index === 0 && target ? target : view;\n const to = target ?? view;\n const holdMs = shot.hold ?? 800;\n for (const cmd of shot.run ?? []) board.runCommand(cmd);\n const entry: CameraShot = {\n shot,\n index,\n startMs,\n moveMs,\n holdMs,\n from,\n to,\n transition,\n motionBlur: transitionStrength(transition, shot.camera?.motionBlur),\n elements: board.elements().map((e) => ({ ...e })),\n };\n view = to;\n startMs += moveMs + holdMs;\n return entry;\n });\n}\n\n/** The time the player waits between steps of a camera move, in ms (its blur is measured over one). */\nexport const CAMERA_STEP_MS = CAMERA_MS / CAMERA_STEPS;\n\n/**\n * The camera `ms` into a shot: part-way through its move (eased, with its\n * motion blur) or settled.\n */\nexport function cameraAt(entry: CameraShot, ms: number, stage: Stage): View {\n if (!(entry.moveMs > 0) || ms >= entry.moveMs) return entry.to;\n return transitionView(entry.transition, entry.from, entry.to, Math.max(0, ms) / entry.moveMs, stage, entry.motionBlur);\n}\n\n/** Whether the shot's move jumps from one framing to the other halfway (a whip or zoom). */\nexport const cutsHalfway = (entry: CameraShot): boolean =>\n entry.moveMs > 0 && (entry.transition === 'whip' || entry.transition === 'zoom');\n\n/** The camera at one frame of a scene's timeline. */\nexport interface CameraSample {\n frame: number;\n view: View;\n /** The shot (0-based) the frame is in. */\n shot: number;\n}\n\n/**\n * The camera sampled for a timeline at `frameRate`: every `every` frames\n * during each move (so an editor's straight-line interpolation follows the\n * eased path and perspective), and once where each move settles. Views\n * include the move's motion blur.\n */\nexport function cameraSamples(scene: Scene, frameRate: number, every = 2): CameraSample[] {\n const stage = sceneStage(scene);\n const samples: CameraSample[] = [];\n // A later sample on the same frame (a shot with no hold) replaces the earlier one.\n const add = (sample: CameraSample) => {\n if (samples.at(-1)?.frame === sample.frame) samples.pop();\n samples.push(sample);\n };\n for (const entry of cameraTrack(scene)) {\n const start = Math.round((entry.startMs / 1000) * frameRate);\n const moveFrames = Math.round((entry.moveMs / 1000) * frameRate);\n const at = (f: number) => add({ frame: start + f, view: cameraAt(entry, (f / frameRate) * 1000, stage), shot: entry.index });\n // A whip or zoom jumps halfway: key the frames either side of the jump, so straight-line interpolation never tweens across it.\n const cut = cutsHalfway(entry) ? Math.ceil((entry.moveMs / 2000) * frameRate) : -1;\n for (let f = 0; f < moveFrames; f += every) {\n if (cut > 0 && f < cut && f + every >= cut) {\n at(f);\n if (cut - 1 > f) at(cut - 1);\n at(cut);\n continue;\n }\n at(f);\n }\n add({ frame: start + moveFrames, view: entry.to, shot: entry.index });\n }\n return samples;\n}\n"],"mappings":"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","names":["motionBlur","view","before","x","y"]}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import {
|
|
2
|
-
CAMERA_MS,
|
|
3
2
|
DEFAULT_PERSPECTIVE,
|
|
4
3
|
DEFAULT_STAGE,
|
|
5
4
|
boardHeight,
|
|
@@ -10,8 +9,9 @@ import {
|
|
|
10
9
|
isElementCommand,
|
|
11
10
|
isUpdateCommand,
|
|
12
11
|
projectPoint,
|
|
13
|
-
sceneStage
|
|
14
|
-
|
|
12
|
+
sceneStage,
|
|
13
|
+
shotMoveMs
|
|
14
|
+
} from "./chunk-5USSDEQL.js";
|
|
15
15
|
|
|
16
16
|
// src/export/motn.ts
|
|
17
17
|
function escapeXml(unsafe) {
|
|
@@ -39,6 +39,8 @@ function cameraGroupOpen(scene, frameRate, totalFrames, id) {
|
|
|
39
39
|
lines.push(` <!-- 3D group: view it through a camera ${distance}px from the canvas -->`);
|
|
40
40
|
}
|
|
41
41
|
for (const shot of scene.shots) {
|
|
42
|
+
const transition = shot.camera?.transition;
|
|
43
|
+
if (transition && transition !== "move") lines.push(` <!-- ${escapeXml(shot.title ?? "Shot")}: ${transition} in -->`);
|
|
42
44
|
if (shot.camera?.motionBlur) lines.push(` <!-- ${escapeXml(shot.title ?? "Shot")}: motion blur ${shot.camera.motionBlur} on the move in -->`);
|
|
43
45
|
}
|
|
44
46
|
lines.push(` <timing in="0" out="${totalFrames - 1}" offset="0"/>`);
|
|
@@ -71,7 +73,7 @@ function exportMotn(scene, options = {}) {
|
|
|
71
73
|
const height = scene.stage?.height ?? DEFAULT_STAGE.height;
|
|
72
74
|
let currentMs = 0;
|
|
73
75
|
const shotTimings = scene.shots.map((shot, idx) => {
|
|
74
|
-
const camMs =
|
|
76
|
+
const camMs = shotMoveMs(shot, idx);
|
|
75
77
|
const holdMs = shot.hold ?? 800;
|
|
76
78
|
const durationMs = camMs + holdMs;
|
|
77
79
|
const startMs = currentMs;
|
|
@@ -283,7 +285,7 @@ function exportLottie(scene, options = {}) {
|
|
|
283
285
|
const height = scene.stage?.height ?? DEFAULT_STAGE.height;
|
|
284
286
|
let currentMs = 0;
|
|
285
287
|
const shotTimings = scene.shots.map((shot, idx) => {
|
|
286
|
-
const camMs =
|
|
288
|
+
const camMs = shotMoveMs(shot, idx);
|
|
287
289
|
const holdMs = shot.hold ?? 800;
|
|
288
290
|
const durationMs = camMs + holdMs;
|
|
289
291
|
const startMs = currentMs;
|
|
@@ -445,7 +447,7 @@ function escapeXml2(unsafe) {
|
|
|
445
447
|
function calculateShotTimings(scene, frameRate = 30) {
|
|
446
448
|
let currentMs = 0;
|
|
447
449
|
return scene.shots.map((shot, index) => {
|
|
448
|
-
const camMs =
|
|
450
|
+
const camMs = shotMoveMs(shot, index);
|
|
449
451
|
const holdMs = shot.hold ?? 800;
|
|
450
452
|
const durationMs = camMs + holdMs;
|
|
451
453
|
const startMs = currentMs;
|
|
@@ -741,9 +743,10 @@ function exportFcp7Xml(scene, options = {}) {
|
|
|
741
743
|
lines.push("</xmeml>");
|
|
742
744
|
return lines.join("\n");
|
|
743
745
|
}
|
|
744
|
-
function describeCamera(view, motionBlur, stage) {
|
|
746
|
+
function describeCamera(view, motionBlur, stage, transition = "move") {
|
|
745
747
|
const [cx, cy] = centreOf(view, stage);
|
|
746
|
-
const
|
|
748
|
+
const lead = transition === "move" ? "" : `${transition} to `;
|
|
749
|
+
const parts = [`${lead}zoom ${r4(view.zoom).toFixed(2)}x on (${Math.round(cx)}, ${Math.round(cy)})`];
|
|
747
750
|
if (view.tilt) parts.push(`tilt ${r4(view.tilt)}\xB0`);
|
|
748
751
|
if (view.turn) parts.push(`turn ${r4(view.turn)}\xB0`);
|
|
749
752
|
if (view.roll) parts.push(`roll ${r4(view.roll)}\xB0`);
|
|
@@ -779,7 +782,7 @@ function exportCmx3600Edl(scene, options = {}) {
|
|
|
779
782
|
lines.push(`* COMMENT: ${shot.caption}`);
|
|
780
783
|
}
|
|
781
784
|
const camera = track?.[index];
|
|
782
|
-
if (camera && shot.camera) lines.push(`* CAMERA: ${describeCamera(camera.to, camera.motionBlur, sceneStage(scene))}`);
|
|
785
|
+
if (camera && shot.camera) lines.push(`* CAMERA: ${describeCamera(camera.to, camera.motionBlur, sceneStage(scene), camera.transition)}`);
|
|
783
786
|
lines.push("");
|
|
784
787
|
});
|
|
785
788
|
return lines.join("\n");
|
|
@@ -795,4 +798,4 @@ export {
|
|
|
795
798
|
exportFcp7Xml,
|
|
796
799
|
exportCmx3600Edl
|
|
797
800
|
};
|
|
798
|
-
//# sourceMappingURL=chunk-
|
|
801
|
+
//# sourceMappingURL=chunk-74ZLCZOG.js.map
|