- Unreal: ready queue depth 8 -> 16 (cap 4 worker-sets / 64), startup mesh-apply cap 12 -> 24 within the unchanged 14 ms budget. Owner SW arrival, same 2,850 keys: initial set ready 4.78 -> 3.74 s, queue p95 123 -> 35 ms, request-to-apply p95 139 -> 45 ms; walkable gate 1.07 -> 1.11 s; per-frame apply p95 0.73 ms. - Core composer: mouth-support segments filtered by tile bounds, mouth relief result reused (route checks -80/-87% in passage tiles). Hashes identical W1/W8/W12, Clang and GCC. - Opt-in per-tile composer counters. Verify control meshes stored as .xz (78.5 -> 18.4 MB). - Fast tier 269 PASS. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
211 lines
9.6 KiB
Python
211 lines
9.6 KiB
Python
#!/usr/bin/env python3
|
|
"""Measure the L46 entrance floor from open terrain through its first ten metres."""
|
|
from __future__ import annotations
|
|
|
|
import importlib.util
|
|
import argparse
|
|
import json
|
|
import math
|
|
import os
|
|
from pathlib import Path
|
|
import sys
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[4]
|
|
BASELINES = Path(os.environ.get("VOXELFORGE_VERIFY_OUT", ROOT / "Saved/Linux/l47")) / "baselines"
|
|
PROFILE_PATH = ROOT / "Standalone/Research/WorldAssemblerM3/Linux32/AuditRingTerraceL32.py"
|
|
spec = importlib.util.spec_from_file_location("ring_profile", PROFILE_PATH)
|
|
if spec is None or spec.loader is None:
|
|
raise RuntimeError(f"cannot load {PROFILE_PATH}")
|
|
PROFILE = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(PROFILE)
|
|
|
|
ARTIFACTS = ROOT / "Saved/Linux/l47"
|
|
PARSER = argparse.ArgumentParser(description=__doc__)
|
|
PARSER.add_argument("--layout", type=Path,
|
|
default=BASELINES / "landmarks/l46-entrance-control-layout.json")
|
|
PARSER.add_argument("--mesh", type=Path,
|
|
default=BASELINES / "landmarks/l46-entrance-control.vfmesh")
|
|
PARSER.add_argument("--out", type=Path, default=ARTIFACTS / "entrance-profile-before.json")
|
|
PARSER.add_argument("--expect", choices=("pass", "fail", "measure"), default="measure")
|
|
PARSER.add_argument("--lod", default="0", help="LOD level, or 'all' to include every exported level")
|
|
OPTIONS = PARSER.parse_args()
|
|
LAYOUT = OPTIONS.layout.resolve()
|
|
MESH = OPTIONS.mesh.resolve()
|
|
OUT = OPTIONS.out.resolve()
|
|
|
|
layout = json.loads(LAYOUT.read_text(encoding="utf-8"))
|
|
edge = layout["cavern_edges"][0]
|
|
controls = [[value / 1_000_000.0 for value in point]
|
|
for point in edge["control_points_um"]]
|
|
|
|
|
|
def bezier(t: float) -> tuple[float, float, float]:
|
|
u = 1.0 - t
|
|
weights = (u ** 3, 3.0 * u * u * t, 3.0 * u * t * t, t ** 3)
|
|
return tuple(sum(weights[i] * controls[i][axis] for i in range(4))
|
|
for axis in range(3))
|
|
|
|
|
|
route = [bezier(i / 4096.0) for i in range(4097)]
|
|
arc = [0.0]
|
|
for a, b in zip(route, route[1:]):
|
|
arc.append(arc[-1] + math.hypot(b[0] - a[0], b[1] - a[1]))
|
|
|
|
|
|
def route_at(distance: float) -> tuple[float, float, float]:
|
|
if distance < 0.0:
|
|
start = route[0]
|
|
dx = route[1][0] - start[0]
|
|
dy = route[1][1] - start[1]
|
|
dz = route[1][2] - start[2]
|
|
horizontal = math.hypot(dx, dy)
|
|
scale = distance / (arc[1] - arc[0])
|
|
return start[0] + dx * scale, start[1] + dy * scale, start[2] + dz * scale
|
|
index = min(len(arc) - 2, max(0, next(
|
|
(i for i, value in enumerate(arc) if value >= distance), len(arc) - 1) - 1))
|
|
span = arc[index + 1] - arc[index]
|
|
t = 0.0 if span <= 0.0 else (distance - arc[index]) / span
|
|
return tuple(route[index][axis] + (route[index + 1][axis] - route[index][axis]) * t
|
|
for axis in range(3))
|
|
|
|
|
|
entry = route[0]
|
|
tangent = route[1]
|
|
tx, ty = tangent[0] - entry[0], tangent[1] - entry[1]
|
|
length = math.hypot(tx, ty)
|
|
tx, ty = tx / length, ty / length
|
|
nx, ny = -ty, tx
|
|
requested_lod = None if OPTIONS.lod == "all" else int(OPTIONS.lod)
|
|
voxel_um, triangles, tile_count = PROFILE.read_scene(
|
|
str(MESH), requested_lod, entry[2] - 7.0, entry[2] + 5.0,
|
|
(entry[0] - 14.0, entry[0] + 14.0, entry[1] - 14.0, entry[1] + 14.0))
|
|
buckets = PROFILE.build_buckets(
|
|
triangles, True,
|
|
(entry[0] - 14.0, entry[0] + 14.0, entry[1] - 14.0, entry[1] + 14.0),
|
|
cell_size=1.0)
|
|
down_buckets = PROFILE.build_buckets(
|
|
triangles, False,
|
|
(entry[0] - 14.0, entry[0] + 14.0, entry[1] - 14.0, entry[1] + 14.0),
|
|
cell_size=1.0)
|
|
mesh_points = [vertex[0] for triangle in triangles for vertex in triangle[2]]
|
|
print(f"[EntranceProfileGeometry] entry_m=({entry[0]:.3f},{entry[1]:.3f},{entry[2]:.3f}) "
|
|
f"point10_m=({route_at(10.0)[0]:.3f},{route_at(10.0)[1]:.3f},"
|
|
f"{route_at(10.0)[2]:.3f}) mesh_xy=({min(p[0] for p in mesh_points):.3f},"
|
|
f"{max(p[0] for p in mesh_points):.3f},{min(p[1] for p in mesh_points):.3f},"
|
|
f"{max(p[1] for p in mesh_points):.3f}) mesh_z=({min(p[2] for p in mesh_points):.3f},"
|
|
f"{max(p[2] for p in mesh_points):.3f})")
|
|
for sample_distance in range(11):
|
|
sx, sy, expected = route_at(float(sample_distance))
|
|
bucket = buckets.get((math.floor(sx), math.floor(sy)), ())
|
|
hit = PROFILE.sample_surface(buckets, sx, sy, True, 1.0,
|
|
preferred_height=expected)
|
|
down_hit = PROFILE.sample_surface(down_buckets, sx, sy, False, 1.0,
|
|
preferred_height=expected)
|
|
print(f"[EntranceProfileBucket] distance_m={sample_distance:.1f} "
|
|
f"xy_m=({sx:.3f},{sy:.3f}) triangles={len(bucket)} "
|
|
f"hit_z_m={None if hit is None else round(hit[0], 4)} "
|
|
f"down_hit_z_m={None if down_hit is None else round(down_hit[0], 4)} "
|
|
f"expected_z_m={expected:.4f}")
|
|
|
|
rows = []
|
|
for rail, lateral in (("left", -1.5), ("center", 0.0), ("right", 1.5)):
|
|
distance = -5.0
|
|
while distance <= 10.000001:
|
|
x, y, expected = route_at(distance)
|
|
x += nx * lateral
|
|
y += ny * lateral
|
|
hit = PROFILE.sample_surface(buckets, x, y, True, 1.0,
|
|
preferred_height=expected)
|
|
rows.append({
|
|
"rail": rail,
|
|
"distance_m": round(distance, 3),
|
|
"x_m": x,
|
|
"y_m": y,
|
|
"expected_floor_m": expected,
|
|
"surface_z_m": None if hit is None else hit[0],
|
|
"normal_z": None if hit is None else hit[1][2],
|
|
"residual_m": None if hit is None else hit[0] - expected,
|
|
})
|
|
distance += 0.125
|
|
|
|
results = {}
|
|
for rail in ("left", "center", "right"):
|
|
profile = [row for row in rows if row["rail"] == rail and row["surface_z_m"] is not None
|
|
and 0.0 <= row["distance_m"] <= 10.0]
|
|
jumps = []
|
|
for previous, current in zip(profile, profile[1:]):
|
|
expected_delta = current["expected_floor_m"] - previous["expected_floor_m"]
|
|
residual_delta = current["residual_m"] - previous["residual_m"]
|
|
if abs(residual_delta) >= 0.025:
|
|
jumps.append({"from_m": previous["distance_m"], "to_m": current["distance_m"],
|
|
"delta_m": current["surface_z_m"] - previous["surface_z_m"],
|
|
"residual_delta_m": residual_delta,
|
|
"expected_delta_m": expected_delta})
|
|
residuals = [row["residual_m"] for row in profile]
|
|
ripple_residuals = []
|
|
for row in profile:
|
|
nearby = [other["residual_m"] for other in profile
|
|
if abs(other["distance_m"] - row["distance_m"]) <= 1.0]
|
|
ripple_residuals.append((row["distance_m"], row["residual_m"]
|
|
- sum(nearby) / max(1, len(nearby))))
|
|
amplitude, period = 0.0, 0.0
|
|
for candidate in (0.375 + index * 0.125 for index in range(30)):
|
|
cosine = sum(value * math.cos(2.0 * math.pi * distance / candidate)
|
|
for distance, value in ripple_residuals)
|
|
sine = sum(value * math.sin(2.0 * math.pi * distance / candidate)
|
|
for distance, value in ripple_residuals)
|
|
current_amplitude = 2.0 * math.hypot(cosine, sine) / max(1, len(ripple_residuals))
|
|
if current_amplitude > amplitude:
|
|
amplitude, period = current_amplitude, candidate
|
|
step_error = max((abs(item["residual_delta_m"]) for item in jumps), default=0.0)
|
|
coverage = len(profile) / 81.0
|
|
rail_passed = (coverage >= 0.85 and amplitude <= 0.015 and step_error <= 0.05)
|
|
results[rail] = {
|
|
"coverage": coverage,
|
|
"max_adjacent_step_m": max((abs(item["delta_m"]) for item in jumps), default=0.0),
|
|
"max_step_residual_m": step_error,
|
|
"ripple_amplitude_m": amplitude,
|
|
"ripple_period_m": period,
|
|
"passed": rail_passed,
|
|
"jumps_over_0_04m": jumps,
|
|
"residual_peak_to_peak_m": max(residuals) - min(residuals) if residuals else None,
|
|
}
|
|
|
|
payload = {
|
|
"layout": str(LAYOUT.relative_to(ROOT)),
|
|
"mesh": str(MESH.relative_to(ROOT)),
|
|
"voxel_um": voxel_um,
|
|
"selected_tiles": tile_count,
|
|
"sample_spacing_m": 0.125,
|
|
"distance_range_m": [-5.0, 10.0],
|
|
"lateral_offsets_m": {"left": -1.5, "center": 0.0, "right": 1.5},
|
|
"rails": results,
|
|
"samples": rows,
|
|
}
|
|
all_passed = all(result["passed"] for result in results.values())
|
|
expected_ok = (OPTIONS.expect == "measure"
|
|
or (OPTIONS.expect == "pass" and all_passed)
|
|
or (OPTIONS.expect == "fail" and not all_passed))
|
|
OUT.write_text(json.dumps(payload, indent=2) + "\n", encoding="utf-8")
|
|
print(f"[EntranceProfileBefore] voxel_um={voxel_um} tiles={tile_count} "
|
|
f"lod={OPTIONS.lod} spacing_m=0.125 distance_m=-5.000..10.000 status=MEASURED")
|
|
for rail, summary in results.items():
|
|
print(f"[EntranceFloorRailAudit] rail={rail} coverage={summary['coverage']:.3f} "
|
|
f"ripple_amplitude_m={summary['ripple_amplitude_m']:.4f} "
|
|
f"ripple_period_m={summary['ripple_period_m']:.3f} "
|
|
f"max_step_residual_m={summary['max_step_residual_m']:.4f} "
|
|
f"status={'PASS' if summary['passed'] else 'FAIL'}")
|
|
for jump in summary["jumps_over_0_04m"]:
|
|
print(f"[EntranceProfileJump] rail={rail} from_m={jump['from_m']:.3f} "
|
|
f"to_m={jump['to_m']:.3f} delta_m={jump['delta_m']:+.4f} "
|
|
f"expected_delta_m={jump['expected_delta_m']:+.4f} "
|
|
f"residual_delta_m={jump['residual_delta_m']:+.4f}")
|
|
failed_rails = sum(not result["passed"] for result in results.values())
|
|
print(f"[EntranceFloorMeshAudit] samples_per_rail=81 distance_m=0.000..10.000 "
|
|
f"rails=3 failed_rails={failed_rails} observed={'PASS' if all_passed else 'FAIL'} "
|
|
f"expected={OPTIONS.expect.upper()} status={'PASS' if expected_ok else 'FAIL'}")
|
|
print(f"[EntranceProfileJSON] file={OUT} status={'PASS' if expected_ok else 'FAIL'}")
|
|
if not expected_ok:
|
|
raise SystemExit(1)
|