robot_description_builder/link/geometry/
sphere_geometry.rs

1use super::{GeometryInterface, GeometryShapeContainer};
2use crate::transform::Mirror;
3use std::f32::consts::{FRAC_PI_3, PI};
4
5#[cfg(feature = "urdf")]
6use crate::to_rdf::to_urdf::ToURDF;
7#[cfg(feature = "xml")]
8use quick_xml::{events::attributes::Attribute, name::QName};
9
10/// A Represenation for a Sphere Geometry.
11///
12/// This Sphere is centered at the origin. (URDF)
13// The fields are public for the Python wrapper. It doesn't change much for the Rust side, since most of the time these will be `Box<dyn GeometryInterface + Sync + Send>`.
14#[derive(Debug, PartialEq, Clone)]
15pub struct SphereGeometry {
16	/// The radius of the Sphere.
17	pub radius: f32,
18}
19
20impl SphereGeometry {
21	/// Creates a new `SphereGeometry` with the specified `radius`.
22	pub fn new(radius: f32) -> Self {
23		Self { radius }
24	}
25}
26
27impl GeometryInterface for SphereGeometry {
28	fn volume(&self) -> f32 {
29		4f32 * FRAC_PI_3 * self.radius * self.radius * self.radius
30	}
31
32	fn surface_area(&self) -> f32 {
33		4f32 * PI * self.radius * self.radius
34	}
35
36	fn boxed_clone(&self) -> Box<dyn GeometryInterface + Sync + Send> {
37		Box::new(self.clone())
38	}
39
40	fn bounding_box(&self) -> (f32, f32, f32) {
41		let diameter = 2. * self.radius;
42		(diameter, diameter, diameter)
43	}
44
45	fn shape_container(&self) -> GeometryShapeContainer {
46		self.clone().into()
47	}
48}
49
50impl Mirror for SphereGeometry {
51	fn mirrored(&self, _mirror_matrix: &nalgebra::Matrix3<f32>) -> Self {
52		self.clone()
53	}
54}
55
56#[cfg(feature = "urdf")]
57impl ToURDF for SphereGeometry {
58	fn to_urdf(
59		&self,
60		writer: &mut quick_xml::Writer<std::io::Cursor<Vec<u8>>>,
61		_urdf_config: &crate::to_rdf::to_urdf::URDFConfig,
62	) -> Result<(), quick_xml::Error> {
63		let element = writer.create_element("geometry");
64		element.write_inner_content(|writer| -> quick_xml::Result<()> {
65			writer
66				.create_element("sphere")
67				.with_attribute(Attribute {
68					key: QName(b"radius"),
69					value: self.radius.to_string().as_bytes().into(),
70				})
71				.write_empty()?;
72			Ok(())
73		})?;
74		Ok(())
75	}
76}
77
78impl From<SphereGeometry> for Box<dyn GeometryInterface + Sync + Send> {
79	fn from(value: SphereGeometry) -> Self {
80		Box::new(value)
81	}
82}
83
84#[cfg(test)]
85mod tests {
86	use std::f32::consts::{FRAC_PI_3, PI};
87	#[cfg(feature = "xml")]
88	use std::io::Seek;
89	use test_log::test;
90
91	use crate::link::geometry::{
92		geometry_shape_data::GeometryShapeContainer, sphere_geometry::SphereGeometry,
93		GeometryInterface,
94	};
95	#[cfg(feature = "urdf")]
96	use crate::to_rdf::to_urdf::{ToURDF, URDFConfig};
97
98	#[test]
99	fn volume() {
100		assert_eq!(SphereGeometry::new(1.0).volume(), FRAC_PI_3 * 4.);
101		assert_eq!(SphereGeometry::new(2.0).volume(), FRAC_PI_3 * 32.);
102		assert_eq!(
103			SphereGeometry::new(9.0).volume(),
104			4. * FRAC_PI_3 * 9. * 9. * 9.
105		);
106		assert_eq!(
107			SphereGeometry::new(75.35).volume(),
108			(std::f64::consts::FRAC_PI_3 * 1711235.4215) as f32
109		);
110	}
111
112	#[test]
113	fn surface_area() {
114		assert_eq!(SphereGeometry::new(1.0).surface_area(), PI * 4.);
115		assert_eq!(SphereGeometry::new(2.0).surface_area(), PI * 16.);
116		assert_eq!(SphereGeometry::new(9.0).surface_area(), PI * 324.);
117		assert_eq!(SphereGeometry::new(75.35).surface_area(), PI * 22710.49);
118	}
119
120	#[test]
121	fn boxed_clone() {
122		assert_eq!(
123			SphereGeometry::new(1.0).boxed_clone(),
124			SphereGeometry::new(1.0).into()
125		);
126		assert_eq!(
127			SphereGeometry::new(2.0).boxed_clone(),
128			SphereGeometry::new(2.0).into()
129		);
130		assert_eq!(
131			SphereGeometry::new(9.0).boxed_clone(),
132			SphereGeometry::new(9.0).into()
133		);
134		assert_eq!(
135			SphereGeometry::new(75.35).boxed_clone(),
136			SphereGeometry::new(75.35).into()
137		);
138	}
139
140	#[test]
141	fn bounding_box() {
142		assert_eq!(SphereGeometry::new(1.0).bounding_box(), (2., 2., 2.));
143		assert_eq!(SphereGeometry::new(2.0).bounding_box(), (4., 4., 4.));
144		assert_eq!(SphereGeometry::new(9.0).bounding_box(), (18., 18., 18.));
145		assert_eq!(
146			SphereGeometry::new(75.35).bounding_box(),
147			(150.7, 150.7, 150.7)
148		);
149	}
150
151	#[test]
152	fn get_shape() {
153		assert_eq!(
154			SphereGeometry::new(1.0).shape_container(),
155			GeometryShapeContainer::Sphere(SphereGeometry::new(1.0))
156		);
157		assert_eq!(
158			SphereGeometry::new(2.0).shape_container(),
159			GeometryShapeContainer::Sphere(SphereGeometry::new(2.0))
160		);
161		assert_eq!(
162			SphereGeometry::new(9.0).shape_container(),
163			GeometryShapeContainer::Sphere(SphereGeometry::new(9.0))
164		);
165		assert_eq!(
166			SphereGeometry::new(75.35).shape_container(),
167			GeometryShapeContainer::Sphere(SphereGeometry::new(75.35))
168		);
169	}
170
171	#[cfg(feature = "urdf")]
172	#[test]
173	fn to_urdf() {
174		{
175			let mut writer = quick_xml::Writer::new(std::io::Cursor::new(Vec::new()));
176			assert!(SphereGeometry::new(1.0)
177				.to_urdf(&mut writer, &URDFConfig::default())
178				.is_ok());
179
180			writer.get_mut().rewind().unwrap();
181
182			assert_eq!(
183				std::io::read_to_string(writer.into_inner()).unwrap(),
184				String::from(r#"<geometry><sphere radius="1"/></geometry>"#)
185			);
186		}
187		{
188			let mut writer = quick_xml::Writer::new(std::io::Cursor::new(Vec::new()));
189			assert!(SphereGeometry::new(2.0)
190				.to_urdf(&mut writer, &URDFConfig::default())
191				.is_ok());
192
193			writer.get_mut().rewind().unwrap();
194
195			assert_eq!(
196				std::io::read_to_string(writer.into_inner()).unwrap(),
197				String::from(r#"<geometry><sphere radius="2"/></geometry>"#)
198			);
199		}
200		{
201			let mut writer = quick_xml::Writer::new(std::io::Cursor::new(Vec::new()));
202			assert!(SphereGeometry::new(9.0)
203				.to_urdf(&mut writer, &URDFConfig::default())
204				.is_ok());
205
206			writer.get_mut().rewind().unwrap();
207
208			assert_eq!(
209				std::io::read_to_string(writer.into_inner()).unwrap(),
210				String::from(r#"<geometry><sphere radius="9"/></geometry>"#)
211			);
212		}
213		{
214			let mut writer = quick_xml::Writer::new(std::io::Cursor::new(Vec::new()));
215			assert!(SphereGeometry::new(75.35)
216				.to_urdf(&mut writer, &URDFConfig::default())
217				.is_ok());
218
219			writer.get_mut().rewind().unwrap();
220
221			assert_eq!(
222				std::io::read_to_string(writer.into_inner()).unwrap(),
223				String::from(r#"<geometry><sphere radius="75.35"/></geometry>"#)
224			);
225		}
226	}
227}