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.
78 lines
3.2 KiB
C
78 lines
3.2 KiB
C
#include "geodesic.h"
|
|
#include "observer_track.h"
|
|
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
|
|
static int nearly_equal(double a, double b) { return fabs(a - b) < 1e-12; }
|
|
|
|
static int check_ray(const SpacetimeSource *source,
|
|
const ObserverState *observer,
|
|
const double local_direction[3],
|
|
const double expected[3]) {
|
|
const GeodesicTraceConfig config = {.coordinate_time_step = 0.25,
|
|
.max_steps = 100};
|
|
RayEndpoint ray =
|
|
geodesic_trace_past(source, observer, local_direction, &config);
|
|
if (ray.outcome != RAY_OUTCOME_ESCAPED ||
|
|
!nearly_equal(ray.frequency_ratio, 1.0) ||
|
|
!nearly_equal(ray.n_infinity[0], expected[0]) ||
|
|
!nearly_equal(ray.n_infinity[1], expected[1]) ||
|
|
!nearly_equal(ray.n_infinity[2], expected[2])) {
|
|
fprintf(stderr, "flat-space geodesic regression failed\n");
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int main(void) {
|
|
SpacetimeSource source = {0};
|
|
MetricSlab *slab = NULL;
|
|
MetricData metric;
|
|
const ObserverState observer = observer_fixed_at_origin();
|
|
if (spacetime_create_minkowski(&source, 10.0) ||
|
|
spacetime_load_slab(&source, 0.0, -1.0, &slab) ||
|
|
spacetime_slab_eval(slab, -0.5, (double[]){0.0, 0.0, 0.0}, &metric) ||
|
|
metric.alpha != 1.0 ||
|
|
!spacetime_slab_eval(slab, 0.25, (double[]){0.0, 0.0, 0.0}, &metric))
|
|
return 1;
|
|
int result = check_ray(&source, &observer, (double[]){1.0, 0.0, 0.0},
|
|
(double[]){0.0, 0.0, -1.0}) ||
|
|
check_ray(&source, &observer, (double[]){0.0, 0.0, 1.0},
|
|
(double[]){1.0, 0.0, 0.0});
|
|
const ObserverState look_at_ra_zero =
|
|
observer_fixed_at_origin_look_at(0.0, 0.0);
|
|
result = result || check_ray(&source, &look_at_ra_zero,
|
|
(double[]){1.0, 0.0, 0.0},
|
|
(double[]){1.0, 0.0, 0.0});
|
|
/* Standard ICRS has +Z at the north celestial pole and +Y at increasing
|
|
* RA. A right-handed north-up camera consequently has west to its right. */
|
|
result = result || check_ray(&source, &look_at_ra_zero,
|
|
(double[]){0.0, 1.0, 0.0},
|
|
(double[]){0.0, 0.0, 1.0}) ||
|
|
check_ray(&source, &look_at_ra_zero,
|
|
(double[]){0.0, 0.0, 1.0},
|
|
(double[]){0.0, -1.0, 0.0});
|
|
ObserverTrack accelerated = {0};
|
|
ObserverState final_observer;
|
|
if (observer_track_generate_minkowski_acceleration(&accelerated, 1.52, 2.0,
|
|
1.0 / 30.0) ||
|
|
observer_track_interpolate(&accelerated, 2.0, &final_observer, NULL)) {
|
|
result = 1;
|
|
} else {
|
|
const RayEndpoint forward = geodesic_trace_past(
|
|
&source, &final_observer, (double[]){1.0, 0.0, 0.0},
|
|
&(GeodesicTraceConfig){.coordinate_time_step = 0.25, .max_steps = 100});
|
|
const double expected_g = sqrt(1.0 + 3.04 * 3.04) + 3.04;
|
|
if (forward.outcome != RAY_OUTCOME_ESCAPED ||
|
|
!nearly_equal(forward.frequency_ratio, expected_g)) {
|
|
fputs("accelerated-observer Doppler regression failed\n", stderr);
|
|
result = 1;
|
|
}
|
|
}
|
|
observer_track_destroy(&accelerated);
|
|
spacetime_free_slab(slab);
|
|
spacetime_destroy(&source);
|
|
return result;
|
|
}
|