5 Commits

Author SHA1 Message Date
xfy
3bf1f1ac83 feat(ui): add preset module with hash-based Custom detection
Preset::Cinematic/Performance/Web write 5 params (steps, render_scale,
bloom/disk/aa quality); Custom is a no-op marker. params_hash hashes
ONLY preset-touched fields so non-preset edits (disk_tilt, etc.) don't
spuriously flip the bar to Custom. Test-pinned via tests/preset_test.rs.

Exports ui + params from lib.rs so the test can construct params.

Lib-compile unblock: the library crate could not build with only
`pub mod {params,physics,ui}` because src/ui/mod.rs's ui_system
references crate::camera and crate::scene (transitively crate::render).
Added pub mod {camera,scene,render,web} to lib.rs and reworked main.rs
to consume them via `use singularity_rs::*` instead of redeclaring
the same file-level modules. preset is declared `pub mod` (not private
`mod`) because tests/preset_test.rs imports it cross-crate.
2026-07-17 15:27:18 +08:00
xfy
334e65fc76 test(physics): mirror Kerr RK45 loop in CPU, cover spin=0 degeneracy + spin>0 capture
Phase 2 shipped the Kerr deriv() and the adaptive RK45 integration loop in
the shader (black_hole.wgsl:260, 320-390), but the CPU mirror in physics.rs
only covered the single-step Kerr derivative (kerr_bending_accel), not the
loop. That left the AGENTS.md 'CPU <-> shader mirror' contract broken for
Phase 2: the spin-dependent capture radius, the adaptive step control, and
the budget = accepted-steps-only / dt_min forced-accept semantics existed
only on the GPU, with nothing testable on the CPU side.

Add a faithful CPU mirror and the loop-level tests that close the gap:

- rk45_step(): Dormand-Prince step mirroring black_hole.wgsl:260-285, using
  kerr_bending_accel so it is also the Phase 1 step at chi=0.
- is_captured_rk45(): the full adaptive loop mirroring black_hole.wgsl:320-390
  — same seeding (total_path / steps), same reject/retry at dt_min (the
  forced-accept floor that prevents infinite retry), same accept-then-refine,
  and the spin-dependent capture radius r+(chi) via kerr_horizon.

Tests (tests/physics_test.rs):
- spin=0 RK45 loop captures below bcrit and escapes above (degeneracy).
- spin=0.9 still captures a b~2.0 ray (horizon shrinks but not past b<bcrit).
- capture set does not grow as spin increases across a bcrit-straddling sweep.
- rk45_step error shrinks monotonically with dt (the property the adaptive
  loop's reject/accept decision depends on).

Note: probing the error scaling showed it falls between 2nd and 4th order in
dt, not a clean 5th, because of the per-stage normalize() projection the
shader (and now the mirror) applies. The loop only relies on monotonicity, so
this is correct-as-shipped; documented in a doc-comment on rk45_step so a
future reader is not misled by the '45' in the name.

cargo test: 17 passed (3 Phase 1 inline + 14 integration, up from 12).
2026-07-14 15:51:53 +08:00
xfy
d8b77c00c8 feat: Kerr deriv() with frame-dragging + spin-dependent horizon 2026-07-14 14:22:37 +08:00
xfy
1bde9f382c feat: add kerr_isco and kerr_horizon CPU helpers with tests 2026-07-14 14:15:25 +08:00
xfy
d5e3008343 test: CPU-mirrored geodesic physics + bcrit/capture unit tests 2026-07-10 10:43:11 +08:00