Skip to content
Konjure / spatial intelligence

Inspect the Rust SDK

Run a typed Rust scene, inspect the same document in WASM, and compare native output while improving one component at a time.

inspect.rsRead only
Sourcerust
use konjure_sdk::{
    Action, ActionName, Animation, Color, Entity, EntityId, Event, FORMAT_VERSION, Geometry,
    Receipt, Runtime, SceneDocument, SceneId, Transform, Vec3,
};
use serde::Serialize;
use std::{
    collections::BTreeMap,
    env,
    error::Error,
    fs,
    io::{Error as IoError, ErrorKind},
    path::{Path, PathBuf},
};
#[derive(Serialize)]
struct Trace {
    format: &'static str,
    version: u16,
    scene_id: SceneId,
    frames: Vec<konjure_sdk::Frame>,
    events: Vec<Event>,
    receipts: Vec<Receipt>,
}
fn vec3(x: f64, y: f64, z: f64) -> Vec3 {
    Vec3 { x, y, z }
}

fn invoke(action: &str) -> Event {
    Event::InvokeAction {
        entity: EntityId("beacon".into()),
        action: ActionName(action.into()),
    }
}
fn scene() -> SceneDocument {
    let mut actions = BTreeMap::new();
    actions.insert(ActionName("toggle_motion".into()), Action::ToggleMotion);
    actions.insert(
        ActionName("notify_operator".into()),
        Action::ProposeEffect {
            effect: "notify_operator".into(),
        },
    );
    SceneDocument {
        format: "konjure.scene".into(),
        version: FORMAT_VERSION,
        id: SceneId("rust_inspect".into()),
        title: "Typed Rust inspection scene".into(),
        entities: vec![
            Entity {
                id: EntityId("station".into()),
                parent: None,
                transform: Transform {
                    translation: vec3(1.0, 0.0, 0.0),
                    ..Transform::default()
                },
                geometry: Geometry::Group,
                color: Color::WHITE,
                material: Default::default(),
                animations: vec![],
                actions: BTreeMap::new(),
            },
            Entity {
                id: EntityId("beacon".into()),
                parent: Some(EntityId("station".into())),
                transform: Transform {
                    translation: vec3(0.0, 0.5, -1.0),
                    ..Transform::default()
                },
                geometry: Geometry::Box {
                    size_m: vec3(0.3, 0.3, 0.3),
                },
                color: Color {
                    r: 111,
                    g: 227,
                    b: 255,
                    a: 255,
                },
                material: Default::default(),
                animations: vec![Animation::Orbit {
                    radius_m: 2.0,
                    period_s: 4.0,
                }],
                actions,
            },
        ],
        conversation: vec![],
    }
}

fn position(frame: &konjure_sdk::Frame, entity: &str) -> [f64; 3] {
    let draw = frame
        .draws
        .iter()
        .find(|draw| draw.entity.0 == entity)
        .expect("example entity is present");
    [draw.matrix[12], draw.matrix[13], draw.matrix[14]]
}

fn write_artifacts(
    directory: &Path,
    scene: &SceneDocument,
    trace: &Trace,
) -> Result<(), Box<dyn Error>> {
    fs::create_dir_all(directory)?;
    fs::write(
        directory.join("scene.konjure"),
        serde_json::to_vec_pretty(scene)?,
    )?;
    fs::write(directory.join("trace.json"), serde_json::to_vec(trace)?)?;
    Ok(())
}

fn main() -> Result<(), Box<dyn Error>> {
    let arguments = env::args_os().skip(1).collect::<Vec<_>>();
    let output = match arguments.as_slice() {
        [] => None,
        [directory] => Some(PathBuf::from(directory)),
        _ => {
            return Err(
                IoError::new(ErrorKind::InvalidInput, "usage: inspect [output-directory]").into(),
            );
        }
    };
    let scene = scene();
    let mut runtime = Runtime::new(scene.clone())?;
    let start = runtime.sample(0.0)?;
    let moving = runtime.sample(1.0)?;
    let events = vec![invoke("toggle_motion"), invoke("notify_operator")];
    let pause = runtime.dispatch(events[0].clone())?;
    let paused = runtime.sample(2.0)?;
    let proposed = runtime.dispatch(events[1].clone())?;
    let trace = Trace {
        format: "konjure.inspect.trace",
        version: 1,
        scene_id: scene.id.clone(),
        frames: vec![start, moving.clone(), paused.clone()],
        events,
        receipts: vec![pause.clone(), proposed.clone()],
    };

    println!("Konjure typed Rust inspection: {}", scene.title);
    println!(
        "entities: {} (station group, beacon box)",
        scene.entities.len()
    );
    println!(
        "t=1.0 beacon world position: {:?}",
        position(&moving, "beacon")
    );
    println!("toggle_motion receipt: {:?}", pause.status);
    println!(
        "t=2.0 paused beacon world position: {:?}",
        position(&paused, "beacon")
    );
    println!(
        "notify_operator receipt: {:?}; no external effect was executed",
        proposed.status
    );
    if let Some(directory) = output {
        write_artifacts(&directory, &scene, &trace)?;
        println!(
            "wrote {}/scene.konjure and {}/trace.json",
            directory.display(),
            directory.display()
        );
    } else {
        println!("pass an output directory to write scene.konjure and trace.json");
    }
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;
    use konjure_sdk::{ReceiptStatus, RuntimeEvent};

    #[test]
    fn typed_scene_has_box_geometry_and_hierarchical_absolute_time_motion() {
        let document = scene();
        assert!(matches!(
            document.entities[1].geometry,
            Geometry::Box { .. }
        ));
        let frame = Runtime::new(document)
            .expect("valid typed scene")
            .sample(1.0)
            .expect("sample");
        assert!(
            position(&frame, "beacon")
                .iter()
                .zip([1.0, 0.5, 1.0])
                .all(|(actual, expected)| (*actual - expected).abs() < 1e-12)
        );
    }

    #[test]
    fn rejected_action_preserves_sampled_state() {
        let mut runtime = Runtime::new(scene()).expect("valid typed scene");
        let before = runtime.sample(1.0).expect("sample");
        assert!(runtime.dispatch(invoke("undeclared")).is_err());
        assert_eq!(runtime.sample(1.0).expect("sample"), before);
    }

    #[test]
    fn declared_actions_apply_or_only_propose_effects() {
        let mut runtime = Runtime::new(scene()).expect("valid typed scene");
        let pause = runtime
            .dispatch(invoke("toggle_motion"))
            .expect("declared action");
        assert_eq!(pause.status, ReceiptStatus::Applied);
        assert_eq!(
            position(&runtime.sample(2.0).expect("sample"), "beacon"),
            [1.0, 0.5, -1.0]
        );
        let before = runtime.sample(2.0).expect("sample").draws;
        let proposal = runtime
            .dispatch(invoke("notify_operator"))
            .expect("declared proposal");
        assert_eq!(proposal.status, ReceiptStatus::Proposed);
        assert!(matches!(
            proposal.event,
            RuntimeEvent::EffectProposed { .. }
        ));
        assert_eq!(runtime.sample(2.0).expect("sample").draws, before);
    }
}
Typed Rust inspection sceneRust → WASM · drag to orbit
Generated scene.konjureWASM preview
Loading the exported Rust scene…
Live WASM frame or receipt
Waiting for a sample.

Run the example

Run the example, change one value or contract, and run its tests. Rebuild the SDK assets to explore your result here. These controls call the Rust/WASM runtime; scene behavior is not reimplemented in this page.

Develop the SDK →
Sourcebash
cargo run -p konjure-sdk --example inspect
cargo test -p konjure-sdk --example inspect
bash scripts/check-sdk.sh
npm run sdk:wasm

Native output

The example samples three frames, toggles motion off, and proposes an external effect. Toggling motion restores the authored position; it does not freeze an orbit at its current point. An effect proposal does not execute a tool or control hardware.

Read the native trace
Sourcejson
{
  "format": "konjure.inspect.trace",
  "version": 1,
  "scene_id": "rust_inspect",
  "frames": [
    {
      "time_seconds": 0,
      "draws": [
        {
          "entity": "beacon",
          "mesh_entity": "beacon",
          "transform": {
            "translation": {
              "x": 2,
              "y": 0.5,
              "z": -1
            },
            "rotation": {
              "x": 0,
              "y": 0,
              "z": 0,
              "w": 1
            },
            "scale": {
              "x": 1,
              "y": 1,
              "z": 1
            }
          },
          "matrix": [
            1,
            0,
            0,
            0,
            0,
            1,
            0,
            0,
            0,
            0,
            1,
            0,
            3,
            0.5,
            -1,
            1
          ],
          "color": {
            "r": 111,
            "g": 227,
            "b": 255,
            "a": 255
          }
        },
        {
          "entity": "station",
          "mesh_entity": null,
          "transform": {
            "translation": {
              "x": 1,
              "y": 0,
              "z": 0
            },
            "rotation": {
              "x": 0,
              "y": 0,
              "z": 0,
              "w": 1
            },
            "scale": {
              "x": 1,
              "y": 1,
              "z": 1
            }
          },
          "matrix": [
            1,
            0,
            0,
            0,
            0,
            1,
            0,
            0,
            0,
            0,
            1,
            0,
            1,
            0,
            0,
            1
          ],
          "color": {
            "r": 255,
            "g": 255,
            "b": 255,
            "a": 255
          }
        }
      ],
      "events": []
    },
    {
      "time_seconds": 1,
      "draws": [
        {
          "entity": "beacon",
          "mesh_entity": "beacon",
          "transform": {
            "translation": {
              "x": 1.2246467991473532e-16,
              "y": 0.5,
              "z": 1
            },
            "rotation": {
              "x": 0,
              "y": 0,
              "z": 0,
              "w": 1
            },
            "scale": {
              "x": 1,
              "y": 1,
              "z": 1
            }
          },
          "matrix": [
            1,
            0,
            0,
            0,
            0,
            1,
            0,
            0,
            0,
            0,
            1,
            0,
            1.0000000000000002,
            0.5,
            1,
            1
          ],
          "color": {
            "r": 111,
            "g": 227,
            "b": 255,
            "a": 255
          }
        },
        {
          "entity": "station",
          "mesh_entity": null,
          "transform": {
            "translation": {
              "x": 1,
              "y": 0,
              "z": 0
            },
            "rotation": {
              "x": 0,
              "y": 0,
              "z": 0,
              "w": 1
            },
            "scale": {
              "x": 1,
              "y": 1,
              "z": 1
            }
          },
          "matrix": [
            1,
            0,
            0,
            0,
            0,
            1,
            0,
            0,
            0,
            0,
            1,
            0,
            1,
            0,
            0,
            1
          ],
          "color": {
            "r": 255,
            "g": 255,
            "b": 255,
            "a": 255
          }
        }
      ],
      "events": []
    },
    {
      "time_seconds": 2,
      "draws": [
        {
          "entity": "beacon",
          "mesh_entity": "beacon",
          "transform": {
            "translation": {
              "x": 0,
              "y": 0.5,
              "z": -1
            },
            "rotation": {
              "x": 0,
              "y": 0,
              "z": 0,
              "w": 1
            },
            "scale": {
              "x": 1,
              "y": 1,
              "z": 1
            }
          },
          "matrix": [
            1,
            0,
            0,
            0,
            0,
            1,
            0,
            0,
            0,
            0,
            1,
            0,
            1,
            0.5,
            -1,
            1
          ],
          "color": {
            "r": 111,
            "g": 227,
            "b": 255,
            "a": 255
          }
        },
        {
          "entity": "station",
          "mesh_entity": null,
          "transform": {
            "translation": {
              "x": 1,
              "y": 0,
              "z": 0
            },
            "rotation": {
              "x": 0,
              "y": 0,
              "z": 0,
              "w": 1
            },
            "scale": {
              "x": 1,
              "y": 1,
              "z": 1
            }
          },
          "matrix": [
            1,
            0,
            0,
            0,
            0,
            1,
            0,
            0,
            0,
            0,
            1,
            0,
            1,
            0,
            0,
            1
          ],
          "color": {
            "r": 255,
            "g": 255,
            "b": 255,
            "a": 255
          }
        }
      ],
      "events": [
        {
          "action_applied": {
            "entity": "beacon",
            "action": "toggle_motion"
          }
        }
      ]
    }
  ],
  "events": [
    {
      "kind": "invoke_action",
      "entity": "beacon",
      "action": "toggle_motion"
    },
    {
      "kind": "invoke_action",
      "entity": "beacon",
      "action": "notify_operator"
    }
  ],
  "receipts": [
    {
      "status": "applied",
      "event": {
        "action_applied": {
          "entity": "beacon",
          "action": "toggle_motion"
        }
      }
    },
    {
      "status": "proposed",
      "event": {
        "effect_proposed": {
          "entity": "beacon",
          "effect": "notify_operator"
        }
      }
    }
  ]
}

Source and tests

The tests below assert independently expected positions, rejected-action preservation, and applied versus proposed receipts.

Read the Rust example and tests
Sourcerust
use konjure_sdk::{
    Action, ActionName, Animation, Color, Entity, EntityId, Event, FORMAT_VERSION, Geometry,
    Receipt, Runtime, SceneDocument, SceneId, Transform, Vec3,
};
use serde::Serialize;
use std::{
    collections::BTreeMap,
    env,
    error::Error,
    fs,
    io::{Error as IoError, ErrorKind},
    path::{Path, PathBuf},
};
#[derive(Serialize)]
struct Trace {
    format: &'static str,
    version: u16,
    scene_id: SceneId,
    frames: Vec<konjure_sdk::Frame>,
    events: Vec<Event>,
    receipts: Vec<Receipt>,
}
fn vec3(x: f64, y: f64, z: f64) -> Vec3 {
    Vec3 { x, y, z }
}

fn invoke(action: &str) -> Event {
    Event::InvokeAction {
        entity: EntityId("beacon".into()),
        action: ActionName(action.into()),
    }
}
fn scene() -> SceneDocument {
    let mut actions = BTreeMap::new();
    actions.insert(ActionName("toggle_motion".into()), Action::ToggleMotion);
    actions.insert(
        ActionName("notify_operator".into()),
        Action::ProposeEffect {
            effect: "notify_operator".into(),
        },
    );
    SceneDocument {
        format: "konjure.scene".into(),
        version: FORMAT_VERSION,
        id: SceneId("rust_inspect".into()),
        title: "Typed Rust inspection scene".into(),
        entities: vec![
            Entity {
                id: EntityId("station".into()),
                parent: None,
                transform: Transform {
                    translation: vec3(1.0, 0.0, 0.0),
                    ..Transform::default()
                },
                geometry: Geometry::Group,
                color: Color::WHITE,
                material: Default::default(),
                animations: vec![],
                actions: BTreeMap::new(),
            },
            Entity {
                id: EntityId("beacon".into()),
                parent: Some(EntityId("station".into())),
                transform: Transform {
                    translation: vec3(0.0, 0.5, -1.0),
                    ..Transform::default()
                },
                geometry: Geometry::Box {
                    size_m: vec3(0.3, 0.3, 0.3),
                },
                color: Color {
                    r: 111,
                    g: 227,
                    b: 255,
                    a: 255,
                },
                material: Default::default(),
                animations: vec![Animation::Orbit {
                    radius_m: 2.0,
                    period_s: 4.0,
                }],
                actions,
            },
        ],
        conversation: vec![],
    }
}

fn position(frame: &konjure_sdk::Frame, entity: &str) -> [f64; 3] {
    let draw = frame
        .draws
        .iter()
        .find(|draw| draw.entity.0 == entity)
        .expect("example entity is present");
    [draw.matrix[12], draw.matrix[13], draw.matrix[14]]
}

fn write_artifacts(
    directory: &Path,
    scene: &SceneDocument,
    trace: &Trace,
) -> Result<(), Box<dyn Error>> {
    fs::create_dir_all(directory)?;
    fs::write(
        directory.join("scene.konjure"),
        serde_json::to_vec_pretty(scene)?,
    )?;
    fs::write(directory.join("trace.json"), serde_json::to_vec(trace)?)?;
    Ok(())
}

fn main() -> Result<(), Box<dyn Error>> {
    let arguments = env::args_os().skip(1).collect::<Vec<_>>();
    let output = match arguments.as_slice() {
        [] => None,
        [directory] => Some(PathBuf::from(directory)),
        _ => {
            return Err(
                IoError::new(ErrorKind::InvalidInput, "usage: inspect [output-directory]").into(),
            );
        }
    };
    let scene = scene();
    let mut runtime = Runtime::new(scene.clone())?;
    let start = runtime.sample(0.0)?;
    let moving = runtime.sample(1.0)?;
    let events = vec![invoke("toggle_motion"), invoke("notify_operator")];
    let pause = runtime.dispatch(events[0].clone())?;
    let paused = runtime.sample(2.0)?;
    let proposed = runtime.dispatch(events[1].clone())?;
    let trace = Trace {
        format: "konjure.inspect.trace",
        version: 1,
        scene_id: scene.id.clone(),
        frames: vec![start, moving.clone(), paused.clone()],
        events,
        receipts: vec![pause.clone(), proposed.clone()],
    };

    println!("Konjure typed Rust inspection: {}", scene.title);
    println!(
        "entities: {} (station group, beacon box)",
        scene.entities.len()
    );
    println!(
        "t=1.0 beacon world position: {:?}",
        position(&moving, "beacon")
    );
    println!("toggle_motion receipt: {:?}", pause.status);
    println!(
        "t=2.0 paused beacon world position: {:?}",
        position(&paused, "beacon")
    );
    println!(
        "notify_operator receipt: {:?}; no external effect was executed",
        proposed.status
    );
    if let Some(directory) = output {
        write_artifacts(&directory, &scene, &trace)?;
        println!(
            "wrote {}/scene.konjure and {}/trace.json",
            directory.display(),
            directory.display()
        );
    } else {
        println!("pass an output directory to write scene.konjure and trace.json");
    }
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;
    use konjure_sdk::{ReceiptStatus, RuntimeEvent};

    #[test]
    fn typed_scene_has_box_geometry_and_hierarchical_absolute_time_motion() {
        let document = scene();
        assert!(matches!(
            document.entities[1].geometry,
            Geometry::Box { .. }
        ));
        let frame = Runtime::new(document)
            .expect("valid typed scene")
            .sample(1.0)
            .expect("sample");
        assert!(
            position(&frame, "beacon")
                .iter()
                .zip([1.0, 0.5, 1.0])
                .all(|(actual, expected)| (*actual - expected).abs() < 1e-12)
        );
    }

    #[test]
    fn rejected_action_preserves_sampled_state() {
        let mut runtime = Runtime::new(scene()).expect("valid typed scene");
        let before = runtime.sample(1.0).expect("sample");
        assert!(runtime.dispatch(invoke("undeclared")).is_err());
        assert_eq!(runtime.sample(1.0).expect("sample"), before);
    }

    #[test]
    fn declared_actions_apply_or_only_propose_effects() {
        let mut runtime = Runtime::new(scene()).expect("valid typed scene");
        let pause = runtime
            .dispatch(invoke("toggle_motion"))
            .expect("declared action");
        assert_eq!(pause.status, ReceiptStatus::Applied);
        assert_eq!(
            position(&runtime.sample(2.0).expect("sample"), "beacon"),
            [1.0, 0.5, -1.0]
        );
        let before = runtime.sample(2.0).expect("sample").draws;
        let proposal = runtime
            .dispatch(invoke("notify_operator"))
            .expect("declared proposal");
        assert_eq!(proposal.status, ReceiptStatus::Proposed);
        assert!(matches!(
            proposal.event,
            RuntimeEvent::EffectProposed { .. }
        ));
        assert_eq!(runtime.sample(2.0).expect("sample").draws, before);
    }
}