// grammar.js — classic cinema vocabulary as PURE DATA (Lane A owns; every lane reads). // No imports, no DOM, no three — safe under plain node. Presets/LLM ops both resolve here, // so the button path and the /director path can never drift apart. // Shot sizes: vertical span the framing should cover, as a fraction of subject height, // @ where the frame centers on the body (fraction of subject height from the feet). export const SHOTS = { ECU: { span: 0.12, center: 0.93, label: 'extreme close-up' }, CU: { span: 0.25, center: 0.87, label: 'close-up' }, MCU: { span: 0.45, center: 0.75, label: 'medium close-up' }, MS: { span: 0.65, center: 0.65, label: 'medium shot' }, MWS: { span: 0.85, center: 0.55, label: 'medium wide' }, WS: { span: 1.2, center: 0.5, label: 'wide shot' }, EWS: { span: 2.5, center: 0.5, label: 'extreme wide' }, }; // Camera attitude relative to the aim point. elev = degrees above (+) / below (−) the // aim; roll = dutch tilt in degrees; ots = over-the-shoulder of the OTHER subject. export const ANGLES = { eye: { elev: 0, roll: 0 }, low: { elev: -18, roll: 0 }, high: { elev: 22, roll: 0 }, dutch: { elev: 0, roll: 8 }, ots_L: { elev: 0, roll: 0, ots: 'L' }, ots_R: { elev: 0, roll: 0, ots: 'R' }, }; // Full-frame focal lengths (mm). Vertical fov = 2·atan(12/f) — 24mm-tall sensor. export const FOCALS = [18, 24, 35, 50, 85]; // Lighting looks: key ("sun") color/intensity + boom direction (degrees), hemisphere // ambient, and a background clear color. Values are what applyLight writes verbatim. export const LIGHTS = { day: { sun: { color: '#fff6e4', intensity: 3.0, elev: 55, azim: 35 }, ambient: { color: '#dfe9ff', intensity: 1.15 }, bg: '#8fb0d6' }, golden_hour: { sun: { color: '#ffb469', intensity: 2.6, elev: 9, azim: -55 }, ambient: { color: '#ffd9b0', intensity: 0.55 }, bg: '#d98a52' }, night: { sun: { color: '#7e93d6', intensity: 0.6, elev: 40, azim: 150 }, ambient: { color: '#31405e', intensity: 0.4 }, bg: '#0a0e1a' }, noir: { sun: { color: '#ffffff', intensity: 3.4, elev: 52, azim: 75 }, ambient: { color: '#1c1c24', intensity: 0.15 }, bg: '#08080a' }, horror: { sun: { color: '#b9d6a4', intensity: 1.7, elev: -30, azim: 10 }, // underlight ambient: { color: '#25302b', intensity: 0.25 }, bg: '#05070a' }, neon: { sun: { color: '#ff4fd8', intensity: 1.9, elev: 25, azim: 120 }, ambient: { color: '#2de2e6', intensity: 0.9 }, bg: '#150420' }, overcast: { sun: { color: '#dfe4ea', intensity: 1.2, elev: 70, azim: 0 }, ambient: { color: '#c7cdd6', intensity: 1.4 }, bg: '#9aa3ad' }, }; // Camera MOVES (M11): each one solves to a START and an END camera state off the same subject. // `kind` selects the solver in presets.js. Amounts are FRAME-RELATIVE (multiples of the framed // half-height / half-width, which is span·H/2 by definition) so a move reads the same on a // close-up and a wide, on an 18mm and an 85mm — never a magic metre count. // dolly push/pull along the camera's own view axis; distance of one shot size (rot+fov fixed) // truck lateral, camera stays PARALLEL (rot fixed); amount = fraction of the half-frame WIDTH // crane vertical, re-aims at the subject; amount = fraction of the half-frame HEIGHT // orbit arc around the subject at constant distance; degrees of sweep // zoom focal length ONLY — pos+rot identical, fov changes (a zoom is not a dolly) // dir: −1 = camera-left / down, +1 = camera-right / up. step: rungs along SHOT_ORDER / FOCALS. export const MOVES = { push_in: { kind:'dolly', step: 1, label:'push in' }, pull_out: { kind:'dolly', step: -1, label:'pull out' }, truck_L: { kind:'truck', dir: -1, amount: 0.55, label:'truck left' }, truck_R: { kind:'truck', dir: 1, amount: 0.55, label:'truck right' }, crane_up: { kind:'crane', dir: 1, amount: 1.2, label:'crane up' }, crane_down: { kind:'crane', dir: -1, amount: 1.2, label:'crane down' }, orbit_L: { kind:'orbit', dir: -1, degrees: 25, label:'orbit left' }, orbit_R: { kind:'orbit', dir: 1, degrees: 25, label:'orbit right' }, zoom_in: { kind:'zoom', step: 1, label:'zoom in' }, zoom_out: { kind:'zoom', step: -1, label:'zoom out' }, }; // Shot ladder, wide → tight. push_in/pull_out step one rung along it; ends clamp (see DOLLY_CLAMP). export const SHOT_ORDER = ['EWS', 'WS', 'MWS', 'MS', 'MCU', 'CU', 'ECU']; // Move timing: `inout` for anything with a human on the wheel, `linear` for mechanical/motion-control. export const EASES = ['inout', 'linear']; export const MOVE_MAX_DUR = 6; // seconds — default duration is the scene's remainder, capped here export const DOLLY_CLAMP = 1.4; // already at ECU/EWS → dolly this factor closer (÷) / farther (×) // Blocking marks, stage-relative. pos marks move (keep current y); face marks rotate. export const MARKS = { center: { pos: [0, 0, 0] }, two_shot_L: { pos: [-0.7, 0, 0] }, two_shot_R: { pos: [0.7, 0, 0] }, face_camera: { face: 'camera' }, face_other: { face: 'other' }, };