passages: two-pass swept-tube tunnels (owner design, L99, v215) - pending owner visual review

Route plan unchanged. Per tile, route segments whose bounded influence (tube + 4 m) touches the
tile write min-combined tube distance (base sweep); relief/floor/mouth shaping only in the shell
(detail sweep); 0.5 m edge blend into the strate field. Near-ready @W12: SW 1.01->0.80,
FI 2.23->1.43, TN 2.18->1.37, VS 1.09->0.98 s; whole view FI 3.38->2.37 s. All passage walk
audits pass (21,425/21,425 canonical, 3/3 interfaces); 24-run Clang/GCC W1/W8/W12 determinism;
0 sign flips. Change: mean 0.02 cm / 0.05 deg; max 25.9 cm (passage walls/roofs ~3.5 m from
route, LOD2) and 49.8 deg (rock walls 13-25 m from routes, LOD2). Captures with diffs in
Linux99/captures. Fast tier PASS (v215 pins).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
This commit is contained in:
Fr0zkaandClaude Opus 5.5 committed 2026-10-28 14:38:45 +01:00
1 parent 36140b573f
commit fa6363b31a
67 files changed
+2022 -45

No files matched your search

@@ -0,0 +1,406 @@
#!/usr/bin/env python3
"""Reframe and verify the ten L99 passage-change hotspot captures."""
from __future__ import annotations
import json
import math
import struct
import subprocess
import sys
import time
import zlib
from array import array
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
HERE = Path(__file__).resolve().parent
CAPTURES = HERE / "captures"
RUNS = ROOT / "Saved/Linux/l99/captures-fix"
LAYOUT = ROOT / "Saved/Linux/l99/resolved-sw.json"
REACH = ROOT / "Saved/Linux/l99/carve-reach/summary.json"
DATA = ROOT / "Standalone/data/owner_world.json"
RENDERER = ROOT / "Saved/Linux/l99/bin/VoxelForgeOffscreenCapture-l99-clang"
BINARIES = {
"before-l98": ROOT / "Saved/Linux/l98/bin/VoxelForgeCoreHeadless-l98-clang19",
"after-l99": ROOT / "Saved/Linux/l99/bin/VoxelForgeCoreHeadless-l99-clang",
}
sys.path.insert(0, str(HERE))
from RunL99Captures import angles, fmt, make_passages, nearest_route, norm, add, mul, sub # noqa: E402
def screen_projection(camera, target, yaw_degrees, pitch_degrees, width=1600, height=900,
horizontal_fov=56.0):
yaw, pitch = math.radians(yaw_degrees), math.radians(pitch_degrees)
forward = (math.sin(yaw) * math.cos(pitch), math.cos(yaw) * math.cos(pitch),
math.sin(pitch))
right = (math.cos(yaw), -math.sin(yaw), 0.0)
up = (right[1] * forward[2] - right[2] * forward[1],
right[2] * forward[0] - right[0] * forward[2],
right[0] * forward[1] - right[1] * forward[0])
delta = sub(target, camera)
depth = sum(delta[i] * forward[i] for i in range(3))
x = sum(delta[i] * right[i] for i in range(3))
y = sum(delta[i] * up[i] for i in range(3))
tan_h = math.tan(math.radians(horizontal_fov) * 0.5)
tan_v = tan_h / (width / height)
return (0.5 + x / (2.0 * depth * tan_h),
0.5 - y / (2.0 * depth * tan_v))
def run(name: str, command: list[str]) -> dict:
log = RUNS / f"{name}.log"
wrapped = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
"timeout", "1740s", *map(str, command)]
start = time.monotonic()
with log.open("w", encoding="utf-8") as stream:
result = subprocess.run(wrapped, cwd=ROOT, stdout=stream,
stderr=subprocess.STDOUT, check=False, timeout=1745)
row = {"name": name, "exit_code": result.returncode,
"wall_s": round(time.monotonic() - start, 3),
"log": str(log.relative_to(ROOT))}
print("[L99CaptureFixRun] " + json.dumps(row, sort_keys=True), flush=True)
if result.returncode != 0:
raise RuntimeError(f"capture step failed: {name}; see {log}")
return row
def closest_on_triangle(p, a, b, c):
"""Ericson closest point, with barycentric weights for normal interpolation."""
ab, ac, ap = sub(b, a), sub(c, a), sub(p, a)
d1, d2 = sum(ab[i] * ap[i] for i in range(3)), sum(ac[i] * ap[i] for i in range(3))
if d1 <= 0.0 and d2 <= 0.0:
return a, (1.0, 0.0, 0.0)
bp = sub(p, b)
d3, d4 = sum(ab[i] * bp[i] for i in range(3)), sum(ac[i] * bp[i] for i in range(3))
if d3 >= 0.0 and d4 <= d3:
return b, (0.0, 1.0, 0.0)
vc = d1 * d4 - d3 * d2
if vc <= 0.0 and d1 >= 0.0 and d3 <= 0.0:
v = d1 / (d1 - d3)
return add(a, mul(ab, v)), (1.0 - v, v, 0.0)
cp = sub(p, c)
d5, d6 = sum(ab[i] * cp[i] for i in range(3)), sum(ac[i] * cp[i] for i in range(3))
if d6 >= 0.0 and d5 <= d6:
return c, (0.0, 0.0, 1.0)
vb = d5 * d2 - d1 * d6
if vb <= 0.0 and d2 >= 0.0 and d6 <= 0.0:
w = d2 / (d2 - d6)
return add(a, mul(ac, w)), (1.0 - w, 0.0, w)
va = d3 * d6 - d5 * d4
if va <= 0.0 and (d4 - d3) >= 0.0 and (d5 - d6) >= 0.0:
w = (d4 - d3) / ((d4 - d3) + (d5 - d6))
return add(b, mul(sub(c, b), w)), (0.0, 1.0 - w, w)
denominator = 1.0 / (va + vb + vc)
v, w = vb * denominator, vc * denominator
return add(a, add(mul(ab, v), mul(ac, w))), (1.0 - v - w, v, w)
def nearest_surface(mesh: Path, point):
"""Find the closest non-skirt LOD2 triangle in the point's tile neighbourhood."""
best = None
target_key = tuple(math.floor(value / 32.0) for value in point)
with mesh.open("rb") as stream:
magic = stream.read(8)
materials = magic == b"VFMSH002"
if magic not in (b"VFMSH001", b"VFMSH002"):
raise RuntimeError(f"unsupported mesh header {magic!r}: {mesh}")
version, voxel_um, tile_count = struct.unpack("<3I", stream.read(12))
if version != (2 if materials else 1):
raise RuntimeError(f"mesh header version mismatch: {mesh}")
stream.seek(40, 1)
lod_counts = {}
relevant_tiles = []
for _ in range(tile_count):
key = struct.unpack("<4i", stream.read(16))
surface_count, _, vertex_count, index_count, mask_count = struct.unpack(
"<5I", stream.read(20))
lod_counts[key[3]] = lod_counts.get(key[3], 0) + 1
positions = array("f")
positions.frombytes(stream.read(vertex_count * 12))
normals = array("f")
normals.frombytes(stream.read(vertex_count * 12))
if materials:
stream.seek(vertex_count * 10, 1)
indices = array("I")
indices.frombytes(stream.read(index_count * 4))
masks = stream.read(mask_count)
if struct.pack("=I", 1) != struct.pack("<I", 1):
positions.byteswap()
normals.byteswap()
indices.byteswap()
if key[3] != 2 or any(abs(key[axis] - target_key[axis]) > 1
for axis in range(3)):
continue
relevant_tiles.append(key)
xyz = [tuple(float(positions[i * 3 + axis]) for axis in range(3))
for i in range(vertex_count)]
nrm = [tuple(float(normals[i * 3 + axis]) for axis in range(3))
for i in range(vertex_count)]
triangle_count = index_count // 3
for tri in range(triangle_count):
if masks and masks[tri] != 0:
continue
ids = tuple(int(indices[tri * 3 + corner]) for corner in range(3))
if any(index >= surface_count for index in ids):
continue
hit, weights = closest_on_triangle(point, *(xyz[index] for index in ids))
distance_sq = sum((hit[axis] - point[axis]) ** 2 for axis in range(3))
if best is not None and distance_sq >= best[0]:
continue
raw_normal = tuple(sum(weights[k] * nrm[ids[k]][axis] for k in range(3))
for axis in range(3))
length = math.sqrt(sum(value * value for value in raw_normal))
if length < 1.0e-8:
continue
best = (distance_sq, hit, tuple(value / length for value in raw_normal), key,
ids, weights)
if best is None:
raise RuntimeError(f"no LOD2 surface triangles near {point}; lods={lod_counts}")
return {"distance_m": math.sqrt(best[0]), "position_m": list(best[1]),
"normal": list(best[2]), "tile": list(best[3]),
"triangle_vertices": list(best[4]), "barycentric": list(best[5]),
"lod_counts": lod_counts, "nearby_lod2_tiles": len(relevant_tiles)}
def decode_rgba(path: Path) -> tuple[int, int, bytes]:
command = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
"timeout", "1740s", "ffmpeg", "-v", "error", "-i", str(path),
"-frames:v", "1", "-f", "rawvideo", "-pix_fmt", "rgba", "pipe:1"]
result = subprocess.run(command, cwd=ROOT, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, check=False, timeout=1745)
if result.returncode != 0:
raise RuntimeError(f"PNG decode failed for {path}: {result.stderr.decode(errors='replace')}")
# ffmpeg rawvideo has no dimensions. Read the PNG IHDR separately for an exact size.
with path.open("rb") as stream:
if stream.read(8) != b"\x89PNG\r\n\x1a\n":
raise RuntimeError(f"invalid PNG signature: {path}")
stream.seek(16)
width, height = struct.unpack(">II", stream.read(8))
if len(result.stdout) != width * height * 4:
raise RuntimeError(f"decoded RGBA size mismatch for {path}: {len(result.stdout)}")
return width, height, result.stdout
def write_png(path: Path, width: int, height: int, rgba: bytes):
def chunk(kind: bytes, data: bytes) -> bytes:
payload = kind + data
return struct.pack(">I", len(data)) + payload + struct.pack(">I", zlib.crc32(payload) & 0xffffffff)
rows = bytearray()
stride = width * 4
for y in range(height):
rows.append(0)
rows.extend(rgba[y * stride:(y + 1) * stride])
png = (b"\x89PNG\r\n\x1a\n"
+ chunk(b"IHDR", struct.pack(">2I5B", width, height, 8, 6, 0, 0, 0))
+ chunk(b"IDAT", zlib.compress(bytes(rows), 9))
+ chunk(b"IEND", b""))
path.write_bytes(png)
def pixel_diff(before: Path, after: Path, output: Path, amplify: int = 64):
width, height, a = decode_rgba(before)
width_b, height_b, b = decode_rgba(after)
if (width, height) != (width_b, height_b):
raise RuntimeError(f"image dimensions differ: {before} and {after}")
diff = bytearray(len(a))
changed = 0
channel_sum = 0
max_delta = 0
for offset in range(0, len(a), 4):
dr = abs(a[offset] - b[offset])
dg = abs(a[offset + 1] - b[offset + 1])
db = abs(a[offset + 2] - b[offset + 2])
if dr or dg or db:
changed += 1
channel_sum += dr + dg + db
max_delta = max(max_delta, dr, dg, db)
diff[offset] = min(255, dr * amplify)
diff[offset + 1] = min(255, dg * amplify)
diff[offset + 2] = min(255, db * amplify)
diff[offset + 3] = 255
write_png(output, width, height, bytes(diff))
return {"width": width, "height": height, "changed_pixels": changed,
"total_pixels": width * height, "changed_percent": round(100.0 * changed / (width * height), 5),
"sum_abs_rgb": channel_sum, "max_channel_delta": max_delta,
"diff_amplification": amplify}
def surface_description(region, outward_normal, route_height_delta, route_radius,
route_distance):
nz = outward_normal[2]
if region == 2:
return "passage mouth surface"
if region == 1:
return "passage floor"
if region == 0:
if route_distance > route_radius + 2.0:
if nz < -0.55:
return "rock overhang / underside near passage"
if abs(nz) < 0.35:
return "other strata rock wall near passage"
return "sloping strata surface near passage"
if route_height_delta > 0.55 * route_radius:
return "passage upper wall / roof"
if route_height_delta < -0.55 * route_radius:
return "passage floor"
return "passage wall"
if nz < -0.55:
return "rock overhang / island underside outside the passage envelope"
if nz > 0.7:
return "upper terrain surface outside the passage envelope"
if abs(nz) < 0.35:
return "other strata rock wall outside the passage envelope"
return "sloping strata surface outside the passage envelope"
def main():
RUNS.mkdir(parents=True, exist_ok=True)
CAPTURES.mkdir(parents=True, exist_ok=True)
for path in (*BINARIES.values(), RENDERER, DATA, LAYOUT, REACH):
if not path.is_file():
raise SystemExit(f"missing input: {path}")
layout = json.loads(LAYOUT.read_text(encoding="utf-8"))
reach = json.loads(REACH.read_text(encoding="utf-8"))
_, routes = make_passages(layout)
sites = []
for kind, rows, value_key, point_key, region_key in (
("displacement", reach["top_displacement_tiles"], "max_displacement_cm",
"displacement_location_metres", "displacement_region"),
("normal", reach["top_normal_tiles"], "max_normal_degrees",
"normal_location_metres", "normal_region"),
):
for index, row in enumerate(rows, 1):
point = tuple(float(v) for v in row[point_key])
route, passage_id = min(((nearest_route(route_points, point), stable_id)
for stable_id, route_points in routes),
key=lambda candidate: candidate[0][0])
sites.append({"name": f"top-{kind}-{index:02d}", "kind": kind,
"point": point, "strate": row["strate"],
"value": float(row[value_key]), "region": int(row[region_key]),
"nearest_passage": passage_id,
"passage_distance_m": float(route[0]),
"route_tangent": list(route[2]),
"route_height_delta_m": point[2] - route[1][2],
"route_radius_m": float(route[4]),
"row": row})
records = []
for site in sites:
point = site["point"]
# Place the export camera far enough away that the audited location is in the LOD2 shell.
# The offscreen renderer then moves back to the exact changed surface for a close view.
direction = -1.0 if point[0] >= 0.0 else 1.0
export_camera = (point[0] + direction * 80.0, point[1], point[2])
yaw_export, pitch_export = 0.0, 0.0
meshes = {}
exports = {}
for label, binary in BINARIES.items():
mesh = RUNS / f"{site['name']}-{label}.vfmesh"
exports[label] = run(f"{site['name']}-{label}-export", [binary, DATA,
"--world-assembler-prototype", "--stream-benchmark", "--stream-pipeline",
"--workers=8", "--force-generate", "--clip-radius=3",
"--max-clip-level=2", "--max-level=2", "--render-distance-chunks=0",
f"--camera={fmt((*export_camera, yaw_export, pitch_export))}",
f"--mesh-export={mesh}"])
if not mesh.is_file() or mesh.stat().st_size <= 0:
raise RuntimeError(f"empty exported scene for {site['name']} {label}")
meshes[label] = mesh
anchor = nearest_surface(meshes["before-l98"], point)
after_anchor = nearest_surface(meshes["after-l99"], point)
expected_tile = list(site["row"]["tile"])
if anchor["tile"] != expected_tile or after_anchor["tile"] != expected_tile:
raise RuntimeError(f"change tile not represented by the LOD2 export for {site['name']}: "
f"expected={expected_tile} before={anchor['tile']} "
f"after={after_anchor['tile']}")
if anchor["distance_m"] > 0.1 or after_anchor["distance_m"] > 0.1:
raise RuntimeError(f"change point too far from exported surface for {site['name']}: "
f"before={anchor['distance_m']:.3f}m "
f"after={after_anchor['distance_m']:.3f}m")
camera_distance = 3.25
# Marching-cubes normals point into the solid in these exports, so the air-side view
# starts along their negative direction.
air_normal = mul(tuple(anchor["normal"]), -1.0)
camera_direction = air_normal
if site["kind"] == "displacement":
route_tangent = tuple(site["route_tangent"])
tangent = sub(route_tangent, mul(air_normal,
sum(route_tangent[i] * air_normal[i] for i in range(3))))
tangent = norm(tangent)
camera_direction = norm(add(mul(air_normal, 0.70710678),
mul(tangent, 0.70710678)))
camera = add(tuple(anchor["position_m"]), mul(camera_direction, camera_distance))
yaw, pitch = angles(camera, point)
actual_camera_distance = math.sqrt(sum((camera[axis] - point[axis]) ** 2
for axis in range(3)))
if actual_camera_distance > 5.0 or actual_camera_distance < 2.0:
raise RuntimeError(f"changed point outside close framing range for {site['name']}: "
f"camera-to-point={actual_camera_distance:.3f}m; "
f"surface-offset={anchor['distance_m']:.3f}m")
target_uv = screen_projection(camera, point, yaw, pitch)
projection_error_px = math.hypot((target_uv[0] - 0.5) * 1600,
(target_uv[1] - 0.5) * 900)
if projection_error_px > 0.01:
raise RuntimeError(f"changed point is off-axis for {site['name']}: {target_uv}")
view = {**site, "focus": list(point), "camera": list(camera),
"target": list(point), "yaw": yaw, "pitch": pitch,
"target_screen_uv": list(target_uv),
"target_projection_error_px": projection_error_px,
"change_tile_lod2": expected_tile,
"surface_match_distance_m": anchor["distance_m"],
"camera_to_surface_m": camera_distance,
"camera_to_changed_point_m": actual_camera_distance,
"camera_side": "air side, 45-degree passage-tangent view"
if site["kind"] == "displacement" else "air side (negative exported normal)",
"surface_anchor": anchor, "after_surface_anchor": after_anchor,
"surface_type": surface_description(site["region"],
tuple(-value for value in anchor["normal"]),
site["route_height_delta_m"], site["route_radius_m"],
site["passage_distance_m"]),
"export_camera": list(export_camera), "export_lod_counts": anchor["lod_counts"]}
render_records = {}
render_shade = "normal" if site["kind"] == "normal" else "readable"
view["render_shade"] = render_shade
for label in BINARIES:
suffix = "before-l98" if label == "before-l98" else "after-l99"
image = CAPTURES / f"{site['name']}-{suffix}.png"
render_args = [RENDERER, meshes[label], image, "--size=1600x900",
"--fov-deg=56", f"--camera-m={fmt(camera)}", f"--yaw-deg={yaw:.8f}",
f"--pitch-deg={pitch:.8f}", f"--shade={render_shade}", "--headlamp=on",
"--height-tint=on", "--tile-edges=off", "--tile-skirts=off",
f"--height-center-m={point[2]:.6f}", "--height-range-m=8",
f"--focus-region={fmt((*point, 10.0))}"]
render_records[label] = run(f"{site['name']}-{label}-render", render_args)
render_records[label]["image"] = str(image.relative_to(ROOT))
render_records[label]["image_bytes"] = image.stat().st_size
for mesh in meshes.values():
mesh.unlink()
diff_path = CAPTURES / f"{site['name']}-diff-amplified.png"
pixel_stats = pixel_diff(CAPTURES / f"{site['name']}-before-l98.png",
CAPTURES / f"{site['name']}-after-l99.png", diff_path)
record = {"view": view, "before": render_records["before-l98"],
"after": render_records["after-l99"],
"diff": str(diff_path.relative_to(ROOT)), "pixel_diff": pixel_stats,
"exports": exports}
records.append(record)
print("[L99CaptureFixSite] " + json.dumps({"name": site["name"],
"lod_counts": anchor["lod_counts"], "surface_distance_m": anchor["distance_m"],
"camera_distance_m": camera_distance,
"changed_pixels": pixel_stats["changed_pixels"],
"surface_type": view["surface_type"], "passage": site["nearest_passage"],
"passage_distance_m": site["passage_distance_m"]}, sort_keys=True), flush=True)
summary = {"source_layout_identity": layout["stable_identity"],
"capture_lod": 2, "image_size": [1600, 900],
"camera_surface_distance_m": 3.25,
"sites": records}
(RUNS / "fixed-summary.json").write_text(json.dumps(summary, indent=2) + "\n",
encoding="utf-8")
print("[L99CaptureFixSummary] " + json.dumps({
"sites": len(records),
"changed_pixels": {r["view"]["name"]: r["pixel_diff"]["changed_pixels"]
for r in records},
"fixed_summary": str((RUNS / "fixed-summary.json").relative_to(ROOT))},
sort_keys=True), flush=True)
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,114 @@
# Linux 99 — bounded swept-tube passages
Branch: `assembler/world` · evaluator version: **215** · no commit made.
## Design
The route plan and its three passage splines are unchanged. L98 and L99 resolve the same layout identity (`resolved-layout-v3-fnv64:0589300eb07dfe06`) and exact passage control points. L99 prepares each tile's sorted route-segment candidates from tube bounds expanded by 4 m, min-combines their tube distances, then applies localized wall/floor relief and mouth shaping in the detail sweep. A 0.5 m edge blend bounds the integration with the strate field. Existing tread, clearance, mouth fade, and void-crossing support remain in generation.
## Speed
L80 protocol: three interleaved measured L98/L99 pairs at W8 and W12, with warmups, for each stratum. Entries are median seconds (`L98 → L99`). Stratum labels are explicit: SW = Surface World, FI = Floating Islands, TN = Tunnel Network, VS = Vertical Shafts. Whole/near tile counts are shown to keep the world labels fixed.
| Workers | Stratum | Whole / LOD0–2 tiles | Near ready (s) | Whole-view ready (s) |
|---:|---|---:|---:|---:|
| 8 | SW | 2,850 / 1,408 | 1.179 → 0.970 | 2.605 → 2.241 |
| 8 | FI | 2,249 / 1,216 | 2.342 → 1.499 | 3.583 → 2.524 |
| 8 | TN | 2,233 / 1,344 | 2.357 → 1.506 | 3.420 → 2.449 |
| 8 | VS | 2,329 / 1,344 | 2.204 → 1.721 | 3.429 → 3.028 |
| 12 | SW | 2,850 / 1,408 | 1.006 → 0.799 | 2.114 → 1.845 |
| 12 | FI | 2,249 / 1,216 | 2.225 → 1.432 | 3.382 → 2.373 |
| 12 | TN | 2,233 / 1,344 | 2.183 → 1.369 | 3.188 → 2.237 |
| 12 | VS | 2,329 / 1,344 | 1.093 → 0.983 | 2.019 → 1.884 |
All eight cells beat L98. FI/TN approach the ~1 s target, but remain above the passage-off ceilings (0.81/0.85 s). Native Clang and GCC builds took about 11m30s and 1m38s combined; these are accounted separately from readiness times. The Unreal `VFHostEditor Linux Development` build succeeded after timing in 71.50 s with `-NoUBA -MaxParallelActions=4`.
The fast tier is **PASS**: 80 runs, 301.4 s, 269 passing checks and 5 retained historical-hash diagnostics. Both v215 owner hash pins pass.
## Passage audits and measured work
The full-view canonical audit (`max-level=-1`, W12) passes **21,425/21,425** centerline samples. All three resolved interfaces passed:
| Interface | Center / capsule samples | Blocked | Support samples / unsupported | Grade failures | Mouth content / radius | Result |
|---|---:|---:|---:|---:|---:|---|
| 0–1 | 4,151 / 261,513 | 0 | 27,206 / 0 | 0 | 2/2 · 2/2 | PASS |
| 1–2 | 239 / 15,057 | 0 | 1,309 / 0 | 0 | 2/2 · 2/2 | PASS |
| 2–3 | 1,027 / 64,701 | 0 | 5,651 / 0 | 0 | 2/2 · 2/2 | PASS |
The audit summary reports naturalness 3/3, all three interfaces passed, and zero route-binding failures. The fast-tier owner style-1 and style-2 audits also pass, as does the arrival/departure chain (`interfaces=3/3 all_walkable=true`). Capsule clearance, support, headroom, grade, floor and mouth profiles, route binding, open-air, no-long-bridge, rock-column and ripple checks pass.
L77 near-LOD passage CPU share (three repeats, measured mean):
| Stratum | L98 | L99 |
|---|---:|---:|
| SW | 42.83% | 19.75% |
| FI | 56.22% | 34.78% |
| TN | 55.58% | 28.81% |
| VS | 30.21% | 8.56% |
Measured LOD0–2 sweep counters (W8):
| Stratum | Bounded segment candidates | Segment/sample pairs (outside-bound rejects) | Grid samples (no influence) | Base / detail samples |
|---|---:|---:|---:|---:|
| SW | 892 | 8,568,233 (4,699,672) | 14,011,370 (11,949,427) | 2,061,943 / 210,393 |
| FI | 1,439 | 15,261,585 (8,730,625) | 21,565,975 (18,604,965) | 3,377,667 / 347,335 |
| TN | 1,560 | 14,723,215 (8,313,402) | 17,969,538 (15,233,648) | 3,152,585 / 332,547 |
| VS | 1,558 | 14,092,586 (7,348,748) | 20,749,529 (18,066,064) | 2,683,465 / 273,636 |
| **Total** | **5,449** | **52,645,619 (29,092,447)** | **74,296,412 (63,854,104)** | **11,275,660 / 1,163,911** |
The profile covered 478/629/614/699 near tiles (SW/FI/TN/VS). Counts are measured. The bounded-reject and no-influence counts mean those segment tests and tube samples do not proceed to distance composition; the remaining 43,354,950 prepared-sweep lookups had 11,996,257 hits and 31,358,693 misses.
## World change and captures
Carve-reach comparisons covered every desired near-set tile: SW 1,408/1,408, FI 1,216/1,216, TN 1,344/1,344, VS 1,344/1,344. Every stratum had zero sign flips and zero edge-crossing mismatches.
| Stratum | Max / mean displacement | Max / mean normal change | Moved crossings | Pairing failures |
|---|---:|---:|---:|---:|
| SW | 20.11 / 0.0180 cm | 21.35 / 0.0874° | 9,214 | 21 |
| FI | 19.80 / 0.0185 cm | 29.91 / 0.0477° | 10,238 | 114 |
| TN | 21.74 / 0.0387 cm | 41.79 / 0.0291° | 11,370 | 116 |
| VS | 25.88 / 0.0178 cm | 49.82 / 0.00731° | 5,087 | 32 |
The audit's `FAIL` status is only its existing 1 cm displacement / 2° normal-change diagnostic threshold; those thresholds were not an L99 gate. Maxima and their coordinates are in the capture index. Paired L98/L99 captures include the top five displacement sites, top five normal-change sites, both mouths of all three passages, and a midpoint view for each passage: **19 matched views / 38 before-and-after PNGs** in [`captures/INDEX.md`](captures/INDEX.md), plus ten amplified hotspot diffs.
## Determinism and version
All 24 whole-view runs passed: Clang/GCC at W1/W8/W12 across SW/FI/TN/VS. The 20 peer comparisons had zero tile-count, per-tile hash, or whole-hash mismatches. Representative Clang W1 whole hashes (tile count followed by four components):
| Stratum | Whole hash |
|---|---|
| SW | `2850, a962f8cb29383f0e, 3deecb1a8f537726, fa367c6df153636f, 0b0a55b24243bd1e` |
| FI | `2249, 1d867417832047f9, 3f5bf03b78621a43, 601921b84046915d, c00d44660831d266` |
| TN | `2233, 3c0f10f94c18944d, e213da2d3bc34fe1, 318aaa3a18a997be, 7a1cd0183cc57c07` |
| VS | `2329, 3005885856f8adef, 2e3181ca8152f93c, 0cddc16aa45946bb, 69eb58192bab4302` |
Version 215 pins: [`owner-hash-floating-islands-style2-v215.baseline`](../../../../Tools/verify/baselines/owner-hash/canonical/owner-hash-floating-islands-style2-v215.baseline) and [`owner-hash-surface-world-style1-v215.baseline`](../../../../Tools/verify/baselines/owner-hash/canonical/owner-hash-surface-world-style1-v215.baseline). Source stamp: `0d31d588192e30bd`.
## Failed runs
- The first fast-tier attempt stopped at stale pre-L99 hash pins; v215 pins were added and the full rerun passed.
- A zero-radius smoke configuration aborted with `std::bad_alloc` on both L98 and L99; it was an invalid diagnostic setup. Capture probes first rejected an overly strict mouth-anchor assumption, then hit open-contour clip-cap errors; a camera-coordinate bug also produced an empty render. Those probes were corrected, and the final matched views rendered successfully without clipping.
- The displacement/normal diagnostic returned exit 14 because its legacy 1 cm / 2° thresholds were exceeded. This reports the measured world change above, not a passage walkability failure.
All measured workload processes were capped below 30 minutes and run on CPUs 0–11; headless processes used the 6 GiB address-space limit. Logs and summaries are under `Saved/Linux/l99/`. No install, commit, push, or stash was made.
## Captures (fixed)
The first hotspot views exported LOD0, while these maxima come from LOD2. The fixed views export the matching LOD2 audit tile, put the exact audit point at image center, and place the camera 3.25 m from its L98 surface (3.25–3.29 m from the audit point). Each before-and-after surface triangle is within 0.09 m of the reported point in the matching LOD2 tile. Displacement views look obliquely along the passage; normal-change views use normal-color shading. Pixel counts below are measured decoded RGB pixels with any changed channel; each diff image is absolute RGB difference amplified 64×. All ten pairs differ.
Strata labels: SW = Surface World, FI = Floating Islands, TN = Tunnel Network, VS = Vertical Shafts. Passage distances are measured from the site to the nearest route centerline; surface labels use the nearest LOD2 surface and passage-region class.
| Site | Strate · change · focus (m) | Surface · nearest passage | Changed pixels | Before / after / diff |
|---|---|---|---:|---|
| `top-displacement-01` | VS · 25.88 cm · (18.000, -2.000, -817.035) | passage mouth surface · passage/2-3/0, 3.17 m | 557,549 | [L98](captures/top-displacement-01-before-l98.png) / [L99](captures/top-displacement-01-after-l99.png) / [Δ×64](captures/top-displacement-01-diff-amplified.png) |
| `top-displacement-02` | TN · 21.74 cm · (26.000, -4.000, -692.765) | passage upper wall/roof · passage/0-1/0, 3.47 m | 565,587 | [L98](captures/top-displacement-02-before-l98.png) / [L99](captures/top-displacement-02-after-l99.png) / [Δ×64](captures/top-displacement-02-diff-amplified.png) |
| `top-displacement-03` | SW · 20.11 cm · (-23.127, 4.000, -490.000) | passage upper wall/roof · passage/0-1/0, 3.45 m | 760,804 | [L98](captures/top-displacement-03-before-l98.png) / [L99](captures/top-displacement-03-after-l99.png) / [Δ×64](captures/top-displacement-03-diff-amplified.png) |
| `top-displacement-04` | FI · 19.80 cm · (16.000, -17.133, -668.000) | passage upper wall/roof · passage/0-1/0, 3.47 m | 729,645 | [L98](captures/top-displacement-04-before-l98.png) / [L99](captures/top-displacement-04-after-l99.png) / [Δ×64](captures/top-displacement-04-diff-amplified.png) |
| `top-displacement-05` | SW · 19.01 cm · (20.000, 15.067, -500.000) | passage wall · passage/0-1/0, 3.51 m | 372,637 | [L98](captures/top-displacement-05-before-l98.png) / [L99](captures/top-displacement-05-after-l99.png) / [Δ×64](captures/top-displacement-05-diff-amplified.png) |
| `top-normal-01` | VS · 49.82° · (39.696, 6.000, -820.000) | other strata rock wall outside passage envelope · passage/2-3/0, 24.47 m | 423,185 | [L98](captures/top-normal-01-before-l98.png) / [L99](captures/top-normal-01-after-l99.png) / [Δ×64](captures/top-normal-01-diff-amplified.png) |
| `top-normal-02` | TN · 41.79° · (34.000, 5.849, -892.000) | other strata rock wall outside passage envelope · passage/2-3/0, 19.85 m | 539,369 | [L98](captures/top-normal-02-before-l98.png) / [L99](captures/top-normal-02-after-l99.png) / [Δ×64](captures/top-normal-02-diff-amplified.png) |
| `top-normal-03` | TN · 35.51° · (38.000, 3.770, -844.000) | other strata rock wall outside passage envelope · passage/2-3/0, 23.26 m | 738,068 | [L98](captures/top-normal-03-before-l98.png) / [L99](captures/top-normal-03-after-l99.png) / [Δ×64](captures/top-normal-03-diff-amplified.png) |
| `top-normal-04` | VS · 35.05° · (-6.234, 28.000, -824.000) | rock overhang/underside near passage · passage/2-3/0, 13.06 m | 770,128 | [L98](captures/top-normal-04-before-l98.png) / [L99](captures/top-normal-04-after-l99.png) / [Δ×64](captures/top-normal-04-diff-amplified.png) |
| `top-normal-05` | FI · 29.91° · (-39.904, -16.000, -690.000) | rock overhang/island underside outside passage envelope · passage/0-1/0, 24.12 m | 291,241 | [L98](captures/top-normal-05-before-l98.png) / [L99](captures/top-normal-05-after-l99.png) / [Δ×64](captures/top-normal-05-diff-amplified.png) |
The earlier LOD0 pairs and a first LOD2 placement did not include the audited tile; the latter placed displacement-01's nearest surface 22.73 m away and failed the close-framing check. The final LOD2 tile keys match all ten audit rows and all ten image pairs pass the raw-pixel comparison. Export and render logs plus exact camera, triangle, and pixel-diff metadata are in `Saved/Linux/l99/captures-fix/`.
@@ -0,0 +1,380 @@
#!/usr/bin/env python3
"""Capture matched L98/L99 tunnel change and passage views."""
from __future__ import annotations
import json
import math
import struct
import subprocess
import time
from array import array
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
OUT = ROOT / "Saved/Linux/l99/captures"
IMAGE_DIR = Path(__file__).resolve().parent / "captures"
DATA = ROOT / "Standalone/data/owner_world.json"
LAYOUT_PATH = ROOT / "Saved/Linux/l99/resolved-sw.json"
REACH_SUMMARY = ROOT / "Saved/Linux/l99/carve-reach/summary.json"
BINARIES = {
"before-l98": ROOT / "Saved/Linux/l98/bin/VoxelForgeCoreHeadless-l98-clang19",
"after-l99": ROOT / "Saved/Linux/l99/bin/VoxelForgeCoreHeadless-l99-clang",
}
RENDERER = ROOT / "Saved/Linux/l99/bin/VoxelForgeOffscreenCapture-l99-clang"
VOXEL_Q = 256.0
CORE_UM_PER_VOXEL = 250_000.0
def add(a, b):
return tuple(x + y for x, y in zip(a, b))
def sub(a, b):
return tuple(x - y for x, y in zip(a, b))
def mul(a, s):
return tuple(x * s for x in a)
def dot(a, b):
return sum(x * y for x, y in zip(a, b))
def norm(a):
length = math.sqrt(dot(a, a))
if length < 1.0e-8:
raise ValueError("degenerate capture direction")
return tuple(x / length for x in a)
def fmt(values):
return ",".join(f"{value:.9f}" for value in values)
def angles(camera, target):
delta = sub(target, camera)
return (math.degrees(math.atan2(delta[0], delta[1])),
math.degrees(math.atan2(delta[2], math.hypot(delta[0], delta[1]))))
def interpolate(points, distance):
remaining = max(0.0, distance)
for a, b in zip(points, points[1:]):
delta = sub(b[0], a[0])
length = math.sqrt(dot(delta, delta))
if length < 1.0e-8:
continue
if remaining <= length:
t = remaining / length
return (tuple(a[0][i] + delta[i] * t for i in range(3)),
a[1] + (b[1] - a[1]) * t)
remaining -= length
return points[-1]
def route_frame(points, distance):
center, radius = interpolate(points, distance)
before, _ = interpolate(points, max(0.0, distance - 0.25))
after, _ = interpolate(points, min(sum(
math.sqrt(dot(sub(b[0], a[0]), sub(b[0], a[0])))
for a, b in zip(points, points[1:])), distance + 0.25))
tangent = norm(sub(after, before))
horizontal = norm((tangent[0], tangent[1], 0.0))
side = (-horizontal[1], horizontal[0], 0.0)
return center, radius, tangent, side
def route_length(points):
return sum(math.sqrt(dot(sub(b[0], a[0]), sub(b[0], a[0])))
for a, b in zip(points, points[1:]))
def nearest_route(points, point):
best = None
distance_along = 0.0
for a, b in zip(points, points[1:]):
delta = sub(b[0], a[0])
length_sq = dot(delta, delta)
if length_sq < 1.0e-10:
continue
segment_length = math.sqrt(length_sq)
t = max(0.0, min(1.0, dot(sub(point, a[0]), delta) / length_sq))
center = add(a[0], mul(delta, t))
tangent = norm(delta)
radial = sub(point, center)
radial = sub(radial, mul(tangent, dot(radial, tangent)))
distance = math.sqrt(dot(sub(point, center), sub(point, center)))
if best is None or distance < best[0]:
radius = a[1] + (b[1] - a[1]) * t
best = (distance, center, tangent, radial, radius,
distance_along + t * segment_length)
distance_along += segment_length
if best is None:
raise ValueError("passage route has no non-degenerate segment")
return best
def run(name, command, headless):
log = OUT / f"{name}.log"
wrapped = ["taskset", "-c", "0-11"]
if headless:
wrapped += ["prlimit", "--as=6442450944", "--"]
wrapped += ["timeout", "1740s", *map(str, command)]
started = time.monotonic()
with log.open("w", encoding="utf-8") as stream:
result = subprocess.run(wrapped, cwd=ROOT, stdout=stream,
stderr=subprocess.STDOUT, check=False, timeout=1745)
row = {"name": name, "exit_code": result.returncode,
"wall_s": round(time.monotonic() - started, 3),
"log": str(log.relative_to(ROOT))}
print("[L99CaptureRun] " + json.dumps(row, sort_keys=True), flush=True)
if result.returncode != 0:
raise RuntimeError(f"capture step failed: {name}; see {log}")
return row
def nearest_surface_anchor(mesh, point):
"""Return the nearest surface vertex and outward mesh normal from a VFMSH scene."""
best_distance_sq = float("inf")
best_position = best_normal = None
with mesh.open("rb") as stream:
magic = stream.read(8)
has_materials = magic == b"VFMSH002"
if magic not in (b"VFMSH001", b"VFMSH002"):
raise RuntimeError(f"unsupported mesh scene header: {mesh}")
version, voxel_um, tile_count = struct.unpack("<3I", stream.read(12))
if version != (2 if has_materials else 1):
raise RuntimeError(f"mesh scene version mismatch: {mesh}")
stream.seek(40, 1) # stored camera position, yaw, and pitch
for _ in range(tile_count):
key = struct.unpack("<4i", stream.read(16))
surface_count, _, vertex_count, index_count, mask_count = struct.unpack(
"<5I", stream.read(20))
extent = (32 << key[3]) * voxel_um / 1_000_000.0
origin = (key[0] * extent, key[1] * extent, key[2] * extent)
if any(point[axis] < origin[axis] - 20.0 or
point[axis] > origin[axis] + extent + 20.0 for axis in range(3)):
stream.seek(vertex_count * 24 + (10 * vertex_count if has_materials else 0)
+ index_count * 4 + mask_count, 1)
continue
positions = array("f")
positions.frombytes(stream.read(vertex_count * 12))
normals = array("f")
normals.frombytes(stream.read(vertex_count * 12))
if has_materials:
stream.seek(vertex_count * 10, 1)
stream.seek(index_count * 4 + mask_count, 1)
if struct.pack("=I", 1) != struct.pack("<I", 1):
positions.byteswap()
normals.byteswap()
for index in range(min(surface_count, vertex_count)):
offset = index * 3
# VFMSH stores vertex coordinates in world space; tile origin
# is metadata for edge rendering and must not be added again.
position = tuple(float(positions[offset + axis]) for axis in range(3))
delta = sub(position, point)
distance_sq = dot(delta, delta)
if distance_sq >= best_distance_sq:
continue
raw_normal = tuple(float(normals[offset + axis]) for axis in range(3))
if dot(raw_normal, raw_normal) < 1.0e-10:
continue
best_distance_sq = distance_sq
best_position = position
best_normal = norm(raw_normal)
if best_position is None:
raise RuntimeError(f"no nearby surface vertices in mesh scene: {mesh}")
return {"position_m": best_position, "normal": best_normal,
"distance_m": math.sqrt(best_distance_sq)}
def make_passages(layout):
q_to_m = layout["core_voxel_size_um"] / (VOXEL_Q * 1_000_000.0)
passages = []
all_routes = []
for passage in layout["passages"]:
controls = [(tuple(float(q) * q_to_m for q in p["position_q"]),
float(p["radius_q"]) * q_to_m)
for p in passage["control_points"]]
upper = tuple(float(q) * q_to_m for q in passage["upper_mouth_q"])
lower = tuple(float(q) * q_to_m for q in passage["lower_mouth_q"])
ui = min(range(len(controls)), key=lambda i: dot(sub(controls[i][0], upper),
sub(controls[i][0], upper)))
li = min(range(len(controls)), key=lambda i: dot(sub(controls[i][0], lower),
sub(controls[i][0], lower)))
points = controls[ui:li + 1]
if len(points) < 2:
raise RuntimeError(f"invalid mouth path: {passage['stable_id']}")
upper_offset = math.sqrt(dot(sub(points[0][0], upper), sub(points[0][0], upper)))
lower_offset = math.sqrt(dot(sub(points[-1][0], lower), sub(points[-1][0], lower)))
length = route_length(points)
passage_views = []
for endpoint, distance, camera_distance, outward_sign in (
("upper-mouth", 0.0, min(length, 7.4), -1.0),
("lower-mouth", length, max(0.0, length - 5.5), 1.0)):
center, _, tangent, side = route_frame(points, distance)
camera_center, _, _, camera_side = route_frame(points, camera_distance)
camera = (camera_center[0] + camera_side[0] * 2.5,
camera_center[1] + camera_side[1] * 2.5, camera_center[2])
outward = mul(tangent, outward_sign)
target = (center[0] + outward[0] * 8.0 + side[0] * 2.5,
center[1] + outward[1] * 8.0 + side[1] * 2.5, center[2])
yaw, pitch = angles(camera, target)
passage_views.append({"name": f"{passage['stable_id']}-{endpoint}",
"kind": "mouth", "focus": center, "camera": camera, "target": target,
"yaw": yaw, "pitch": pitch, "clip": None, "fov": 80.0,
"focus_radius": 20.0, "passage": passage["stable_id"],
"camera_location": "passage-air"})
mid = length * 0.5
center, radius, tangent, side = route_frame(points, mid)
camera = (center[0] + side[0] * 0.75, center[1] + side[1] * 0.75,
center[2] - radius + 1.6)
target_pos, target_radius = interpolate(points, min(length, mid + 14.0))
target = (target_pos[0] + side[0] * 2.35, target_pos[1] + side[1] * 2.35,
target_pos[2] - target_radius + 1.8)
yaw, pitch = angles(camera, target)
passage_views.append({"name": f"{passage['stable_id']}-mid-tunnel",
"kind": "mid-tunnel", "focus": center, "camera": camera,
"target": target, "yaw": yaw, "pitch": pitch, "clip": None,
"fov": 78.0, "focus_radius": 24.0, "passage": passage["stable_id"]})
passages.append({"stable_id": passage["stable_id"], "route_length_m": length,
"control_points": len(points), "upper_mouth_anchor_offset_m": upper_offset,
"lower_mouth_anchor_offset_m": lower_offset, "views": passage_views})
all_routes.append((passage["stable_id"], points))
return passages, all_routes
def make_hotspot_views(routes, reach_summary):
views = []
for series, rows, value_key, location_key, region_key in (
("displacement", reach_summary["top_displacement_tiles"],
"max_displacement_cm", "displacement_location_metres", "displacement_region"),
("normal", reach_summary["top_normal_tiles"],
"max_normal_degrees", "normal_location_metres", "normal_region"),
):
for index, row in enumerate(rows, 1):
point = tuple(row[location_key])
candidates = [(nearest_route(route, point), name, route)
for name, route in routes]
(distance, center, tangent, radial, radius, along), nearest_id, route = min(
candidates, key=lambda item: item[0][0])
if math.sqrt(dot(radial, radial)) < 0.25:
horizontal = norm((tangent[0], tangent[1], 0.0))
radial = (-horizontal[1], horizontal[0], 0.0)
outward = norm(radial)
camera = add(point, mul(outward, 20.0))
target = point
plane = None
yaw, pitch = angles(camera, target)
views.append({"name": f"top-{series}-{index:02d}", "kind": f"top-{series}",
"focus": point, "camera": camera, "target": target,
"yaw": yaw, "pitch": pitch, "clip": plane, "fov": 68.0,
"focus_radius": 18.0, "strate": row["strate"],
"value": row[value_key], "region": row[region_key],
"camera_anchor": "nearest-surface-l98-v5", "nearest_passage": nearest_id,
"route_distance_m": distance})
return views
def export_and_render(view, label, binary):
safe = view["name"].replace("/", "-")
stem = f"{safe}-{label}"
mesh = OUT / f"{stem}.vfmesh"
image = IMAGE_DIR / f"{safe}-{label}.png"
camera, target = view["camera"], view["target"]
yaw, pitch = angles(camera, target)
export_args = [binary, DATA, "--world-assembler-prototype", "--stream-benchmark",
"--stream-pipeline", "--workers=8", "--force-generate", "--clip-radius=3",
"--max-clip-level=0", "--max-level=0", "--render-distance-chunks=0",
f"--camera={fmt((*camera, yaw, pitch))}", f"--mesh-export={mesh}"]
export_row = run(f"{stem}-export", export_args, headless=True)
export_row["scene_bytes"] = mesh.stat().st_size if mesh.exists() else 0
if export_row["scene_bytes"] <= 0:
raise RuntimeError(f"empty mesh scene: {mesh}")
if label == "before-l98" and view.get("camera_anchor") == "nearest-surface-l98-v5":
anchor = nearest_surface_anchor(mesh, view["focus"])
camera = add(anchor["position_m"], mul(anchor["normal"], 8.0))
view["camera"] = camera
view["target"] = anchor["position_m"]
view["yaw"], view["pitch"] = angles(camera, view["target"])
view["clip"] = None
view["surface_anchor"] = anchor
yaw, pitch = view["yaw"], view["pitch"]
render_args = [RENDERER, mesh, image, "--size=1600x900",
f"--fov-deg={view['fov']:.1f}", f"--camera-m={fmt(camera)}",
f"--yaw-deg={yaw:.6f}", f"--pitch-deg={pitch:.6f}", "--shade=readable",
"--headlamp=on", "--height-tint=on", "--tile-edges=off", "--tile-skirts=off",
f"--height-center-m={view['focus'][2]:.6f}", "--height-range-m=10",
f"--focus-region={fmt((*view['focus'], view['focus_radius']))}"]
if view["clip"] is not None:
render_args.append(f"--clip-plane={fmt(view['clip'])}")
try:
render_row = run(f"{stem}-render", render_args, headless=True)
except RuntimeError:
if view["clip"] is None:
raise
retry_args = [arg for arg in render_args
if not (isinstance(arg, str) and arg.startswith("--clip-plane="))]
render_row = run(f"{stem}-render-no-clip-retry", retry_args, headless=True)
render_row["clip_fallback"] = True
render_row["image"] = str(image.relative_to(ROOT))
render_row["image_bytes"] = image.stat().st_size if image.exists() else 0
if render_row["image_bytes"] <= 0:
raise RuntimeError(f"empty image: {image}")
mesh.unlink()
return export_row, render_row
def main():
OUT.mkdir(parents=True, exist_ok=True)
IMAGE_DIR.mkdir(parents=True, exist_ok=True)
for path in (*BINARIES.values(), RENDERER, DATA, LAYOUT_PATH, REACH_SUMMARY):
if not path.is_file():
raise SystemExit(f"missing input: {path}")
layout = json.loads(LAYOUT_PATH.read_text(encoding="utf-8"))
reach_summary = json.loads(REACH_SUMMARY.read_text(encoding="utf-8"))
passages, routes = make_passages(layout)
views = [view for passage in passages for view in passage["views"]]
views += make_hotspot_views(routes, reach_summary)
partial_path = OUT / "runs-partial.json"
existing_records = {}
if partial_path.is_file():
for item in json.loads(partial_path.read_text(encoding="utf-8")):
existing_records[item["view"]["name"]] = item
records = []
for view in views:
prior = existing_records.get(view["name"])
camera_matches = (prior is not None and
prior["view"].get("camera_anchor") == view.get("camera_anchor"))
if camera_matches:
for key in ("camera", "target", "yaw", "pitch", "clip", "surface_anchor"):
if key in prior["view"]:
view[key] = prior["view"][key]
if camera_matches and all(version in prior and
(ROOT / prior[version]["render"]["image"]).is_file()
for version in BINARIES):
records.append(prior)
print(f"[L99CaptureSkip] complete_pair={view['name']}", flush=True)
continue
pair = {"view": view}
for label, binary in BINARIES.items():
export_row, render_row = export_and_render(view, label, binary)
pair[label] = {"export": export_row, "render": render_row}
(OUT / "runs-partial.json").write_text(json.dumps(records + [pair], indent=2) + "\n",
encoding="utf-8")
records.append(pair)
summary = {"source_layout_identity": layout["stable_identity"],
"view_count": len(views), "paired_png_count": len(views) * 2,
"passages": passages, "views": records}
(OUT / "summary.json").write_text(json.dumps(summary, indent=2) + "\n",
encoding="utf-8")
print("[L99CaptureSummary] " + json.dumps({
"views": len(views), "paired_png_count": len(views) * 2,
"images": [row[version]["render"]["image"] for row in records
for version in BINARIES]}, sort_keys=True), flush=True)
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,137 @@
#!/usr/bin/env python3
"""Compare the current unbounded passage carve with the L99 four-metre tube."""
from __future__ import annotations
import json
import re
import subprocess
import sys
import time
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
OUT = ROOT / "Saved/Linux/l99/carve-reach"
DATA = ROOT / "Standalone/data/owner_world.json"
HEADLESS = ROOT / "Saved/Linux/l99/bin/VoxelForgeCoreHeadless-l99-clang"
CAMERAS = {
"SW": (8.0, 16.0, -372.2626953125),
"FI": (25.8369140625, 3.19921875, -737.0),
"TN": (19.166015625, -14.2001953125, -753.3740234375),
"VS": (-20.0625, -5.068359375, -900.353515625),
}
SUMMARY = re.compile(r"^\[L93PassageReachSummary\] reach_m=4\.00 (.*)$", re.M)
COVERAGE = re.compile(r"^\[L93PassageReachCoverage\] (.*)$", re.M)
TILE = re.compile(
r"^\[L93PassageReachTile\] reach_m=4\.00 key=\(([-\d]+),([-\d]+),([-\d]+),lod(\d+)\) "
r"samples=(\d+) changed=(\d+) sign_flips=(\d+) moved_edges=(\d+) "
r"max_displacement_cm=([\deE+.-]+) max_displacement_xyz_vox=\(([^)]*)\) "
r"displacement_region=(\d+) normal_vertices=(\d+) max_normal_degrees=([\deE+.-]+) "
r"max_normal_xyz_vox=\(([^)]*)\) normal_region=(\d+) changed_mesh=(\d+)$", re.M)
VOXEL_SIZE_M = 0.25
def xyz(text: str) -> list[float]:
return [float(part) for part in text.split(",")]
def parse_locations(output: str, strate: str) -> tuple[list[dict], list[dict]]:
rows = []
for match in TILE.finditer(output):
(x, y, z, lod, samples, changed, flips, moved, displacement, displacement_xyz,
displacement_region, normal_vertices, angle, normal_xyz, normal_region,
changed_mesh) = match.groups()
disp_vox = xyz(displacement_xyz)
normal_vox = xyz(normal_xyz)
rows.append({
"strate": strate, "tile": [int(x), int(y), int(z), int(lod)],
"samples": int(samples), "changed_samples": int(changed),
"sign_flips": int(flips), "moved_edges": int(moved),
"max_displacement_cm": float(displacement),
"displacement_location_voxels": disp_vox,
"displacement_location_metres": [value * VOXEL_SIZE_M for value in disp_vox],
"displacement_region": int(displacement_region),
"normal_vertices": int(normal_vertices), "max_normal_degrees": float(angle),
"normal_location_voxels": normal_vox,
"normal_location_metres": [value * VOXEL_SIZE_M for value in normal_vox],
"normal_region": int(normal_region), "changed_mesh": bool(int(changed_mesh)),
})
top_displacement = sorted(rows, key=lambda row: row["max_displacement_cm"], reverse=True)[:5]
top_normals = sorted(rows, key=lambda row: row["max_normal_degrees"], reverse=True)[:5]
return top_displacement, top_normals
def top_unique(rows: list[dict], value_key: str, location_key: str) -> list[dict]:
chosen, seen = [], set()
for row in sorted(rows, key=lambda item: item[value_key], reverse=True):
if row[value_key] <= 0.0:
continue
location = tuple(round(value, 4) for value in row[location_key])
if location in seen:
continue
seen.add(location)
chosen.append(row)
if len(chosen) == 5:
break
return chosen
def run(strate: str) -> dict:
OUT.mkdir(parents=True, exist_ok=True)
log = OUT / f"reach-{strate.lower()}.log"
camera = ",".join(f"{value:.12f}" for value in CAMERAS[strate]) + ",0,0"
command = [
"taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
"timeout", "1740s", str(HEADLESS), str(DATA), "--world-assembler-prototype",
"--stream-benchmark", "--stream-pipeline", "--workers=8", "--clip-radius=3",
"--max-clip-level=8", "--max-level=8", f"--camera={camera}",
"--audit-passage-carve-reach",
]
start = time.monotonic()
with log.open("w", encoding="utf-8") as stream:
result = subprocess.run(command, cwd=ROOT, stdout=stream,
stderr=subprocess.STDOUT, check=False, timeout=1740)
output = log.read_text(encoding="utf-8", errors="replace")
summary_match, coverage_match = SUMMARY.search(output), COVERAGE.search(output)
if summary_match is None or coverage_match is None or result.returncode in (-9, 124):
raise RuntimeError(f"incomplete carve-reach audit: exit={result.returncode} {log}")
reach = dict(re.findall(r"([a-zA-Z0-9_]+)=([^ ]+)", summary_match.group(1)))
coverage = dict(re.findall(r"([a-zA-Z0-9_]+)=([^ ]+)", coverage_match.group(1)))
top_displacement, top_normals = parse_locations(output, strate)
row = {
"strate": strate, "exit_code": result.returncode,
"wall_s": round(time.monotonic() - start, 3), "coverage": coverage,
"reach_4m_summary": reach,
"reach_4m_tile_rows": len(TILE.findall(output)),
"top_displacement_tiles": top_displacement,
"top_normal_tiles": top_normals,
"log": str(log.relative_to(ROOT)),
}
print("[L99CarveReachRun] " + json.dumps(row, sort_keys=True), flush=True)
return row
def main() -> int:
if not HEADLESS.is_file() or not DATA.is_file():
raise SystemExit(f"missing input: {HEADLESS if not HEADLESS.is_file() else DATA}")
if "--summarize-only" in sys.argv:
rows = json.loads((OUT / "runs-partial.json").read_text(encoding="utf-8"))
else:
rows = []
for strate in CAMERAS:
rows.append(run(strate))
(OUT / "runs-partial.json").write_text(json.dumps(rows, indent=2) + "\n",
encoding="utf-8")
all_displacements = [row for result in rows for row in result["top_displacement_tiles"]]
all_normals = [row for result in rows for row in result["top_normal_tiles"]]
top_displacement = top_unique(all_displacements, "max_displacement_cm",
"displacement_location_metres")
top_normals = top_unique(all_normals, "max_normal_degrees", "normal_location_metres")
summary = {"runs": rows, "top_displacement_tiles": top_displacement,
"top_normal_tiles": top_normals}
(OUT / "summary.json").write_text(json.dumps(summary, indent=2) + "\n",
encoding="utf-8")
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,98 @@
#!/usr/bin/env python3
"""Three interleaved L77 passage-share censuses for L98 and L99, all four strata."""
from __future__ import annotations
import json
import re
import statistics
import subprocess
import time
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
OUT = ROOT / "Saved/Linux/l99/census"
DATA = ROOT / "Standalone/data/owner_world.json"
BINARIES = {
"L98": ROOT / "Saved/Linux/l98/profile-post-bin/VoxelForgeCoreHeadless-l98-post-profile",
"L99": ROOT / "Saved/Linux/l99/profile-bin/VoxelForgeCoreHeadless-l99-profile-clang",
}
CAMERAS = {
"SW": (8.0, 16.0, -372.2626953125),
"FI": (25.8369140625, 3.19921875, -737.0),
"TN": (19.166015625, -14.2001953125, -753.3740234375),
"VS": (-20.0625, -5.068359375, -900.353515625),
}
SAMPLE = re.compile(
r"^\[L77Sample\] pc=(0x\w+) lod=(\d+) .*?phase=(\S+) .*?weight=(\d+)$", re.M)
SAMPLER = re.compile(
r"^\[L77Sampler\] action=stop .*?samples=(\d+).*?status=PASS$", re.M)
def run(strate: str, label: str, rep: int) -> dict:
OUT.mkdir(parents=True, exist_ok=True)
log = OUT / f"{label.lower()}-{strate.lower()}-r{rep}.log"
camera = ",".join(f"{value:.12f}" for value in CAMERAS[strate]) + ",0,0"
command = [
"taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
"timeout", "1740s", str(BINARIES[label]), str(DATA),
"--world-assembler-prototype", "--stream-benchmark", "--stream-pipeline",
"--workers=8", "--clip-radius=3", "--max-clip-level=8", "--max-level=8",
f"--camera={camera}", "--profile-lod-work", "--linux77-profile",
]
start = time.monotonic()
with log.open("w", encoding="utf-8") as stream:
result = subprocess.run(command, cwd=ROOT, stdout=stream,
stderr=subprocess.STDOUT, check=False, timeout=1740)
output = log.read_text(encoding="utf-8", errors="replace")
samples = [(int(lod), phase, int(weight))
for _pc, lod, phase, weight in SAMPLE.findall(output)]
sampler = SAMPLER.search(output)
if result.returncode or not samples or sampler is None:
raise RuntimeError(f"L77 census failed: exit={result.returncode} {log}")
near = [row for row in samples if row[0] <= 2]
passage = sum(weight for _, phase, weight in near if phase == "passages")
total = sum(weight for _, _, weight in near)
row = {
"strate": strate, "binary": label, "replicate": rep,
"exit_code": result.returncode, "wall_s": round(time.monotonic() - start, 3),
"sampler_samples": int(sampler.group(1)), "near_lod_samples": len(near),
"near_lod_weighted_ticks": total,
"near_lod_passage_weighted_ticks": passage,
"near_lod_passage_share_pct": 100.0 * passage / total,
"log": str(log.relative_to(ROOT)),
}
print("[L99CensusRun] " + json.dumps(row, sort_keys=True), flush=True)
return row
def main() -> int:
for binary in (*BINARIES.values(), DATA):
if not binary.is_file():
raise SystemExit(f"missing input: {binary}")
rows = []
orders = (("L98", "L99"), ("L99", "L98"), ("L98", "L99"))
for rep, order in enumerate(orders, 1):
for label in order:
for strate in CAMERAS:
rows.append(run(strate, label, rep))
(OUT / "runs-partial.json").write_text(json.dumps(rows, indent=2) + "\n",
encoding="utf-8")
summary = []
for strate in CAMERAS:
item = {"strate": strate, "pairs": 3}
for label in BINARIES:
subset = [row for row in rows if row["strate"] == strate
and row["binary"] == label]
values = [row["near_lod_passage_share_pct"] for row in subset]
item[f"{label}_runs_pct"] = values
item[f"{label}_mean_pct"] = statistics.mean(values)
item["change_pp"] = item["L99_mean_pct"] - item["L98_mean_pct"]
summary.append(item)
(OUT / "summary.json").write_text(json.dumps(
{"runs": rows, "near_lod_passage_share": summary}, indent=2) + "\n",
encoding="utf-8")
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,124 @@
#!/usr/bin/env python3
"""Check whole-view tile hashes across W1/W8/W12 and Clang/GCC."""
from __future__ import annotations
import json
import re
import subprocess
import time
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
OUT = ROOT / "Saved/Linux/l99/hashes"
DATA = ROOT / "Standalone/data/owner_world.json"
BINARIES = {
"Clang": ROOT / "Saved/Linux/l99/bin/VoxelForgeCoreHeadless-l99-clang",
"GCC": ROOT / "Saved/Linux/l99/bin/VoxelForgeCoreHeadless-l99-gcc",
}
CAMERAS = {
"SW": (8.0, 16.0, -372.2626953125),
"FI": (25.8369140625, 3.19921875, -737.0),
"TN": (19.166015625, -14.2001953125, -753.3740234375),
"VS": (-20.0625, -5.068359375, -900.353515625),
}
TILE_HASH = re.compile(
r"^\[PipelineTileHash\] lod=(-?\d+) x=(-?\d+) y=(-?\d+) z=(-?\d+) "
r"density=(0x[0-9a-f]+) mesh=(0x[0-9a-f]+) payload=(0x[0-9a-f]+) "
r"topology=(0x[0-9a-f]+)$", re.M)
WHOLE_HASH = re.compile(
r"^\[PipelineWholeViewHash\] tiles=(\d+) density=(0x[0-9a-f]+) "
r"mesh=(0x[0-9a-f]+) payload=(0x[0-9a-f]+) topology=(0x[0-9a-f]+)$", re.M)
FIELDS = ("density", "mesh", "payload", "topology")
def run(strate: str, compiler: str, workers: int) -> dict:
OUT.mkdir(parents=True, exist_ok=True)
log = OUT / f"{strate.lower()}-{compiler.lower()}-w{workers}.log"
camera = ",".join(f"{value:.12f}" for value in CAMERAS[strate]) + ",0,0"
command = [
"taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
"timeout", "1740s", str(BINARIES[compiler]), str(DATA),
"--world-assembler-prototype", "--stream-benchmark", "--stream-pipeline",
f"--workers={workers}", "--clip-radius=3", "--max-clip-level=8",
"--max-level=8", f"--camera={camera}", "--stream-hash-tiles",
]
start = time.monotonic()
with log.open("w", encoding="utf-8") as stream:
result = subprocess.run(command, cwd=ROOT, stdout=stream,
stderr=subprocess.STDOUT, check=False, timeout=1740)
output = log.read_text(encoding="utf-8", errors="replace")
hashes = {
(int(m.group(1)), int(m.group(2)), int(m.group(3)), int(m.group(4))):
m.group(5, 6, 7, 8) for m in TILE_HASH.finditer(output)
}
whole = WHOLE_HASH.search(output)
stamp = re.search(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]{64})$", output, re.M)
if (result.returncode or whole is None or stamp is None
or len(hashes) != int(whole.group(1))):
raise RuntimeError(f"hash run failed or incomplete: exit={result.returncode} {log}")
row = {
"strate": strate, "compiler": compiler, "workers": workers,
"source_stamp": stamp.group(1), "exit_code": result.returncode,
"wall_s": round(time.monotonic() - start, 3), "tile_count": len(hashes),
"tiles_by_lod": {str(lod): sum(1 for key in hashes if key[0] == lod)
for lod in sorted({key[0] for key in hashes})},
"whole_hash": list(whole.groups()), "tile_hashes": hashes,
"log": str(log.relative_to(ROOT)),
}
print("[L99HashRun] " + json.dumps(
{key: value for key, value in row.items() if key != "tile_hashes"},
sort_keys=True), flush=True)
return row
def compare(strate: str, reference: dict, candidate: dict) -> dict:
keys = set(reference["tile_hashes"]) | set(candidate["tile_hashes"])
mismatches = {
field: sum(reference["tile_hashes"].get(key, (None,) * 4)[index]
!= candidate["tile_hashes"].get(key, (None,) * 4)[index]
for key in keys)
for index, field in enumerate(FIELDS)
}
row = {
"strate": strate, "reference": f"{reference['compiler']}-w{reference['workers']}",
"candidate": f"{candidate['compiler']}-w{candidate['workers']}",
"tile_counts_match": reference["tile_count"] == candidate["tile_count"],
"whole_hashes_match": reference["whole_hash"] == candidate["whole_hash"],
"tile_hashes_match": all(value == 0 for value in mismatches.values())
and reference["tile_hashes"].keys() == candidate["tile_hashes"].keys(),
"component_mismatch_tiles": mismatches,
"tiles_reference": reference["tile_count"], "tiles_candidate": candidate["tile_count"],
"reference_whole_hash": reference["whole_hash"],
"candidate_whole_hash": candidate["whole_hash"],
}
print("[L99HashComparison] " + json.dumps(row, sort_keys=True), flush=True)
return row
def main() -> int:
for binary in (*BINARIES.values(), DATA):
if not binary.is_file():
raise SystemExit(f"missing input: {binary}")
detail, comparisons = [], []
for strate in CAMERAS:
group = {(compiler, workers): run(strate, compiler, workers)
for compiler in BINARIES for workers in (1, 8, 12)}
reference = group[("Clang", 1)]
for key, candidate in group.items():
if key == ("Clang", 1):
continue
comparisons.append(compare(strate, reference, candidate))
detail.extend({key: value for key, value in row.items() if key != "tile_hashes"}
for row in group.values())
(OUT / "runs-partial.json").write_text(json.dumps(
{"runs": detail, "comparisons": comparisons}, indent=2) + "\n",
encoding="utf-8")
summary = {"runs": detail, "comparisons": comparisons}
(OUT / "summary.json").write_text(json.dumps(summary, indent=2) + "\n",
encoding="utf-8")
return 0 if all(row["tile_counts_match"] and row["whole_hashes_match"]
and row["tile_hashes_match"] for row in comparisons) else 1
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,103 @@
#!/usr/bin/env python3
"""L80 cold-readiness protocol: three interleaved L98/L99 pairs at W8 and W12."""
from __future__ import annotations
import json
import re
import statistics
import subprocess
import time
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
OUT = ROOT / "Saved/Linux/l99/readiness"
DATA = ROOT / "Standalone/data/owner_world.json"
BINARIES = {
"L98": ROOT / "Saved/Linux/l98/bin/VoxelForgeCoreHeadless-l98-clang19",
"L99": ROOT / "Saved/Linux/l99/bin/VoxelForgeCoreHeadless-l99-clang",
}
# Resolved owner camera index -> stratum. Keep the labels tied to the canonical layout.
CAMERAS = {
"SW": (8.0, 16.0, -372.2626953125),
"FI": (25.8369140625, 3.19921875, -737.0),
"TN": (19.166015625, -14.2001953125, -753.3740234375),
"VS": (-20.0625, -5.068359375, -900.353515625),
}
PIPELINE = re.compile(r"^\[StreamPipeline\] (.*)$", re.M)
FIELDS = re.compile(r"([a-zA-Z0-9_]+)=([^ ]+)")
NEAR = re.compile(r"^\[WorldAssemblerNearLODReady\] lods=0-2 tiles=(\d+)/(\d+) "
r"cold_ready_s=([0-9.]+)$", re.M)
def run(strate: str, workers: int, label: str, phase: str, rep: int) -> dict:
OUT.mkdir(parents=True, exist_ok=True)
log = OUT / f"w{workers}-{strate.lower()}-{label.lower()}-{phase}{rep}.log"
camera = ",".join(f"{v:.12f}" for v in CAMERAS[strate]) + ",0,0"
command = [
"taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
"env", "timeout", "1740s", str(BINARIES[label]), str(DATA),
"--world-assembler-prototype", "--stream-benchmark", "--stream-pipeline",
f"--workers={workers}", "--clip-radius=3", "--max-clip-level=8",
"--max-level=8", f"--camera={camera}", "--profile-lod-work",
"--stream-scheduler-policy=3",
]
start = time.monotonic()
with log.open("w", encoding="utf-8") as stream:
result = subprocess.run(command, cwd=ROOT, stdout=stream,
stderr=subprocess.STDOUT, check=False, timeout=1740)
output = log.read_text(encoding="utf-8", errors="replace")
pipeline_match, near_match = PIPELINE.search(output), NEAR.search(output)
if result.returncode or pipeline_match is None or near_match is None:
raise RuntimeError(f"readiness run failed: exit={result.returncode} {log}")
pipeline = dict(FIELDS.findall(pipeline_match.group(1)))
row = {
"strate": strate, "workers": workers, "binary": label, "phase": phase,
"replicate": rep, "exit_code": result.returncode,
"wall_s": round(time.monotonic() - start, 3),
"lod02_tiles": f"{near_match.group(1)}/{near_match.group(2)}",
"lod02_ready_s": float(near_match.group(3)),
"whole_view_ready_s": float(pipeline["whole_view_ready"].removesuffix("s")),
"whole_tiles": pipeline["whole_tiles"].split("/")[0],
"log": str(log.relative_to(ROOT)),
}
print("[L99ReadinessRun] " + json.dumps(row, sort_keys=True), flush=True)
return row
def main() -> int:
for binary in (*BINARIES.values(), DATA):
if not binary.is_file():
raise SystemExit(f"missing input: {binary}")
rows = []
orders = (("L98", "L99"), ("L99", "L98"), ("L98", "L99"))
for workers in (8, 12):
for strate in CAMERAS:
for label in BINARIES:
run(strate, workers, label, "warmup", 0)
for strate in CAMERAS:
for rep, order in enumerate(orders, 1):
for label in order:
rows.append(run(strate, workers, label, "measured", rep))
(OUT / "runs-partial.json").write_text(json.dumps(rows, indent=2) + "\n",
encoding="utf-8")
summary = []
for workers in (8, 12):
for strate in CAMERAS:
group = [row for row in rows
if row["workers"] == workers and row["strate"] == strate]
item = {"workers": workers, "strate": strate, "measured_pairs": 3}
for label in BINARIES:
subset = [row for row in group if row["binary"] == label]
for metric in ("lod02_ready_s", "whole_view_ready_s"):
values = [row[metric] for row in subset]
item[f"{label}_{metric}_runs"] = values
item[f"{label}_{metric}_median"] = statistics.median(values)
item[f"{label}_process_wall_s_runs"] = [row["wall_s"] for row in subset]
summary.append(item)
(OUT / "summary.json").write_text(json.dumps(
{"runs": rows, "summary": summary}, indent=2) + "\n", encoding="utf-8")
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,78 @@
#!/usr/bin/env python3
"""Count bounded-sweep candidates, outside-volume rejects, and cache lookups."""
from __future__ import annotations
import json
import re
import subprocess
import time
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
OUT = ROOT / "Saved/Linux/l99/sweep-counts"
DATA = ROOT / "Standalone/data/owner_world.json"
HEADLESS = ROOT / "Saved/Linux/l99/bin/VoxelForgeCoreHeadless-l99-clang"
CAMERAS = {
"SW": (8.0, 16.0, -372.2626953125),
"FI": (25.8369140625, 3.19921875, -737.0),
"TN": (19.166015625, -14.2001953125, -753.3740234375),
"VS": (-20.0625, -5.068359375, -900.353515625),
}
LINE = re.compile(r"^\[PassageComposerTile\] key=\([^,]+,[^,]+,[^,]+,lod(\d+)\) (.*)$", re.M)
FIELDS = re.compile(r"([a-zA-Z0-9_]+)=([^ ]+)")
COUNT_KEYS = (
"route_candidates", "sweep_segments", "sweep_pairs", "sweep_outside_pairs",
"sweep_min_updates", "sweep_base_samples", "sweep_detail_samples",
"sweep_grid_samples", "sweep_no_influence_samples", "sweep_lookups",
"sweep_cache_hits", "sweep_base_hits", "sweep_outside_hits", "sweep_missing",
)
def run(strate: str) -> dict:
OUT.mkdir(parents=True, exist_ok=True)
log = OUT / f"profile-sweep-{strate.lower()}.log"
camera = ",".join(f"{value:.12f}" for value in CAMERAS[strate]) + ",0,0"
command = [
"taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
"timeout", "1740s", str(HEADLESS), str(DATA), "--world-assembler-prototype",
"--stream-benchmark", "--stream-pipeline", "--workers=8", "--clip-radius=3",
"--max-clip-level=8", "--max-level=8", f"--camera={camera}",
"--profile-passage-composer",
]
start = time.monotonic()
with log.open("w", encoding="utf-8") as stream:
result = subprocess.run(command, cwd=ROOT, stdout=stream,
stderr=subprocess.STDOUT, check=False, timeout=1740)
output = log.read_text(encoding="utf-8", errors="replace")
if result.returncode != 0:
raise RuntimeError(f"sweep count run failed: exit={result.returncode} {log}")
rows = []
for level_text, tail in LINE.findall(output):
level = int(level_text)
if level > 2:
continue
fields = dict(FIELDS.findall(tail))
rows.append({"lod": level, **{key: int(fields.get(key, 0)) for key in COUNT_KEYS}})
if not rows:
raise RuntimeError(f"no profiled near-set passage tiles: {log}")
totals = {key: sum(row[key] for row in rows) for key in COUNT_KEYS}
row = {
"strate": strate, "workers": 8, "near_profiled_tiles": len(rows),
"near_tiles_by_lod": {str(level): sum(1 for item in rows if item["lod"] == level)
for level in range(3)},
"counters": totals, "wall_s": round(time.monotonic() - start, 3),
"log": str(log.relative_to(ROOT)),
}
print("[L99SweepCounts] " + json.dumps(row, sort_keys=True), flush=True)
return row
def main() -> int:
rows = [run(strate) for strate in CAMERAS]
(OUT / "summary.json").write_text(json.dumps({"runs": rows}, indent=2) + "\n",
encoding="utf-8")
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,44 @@
# L99 matched captures
Each row links a before image from L98 Clang and an after image from L99 Clang. Images are 1600×900; focus coordinates are world metres.
| View | Focus / measured change | Before | After |
|---|---|---|---|
| `passage-0-1-0-upper-mouth` | passage/0-1/0 · (8.000, 16.000, -371.893) | [L98](passage-0-1-0-upper-mouth-before-l98.png) | [L99](passage-0-1-0-upper-mouth-after-l99.png) |
| `passage-0-1-0-lower-mouth` | passage/0-1/0 · (25.837, 3.199, -733.630) | [L98](passage-0-1-0-lower-mouth-before-l98.png) | [L99](passage-0-1-0-lower-mouth-after-l99.png) |
| `passage-0-1-0-mid-tunnel` | passage/0-1/0 · (-5.507, 20.753, -552.274) | [L98](passage-0-1-0-mid-tunnel-before-l98.png) | [L99](passage-0-1-0-mid-tunnel-after-l99.png) |
| `passage-1-2-0-upper-mouth` | passage/1-2/0 · (19.166, -14.200, -728.831) | [L98](passage-1-2-0-upper-mouth-before-l98.png) | [L99](passage-1-2-0-upper-mouth-after-l99.png) |
| `passage-1-2-0-lower-mouth` | passage/1-2/0 · (19.166, -14.200, -750.004) | [L98](passage-1-2-0-lower-mouth-before-l98.png) | [L99](passage-1-2-0-lower-mouth-after-l99.png) |
| `passage-1-2-0-mid-tunnel` | passage/1-2/0 · (-8.533, 12.539, -740.857) | [L98](passage-1-2-0-mid-tunnel-before-l98.png) | [L99](passage-1-2-0-mid-tunnel-after-l99.png) |
| `passage-2-3-0-upper-mouth` | passage/2-3/0 · (-20.062, -5.068, -809.025) | [L98](passage-2-3-0-upper-mouth-before-l98.png) | [L99](passage-2-3-0-upper-mouth-after-l99.png) |
| `passage-2-3-0-lower-mouth` | passage/2-3/0 · (-20.062, -5.068, -896.983) | [L98](passage-2-3-0-lower-mouth-before-l98.png) | [L99](passage-2-3-0-lower-mouth-after-l99.png) |
| `passage-2-3-0-mid-tunnel` | passage/2-3/0 · (15.521, 4.241, -854.448) | [L98](passage-2-3-0-mid-tunnel-before-l98.png) | [L99](passage-2-3-0-mid-tunnel-after-l99.png) |
| `top-displacement-01` | VS · 25.88 cm · (18.000, -2.000, -817.035) | [L98](top-displacement-01-before-l98.png) | [L99](top-displacement-01-after-l99.png) |
| `top-displacement-02` | TN · 21.74 cm · (26.000, -4.000, -692.765) | [L98](top-displacement-02-before-l98.png) | [L99](top-displacement-02-after-l99.png) |
| `top-displacement-03` | SW · 20.11 cm · (-23.127, 4.000, -490.000) | [L98](top-displacement-03-before-l98.png) | [L99](top-displacement-03-after-l99.png) |
| `top-displacement-04` | FI · 19.80 cm · (16.000, -17.133, -668.000) | [L98](top-displacement-04-before-l98.png) | [L99](top-displacement-04-after-l99.png) |
| `top-displacement-05` | SW · 19.01 cm · (20.000, 15.067, -500.000) | [L98](top-displacement-05-before-l98.png) | [L99](top-displacement-05-after-l99.png) |
| `top-normal-01` | VS · 49.82° · (39.696, 6.000, -820.000) | [L98](top-normal-01-before-l98.png) | [L99](top-normal-01-after-l99.png) |
| `top-normal-02` | TN · 41.79° · (34.000, 5.849, -892.000) | [L98](top-normal-02-before-l98.png) | [L99](top-normal-02-after-l99.png) |
| `top-normal-03` | TN · 35.51° · (38.000, 3.770, -844.000) | [L98](top-normal-03-before-l98.png) | [L99](top-normal-03-after-l99.png) |
| `top-normal-04` | VS · 35.05° · (-6.234, 28.000, -824.000) | [L98](top-normal-04-before-l98.png) | [L99](top-normal-04-after-l99.png) |
| `top-normal-05` | FI · 29.91° · (-39.904, -16.000, -690.000) | [L98](top-normal-05-before-l98.png) | [L99](top-normal-05-after-l99.png) |
## Captures (fixed)
The ten hotspot pairs now export their matching LOD2 audit tile. Each audit point is centered and 3.25–3.29 m from the camera; the nearest before/after surface triangles lie within 0.09 m of it. Displacement images use an oblique passage view; normal-change images use normal-color shading. Changed-pixel counts are measured on decoded 1600×900 RGB pixels (any channel differs); each difference image is absolute RGB diff amplified 64×. No pair is identical.
| View | Strate | Surface; nearest passage distance | Changed pixels | L98 | L99 | Abs diff ×64 |
|---|---|---|---:|---|---|---|
| `top-displacement-01` | VS | passage mouth; passage/2-3/0 · 3.17 m | 557,549 | [Before](top-displacement-01-before-l98.png) | [After](top-displacement-01-after-l99.png) | [Diff](top-displacement-01-diff-amplified.png) |
| `top-displacement-02` | TN | passage upper wall/roof; passage/0-1/0 · 3.47 m | 565,587 | [Before](top-displacement-02-before-l98.png) | [After](top-displacement-02-after-l99.png) | [Diff](top-displacement-02-diff-amplified.png) |
| `top-displacement-03` | SW | passage upper wall/roof; passage/0-1/0 · 3.45 m | 760,804 | [Before](top-displacement-03-before-l98.png) | [After](top-displacement-03-after-l99.png) | [Diff](top-displacement-03-diff-amplified.png) |
| `top-displacement-04` | FI | passage upper wall/roof; passage/0-1/0 · 3.47 m | 729,645 | [Before](top-displacement-04-before-l98.png) | [After](top-displacement-04-after-l99.png) | [Diff](top-displacement-04-diff-amplified.png) |
| `top-displacement-05` | SW | passage wall; passage/0-1/0 · 3.51 m | 372,637 | [Before](top-displacement-05-before-l98.png) | [After](top-displacement-05-after-l99.png) | [Diff](top-displacement-05-diff-amplified.png) |
| `top-normal-01` | VS | other strata rock wall outside envelope; passage/2-3/0 · 24.47 m | 423,185 | [Before](top-normal-01-before-l98.png) | [After](top-normal-01-after-l99.png) | [Diff](top-normal-01-diff-amplified.png) |
| `top-normal-02` | TN | other strata rock wall outside envelope; passage/2-3/0 · 19.85 m | 539,369 | [Before](top-normal-02-before-l98.png) | [After](top-normal-02-after-l99.png) | [Diff](top-normal-02-diff-amplified.png) |
| `top-normal-03` | TN | other strata rock wall outside envelope; passage/2-3/0 · 23.26 m | 738,068 | [Before](top-normal-03-before-l98.png) | [After](top-normal-03-after-l99.png) | [Diff](top-normal-03-diff-amplified.png) |
| `top-normal-04` | VS | rock overhang/underside near passage; passage/2-3/0 · 13.06 m | 770,128 | [Before](top-normal-04-before-l98.png) | [After](top-normal-04-after-l99.png) | [Diff](top-normal-04-diff-amplified.png) |
| `top-normal-05` | FI | rock overhang/island underside outside envelope; passage/0-1/0 · 24.12 m | 291,241 | [Before](top-normal-05-before-l98.png) | [After](top-normal-05-after-l99.png) | [Diff](top-normal-05-diff-amplified.png) |
**Complete:** 19 matched views, 38 before/after PNGs, and 10 amplified hotspot diffs.
Binary file not shown.

After

Width:  |  Height:  |  Size: 199 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 269 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 249 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB