#!/usr/bin/env python3 """Freeze a 20-degree mouth-crease gate and project its sample spacing at 1440p.""" import ast import contextlib import importlib.util import io import json import math import re import argparse from pathlib import Path ROOT = Path(__file__).resolve().parents[4] BASELINES = ROOT / "Standalone/Tools/verify/baselines" BASE_PATH = ROOT / "Standalone/Research/WorldAssemblerM3/Linux37/RunMouthFragmentAuditL37.py" SPEC = importlib.util.spec_from_file_location("mouth_fragment_l37", BASE_PATH) BASE = importlib.util.module_from_spec(SPEC) SPEC.loader.exec_module(BASE) # Keep the near limit frozen at 20 degrees. The saved L37 owner overview measures # 25.542 degrees, so the overview regression limit is 26 degrees: it admits that # baseline while still rejecting the saved L36 washboard (27.133 degrees). NEAR_NORMAL_LIMIT_DEG = 20.0 OVERVIEW_NORMAL_LIMIT_DEG = 26.0 MIN_VISIBLE_PAIRS = 20 MIN_PROFILE_COVERAGE = 0.05 CAMERA = (1.691820, -3.424982, -54.224198) TARGET = (6.298233, 1.137233, -55.468264) FOV_DEG = 80.0 WIDTH_1440P = 2560 HEIGHT_1440P = 1440 MOUTH = (5.600, 0.445, -55.156) OVERVIEW_CAMERA = (MOUTH[0] - 9.75, MOUTH[1] - 11.4, MOUTH[2] + 8.0) def normalize(vector): magnitude = math.sqrt(sum(value * value for value in vector)) return tuple(value / magnitude for value in vector) def view_basis(camera, target): forward = normalize(tuple(target[i] - camera[i] for i in range(3))) right = normalize((forward[1], -forward[0], 0.0)) up = (right[1] * forward[2] - right[2] * forward[1], right[2] * forward[0] - right[0] * forward[2], right[0] * forward[1] - right[1] * forward[0]) return forward, right, up def visible_crease(scene_data, camera, target, center=None, preferred=None, inner=None, outer=None): cx, cy = center or BASE.CENTER inner = BASE.INNER if inner is None else inner outer = BASE.OUTER if outer is None else outer bounds = (cx - outer, cx + outer, cy - outer, cy + outer) voxel_um, triangles, _ = scene_data preferred = BASE.PREFERRED if preferred is None else preferred cell = max(2.0, 16.0 * voxel_um / 1_000_000.0) buckets = BASE.RING.build_buckets(triangles, True, bounds, cell) forward, right, up = view_basis(camera, target) tan_h = math.tan(math.radians(FOV_DEG / 2.0)) tan_v = math.tan(math.atan(tan_h * HEIGHT_1440P / WIDTH_1440P)) focal = WIDTH_1440P / (2.0 * tan_h) lines = [] count = round((outer - inner) / BASE.STEP) for degrees in (135.0, 180.0, 225.0, 270.0, 315.0): angle = math.radians(degrees) lines.append([(cx + radius * math.cos(angle), cy + radius * math.sin(angle)) for radius in (inner + index * BASE.STEP for index in range(count + 1))]) tangent_count = round(8.0 / BASE.STEP) for degrees in (225.0, 315.0): angle = math.radians(degrees) normal = (math.cos(angle), math.sin(angle)) tangent = (-normal[1], normal[0]) lines.append([(cx + 6.5 * normal[0] + (-4.0 + index * BASE.STEP) * tangent[0], cy + 6.5 * normal[1] + (-4.0 + index * BASE.STEP) * tangent[1]) for index in range(tangent_count + 1)]) maximum, largest_pixels, worst_depth, visible_pairs = 0.0, 0.0, 0.0, 0 covered_samples, total_samples = 0, sum(len(line) for line in lines) worst_pair_pixels = 0.0 worst_pair = None def project(point): offset = tuple(point[i] - camera[i] for i in range(3)) depth = sum(offset[i] * forward[i] for i in range(3)) x = sum(offset[i] * right[i] for i in range(3)) y = sum(offset[i] * up[i] for i in range(3)) return depth, x, y for points in lines: samples = [BASE.RING.sample_surface(buckets, x, y, True, cell, preferred) for x, y in points] covered_samples += sum(sample is not None for sample in samples) for index in range(1, len(samples)): before, current = samples[index - 1], samples[index] if before is None or current is None: continue p0 = (points[index - 1][0], points[index - 1][1], before[0]) p1 = (points[index][0], points[index][1], current[0]) midpoint = tuple((p0[axis] + p1[axis]) * 0.5 for axis in range(3)) depth, horizontal, vertical = project(midpoint) if (depth <= 0.0 or abs(horizontal) > depth * tan_h or abs(vertical) > depth * tan_v): continue visible_pairs += 1 angle = math.degrees(math.acos(max(-1.0, min(1.0, sum(a * b for a, b in zip(before[1], current[1])))))) d0, x0, y0 = project(p0) d1, x1, y1 = project(p1) pixels = math.hypot(focal * x0 / d0 - focal * x1 / d1, focal * y0 / d0 - focal * y1 / d1) largest_pixels = max(largest_pixels, pixels) if angle > maximum: maximum, worst_pair, worst_depth = angle, (p0, p1), depth worst_pair_pixels = pixels coverage = covered_samples / total_samples if total_samples else 0.0 return (maximum, worst_depth, largest_pixels, visible_pairs, worst_pair, worst_pair_pixels, coverage) def run(path, label, *, center=None, height_min=None, height_max=None, preferred=None, near_camera=None, target=None, overview_camera=None, profile_inner=None, profile_outer=None, profile_coverage_limit=None): path = Path(path) center = center or BASE.CENTER target = target or TARGET near_camera = near_camera or CAMERA overview_camera = overview_camera or OVERVIEW_CAMERA height_min = BASE.HEIGHT_MIN if height_min is None else height_min height_max = BASE.HEIGHT_MAX if height_max is None else height_max profile_inner = BASE.INNER if profile_inner is None else profile_inner profile_outer = BASE.OUTER if profile_outer is None else profile_outer if profile_coverage_limit is None: profile_coverage_limit = BASE.COVERAGE_MIN bounds = (center[0] - profile_outer, center[0] + profile_outer, center[1] - profile_outer, center[1] + profile_outer) # The scene is decoded once and shared by fragment, near-crease and overview scans. scene_data = BASE.RING.read_scene(str(path), 0, height_min, height_max, bounds) stream = io.StringIO() with contextlib.redirect_stdout(stream): passed, maximum = BASE.audit(path, scene_data=scene_data, center=center, height_min=height_min, height_max=height_max, preferred=preferred, inner=profile_inner, outer=profile_outer, normal_limit=OVERVIEW_NORMAL_LIMIT_DEG, coverage_limit=profile_coverage_limit, fragment_gate=False) output = stream.getvalue() print(output, end="") near = visible_crease(scene_data, near_camera, target, center, preferred, profile_inner, profile_outer) far = visible_crease(scene_data, overview_camera, target, center, preferred, profile_inner, profile_outer) print(f"[MouthCreasePixels] build={label} sample_step_m={BASE.STEP:.3f} " f"horizontal_fov_deg={FOV_DEG:.1f} resolution=2560x1440 " f"near_camera_to_mouth_m={math.dist(near_camera, (*center, target[2])):.3f} " f"near_visible_pairs={near[3]} near_max_visible_deg={near[0]:.6f} " f"near_profile_coverage={near[6]:.3f} " f"near_depth_m={near[1]:.3f} near_sample_spacing_px={near[2]:.2f} " f"near_worst_pair_spacing_px={near[5]:.2f} " f"overview_camera_to_mouth_m={math.dist(overview_camera, (*center, target[2])):.3f} " f"overview_visible_pairs={far[3]} overview_max_visible_deg={far[0]:.6f} " f"overview_profile_coverage={far[6]:.3f} " f"overview_depth_m={far[1]:.3f} overview_sample_spacing_px={far[2]:.2f} " f"overview_worst_pair_spacing_px={far[5]:.2f} overall_max_deg={maximum:.6f}") near_ok = (near[3] >= MIN_VISIBLE_PAIRS and near[6] >= MIN_PROFILE_COVERAGE and near[0] <= NEAR_NORMAL_LIMIT_DEG) overview_ok = (far[3] >= MIN_VISIBLE_PAIRS and far[6] >= MIN_PROFILE_COVERAGE and far[0] <= OVERVIEW_NORMAL_LIMIT_DEG) views_pass = near_ok and overview_ok print(f"[MouthCreaseViewGate] build={label} near_limit_deg={NEAR_NORMAL_LIMIT_DEG:.1f} " f"overview_limit_deg={OVERVIEW_NORMAL_LIMIT_DEG:.1f} " f"min_visible_pairs={MIN_VISIBLE_PAIRS} " f"min_profile_coverage={MIN_PROFILE_COVERAGE:.2f} " f"near={'PASS' if near_ok else 'FAIL'} overview={'PASS' if overview_ok else 'FAIL'} " f"status={'PASS' if views_pass else 'FAIL'}") return passed and views_pass, max(maximum, near[0], far[0]) def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--candidate", type=Path, default=ROOT / "Saved/Linux/l37/owner-fi-ground-mouth.vfmesh") parser.add_argument("--candidate-sw", type=Path) parser.add_argument("--layout-sw", type=Path) parser.add_argument("--capture-log-sw", type=Path) args = parser.parse_args() paths = { "L35": BASELINES / "mouth/l35/owner-fi-ground-mouth.vfmesh", "L36": BASELINES / "mouth/l36/owner-fi-ground-mouth.vfmesh", "L37": args.candidate if args.candidate.is_absolute() else ROOT / args.candidate, } results = {label: run(path, label) for label, path in paths.items()} controls = (not results["L35"][0] and not results["L36"][0] and results["L35"][1] > OVERVIEW_NORMAL_LIMIT_DEG and results["L36"][1] > OVERVIEW_NORMAL_LIMIT_DEG) final = (results["L37"][0] and results["L37"][1] <= OVERVIEW_NORMAL_LIMIT_DEG) if args.candidate_sw: if not args.layout_sw or not args.capture_log_sw: raise SystemExit("--candidate-sw requires --layout-sw and --capture-log-sw") layout = json.loads(args.layout_sw.read_text(encoding="utf-8")) passage = layout["passages"][0] q_to_m = layout["core_voxel_size_um"] / (256.0 * 1_000_000.0) mouth = tuple(value * q_to_m for value in passage["lower_mouth_q"]) capture = args.capture_log_sw.read_text(encoding="utf-8", errors="replace") camera_match = re.search(r"\[L37CaptureCamera\] view=owner-sw-ground-mouth .*?" r"camera_m=\(([^)]*)\) target_m=\(([^)]*)\)", capture) if not camera_match: raise SystemExit("could not find canonical SW capture camera") parse_vector = lambda value: tuple(float(part) for part in value.split(",")) near_camera, target = map(parse_vector, camera_match.groups()) overview_camera = (mouth[0] + 2.5 * (near_camera[0] - mouth[0]), mouth[1] + 2.5 * (near_camera[1] - mouth[1]), near_camera[2] + 7.0) sw_final, sw_max = run(args.candidate_sw, "owner-sw-style1", center=mouth[:2], height_min=mouth[2] - 24.0, height_max=mouth[2] + 16.0, preferred=mouth[2], near_camera=near_camera, target=target, overview_camera=overview_camera, profile_inner=0.5, profile_outer=4.5, profile_coverage_limit=0.0) final = final and sw_final and sw_max <= OVERVIEW_NORMAL_LIMIT_DEG print(f"[MouthCreaseGate] near_limit_deg={NEAR_NORMAL_LIMIT_DEG:.1f} " f"overview_limit_deg={OVERVIEW_NORMAL_LIMIT_DEG:.1f} " f"L35_42deg_fail_control={'PASS' if controls else 'FAIL'} " f"L36_27deg_fail_control={'PASS' if controls else 'FAIL'} " f"real_owner_candidates={'PASS' if final else 'FAIL'} " f"status={'PASS' if controls and final else 'FAIL'}") raise SystemExit(0 if controls and final else 1) if __name__ == "__main__": main()