Files
RuView/v2/crates/ruview-offaxis/src/lib.rs
Claude 3161af52da feat(offaxis): clean-room Kooima off-axis projection in Rust/WASM (ADR-324)
New leaf crate v2/crates/ruview-offaxis implementing ADR-324's projection
core from the published math only (Kooima 2008 generalized perspective
projection; Casiez 2012 one-euro filter) — no code from the unlicensed
prior-art repo. Dependency-free native core; wasm-bindgen surface gated to
wasm32 (cdylib+rlib, ~54 KB wasm after bindgen).

- projection: Screen (3 corners, any orientation) + off_axis() -> typed
  errors, never NaN matrices; column-major f64 (three.js Matrix4 layout).
  Tests pin the defining invariants: screen corners -> NDC corners for a
  grid of eye positions and tilted screens; screen-plane points are
  eye-invariant; centered eye reduces to the symmetric frustum; near/far
  map to NDC -1/+1.
- filter: one-euro with injected timestamps (no clock in crate);
  monotonicity/convergence/NaN-rejection tests.
- rf: field-peak extraction mirroring field_localize.rs constants
  (X_SCALE 0.6, Z_SCALE 0.5, PEAK_THRESHOLD 0.35) and the Tier B
  coarse-parallax stage (deadband, gain, hard clamp) so over-claiming is
  impossible at the API level. No accuracy numbers asserted.
- wasm: OffAxisCamera + RfParallax bindgen classes; per-frame updates hold
  last good state instead of throwing.
- benches (criterion): full Tier B frame ~598 ns; argmax scan optimized
  -18%/-33% (20x20/100x100). MEASURED table + reproducer in README.
- examples/three.js/demos/07-off-axis-window.html: demo with SYNTHETIC
  mouse simulator and labeled RF Tier B mode ('coarse body parallax - not
  head tracking'), physical calibration panel, /ws/sensing input, and a
  build-instructions overlay when the local pkg/ output is missing
  (generated artifacts stay uncommitted; .gitignore entry added).
- Validated 23 unit tests + doctest, clippy clean, wasm32 release build,
  Node smoke test of the bindgen output, and a headless-Chromium run of
  the demo (engine load, eye response, mode labels, ws failure path).
- ADR-324: header + section 2.5 amendment recording the Rust/WASM core.

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01BU3NcEgTpAVvu5QGtw4czT
2026-08-16 23:52:43 +00:00

68 lines
2.8 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! # `ruview-offaxis` — clean-room off-axis (head-coupled) perspective (ADR-324)
//!
//! Generalized perspective projection for "window into the screen"
//! (fish-tank VR / head-coupled perspective) demos, implemented from the
//! published math — Robert Kooima, *Generalized Perspective Projection*
//! (2008) — plus the supporting input stages the ADR-324 demo needs:
//!
//! - [`Screen`] / [`off_axis`]: three physical screen corners + a tracked
//! eye → asymmetric frustum and screen-aligned view matrix (column-major
//! `f64`, the three.js `Matrix4.elements` layout).
//! - [`OneEuro`] / [`OneEuro3`]: the one-euro filter (Casiez et al., CHI
//! 2012) for tracking-noise smoothing with injected timestamps.
//! - [`rf`]: RuView-specific input mapping — `/ws/sensing` signal-field
//! peak extraction (constants mirroring the sensing server's
//! `field_localize.rs`) and the bounded **Tier B** coarse-parallax stage.
//! - A wasm-bindgen surface (wasm32 only) exposing [`wasm::OffAxisCamera`]
//! and [`wasm::RfParallax`] to the browser demos.
//!
//! ## Clean-room statement
//!
//! ADR-324 records that the prior-art repository (`icurtis1/off-axis-sneaker`)
//! is unlicensed: no code, assets, or derived text from it appear here. This
//! crate is written solely from the published Kooima and Casiez papers and
//! RuView's own source.
//!
//! ## Honesty contract (repo rule)
//!
//! Nothing in this crate asserts sensing accuracy. The Tier B RF path is
//! **coarse body parallax by construction** — deadbanded, gain-limited,
//! hard-clamped — because a single-link CSI field peak is a representation
//! of field energy, not metric localization (see
//! `wifi-densepose-sensing-server/src/field_localize.rs`). Numeric defaults
//! are interaction-design choices (`CLAIMED`); benchmark numbers live in the
//! README tagged `MEASURED` with their reproducer.
//!
//! ## Native quick start
//!
//! ```
//! use ruview_offaxis::{off_axis, Screen, Vec3};
//!
//! // A 60 cm × 34 cm screen centered at the origin, eye 65 cm away and
//! // 10 cm to the right.
//! let screen = Screen::centered(0.60, 0.34)?;
//! let oa = off_axis(&screen, Vec3::new(0.10, 0.0, 0.65), 0.05, 100.0)?;
//! // Column-major, ready for three.js Matrix4.fromArray / any GL pipeline.
//! let _m: [f64; 16] = oa.view_projection();
//! # Ok::<(), ruview_offaxis::OffAxisError>(())
//! ```
#![forbid(unsafe_code)]
#![warn(missing_docs)]
pub mod filter;
pub mod math;
pub mod projection;
pub mod rf;
#[cfg(target_arch = "wasm32")]
pub mod wasm;
pub use filter::{OneEuro, OneEuro3, OneEuroConfig};
pub use math::{Mat4, Vec3};
pub use projection::{off_axis, OffAxis, OffAxisError, Screen, MIN_EYE_DISTANCE};
pub use rf::{
field_peak, CoarseParallax, CoarseParallaxConfig, FieldPeak, ScreenCalibration,
FIELD_PEAK_THRESHOLD, FIELD_X_SCALE, FIELD_Z_SCALE,
};