#!/usr/bin/env python3 """Render the fixed 17 m FI mouth overview as a small JPEG.""" import math import os import subprocess import time from pathlib import Path ROOT = Path(__file__).resolve().parents[4] OUT = Path(os.environ.get("VOXELFORGE_L37_OUT", ROOT / "Saved/Linux/l37")) CAPTURES = Path(os.environ.get("VOXELFORGE_L37_CAPTURE_DIR", Path(__file__).resolve().parent / "captures")) MESH = Path(os.environ.get("VOXELFORGE_L37_MESH", OUT / "owner-fi-ground-mouth.vfmesh")) RENDERER = Path(os.environ.get("VOXELFORGE_L37_RENDERER", OUT / "bin/VoxelForgeOffscreenCapture-l37-final-clang")) PNG = OUT / "capture-png/owner-fi-ground-mouth-overview.png" JPEG = CAPTURES / "owner-fi-ground-mouth-overview.jpg" MOUTH = (5.600, 0.445, -55.156) CAMERA = (MOUTH[0] - 9.75, MOUTH[1] - 11.4, MOUTH[2] + 8.0) TARGET = (6.298233, 1.137233, -55.468264) FOV = 80.0 def normalize(vector): magnitude = math.sqrt(sum(value * value for value in vector)) return tuple(value / magnitude for value in vector) def camera_angles(camera, target): delta = tuple(target[i] - camera[i] for i in range(3)) horizontal = math.hypot(delta[0], delta[1]) return math.degrees(math.atan2(delta[0], delta[1])), math.degrees( math.atan2(delta[2], horizontal)) def fmt(values): return ",".join(f"{value:.6f}" for value in values) def run(name, command): log = OUT / f"capture-overview-{name}.log" wrapped = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--", "timeout", "1800s", *map(str, command)] started = time.monotonic() with log.open("w", encoding="utf-8") as stream: result = subprocess.run(wrapped, cwd=ROOT, stdout=stream, stderr=subprocess.STDOUT, timeout=1800, check=False) elapsed = time.monotonic() - started print(f"[L37OverviewCaptureRun] step={name} elapsed={elapsed:.1f}s " f"exit={result.returncode} log={log}", flush=True) if result.returncode != 0: raise SystemExit(f"{name} failed; see {log}") def main(): if not MESH.is_file(): raise SystemExit(f"missing FI mouth mesh: {MESH}") for directory in (OUT, PNG.parent, CAPTURES): directory.mkdir(parents=True, exist_ok=True) yaw, pitch = camera_angles(CAMERA, TARGET) run("render", [RENDERER, MESH, PNG, "--size=1600x900", f"--fov-deg={FOV}", f"--camera-m={fmt(CAMERA)}", f"--yaw-deg={yaw:.6f}", f"--pitch-deg={pitch:.6f}", "--tile-skirts=off", "--shade=readable", "--headlamp=on", "--height-tint=on", f"--height-center-m={TARGET[2]:.6f}", "--height-range-m=8", "--tile-edges=off", f"--focus-region={fmt((*MOUTH, 20.0))}"]) run("jpeg", ["ffmpeg", "-nostdin", "-hide_banner", "-loglevel", "error", "-y", "-i", PNG, "-frames:v", "1", "-q:v", "4", JPEG]) size = JPEG.stat().st_size if JPEG.exists() else 0 distance = math.dist(CAMERA, MOUTH) print(f"[L37OverviewCapture] file={JPEG.relative_to(ROOT)} camera_to_mouth_m=" f"{distance:.3f} camera_m=({fmt(CAMERA)}) target_m=({fmt(TARGET)}) " f"fov_deg={FOV:.1f} resolution=1600x900 bytes={size} " f"status={'PASS' if 0 < size <= 300_000 else 'FAIL'}", flush=True) raise SystemExit(0 if 0 < size <= 300_000 else 1) if __name__ == "__main__": main()