sim-lib-view-spatial 0.1.0

Spatial glasses surface encoding over the shared SIM Web Scene and device adapters.
Documentation
use sim_kernel::{Expr, Symbol};
use sim_lib_scene::{AnchorSpace, Transform3};

use crate::{
    PanelPlacement, VioTrackingStatus, WorldAnchorObservation, WorldAnchorResolver,
    resolve_world_anchor,
};

#[test]
fn world_anchor_falls_back_when_vio_unstable() {
    let placement = panel_placement();
    let resolver = resolver();

    let stable = resolve_world_anchor(&placement, VioTrackingStatus::Stable6Dof, &resolver);
    assert_eq!(stable.anchor_space(), AnchorSpace::World);
    assert_eq!(stable.anchor(), &Symbol::new("desk-plane"));
    assert_transform_near(stable.transform(), [0.2, 1.1, -1.7]);

    let unstable = resolve_world_anchor(&placement, VioTrackingStatus::Limited, &resolver);
    assert_eq!(unstable.anchor_space(), AnchorSpace::Head);
    assert_eq!(unstable.transform(), &placement.transform);
    assert_eq!(
        unstable.reason(),
        Some(&Symbol::qualified("world-anchor", "unstable-vio"))
    );

    let restored = resolve_world_anchor(&placement, VioTrackingStatus::Stable6Dof, &resolver);
    assert_eq!(restored, stable);
}

#[test]
fn missing_world_anchor_degrades_to_head_locked() {
    let placement = panel_placement();
    let resolver = WorldAnchorResolver::default();

    let resolution = resolve_world_anchor(&placement, VioTrackingStatus::Stable6Dof, &resolver);

    assert_eq!(resolution.anchor_space(), AnchorSpace::Head);
    assert_eq!(
        resolution.reason(),
        Some(&Symbol::qualified("world-anchor", "missing-world-anchor"))
    );
}

#[test]
fn tracking_status_reads_shared_xr_symbols() {
    assert_eq!(
        VioTrackingStatus::from_expr(&Expr::Symbol(Symbol::qualified(
            "stream/xr-tracking",
            "tracked"
        )))
        .unwrap(),
        VioTrackingStatus::Stable6Dof
    );
    assert_eq!(
        VioTrackingStatus::Limited.to_expr(),
        Expr::Symbol(Symbol::qualified("stream/xr-tracking", "limited"))
    );
}

fn panel_placement() -> PanelPlacement {
    PanelPlacement::new(
        Symbol::new("main"),
        AnchorSpace::World,
        Transform3::new([0.2, 0.1, -0.1], [0.0, 0.0, 0.0, 1.0], [1.0, 1.0, 1.0]),
    )
    .with_world_anchor(Symbol::new("desk-plane"))
}

fn resolver() -> WorldAnchorResolver {
    WorldAnchorResolver::new([WorldAnchorObservation::new(
        Symbol::new("desk-plane"),
        Transform3::new([0.0, 1.0, -1.6], [0.0, 0.0, 0.0, 1.0], [1.0, 1.0, 1.0]),
    )])
}

fn assert_transform_near(transform: &Transform3, expected: [f64; 3]) {
    for (actual, expected) in transform.translate_m.into_iter().zip(expected) {
        assert!((actual - expected).abs() < 0.000_001);
    }
}