use std::sync::{OnceLock, RwLock};
use rustc_hash::FxHashMap;
use crate::generated::IfcType;
use crate::legacy_entities::get_legacy_entity_info;
fn normalise_uppercase(type_name: &str) -> std::borrow::Cow<'_, str> {
if type_name.bytes().any(|b| b.is_ascii_lowercase()) {
std::borrow::Cow::Owned(type_name.to_ascii_uppercase())
} else {
std::borrow::Cow::Borrowed(type_name)
}
}
fn cached<F>(cache: &RwLock<FxHashMap<String, bool>>, key: &str, f: F) -> bool
where
F: FnOnce() -> bool,
{
if let Ok(read) = cache.read() {
if let Some(&v) = read.get(key) {
return v;
}
}
let value = f();
if let Ok(mut write) = cache.write() {
write.insert(key.to_owned(), value);
}
value
}
pub fn has_geometry_by_name(type_name: &str) -> bool {
static CACHE: OnceLock<RwLock<FxHashMap<String, bool>>> = OnceLock::new();
let cache = CACHE.get_or_init(|| RwLock::new(FxHashMap::default()));
let upper = normalise_uppercase(type_name);
cached(cache, upper.as_ref(), || compute_has_geometry(upper.as_ref()))
}
fn compute_has_geometry(upper: &str) -> bool {
if let Some(info) = get_legacy_entity_info(upper) {
return info.has_geometry;
}
let t = IfcType::from_str(upper);
if matches!(t, IfcType::Unknown(_)) {
return upper.starts_with("IFCREINFORCING") || upper.starts_with("IFCREINFORCED");
}
if !t.is_subtype_of(IfcType::IfcProduct) {
return false;
}
!is_non_geometric_spatial(t)
}
fn is_non_geometric_spatial(t: IfcType) -> bool {
if t.is_subtype_of(IfcType::IfcSpace)
|| t.is_subtype_of(IfcType::IfcSite)
|| t.is_subtype_of(IfcType::IfcSpatialZone)
{
return false;
}
t.is_subtype_of(IfcType::IfcSpatialElement)
}
pub fn is_simple_geometry_type(type_name: &str) -> bool {
static CACHE: OnceLock<RwLock<FxHashMap<String, bool>>> = OnceLock::new();
let cache = CACHE.get_or_init(|| RwLock::new(FxHashMap::default()));
let upper = normalise_uppercase(type_name);
cached(cache, upper.as_ref(), || compute_is_simple(upper.as_ref()))
}
fn compute_is_simple(upper: &str) -> bool {
let t = match get_legacy_entity_info(upper) {
Some(info) => info.base_type,
None => IfcType::from_str(upper),
};
if matches!(t, IfcType::Unknown(_)) {
return true;
}
let is_secondary = t.is_subtype_of(IfcType::IfcOpeningElement)
|| t.is_subtype_of(IfcType::IfcWindow)
|| t.is_subtype_of(IfcType::IfcDoor)
|| t.is_subtype_of(IfcType::IfcFurnishingElement)
|| t.is_subtype_of(IfcType::IfcDistributionElement)
|| matches!(
t,
IfcType::IfcSpace
| IfcType::IfcSpatialZone
| IfcType::IfcSite
| IfcType::IfcAnnotation
| IfcType::IfcVirtualElement
| IfcType::IfcBuildingElementProxy
);
!is_secondary
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn building_elements_have_geometry() {
for name in [
"IFCWALL",
"IFCSLAB",
"IFCBEAM",
"IFCCOLUMN",
"IFCDOOR",
"IFCWINDOW",
"IFCROOF",
"IFCSTAIR",
"IFCSHADINGDEVICE",
] {
assert!(has_geometry_by_name(name), "{name} should have geometry");
}
}
#[test]
fn mep_elements_have_geometry() {
for name in [
"IFCFLOWSEGMENT",
"IFCFLOWFITTING",
"IFCENERGYCONVERSIONDEVICE",
"IFCFLOWTREATMENTDEVICE",
"IFCBOILER",
"IFCPUMP",
"IFCVALVE",
] {
assert!(has_geometry_by_name(name), "{name} should have geometry");
}
}
#[test]
fn solar_device_has_geometry() {
assert!(has_geometry_by_name("IFCSOLARDEVICE"));
assert!(has_geometry_by_name("IfcSolarDevice"));
}
#[test]
fn ifc4x3_infrastructure_have_geometry() {
for name in [
"IFCBEARING",
"IFCKERB",
"IFCPAVEMENT",
"IFCRAIL",
"IFCTRACKELEMENT",
"IFCSIGN",
"IFCSIGNAL",
"IFCEARTHWORKSCUT",
] {
assert!(has_geometry_by_name(name), "{name} should have geometry");
}
}
#[test]
fn reinforcement_variants_have_geometry() {
assert!(has_geometry_by_name("IFCREINFORCINGBAR"));
assert!(has_geometry_by_name("IFCREINFORCINGMESH"));
assert!(has_geometry_by_name("IFCREINFORCEDSOIL"));
}
#[test]
fn standardcase_and_elementedcase_have_geometry() {
for name in [
"IFCBEAMSTANDARDCASE",
"IFCSLABSTANDARDCASE",
"IFCSLABELEMENTEDCASE",
"IFCWALLSTANDARDCASE",
"IFCWALLELEMENTEDCASE",
"IFCDOORSTANDARDCASE",
"IFCWINDOWSTANDARDCASE",
"IFCOPENINGSTANDARDCASE",
] {
assert!(has_geometry_by_name(name), "{name} should have geometry");
}
}
#[test]
fn space_and_site_have_geometry() {
assert!(has_geometry_by_name("IFCSPACE"));
assert!(has_geometry_by_name("IFCSITE"));
assert!(has_geometry_by_name("IFCOPENINGELEMENT"));
assert!(has_geometry_by_name("IFCSPATIALZONE"));
}
#[test]
fn legacy_ifc2x3_distribution_names_have_geometry() {
assert!(has_geometry_by_name("IFCEQUIPMENTELEMENT"));
assert!(has_geometry_by_name("IFCELECTRICALDISTRIBUTIONPOINT"));
}
#[test]
fn non_geometric_spatial_excluded() {
for name in [
"IFCBUILDING",
"IFCBUILDINGSTOREY",
"IFCFACILITY",
"IFCFACILITYPART",
"IFCSPATIALELEMENT",
"IFCSPATIALSTRUCTUREELEMENT",
"IFCBRIDGE",
"IFCROAD",
"IFCRAILWAY",
"IFCMARINEFACILITY",
"IFCBRIDGEPART",
"IFCFACILITYPARTCOMMON",
"IFCEXTERNALSPATIALELEMENT",
"IFCEXTERNALSPATIALSTRUCTUREELEMENT",
] {
assert!(!has_geometry_by_name(name), "{name} should NOT have geometry");
}
}
#[test]
fn non_products_excluded() {
for name in [
"IFCPROJECT",
"IFCMATERIAL",
"IFCPROPERTYSET",
"IFCRELAGGREGATES",
"IFCDIMENSIONALEXPONENTS",
"IFCSURFACESTYLERENDERING",
"IFCGEOMETRICREPRESENTATIONSUBCONTEXT",
"IFCCARTESIANPOINT",
] {
assert!(!has_geometry_by_name(name), "{name} should NOT have geometry");
}
}
#[test]
fn legacy_proxy_and_buildingelement_have_geometry() {
assert!(has_geometry_by_name("IFCPROXY"));
assert!(has_geometry_by_name("IFCBUILDINGELEMENT"));
}
#[test]
fn unknown_garbage_excluded() {
assert!(!has_geometry_by_name("IFCNOTAREALTYPE"));
assert!(!has_geometry_by_name(""));
assert!(!has_geometry_by_name("FOOREINFORCEDBAR"));
}
#[test]
fn cached_results_are_consistent() {
for _ in 0..3 {
assert!(has_geometry_by_name("IFCWALL"));
assert!(!has_geometry_by_name("IFCPROJECT"));
assert!(is_simple_geometry_type("IFCWALL"));
assert!(!is_simple_geometry_type("IFCWINDOW"));
}
}
#[test]
fn is_simple_geometry_type_routes_correctly() {
assert!(is_simple_geometry_type("IFCWALL"));
assert!(is_simple_geometry_type("IFCSLAB"));
assert!(is_simple_geometry_type("IFCBEAM"));
assert!(is_simple_geometry_type("IFCCOLUMN"));
assert!(!is_simple_geometry_type("IFCWINDOW"));
assert!(!is_simple_geometry_type("IFCDOOR"));
assert!(!is_simple_geometry_type("IFCOPENINGELEMENT"));
assert!(!is_simple_geometry_type("IFCFLOWSEGMENT"));
assert!(!is_simple_geometry_type("IFCSOLARDEVICE"));
assert!(!is_simple_geometry_type("IFCSPACE"));
assert!(!is_simple_geometry_type("IFCANNOTATION"));
assert!(!is_simple_geometry_type("IFCBUILDINGELEMENTPROXY"));
assert!(is_simple_geometry_type("IfcWall"));
assert!(!is_simple_geometry_type("IfcDoor"));
}
}