"""R0-1 — deterministic golden render: bundle x tile -> PNG. A faithful Python port of the CURRENT frontend composite (canvas-engine.ts): texture prep (wrap detection -> period-snap -> masked-shift fallback), mip pyramid + trilinear with per-pixel footprint LOD, shade-map decode, homography- mapped light vector + gloss-gated specular, colour cast, soft highlight clip, confidence-map alpha. Texture-prep and sampling primitives are imported from verify_n1_sim so this stays in lockstep with the certified implementations. Usage: python golden_render.py The output is resized to max-dim 720 so goldens stay small and stable. """ import base64 import gzip import json import sys import numpy as np from PIL import Image from verify_n1_sim import ( build_mips, detect_wrap_mode, flatten_luminance, make_seamless, period_snap, sample_bilinear_wrap, ) OUT_MAX_DIM = 720 def load_bundle(path): if path.endswith(".gz"): with gzip.open(path, "rt") as f: return json.load(f) return json.load(open(path)) def estimate_gloss(tex): """Port of estimateGloss (canvas-engine.ts): mean 4px luminance gradient.""" lum = tex[:, :, 0] * 0.299 + tex[:, :, 1] * 0.587 + tex[:, :, 2] * 0.114 a = lum[::4, 4::4] b = lum[::4, :-4:4][:, : a.shape[1]] mean_grad = float(np.mean(np.abs(a - b))) if a.size else 0.0 return float(np.clip(1 - mean_grad / 24, 0, 1)) def soft_clip(v): """Port of softClipByte: linear below 220, rational shoulder above.""" knee, rng = 220.0, 35.0 t = v - knee return np.where(v <= knee, v, knee + (t * rng) / (t + rng)) def apply_shade(sample, m): """Port of applyShade (canvas-engine.ts) — N4 highlight-preserving shading. Dimming (m <= 1) stays a physical multiply. Brightening near-white texels by multiply clips at 255 and erases pale-tile detail (the white-tile washout), so for m > 1 blend toward a screen-style lift by texel brightness: dark texels keep the multiply, bright texels brighten by closing their gap to white — grout/figuring contrast survives by construction because the result stays below 255. """ mult = sample * m screen = 255.0 - (255.0 - sample) / np.maximum(m, 1e-6) t = sample / 255.0 w = t * t * (3.0 - 2.0 * t) return np.where(m <= 1.0, mult, mult * (1.0 - w) + screen * w) def prepare_texture(tile_path): tex = np.asarray(Image.open(tile_path).convert("RGB")) h, w, _ = tex.shape mode, _, _ = detect_wrap_mode(tex) repeat_scale = 1.0 lay_mode = "repeat" if mode != "wrap": prepared, info = period_snap(tex) if info[0] == "snap": repeat_scale = prepared.shape[1] / w tex = prepared else: tex = make_seamless(tex) # R2-3 v1 — truly organic materials (healed, not periodic) lay as # procedural cells; periodic/authored tiles keep the plain repeat. lay_mode = "cells" # R2-3b — strip the photo's baked lighting (mirrors canvas-engine.ts): # snapped patterns stop showing tonal seams at every repeat; healed # organic textures stop reading as patchwork across cells. tex = flatten_luminance(tex) return tex, repeat_scale, lay_mode _M32 = np.uint64(0xFFFFFFFF) def hash01(a, b): """Port of hash01 (canvas-engine.ts) — bit-exact with Math.imul/>>> JS semantics so both engines lay identical cells.""" a = (np.asarray(a, np.int64) & 0xFFFFFFFF).astype(np.uint64) b = (np.asarray(b, np.int64) & 0xFFFFFFFF).astype(np.uint64) h = ((a * np.uint64(374761393)) & _M32) ^ ((b * np.uint64(668265263)) & _M32) h = ((h ^ (h >> np.uint64(13))) * np.uint64(1274126177)) & _M32 return ((h ^ (h >> np.uint64(16))) >> np.uint64(8)).astype(np.float64) / 16777216.0 def procedural_cells(rx, ry, repeat_w, repeat_h, grout_frac, tw, th, footprint, pattern="straight"): """Port of the R2-3 v1 cell logic (canvas-engine.ts composite): per-cell toroidal grain offset, tone jitter, pattern-driven bond (straight = random running bond, brick = fixed half offset, grid/diamond = aligned), and an anti-aliased seam mask. Returns (u, v, tone, blend).""" row = np.floor(ry / repeat_h).astype(np.int64) if pattern in ("grid", "diamond"): stagger = np.zeros(len(row), np.float64) elif pattern == "brick": stagger = (row & 1) * 0.5 else: stagger = hash01(row, np.full_like(row, 0x9E37)) sx = rx + stagger * repeat_w col = np.floor(sx / repeat_w).astype(np.int64) lu = sx / repeat_w - col lv = ry / repeat_h - row # R2-3b — grid/diamond tiles are factory prints (real tiles repeat their # print): content stays aligned per cell; organic grain keeps the # per-cell toroidal window. Mirrors canvas-engine.ts. if pattern in ("grid", "diamond"): u = np.mod(lu, 1.0) v = np.mod(lv, 1.0) else: u = np.mod(lu + hash01(col, row), 1.0) v = np.mod(lv + hash01(col + 0x55, row - 0x21), 1.0) tone = 0.94 + 0.12 * hash01(col - 0x13, row + 0x77) half_u = grout_frac * 0.5 half_v = half_u * (repeat_w / repeat_h) aa_u = np.maximum(footprint / tw, 1e-4) aa_v = np.maximum(footprint / th, 1e-4) du_b = np.minimum(lu, 1.0 - lu) dv_b = np.minimum(lv, 1.0 - lv) b_u = np.clip((du_b - half_u) / aa_u + 0.5, 0.0, 1.0) b_v = np.clip((dv_b - half_v) / aa_v + 0.5, 0.0, 1.0) return u, v, tone, 1.0 - np.minimum(b_u, b_v) def apply_reflection(texel, base, xs, ys, h, w): """Port of the R4-2 gloss reflection (canvas-engine.ts composite): mirror the above-floor scene about the per-column contact line, faded with distance and weighted by reflected brightness squared.""" ys_f = ys.astype(np.float64) top = np.full(w, np.inf) np.minimum.at(top, xs, ys_f) floor_flags = np.zeros((h, w), bool) floor_flags[ys, xs] = True ty = top[xs] ry_refl = np.floor(2.0 * ty - ys_f).astype(np.int64) valid = np.isfinite(ty) & (ry_refl >= 0) & (ry_refl < h) ry_c = np.clip(ry_refl, 0, h - 1) src_floor = floor_flags[ry_c, xs] refl = base[ry_c, xs] lum = (refl[:, 0] * 0.299 + refl[:, 1] * 0.587 + refl[:, 2] * 0.114) / 255.0 fade = np.maximum(0.0, 1.0 - (ys_f - ty) / (h * 0.35)) k = np.where(valid & ~src_floor, 0.22 * fade * lum * lum, 0.0) k = np.where(k > 0.003, k, 0.0) return texel * (1.0 - k[:, None]) + refl * k[:, None] def render(bundle_path, tile_path, finish="matte"): d = load_bundle(bundle_path) w, h = d["width"], d["height"] base = np.asarray( Image.open(__import__("io").BytesIO(base64.b64decode(d["pixels"]))).convert("RGB") ).astype(np.float64) seg = max(d["segments"], key=lambda s: len(s["mask"])) mask_idx = np.frombuffer(base64.b64decode(seg["mask"]), dtype=np.uint32) mask = np.zeros(w * h, bool) mask[mask_idx] = True mask = mask.reshape(h, w) H = np.asarray(seg["homography"], np.float64).reshape(3, 3) plane = seg.get("plane") or {} plane_w = max(plane.get("width", w), 1) plane_h = max(plane.get("height", h), 1) plane_cx = plane.get("x", 0) + plane_w / 2 plane_cy = plane.get("y", 0) + plane_h / 2 rot_deg = plane.get("defaultRotation") or 0.0 rad = np.deg2rad(rot_deg) cos, sin = np.cos(-rad), np.sin(-rad) shade_map = ( np.frombuffer(base64.b64decode(seg["shadeMap"]), np.uint8).reshape(h, w).astype(np.float64) if seg.get("shadeMap") else None ) shade_lo, shade_hi = seg.get("shadeRange") or (0.55, 1.35) conf = ( np.frombuffer(base64.b64decode(seg["confidenceMap"]), np.uint8).reshape(h, w).astype(np.float64) / 255.0 if seg.get("confidenceMap") else None ) ct = seg.get("colorTemperature") or {} if "cast" in ct: ct = ct["cast"] col = np.array([ct.get("r", 1.0), ct.get("g", 1.0), ct.get("b", 1.0)]) lv = seg.get("lightVector") tex, repeat_scale, lay_mode = prepare_texture(tile_path) # R4-2b — catalog finish overrides the texture-smoothness estimate (T9): # matte gets no sheen however smooth the print; gloss keeps a baseline. # Mirrors canvas-engine.ts. gloss_est = estimate_gloss(np.asarray(Image.open(tile_path).convert("RGB")).astype(np.float64)) gloss = 0.0 if finish == "matte" else max(0.5, gloss_est) if finish == "gloss" else gloss_est # R2-3 v1 — derived seam tone + width; mirrors canvas-engine.ts (same # every-16th-pixel mean, same no-physical-metadata fraction). grout_col = tex.reshape(-1, 3)[::16].astype(np.float64).mean(axis=0) * 0.45 grout_frac = 0.012 th, tw, _ = tex.shape mips = build_mips(tex) max_l = len(mips) - 1 # R1-3 — mirror of canvas-engine.ts: metric plane scale when present # (pixel-ish or metre plane units alike — backend gates mpu hard), # heuristic fallback otherwise (info.scale = 1 in goldens). DEFAULT_TILE_M = 0.6 mpu = plane.get("metersPerUnit") repeat_w = 0.0 if mpu and mpu > 0: repeat_w = (DEFAULT_TILE_M / mpu) * repeat_scale if not (np.isfinite(repeat_w) and repeat_w > 0): repeat_w = 0.0 if not repeat_w: repeat_w = max(48.0, min(plane_w, plane_h) * 0.22) * repeat_scale repeat_h = repeat_w * (th / tw) ys, xs = np.nonzero(mask) xs_f, ys_f = xs.astype(np.float64), ys.astype(np.float64) def to_plane(px, py): z = H[2, 0] * px + H[2, 1] * py + H[2, 2] z = np.where(np.abs(z) < 1e-6, 1e-6, z) return ( (H[0, 0] * px + H[0, 1] * py + H[0, 2]) / z, (H[1, 0] * px + H[1, 1] * py + H[1, 2]) / z, ) fx, fy = to_plane(xs_f, ys_f) fx1, fy1 = to_plane(xs_f + 1, ys_f) fx2, fy2 = to_plane(xs_f, ys_f + 1) def rot(ax, ay): dx = ax - plane_cx dy = ay - plane_cy return dx * cos - dy * sin, dx * sin + dy * cos rx, ry = rot(fx, fy) rx1, ry1 = rot(fx1, fy1) rx2, ry2 = rot(fx2, fy2) tcx, tcy = (rx / repeat_w) * tw, (ry / repeat_h) * th du = np.hypot((rx1 / repeat_w) * tw - tcx, (ry1 / repeat_h) * th - tcy) dv = np.hypot((rx2 / repeat_w) * tw - tcx, (ry2 / repeat_h) * th - tcy) footprint = np.maximum(np.maximum(du, dv), 1e-3) lod = np.log2(footprint) + 0.5 if lay_mode == "cells": u, v, cell_tone, grout_blend = procedural_cells( rx, ry, repeat_w, repeat_h, grout_frac, tw, th, footprint ) else: u = np.mod(rx / repeat_w, 1.0) v = np.mod(ry / repeat_h, 1.0) l0 = np.clip(np.floor(lod), 0, max_l).astype(np.int64) frac = np.clip(lod - l0, 0, 1) sample = np.zeros((len(xs), 3), np.float64) for lev in range(max_l + 1): sel = l0 == lev if not sel.any(): continue a = mips[lev] sa = sample_bilinear_wrap(a, u[sel] * a.shape[1], v[sel] * a.shape[0]) if lev < max_l: b = mips[lev + 1] sb = sample_bilinear_wrap(b, u[sel] * b.shape[1], v[sel] * b.shape[0]) sample[sel] = sa + (sb - sa) * frac[sel][:, None] else: sample[sel] = sa if lay_mode == "cells": sample = ( sample * cell_tone[:, None] * (1.0 - grout_blend[:, None]) + grout_col[None, :] * grout_blend[:, None] ) shade = ( shade_lo + (shade_map[ys, xs] / 255.0) * (shade_hi - shade_lo) if shade_map is not None else np.full(len(xs), 1.0) ) specular = np.zeros(len(xs)) if lv: lvx, lvy = lv.get("x", 0.0), lv.get("y", 0.0) a = to_plane(np.array([w * 0.5]), np.array([h * 0.75])) step = min(w, h) * 0.05 b = to_plane(np.array([w * 0.5 + lvx * step]), np.array([h * 0.75 + lvy * step])) dxv, dyv = b[0][0] - a[0][0], b[1][0] - a[1][0] ln = np.hypot(dxv, dyv) if ln > 1e-6: lvx, lvy = dxv / ln, dyv / ln dfx = (fx - plane_cx) / (plane_w * 0.5) dfy = (fy - plane_cy) / (plane_h * 0.5) dlen = np.hypot(dfx, dfy) ok = dlen > 0.01 dot = np.where(ok, (dfx * lvx + dfy * lvy) / np.maximum(dlen, 1e-9), 0.0) specular = 0.12 * gloss * np.maximum(0, dot) ** 4 texel = soft_clip(apply_shade(sample, shade[:, None] * col[None, :]) + specular[:, None] * 255.0) if finish == "gloss": texel = apply_reflection(texel, base, xs, ys, h, w) alpha = conf[ys, xs][:, None] if conf is not None else np.ones((len(xs), 1)) out = base.copy() out[ys, xs] = np.clip(texel * alpha + base[ys, xs] * (1 - alpha), 0, 255) img = Image.fromarray(out.astype(np.uint8)) scale = min(OUT_MAX_DIM / max(img.size), 1.0) if scale < 1.0: img = img.resize((round(img.width * scale), round(img.height * scale)), Image.BILINEAR) return img def main(): if len(sys.argv) != 4: print(__doc__) return 2 render(sys.argv[1], sys.argv[2]).save(sys.argv[3]) print(f"saved {sys.argv[3]}") return 0 if __name__ == "__main__": raise SystemExit(main())