Engine: tooling/garment-fit/blender_batch_fit_skinned.py (G1 — the skinned Surface-Deform batch the old script couldn't produce; self-check green), blender_author_offset_shell.py (route c: garment shells from OUR body segments, weights inherited by construction, bone-plane cuts, procedural RGBA region mask, UV2 chest channel), make_logo.py. Shader: toon_garment.gdshader — channel-blended 4-region tint + UV2 logo composited after tint / before toon shading. Proof: tshirt_modern fitted to the six healthy bodies, manifest entry with style:modern + logo_capable, thrds wordmark, 18-assertion test suite, 216-capture chromakey QA. Key finding (Q-060 evidence): single-reference SD-fit of an offset-shell degrades on girth-divergent bodies (muscular_m worst) — 24mm standoff tripled headroom but the mechanism limits. Route guidance recorded on T-1089: per-body shell authoring for offset-shell garments; SD-fit for derived/hand-authored ones. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
83 lines
2.9 KiB
Python
83 lines
2.9 KiB
Python
"""
|
|
make_logo.py (T-1089 gap G4 — brand-logo supply stub)
|
|
|
|
Generate a flat 2D wordmark PNG for a clothing brand decal (D-244: flat 2D
|
|
artwork on 3D surfaces). White-on-transparent, ~256x256, drawn with its own
|
|
crisp edge so the inverted-hull character outline (which never samples the
|
|
decal) leaves it untouched. Must stay legible at 16-32 px gameplay zoom (D-044).
|
|
|
|
This is a PLACEHOLDER supply stub. The production path is /image-gen (Gemini)
|
|
per the feasibility §4 pipeline; this script gives the engine a real decal to
|
|
render now, and doubles as the deterministic fallback generator.
|
|
|
|
Run:
|
|
python3 tooling/garment-fit/make_logo.py <text> <out_png> [--size 256]
|
|
|
|
Example (the canonical Braemar fiber co-op, wiki/corporations/thrds.md — always
|
|
lowercase):
|
|
python3 tooling/garment-fit/make_logo.py thrds \
|
|
client/assets/characters/logos/thrds.png
|
|
"""
|
|
import sys
|
|
import os
|
|
from PIL import Image, ImageDraw, ImageFont
|
|
|
|
FONT_CANDIDATES = [
|
|
"/usr/share/fonts/fira-code/FiraCode-Bold.ttf",
|
|
"/usr/share/fonts/adwaita-mono-fonts/AdwaitaMono-Bold.ttf",
|
|
"/usr/share/fonts/dejavu-sans-fonts/DejaVuSans-Bold.ttf",
|
|
]
|
|
|
|
|
|
def load_font(px):
|
|
for path in FONT_CANDIDATES:
|
|
if os.path.isfile(path):
|
|
return ImageFont.truetype(path, px), os.path.basename(path)
|
|
return ImageFont.load_default(), "PIL-default"
|
|
|
|
|
|
def make_logo(text, out_png, size=256):
|
|
img = Image.new("RGBA", (size, size), (0, 0, 0, 0))
|
|
draw = ImageDraw.Draw(img)
|
|
|
|
# Grow the font until the wordmark fills ~82% of the width.
|
|
target_w = int(size * 0.82)
|
|
px = size
|
|
font, font_name = load_font(px)
|
|
for px in range(size, 8, -2):
|
|
font, font_name = load_font(px)
|
|
bbox = draw.textbbox((0, 0), text, font=font)
|
|
if (bbox[2] - bbox[0]) <= target_w and (bbox[3] - bbox[1]) <= int(size * 0.5):
|
|
break
|
|
|
|
bbox = draw.textbbox((0, 0), text, font=font)
|
|
tw, th = bbox[2] - bbox[0], bbox[3] - bbox[1]
|
|
x = (size - tw) / 2 - bbox[0]
|
|
y = (size - th) / 2 - bbox[1]
|
|
# White wordmark, fully opaque.
|
|
draw.text((x, y), text, font=font, fill=(255, 255, 255, 255))
|
|
# A thin underscore bar under the wordmark — reads as a modern corporate mark
|
|
# and gives the decal a stable baseline anchor at low zoom.
|
|
bar_y = y + bbox[3] + int(size * 0.03)
|
|
bar_h = max(2, int(size * 0.02))
|
|
draw.rectangle(
|
|
[(size * 0.12, bar_y), (size * 0.88, bar_y + bar_h)],
|
|
fill=(255, 255, 255, 255),
|
|
)
|
|
|
|
os.makedirs(os.path.dirname(out_png), exist_ok=True)
|
|
img.save(out_png)
|
|
print(f"wrote {out_png} ({size}x{size}, font={font_name}, glyph_px={px})")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
if len(sys.argv) < 3:
|
|
print("Usage: make_logo.py <text> <out_png> [--size N]")
|
|
sys.exit(1)
|
|
text = sys.argv[1]
|
|
out = sys.argv[2]
|
|
size = 256
|
|
if "--size" in sys.argv:
|
|
size = int(sys.argv[sys.argv.index("--size") + 1])
|
|
make_logo(text, out, size)
|