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