#!/usr/bin/env python3 """Render the 23 L125 camera poses with the L130 evaluator and compare them.""" from __future__ import annotations import html import importlib.util import json import shutil from pathlib import Path ROOT = Path(__file__).resolve().parents[4] RESEARCH = Path(__file__).resolve().parent CAPTURES = RESEARCH / "captures" SAVED = ROOT / "Saved/Linux/l130/captures" BASELINE_INDEX = (ROOT / "Standalone/Research/WorldAssemblerM3/Linux119" / "captures/L119/capture-index.json") REFERENCE = ROOT / "Standalone/Research/WorldAssemblerM3/Linux125/captures/L125" LAYOUT = ROOT / "Saved/Linux/l130/hashes/owner4-surface-world-w12-clang.layout.json" HEADLESS = ROOT / "Saved/Linux/l130/bin/VoxelForgeCoreHeadless-l130-clang" RENDERER = ROOT / "Saved/Linux/l130/bin/VoxelForgeOffscreenCapture-l130-clang" HELPER_PATH = ROOT / "Standalone/Research/WorldAssemblerM3/Linux113/RunL113Captures.py" def main() -> int: if any(not path.is_file() for path in (BASELINE_INDEX, REFERENCE / "passage-0-1-0-rock-floor-station-1.png", LAYOUT, HEADLESS, RENDERER)): raise SystemExit("missing pose, layout, or L130 capture input") spec = importlib.util.spec_from_file_location("l125_capture_runner", ROOT / "Standalone/Research/WorldAssemblerM3/Linux125/RunL125Captures.py") if spec is None or spec.loader is None: raise SystemExit("cannot load the verified L125 pose set") runner = importlib.util.module_from_spec(spec) spec.loader.exec_module(runner) runner.RESEARCH = RESEARCH runner.CAPTURES = CAPTURES runner.SAVED = SAVED runner.LAYOUT = LAYOUT runner.CLANG = HEADLESS runner.RENDERER = RENDERER runner.HELPERS.RESEARCH = RESEARCH runner.HELPERS.CAPTURES = CAPTURES runner.HELPERS.SAVED = SAVED runner.HELPERS.BINARIES = {"L125": HEADLESS} runner.HELPERS.RENDERERS = {"L125": RENDERER} if runner.main() != 0: raise SystemExit("L130 pose generation failed") new_captures = CAPTURES / "L125" new_saved = SAVED / "L125" index_path = new_captures / "capture-index.json" data = json.loads(index_path.read_text()) if len(data["captures"]) != 23: raise SystemExit(f"expected 23 poses, found {len(data['captures'])}") identical = 0 sections = [] for row in data["captures"]: name = row["name"] old_image = REFERENCE / f"{name}.png" new_image = new_captures / f"{name}.png" if not old_image.is_file() or not new_image.is_file(): raise SystemExit(f"missing comparison image for {name}") same = old_image.read_bytes() == new_image.read_bytes() identical += int(same) row["variant"] = "L130-v227" row["capture"] = row["capture"].replace("captures/L125/", "captures/L130/") row["scene"] = row["scene"].replace("captures/L125/", "captures/L130/") row["logs"] = [path.replace("captures/L125/", "captures/L130/") for path in row.get("logs", [])] sections.append(f"

{html.escape(row['passage'])} — " f"{html.escape(row.get('station', ''))}

{html.escape(row['kind'])} · " f"byte identical: {str(same).lower()}

" f"
L125 v225
" f"
L130 v227
" "
") if identical != 23: raise SystemExit(f"only {identical}/23 L125 pose captures are byte-identical") index_path.write_text(json.dumps(data, indent=2) + "\n") old_captures, old_saved = CAPTURES / "L130", SAVED / "L130" if old_captures.exists(): shutil.rmtree(old_captures) if old_saved.exists(): shutil.rmtree(old_saved) new_captures.rename(old_captures) new_saved.rename(old_saved) page = ("L130 passage captures" "" "

L125 v225 vs L130 v227: passage captures

" + "\n".join(sections)) (CAPTURES / "COMPARE.html").write_text(page) print(f"[L130PoseCapture] poses=23 byte_identical={identical}/23", flush=True) return 0 if __name__ == "__main__": raise SystemExit(main())