egml_core/model/geometry/primitives/
solid.rs1use crate::error::Error;
2use crate::impl_abstract_solid_traits;
3use crate::model::geometry::primitives::shell_property::ShellProperty;
4use crate::model::geometry::primitives::{AbstractSolid, AsAbstractSolid, AsAbstractSolidMut};
5use crate::model::geometry::{DirectPosition, Envelope};
6use nalgebra::Isometry3;
7
8#[derive(Debug, Clone, PartialEq)]
13pub struct Solid {
14 pub(crate) abstract_solid: AbstractSolid,
15 exterior: Option<ShellProperty>,
16}
17
18impl Solid {
19 pub fn new(exterior: Option<ShellProperty>) -> Result<Self, Error> {
25 Ok(Self {
26 abstract_solid: AbstractSolid::default(),
27 exterior,
28 })
29 }
30
31 pub fn exterior(&self) -> Option<&ShellProperty> {
32 self.exterior.as_ref()
33 }
34}
35
36impl Solid {
37 pub fn points(&self) -> Vec<&DirectPosition> {
38 if let Some(exterior) = &self.exterior
39 && let Some(object) = &exterior.object
40 {
41 object.points()
42 } else {
43 Vec::new()
44 }
45 }
46
47 pub fn volume_3d(&self) -> Result<f64, Error> {
56 let shell_property = self.exterior.as_ref().ok_or(Error::MissingExteriorShell)?;
57 let shell =
58 shell_property
59 .object
60 .as_ref()
61 .ok_or_else(|| Error::UnresolvedShellReference {
62 href: shell_property.href.clone(),
63 })?;
64 shell.volume_3d()
65 }
66
67 pub fn apply_transform(&mut self, m: &Isometry3<f64>) {
68 if let Some(exterior) = self.exterior.as_mut()
69 && let Some(object) = exterior.object.as_mut()
70 {
71 object.apply_transform(m)
72 }
73 }
74
75 pub fn compute_envelope(&self) -> Option<Envelope> {
77 if let Some(exterior) = &self.exterior
78 && let Some(object) = &exterior.object
79 {
80 object.compute_envelope()
81 } else {
82 None
83 }
84 }
85}
86
87impl AsAbstractSolid for Solid {
88 fn abstract_solid(&self) -> &AbstractSolid {
89 &self.abstract_solid
90 }
91}
92
93impl AsAbstractSolidMut for Solid {
94 fn abstract_solid_mut(&mut self) -> &mut AbstractSolid {
95 &mut self.abstract_solid
96 }
97}
98
99impl_abstract_solid_traits!(Solid);
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104 use crate::model::geometry::Envelope;
105 use crate::model::geometry::primitives::ShellProperty;
106
107 #[test]
108 fn volume_3d_unit_cube() {
109 let solid = Envelope::new(
110 DirectPosition::new(0.0, 0.0, 0.0).unwrap(),
111 DirectPosition::new(1.0, 1.0, 1.0).unwrap(),
112 )
113 .unwrap()
114 .to_solid()
115 .unwrap();
116 assert!((solid.volume_3d().unwrap() - 1.0).abs() < 1e-10);
117 }
118
119 #[test]
120 fn volume_3d_2x3x4_box() {
121 let solid = Envelope::new(
122 DirectPosition::new(0.0, 0.0, 0.0).unwrap(),
123 DirectPosition::new(2.0, 3.0, 4.0).unwrap(),
124 )
125 .unwrap()
126 .to_solid()
127 .unwrap();
128 assert!((solid.volume_3d().unwrap() - 24.0).abs() < 1e-10);
129 }
130
131 #[test]
132 fn volume_3d_missing_exterior_shell() {
133 let solid = Solid::new(None).unwrap();
134 assert_eq!(solid.volume_3d(), Err(Error::MissingExteriorShell));
135 }
136
137 #[test]
138 fn volume_3d_unresolved_shell_reference() {
139 let solid = Solid::new(Some(ShellProperty::new_href(
140 "urn:example:shell-1".to_string(),
141 )))
142 .unwrap();
143 assert_eq!(
144 solid.volume_3d(),
145 Err(Error::UnresolvedShellReference {
146 href: Some("urn:example:shell-1".to_string())
147 })
148 );
149 }
150}