#!/usr/bin/env python3 """Generate crop root anchors from transparent PNG bounds.""" from __future__ import annotations from pathlib import Path from PIL import Image ROOT = Path(__file__).resolve().parents[1] CROP_DIR = ROOT / "CocosFarm" / "assets" / "resources" / "textures" / "stage1" OUTPUT = ROOT / "CocosFarm" / "assets" / "scripts" / "mock" / "Stage1CropFootAnchorMock.ts" def fmt(value: float) -> str: if value == int(value): return str(int(value)) return str(round(value, 3)).rstrip("0").rstrip(".") def main() -> int: anchors: dict[str, tuple[float, float]] = {} for path in sorted(CROP_DIR.glob("Crop_*.png")): image = Image.open(path).convert("RGBA") alpha = image.getchannel("A") bounds = alpha.getbbox() if bounds is None: continue left, top, right, bottom = bounds width, height = image.size if "Seed" in path.stem: foot_x = (left + right) / 2 - width / 2 foot_y = height / 2 - bottom else: visible_height = bottom - top root_band_height = max(4, round(visible_height * 0.14)) root_top = max(top, bottom - root_band_height) root_bounds = alpha.crop((0, root_top, width, bottom)).getbbox() if root_bounds is None: continue root_left, root_band_top, root_right, root_band_bottom = root_bounds root_top_world = root_top + root_band_top root_bottom_world = root_top + root_band_bottom foot_x = (root_left + root_right) / 2 - width / 2 foot_y = height / 2 - (root_top_world + root_bottom_world) / 2 anchors[path.stem] = (foot_x, foot_y) lines = [ "export interface Stage1CropFootAnchor {", " footX: number;", " footY: number;", "}", "", "export const STAGE1_CROP_FOOT_ANCHORS: Record = {", ] for index, (asset_id, (foot_x, foot_y)) in enumerate(anchors.items()): suffix = "," if index < len(anchors) - 1 else "" lines.extend( [ f' "{asset_id}": {{', f' "footX": {fmt(foot_x)},', f' "footY": {fmt(foot_y)}', f" }}{suffix}", ] ) lines.append("};") OUTPUT.write_text("\n".join(lines) + "\n", encoding="utf-8") print(f"wrote {OUTPUT} ({len(anchors)} anchors)") return 0 if __name__ == "__main__": raise SystemExit(main())