mujoco_rs/wrappers/
mj_interface.rs

1//! MuJoCo user interface
2//! This IS NOT YET TESTED.
3use super::mj_rendering::MjrContext;
4use crate::mujoco_c::*;
5
6/******************************** */
7//           Constants
8/******************************** */
9const MAX_UI_STORAGE_SIZE: usize = 256;  // Rust-specific constant for the location where the UI will write data.
10
11
12/******************************** */
13//           Enums
14/******************************** */
15/// Mouse button IDs used in the UI framework.
16pub type MjtButton = mjtButton;
17
18/// Event types used in the UI framework.
19pub type MjtEvent = mjtEvent;
20
21/// Item types used in the UI framework.
22pub type MjtItem = mjtItem;
23
24/// State of a UI section.
25pub type MjtSection = mjtSection;
26
27
28/***********************************************************************************************************************
29** MjuiState
30***********************************************************************************************************************/
31pub type MjuiState = mjuiState;
32
33impl Default for MjuiState{
34    fn default() -> Self {
35        unsafe { std::mem::zeroed() }
36    }
37}
38
39/***********************************************************************************************************************
40** MjuiThemeSpacing
41***********************************************************************************************************************/
42pub type MjuiThemeSpacing = mjuiThemeSpacing;
43
44impl Default for MjuiThemeSpacing{
45    fn default() -> Self {
46        unsafe { std::mem::zeroed() }
47    }
48}
49
50
51/***********************************************************************************************************************
52** MjuiThemeColor
53***********************************************************************************************************************/
54pub type MjuiThemeColor = mjuiThemeColor;
55
56impl Default for MjuiThemeColor{
57    fn default() -> Self {
58        unsafe { std::mem::zeroed() }
59    }
60}
61
62
63/***********************************************************************************************************************
64** MjuiItem
65***********************************************************************************************************************/
66// pub enum MjuiItemModifier {
67//     None = 0,
68//     Control,
69//     Shift,
70//     Alt
71// }
72
73// impl MjuiItemModifier {
74//     fn from_ffi(ffi: i32) -> Self {
75//         match ffi {
76//             0 => Self::None,
77//             1 => Self::Control,
78//             2 => Self::Shift,
79//             4 => Self::Alt,
80//             _ => unreachable!()
81//         }
82//     }
83// }
84
85#[derive(Clone)]
86pub enum MjuiItemState {
87    Disable = 0,
88    Enable,
89}
90
91// /// Possible data types of the Edit widget.
92// pub enum MjuiItemEditType {
93//     String (String),
94//     Integer (i32),
95//     Double (f64),
96// }
97
98// /// Type of MuJoCo's UI widgets.
99// pub enum MjuiItemType { 
100//     Check { modifier: MjuiItemModifier, data: bool },
101//     Radio { data: bool },
102//     RadioLine { data: bool },
103//     Select,
104//     Slider { range: (f64, f64), divisions: f64, data: f64 },
105//     Edit { range: (f64, f64), data: MjuiItemEditType },
106//     Button,
107//     Other
108// }
109
110// /// MuJoCo UI widget
111// pub struct MjuiItem {
112//     pub name: String,
113//     pub state: MjuiItemState,
114//     pub sectionid: u32,
115//     pub itemid: u32,
116//     pub userid: u32,
117//     pub type_prop: MjuiItemType
118// }
119
120// impl MjuiItem {
121//     pub(crate) fn from_ffi(ffi: &mjuiItem) -> Self {
122//         /* Convert name */
123//         let name_len = ffi.name.iter().position(|&x| x == 0).unwrap_or(ffi.name.len());
124//         let u8_name = ffi.name.map(|x| x as u8);
125//         let name = String::from_utf8_lossy(&u8_name[..name_len]).to_string();
126
127//         /* Convert state */
128//         let state = match ffi.state {
129//             0 => MjuiItemState::Disable,
130//             _ => MjuiItemState::Enable
131//         };
132
133//         use MjuiItemType::*;
134//         use mjtItem::*;
135        
136//         let ffi_type_enum = ffi.type_.try_into().unwrap();
137//         let type_prop = match ffi_type_enum {
138//             mjITEM_CHECKBYTE | mjITEM_CHECKINT => {
139//                 let single =  unsafe { ffi.__bindgen_anon_1.single.as_ref() };
140//                 let data = if ffi.type_ == mjITEM_CHECKBYTE as i32 {
141//                     unsafe { *std::mem::transmute::<_, *mut i32>(ffi.pdata).as_ref().unwrap() == 1 }
142//                 }
143//                 else {
144//                     unsafe { *std::mem::transmute::<_, *mut mjtNum>(ffi.pdata).as_ref().unwrap() == 1.0 }
145//                 };
146
147//                 Check { modifier: MjuiItemModifier::from_ffi(single.modifier), data}
148//             }
149
150//             mjITEM_BUTTON => Button,
151
152//             mjITEM_RADIO => Radio { data: unsafe { *std::mem::transmute::<_, *mut i32>(ffi.pdata).as_ref().unwrap() == 1 } },        
153
154//             mjITEM_RADIOLINE => RadioLine { data: unsafe { *std::mem::transmute::<_, *mut i32>(ffi.pdata).as_ref().unwrap() == 1 } },    
155
156//             mjITEM_SLIDERINT | mjITEM_SLIDERNUM => {
157//                 let data = if ffi.type_ == mjITEM_SLIDERINT as i32 {
158//                     unsafe { *std::mem::transmute::<_, *mut i32>(ffi.pdata).as_ref().unwrap() as f64 }
159//                 }
160//                 else {
161//                     unsafe { *std::mem::transmute::<_, *mut f64>(ffi.pdata).as_ref().unwrap() }
162//                 };
163
164//                 let slider = unsafe { ffi.__bindgen_anon_1.slider.as_ref() };
165
166//                 Slider { range: slider.range.into(), divisions: slider.divisions, data }
167//             }
168
169//             mjITEM_EDITINT => {  // | mjITEM_EDITNUM | mjITEM_EDITFLOAT | mjITEM_EDITTXT
170//                 let edit = unsafe { ffi.__bindgen_anon_1.slider.as_ref() };
171//                 let data = unsafe { *std::mem::transmute::<_, *mut i32>(ffi.pdata).as_ref().unwrap() };
172//                 Edit { range: edit.range.into(), data: MjuiItemEditType::Integer(data)}
173//             }
174
175//             mjITEM_EDITNUM => {  // | mjITEM_EDITNUM | mjITEM_EDITFLOAT | mjITEM_EDITTXT
176//                 let edit = unsafe { ffi.__bindgen_anon_1.slider.as_ref() };
177//                 let data = unsafe { *std::mem::transmute::<_, *mut mjtNum>(ffi.pdata).as_ref().unwrap() };
178//                 Edit { range: edit.range.into(), data: MjuiItemEditType::Double(data)}
179//             }
180
181//             mjITEM_EDITFLOAT => {  // | mjITEM_EDITNUM | mjITEM_EDITFLOAT | mjITEM_EDITTXT
182//                 let edit = unsafe { ffi.__bindgen_anon_1.slider.as_ref() };
183//                 let data = unsafe { *std::mem::transmute::<_, *mut f32>(ffi.pdata).as_ref().unwrap() };
184//                 Edit { range: edit.range.into(), data: MjuiItemEditType::Double(data as f64)}
185//             }
186
187//             mjITEM_EDITTXT => {  // | mjITEM_EDITNUM | mjITEM_EDITFLOAT | mjITEM_EDITTXT
188//                 let edit = unsafe { ffi.__bindgen_anon_1.slider.as_ref() };
189//                 let data_char = unsafe { std::slice::from_raw_parts(ffi.pdata as *const u8, mjMAXUINAME as usize) };
190//                 let data_len = data_char.iter().position(|&x| x == 0).unwrap_or(mjMAXUINAME as usize);
191//                 let data = String::from_utf8_lossy(&data_char[..data_len]).to_string();
192
193//                 Edit { range: edit.range.into(), data: MjuiItemEditType::String(data)}
194//             }
195
196//             _ => Other
197//         };
198
199//         Self {
200//             name, state, sectionid: ffi.sectionid as u32, itemid: ffi.itemid as u32,
201//             userid: ffi.userid as u32, type_prop
202//         }
203//     }
204// }
205
206
207/***********************************************************************************************************************
208** MjuiSection
209***********************************************************************************************************************/
210pub type MjuiSection = mjuiSection;
211
212impl MjuiSection {
213    /// Returns the ``name`` attribute in Rust's ``String``.
214    pub fn name(&self) -> String {
215        let len = self.name.iter().position(|&x| x == 0).unwrap_or(mjMAXUINAME as usize);
216        let u8_arr: [u8; mjMAXUINAME as usize] = self.name.map(|x| x as u8);
217        String::from_utf8_lossy(&u8_arr[..len]).to_string()
218    }
219
220    /// Sets the ``name`` attribute from ``name``, which is Rust's ``String``.
221    pub fn set_name(&mut self, name: String) {
222        let by = name.as_bytes();
223        assert!(by.len() < mjMAXUINAME as usize);
224        unsafe { self.name[..by.len()].copy_from_slice(std::slice::from_raw_parts(by.as_ptr() as *const i8, by.len())); }
225        self.name[by.len()] = 0;  // C string zero to mark the end of the string.
226    }
227
228    // pub fn items(&self) -> Box<[MjuiItem]> {
229    //     let b: Box<[MjuiItem]> = self.item[..self.nitem as usize].iter().map(|&x| MjuiItem::from_ffi(x)).collect();
230    //     b
231    // }
232
233    pub fn items(&mut self) -> &mut [mjuiItem] {
234        &mut self.item[..self.nitem as usize]
235    }
236}
237
238
239/***********************************************************************************************************************
240** MjuiDef
241***********************************************************************************************************************/
242/// Widget specific configuration for the MjuiDef
243pub enum MjuiDefType { 
244    Check   { modifier: String },
245    Radio   { values: String },
246    Select  { values: String },
247    Slider  { range: (f64, f64) },
248    Edit    { range: (f64, f64), n_dimensions: usize, type_: MjuiDefDataType }, 
249    Button  { modifier: String },
250    End     // Ending element
251}
252
253
254pub enum MjuiDefDataType {
255    String,
256    Integer,
257    Double,
258}
259
260
261pub struct MjuiDef {
262    pub name: String,
263    pub state: MjuiItemState,
264    pub type_: MjuiDefType,
265
266    // Internal data for writing values to, since Rust's borrow checker
267    // doesn't allow MuJoCo's style of keeping a global pointer to some arbitrary data
268    // while it gets modified by the UI.
269    pub storage: [u8; MAX_UI_STORAGE_SIZE]
270}
271
272impl MjuiDef {
273    /// Creates mjuiDef for usage in MuJoCo's UI functions.
274    pub(crate) fn build_ffi_mut(&mut self) -> mjuiDef {
275        let mut ffi = mjuiDef::default();
276        let by = self.name.as_bytes();
277        unsafe { ffi.name[..by.len()].copy_from_slice(std::slice::from_raw_parts(by.as_ptr() as *const i8, by.len())); }
278
279        use MjuiDefType::*;
280        let (type_, other) = match &self.type_ {
281            Check { modifier } => {
282                (mjtItem::mjITEM_CHECKBYTE, modifier)
283            },
284
285            Radio {values } => {
286                (mjtItem::mjITEM_RADIO, values)
287            },
288
289            Select { values } => {
290                (mjtItem::mjITEM_SELECT, values)
291            },
292
293            Edit { range, n_dimensions, type_ } => {
294                let (t, r) = match type_ {
295                    MjuiDefDataType::String => (MjtItem::mjITEM_EDITTXT, (0.0, 0.0)),
296                    MjuiDefDataType::Integer => (MjtItem::mjITEM_EDITINT, *range),
297                    MjuiDefDataType::Double => (MjtItem::mjITEM_EDITNUM, *range),
298                };
299                (t, &format!("{n_dimensions} {} {}", r.0, r.1))
300            },
301
302            Button { modifier } => {
303                (mjtItem::mjITEM_BUTTON, modifier)
304            },
305
306            Slider { range } => {
307                (mjtItem::mjITEM_SLIDERNUM, &format!("{} {}", range.0, range.1))
308            },
309            End => {
310                (mjtItem::mjITEM_END, &"".to_string())
311            }
312        };
313
314        let other_bytes = other.as_bytes();
315        ffi.type_ = type_ as i32;
316        ffi.state = self.state.clone() as i32;
317        ffi.pdata = self.storage.as_mut_ptr() as *mut std::ffi::c_void;
318        unsafe { ffi.other[..other_bytes.len()].copy_from_slice(std::slice::from_raw_parts(other_bytes.as_ptr() as *const i8, other_bytes.len())); }
319
320        ffi
321    }
322}
323
324
325impl Default for mjuiDef{
326    fn default() -> Self {
327        unsafe { std::mem::zeroed() }
328    }
329}
330
331
332/***********************************************************************************************************************
333** MjUI
334***********************************************************************************************************************/
335impl Default for mjUI{
336    fn default() -> Self {
337        unsafe { std::mem::zeroed() }
338    }
339}
340
341pub struct MjUI {
342    ui: mjUI
343}
344
345impl MjUI {
346    pub fn new() -> Self {
347        Self{ ui: mjUI::default() }
348    }
349
350    pub fn add(&mut self, items: &mut [MjuiDef]) {
351        let last_item = items.last();
352        if let Some(x) = last_item {
353            if let MjuiDefType::End = x.type_ {}
354            else {
355                panic!("last item in the items parameter must be MjuiDefType::End");
356            }
357        }
358
359        let items: Box<[mjuiDef]> = items.iter_mut().map(|hdef| hdef.build_ffi_mut()).collect();
360        unsafe { mjui_add(self.ffi_mut(), items.as_ptr()); }
361    }
362
363    pub fn add_to_section(&mut self, section_id: u32, items: &mut [MjuiDef]) {
364        let items: Box<[mjuiDef]> = items.iter_mut().map(|hdef| hdef.build_ffi_mut()).collect();
365        unsafe { mjui_addToSection(self.ffi_mut(), section_id as i32, items.as_ptr()); }
366    }
367
368    pub fn resize(&mut self, context: &MjrContext) {
369        unsafe { mjui_resize(self.ffi_mut(), context.ffi()); }
370    }
371
372    /// Main update method. Must be called when the UI state changes or the user data changes.
373    pub fn update(&self, section: Option<i32>, item: Option<i32>, state: &MjuiState, context: &MjrContext) {
374        unsafe {
375            mjui_update(
376                section.unwrap_or(-1), item.unwrap_or(-1),
377                self.ffi(),
378                state, context.ffi()
379            );
380        }
381    }
382
383    // The event processing method. Creates a reference to mjuiItem.
384    pub fn event(&mut self, state: &mut MjuiState, context: &MjrContext) -> &mut mjuiItem {
385        let ffi_item = unsafe { mjui_event(self.ffi_mut(), state, context.ffi()) };
386        unsafe {  ffi_item.as_mut().unwrap() }
387    }
388
389    pub fn render(&mut self, state: &MjuiState, context: &MjrContext) {
390        unsafe { mjui_render(self.ffi_mut(), state, context.ffi()); }
391    }
392
393    pub fn ffi(&self) -> &mjUI {
394        &self.ui
395    }
396
397    pub unsafe fn ffi_mut(&mut self) -> &mut mjUI {
398        &mut self.ui
399    }
400}