"""Drawn textures for the robot cell (system Python + PIL): python rc_textures.py -> ../textures/ T_RC_Decals.png 2048 x 1024 RGBA atlas (regions: rc_build.py DECALS): the emblem on the left leaf's glass (a ring with a generic arm mark and "CELL 01" in place of the picture's maker logo), the four button labels, "EMERGENCY STOP", and the robot's forearm lettering (black, generic "R-60" in place of a maker name) T_RC_Screen_UI.png 600 x 810: the control pod's screen, laid out as the picture's (title, status READY, a robot pictogram, a grid of round icons); "CELL 01" in place of the picture's product name """ import os, math from PIL import Image, ImageDraw, ImageFont HERE = os.path.dirname(os.path.abspath(__file__)) OUT = os.path.join(os.path.dirname(HERE), "textures") os.makedirs(OUT, exist_ok=True) FD = r"C:\Windows\Fonts" def font(name, size, var=None): f = ImageFont.truetype(os.path.join(FD, name), size) if var: f.set_variation_by_name(var) return f def rgn_box(r, W=2048, H=1024): u0, v0, u1, v1 = r return (int(u0 * W), int((1 - v1) * H), int(u1 * W), int((1 - v0) * H)) def centred(d, box, text, f, fill, spacing=0): x0, y0, x1, y1 = box if spacing: widths = [d.textlength(ch, font=f) for ch in text] tw = sum(widths) + spacing * (len(text) - 1) bb = d.textbbox((0, 0), text, font=f) x = x0 + (x1 - x0 - tw) / 2 y = y0 + (y1 - y0 - (bb[3] - bb[1])) / 2 - bb[1] for ch, w in zip(text, widths): d.text((x, y), ch, font=f, fill=fill) x += w + spacing return bb = d.textbbox((0, 0), text, font=f) d.text((x0 + (x1 - x0 - (bb[2] - bb[0])) / 2 - bb[0], y0 + (y1 - y0 - (bb[3] - bb[1])) / 2 - bb[1]), text, font=f, fill=fill) DECALS = {"emblem": (0.0, 0.5, 0.25, 1.0), "START": (0.25, 0.875, 0.375, 0.9375), "STOP": (0.375, 0.875, 0.5, 0.9375), "LIGHT": (0.25, 0.8125, 0.375, 0.875), "SERVICE": (0.375, 0.8125, 0.5, 0.875), "ESTOP": (0.25, 0.6875, 0.5, 0.75), "robot": (0.5, 0.5, 1.0, 1.0)} # ------------------------------------------------------------------ decal atlas S = 2 # supersample big = Image.new("RGBA", (2048 * S, 1024 * S), (0, 0, 0, 0)) d = ImageDraw.Draw(big) WH = (236, 238, 240, 255) def B(name): return tuple(c * S for c in rgn_box(DECALS[name])) # emblem: ring + a generic arm mark, "CELL 01" letter-spaced under it (the picture: ring 50 px, text 105 px wide) x0, y0, x1, y1 = B("emblem") cx, cy, R = (x0 + x1) / 2, y0 + (y1 - y0) * 0.36, (x1 - x0) * 0.155 w = 9 * S d.ellipse((cx - R, cy - R, cx + R, cy + R), outline=WH, width=w) pts = [(cx - R * 0.48, cy + R * 0.50), (cx - R * 0.18, cy - R * 0.25), (cx + R * 0.38, cy - R * 0.05)] d.line(pts, fill=WH, width=w, joint="curve") for p in pts: d.ellipse((p[0] - w * 0.9, p[1] - w * 0.9, p[0] + w * 0.9, p[1] + w * 0.9), fill=WH) d.line([(cx + R * 0.38, cy - R * 0.05), (cx + R * 0.38, cy + R * 0.30)], fill=WH, width=w) centred(d, (x0, y0 + (y1 - y0) * 0.60, x1, y0 + (y1 - y0) * 0.74), "CELL 01", font("bahnschrift.ttf", 46 * S, "Light"), WH, spacing=14 * S) # button labels and the emergency stop label (white, as the picture's) for k in ("START", "STOP", "LIGHT", "SERVICE"): centred(d, B(k), k, font("bahnschrift.ttf", 40 * S, "SemiBold Condensed"), WH) centred(d, B("ESTOP"), "EMERGENCY STOP", font("bahnschrift.ttf", 40 * S, "SemiBold Condensed"), WH, spacing=2 * S) # the robot's forearm lettering: black, bold, generic x0, y0, x1, y1 = B("robot") centred(d, (x0 + 40 * S, y0 + 70 * S, x1 - 40 * S, y1 - 70 * S), "R-60", font("bahnschrift.ttf", 300 * S, "Bold"), (14, 14, 15, 255)) big.resize((2048, 1024), Image.LANCZOS).save(os.path.join(OUT, "T_RC_Decals.png")) # ------------------------------------------------------------------ screen UI (portrait) W, H = 600, 810 S2 = 2 im = Image.new("RGB", (W * S2, H * S2), (5, 8, 13)) d = ImageDraw.Draw(im) for y in range(H * S2): # a faint vertical gradient t = y / (H * S2) d.line([(0, y), (W * S2, y)], fill=(int(6 + 6 * t), int(10 + 9 * t), int(17 + 14 * t))) s = lambda *v: tuple(int(x * S2) for x in v) d.text(s(46, 46), "CELL 01", font=font("segoeuib.ttf", 66 * S2), fill=(232, 236, 240)) d.line(s(500, 70, 530, 60), fill=(150, 160, 175), width=3 * S2) d.line(s(500, 150, 525, 158), fill=(150, 160, 175), width=3 * S2) d.text(s(48, 165), "status", font=font("segoeui.ttf", 34 * S2), fill=(70, 150, 200)) d.text(s(46, 205), "READY", font=font("segoeuib.ttf", 64 * S2), fill=(200, 222, 70)) d.line(s(46, 300, 554, 300), fill=(35, 55, 75), width=2 * S2) # robot pictogram (lower left) base = (150, 640) pts = [base, (140, 520), (230, 440), (300, 500)] d.rectangle(s(95, 640, 205, 670), fill=(40, 70, 95)) d.line([s(*p) for p in pts], fill=(120, 165, 200), width=14 * S2, joint="curve") for p in pts[1:]: d.ellipse(s(p[0] - 16, p[1] - 16, p[0] + 16, p[1] + 16), fill=(70, 110, 145)) d.line([s(300, 500), s(305, 545)], fill=(170, 200, 225), width=8 * S2) # icon grid (right) for i in range(2): for j in range(3): cx, cy = 400 + i * 95, 400 + j * 105 d.ellipse(s(cx - 34, cy - 34, cx + 34, cy + 34), outline=(60, 120, 175), width=3 * S2) if (i + j) % 2: d.ellipse(s(cx - 12, cy - 12, cx + 12, cy + 12), fill=(80, 150, 205)) else: d.line([s(cx - 16, cy), s(cx + 16, cy)], fill=(80, 150, 205), width=4 * S2) d.line([s(cx, cy - 16), s(cx, cy + 16)], fill=(80, 150, 205), width=4 * S2) d.rectangle(s(0, 740, 600, 810), fill=(10, 16, 26)) for k in range(5): d.ellipse(s(70 + k * 115 - 9, 775 - 9, 70 + k * 115 + 9, 775 + 9), fill=(55, 95, 135) if k else (90, 200, 120)) im.resize((W, H), Image.LANCZOS).save(os.path.join(OUT, "T_RC_Screen_UI.png")) print("TEXTURES OK", OUT)