1use konjure_sdk::{Diagnostic, Event, Runtime, SceneDocument};
3use wasm_bindgen::prelude::*;
4
5mod language;
6pub use language::*;
7mod browser;
8pub use browser::*;
9mod interchange;
10pub use interchange::*;
11
12const MAX_DOCUMENT_BYTES: usize = 1024 * 1024;
13const FRAME_STRIDE: usize = 20;
14
15fn diagnostic(error: Diagnostic) -> JsValue {
16 JsValue::from_str(&serde_json::to_string(&error).unwrap_or_else(|_| error.to_string()))
17}
18fn json_error(error: impl std::fmt::Display) -> JsValue {
19 JsValue::from_str(&format!("{error}"))
20}
21fn encode(value: &impl serde::Serialize) -> Result<String, JsValue> {
22 serde_json::to_string(value).map_err(json_error)
23}
24
25#[wasm_bindgen]
27pub fn compile(source: &str) -> Result<String, JsValue> {
28 encode(&konjure_sdk::compile(source).map_err(diagnostic)?)
29}
30
31#[wasm_bindgen]
32pub fn sdk_version() -> String {
33 env!("CARGO_PKG_VERSION").into()
34}
35
36#[wasm_bindgen]
38pub struct SdkRuntime {
39 inner: Runtime,
40}
41
42#[wasm_bindgen]
43impl SdkRuntime {
44 #[wasm_bindgen(constructor)]
45 pub fn new(source: &str) -> Result<SdkRuntime, JsValue> {
46 let scene = konjure_sdk::compile(source).map_err(diagnostic)?;
47 Ok(Self {
48 inner: Runtime::new(scene).map_err(diagnostic)?,
49 })
50 }
51
52 #[wasm_bindgen(js_name = fromDocument)]
53 pub fn from_document(document: &str) -> Result<SdkRuntime, JsValue> {
54 if document.len() > MAX_DOCUMENT_BYTES {
55 return Err(JsValue::from_str("document exceeds 1 MiB"));
56 }
57 let scene: SceneDocument = serde_json::from_str(document).map_err(json_error)?;
58 Ok(Self {
59 inner: Runtime::new(scene).map_err(diagnostic)?,
60 })
61 }
62
63 pub fn document_json(&self) -> Result<String, JsValue> {
64 encode(self.inner.scene())
65 }
66 pub fn meshes_json(&self) -> Result<String, JsValue> {
67 encode(self.inner.meshes())
68 }
69 pub fn sample_json(&self, time_seconds: f64) -> Result<String, JsValue> {
70 encode(&self.inner.sample(time_seconds).map_err(diagnostic)?)
71 }
72 pub fn ids_json(&self) -> Result<String, JsValue> {
74 encode(
75 &self
76 .inner
77 .sample(0.0)
78 .map_err(diagnostic)?
79 .draws
80 .iter()
81 .map(|d| &d.entity)
82 .collect::<Vec<_>>(),
83 )
84 }
85 pub fn frame(&self, time_seconds: f64) -> Result<Vec<f32>, JsValue> {
87 let frame = self.inner.sample(time_seconds).map_err(diagnostic)?;
88 let mut packed = Vec::with_capacity(frame.draws.len() * FRAME_STRIDE);
89 for draw in frame.draws {
90 for value in draw.matrix {
91 let value = value as f32;
92 if !value.is_finite() {
93 return Err(JsValue::from_str(
94 "world transform exceeds the render float range",
95 ));
96 }
97 packed.push(value);
98 }
99 packed.extend(
100 [draw.color.r, draw.color.g, draw.color.b, draw.color.a]
101 .map(|v| f32::from(v) / 255.0),
102 );
103 }
104 Ok(packed)
105 }
106 pub fn dispatch(&mut self, event: &str) -> Result<String, JsValue> {
107 if event.len() > 4096 {
108 return Err(JsValue::from_str("event exceeds 4 KiB"));
109 }
110 let event: Event = serde_json::from_str(event).map_err(json_error)?;
111 encode(&self.inner.dispatch(event).map_err(diagnostic)?)
112 }
113}