use crate::select::is_a;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum BodyKind {
Extrusion,
TaperedExtrusion,
Revolution,
TaperedRevolution,
DirectrixSweep,
SweptDisk,
SectionedSpine,
Brep,
Csg,
CsgPrimitive,
HalfSpace,
BoundingBox,
Tessellated,
SurfaceModel,
Face,
GeometricSet,
Curve,
Surface,
Point,
}
const FAMILIES: &[(&str, BodyKind)] = &[
("IFCEXTRUDEDAREASOLID", BodyKind::Extrusion),
("IFCEXTRUDEDAREASOLIDTAPERED", BodyKind::TaperedExtrusion),
("IFCREVOLVEDAREASOLID", BodyKind::Revolution),
("IFCREVOLVEDAREASOLIDTAPERED", BodyKind::TaperedRevolution),
("IFCFIXEDREFERENCESWEPTAREASOLID", BodyKind::DirectrixSweep),
("IFCSURFACECURVESWEPTAREASOLID", BodyKind::DirectrixSweep),
("IFCSWEPTDISKSOLID", BodyKind::SweptDisk),
("IFCSWEPTDISKSOLIDPOLYGONAL", BodyKind::SweptDisk),
("IFCSECTIONEDSPINE", BodyKind::SectionedSpine),
("IFCFACETEDBREP", BodyKind::Brep),
("IFCFACETEDBREPWITHVOIDS", BodyKind::Brep),
("IFCADVANCEDBREP", BodyKind::Brep),
("IFCADVANCEDBREPWITHVOIDS", BodyKind::Brep),
("IFCCSGSOLID", BodyKind::Csg),
("IFCBOOLEANRESULT", BodyKind::Csg),
("IFCBOOLEANCLIPPINGRESULT", BodyKind::Csg),
("IFCBLOCK", BodyKind::CsgPrimitive),
("IFCSPHERE", BodyKind::CsgPrimitive),
("IFCRIGHTCIRCULARCYLINDER", BodyKind::CsgPrimitive),
("IFCRIGHTCIRCULARCONE", BodyKind::CsgPrimitive),
("IFCRECTANGULARPYRAMID", BodyKind::CsgPrimitive),
("IFCHALFSPACESOLID", BodyKind::HalfSpace),
("IFCBOXEDHALFSPACE", BodyKind::HalfSpace),
("IFCPOLYGONALBOUNDEDHALFSPACE", BodyKind::HalfSpace),
("IFCBOUNDINGBOX", BodyKind::BoundingBox),
("IFCTRIANGULATEDFACESET", BodyKind::Tessellated),
("IFCPOLYGONALFACESET", BodyKind::Tessellated),
("IFCSHELLBASEDSURFACEMODEL", BodyKind::SurfaceModel),
("IFCFACEBASEDSURFACEMODEL", BodyKind::SurfaceModel),
("IFCFACESURFACE", BodyKind::Face),
("IFCADVANCEDFACE", BodyKind::Face),
("IFCGEOMETRICSET", BodyKind::GeometricSet),
("IFCGEOMETRICCURVESET", BodyKind::GeometricSet),
("IFCPOINTONCURVE", BodyKind::Point),
("IFCPOINTONSURFACE", BodyKind::Point),
];
impl BodyKind {
pub fn classify(type_name: &str) -> Option<Self> {
let upper = type_name.to_ascii_uppercase();
if is_a(&upper, "IFCCURVE") {
return Some(Self::Curve);
}
if is_a(&upper, "IFCSURFACE") {
return Some(Self::Surface);
}
FAMILIES
.iter()
.find(|(name, _)| *name == upper)
.map(|(_, kind)| *kind)
}
pub fn is_swept_area(self) -> bool {
matches!(
self,
Self::Extrusion
| Self::TaperedExtrusion
| Self::Revolution
| Self::TaperedRevolution
| Self::DirectrixSweep
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::solid::SolidKind;
#[test]
fn body_kinds_refine_solid_kinds() {
for (name, kind) in FAMILIES {
let Some(solid) = SolidKind::classify(name) else {
continue;
};
let expected = match kind {
BodyKind::Extrusion
| BodyKind::TaperedExtrusion
| BodyKind::Revolution
| BodyKind::TaperedRevolution
| BodyKind::DirectrixSweep
| BodyKind::SweptDisk => SolidKind::Swept,
BodyKind::SectionedSpine => SolidKind::SectionedSpine,
BodyKind::Brep => SolidKind::Brep,
BodyKind::Csg if *name == "IFCCSGSOLID" => SolidKind::Csg,
BodyKind::Csg => SolidKind::Boolean,
BodyKind::CsgPrimitive => SolidKind::Csg,
BodyKind::HalfSpace => SolidKind::HalfSpace,
BodyKind::BoundingBox => SolidKind::BoundingBox,
BodyKind::Tessellated => SolidKind::Tessellated,
BodyKind::SurfaceModel | BodyKind::GeometricSet => SolidKind::SurfaceModel,
other => panic!("{name} classified {other:?} is not a solid family"),
};
assert_eq!(solid, expected, "{name}");
}
}
#[test]
fn curves_and_surfaces_classify_by_inheritance() {
assert_eq!(BodyKind::classify("IfcTrimmedCurve"), Some(BodyKind::Curve));
assert_eq!(BodyKind::classify("IFCPLANE"), Some(BodyKind::Surface));
assert_eq!(BodyKind::classify("IfcFaceSurface"), Some(BodyKind::Face));
assert_eq!(BodyKind::classify("IFCADVANCEDFACE"), Some(BodyKind::Face));
assert_eq!(BodyKind::classify("IFCMAPPEDITEM"), None);
assert_eq!(BodyKind::classify("IFCWALL"), None);
}
}