robot_description_builder/link/geometry/
sphere_geometry.rs1use 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#[derive(Debug, PartialEq, Clone)]
15pub struct SphereGeometry {
16 pub radius: f32,
18}
19
20impl SphereGeometry {
21 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}