94 lines
3.8 KiB
Python
94 lines
3.8 KiB
Python
#!/usr/bin/env python3
|
|
"""Render a diagnostic first-stage preview from source evidence."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
from PIL import Image, ImageDraw, ImageFont
|
|
|
|
|
|
PROJECT = Path(__file__).resolve().parents[1]
|
|
SOURCE_NODES = PROJECT / "source_export/source_nodes.json"
|
|
OUT_DIR = PROJECT / "artifacts/canvas_preview"
|
|
|
|
|
|
def read_json(path: Path) -> Any:
|
|
with path.open("r", encoding="utf-8") as fh:
|
|
return json.load(fh)
|
|
|
|
|
|
def cocos_to_image(point: dict[str, float], width: int, height: int) -> tuple[float, float]:
|
|
return width / 2 + float(point.get("x", 0)), height / 2 - float(point.get("y", 0))
|
|
|
|
|
|
def draw_centered_rect(draw: ImageDraw.ImageDraw, node: dict[str, Any], canvas: tuple[int, int], outline: str, fill: str | None = None) -> None:
|
|
pos = node.get("world_position_transformed") or node.get("position") or {"x": 0, "y": 0}
|
|
size = node.get("primary_size") or node.get("content_size") or {"width": 40, "height": 40}
|
|
cx, cy = cocos_to_image(pos, canvas[0], canvas[1])
|
|
w = float(size.get("width") or 40)
|
|
h = float(size.get("height") or 40)
|
|
box = [cx - w / 2, cy - h / 2, cx + w / 2, cy + h / 2]
|
|
draw.rectangle(box, outline=outline, fill=fill, width=2)
|
|
|
|
|
|
def draw_land_cell(draw: ImageDraw.ImageDraw, node: dict[str, Any], canvas: tuple[int, int]) -> None:
|
|
pos = node.get("world_position_transformed") or {"x": 0, "y": 0}
|
|
cx, cy = cocos_to_image(pos, canvas[0], canvas[1])
|
|
size = node.get("primary_size") or {"width": 130, "height": 130}
|
|
w = float(size.get("width") or 130)
|
|
h = float(size.get("height") or 130) * 0.5
|
|
pts = [(cx, cy - h / 2), (cx + w / 2, cy), (cx, cy + h / 2), (cx - w / 2, cy)]
|
|
draw.polygon(pts, outline="#2f7d32", fill="#86bf6a")
|
|
|
|
|
|
def main() -> None:
|
|
data = read_json(SOURCE_NODES)
|
|
width = int(data["design_resolution"]["width"])
|
|
height = int(data["design_resolution"]["height"])
|
|
img = Image.new("RGB", (width, height), "#d7efff")
|
|
draw = ImageDraw.Draw(img, "RGBA")
|
|
|
|
for node in data["background_nodes"]:
|
|
draw_centered_rect(draw, node, (width, height), "#7a9f4b", "#b7d77a55")
|
|
|
|
for node in data["land_candidates"]["nodes"]:
|
|
draw_land_cell(draw, node, (width, height))
|
|
|
|
for node in data["house_and_doghouse"]["doghouse_nodes"]:
|
|
draw_centered_rect(draw, node, (width, height), "#7a3f16", "#b8753555")
|
|
|
|
for record in data["house_and_doghouse"]["skin_cfg_default_records"]:
|
|
pos = record.get("position") or {"x": 0, "y": 0}
|
|
cx, cy = cocos_to_image(pos, width, height)
|
|
label = str(record.get("skin_name", record.get("id")))
|
|
draw.rectangle([cx - 55, cy - 35, cx + 55, cy + 35], outline="#5c2d91", fill="#c7a5ff66", width=2)
|
|
draw.text((cx - 48, cy - 8), label, fill="#2b1545")
|
|
|
|
for node in data["bottom_menu_nodes"]:
|
|
draw_centered_rect(draw, node, (width, height), "#1f4f99", "#6fa8ff55")
|
|
|
|
draw.rectangle([0, 0, width - 1, height - 1], outline="#222222", width=2)
|
|
draw.text((12, 12), "Stage1 diagnostic preview - source evidence only", fill="#111111")
|
|
draw.text((12, 34), f"land candidates: {data['land_candidates']['source_count']} / visible 24 blocked", fill="#8a1f11")
|
|
|
|
OUT_DIR.mkdir(parents=True, exist_ok=True)
|
|
png = OUT_DIR / "stage1_source_preview.png"
|
|
report = OUT_DIR / "stage1_source_preview_report.json"
|
|
img.save(png)
|
|
report.write_text(json.dumps({
|
|
"status": "generated",
|
|
"image": str(png),
|
|
"resolution": {"width": width, "height": height},
|
|
"land_candidates_rendered": data["land_candidates"]["source_count"],
|
|
"visible_land_selection": data["land_candidates"]["selection_status"],
|
|
"note": "Diagnostic rendering only; it does not define final Cocos coordinates.",
|
|
}, indent=2) + "\n", encoding="utf-8")
|
|
print(png)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|