sim-lib-view-spatial 0.1.0

Spatial glasses surface encoding over the shared SIM Web Scene and device adapters.
Documentation
use std::sync::Arc;

use sim_kernel::{Cx, DefaultFactory, EagerPolicy, Expr};
use sim_lib_scene::{Anchor, AnchorSpace, GLANCE_KIND, Transform3};
use sim_lib_view::{SurfaceCaps, SurfaceCodec, UniversalView, View};
use sim_lib_view_device::{DeviceSurfaceCapsExt, GlanceReducer};
use sim_value::{access, build};

use crate::SpatialSurfaceCodec;

#[test]
fn encoders_are_pose_free_and_halo_uses_glance_reducer() {
    let mut cx = Cx::new(Arc::new(EagerPolicy), Arc::new(DefaultFactory));
    let codec = SpatialSurfaceCodec::new();
    let value = build::map(vec![
        ("title", Expr::String("Daily focus".to_owned())),
        ("spatial-layout", layout()),
    ]);

    let viture = SurfaceCaps::from_preset("glasses-luma-ultra", "viture").unwrap();
    let viture_scene = codec.encode(&mut cx, &value, &viture).unwrap();
    assert_scene_kind(&viture_scene, "spatial");
    assert_pose_free(&viture_scene);
    let first_panel = first_child(&viture_scene);
    assert_scene_kind(first_panel, "panel");
    assert_eq!(access::field_str(first_panel, "id"), Some("main"));

    let halo = SurfaceCaps::from_preset("glasses-hud", "halo").unwrap();
    let halo_scene = codec.encode(&mut cx, &value, &halo).unwrap();
    assert_scene_kind(&halo_scene, GLANCE_KIND);
    assert_pose_free(&halo_scene);
    let source_scene = UniversalView
        .encode(
            &mut cx,
            &build::map(vec![("title", Expr::String("Daily focus".to_owned()))]),
        )
        .unwrap();
    let expected_halo = GlanceReducer
        .reduce(&source_scene, &halo.device_profile())
        .unwrap();
    assert_eq!(halo_scene, expected_halo);

    let display = SurfaceCaps::from_preset("glasses", "mirror").unwrap();
    let display_scene = codec.encode(&mut cx, &value, &display).unwrap();
    assert_scene_kind(&display_scene, "stack");
    assert_pose_free(&display_scene);
}

fn layout() -> Expr {
    build::map(vec![(
        "panels",
        build::list(vec![build::map(vec![
            ("id", Expr::String("main".to_owned())),
            ("anchor", Anchor::new(AnchorSpace::World, "desk").to_expr()),
            (
                "transform",
                Transform3::new([0.0, 1.2, -1.6], [0.0, 0.0, 0.0, 1.0], [1.0, 1.0, 1.0]).to_expr(),
            ),
        ])]),
    )])
}

fn assert_scene_kind(scene: &Expr, expected: &str) {
    let kind = sim_lib_scene::node_kind(scene).expect("scene kind");
    assert_eq!(kind.namespace.as_deref(), Some("scene"));
    assert_eq!(kind.name.as_ref(), expected);
}

fn first_child(scene: &Expr) -> &Expr {
    match access::field(scene, "children").expect("children") {
        Expr::List(children) => children.first().expect("at least one child"),
        other => panic!("children must be a list, got {other:?}"),
    }
}

fn assert_pose_free(expr: &Expr) {
    match expr {
        Expr::Map(entries) => {
            for (key, value) in entries {
                if let Expr::Symbol(symbol) = key {
                    assert_ne!(symbol.name.as_ref(), "pose");
                    assert_ne!(symbol.name.as_ref(), "tick");
                }
                assert_pose_free(value);
            }
        }
        Expr::List(items) | Expr::Vector(items) | Expr::Set(items) => {
            for item in items {
                assert_pose_free(item);
            }
        }
        _ => {}
    }
}