# /// script # requires-python = ">=3.9" # dependencies = ["pillow"] # /// """ Generate routes/root/dist-strips.bin from routes/root/dist.png. Splits the 512x512 grayscale Julia distance field into N horizontal bands, re-encodes each band as its own WebP, and concatenates them in CENTER-OUT order (the band the pan/zoom animation shows first) so a streaming client can upload the visible region before the outer bands have finished downloading. WebP q90 is ~half the size of PNG with no visible loss after the color-band mapping. File layout (little-endian): u32 headerLen bytes headerLen -- UTF-8 JSON: {fullW, fullH, bands:[{y,h,off,len}, ...]} bytes payload -- concatenated band WebPs in the order listed in `bands` (off/len are byte ranges within payload, stream order) Re-run only when dist.png changes; the output is committed. Usage: uv run tools/gen-strips.py """ import io import json import os import struct from PIL import Image HERE = os.path.dirname(os.path.abspath(__file__)) ROOT = os.path.dirname(HERE) SRC = os.path.join(ROOT, "routes", "root", "dist.png") OUT = os.path.join(ROOT, "routes", "root", "dist-strips.bin") NBANDS = 8 WEBP_QUALITY = 90 # lossy; ~half of PNG, visually identical. Bump to 95 / use lossless if needed. def center_out(n): """Band indices ordered from the middle outward: e.g. n=8 -> [4,3,5,2,6,1,7,0].""" order = [] lo, hi = n // 2 - 1, n // 2 while lo >= 0 or hi < n: if hi < n: order.append(hi) hi += 1 if lo >= 0: order.append(lo) lo -= 1 return order def main(): img = Image.open(SRC).convert("L") w, h = img.size # Row boundaries (robust if h is not divisible by NBANDS). bounds = [round(i * h / NBANDS) for i in range(NBANDS + 1)] bands_by_index = [] for i in range(NBANDS): y0, y1 = bounds[i], bounds[i + 1] crop = img.crop((0, y0, w, y1)) buf = io.BytesIO() crop.save(buf, format="WEBP", quality=WEBP_QUALITY, method=6) bands_by_index.append((y0, y1 - y0, buf.getvalue())) payload = bytearray() meta = [] order = center_out(NBANDS) for idx in order: y, bh, png = bands_by_index[idx] meta.append({"y": y, "h": bh, "off": len(payload), "len": len(png)}) payload += png header = json.dumps( {"fullW": w, "fullH": h, "bands": meta}, separators=(",", ":") ).encode("utf-8") with open(OUT, "wb") as f: f.write(struct.pack(" {NBANDS} bands, center-out order {order}") print( f"header {len(header)}B, payload {len(payload)}B, total {total}B " f"({total / 1024:.1f} KB) vs dist.png {src_size}B ({src_size / 1024:.1f} KB)" ) print(f"wrote {OUT}") if __name__ == "__main__": main()