Files
VoxelForge/Standalone/Research/WorldAssemblerM3/Linux35/AuditTunnelMeshNormalsL35.py
Fr0zkaandClaude Opus 5.5 ba017077d1 wip(assembler): round L35 -- smooth passage walls without regular ribs (evaluator v21)
The shared passage carve took the minimum of piecewise-linear capsules (scallops at joins) and added
wall relief on regular 8 m / 4 m octaves, which read as evenly spaced ribs. Interior sweep joins are
now Catmull-Rom smoothed and the relief uses irregular rotated 43/29/19 m scales; the natural wobble
remains. New axial ripple audit (0.125 m radius samples, 0.5-8 m periods): beaded control and L34
geometry fail, all 24 passage rows pass. FI mouth tread apron tapered. 30/30 determinism, 16/16
invariants, ring audits LOD0-8 pass; cost within noise.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CSFYgsdW8ejZSyiMf4qRSX
2026-10-22 07:28:50 +02:00

124 lines
5.1 KiB
Python

#!/usr/bin/env python3
"""Measure axial normal ripple in a camera-matched tunnel mesh window."""
import importlib.util
import math
import re
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
RESEARCH = Path(__file__).resolve().parent
RING_AUDIT = ROOT / "Standalone/Research/WorldAssemblerM3/Linux32/AuditRingTerraceL32.py"
SPEC = importlib.util.spec_from_file_location("ring_profile_l32", RING_AUDIT)
PROFILE = importlib.util.module_from_spec(SPEC)
SPEC.loader.exec_module(PROFILE)
CAMERA_LOG = ROOT / "Saved/Linux/l34/run-capture-standard-set-final-radius3.log"
CAMERA_RE = re.compile(
r"^\[L34CaptureCamera\] view=(\S+).*?camera_m=\(([^)]*)\) "
r"target_m=\(([^)]*)\)", re.M)
MAXIMUM_AXIAL_NORMAL_AMPLITUDE = 0.0255
def normalize(v):
length = math.sqrt(sum(x * x for x in v))
return tuple(x / length for x in v) if length > 1.0e-12 else (0.0, 0.0, 0.0)
def fit(samples, spacing=0.25):
if len(samples) < 32:
return 0.0, 0.0
best_period, best_amplitude = 0.0, 0.0
for index in range(8, 129):
period = index * 0.0625
if period > 8.0:
break
omega = 2.0 * math.pi / period
cc = ss = cs = yc = ys = 0.0
for i, value in enumerate(samples):
phase = omega * (i * spacing)
c, s = math.cos(phase), math.sin(phase)
cc += c * c
ss += s * s
cs += c * s
yc += value * c
ys += value * s
determinant = cc * ss - cs * cs
if determinant <= 1.0e-9:
continue
cosine = (yc * ss - ys * cs) / determinant
sine = (ys * cc - yc * cs) / determinant
amplitude = math.hypot(cosine, sine)
if amplitude > best_amplitude:
best_period, best_amplitude = period, amplitude
return best_period, best_amplitude
def main():
if len(sys.argv) != 3:
raise SystemExit("usage: AuditTunnelMeshNormalsL35.py MESH L34_VIEW_NAME")
mesh = Path(sys.argv[1])
view_name = sys.argv[2]
cameras = {name: (tuple(map(float, camera.split(","))),
tuple(map(float, target.split(","))))
for name, camera, target in CAMERA_RE.findall(
CAMERA_LOG.read_text(encoding="utf-8", errors="replace"))}
if view_name not in cameras:
raise SystemExit(f"missing L34 camera: {view_name}")
camera, target = cameras[view_name]
axis = normalize(tuple(target[i] - camera[i] for i in range(3)))
down = (0.0, 0.0, -1.0)
axis_down = sum(axis[i] * down[i] for i in range(3))
floor_dir = normalize(tuple(down[i] - axis_down * axis[i] for i in range(3)))
x_min, x_max = min(camera[0], target[0]) - 8.0, max(camera[0], target[0]) + 8.0
y_min, y_max = min(camera[1], target[1]) - 8.0, max(camera[1], target[1]) + 8.0
z_min, z_max = min(camera[2], target[2]) - 8.0, max(camera[2], target[2]) + 8.0
voxel_um, triangles, tiles = PROFILE.read_scene(
str(mesh), None, z_min, z_max, (x_min, x_max, y_min, y_max))
spacing = 0.25
bin_count = 48
sums = [0.0] * bin_count
counts = [0] * bin_count
for _, _, verts in triangles:
centroid = tuple(sum(v[0][axis_index] for v in verts) / 3.0
for axis_index in range(3))
delta = tuple(centroid[i] - camera[i] for i in range(3))
along = sum(delta[i] * axis[i] for i in range(3))
if not 0.0 <= along < bin_count * spacing:
continue
radial = tuple(delta[i] - along * axis[i] for i in range(3))
radius = math.sqrt(sum(value * value for value in radial))
if not 0.5 <= radius <= 4.0:
continue
radial_unit = tuple(value / radius for value in radial)
if sum(radial_unit[i] * floor_dir[i] for i in range(3)) < 0.25:
continue
normal = normalize(tuple(sum(v[1][axis_index] for v in verts) / 3.0
for axis_index in range(3)))
axial = abs(sum(normal[i] * axis[i] for i in range(3)))
bin_index = int(along / spacing)
sums[bin_index] += axial
counts[bin_index] += 1
valid = [(i, sums[i] / counts[i]) for i in range(bin_count) if counts[i]]
if len(valid) < 32:
print(f"[TunnelMeshNormalAudit] mesh={mesh} view={view_name} tiles={tiles} "
f"bins={len(valid)}/{bin_count} status=INSUFFICIENT")
raise SystemExit(1)
values = [sums[i] / counts[i] if counts[i] else 0.0 for i in range(bin_count)]
mean = sum(value for _, value in valid) / len(valid)
values = [value - mean for value in values]
period, amplitude = fit(values, spacing)
passed = amplitude < MAXIMUM_AXIAL_NORMAL_AMPLITUDE
print(f"[TunnelMeshNormalAudit] mesh={mesh} view={view_name} voxel_um={voxel_um} "
f"tiles={tiles} bins={len(valid)}/{bin_count} floor_sector=0.25 "
f"period_m={period:.3f} axial_normal_amplitude={amplitude:.5f} "
f"axial_normal_amplitude_deg={math.degrees(math.asin(min(1.0, amplitude))):.3f} "
f"threshold={MAXIMUM_AXIAL_NORMAL_AMPLITUDE:.4f} "
f"status={'PASS' if passed else 'FAIL'}")
if not passed:
raise SystemExit(1)
if __name__ == "__main__":
main()