Files
VoxelForge/Standalone/Research/WorldAssemblerM3/Linux110/MakeL110ContactSheets.py
T
Fr0zkaandClaude Opus 5.5 bf39712792 passages: smoothed open-air shell joins (L110, v257) - partial, not merged
Bands = end profiles of per-control-span shell capsules (~9.35 m pitch = span spacing).
Shell now smooth-unions adjacent spans over 6 m and reuses the interior chord distance; a 0.60 m
clearance fades in 5-20 m from each mouth. Close-ups: bands reduced but still visible on part of
the exposed shell. Cost vs L109: near-ready +8..+12% (FI 0.88 -> 1.00, TN 0.92 -> 1.01 s @W12).
Audits, Clang/GCC determinism, fast tier and Linux UE build pass. Kept on its own branch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
2026-10-29 16:07:52 +01:00

56 lines
2.2 KiB
Python

#!/usr/bin/env python3
"""Build review sheets for every L110 before/after passage capture."""
from __future__ import annotations
import subprocess
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
CAPTURES = Path(__file__).resolve().parent / "captures"
SHEETS = CAPTURES / "contact-sheets"
GROUPS = {
"openair-view-1": ("*-openair-*-view-1.png", "3x3"),
"openair-view-2": ("*-openair-*-view-2.png", "3x3"),
"openair-view-3": ("*-openair-*-view-3.png", "3x3"),
"openair-closeups": ("*-openair-*-closeup-*.png", "3x4"),
"mouths": ("*-*-*-*-mouth.png", "3x2"),
"stations": ("*-station-*.png", "3x3"),
}
def build(variant: str, group: str, pattern: str, layout: str) -> int:
files = sorted((CAPTURES / variant).glob(pattern))
if not files:
raise RuntimeError(f"no images matched {variant}/{pattern}")
columns, rows = (int(value) for value in layout.split("x"))
scale = "480:270"
output = SHEETS / f"{variant}-{group}.png"
command = ["ffmpeg", "-hide_banner", "-loglevel", "error", "-y",
"-f", "image2", "-pattern_type", "glob", "-framerate", "1",
"-i", str(CAPTURES / variant / pattern), "-vf",
f"scale={scale},tile={columns}x{rows}:nb_frames={columns*rows}:padding=6:margin=6",
"-frames:v", "1", str(output)]
result = subprocess.run(command, cwd=ROOT, check=False,
stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
if result.returncode:
raise RuntimeError(f"ffmpeg failed for {variant}/{group}: {result.stderr.strip()}")
return len(files)
def main() -> int:
SHEETS.mkdir(parents=True, exist_ok=True)
rows = []
for variant in ("before", "after"):
for group, (pattern, layout) in GROUPS.items():
count = build(variant, group, pattern, layout)
rows.append(f"- [{variant} {group}]({variant}-{group}.png) — {count} views")
(SHEETS / "INDEX.md").write_text("# L110 capture contact sheets\n\n"
+ "\n".join(rows) + "\n", encoding="utf-8")
print(f"contact sheets={len(rows)} captures={sum(
int(row.rsplit('— ', 1)[1].split()[0]) for row in rows)}")
return 0
if __name__ == "__main__":
raise SystemExit(main())