"""Writes room names onto the plan renders (previews/Vessel_01..., Vessel_02..., Vessel_35...) -> *_labelled.png. python build/annotate_plan.py Positions are ship metres; each plan's .png.json (written by bl_render.py) says what the picture covers. """ import json import os from PIL import Image, ImageDraw, ImageFont HERE = os.path.dirname(os.path.abspath(__file__)) PREV = os.path.join(os.path.dirname(HERE), "previews") LABELS = { "Vessel_01_plan_main_deck": [ ("ENGINE\nCONTROL", -44.5, 0.0), ("REACTOR\nROOM", -36.6, 0.0), ("WORKS HALL\ncargo ore processing fabrication", -20.4, 0.0), ("boarding gate\n(port service hatch)", -20.4, 9.6), ("service gate", -20.4, -9.6), ("SUB BAY (moon pool)\nSeason 1 walls", -4.6, 5.2), ("well", 6.6, -1.9), ("launch arm\n(Tender at BOARD)", 3.0, -9.75), ("stair to the\ncommand deck", 12.0, 7.7), ("FORE PASSAGE", 26.7, 0.0), ("GALLEY", 21.3, 5.2), ("QUARTERS A", 30.3, 5.2), ("HABITATION", 21.3, -5.2), ("QUARTERS B", 30.3, -5.2), ("SENSOR ROOM", 38.2, 3.4), ("LIFT\nROOM", 38.2, -5.2), ], "Vessel_02_plan_command_deck": [ ("(works hall below)", -20.4, 9.6), ("TENDER CAROUSEL\nhub on the centre line", 3.0, 9.3), ("gallery", 13.8, -5.0), ("CONTROL ROOM\nCORRIDOR", 19.4, 0.0), ("CREW\nQUARTERS", 23.06, 5.3), ("CAPTAIN'S\nQUARTERS", 24.9, -5.5), ("BRIDGE", 34.2, 5.6), ("command floor and terminal\n(Season 1)", 40.6, -8.2), ], "Vessel_35_quarters_plan": [ ("CONTROL ROOM CORRIDOR (level floor, no stairs)", 23.06, 0.0), ("CREW QUARTERS", 23.06, 5.35), ("CAPTAIN'S QUARTERS", 22.6, -5.2), ("berths 1 to 6, each with a locker", 23.06, 8.75), ("stores and cabinets", 19.2, 2.72), ("mess bench", 28.46, 2.72), ("bunk", 24.9, -8.75), ("work station", 21.25, -8.75), ("bench, chart", 21.5, -2.72), ("mess", 17.66, -8.75), ("lounge", 28.43, -8.75), ("lockers", 14.9, -4.75), ("window", 31.15, -4.75), ("door, landing", 24.86, -2.72), ("gallery", 14.9, -7.0), ("bridge", 31.2, 7.0), ], } LEGEND = "Season 2: engineering spaces, passages, the rooms' structure. Season 1: sub bay walls and lettering, bridge command floor, furniture." def font(size): for name in ("arialbd.ttf", "arial.ttf", "DejaVuSans-Bold.ttf"): try: return ImageFont.truetype(name, size) except OSError: continue return ImageFont.load_default() for base, labels in LABELS.items(): png = os.path.join(PREV, base + ".png") if not os.path.isfile(png): continue with open(png + ".json", "r", encoding="utf-8") as f: cov = json.load(f) im = Image.open(png).convert("RGB") W, H = im.size d = ImageDraw.Draw(im) ft = font(max(14, W // 150)) for text, x, y in labels: px = (x - (cov["cx"] - cov["width"] / 2)) / cov["width"] * W py = ((cov["cy"] + cov["height"] / 2) - y) / cov["height"] * H box = d.multiline_textbbox((px, py), text, font=ft, anchor="mm", align="center", spacing=2) d.rectangle([box[0] - 5, box[1] - 3, box[2] + 5, box[3] + 3], fill=(12, 16, 22)) d.multiline_text((px, py), text, font=ft, fill=(255, 235, 150), anchor="mm", align="center", spacing=2) d.text((12, H - 26), "bow to the right, port at the top. " + LEGEND, font=font(max(12, W // 200)), fill=(40, 40, 40)) out = os.path.join(PREV, base + "_labelled.png") im.save(out) print("wrote", out)