Feat: Rework ray termination into escaped/dark/unresolved/incomplete

Replace the position capture cutoff with a camera-relative dark threshold
shared by every backend, and carry explicit outcome/reason provenance
through the ray, RayPool, adaptive mesh, lens-map and replay paths.

- eval/eval_slab return SpacetimePointStatus; remove SPACETIME_RAY_CAPTURED
  and the Schwarzschild capture radius; decouple observer construction from
  ray position.
- RayEndpoint stores RayOutcome/RayReason plus the last trusted state;
  budget exhaustion is retryable UNRESOLVED, data/integration failures are
  INCOMPLETE.
- Normal dark terminal is L - L0 >= --dark-threshold (default 8), with L0
  taken at the camera event and kept distinct from the worldtube entry
  energy; photon energy and frequency ratio are never reset.
- Implement E/D/U triangle decisions with merged budget retries, persistent
  probe witnesses promoted in place by vertex identity, conformity settling,
  and approximate-black boundary provenance with achieved-scale statistics.
- Add RayPool continuation state and per-ray step budgets.
- Bump lens-map to v2 with explicit end/outcome/reason, approx_black,
  threshold/retry/geometry provenance and per-frame retry counts; reject v1.
- Gate production output on incomplete/error results, overridable with
  --allow-incomplete.
- Update AGENTS.md, the design document and usage docs; add the termination
  oracle and regression coverage.

make -B -j4 BUILD_TYPE=Debug test passes with bit-identical reference HDRs.
This commit is contained in:
wyj committed 2026-10-05 06:22:47 -04:00
1 parent 7fde49308b
commit f7380cbf75
31 files changed
+2349 -402

No files matched your search

+29 -12
View File
@@ -54,12 +54,24 @@ Single-frame camera options cannot be combined with `--observer-track`,
`--frames-dir`, or `--lens-map-input`.
Schwarzschild uses Cartesian ingoing Kerr–Schild coordinates with `M=1`.
Cameras at and inside the horizon `r=2` are allowed with a valid timelike
coordinate velocity. The current backend excludes camera positions at or
inside its capture cutoff `r=1.5`; its finite escape radius is `256`.
These remain analytic demonstration settings, not criteria for future NR data.
Cameras at and inside the horizon `r=2` (and inside the old `r=1.5` guard) are
allowed with a valid timelike coordinate velocity; position never decides a ray
endpoint. Its finite escape radius is `256`. These remain analytic demonstration
settings, not criteria for future NR data.
Zero coordinate velocity at or inside the horizon is not timelike and is rejected.
The normal dark terminal, for every backend, is the camera-relative local energy
growth `L - L0 >= T` (default `T = 8`, overridable with `--dark-threshold`),
where `L = ln(alpha p^0)` and `L0` is the photon's `L` at the **camera event**
(kept distinct from the escape-worldtube entry energy for an external camera).
A constant camera boost cancels, so a large initial `L` alone does not produce a
dark ray. Neither the photon energy nor the frequency ratio is reset;
budget-exhausted and data/integration failures are separate
unresolved/incomplete outcomes.
Failed and unresolved midpoint probes are retained as diagnostic samples, not
discarded after refinement. Lens-map replay uses its saved geometric policy and
the same incomplete-output check as live tracing.
The following complete examples use the bundled synthetic catalog:
```sh
@@ -108,11 +120,12 @@ The bubble therefore propagates through the coordinates, and the metric is
time-dependent: the renderer evaluates `f(r_s)` and its spatial derivatives at
each coordinate time, while the extrinsic curvature supplies the required
`d_t gamma` information to the 3+1 null-ray equations. The exotic matter that
would source the bubble is treated as optically transparent, so there is
**no capture**: rays are only active or escaped. This is why the backend
requires a sub-luminal `|v_s| < 1`; at or above `1` the metric develops an
ergoregion/event horizon and static observers cease to exist, which is outside
the current no-capture scope.
would source the bubble is treated as optically transparent and there is no
horizon, so in practice rays are active or escaped: the bubble has no causal
boundary at which `L - L0` can diverge, and the shared dark policy is not
expected to trigger. This is why the backend requires a sub-luminal
`|v_s| < 1`; at or above `1` the metric develops an ergoregion/event horizon and
static observers cease to exist, which is outside the current scope.
| Option | Meaning / default |
| --- | --- |
@@ -257,9 +270,13 @@ source-sky/lens-map length. A locally escaped triangle is split only when
`e / max(s, 1e-15) > --refine-angle-rel`. `P` and `A`
prevent selecting a leaf already at or below the requested image-plane
long-edge and area scales.
Triangles whose three vertices disagree between capture and escape are split
independently of the direction-error thresholds, allowing the mesh to follow a
shadow boundary.
Triangles whose three vertices straddle a dark/escape or unresolved/dark
boundary are split independently of the direction-error thresholds, allowing the
mesh to follow a shadow boundary. Unresolved vertices with an escape vertex (or
three unresolved vertices) are retried with more step budget before any split;
at the configured total cap the render is reported incomplete unless
`--allow-incomplete` is given. A UUD/UDD boundary triangle at the geometric stop
scale is approximately blackened and recorded with its image-plane area.
Independently of the midpoint geometry test, an all-escaped triangle also
computes the discrete lens Jacobian