mirror of
https://github.com/ruvnet/RuView.git
synced 2026-08-27 18:46:09 +00:00
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
68 lines
2.8 KiB
Rust
68 lines
2.8 KiB
Rust
//! # `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,
|
||
};
|