pub mod bbox;
pub mod boolean;
pub mod brep;
pub mod csg;
pub mod halfspace;
pub mod surface_model;
pub mod swept;
pub mod tessellated;
#[cfg(test)]
pub(crate) mod testkit;
pub use bbox::BoundingBox;
pub use boolean::{
BooleanClippingResult, BooleanOperator, BooleanResult, IfcBooleanOperator, OperandKind,
ParseIfcBooleanOperatorError,
};
pub use brep::{
AdvancedBrep, AdvancedBrepWithVoids, FacetedBrep, FacetedBrepWithVoids, ManifoldSolidBrep,
};
pub use csg::{
Block, CsgPrimitive3D, CsgSolid, RectangularPyramid, RightCircularCone, RightCircularCylinder,
Sphere,
};
pub use halfspace::{BoxedHalfSpace, HalfSpaceSolid, PolygonalBoundedHalfSpace};
pub use surface_model::{
FaceBasedSurfaceModel, GeometricCurveSet, GeometricSet, ShellBasedSurfaceModel,
};
pub use swept::{
ExtrudedAreaSolid, ExtrudedAreaSolidTapered, FixedReferenceSweptAreaSolid, RevolvedAreaSolid,
RevolvedAreaSolidTapered, SectionedSpine, SurfaceCurveSweptAreaSolid, SweptAreaSolid,
SweptDiskSolid, SweptDiskSolidPolygonal,
};
pub use tessellated::{
IndexedPolygonalFace, IndexedPolygonalFaceWithVoids, PolygonalFaceSet, TessellatedFaceSet,
TessellatedItem, TriangulatedFaceSet,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SolidKind {
Swept,
Brep,
Csg,
HalfSpace,
Boolean,
Tessellated,
SurfaceModel,
BoundingBox,
SectionedSpine,
}
impl SolidKind {
pub fn classify(type_name: &str) -> Option<Self> {
let n = type_name.to_ascii_uppercase();
let kind = match n.as_str() {
"IFCSWEPTAREASOLID"
| "IFCEXTRUDEDAREASOLID"
| "IFCEXTRUDEDAREASOLIDTAPERED"
| "IFCREVOLVEDAREASOLID"
| "IFCREVOLVEDAREASOLIDTAPERED"
| "IFCSURFACECURVESWEPTAREASOLID"
| "IFCFIXEDREFERENCESWEPTAREASOLID"
| "IFCSWEPTDISKSOLID"
| "IFCSWEPTDISKSOLIDPOLYGONAL" => Self::Swept,
"IFCSECTIONEDSPINE" => Self::SectionedSpine,
"IFCMANIFOLDSOLIDBREP"
| "IFCFACETEDBREP"
| "IFCFACETEDBREPWITHVOIDS"
| "IFCADVANCEDBREP"
| "IFCADVANCEDBREPWITHVOIDS" => Self::Brep,
"IFCCSGSOLID"
| "IFCCSGPRIMITIVE3D"
| "IFCBLOCK"
| "IFCRECTANGULARPYRAMID"
| "IFCRIGHTCIRCULARCONE"
| "IFCRIGHTCIRCULARCYLINDER"
| "IFCSPHERE" => Self::Csg,
"IFCHALFSPACESOLID" | "IFCBOXEDHALFSPACE" | "IFCPOLYGONALBOUNDEDHALFSPACE" => {
Self::HalfSpace
}
"IFCBOOLEANRESULT" | "IFCBOOLEANCLIPPINGRESULT" => Self::Boolean,
"IFCTESSELLATEDITEM"
| "IFCTESSELLATEDFACESET"
| "IFCTRIANGULATEDFACESET"
| "IFCPOLYGONALFACESET"
| "IFCINDEXEDPOLYGONALFACE"
| "IFCINDEXEDPOLYGONALFACEWITHVOIDS" => Self::Tessellated,
"IFCSHELLBASEDSURFACEMODEL"
| "IFCFACEBASEDSURFACEMODEL"
| "IFCGEOMETRICSET"
| "IFCGEOMETRICCURVESET" => Self::SurfaceModel,
"IFCBOUNDINGBOX" => Self::BoundingBox,
_ => return None,
};
Some(kind)
}
pub fn is_finite_solid(self) -> bool {
matches!(
self,
Self::Swept | Self::Brep | Self::Csg | Self::SectionedSpine
)
}
pub fn requires_boolean_context(self) -> bool {
matches!(self, Self::HalfSpace)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn every_geometric_model_resource_entity_classifies() {
let expected: &[(&str, SolidKind)] = &[
("IfcSweptAreaSolid", SolidKind::Swept),
("IfcExtrudedAreaSolid", SolidKind::Swept),
("IfcExtrudedAreaSolidTapered", SolidKind::Swept),
("IfcRevolvedAreaSolid", SolidKind::Swept),
("IfcRevolvedAreaSolidTapered", SolidKind::Swept),
("IfcSurfaceCurveSweptAreaSolid", SolidKind::Swept),
("IfcFixedReferenceSweptAreaSolid", SolidKind::Swept),
("IfcSweptDiskSolid", SolidKind::Swept),
("IfcSweptDiskSolidPolygonal", SolidKind::Swept),
("IfcSectionedSpine", SolidKind::SectionedSpine),
("IfcManifoldSolidBrep", SolidKind::Brep),
("IfcFacetedBrep", SolidKind::Brep),
("IfcFacetedBrepWithVoids", SolidKind::Brep),
("IfcAdvancedBrep", SolidKind::Brep),
("IfcAdvancedBrepWithVoids", SolidKind::Brep),
("IfcCsgSolid", SolidKind::Csg),
("IfcCsgPrimitive3D", SolidKind::Csg),
("IfcBlock", SolidKind::Csg),
("IfcRectangularPyramid", SolidKind::Csg),
("IfcRightCircularCone", SolidKind::Csg),
("IfcRightCircularCylinder", SolidKind::Csg),
("IfcSphere", SolidKind::Csg),
("IfcHalfSpaceSolid", SolidKind::HalfSpace),
("IfcBoxedHalfSpace", SolidKind::HalfSpace),
("IfcPolygonalBoundedHalfSpace", SolidKind::HalfSpace),
("IfcBooleanResult", SolidKind::Boolean),
("IfcBooleanClippingResult", SolidKind::Boolean),
("IfcTessellatedItem", SolidKind::Tessellated),
("IfcTessellatedFaceSet", SolidKind::Tessellated),
("IfcTriangulatedFaceSet", SolidKind::Tessellated),
("IfcPolygonalFaceSet", SolidKind::Tessellated),
("IfcIndexedPolygonalFace", SolidKind::Tessellated),
("IfcIndexedPolygonalFaceWithVoids", SolidKind::Tessellated),
("IfcShellBasedSurfaceModel", SolidKind::SurfaceModel),
("IfcFaceBasedSurfaceModel", SolidKind::SurfaceModel),
("IfcGeometricSet", SolidKind::SurfaceModel),
("IfcGeometricCurveSet", SolidKind::SurfaceModel),
("IfcBoundingBox", SolidKind::BoundingBox),
];
for (name, kind) in expected {
assert_eq!(
SolidKind::classify(name),
Some(*kind),
"{name} must classify"
);
assert_eq!(
SolidKind::classify(&name.to_ascii_uppercase()),
Some(*kind),
"{name} must classify upper-cased"
);
}
assert_eq!(expected.len(), 38, "coverage list must not shrink silently");
}
#[test]
fn entities_outside_this_schema_do_not_classify() {
for name in ["IfcWall", "IfcPolyline", "IfcCartesianPoint", "IfcPlane"] {
assert_eq!(SolidKind::classify(name), None, "{name}");
}
}
#[test]
fn only_genuinely_bounded_families_report_a_finite_volume() {
for kind in [
SolidKind::Swept,
SolidKind::Brep,
SolidKind::Csg,
SolidKind::SectionedSpine,
] {
assert!(kind.is_finite_solid(), "{kind:?}");
}
for kind in [
SolidKind::HalfSpace,
SolidKind::Boolean,
SolidKind::Tessellated,
SolidKind::SurfaceModel,
SolidKind::BoundingBox,
] {
assert!(!kind.is_finite_solid(), "{kind:?}");
}
}
#[test]
fn only_half_spaces_require_a_boolean_context() {
assert!(SolidKind::HalfSpace.requires_boolean_context());
for kind in [
SolidKind::Swept,
SolidKind::Brep,
SolidKind::Csg,
SolidKind::Boolean,
SolidKind::Tessellated,
SolidKind::SurfaceModel,
SolidKind::BoundingBox,
SolidKind::SectionedSpine,
] {
assert!(!kind.requires_boolean_context(), "{kind:?}");
}
}
#[test]
fn sectioned_spine_is_classified_apart_from_the_swept_area_family() {
assert_ne!(
SolidKind::classify("IfcSectionedSpine"),
SolidKind::classify("IfcExtrudedAreaSolid")
);
}
}