1use crate::select::is_a;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
19#[non_exhaustive]
20pub enum BodyKind {
21 Extrusion,
23 TaperedExtrusion,
25 Revolution,
27 TaperedRevolution,
29 DirectrixSweep,
32 SweptDisk,
35 SectionedSpine,
38 Brep,
40 Csg,
43 CsgPrimitive,
46 HalfSpace,
48 BoundingBox,
50 Tessellated,
52 SurfaceModel,
55 Face,
58 GeometricSet,
60 Curve,
62 Surface,
64 Point,
68}
69
70const FAMILIES: &[(&str, BodyKind)] = &[
73 ("IFCEXTRUDEDAREASOLID", BodyKind::Extrusion),
74 ("IFCEXTRUDEDAREASOLIDTAPERED", BodyKind::TaperedExtrusion),
75 ("IFCREVOLVEDAREASOLID", BodyKind::Revolution),
76 ("IFCREVOLVEDAREASOLIDTAPERED", BodyKind::TaperedRevolution),
77 ("IFCFIXEDREFERENCESWEPTAREASOLID", BodyKind::DirectrixSweep),
78 (
80 "IFCDIRECTRIXDERIVEDREFERENCESWEPTAREASOLID",
81 BodyKind::DirectrixSweep,
82 ),
83 ("IFCSURFACECURVESWEPTAREASOLID", BodyKind::DirectrixSweep),
84 ("IFCSWEPTDISKSOLID", BodyKind::SweptDisk),
85 ("IFCSWEPTDISKSOLIDPOLYGONAL", BodyKind::SweptDisk),
86 ("IFCSECTIONEDSPINE", BodyKind::SectionedSpine),
87 ("IFCSECTIONEDSOLIDHORIZONTAL", BodyKind::SectionedSpine),
89 ("IFCFACETEDBREP", BodyKind::Brep),
90 ("IFCFACETEDBREPWITHVOIDS", BodyKind::Brep),
91 ("IFCADVANCEDBREP", BodyKind::Brep),
92 ("IFCADVANCEDBREPWITHVOIDS", BodyKind::Brep),
93 ("IFCCSGSOLID", BodyKind::Csg),
94 ("IFCBOOLEANRESULT", BodyKind::Csg),
95 ("IFCBOOLEANCLIPPINGRESULT", BodyKind::Csg),
96 ("IFCBLOCK", BodyKind::CsgPrimitive),
97 ("IFCSPHERE", BodyKind::CsgPrimitive),
98 ("IFCRIGHTCIRCULARCYLINDER", BodyKind::CsgPrimitive),
99 ("IFCRIGHTCIRCULARCONE", BodyKind::CsgPrimitive),
100 ("IFCRECTANGULARPYRAMID", BodyKind::CsgPrimitive),
101 ("IFCHALFSPACESOLID", BodyKind::HalfSpace),
102 ("IFCBOXEDHALFSPACE", BodyKind::HalfSpace),
103 ("IFCPOLYGONALBOUNDEDHALFSPACE", BodyKind::HalfSpace),
104 ("IFCBOUNDINGBOX", BodyKind::BoundingBox),
105 ("IFCTRIANGULATEDFACESET", BodyKind::Tessellated),
106 ("IFCTRIANGULATEDIRREGULARNETWORK", BodyKind::Tessellated),
108 ("IFCPOLYGONALFACESET", BodyKind::Tessellated),
109 ("IFCSHELLBASEDSURFACEMODEL", BodyKind::SurfaceModel),
110 ("IFCFACEBASEDSURFACEMODEL", BodyKind::SurfaceModel),
111 ("IFCFACESURFACE", BodyKind::Face),
112 ("IFCADVANCEDFACE", BodyKind::Face),
113 ("IFCGEOMETRICSET", BodyKind::GeometricSet),
114 ("IFCGEOMETRICCURVESET", BodyKind::GeometricSet),
115 ("IFCPOINTONCURVE", BodyKind::Point),
116 ("IFCPOINTONSURFACE", BodyKind::Point),
117 ("IFCPOINTBYDISTANCEEXPRESSION", BodyKind::Point),
119 ("IFCAXIS2PLACEMENTLINEAR", BodyKind::Point),
120 ("IFCCURVESEGMENT", BodyKind::Curve),
123];
124
125impl BodyKind {
126 pub fn classify(type_name: &str) -> Option<Self> {
131 let upper = type_name.to_ascii_uppercase();
132 if is_a(&upper, "IFCCURVE") {
135 return Some(Self::Curve);
136 }
137 if is_a(&upper, "IFCSURFACE") {
138 return Some(Self::Surface);
139 }
140 FAMILIES
141 .iter()
142 .find(|(name, _)| *name == upper)
143 .map(|(_, kind)| *kind)
144 }
145
146 pub fn is_swept_area(self) -> bool {
149 matches!(
150 self,
151 Self::Extrusion
152 | Self::TaperedExtrusion
153 | Self::Revolution
154 | Self::TaperedRevolution
155 | Self::DirectrixSweep
156 )
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use super::*;
163 use crate::solid::SolidKind;
164
165 #[test]
167 fn body_kinds_refine_solid_kinds() {
168 for (name, kind) in FAMILIES {
169 let Some(solid) = SolidKind::classify(name) else {
170 continue;
171 };
172 let expected = match kind {
173 BodyKind::Extrusion
174 | BodyKind::TaperedExtrusion
175 | BodyKind::Revolution
176 | BodyKind::TaperedRevolution
177 | BodyKind::DirectrixSweep
178 | BodyKind::SweptDisk => SolidKind::Swept,
179 BodyKind::SectionedSpine => SolidKind::SectionedSpine,
180 BodyKind::Brep => SolidKind::Brep,
181 BodyKind::Csg if *name == "IFCCSGSOLID" => SolidKind::Csg,
182 BodyKind::Csg => SolidKind::Boolean,
183 BodyKind::CsgPrimitive => SolidKind::Csg,
184 BodyKind::HalfSpace => SolidKind::HalfSpace,
185 BodyKind::BoundingBox => SolidKind::BoundingBox,
186 BodyKind::Tessellated => SolidKind::Tessellated,
187 BodyKind::SurfaceModel | BodyKind::GeometricSet => SolidKind::SurfaceModel,
188 other => panic!("{name} classified {other:?} is not a solid family"),
189 };
190 assert_eq!(solid, expected, "{name}");
191 }
192 }
193
194 #[test]
195 fn curves_and_surfaces_classify_by_inheritance() {
196 assert_eq!(BodyKind::classify("IfcTrimmedCurve"), Some(BodyKind::Curve));
197 assert_eq!(BodyKind::classify("IFCPLANE"), Some(BodyKind::Surface));
198 assert_eq!(BodyKind::classify("IfcFaceSurface"), Some(BodyKind::Face));
200 assert_eq!(BodyKind::classify("IFCADVANCEDFACE"), Some(BodyKind::Face));
201 assert_eq!(BodyKind::classify("IFCMAPPEDITEM"), None);
202 assert_eq!(BodyKind::classify("IFCWALL"), None);
203 }
204
205 #[test]
208 fn ifc4x3_curves_and_surfaces_classify_by_inheritance() {
209 for curve in [
210 "IFCCLOTHOID",
211 "IFCCOSINESPIRAL",
212 "IFCSINESPIRAL",
213 "IFCSECONDORDERPOLYNOMIALSPIRAL",
214 "IFCTHIRDORDERPOLYNOMIALSPIRAL",
215 "IFCSEVENTHORDERPOLYNOMIALSPIRAL",
216 "IFCPOLYNOMIALCURVE",
217 "IFCOFFSETCURVEBYDISTANCES",
218 "IFCSEGMENTEDREFERENCECURVE",
219 "IFCGRADIENTCURVE",
220 ] {
221 assert_eq!(BodyKind::classify(curve), Some(BodyKind::Curve), "{curve}");
222 }
223 assert_eq!(
224 BodyKind::classify("IFCSECTIONEDSURFACE"),
225 Some(BodyKind::Surface)
226 );
227 assert_eq!(BodyKind::classify("IFCCURVESEGMENT"), Some(BodyKind::Curve));
230 assert!(!crate::select::is_a("IFCCURVESEGMENT", "IFCCURVE"));
231 }
232}