passages: floor profile by nearest centreline + metric distance outside voids (L114, v222) - WIP
Floor ribs are present in L109/L112/L113 rock tunnels too: the floor height comes from the nearest segment, so it jumps (up to 5.9-7.3 cm) where the nearest segment switches. v222 reduces them; faint ribs remain. Quiet-machine timing (load 0.64): near-ready 9-17% slower than L109 (FI W12 0.95 -> 1.10 s); candidate totals ~5x L109. Fast tier FAIL: 2 SW wall-shell probes of 31,381 read host air. Walk/grade audits pass; Clang/GCC deterministic; UE builds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
No files matched your search
@@ -0,0 +1,45 @@
|
||||
# Linux 114 — floor profile and speed
|
||||
|
||||
Evaluator version: **222**. Worktree remains on `wip/l111-halfpipe-spline`; no commit was made.
|
||||
|
||||
## Floor
|
||||
|
||||
The L109, L112 and L113 50% rock-station captures all show transverse ribs. In v222, tube-body selection uses round distance and the floor profile is selected separately by nearest centerline. The passage now supplies a signed metric distance outside its void instead of inheriting the host field's saturated deep-rock value.
|
||||
|
||||
At the L114 passage 1–2 rock-station column, the raw source value at the floor was saturated at `-256.160522 m`; the composed floor value was `-0.007221 m`. The vertical profile is nearly linear:
|
||||
|
||||
| Height from planned floor (m) | -0.50 | -0.25 | -0.10 | 0.00 | +0.10 | +0.25 | +0.50 |
|
||||
|---|---:|---:|---:|---:|---:|---:|---:|
|
||||
| Density (m) | -0.5064 | -0.2568 | -0.1070 | -0.00722 | +0.0926 | +0.2423 | +0.4919 |
|
||||
|
||||
Along the roughly 10° rock slope, the density at the planned floor changes sharply across one band:
|
||||
|
||||
| Route distance (m) | 57.495 | 57.795 | 57.895 | 57.995 | 58.095 | 58.295 | 58.495 |
|
||||
|---|---:|---:|---:|---:|---:|---:|---:|
|
||||
| Density at planned floor (m) | +0.00724 | +0.00726 | +0.00724 | -0.07260 | -0.05748 | -0.02614 | +0.00667 |
|
||||
| Zero-crossing offset (m) | -0.00722 | -0.00720 | -0.00721 | +0.07234 | +0.05730 | +0.02607 | -0.00665 |
|
||||
|
||||
Across 51 samples, the maximum offset is 5.9 cm on passage 0–1 and 7.3 cm on passage 1–2. The L114 rock-floor close-up shows a large reduction from L109/L112/L113, but faint transverse ridges remain. The floor is improved, not fully smooth yet. Full probes: `Saved/Linux/l114/audit-owner-clang.log`.
|
||||
|
||||
## Speed
|
||||
|
||||
Timing was deferred until load fell from 5.46 to 0.69; the runner's gate measured **0.64** immediately before the batch. It ran five interleaved L80 samples per cell across all four strates. Values are median seconds, `LOD0–2 near-ready / whole-view-ready`, ordered L109 → L113 → L114.
|
||||
|
||||
| Strate | W8 near / whole | W12 near / whole |
|
||||
|---|---|---|
|
||||
| SW | 0.778 → 0.851 → 0.849 / 1.808 → 1.874 → 1.861 | 0.802 → 0.877 → 0.936 / 1.751 → 1.872 → 1.912 |
|
||||
| FI | 1.177 → 1.271 → 1.380 / 2.201 → 2.255 → 2.450 | 0.946 → 1.122 → 1.102 / 1.816 → 2.012 → 1.993 |
|
||||
| TN | 1.112 → 1.201 → 1.211 / 1.977 → 2.109 → 2.166 | 0.959 → 1.090 → 1.074 / 1.786 → 1.925 → 1.918 |
|
||||
| VS | 1.019 → 1.149 → 1.156 / 2.065 → 2.308 → 2.263 | 0.950 → 1.074 → 1.098 / 1.963 → 2.131 → 2.166 |
|
||||
|
||||
L114 is 9–17% slower than L109 at near-ready and 3–11% at whole-view. L114 candidate totals match L113 and exceed L109: SW `3102 vs 583`, FI `5384 vs 1036`, TN `3701 vs 869`, VS `2197 vs 644` feature/route candidates across the profiled tiles. The L77 W8 census estimates median extra worker CPU of `0.71 s` at LOD0–2 and `0.88 s` whole-view across the four cameras. The largest added buckets are broad `other` work (`+0.72 / +0.60 s`) and passage work (`+0.21 / +0.40 s`); composition-bands work is flat or lower. L77 values are weighted sample estimates, not direct phase timers.
|
||||
|
||||
## Checks and captures
|
||||
|
||||
- Walkability and the 15° forward-grade plan cap pass; the fast audit reports maxima of 10.48° (SW) and 10.01° (FI).
|
||||
- Clang/GCC hashes match at W1/W8/W12 for all four strates and both two-strate recipes: 18 runs, source stamp `735079f…`.
|
||||
- Fast tier is **FAIL**: the SW owner audit reports 2 wall-shell violations in 31,381 probes. Both samples return the same positive host density (`+35.742 m`, `+35.578 m`) where the shell audit expects solid. This remains unresolved after the two floor geometry designs.
|
||||
- Linux `VFHostEditor` build succeeded in 80.6 s with `-NoUBA -MaxParallelActions=4`; Unreal was not run under `prlimit`.
|
||||
- The [capture index](captures/INDEX.md) lists 30 programmatically verified views: 14 open-air outside/interior close-ups and one sloped rock-floor close-up for each of L113 and L114. Four low-contrast open-air images are flagged as geometry-visible. See the [L114 rock floor](captures/L114/after/passage-1-2-0-rock-floor-midpoint.png) and the matching [L109](../Linux109/captures/after/passage-1-2-0-station-50.png), [L112](../Linux112/captures/after/passage-1-2-0-station-50.png) and [L113](../Linux113/captures/after/passage-1-2-0-station-50.png) station captures.
|
||||
|
||||
Detailed timing, candidate, L77, build, hash and verification logs are under `Saved/Linux/l114/`.
|
||||
@@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Compare per-tile passage candidate-list sizes for L109, L113, and L114."""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import math
|
||||
import re
|
||||
import statistics
|
||||
import subprocess
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[4]
|
||||
OUT = ROOT / "Saved/Linux/l114/candidate-lists"
|
||||
DATA = ROOT / "Standalone/data/owner_world.json"
|
||||
BINARIES = {
|
||||
"L109": ROOT / "Saved/Linux/l109/bin-final/VoxelForgeCoreHeadless-l109-clang",
|
||||
"L113": ROOT / "Saved/Linux/l113/bin/VoxelForgeCoreHeadless-l113-clang",
|
||||
"L114": ROOT / "Saved/Linux/l114/bin/VoxelForgeCoreHeadless-l114-clang",
|
||||
}
|
||||
CAMERAS = {
|
||||
0: (8.0, 16.0, -372.2626953125),
|
||||
1: (25.8369140625, 3.19921875, -737.0),
|
||||
2: (19.166015625, -14.2001953125, -753.3740234375),
|
||||
3: (-20.0625, -5.068359375, -900.353515625),
|
||||
}
|
||||
LIST = re.compile(r"^\[PassageComposerTile\].*?features=(\d+) route_candidates=(\d+)", re.M)
|
||||
|
||||
|
||||
def percentile(values: list[int], fraction: float) -> int:
|
||||
ordered = sorted(values)
|
||||
if not ordered:
|
||||
return 0
|
||||
return ordered[min(len(ordered) - 1,
|
||||
max(0, math.ceil(fraction * len(ordered)) - 1))]
|
||||
|
||||
|
||||
def run(strate: int, version: str) -> dict:
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
log = OUT / f"{version}-s{strate}.log"
|
||||
camera = ",".join(f"{v:.12f}" for v in CAMERAS[strate]) + ",0,0"
|
||||
command = [
|
||||
"taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
|
||||
"timeout", "1740s", str(BINARIES[version]), str(DATA),
|
||||
"--world-assembler-prototype", "--stream-benchmark", "--stream-pipeline",
|
||||
"--workers=12", "--clip-radius=3", "--max-clip-level=8", "--max-level=8",
|
||||
f"--camera={camera}", "--profile-passage-composer", "--stream-scheduler-policy=3",
|
||||
]
|
||||
started = time.monotonic()
|
||||
with log.open("w", encoding="utf-8") as stream:
|
||||
result = subprocess.run(command, cwd=ROOT, stdout=stream,
|
||||
stderr=subprocess.STDOUT, check=False, timeout=1740)
|
||||
output = log.read_text(encoding="utf-8", errors="replace")
|
||||
features = [int(row[0]) for row in LIST.findall(output)]
|
||||
routes = [int(row[1]) for row in LIST.findall(output)]
|
||||
if result.returncode or not features:
|
||||
raise SystemExit(f"candidate-list profile missing: {log}; exit={result.returncode}")
|
||||
row = {
|
||||
"strate": strate, "version": version, "profiled_tiles": len(features),
|
||||
"feature_candidates": {"sum": sum(features), "min": min(features),
|
||||
"median": statistics.median(features), "p95": percentile(features, .95),
|
||||
"max": max(features)},
|
||||
"route_segment_candidates": {"sum": sum(routes), "min": min(routes),
|
||||
"median": statistics.median(routes), "p95": percentile(routes, .95),
|
||||
"max": max(routes)},
|
||||
"elapsed_s": round(time.monotonic() - started, 3),
|
||||
"log": str(log.relative_to(ROOT)),
|
||||
}
|
||||
print("[L114CandidateLists] " + json.dumps(row, sort_keys=True), flush=True)
|
||||
return row
|
||||
|
||||
|
||||
def main() -> int:
|
||||
if any(not path.is_file() for path in (*BINARIES.values(), DATA)):
|
||||
raise SystemExit("missing L109/L113/L114 candidate-list input")
|
||||
rows = [run(strate, version) for strate in range(4)
|
||||
for version in ("L109", "L113", "L114")]
|
||||
(OUT / "summary.json").write_text(json.dumps(rows, indent=2) + "\n",
|
||||
encoding="utf-8")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,252 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Copy verified L113 wall views and capture L113/L114 rock floors plus L114 walls."""
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import importlib.util
|
||||
import json
|
||||
import math
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[4]
|
||||
RESEARCH = Path(__file__).resolve().parent
|
||||
CAPTURES = RESEARCH / "captures"
|
||||
SAVED = ROOT / "Saved/Linux/l114/captures"
|
||||
L113_RESEARCH = ROOT / "Standalone/Research/WorldAssemblerM3/Linux113"
|
||||
L113_SAVED = ROOT / "Saved/Linux/l113"
|
||||
L114_SAVED = ROOT / "Saved/Linux/l114"
|
||||
sys.dont_write_bytecode = True
|
||||
SPEC = importlib.util.spec_from_file_location(
|
||||
"l113_capture_helpers", L113_RESEARCH / "RunL113Captures.py")
|
||||
if SPEC is None or SPEC.loader is None:
|
||||
raise SystemExit("cannot load the verified L113 capture helpers")
|
||||
HELPERS = importlib.util.module_from_spec(SPEC)
|
||||
SPEC.loader.exec_module(HELPERS)
|
||||
HELPERS.RESEARCH = RESEARCH
|
||||
|
||||
BINARIES = {
|
||||
"L113": L113_SAVED / "bin/VoxelForgeCoreHeadless-l113-clang",
|
||||
"L114": L114_SAVED / "bin/VoxelForgeCoreHeadless-l114-clang",
|
||||
}
|
||||
RENDERERS = {
|
||||
"L113": L113_SAVED / "bin/VoxelForgeOffscreenCapture-l113-clang",
|
||||
"L114": L114_SAVED / "bin/VoxelForgeOffscreenCapture-l114-clang",
|
||||
}
|
||||
|
||||
|
||||
def run_quality(image: Path, log: Path) -> str:
|
||||
log.parent.mkdir(parents=True, exist_ok=True)
|
||||
command = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
|
||||
"timeout", "1740s", "python3", "-B",
|
||||
str(ROOT / "Standalone/Tools/verify/capture_quality.py"), str(image)]
|
||||
with log.open("w", encoding="utf-8") as stream:
|
||||
result = subprocess.run(command, cwd=ROOT, stdout=stream,
|
||||
stderr=subprocess.STDOUT, check=False, timeout=1740)
|
||||
output = log.read_text(encoding="utf-8", errors="replace")
|
||||
# L113 already admitted low-contrast views when geometry covered at least
|
||||
# 0.1% of pixels; use the same visibility rule for its copied references.
|
||||
# The caller still rejects decode failures because they have no metrics.
|
||||
if result.returncode and "coverage_luma_gt12=" not in output:
|
||||
raise RuntimeError(f"capture quality could not inspect image: {image}; log={log}")
|
||||
return output
|
||||
|
||||
|
||||
def pixel_status(output: str) -> str:
|
||||
if "status=PASS" in output:
|
||||
return "pass"
|
||||
metrics = re.search(r"coverage_luma_gt12=([\d.]+)", output)
|
||||
if metrics and float(metrics.group(1)) >= 0.001:
|
||||
return "low_contrast_geometry_visible"
|
||||
raise RuntimeError("capture has insufficient visible-pixel coverage")
|
||||
|
||||
|
||||
def copy_l113_wall_views() -> list[dict]:
|
||||
source_index = L113_RESEARCH / "captures/capture-index.json"
|
||||
if not source_index.is_file():
|
||||
raise SystemExit(f"missing L113 capture index: {source_index}")
|
||||
document = json.loads(source_index.read_text(encoding="utf-8"))
|
||||
source_root = L113_RESEARCH
|
||||
target_root = CAPTURES / "L113"
|
||||
target_root.mkdir(parents=True, exist_ok=True)
|
||||
rows = []
|
||||
for source_row in document.get("captures", []):
|
||||
if source_row.get("variant") != "after" or source_row.get("kind") != "open-air-close":
|
||||
continue
|
||||
source = source_root / source_row["capture"]
|
||||
target = target_root / Path(source_row["capture"]).name
|
||||
shutil.copy2(source, target)
|
||||
source_hash = hashlib.sha256(source.read_bytes()).hexdigest()
|
||||
target_hash = hashlib.sha256(target.read_bytes()).hexdigest()
|
||||
if source_hash != target_hash:
|
||||
raise RuntimeError(f"L113 capture copy changed bytes: {source}")
|
||||
quality_log = SAVED / "L113-copied" / f"{target.stem}-quality.log"
|
||||
quality = run_quality(target, quality_log)
|
||||
row = dict(source_row)
|
||||
row.update({"variant": "L113", "capture": str(target.relative_to(RESEARCH)),
|
||||
"source_capture": str(source.relative_to(ROOT)),
|
||||
"source_sha256": source_hash, "verified": True,
|
||||
"pixel_quality_status": pixel_status(quality),
|
||||
"copy_bytes_verified": True,
|
||||
"copy_quality_log": str(quality_log.relative_to(ROOT))})
|
||||
if (row.get("closeup_camera_target_distance_m") is None
|
||||
or abs(row["closeup_camera_target_distance_m"] - 4.0) > 0.02
|
||||
or row.get("closeup_target_surface_distance_m", 99.0) > 5.0
|
||||
or row.get("closeup_wall_normal_alignment", -1.0) < 0.25):
|
||||
raise RuntimeError(f"L113 open-air close-up geometry check failed: {source}")
|
||||
rows.append(row)
|
||||
if not rows:
|
||||
raise RuntimeError("no L113 outside/interior close-ups were copied")
|
||||
return rows
|
||||
|
||||
|
||||
def configure_helpers(version: str) -> None:
|
||||
output_folder = CAPTURES / version
|
||||
save_folder = SAVED / version
|
||||
output_folder.mkdir(parents=True, exist_ok=True)
|
||||
save_folder.mkdir(parents=True, exist_ok=True)
|
||||
HELPERS.CAPTURES = output_folder
|
||||
HELPERS.SAVED = save_folder
|
||||
HELPERS.BINARIES = {"after": BINARIES[version]}
|
||||
HELPERS.RENDERERS = {"after": RENDERERS[version]}
|
||||
HELPERS.RESEARCH = RESEARCH
|
||||
|
||||
|
||||
def parse_sections(log: Path) -> list[dict]:
|
||||
sections = []
|
||||
text = log.read_text(encoding="utf-8", errors="replace")
|
||||
for passage, section, start, end, samples, midpoint, radius in HELPERS.SECTION.findall(text):
|
||||
sections.append({"passage": passage, "section": int(section),
|
||||
"start_m": float(start), "end_m": float(end),
|
||||
"samples": int(samples), "midpoint": HELPERS.vector(midpoint),
|
||||
"radius_m": float(radius)})
|
||||
return sections
|
||||
|
||||
|
||||
def make_rock_floor_view(layout: dict, passage_id: str, version: str) -> dict:
|
||||
passage = next(item for item in layout["passages"]
|
||||
if item["stable_id"] == passage_id)
|
||||
raw_route = HELPERS.smooth_route(layout, passage)
|
||||
route, body_depth = HELPERS.derived_smooth_route(raw_route, passage, layout)
|
||||
lengths, prefix = HELPERS.route_lengths(route)
|
||||
station = prefix[-1] * 0.5
|
||||
center, radius = HELPERS.at_distance(route, lengths, prefix, station)
|
||||
floor_depth = min(body_depth, max(0.0, radius - 0.23))
|
||||
planned_floor = (center[0], center[1], center[2] - floor_depth)
|
||||
tangent = HELPERS.tangent_at(route, lengths, prefix, station)
|
||||
grade_deg = math.degrees(math.atan2(abs(tangent[2]),
|
||||
math.hypot(tangent[0], tangent[1])))
|
||||
# Frame the grade in profile with a shallow, in-tunnel view. The close
|
||||
# overhead angle hid the floor in a nearly uniform patch of rock.
|
||||
camera = tuple(planned_floor[axis] - 2.5 * tangent[axis]
|
||||
+ (1.25 if axis == 2 else 0.0) for axis in range(3))
|
||||
yaw, pitch = HELPERS.angles(camera, planned_floor)
|
||||
safe_id = HELPERS.sanitize(passage_id)
|
||||
return {"name": f"{safe_id}-rock-floor-midpoint",
|
||||
"passage": passage_id, "kind": "open-air",
|
||||
"station": f"rock-floor/{station:.1f}m", "camera": camera,
|
||||
"target": planned_floor, "yaw": yaw, "pitch": pitch,
|
||||
"focus": planned_floor, "center_z": planned_floor[2],
|
||||
"route_distance_m": station, "requested_route_distance_m": station,
|
||||
"route_length_m": prefix[-1], "closeup_side": "upward-floor",
|
||||
"planned_floor_m": planned_floor[2], "planned_grade_deg": grade_deg,
|
||||
"version": version}
|
||||
|
||||
|
||||
def capture_rock_floor(layout: dict, version: str, passage_id: str) -> dict:
|
||||
configure_helpers(version)
|
||||
view = make_rock_floor_view(layout, passage_id, version)
|
||||
row = HELPERS.capture("after", view, layout)
|
||||
scene = ROOT / row["scene"]
|
||||
floor_anchor = HELPERS.nearest_surface_anchor(scene, view["target"], margin_m=5.0,
|
||||
preferred_normal=(0.0, 0.0, 1.0), minimum_alignment=0.85)
|
||||
floor_normal_alignment = HELPERS.dot(floor_anchor["normal"], (0.0, 0.0, 1.0))
|
||||
camera_to_floor = math.dist(row["camera_actual_m"], floor_anchor["position_m"])
|
||||
camera_to_target = math.dist(row["camera_actual_m"], row["target_actual_m"])
|
||||
row["variant"] = version
|
||||
row["kind"] = "rock-floor-close"
|
||||
row["planned_floor_m"] = view["planned_floor_m"]
|
||||
row["planned_grade_deg"] = view["planned_grade_deg"]
|
||||
row["floor_anchor_position_m"] = floor_anchor["position_m"]
|
||||
row["floor_anchor_normal"] = floor_anchor["normal"]
|
||||
row["closeup_camera_target_distance_m"] = camera_to_target
|
||||
row["closeup_camera_to_floor_distance_m"] = camera_to_floor
|
||||
row["closeup_target_surface_distance_m"] = floor_anchor["distance_m"]
|
||||
row["closeup_wall_normal_alignment"] = floor_normal_alignment
|
||||
row["verified_upward_floor_normal"] = floor_normal_alignment >= 0.85
|
||||
if (not row["verified"]
|
||||
or not 2.0 <= camera_to_floor <= 3.2
|
||||
or row["closeup_target_surface_distance_m"] > 0.25
|
||||
or not row["verified_upward_floor_normal"]
|
||||
or row["planned_grade_deg"] < 2.0
|
||||
or row["drawn_triangles"] <= 0
|
||||
or row["pixel_quality_status"] not in
|
||||
("pass", "low_contrast_geometry_visible")):
|
||||
raise RuntimeError(f"{version} rock-floor close-up failed geometry/pixel checks")
|
||||
return row
|
||||
|
||||
|
||||
def capture_l114_open_air(layout: dict, sections: list[dict]) -> list[dict]:
|
||||
configure_helpers("L114")
|
||||
views = HELPERS.make_views(layout, sections, use_derived_shape=True,
|
||||
use_spline=True, include_open_air=True)
|
||||
views = [view for view in views if view["kind"] == "open-air-close"]
|
||||
rows = [HELPERS.capture("after", view, layout) for view in views]
|
||||
for row in rows:
|
||||
row["variant"] = "L114"
|
||||
if len(rows) != 2 * len(sections):
|
||||
raise RuntimeError("L114 open-air outside/interior close-up count mismatch")
|
||||
return rows
|
||||
|
||||
|
||||
def main() -> int:
|
||||
required = (*BINARIES.values(), *RENDERERS.values(),
|
||||
L113_SAVED / "audit-layout-candidate.json",
|
||||
L114_SAVED / "audit-layout-candidate.json",
|
||||
L114_SAVED / "audit-owner-clang.log")
|
||||
if any(not path.is_file() for path in required):
|
||||
raise SystemExit("missing L113/L114 capture binaries, layouts, or audit log")
|
||||
CAPTURES.mkdir(parents=True, exist_ok=True)
|
||||
l113_rows = copy_l113_wall_views()
|
||||
l113_layout = json.loads((L113_SAVED / "audit-layout-candidate.json").read_text())
|
||||
l114_layout = json.loads((L114_SAVED / "audit-layout-candidate.json").read_text())
|
||||
l114_sections = parse_sections(L114_SAVED / "audit-owner-clang.log")
|
||||
if not l114_sections:
|
||||
raise SystemExit("no L114 open-air sections found in passage audit")
|
||||
|
||||
l113_rows.append(capture_rock_floor(l113_layout, "L113", "passage/1-2/0"))
|
||||
l114_rows = capture_l114_open_air(l114_layout, l114_sections)
|
||||
l114_rows.append(capture_rock_floor(l114_layout, "L114", "passage/1-2/0"))
|
||||
rows = [*l113_rows, *l114_rows]
|
||||
(CAPTURES / "capture-index.json").write_text(
|
||||
json.dumps({"captures": rows}, indent=2) + "\n", encoding="utf-8")
|
||||
lines = ["# L113 and L114 floor captures", "",
|
||||
"L113 open-air close-ups are byte-checked copies of the verified L113 set. "
|
||||
"L113 and L114 rock-floor views and L114 open-air close-ups were rendered "
|
||||
"from their respective meshes. Each image passed the pixel check. Rock-floor "
|
||||
"anchors require an upward normal alignment of at least 0.85, a floor offset "
|
||||
"of at most 0.25 m, and a camera distance between 2.0 and 3.2 m.", ""]
|
||||
for version in ("L113", "L114"):
|
||||
lines.extend([f"## {version}", ""])
|
||||
for row in rows:
|
||||
if row["variant"] != version:
|
||||
continue
|
||||
image = Path(row["capture"]).name
|
||||
lines.append(f"- [{row['station']}]({row['capture']}) — {row['kind']}; "
|
||||
f"{row.get('drawn_triangles', 0)} visible triangles; "
|
||||
f"{row['pixel_quality_status']}")
|
||||
lines.append("")
|
||||
(CAPTURES / "INDEX.md").write_text("\n".join(lines), encoding="utf-8")
|
||||
floor_rows = [row for row in rows if row["kind"] == "rock-floor-close"]
|
||||
if len(floor_rows) != 2 or not all(row["verified_upward_floor_normal"] for row in floor_rows):
|
||||
raise RuntimeError("L113/L114 rock-floor capture verification failed")
|
||||
print(f"[L114CaptureSummary] captures={len(rows)} L113={len(l113_rows)} "
|
||||
f"L114={len(l114_rows)} rock_floor_verified={len(floor_rows)}", flush=True)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Sample passage density vertically and along representative rock/open-air floors."""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[4]
|
||||
OUT = ROOT / "Saved/Linux/l114"
|
||||
BINARY = ROOT / "Saved/Linux/l114/bin/VoxelForgeCoreHeadless-l114-clang"
|
||||
DATA = ROOT / "Standalone/data/owner_world.json"
|
||||
|
||||
|
||||
def main() -> int:
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
log = OUT / "audit-owner-clang.log"
|
||||
layout = OUT / "audit-layout-candidate.json"
|
||||
command = [
|
||||
"taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
|
||||
"timeout", "1740s", str(BINARY), str(DATA), "--world-assembler-prototype",
|
||||
"--stream-benchmark", "--stream-pipeline", "--workers=12", "--clip-radius=3",
|
||||
"--max-clip-level=8", "--max-level=8", "--camera=8,16,-372.2626953125,0,0",
|
||||
"--audit-resolved-layout-passages", f"--resolved-layout-output={layout}",
|
||||
]
|
||||
with log.open("w", encoding="utf-8") as stream:
|
||||
result = subprocess.run(command, cwd=ROOT,
|
||||
env={**os.environ,
|
||||
"VOXELFORGE_L114_DENSITY_PROFILE": "1"},
|
||||
stdout=stream, stderr=subprocess.STDOUT,
|
||||
check=False, timeout=1740)
|
||||
print(f"[L114DensityProbe] exit={result.returncode} log={log.relative_to(ROOT)}",
|
||||
flush=True)
|
||||
return result.returncode
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,103 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Check Clang/GCC hash identity at W1/W8/W12 and pin v222 owner hashes."""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[4]
|
||||
OUT = ROOT / "Saved/Linux/l114/hashes"
|
||||
DATASETS = {
|
||||
"owner4": ROOT / "Standalone/data/owner_world.json",
|
||||
"owner2": ROOT / "Standalone/data/regression/owner_world_2strate_l61.json",
|
||||
}
|
||||
BINARIES = {
|
||||
"clang": ROOT / "Saved/Linux/l114/bin/VoxelForgeCoreHeadless-l114-clang",
|
||||
"gcc": ROOT / "Saved/Linux/l114/bin/VoxelForgeCoreHeadless-l114-gcc",
|
||||
}
|
||||
HASH = re.compile(r"^\[Pipeline(?:FullDetailHash|WholeViewHash|LODHash|ExactQueryHash)\].*$", re.M)
|
||||
STAMP = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=(\S+)$", re.M)
|
||||
|
||||
|
||||
def run(dataset: str, recipe: str, workers: int, compiler: str) -> dict:
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
label = f"{dataset}-{recipe}-w{workers}-{compiler}"
|
||||
log = OUT / f"{label}.log"
|
||||
layout = OUT / f"{label}.layout.json"
|
||||
command = [
|
||||
"taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
|
||||
"timeout", "1740s", str(BINARIES[compiler]), str(DATASETS[dataset]),
|
||||
f"--workers={workers}", "--world-assembler-prototype", "--stream-benchmark",
|
||||
"--stream-pipeline", "--force-generate", "--clip-radius=3",
|
||||
"--max-clip-level=8", "--max-level=8", f"--recipe={recipe}",
|
||||
"--stream-hash", f"--resolved-layout-output={layout}",
|
||||
]
|
||||
started = time.monotonic()
|
||||
with log.open("w", encoding="utf-8") as stream:
|
||||
result = subprocess.run(command, cwd=ROOT, stdout=stream,
|
||||
stderr=subprocess.STDOUT, check=False, timeout=1740)
|
||||
output = log.read_text(encoding="utf-8", errors="replace")
|
||||
hashes = HASH.findall(output)
|
||||
if result.returncode or len(hashes) != 12:
|
||||
raise SystemExit(f"hash run failed or incomplete: {label}; exit={result.returncode} "
|
||||
f"hashes={len(hashes)} log={log}")
|
||||
row = {"dataset": dataset, "recipe": recipe, "workers": workers,
|
||||
"compiler": compiler, "exit_code": result.returncode,
|
||||
"hashes": hashes, "source_stamp": (STAMP.search(output).group(1)
|
||||
if STAMP.search(output) else None),
|
||||
"elapsed_s": round(time.monotonic() - started, 3),
|
||||
"log": str(log.relative_to(ROOT))}
|
||||
print("[L114HashRun] " + json.dumps(row, sort_keys=True), flush=True)
|
||||
return row
|
||||
|
||||
|
||||
def main() -> int:
|
||||
if any(not path.is_file() for path in (*DATASETS.values(), *BINARIES.values())):
|
||||
raise SystemExit("missing L114 hash input")
|
||||
rows = []
|
||||
recipes = {"owner4": ("surface-world",),
|
||||
"owner2": ("surface-world", "floating-islands")}
|
||||
for dataset, dataset_recipes in recipes.items():
|
||||
for recipe in dataset_recipes:
|
||||
for workers in (1, 8, 12):
|
||||
for compiler in ("clang", "gcc"):
|
||||
rows.append(run(dataset, recipe, workers, compiler))
|
||||
(OUT / "runs-partial.json").write_text(
|
||||
json.dumps(rows, indent=2) + "\n", encoding="utf-8")
|
||||
groups = {}
|
||||
for dataset, dataset_recipes in recipes.items():
|
||||
for recipe in dataset_recipes:
|
||||
key = f"{dataset}/{recipe}"
|
||||
group = [row for row in rows if row["dataset"] == dataset
|
||||
and row["recipe"] == recipe]
|
||||
reference = group[0]["hashes"]
|
||||
groups[key] = {"all_worker_compiler_hashes_match": all(
|
||||
row["hashes"] == reference for row in group),
|
||||
"source_stamps": sorted({row["source_stamp"] for row in group}),
|
||||
"runs": [{"workers": row["workers"], "compiler": row["compiler"],
|
||||
"log": row["log"]} for row in group]}
|
||||
if not groups[key]["all_worker_compiler_hashes_match"]:
|
||||
raise SystemExit(f"determinism mismatch: {key}")
|
||||
if dataset == "owner2":
|
||||
canonical = next(row for row in group if row["workers"] == 12
|
||||
and row["compiler"] == "clang")
|
||||
recipe_name = "floating-islands" if recipe == "floating-islands" else "surface-world"
|
||||
style = 1 if recipe_name == "surface-world" else 2
|
||||
baseline = (ROOT / "Standalone/Tools/verify/baselines/owner-hash/canonical"
|
||||
/ f"owner-hash-{recipe_name}-style{style}-v222.baseline")
|
||||
baseline.write_text("\n".join(canonical["hashes"]) + "\n",
|
||||
encoding="utf-8")
|
||||
summary = {"runs": rows, "groups": groups,
|
||||
"owner4_all_four_strata": "owner4/surface-world",
|
||||
"owner2_canonical_pins": ["owner2/surface-world",
|
||||
"owner2/floating-islands"]}
|
||||
(OUT / "summary.json").write_text(json.dumps(summary, indent=2) + "\n",
|
||||
encoding="utf-8")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,140 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Compare L77 worker phase samples for L109 and L114 across four cameras."""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
import statistics
|
||||
import subprocess
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[4]
|
||||
OUT = ROOT / "Saved/Linux/l114/l77-census"
|
||||
DATA = ROOT / "Standalone/data/owner_world.json"
|
||||
BINARIES = {
|
||||
"L109": ROOT / "Saved/Linux/l109/bin-final-profile/VoxelForgeCoreHeadless-l109-candidate-profile",
|
||||
"L114": ROOT / "Saved/Linux/l114/bin-profile/VoxelForgeCoreHeadless-l114-clang-l77",
|
||||
}
|
||||
CAMERAS = {
|
||||
0: (8.0, 16.0, -372.2626953125),
|
||||
1: (25.8369140625, 3.19921875, -737.0),
|
||||
2: (19.166015625, -14.2001953125, -753.3740234375),
|
||||
3: (-20.0625, -5.068359375, -900.353515625),
|
||||
}
|
||||
WORKER = re.compile(r"^\[WorldAssemblerLODWorker\] (.*)$", re.M)
|
||||
SAMPLE = re.compile(r"^\[L77Sample\] (.*)$", re.M)
|
||||
SAMPLER = re.compile(r"^\[L77Sampler\] action=stop (.*)$", re.M)
|
||||
FIELDS = re.compile(r"([a-zA-Z0-9_]+)=([^ ]+)")
|
||||
|
||||
|
||||
def run(version: str, strate: int, replicate: int) -> dict:
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
label = f"{version.lower()}-s{strate}-r{replicate}"
|
||||
log = OUT / f"{label}.log"
|
||||
camera = ",".join(f"{value:.12f}" for value in CAMERAS[strate]) + ",0,0"
|
||||
command = [
|
||||
"taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
|
||||
"timeout", "1740s", str(BINARIES[version]), str(DATA),
|
||||
"--world-assembler-prototype", "--stream-benchmark", "--stream-pipeline",
|
||||
"--workers=8", "--clip-radius=3", "--max-clip-level=8", "--max-level=8",
|
||||
f"--camera={camera}", "--profile-lod-work", "--stream-scheduler-policy=3",
|
||||
"--linux77-profile",
|
||||
]
|
||||
started = time.monotonic()
|
||||
with log.open("w", encoding="utf-8") as stream:
|
||||
result = subprocess.run(command, cwd=ROOT, stdout=stream,
|
||||
stderr=subprocess.STDOUT, check=False, timeout=1740)
|
||||
content = log.read_text(encoding="utf-8", errors="replace")
|
||||
sampler_match = SAMPLER.search(content)
|
||||
worker_rows = [dict(FIELDS.findall(match.group(1))) for match in WORKER.finditer(content)]
|
||||
samples = [dict(FIELDS.findall(match.group(1))) for match in SAMPLE.finditer(content)]
|
||||
if result.returncode or sampler_match is None or not worker_rows or not samples:
|
||||
raise SystemExit(f"L77 census failed or markers missing: {log}; exit={result.returncode}")
|
||||
sampler = dict(FIELDS.findall(sampler_match.group(1)))
|
||||
if sampler.get("status") != "PASS":
|
||||
raise SystemExit(f"L77 sampler failed: {log}: {sampler}")
|
||||
|
||||
scopes = {}
|
||||
for scope, lods in (("lod0_2", set(range(3))), ("whole_view", set(range(9)))):
|
||||
selected = [row for row in samples if int(row["lod"]) in lods]
|
||||
worker_cpu_s = sum(float(row["worker_sum_s"]) for row in worker_rows
|
||||
if int(row["lod"]) in lods)
|
||||
phase_weights: dict[str, int] = {}
|
||||
stage_weights: dict[str, int] = {}
|
||||
for row in selected:
|
||||
weight = int(row.get("weight", "1"))
|
||||
phase = row.get("phase", "other")
|
||||
stage = row.get("stage", "other")
|
||||
phase_weights[phase] = phase_weights.get(phase, 0) + weight
|
||||
stage_weights[stage] = stage_weights.get(stage, 0) + weight
|
||||
total = sum(phase_weights.values())
|
||||
|
||||
def estimate(weights: dict[str, int]) -> dict:
|
||||
return {key: {"share_pct": round(100.0 * weight / total, 3),
|
||||
"worker_s_estimate": round(worker_cpu_s * weight / total, 4)}
|
||||
for key, weight in sorted(weights.items(), key=lambda item: (-item[1], item[0]))}
|
||||
|
||||
scopes[scope] = {
|
||||
"worker_cpu_s": round(worker_cpu_s, 4),
|
||||
"weighted_samples": total,
|
||||
"phase": estimate(phase_weights),
|
||||
"stage": estimate(stage_weights),
|
||||
}
|
||||
row = {
|
||||
"version": version, "strate": strate, "replicate": replicate,
|
||||
"exit_code": result.returncode, "sampler": sampler, "scopes": scopes,
|
||||
"log": str(log.relative_to(ROOT)),
|
||||
"runner_wall_s": round(time.monotonic() - started, 3),
|
||||
}
|
||||
print("[L114L77Run] " + json.dumps(row, sort_keys=True), flush=True)
|
||||
return row
|
||||
|
||||
|
||||
def summarize(rows: list[dict]) -> dict:
|
||||
summary = []
|
||||
for strate in range(4):
|
||||
for scope in ("lod0_2", "whole_view"):
|
||||
groups = {version: [row for row in rows if row["strate"] == strate
|
||||
and row["version"] == version]
|
||||
for version in ("L109", "L114")}
|
||||
phases = sorted({phase for group in groups.values() for row in group
|
||||
for phase in row["scopes"][scope]["phase"]})
|
||||
stages = sorted({stage for group in groups.values() for row in group
|
||||
for stage in row["scopes"][scope]["stage"]})
|
||||
item = {"strate": strate, "scope": scope, "replicates": 2}
|
||||
for version, group in groups.items():
|
||||
item[f"{version}_worker_cpu_s_median"] = statistics.median(
|
||||
row["scopes"][scope]["worker_cpu_s"] for row in group)
|
||||
for kind, names in (("phase", phases), ("stage", stages)):
|
||||
for name in names:
|
||||
for metric in ("share_pct", "worker_s_estimate"):
|
||||
for version, group in groups.items():
|
||||
values = [row["scopes"][scope][kind].get(name, {})
|
||||
.get(metric, 0.0) for row in group]
|
||||
item[f"{version}_{kind}_{name}_{metric}_median"] = \
|
||||
statistics.median(values)
|
||||
item[f"L114_minus_L109_{kind}_{name}_{metric}"] = round(
|
||||
item[f"L114_{kind}_{name}_{metric}_median"]
|
||||
- item[f"L109_{kind}_{name}_{metric}_median"], 4)
|
||||
summary.append(item)
|
||||
return {"runs": rows, "summary": summary}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
if any(not path.is_file() for path in (*BINARIES.values(), DATA)):
|
||||
raise SystemExit("missing L109/L114 L77 profile input")
|
||||
rows = []
|
||||
for strate in range(4):
|
||||
for replicate in (1, 2):
|
||||
for version in ("L109", "L114"):
|
||||
rows.append(run(version, strate, replicate))
|
||||
(OUT / "runs-partial.json").write_text(
|
||||
json.dumps(rows, indent=2) + "\n", encoding="utf-8")
|
||||
(OUT / "summary.json").write_text(
|
||||
json.dumps(summarize(rows), indent=2) + "\n", encoding="utf-8")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,124 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Five interleaved L80 readiness runs for L109, L113, and L114."""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import statistics
|
||||
import subprocess
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[4]
|
||||
OUT = ROOT / "Saved/Linux/l114/timing"
|
||||
DATA = ROOT / "Standalone/data/owner_world.json"
|
||||
BINARIES = {
|
||||
"L109": ROOT / "Saved/Linux/l109/bin-final/VoxelForgeCoreHeadless-l109-clang",
|
||||
"L113": ROOT / "Saved/Linux/l113/bin/VoxelForgeCoreHeadless-l113-clang",
|
||||
"L114": ROOT / "Saved/Linux/l114/bin/VoxelForgeCoreHeadless-l114-clang",
|
||||
}
|
||||
CAMERAS = {
|
||||
0: (8.0, 16.0, -372.2626953125),
|
||||
1: (25.8369140625, 3.19921875, -737.0),
|
||||
2: (19.166015625, -14.2001953125, -753.3740234375),
|
||||
3: (-20.0625, -5.068359375, -900.353515625),
|
||||
}
|
||||
PIPELINE = re.compile(r"^\[StreamPipeline\] (.*)$", re.M)
|
||||
FIELDS = re.compile(r"([a-zA-Z0-9_]+)=([^ ]+)")
|
||||
NEAR = re.compile(
|
||||
r"^\[WorldAssemblerNearLODReady\] lods=0-2 tiles=(\d+)/(\d+) cold_ready_s=([0-9.]+)$",
|
||||
re.M,
|
||||
)
|
||||
|
||||
|
||||
def run(strate: int, workers: int, version: str, phase: str, replicate: int) -> dict:
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
label = f"s{strate}-w{workers}-{version}-{phase}{replicate}"
|
||||
log = OUT / f"{label}.log"
|
||||
camera = ",".join(f"{v:.12f}" for v in CAMERAS[strate]) + ",0,0"
|
||||
command = [
|
||||
"taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
|
||||
"timeout", "1740s", str(BINARIES[version]), str(DATA),
|
||||
"--world-assembler-prototype", "--stream-benchmark", "--stream-pipeline",
|
||||
f"--workers={workers}", "--clip-radius=3", "--max-clip-level=8", "--max-level=8",
|
||||
f"--camera={camera}", "--profile-lod-work", "--stream-scheduler-policy=3",
|
||||
]
|
||||
start = time.monotonic()
|
||||
with log.open("w", encoding="utf-8") as output:
|
||||
result = subprocess.run(command, cwd=ROOT, stdout=output,
|
||||
stderr=subprocess.STDOUT, check=False, timeout=1740)
|
||||
content = log.read_text(encoding="utf-8", errors="replace")
|
||||
pipeline_match = PIPELINE.search(content)
|
||||
near_match = NEAR.search(content)
|
||||
if result.returncode or pipeline_match is None or near_match is None:
|
||||
raise SystemExit(f"readiness run failed or trace missing: {log}; exit={result.returncode}")
|
||||
pipeline = dict(FIELDS.findall(pipeline_match.group(1)))
|
||||
row = {
|
||||
"strate": strate, "workers": workers, "version": version,
|
||||
"phase": phase, "replicate": replicate, "exit_code": result.returncode,
|
||||
"wall_s": round(time.monotonic() - start, 3),
|
||||
"lod02_ready_s": float(near_match.group(3)),
|
||||
"whole_view_ready_s": float(pipeline["whole_view_ready"].removesuffix("s")),
|
||||
"whole_tiles": int(pipeline["whole_tiles"].split("/")[0]),
|
||||
"log": str(log.relative_to(ROOT)),
|
||||
}
|
||||
print("[L114TimingRun] " + json.dumps(row, sort_keys=True), flush=True)
|
||||
return row
|
||||
|
||||
|
||||
def main() -> int:
|
||||
if not DATA.is_file() or any(not binary.is_file() for binary in BINARIES.values()):
|
||||
raise SystemExit("missing L109/L113/L114 data or timing binary")
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
load_1m = os.getloadavg()[0]
|
||||
uptime = subprocess.run(["uptime"], cwd=ROOT, text=True, capture_output=True,
|
||||
check=True).stdout.strip()
|
||||
top = subprocess.run(["ps", "-eo", "pid,pcpu,comm", "--sort=-pcpu"], cwd=ROOT,
|
||||
text=True, capture_output=True, check=True).stdout
|
||||
(OUT / "machine-load-before.txt").write_text(
|
||||
f"uptime: {uptime}\n1-minute load: {load_1m:.2f}\ntop CPU users:\n{top}",
|
||||
encoding="utf-8")
|
||||
print(f"[L114TimingGate] load_1m={load_1m:.2f} uptime={uptime}", flush=True)
|
||||
if load_1m >= 1.5:
|
||||
raise SystemExit(f"timing refused: 1-minute load {load_1m:.2f} is not below 1.5")
|
||||
rows = []
|
||||
versions = ("L109", "L113", "L114")
|
||||
for strate in range(4):
|
||||
for workers in (8, 12):
|
||||
for version in versions:
|
||||
run(strate, workers, version, "warmup", 0)
|
||||
for replicate in range(1, 6):
|
||||
offset = (replicate - 1) % len(versions)
|
||||
order = versions[offset:] + versions[:offset]
|
||||
for version in order:
|
||||
rows.append(run(strate, workers, version, "measured", replicate))
|
||||
(OUT / "runs-partial.json").write_text(
|
||||
json.dumps(rows, indent=2) + "\n", encoding="utf-8")
|
||||
summary = []
|
||||
for strate in range(4):
|
||||
for workers in (8, 12):
|
||||
item = {"strate": strate, "workers": workers, "replicates": 5}
|
||||
medians = {}
|
||||
for version in versions:
|
||||
group = [row for row in rows if row["strate"] == strate
|
||||
and row["workers"] == workers and row["version"] == version]
|
||||
medians[version] = {}
|
||||
for key in ("lod02_ready_s", "whole_view_ready_s"):
|
||||
values = [row[key] for row in group]
|
||||
medians[version][key] = statistics.median(values)
|
||||
item[f"{version}_{key}_values"] = values
|
||||
item[f"{version}_{key}_median"] = medians[version][key]
|
||||
for comparison in ("L109", "L113"):
|
||||
for key in ("lod02_ready_s", "whole_view_ready_s"):
|
||||
item[f"L114_vs_{comparison}_{key}_delta_pct"] = round(
|
||||
100.0 * (medians["L114"][key] / medians[comparison][key] - 1.0), 2)
|
||||
summary.append(item)
|
||||
(OUT / "summary.json").write_text(
|
||||
json.dumps({"runs": rows, "summary": summary}, indent=2) + "\n",
|
||||
encoding="utf-8")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,39 @@
|
||||
# L113 and L114 floor captures
|
||||
|
||||
L113 open-air close-ups are byte-checked copies of the verified L113 set. L113 and L114 rock-floor views and L114 open-air close-ups were rendered from their respective meshes. Each image passed the pixel check. Rock-floor anchors require an upward normal alignment of at least 0.85, a floor offset of at most 0.25 m, and a camera distance between 2.0 and 3.2 m.
|
||||
|
||||
## L113
|
||||
|
||||
- [section-1/4m-outside](captures/L113/passage-0-1-0-openair-1-4m-outside.png) — open-air-close; 279171 visible triangles; pass
|
||||
- [section-1/4m-interior-wall](captures/L113/passage-0-1-0-openair-1-4m-interior-wall.png) — open-air-close; 312014 visible triangles; pass
|
||||
- [section-2/4m-outside](captures/L113/passage-0-1-0-openair-2-4m-outside.png) — open-air-close; 279171 visible triangles; pass
|
||||
- [section-2/4m-interior-wall](captures/L113/passage-0-1-0-openair-2-4m-interior-wall.png) — open-air-close; 350214 visible triangles; pass
|
||||
- [section-3/4m-outside](captures/L113/passage-0-1-0-openair-3-4m-outside.png) — open-air-close; 153860 visible triangles; pass
|
||||
- [section-3/4m-interior-wall](captures/L113/passage-0-1-0-openair-3-4m-interior-wall.png) — open-air-close; 152882 visible triangles; pass
|
||||
- [section-1/4m-outside](captures/L113/passage-1-2-0-openair-1-4m-outside.png) — open-air-close; 623418 visible triangles; pass
|
||||
- [section-1/4m-interior-wall](captures/L113/passage-1-2-0-openair-1-4m-interior-wall.png) — open-air-close; 612097 visible triangles; pass
|
||||
- [section-2/4m-outside](captures/L113/passage-1-2-0-openair-2-4m-outside.png) — open-air-close; 585844 visible triangles; pass
|
||||
- [section-2/4m-interior-wall](captures/L113/passage-1-2-0-openair-2-4m-interior-wall.png) — open-air-close; 574404 visible triangles; pass
|
||||
- [section-1/4m-outside](captures/L113/passage-2-3-0-openair-1-4m-outside.png) — open-air-close; 104612 visible triangles; low_contrast_geometry_visible
|
||||
- [section-1/4m-interior-wall](captures/L113/passage-2-3-0-openair-1-4m-interior-wall.png) — open-air-close; 94962 visible triangles; low_contrast_geometry_visible
|
||||
- [section-2/4m-outside](captures/L113/passage-2-3-0-openair-2-4m-outside.png) — open-air-close; 475906 visible triangles; pass
|
||||
- [section-2/4m-interior-wall](captures/L113/passage-2-3-0-openair-2-4m-interior-wall.png) — open-air-close; 486760 visible triangles; pass
|
||||
- [rock-floor/57.5m](captures/L113/after/passage-1-2-0-rock-floor-midpoint.png) — rock-floor-close; 641506 visible triangles; pass
|
||||
|
||||
## L114
|
||||
|
||||
- [section-1/4m-outside](captures/L114/after/passage-0-1-0-openair-1-4m-outside.png) — open-air-close; 275347 visible triangles; pass
|
||||
- [section-1/4m-interior-wall](captures/L114/after/passage-0-1-0-openair-1-4m-interior-wall.png) — open-air-close; 308016 visible triangles; low_contrast_geometry_visible
|
||||
- [section-2/4m-outside](captures/L114/after/passage-0-1-0-openair-2-4m-outside.png) — open-air-close; 275347 visible triangles; pass
|
||||
- [section-2/4m-interior-wall](captures/L114/after/passage-0-1-0-openair-2-4m-interior-wall.png) — open-air-close; 345082 visible triangles; pass
|
||||
- [section-3/4m-outside](captures/L114/after/passage-0-1-0-openair-3-4m-outside.png) — open-air-close; 150904 visible triangles; pass
|
||||
- [section-3/4m-interior-wall](captures/L114/after/passage-0-1-0-openair-3-4m-interior-wall.png) — open-air-close; 150354 visible triangles; pass
|
||||
- [section-1/4m-outside](captures/L114/after/passage-1-2-0-openair-1-4m-outside.png) — open-air-close; 620958 visible triangles; pass
|
||||
- [section-1/4m-interior-wall](captures/L114/after/passage-1-2-0-openair-1-4m-interior-wall.png) — open-air-close; 609821 visible triangles; pass
|
||||
- [section-2/4m-outside](captures/L114/after/passage-1-2-0-openair-2-4m-outside.png) — open-air-close; 584696 visible triangles; pass
|
||||
- [section-2/4m-interior-wall](captures/L114/after/passage-1-2-0-openair-2-4m-interior-wall.png) — open-air-close; 572310 visible triangles; pass
|
||||
- [section-1/4m-outside](captures/L114/after/passage-2-3-0-openair-1-4m-outside.png) — open-air-close; 104612 visible triangles; low_contrast_geometry_visible
|
||||
- [section-1/4m-interior-wall](captures/L114/after/passage-2-3-0-openair-1-4m-interior-wall.png) — open-air-close; 94962 visible triangles; low_contrast_geometry_visible
|
||||
- [section-2/4m-outside](captures/L114/after/passage-2-3-0-openair-2-4m-outside.png) — open-air-close; 475818 visible triangles; pass
|
||||
- [section-2/4m-interior-wall](captures/L114/after/passage-2-3-0-openair-2-4m-interior-wall.png) — open-air-close; 486490 visible triangles; pass
|
||||
- [rock-floor/57.5m](captures/L114/after/passage-1-2-0-rock-floor-midpoint.png) — rock-floor-close; 636304 visible triangles; pass
|
||||
|
After Width: | Height: | Size: 540 KiB |
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 126 KiB |
|
After Width: | Height: | Size: 86 KiB |
|
After Width: | Height: | Size: 148 KiB |
|
After Width: | Height: | Size: 215 KiB |
|
After Width: | Height: | Size: 227 KiB |
|
After Width: | Height: | Size: 55 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 121 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 55 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 67 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 98 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 112 KiB |
|
After Width: | Height: | Size: 206 KiB |
|
After Width: | Height: | Size: 172 KiB |
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 121 KiB |
|
After Width: | Height: | Size: 217 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 55 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 67 KiB |