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
This commit is contained in:
1 parent
ba017077d1
commit
8be1c4d9ef
27 files changed
+1787
-55
No files matched your search
@@ -0,0 +1,89 @@
|
||||
#!/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()
|
||||
Reference in new issue
Block a user