robot_description_builder/
material.rs1mod descriptor;
9pub(crate) mod stage;
10
11pub mod data;
12pub use descriptor::MaterialDescriptor;
13
14use std::sync::{Arc, RwLock};
15
16#[cfg(feature = "urdf")]
17use crate::to_rdf::to_urdf::{ToURDF, URDFMaterialMode};
18#[cfg(feature = "xml")]
19use quick_xml::events::attributes::Attribute;
20
21#[cfg(feature = "wrapper")]
22use crate::utils::ArcLock;
23
24use crate::{
25 cluster_objects::{
26 kinematic_data_errors::AddMaterialError, kinematic_data_tree::KinematicDataTree,
27 },
28 identifiers::GroupID,
29 utils::{errored_read_lock, ArcRW},
30};
31
32use data::{MaterialData, MaterialDataReference};
33use stage::MaterialStage;
34
35#[derive(Debug, PartialEq, Clone)]
45pub struct Material(MaterialKind);
46
47impl Material {
48 pub(crate) fn new_unnamed(data: MaterialData) -> Self {
50 Self(MaterialKind::Unnamed(data))
51 }
52
53 pub(crate) fn new_named_uninited(name: impl Into<String>, data: MaterialData) -> Self {
55 Self(MaterialKind::Named {
56 name: name.into(),
57 data: MaterialStage::PreInit(data),
58 })
59 }
60
61 pub(crate) fn new_named_inited(
63 name: impl Into<String>,
64 data: Arc<RwLock<MaterialData>>,
65 ) -> Self {
66 Self(MaterialKind::Named {
67 name: name.into(),
68 data: MaterialStage::Initialized(data),
69 })
70 }
71
72 pub(crate) fn initialize(&mut self, tree: &KinematicDataTree) -> Result<(), AddMaterialError> {
75 match &mut self.0 {
76 MaterialKind::Unnamed(_) => Ok(()),
78 MaterialKind::Named { name, data } => {
79 let material_data = match data {
80 MaterialStage::PreInit(data) => {
81 let material_data_index = Arc::clone(&tree.material_index);
82
83 let other_material = material_data_index.mread()?.get(name).map(Arc::clone);
85
86 match other_material {
87 Some(other_material) => {
88 if *other_material
89 .read()
90 .map_err(|_| errored_read_lock(&other_material))?
93 == *data
94 {
95 other_material
96 } else {
97 return Err(AddMaterialError::Conflict(name.clone()));
98 }
99 }
100 None => {
101 let material_data = Arc::new(RwLock::new(data.clone()));
102 assert!(material_data_index
103 .mwrite()?
104 .insert(name.clone(), Arc::clone(&material_data))
105 .is_none());
106 material_data
107 }
108 }
109 }
110 MaterialStage::Initialized(data) => Arc::clone(data),
111 };
112 data.initialize(material_data);
113 Ok(())
114 }
115 }
116 }
117
118 pub fn name(&self) -> Option<&String> {
122 match &self.0 {
123 MaterialKind::Named { name, data: _ } => Some(name),
124 MaterialKind::Unnamed(_) => None,
125 }
126 }
127
128 pub fn material_data(&self) -> MaterialDataReference {
131 match &self.0 {
132 MaterialKind::Named { name: _, data } => data.data(),
133 MaterialKind::Unnamed(data) => data.into(),
134 }
135 }
136
137 pub fn describe(&self) -> MaterialDescriptor {
141 let descriptor = MaterialDescriptor::new_data(self.material_data().try_into().unwrap()); match &self.0 {
143 MaterialKind::Named { name, data: _ } => descriptor.named(name),
144 MaterialKind::Unnamed(_) => descriptor,
145 }
146 }
147}
148
149#[cfg(feature = "urdf")]
150impl ToURDF for Material {
151 fn to_urdf(
152 &self,
153 writer: &mut quick_xml::Writer<std::io::Cursor<Vec<u8>>>,
154 urdf_config: &crate::to_rdf::to_urdf::URDFConfig,
155 ) -> Result<(), quick_xml::Error> {
156 let mut element = writer.create_element("material");
157
158 match &self.0 {
159 MaterialKind::Named { name, data } => {
160 element = element.with_attribute(Attribute {
161 key: quick_xml::name::QName(b"name"),
162 value: name.display().as_bytes().into(),
163 });
164 match (urdf_config.direct_material_ref, data.used_count()) {
165 (URDFMaterialMode::Referenced, 2..) => element.write_empty()?,
166 (URDFMaterialMode::FullMaterial, _) | (URDFMaterialMode::Referenced, _) => {
167 element.write_inner_content(|writer| data.to_urdf(writer, urdf_config))?
168 }
169 }
170 }
171 MaterialKind::Unnamed(data) => {
172 element.write_inner_content(|writer| data.to_urdf(writer, urdf_config))?
173 }
174 };
175 Ok(())
176 }
177}
178
179#[cfg(feature = "wrapper")]
180impl From<(String, ArcLock<MaterialData>)> for Material {
181 fn from(value: (String, ArcLock<MaterialData>)) -> Self {
182 let name = value.0;
183 let data = value.1;
184
185 Self::new_named_inited(name, data)
186 }
187}
188
189#[derive(Debug, PartialEq)]
191enum MaterialKind {
192 Named { name: String, data: MaterialStage },
197 Unnamed(MaterialData),
201}
202
203impl From<MaterialKind> for Material {
204 fn from(value: MaterialKind) -> Self {
205 Self(value)
206 }
207}
208
209impl Clone for MaterialKind {
210 fn clone(&self) -> Self {
211 match self {
212 Self::Named { name, data } => Self::Named {
213 name: name.clone(),
214 data: data.clone(),
215 },
216 Self::Unnamed(arg0) => Self::Unnamed(arg0.clone()),
217 }
218 }
219}
220
221#[cfg(test)]
222mod tests {
223 use crate::material::MaterialDescriptor;
225 use test_log::test;
226
227 #[cfg(feature = "urdf")]
233 mod to_urdf {
234 use super::{test, MaterialDescriptor};
235 use crate::{
236 link::builder::{LinkBuilder, VisualBuilder},
237 link_data::geometry::BoxGeometry,
238 to_rdf::to_urdf::{ToURDF, URDFConfig, URDFMaterialMode},
239 KinematicInterface,
240 };
241 use std::io::Seek;
242
243 fn test_to_urdf_material(
244 material_builder: MaterialDescriptor,
245 result: String,
246 urdf_config: &URDFConfig,
247 ) {
248 let mut writer = quick_xml::Writer::new(std::io::Cursor::new(Vec::new()));
249 assert!(material_builder
250 .build()
251 .to_urdf(&mut writer, urdf_config)
252 .is_ok());
253
254 writer.get_mut().rewind().unwrap();
255
256 assert_eq!(
257 std::io::read_to_string(writer.into_inner()).unwrap(),
258 result
259 )
260 }
261
262 #[test]
263 fn color_no_name_full() {
264 test_to_urdf_material(
265 MaterialDescriptor::new_color(0.2, 0.4, 0.6, 0.8),
266 String::from(r#"<material><color rgba="0.2 0.4 0.6 0.8"/></material>"#),
267 &URDFConfig::default(),
268 );
269 }
270
271 #[test]
272 fn color_name_full() {
273 test_to_urdf_material(
274 MaterialDescriptor::new_color(0.2, 0.4, 0.6, 0.8).named("test_material"),
275 String::from(
276 r#"<material name="test_material"><color rgba="0.2 0.4 0.6 0.8"/></material>"#,
277 ),
278 &URDFConfig::default(),
279 );
280 }
281
282 #[test]
283 fn color_name_ref() {
284 let tree = LinkBuilder::new("link")
285 .add_visual(VisualBuilder::new_full(
286 None,
287 None,
288 BoxGeometry::new(1., 1., 1.),
289 MaterialDescriptor::new_color(0.2, 0.4, 0.6, 0.8)
290 .named("test_material")
291 .into(),
292 ))
293 .build_tree();
294
295 let mut writer = quick_xml::Writer::new(std::io::Cursor::new(Vec::new()));
296 assert!(tree
297 .get_material("test_material")
298 .unwrap()
299 .to_urdf(
300 &mut writer,
301 &URDFConfig {
302 direct_material_ref: URDFMaterialMode::Referenced,
303 ..Default::default()
304 }
305 )
306 .is_ok());
307
308 writer.get_mut().rewind().unwrap();
309
310 assert_eq!(
311 std::io::read_to_string(writer.into_inner()).unwrap(),
312 String::from(r#"<material name="test_material"/>"#)
313 )
314 }
315
316 #[test]
317 fn texture_no_name_full() {
318 test_to_urdf_material(
319 MaterialDescriptor::new_texture("package://robot_description/..."),
320 String::from(
321 r#"<material><texture filename="package://robot_description/..."/></material>"#,
322 ),
323 &URDFConfig::default(),
324 );
325 }
326
327 #[test]
328 fn texture_name_full() {
329 test_to_urdf_material(
330 MaterialDescriptor::new_texture("package://robot_description/...")
331 .named("texture_material"),
332 String::from(
333 r#"<material name="texture_material"><texture filename="package://robot_description/..."/></material>"#,
334 ),
335 &URDFConfig::default(),
336 );
337 }
338
339 #[test]
340 fn texture_name_ref() {
341 let tree = LinkBuilder::new("link")
342 .add_visual(VisualBuilder::new_full(
343 None,
344 None,
345 BoxGeometry::new(1., 1., 1.),
346 MaterialDescriptor::new_texture("package://robot_description/...")
347 .named("texture_material")
348 .into(),
349 ))
350 .build_tree();
351
352 let mut writer = quick_xml::Writer::new(std::io::Cursor::new(Vec::new()));
353 assert!(tree
354 .get_material("texture_material")
355 .unwrap()
356 .to_urdf(
357 &mut writer,
358 &URDFConfig {
359 direct_material_ref: URDFMaterialMode::Referenced,
360 ..Default::default()
361 }
362 )
363 .is_ok());
364
365 writer.get_mut().rewind().unwrap();
366
367 assert_eq!(
368 std::io::read_to_string(writer.into_inner()).unwrap(),
369 String::from(r#"<material name="texture_material"/>"#)
370 )
371 }
372 }
373}