Skip to main content

oxmpl_js/base/
compound_state.rs

1// Copyright (c) 2025 Junior Sundar
2//
3// SPDX-License-Identifier: BSD-3-Clause
4
5use std::{any::Any, sync::Arc};
6
7use oxmpl::base::state::{
8    CompoundState, RealVectorState, SE2State, SE3State, SO2State, SO3State, State,
9};
10use wasm_bindgen::prelude::*;
11
12use crate::base::{JsRealVectorState, JsSE2State, JsSE3State, JsSO2State, JsSO3State};
13
14#[wasm_bindgen(js_name = CompoundState)]
15pub struct JsCompoundState {
16    #[wasm_bindgen(skip)]
17    pub inner: Arc<CompoundState>,
18}
19
20#[wasm_bindgen(js_class = CompoundState)]
21impl JsCompoundState {
22    #[wasm_bindgen(getter = componentCount)]
23    pub fn component_count(&self) -> usize {
24        self.inner.components.len()
25    }
26
27    #[wasm_bindgen(js_name = getComponent)]
28    pub fn get_component(&self, index: usize) -> Result<JsValue, String> {
29        if index >= self.inner.components.len() {
30            return Err("Index out of bounds".to_string());
31        }
32
33        let component = &self.inner.components[index];
34        let component_ref: &dyn State = component.as_ref();
35        let any_ref = component_ref as &dyn Any;
36
37        if let Some(s) = any_ref.downcast_ref::<RealVectorState>() {
38            return Ok(JsValue::from(JsRealVectorState {
39                inner: Arc::new(s.clone()),
40            }));
41        }
42        if let Some(s) = any_ref.downcast_ref::<SO2State>() {
43            return Ok(JsValue::from(JsSO2State::new(s.value)));
44        }
45        if let Some(s) = any_ref.downcast_ref::<SO3State>() {
46            return Ok(JsValue::from(JsSO3State::new(s.x, s.y, s.z, s.w)));
47        }
48        if let Some(s) = any_ref.downcast_ref::<SE2State>() {
49            return Ok(JsValue::from(JsSE2State::new(
50                s.get_x(),
51                s.get_y(),
52                s.get_yaw(),
53            )));
54        }
55        if let Some(s) = any_ref.downcast_ref::<SE3State>() {
56            let rotation = JsSO3State::new(
57                s.get_rotation().x,
58                s.get_rotation().y,
59                s.get_rotation().z,
60                s.get_rotation().w,
61            );
62            return Ok(JsValue::from(JsSE3State::new(
63                s.get_x(),
64                s.get_y(),
65                s.get_z(),
66                rotation,
67            )));
68        }
69        if let Some(s) = any_ref.downcast_ref::<CompoundState>() {
70            return Ok(JsValue::from(JsCompoundState {
71                inner: Arc::new(s.clone()),
72            }));
73        }
74
75        Err("Unknown component state type".to_string())
76    }
77}
78
79/// A builder for creating `CompoundState` instances from JavaScript.
80///
81/// The builder pattern is used here instead of Runtime Type Inferencing (downcasting) from generic
82/// JavaScript objects because the latter required `unsafe` blocks or complex trait machinery that
83/// is not well-supported by `wasm-bindgen`.
84#[wasm_bindgen(js_name = CompoundStateBuilder)]
85pub struct JsCompoundStateBuilder {
86    components: Vec<Box<dyn State>>,
87}
88
89impl Default for JsCompoundStateBuilder {
90    fn default() -> Self {
91        Self::new()
92    }
93}
94
95#[wasm_bindgen(js_class = CompoundStateBuilder)]
96impl JsCompoundStateBuilder {
97    #[wasm_bindgen(constructor)]
98    pub fn new() -> Self {
99        Self {
100            components: Vec::new(),
101        }
102    }
103
104    #[wasm_bindgen(js_name = addRealVectorState)]
105    pub fn add_real_vector_state(&mut self, state: &JsRealVectorState) {
106        self.components.push(Box::new((*state.inner).clone()));
107    }
108
109    #[wasm_bindgen(js_name = addSO2State)]
110    pub fn add_so2_state(&mut self, state: &JsSO2State) {
111        self.components.push(Box::new((*state.inner).clone()));
112    }
113
114    #[wasm_bindgen(js_name = addSO3State)]
115    pub fn add_so3_state(&mut self, state: &JsSO3State) {
116        self.components.push(Box::new((*state.inner).clone()));
117    }
118
119    #[wasm_bindgen(js_name = addSE2State)]
120    pub fn add_se2_state(&mut self, state: &JsSE2State) {
121        self.components.push(Box::new((*state.inner).clone()));
122    }
123
124    #[wasm_bindgen(js_name = addSE3State)]
125    pub fn add_se3_state(&mut self, state: &JsSE3State) {
126        self.components.push(Box::new((*state.inner).clone()));
127    }
128
129    #[wasm_bindgen(js_name = addCompoundState)]
130    pub fn add_compound_state(&mut self, state: &JsCompoundState) {
131        self.components.push(Box::new((*state.inner).clone()));
132    }
133
134    pub fn build(self) -> JsCompoundState {
135        JsCompoundState {
136            inner: Arc::new(CompoundState::new(self.components)),
137        }
138    }
139}