Files
VoxelForge/Standalone/Research/WorldAssemblerM3/Linux36/ProbeMouthComponentsL36.py
Fr0zkaandClaude Opus 5.5 8be1c4d9ef wip(assembler): round L36 -- tunnel floor without washboard; FI mouth without shards (evaluator v23)
The walk tread was built per segment as a one-sided density clamp, so its crossing with the native
field moved at segment joins (L35: 2.25-2.48 cm washboard). Treads now overlap up to 2 m as bounded
signed-distance slabs (0.06 cm residual). The FI mouth's crater detail and tread met without a smooth
join; v23 filters the local field, drops thin isolated solids and smooth-unions the tread with the
terrain. New floor ripple and mouth fragment/crease audits with L35 as failing controls. 24/24
passage rows, 30/30 determinism, 16/16 invariants, LOD0-8 rings; cost within noise.

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

90 lines
3.7 KiB
Python

#!/usr/bin/env python3
"""Inspect connected LOD0 surface components around the FloatingIslands mouth."""
import argparse
import importlib.util
import math
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
PROFILE_PATH = ROOT / "Standalone/Research/WorldAssemblerM3/Linux32/AuditRingTerraceL32.py"
SPEC = importlib.util.spec_from_file_location("ring_profile", PROFILE_PATH)
PROFILE = importlib.util.module_from_spec(SPEC)
SPEC.loader.exec_module(PROFILE)
def main():
parser = argparse.ArgumentParser()
parser.add_argument("mesh")
parser.add_argument("--center", nargs=2, type=float, default=(5.6, 0.445))
parser.add_argument("--radius", type=float, default=14.0)
parser.add_argument("--z-min", type=float, default=-66.0)
parser.add_argument("--z-max", type=float, default=-50.0)
parser.add_argument("--minimum-triangles", type=int, default=1)
args = parser.parse_args()
cx, cy = args.center
bounds = (cx - args.radius, cx + args.radius, cy - args.radius, cy + args.radius)
voxel_um, triangles, tiles = PROFILE.read_scene(
args.mesh, 0, args.z_min, args.z_max, bounds)
parent = list(range(len(triangles)))
sizes = [1] * len(triangles)
vertex_owner = {}
def find(index):
while parent[index] != index:
parent[index] = parent[parent[index]]
index = parent[index]
return index
def union(a, b):
a, b = find(a), find(b)
if a == b:
return
if sizes[a] < sizes[b]:
a, b = b, a
parent[b] = a
sizes[a] += sizes[b]
for index, (_, _, verts) in enumerate(triangles):
for point, _ in verts:
key = tuple(round(value, 5) for value in point)
other = vertex_owner.setdefault(key, index)
union(index, other)
components = {}
for index, (_, _, verts) in enumerate(triangles):
root = find(index)
item = components.setdefault(root, {"count": 0, "min": [math.inf] * 3,
"max": [-math.inf] * 3, "area": 0.0,
"sum": [0.0] * 3})
item["count"] += 1
points = [v[0] for v in verts]
for point in points:
for axis in range(3):
item["min"][axis] = min(item["min"][axis], point[axis])
item["max"][axis] = max(item["max"][axis], point[axis])
item["sum"][axis] += point[axis]
ab = tuple(points[1][axis] - points[0][axis] for axis in range(3))
ac = tuple(points[2][axis] - points[0][axis] for axis in range(3))
cross = (ab[1] * ac[2] - ab[2] * ac[1],
ab[2] * ac[0] - ab[0] * ac[2],
ab[0] * ac[1] - ab[1] * ac[0])
item["area"] += 0.5 * math.sqrt(sum(value * value for value in cross))
ordered = sorted(components.values(), key=lambda item: item["count"], reverse=True)
print(f"[MouthComponentProbe] mesh={args.mesh} voxel_um={voxel_um} lod=0 "
f"tiles={tiles} triangles={len(triangles)} components={len(ordered)} "
f"bounds=({bounds[0]:.3f},{bounds[1]:.3f},{bounds[2]:.3f},{bounds[3]:.3f}) "
f"z={args.z_min:.3f}..{args.z_max:.3f}")
for item in ordered:
if item["count"] < args.minimum_triangles:
continue
centroid = tuple(value / (3 * item["count"]) for value in item["sum"])
print(f"[MouthComponent] triangles={item['count']} area_m2={item['area']:.4f} "
f"bounds=({','.join(f'{v:.3f}' for v in item['min'])}).."
f"({','.join(f'{v:.3f}' for v in item['max'])}) "
f"centroid=({','.join(f'{v:.3f}' for v in centroid)})")
if __name__ == "__main__":
main()