#!/usr/bin/env python3 """The SimWorld emblem as the site's logo (user, 2026-10-17: "网页的logo换成SimWorld的logo"). - assets/simworld-mark.png: the emblem the user supplied (2000 px, transparent), cropped to its alpha bounds, centred on a square, scaled to 192 px and stored with a 256-colour palette (10 kB; no visible difference at this size). - Every page header: the emblem replaces the wireframe cube in front of "Code4Scene"; the name and "by SimWorld" stay. - Every page's favicon, plus an apple-touch icon: the emblem instead of assets/code4scene-mark.svg (kept in the repo). - assets/buildingbench.css: the mark's size, 30 px (26 px on phones), where the cube was 26 x 29 px. Pages are edited as text, line for line, so nothing else in them changes (paper.html is hand-written for Scholar). scripts/t2s_gallery.py writes the same header and icons into the gallery pages it builds. Re-runnable. python3 scripts/simworld_logo.py [--src /path/to/simworld-emblem.png] """ from __future__ import annotations import argparse import re from pathlib import Path from PIL import Image ROOT = Path(__file__).resolve().parent.parent MARK = "assets/simworld-mark.png" V = "20261017logo" CUBE = re.compile(r'', re.S) OLD_ICON = re.compile(r']*assets/code4scene-mark\.svg[^>]*>') CSS_START, CSS_END = "/* SimWorld emblem in the header (scripts/simworld_logo.py) */", "/* end SimWorld emblem */" CSS = """ .brand-home .brand-mark{display:block;flex:none;width:30px;height:30px} @media(max-width:650px){.brand-home .brand-mark{width:26px;height:26px}} """ def mark(src: Path) -> None: im = Image.open(src).convert("RGBA") im = im.crop(im.getbbox()) side = max(im.size) sq = Image.new("RGBA", (side, side), (0, 0, 0, 0)) sq.alpha_composite(im, ((side - im.width) // 2, (side - im.height) // 2)) sq = sq.resize((192, 192), Image.LANCZOS) sq.quantize(colors=256, method=Image.Quantize.FASTOCTREE, dither=Image.Dither.FLOYDSTEINBERG).save(ROOT / MARK, optimize=True) def page(p: Path) -> bool: s0 = s = p.read_text() prefix = "../" * (len(p.relative_to(ROOT).parts) - 1) src = f"{prefix}{MARK}?v={V}" img = f'' s = CUBE.sub(img, s, count=1) s = re.sub(r']*/>', img, s, count=1) # a re-run: the current version icons = (f'' f'') if OLD_ICON.search(s): s = OLD_ICON.sub(icons, s, count=1) else: s = re.sub(r']*/>', icons, s, count=1) s = re.sub(r'(assets/buildingbench\.css)\?v=[^"]*', rf"\1?v={V}", s) if s != s0: p.write_text(s) return s != s0 def css() -> None: p = ROOT / "assets/buildingbench.css" s = p.read_text() block = CSS_START + CSS + CSS_END if CSS_START in s: s = s[:s.index(CSS_START)] + block + s[s.index(CSS_END) + len(CSS_END):] else: s = s.rstrip("\n") + "\n" + block + "\n" p.write_text(s) def main() -> None: ap = argparse.ArgumentParser(description=__doc__.split("\n")[0]) ap.add_argument("--src", type=Path, help="the emblem image (transparent); omit to keep assets/simworld-mark.png") a = ap.parse_args() if a.src: mark(a.src) assert (ROOT / MARK).exists(), "run once with --src" pages = [ROOT / "index.html", ROOT / "cases.html", ROOT / "paper.html"] + sorted((ROOT / "gallery").rglob("*.html")) n = sum(page(p) for p in pages if p.exists()) css() left = [str(p.relative_to(ROOT)) for p in pages if "code4scene-mark.svg" in p.read_text() or "M15 2 27 9v14" in p.read_text()] assert not left, left print(f"{n} pages updated; mark {(ROOT / MARK).stat().st_size} bytes") if __name__ == "__main__": main()