use ifc_model::{Entity, EntityId};
use crate::release::Release;
use crate::view::{
borrowed_entity, optional_integer, optional_logical, optional_ref, optional_text,
required_enum, required_number, required_number_array_2, MaterialView,
};
use crate::{LayerSetDirection, LogicalValue, MaterialError, MaterialResult};
borrowed_entity!(MaterialLayer, "IFCMATERIALLAYER");
borrowed_entity!(MaterialLayerWithOffsets, "IFCMATERIALLAYERWITHOFFSETS");
pub(crate) fn layer_thickness(
entity_type: &'static str,
id: EntityId,
entity: &Entity,
release: Release<'_>,
) -> MaterialResult<f64> {
let (slot, declared) = release.attribute(entity_type, id, "LayerThickness")?;
let value = required_number(entity_type, id, entity, slot, "LayerThickness")?;
let positive = declared
.type_name
.eq_ignore_ascii_case("IfcPositiveLengthMeasure");
if value < 0.0 || (positive && value == 0.0) {
return Err(MaterialError::InvalidValue {
entity: entity_type,
id,
attribute: "LayerThickness",
value: if positive {
"expected a positive length".to_owned()
} else {
"expected a non-negative length".to_owned()
},
});
}
Ok(value)
}
macro_rules! layer_accessors {
($type:ident, $ifc_name:literal) => {
impl<'m> $type<'m> {
pub fn material_id(self) -> MaterialResult<Option<EntityId>> {
optional_ref(
$ifc_name,
self.id(),
self.entity(),
self.slot("Material")?,
"Material",
)
}
pub fn thickness(self) -> MaterialResult<f64> {
layer_thickness($ifc_name, self.id(), self.entity(), self.release())
}
pub fn is_ventilated(self) -> MaterialResult<Option<LogicalValue>> {
optional_logical(
$ifc_name,
self.id(),
self.entity(),
self.slot("IsVentilated")?,
"IsVentilated",
)
}
pub fn name(self) -> MaterialResult<Option<&'m str>> {
optional_text(
$ifc_name,
self.id(),
self.entity(),
self.slot("Name")?,
"Name",
)
}
pub fn description(self) -> MaterialResult<Option<&'m str>> {
optional_text(
$ifc_name,
self.id(),
self.entity(),
self.slot("Description")?,
"Description",
)
}
pub fn category(self) -> MaterialResult<Option<&'m str>> {
optional_text(
$ifc_name,
self.id(),
self.entity(),
self.slot("Category")?,
"Category",
)
}
pub fn priority(self) -> MaterialResult<Option<i64>> {
let value = optional_integer(
$ifc_name,
self.id(),
self.entity(),
self.slot("Priority")?,
"Priority",
)?;
if value.is_some_and(|value| !(0..=100).contains(&value)) {
return Err(MaterialError::InvalidValue {
entity: $ifc_name,
id: self.id(),
attribute: "Priority",
value: "expected an integer in 0..=100".to_owned(),
});
}
Ok(value)
}
}
};
}
layer_accessors!(MaterialLayer, "IFCMATERIALLAYER");
layer_accessors!(MaterialLayerWithOffsets, "IFCMATERIALLAYERWITHOFFSETS");
impl MaterialLayerWithOffsets<'_> {
pub fn offset_direction(self) -> MaterialResult<LayerSetDirection> {
let token = required_enum(
"IFCMATERIALLAYERWITHOFFSETS",
self.id(),
self.entity(),
self.slot("OffsetDirection")?,
"OffsetDirection",
)?;
LayerSetDirection::parse(token).ok_or_else(|| MaterialError::InvalidValue {
entity: "IFCMATERIALLAYERWITHOFFSETS",
id: self.id(),
attribute: "OffsetDirection",
value: token.to_owned(),
})
}
pub fn offset_values(self) -> MaterialResult<[f64; 2]> {
required_number_array_2(
"IFCMATERIALLAYERWITHOFFSETS",
self.id(),
self.entity(),
self.slot("OffsetValues")?,
"OffsetValues",
)
}
}
impl<'m> MaterialView<'m> {
pub fn layers(self) -> impl Iterator<Item = MaterialLayer<'m>> + 'm {
let release = self.release();
self.model()
.of_type("IFCMATERIALLAYER")
.map(move |(id, entity)| MaterialLayer::from_known(id, entity, release))
}
pub fn layers_with_offsets(self) -> impl Iterator<Item = MaterialLayerWithOffsets<'m>> + 'm {
let release = self.release();
self.model()
.of_type("IFCMATERIALLAYERWITHOFFSETS")
.map(move |(id, entity)| MaterialLayerWithOffsets::from_known(id, entity, release))
}
}