76 lines
3.4 KiB
Python
76 lines
3.4 KiB
Python
"""Render recorded probe geometry for inspection; this is not a desktop screenshot.
|
|
|
|
Usage: python render_probe.py PROBE_STDOUT_JSONL NEW_OUTPUT_DIRECTORY
|
|
Uses one scale across every result for each fixture; input geometry is unchanged.
|
|
"""
|
|
from pathlib import Path
|
|
import hashlib
|
|
import json
|
|
import sys
|
|
|
|
from PIL import Image, ImageDraw, ImageFont
|
|
|
|
|
|
def geometries(value, name=""):
|
|
if not isinstance(value, dict):
|
|
return
|
|
if "routed_edges" in value and "projected_nodes" in value:
|
|
yield name, value
|
|
return
|
|
for key, child in value.items():
|
|
yield from geometries(child, f"{name}.{key}".strip("."))
|
|
|
|
|
|
source, output = map(Path, sys.argv[1:])
|
|
output.mkdir(parents=True, exist_ok=False)
|
|
font = ImageFont.truetype("C:/Windows/Fonts/arial.ttf", 13)
|
|
files = []
|
|
for line in source.read_text(encoding="utf-8").splitlines():
|
|
item = json.loads(line)
|
|
fixture = item.get("fixture")
|
|
if not fixture:
|
|
continue
|
|
variants = list(geometries(item))
|
|
if not variants:
|
|
continue
|
|
largest_width = max(g["drawing_bounds"]["size"]["width"] for _, g in variants)
|
|
largest_height = max(g["drawing_bounds"]["size"]["height"] for _, g in variants)
|
|
scale = min(1.0, 1140 / max(1, largest_width), 690 / max(1, largest_height))
|
|
for name, geometry in variants:
|
|
canvas = Image.new("RGB", (1200, 800), "white")
|
|
draw = ImageDraw.Draw(canvas)
|
|
origin = geometry["drawing_bounds"]["position"]
|
|
|
|
def point(position):
|
|
return (30 + (position["x"] - origin["x"]) * scale,
|
|
70 + (position["y"] - origin["y"]) * scale)
|
|
|
|
def rectangle(position, size):
|
|
x, y = point(position)
|
|
return x, y, x + size["width"] * scale, y + size["height"] * scale
|
|
|
|
draw.text((20, 12), f"{fixture} / {name} | geometry only, not native UI", font=font, fill="black")
|
|
draw.text((20, 32), f"Fixed fixture scale {scale:.4f}; text below identifies bounds, not glyph measurements", font=font, fill="black")
|
|
for node in sorted(geometry["projected_nodes"], key=lambda n: (n["depth"], n["id"])):
|
|
box = rectangle(node["position"], node["size"])
|
|
draw.rectangle(box, fill="#eef2f6", outline="#687787", width=1)
|
|
draw.text((box[0] + 3, box[1] + 3), node["id"][-5:], font=font, fill="#334155")
|
|
for edge in geometry["routed_edges"]:
|
|
points = [point(p) for p in edge["points"]]
|
|
if len(points) > 1:
|
|
draw.line(points, fill="#245c88", width=2)
|
|
label = edge["label"]
|
|
if label["text"]:
|
|
box = rectangle(label["position"], label["size"])
|
|
draw.rectangle(box, outline="#ad5715", width=1)
|
|
target = output / f"{fixture}-{name.replace('.', '-')}.png"
|
|
canvas.save(target)
|
|
files.append({"name": target.name, "bytes": target.stat().st_size,
|
|
"sha256": hashlib.sha256(target.read_bytes()).hexdigest(), "scale": scale})
|
|
(output / "manifest.json").write_text(json.dumps({
|
|
"input_sha256": hashlib.sha256(source.read_bytes()).hexdigest(),
|
|
"renderer_sha256": hashlib.sha256(Path(__file__).read_bytes()).hexdigest(),
|
|
"scope": "Offline schematic of recorded core geometry; no native or font-rendering acceptance.",
|
|
"images": files}, indent=2) + "\n", encoding="utf-8")
|
|
print(f"Rendered {len(files)} geometry records without modifying the probe output.")
|