"""Offline art normalization to the exact assembly table and ball anchors.""" from pathlib import Path import struct from PIL import Image ROOT = Path(__file__).resolve().parents[2] SOURCE = ROOT / "assets/sprites/pyramid" OUT = ROOT / "assets/animations/pyramid" def pack(name, frames): w, h = frames[0].size payload = b"".join(frame.tobytes("raw", "BGRA") for frame in frames) (OUT / name).write_bytes(struct.pack("<4s4I", b"ANM1", w, h, len(frames), len(payload)) + payload) def main(): OUT.mkdir(parents=True, exist_ok=True) source = Image.open(SOURCE / "table-v2.png").convert("RGBA") # Nine-slice-style normalization keeps the mouths and cushion noses aligned # with the shared logical cloth rectangle, rather than scaling the whole art. source_x = [0, 128, 160, 723, 1290, 1320, 1448] source_y = [0, 197, 220, 733, 747, 1086] target_x = [0, 60, 61, 320, 579, 580, 640] target_y = [0, 100, 101, 359, 360, 480] frame = Image.new("RGBA", (640, 480)) for row in range(len(target_y) - 1): for col in range(len(target_x) - 1): cell = source.crop((round(source_x[col] * source.width / 1448), round(source_y[row] * source.height / 1086), round(source_x[col + 1] * source.width / 1448), round(source_y[row + 1] * source.height / 1086))) cell = cell.resize((target_x[col + 1] - target_x[col], target_y[row + 1] - target_y[row]), Image.Resampling.NEAREST) frame.paste(cell, (target_x[col], target_y[row])) frame.putalpha(255) pack("table-v2.bin", [frame]) sheet = Image.open(SOURCE / "balls-v1.png").convert("RGBA") frames = [] for i in range(8): col, row = i % 4, i // 4 cell = sheet.crop((round(col * sheet.width / 4), round(row * sheet.height / 2), round((col + 1) * sheet.width / 4), round((row + 1) * sheet.height / 2))) # Ignore faint generator fringe outside the actual circular ball. cell.putalpha(cell.getchannel("A").point(lambda a: 255 if a >= 192 else 0)) cell = cell.crop(cell.getchannel("A").getbbox()) cell.thumbnail((10, 10), Image.Resampling.LANCZOS) frame = Image.new("RGBA", (12, 12)) frame.paste(cell, ((12 - cell.width) // 2, (12 - cell.height) // 2)) frames.append(frame) pack("balls-v1.bin", frames) print("Packed anchored 640x480 table and eight 12x12 Russian ball poses") if __name__ == "__main__": main()