#!/usr/bin/env python3 """Audit the displaced floor across the passage-mouth fade edge.""" import argparse import math import resource import sys import AuditRingTerraceL32 as ring def main(): parser = argparse.ArgumentParser() parser.add_argument("scene") parser.add_argument("--center", nargs=2, type=float, required=True) parser.add_argument("--lod", type=int, default=0) parser.add_argument("--inner-radius-m", type=float, default=5.5) parser.add_argument("--outer-radius-m", type=float, default=9.5) parser.add_argument("--step-m", type=float, default=0.125) parser.add_argument("--height-min-m", type=float, default=-122.5) parser.add_argument("--height-max-m", type=float, default=-117.5) parser.add_argument("--preferred-height-m", type=float, default=-119.5) parser.add_argument("--highest-surface", action="store_true", help="choose the topmost upward face inside the height window") parser.add_argument("--plateau-min-m", type=float, default=0.5) parser.add_argument("--step-threshold-m", type=float, default=0.1) parser.add_argument("--normal-jump-max-deg", type=float, default=20.0) args = parser.parse_args() if (args.inner_radius_m < 0.0 or args.outer_radius_m <= args.inner_radius_m or args.step_m <= 0.0): parser.error("invalid radial interval or sample spacing") cx, cy = args.center preferred_height = None if args.highest_surface else args.preferred_height_m bounds = (cx - args.outer_radius_m, cx + args.outer_radius_m, cy - args.outer_radius_m, cy + args.outer_radius_m) voxel_um, triangles, selected_tiles = ring.read_scene( args.scene, args.lod, args.height_min_m, args.height_max_m, bounds) bucket_size = max(2.0, 16.0 * voxel_um / 1_000_000.0 * (1 << args.lod)) buckets = ring.build_buckets(triangles, True, bounds, bucket_size) count = round((args.outer_radius_m - args.inner_radius_m) / args.step_m) lines = [] for angle_degrees in (135.0, 180.0, 225.0, 270.0, 315.0): angle = math.radians(angle_degrees) points = [(cx + radius * math.cos(angle), cy + radius * math.sin(angle)) for radius in (args.inner_radius_m + i * args.step_m for i in range(count + 1))] lines.append((f"radial-{angle_degrees:.0f}deg", points)) # Tangential sections stay outside the open mouth while crossing the outer # fade edge on both sides of the 6.5 m center radius. tangent_count = round(8.0 / args.step_m) tangent_values = [-4.0 + i * args.step_m for i in range(tangent_count + 1)] for angle_degrees in (225.0, 315.0): angle = math.radians(angle_degrees) normal = (math.cos(angle), math.sin(angle)) tangent = (-normal[1], normal[0]) points = [(cx + 6.5 * normal[0] + t * tangent[0], cy + 6.5 * normal[1] + t * tangent[1]) for t in tangent_values] lines.append((f"straight-tangent-{angle_degrees:.0f}deg", points)) failed = False max_plateau = 0.0 max_step = 0.0 max_normal = 0.0 min_coverage = 1.0 for name, points in lines: profile = ring.audit_profile(points, buckets, True, args.plateau_min_m, 0.0005, args.step_threshold_m, bucket_size, preferred_height) samples = [ring.sample_surface(buckets, x, y, True, bucket_size, preferred_height) for x, y in points] for index in range(1, len(samples)): prior, current = samples[index - 1], samples[index] if prior is None or current is None: continue dot = max(-1.0, min(1.0, sum(a * b for a, b in zip(prior[1], current[1])))) delta = math.degrees(math.acos(dot)) if delta > args.normal_jump_max_deg: px, py = points[index - 1] x, y = points[index] print(f"[PassageFadeNormalJump] line={name} " f"radius_m={math.hypot(x-cx,y-cy):.3f} " f"from_m=({px:.3f},{py:.3f},{prior[0]:.3f}) " f"to_m=({x:.3f},{y:.3f},{current[0]:.3f}) " f"normal_delta_deg={delta:.3f}") coverage = profile["coverage"] min_coverage = min(min_coverage, coverage) plateau = max((run[0] for run in profile["plateaus"]), default=0.0) step = max((run[1] for run in profile["plateaus"]), default=0.0) angle = profile["max_angle"] max_plateau = max(max_plateau, plateau) max_step = max(max_step, step) max_normal = max(max_normal, angle) ok = (profile["valid"] and plateau < args.plateau_min_m and angle <= args.normal_jump_max_deg) failed |= not ok print(f"[PassageFadeProfile] line={name} coverage={coverage:.3f} " f"height_range_m={profile.get('height_range', 0.0):.6f} " f"flat_step_runs={len(profile['plateaus'])} " f"max_normal_delta_deg={angle:.6f} status={'PASS' if ok else 'FAIL'}") passed = not failed and min_coverage >= 0.95 print(f"[PassageFadeRingAudit] scene={args.scene} lod={args.lod} " f"center_m=({cx:.6f},{cy:.6f}) mouth_radius_m=4.500 " f"fade_m=5.500..7.500 radial_m={args.inner_radius_m:.3f}.." f"{args.outer_radius_m:.3f} lines={len(lines)} selected_tiles={selected_tiles} " f"coverage_min={min_coverage:.3f} longest_flat_run_m={max_plateau:.6f} " f"following_step_m={max_step:.6f} max_normal_delta_deg={max_normal:.6f} " f"thresholds=(coverage>=0.950,plateau<0.500,step>=0.100,normal<=20.000) " f"status={'PASS' if passed else 'FAIL'}") print(f"[PassageFadeRSS] peak_rss_kib={resource.getrusage(resource.RUSAGE_SELF).ru_maxrss}") raise SystemExit(0 if passed else 1) if __name__ == "__main__": main()