1use super::{GeometryInterface, GeometryShapeContainer};
2use crate::{identifiers::GroupID, transform::Mirror};
3use itertools::Itertools;
4use nalgebra::{vector, Matrix3};
5
6#[cfg(feature = "urdf")]
7use crate::to_rdf::to_urdf::ToURDF;
8#[cfg(feature = "xml")]
9use quick_xml::{events::attributes::Attribute, name::QName};
10
11#[derive(Debug, PartialEq, Clone)]
17pub struct MeshGeometry {
18 pub path: String,
22 pub bounding_box: (f32, f32, f32),
26 pub scale: (f32, f32, f32),
31}
32
33impl MeshGeometry {
34 pub fn new(
41 path: impl Into<String>,
42 bounding_box: (f32, f32, f32),
43 scale: Option<(f32, f32, f32)>,
44 ) -> Self {
45 Self {
46 path: path.into(),
47 bounding_box,
48 scale: scale.unwrap_or((1., 1., 1.)),
49 }
50 }
51}
52
53impl GeometryInterface for MeshGeometry {
54 fn volume(&self) -> f32 {
56 self.bounding_box.0 * self.bounding_box.1 * self.bounding_box.2
57 }
58
59 fn surface_area(&self) -> f32 {
61 2. * (self.bounding_box.0 * self.bounding_box.1
62 + self.bounding_box.1 * self.bounding_box.2
63 + self.bounding_box.0 * self.bounding_box.2)
64 }
65
66 fn boxed_clone(&self) -> Box<dyn GeometryInterface + Sync + Send> {
67 Box::new(self.clone())
68 }
69
70 fn bounding_box(&self) -> (f32, f32, f32) {
71 self.bounding_box
72 }
73
74 fn shape_container(&self) -> GeometryShapeContainer {
75 self.clone().into()
76 }
77}
78
79impl Mirror for MeshGeometry {
81 fn mirrored(&self, mirror_matrix: &Matrix3<f32>) -> Self {
82 Self {
87 path: self.path.clone(), bounding_box: self.bounding_box,
89 scale: (mirror_matrix * vector![self.scale.0, self.scale.1, self.scale.2])
90 .iter()
91 .copied()
92 .collect_tuple()
93 .unwrap(),
94 }
95 }
96}
97
98#[cfg(feature = "urdf")]
99impl ToURDF for MeshGeometry {
100 fn to_urdf(
101 &self,
102 writer: &mut quick_xml::Writer<std::io::Cursor<Vec<u8>>>,
103 _urdf_config: &crate::to_rdf::to_urdf::URDFConfig,
104 ) -> Result<(), quick_xml::Error> {
105 let element = writer.create_element("geometry");
106 element.write_inner_content(|writer| -> quick_xml::Result<()> {
107 writer
108 .create_element("mesh")
109 .with_attribute(Attribute {
110 key: QName(b"filename"),
111 value: self.path.display().as_bytes().into(),
113 })
114 .with_attribute(Attribute {
115 key: QName(b"scale"),
116 value: format!("{} {} {}", self.scale.0, self.scale.1, self.scale.2)
117 .as_bytes()
118 .into(),
119 })
120 .write_empty()?;
121 Ok(())
122 })?;
123 Ok(())
124 }
125}
126
127impl From<MeshGeometry> for Box<dyn GeometryInterface + Sync + Send> {
128 fn from(value: MeshGeometry) -> Self {
129 Box::new(value)
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 #[cfg(feature = "xml")]
136 use std::io::Seek;
137 use test_log::test;
138
139 use super::{GeometryInterface, GeometryShapeContainer, MeshGeometry};
140 #[cfg(feature = "urdf")]
141 use crate::to_rdf::to_urdf::{ToURDF, URDFConfig};
142
143 #[test]
144 fn volume() {
145 assert_eq!(
146 MeshGeometry::new(
147 "package://my-package/description/meshes/mesh_[[L]].dae",
148 (1., 5., 1.),
149 None
150 )
151 .volume(),
152 5.
153 );
154 assert_eq!(
155 MeshGeometry::new(
156 "package://my-other-package/description/meshes/symmetrical-mesh.dae",
157 (3., 3., 3.),
158 None
159 )
160 .volume(),
161 27.
162 );
163 assert_eq!(
164 MeshGeometry::new(
165 "package://a-package/description/meshes_[[L]]/arm.dae",
166 (0.5, 0.5, 4.),
167 Some((0.9, 0.9, 0.9))
168 )
169 .volume(),
170 1.
171 );
172 assert_eq!(
173 MeshGeometry::new(
174 "package://a-package/description/meshes/somethingweird.dae",
175 (40.5, 90.5, 4.),
176 Some((1., -1., 1.))
177 )
178 .volume(),
179 14661.
180 );
181 }
182
183 #[test]
184 fn surface_area() {
185 assert_eq!(
186 MeshGeometry::new(
187 "package://my-package/description/meshes/mesh_[[L]].dae",
188 (1., 5., 1.),
189 None
190 )
191 .surface_area(),
192 22.
193 );
194 assert_eq!(
195 MeshGeometry::new(
196 "package://my-other-package/description/meshes/symmetrical-mesh.dae",
197 (3., 3., 3.),
198 None
199 )
200 .surface_area(),
201 54.
202 );
203 assert_eq!(
204 MeshGeometry::new(
205 "package://a-package/description/meshes_[[L]]/arm.dae",
206 (0.5, 0.5, 4.),
207 Some((0.9, 0.9, 0.9))
208 )
209 .surface_area(),
210 8.5
211 );
212 assert_eq!(
213 MeshGeometry::new(
214 "package://a-package/description/meshes/somethingweird.dae",
215 (40.5, 90.5, 4.),
216 Some((1., -1., 1.))
217 )
218 .surface_area(),
219 8378.5
220 );
221 }
222
223 #[test]
224 fn boxed_clone() {
225 assert_eq!(
226 MeshGeometry::new(
227 "package://my-package/description/meshes/mesh_[[L]].dae",
228 (1., 5., 1.),
229 None
230 )
231 .boxed_clone(),
232 MeshGeometry::new(
233 "package://my-package/description/meshes/mesh_[[L]].dae",
234 (1., 5., 1.),
235 None
236 )
237 .into()
238 );
239 assert_eq!(
240 MeshGeometry::new(
241 "package://my-other-package/description/meshes/symmetrical-mesh.dae",
242 (3., 3., 3.),
243 None
244 )
245 .boxed_clone(),
246 MeshGeometry::new(
247 "package://my-other-package/description/meshes/symmetrical-mesh.dae",
248 (3., 3., 3.),
249 None
250 )
251 .into()
252 );
253 assert_eq!(
254 MeshGeometry::new(
255 "package://a-package/description/meshes_[[L]]/arm.dae",
256 (0.5, 0.5, 4.),
257 Some((0.9, 0.9, 0.9))
258 )
259 .boxed_clone(),
260 MeshGeometry::new(
261 "package://a-package/description/meshes_[[L]]/arm.dae",
262 (0.5, 0.5, 4.),
263 Some((0.9, 0.9, 0.9))
264 )
265 .into()
266 );
267 assert_eq!(
268 MeshGeometry::new(
269 "package://a-package/description/meshes/somethingweird.dae",
270 (40.5, 90.5, 4.),
271 Some((1., -1., 1.))
272 )
273 .boxed_clone(),
274 MeshGeometry::new(
275 "package://a-package/description/meshes/somethingweird.dae",
276 (40.5, 90.5, 4.),
277 Some((1., -1., 1.))
278 )
279 .into()
280 );
281 }
282
283 #[test]
284 fn bounding_box() {
285 assert_eq!(
286 MeshGeometry::new(
287 "package://my-package/description/meshes/mesh_[[L]].dae",
288 (1., 5., 1.),
289 None
290 )
291 .bounding_box(),
292 (1., 5., 1.)
293 );
294 assert_eq!(
295 MeshGeometry::new(
296 "package://my-other-package/description/meshes/symmetrical-mesh.dae",
297 (3., 3., 3.),
298 None
299 )
300 .bounding_box(),
301 (3., 3., 3.)
302 );
303 assert_eq!(
304 MeshGeometry::new(
305 "package://a-package/description/meshes_[[L]]/arm.dae",
306 (0.5, 0.5, 4.),
307 Some((0.9, 0.9, 0.9))
308 )
309 .bounding_box(),
310 (0.5, 0.5, 4.)
311 );
312 assert_eq!(
313 MeshGeometry::new(
314 "package://a-package/description/meshes/somethingweird.dae",
315 (40.5, 90.5, 4.),
316 Some((1., -1., 1.))
317 )
318 .bounding_box(),
319 (40.5, 90.5, 4.)
320 );
321 }
322
323 #[test]
324 fn get_shape() {
325 assert_eq!(
326 MeshGeometry::new(
327 "package://my-package/description/meshes/mesh_[[L]].dae",
328 (1., 5., 1.),
329 None
330 )
331 .shape_container(),
332 GeometryShapeContainer::Mesh(MeshGeometry::new(
333 "package://my-package/description/meshes/mesh_[[L]].dae",
334 (1., 5., 1.),
335 None
336 ))
337 );
338 assert_eq!(
339 MeshGeometry::new(
340 "package://my-other-package/description/meshes/symmetrical-mesh.dae",
341 (3., 3., 3.),
342 None
343 )
344 .shape_container(),
345 GeometryShapeContainer::Mesh(MeshGeometry::new(
346 "package://my-other-package/description/meshes/symmetrical-mesh.dae",
347 (3., 3., 3.),
348 None
349 ))
350 );
351 assert_eq!(
352 MeshGeometry::new(
353 "package://a-package/description/meshes_[[L]]/arm.dae",
354 (0.5, 0.5, 4.),
355 Some((0.9, 0.9, 0.9))
356 )
357 .shape_container(),
358 GeometryShapeContainer::Mesh(MeshGeometry::new(
359 "package://a-package/description/meshes_[[L]]/arm.dae",
360 (0.5, 0.5, 4.),
361 Some((0.9, 0.9, 0.9))
362 ))
363 );
364 assert_eq!(
365 MeshGeometry::new(
366 "package://a-package/description/meshes/somethingweird.dae",
367 (40.5, 90.5, 4.),
368 Some((1., -1., 1.))
369 )
370 .shape_container(),
371 GeometryShapeContainer::Mesh(MeshGeometry::new(
372 "package://a-package/description/meshes/somethingweird.dae",
373 (40.5, 90.5, 4.),
374 Some((1., -1., 1.))
375 ))
376 );
377 }
378
379 #[cfg(feature = "urdf")]
380 #[test]
381 fn to_urdf() {
382 {
383 let mut writer = quick_xml::Writer::new(std::io::Cursor::new(Vec::new()));
384 assert!(MeshGeometry::new(
385 "package://my-package/description/meshes/mesh_[[L]].dae",
386 (1., 5., 1.),
387 None
388 )
389 .to_urdf(&mut writer, &URDFConfig::default())
390 .is_ok());
391
392 writer.get_mut().rewind().unwrap();
393
394 assert_eq!(
395 std::io::read_to_string(writer.into_inner()).unwrap(),
396 String::from(
397 r#"<geometry><mesh filename="package://my-package/description/meshes/mesh_L.dae" scale="1 1 1"/></geometry>"#
398 )
399 );
400 }
401 {
402 let mut writer = quick_xml::Writer::new(std::io::Cursor::new(Vec::new()));
403 assert!(MeshGeometry::new(
404 "package://my-other-package/description/meshes/symmetrical-mesh.dae",
405 (3., 3., 3.),
406 None
407 )
408 .to_urdf(&mut writer, &URDFConfig::default())
409 .is_ok());
410
411 writer.get_mut().rewind().unwrap();
412
413 assert_eq!(
414 std::io::read_to_string(writer.into_inner()).unwrap(),
415 String::from(
416 r#"<geometry><mesh filename="package://my-other-package/description/meshes/symmetrical-mesh.dae" scale="1 1 1"/></geometry>"#
417 )
418 );
419 }
420 {
421 let mut writer = quick_xml::Writer::new(std::io::Cursor::new(Vec::new()));
422 assert!(MeshGeometry::new(
423 "package://a-package/description/meshes_[[L]]/arm.dae",
424 (0.5, 0.5, 4.),
425 Some((0.9, 0.9, 0.9))
426 )
427 .to_urdf(&mut writer, &URDFConfig::default())
428 .is_ok());
429
430 writer.get_mut().rewind().unwrap();
431
432 assert_eq!(
433 std::io::read_to_string(writer.into_inner()).unwrap(),
434 String::from(
435 r#"<geometry><mesh filename="package://a-package/description/meshes_L/arm.dae" scale="0.9 0.9 0.9"/></geometry>"#
436 )
437 );
438 }
439 {
440 let mut writer = quick_xml::Writer::new(std::io::Cursor::new(Vec::new()));
441 assert!(MeshGeometry::new(
442 "package://a-package/description/meshes/somethingweird.dae",
443 (40.5, 90.5, 4.),
444 Some((1., -1., 1.))
445 )
446 .to_urdf(&mut writer, &URDFConfig::default())
447 .is_ok());
448
449 writer.get_mut().rewind().unwrap();
450
451 assert_eq!(
452 std::io::read_to_string(writer.into_inner()).unwrap(),
453 String::from(
454 r#"<geometry><mesh filename="package://a-package/description/meshes/somethingweird.dae" scale="1 -1 1"/></geometry>"#
455 )
456 );
457 }
458 }
459}