Spaces:
Sleeping
Sleeping
| # R0-1 — N1 parity gate: the TypeScript periodSnap/detectWrapMode in | |
| # canvas-engine.ts must make the same decisions as the certified Python | |
| # implementations in verify_n1_sim.py, on the real catalog tiles. | |
| # | |
| # Requires: python3 (PIL, numpy), node, esbuild (present via frontend | |
| # node_modules). Run from backend/floor-visualizer/. | |
| set -euo pipefail | |
| HERE="$(cd "$(dirname "$0")" && pwd)" | |
| FRONTEND="$HERE/../../frontend/viz2d-demo" | |
| TMP="$(mktemp -d)" | |
| trap 'rm -rf "$TMP"' EXIT | |
| # 1. Python: decode tiles to raw RGBA + record expected decisions | |
| python3 - "$TMP" << 'EOF' | |
| import json | |
| import sys | |
| import numpy as np | |
| from PIL import Image | |
| sys.path.insert(0, ".") | |
| from verify_n1_sim import detect_wrap_mode, period_snap | |
| tmp = sys.argv[1] | |
| TILES = "../../frontend/viz2d-demo/src/assets/tiles" | |
| cases = ["checkered.jpeg", "rustic-wood.jpg", "floor-natural-stone.jpg", | |
| "basalt-outside-wal.jpg", "mosaic-tile.jpg"] | |
| expected = {} | |
| for name in cases: | |
| key = name.split(".")[0] | |
| im = Image.open(f"{TILES}/{name}").convert("RGBA") | |
| open(f"{tmp}/{key}.bin", "wb").write(im.tobytes()) | |
| rgb = np.asarray(im.convert("RGB")) | |
| mode, _, _ = detect_wrap_mode(rgb) | |
| snap = None | |
| if mode != "wrap": | |
| out, info = period_snap(rgb) | |
| if info[0] == "snap": | |
| snap = [out.shape[1], out.shape[0]] | |
| expected[key] = {"w": im.width, "h": im.height, | |
| "mode": "wrap" if mode == "wrap" else "mirror", "snap": snap} | |
| json.dump(expected, open(f"{tmp}/expected.json", "w")) | |
| print("python decisions:", json.dumps(expected, default=str)) | |
| EOF | |
| # 2. Compile the actual frontend engine and replay the same decisions | |
| (cd "$FRONTEND" && npx esbuild src/visualizer-demo/canvas-engine.ts \ | |
| --format=cjs --outfile="$TMP/ce.cjs" --log-level=error) | |
| node - "$TMP" << 'EOF' | |
| const fs = require("fs"); | |
| const path = require("path"); | |
| const tmp = process.argv[2]; | |
| const { periodSnap, detectWrapMode } = require(path.join(tmp, "ce.cjs")); | |
| const expected = JSON.parse(fs.readFileSync(path.join(tmp, "expected.json"))); | |
| let ok = true; | |
| for (const [key, exp] of Object.entries(expected)) { | |
| const raw = new Uint8ClampedArray(fs.readFileSync(path.join(tmp, key + ".bin"))); | |
| const mode = detectWrapMode(raw, exp.w, exp.h); | |
| let snap = null; | |
| if (mode !== "wrap") { | |
| const r = periodSnap(raw, exp.w, exp.h); | |
| if (r) snap = [r.w, r.h]; | |
| } | |
| const pass = mode === exp.mode && JSON.stringify(snap) === JSON.stringify(exp.snap); | |
| console.log(` [${pass ? "PASS" : "FAIL"}] ${key}: ts mode=${mode} snap=${JSON.stringify(snap)}` | |
| + (pass ? "" : ` expected mode=${exp.mode} snap=${JSON.stringify(exp.snap)}`)); | |
| if (!pass) ok = false; | |
| } | |
| console.log(ok ? "N1 PARITY PASSED" : "N1 PARITY FAILED"); | |
| process.exit(ok ? 0 : 1); | |
| EOF | |