cocos-farm/tools/render_canvas_preview.py

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()