inspect.rsRead only
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.
Generated scene.konjureBuild artifact
{
"format": "konjure.scene",
"version": 1,
"id": "rust_inspect",
"title": "Typed Rust inspection scene",
"entities": [
{
"id": "station",
"parent": null,
"transform": {
"translation": {
"x": 1.0,
"y": 0.0,
"z": 0.0
},
"rotation": {
"x": 0.0,
"y": 0.0,
"z": 0.0,
"w": 1.0
},
"scale": {
"x": 1.0,
"y": 1.0,
"z": 1.0
}
},
"geometry": {
"kind": "group"
},
"color": {
"r": 255,
"g": 255,
"b": 255,
"a": 255
},
"material": {
"roughness": 0.65,
"emissive": 0.0
},
"animations": [],
"actions": {}
},
{
"id": "beacon",
"parent": "station",
"transform": {
"translation": {
"x": 0.0,
"y": 0.5,
"z": -1.0
},
"rotation": {
"x": 0.0,
"y": 0.0,
"z": 0.0,
"w": 1.0
},
"scale": {
"x": 1.0,
"y": 1.0,
"z": 1.0
}
},
"geometry": {
"kind": "box",
"size_m": {
"x": 0.3,
"y": 0.3,
"z": 0.3
}
},
"color": {
"r": 111,
"g": 227,
"b": 255,
"a": 255
},
"material": {
"roughness": 0.65,
"emissive": 0.0
},
"animations": [
{
"kind": "orbit",
"radius_m": 2.0,
"period_s": 4.0
}
],
"actions": {
"notify_operator": {
"kind": "propose_effect",
"effect": "notify_operator"
},
"toggle_motion": {
"kind": "toggle_motion"
}
}
}
],
"conversation": []
}Native trace.jsonBuild artifact
{
"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"
}
}
}
]
}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 →cargo run -p konjure-sdk --example inspect
cargo test -p konjure-sdk --example inspect
bash scripts/check-sdk.sh
npm run sdk:wasmNative 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
{
"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
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);
}
}