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
90 lines
3.7 KiB
Python
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()
|