Files
VoxelForge/Standalone/Research/WorldAssemblerM3/Linux32/FindRingWindowL32.py
T
Fr0zkaandClaude Opus 5.5 d0158c6019 wip(assembler): round L32 -- smooth Gradient interface at full amplitude (no rings, not flat)
Band faces now use signed distance to the displaced solid intervals, continuous across bands, so
MC interpolates the interface instead of terracing at density levels. The Gradient amplitude is
restored to width/8 (about 4 m peak-to-peak at 16 m). Ring/terrace audit at LOD0-8 on the FI floor and SW
ceiling passes; negative controls: L30 (rings) and L31 (flat) both fail. Hole/interface audits,
16 passage rows and 24/24 determinism pass. Evaluator 20. Cost vs L31 SW +1.9%, FI +1.5%.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CSFYgsdW8ejZSyiMf4qRSX
2026-10-21 20:27:29 +02:00

123 lines
6.3 KiB
Python

#!/usr/bin/env python3
"""Stream selected-LOD tiles and find face windows that pass the ring audit."""
import argparse
import array
import importlib.util
from pathlib import Path
import resource
import struct
import sys
def main():
parser = argparse.ArgumentParser()
parser.add_argument("scene")
parser.add_argument("--lod", type=int, required=True)
parser.add_argument("--surface", choices=("floor", "ceiling"), required=True)
parser.add_argument("--height-min-m", type=float, required=True)
parser.add_argument("--height-max-m", type=float, required=True)
parser.add_argument("--preferred-height-m", type=float,
help="lock overlapping-face selection near this expected surface height")
parser.add_argument("--step-m", type=float, default=0.5)
parser.add_argument("--tile-key", nargs=3, type=int,
help="audit only this tile's x/y/z key (LOD is supplied separately)")
args = parser.parse_args()
audit_path = Path(__file__).with_name("AuditRingTerraceL32.py")
spec = importlib.util.spec_from_file_location("ring_audit_l32", audit_path)
audit = importlib.util.module_from_spec(spec)
spec.loader.exec_module(audit)
want_up = args.surface == "floor"
candidates = []
with open(args.scene, "rb") as stream:
if stream.read(8) != b"VFMSH001":
raise SystemExit("invalid VFMSH001 scene")
version, voxel_um, tile_count = struct.unpack("<3I", stream.read(12))
stream.seek(40, 1)
if version != 1:
raise SystemExit("unsupported mesh scene")
for _ in range(tile_count):
header = stream.read(36)
key = struct.unpack("<4i", header[:16])
surface_count, _, vertex_count, index_count, mask_count = struct.unpack(
"<5I", header[16:])
payload = vertex_count * 24 + index_count * 4 + mask_count
if key[3] != args.lod or (args.tile_key and tuple(key[:3]) != tuple(args.tile_key)):
stream.seek(payload, 1)
continue
positions = array.array("f")
positions.frombytes(stream.read(vertex_count * 12))
normals = array.array("f")
normals.frombytes(stream.read(vertex_count * 12))
indices = array.array("I")
indices.frombytes(stream.read(index_count * 4))
masks = stream.read(mask_count)
if sys.byteorder != "little":
positions.byteswap()
normals.byteswap()
indices.byteswap()
triangles = []
vertex_margin = voxel_um / 1_000_000.0 * 0.5
for offset in range(0, index_count, 3):
if masks and masks[offset // 3]:
continue
ids = indices[offset:offset + 3]
if max(ids) >= surface_count:
continue
verts = []
for index in ids:
p = index * 3
verts.append(((positions[p], positions[p + 1], positions[p + 2]),
(normals[p], normals[p + 1], normals[p + 2])))
average_nz = sum(vertex[1][2] for vertex in verts) / 3.0
if (want_up and average_nz < 0.35) or (not want_up and average_nz > -0.35):
continue
z_values = [vertex[0][2] for vertex in verts]
mean_z = sum(z_values) / 3.0
if (mean_z < args.height_min_m or mean_z > args.height_max_m or
min(z_values) < args.height_min_m - vertex_margin or
max(z_values) > args.height_max_m + vertex_margin):
continue
triangles.append((key[3], average_nz, tuple(verts)))
if not triangles:
continue
extent = 32.0 * (1 << key[3]) * voxel_um / 1_000_000.0
cx, cy = (key[0] + 0.5) * extent, (key[1] + 0.5) * extent
radius = extent * 0.5
bounds = (cx - radius, cx + radius, cy - radius, cy + radius)
cell_size = max(2.0, 16.0 * voxel_um / 1_000_000.0 * (1 << key[3]))
buckets = audit.build_buckets(triangles, want_up, bounds, cell_size)
profiles = []
preferred_height = args.preferred_height_m
if preferred_height is None:
preferred_height = args.height_max_m if want_up else args.height_min_m
for _, points in audit.make_lines(cx, cy, radius, args.step_m):
profiles.append(audit.audit_profile(points, buckets, want_up,
0.5, 0.0005, 0.1, cell_size, preferred_height))
valid = [profile for profile in profiles if profile["valid"]]
coverage = min(profile["coverage"] for profile in profiles)
amplitude = max((profile["height_max"] for profile in valid), default=0.0) - \
min((profile["height_min"] for profile in valid), default=0.0)
longest = max((run[0] for profile in valid for run in profile["plateaus"]),
default=0.0)
max_normal = max((profile["max_angle"] for profile in valid), default=float("inf"))
passed = (len(valid) == len(profiles) and coverage >= 0.75 and amplitude >= 3.0
and longest < 0.5 and max_normal <= 20.0)
candidates.append((passed, amplitude, -max_normal, key, cx, cy,
len(triangles), coverage, longest, max_normal))
stream.seek(payload - (vertex_count * 24 + index_count * 4 + mask_count), 1)
candidates.sort(reverse=True, key=lambda item: (item[0], item[1], item[2]))
print(f"[RingWindowSearch] lod={args.lod} surface={args.surface} tiles={len(candidates)} "
f"height_window_m={args.height_min_m:.3f}..{args.height_max_m:.3f} "
f"step_m={args.step_m:.3f}")
for passed, amplitude, neg_normal, key, cx, cy, count, coverage, longest, normal in candidates[:40]:
print(f"[RingWindowCandidate] status={'PASS' if passed else 'FAIL'} key={key} "
f"center_m=({cx:.3f},{cy:.3f}) triangles={count} coverage={coverage:.3f} "
f"amplitude_m={amplitude:.6f} longest_flat_m={longest:.6f} "
f"normal_jump_deg={normal:.6f}")
print(f"[RingWindowRSS] peak_rss_kib="
f"{resource.getrusage(resource.RUSAGE_SELF).ru_maxrss}")
if __name__ == "__main__":
main()