Skip to main content

ifc_material/layer/
definition.rs

1//! Material layer definitions, including authored offsets.
2
3use ifc_model::EntityId;
4
5use crate::view::{
6    borrowed_entity, optional_integer, optional_logical, optional_ref, optional_text,
7    required_enum, required_number, required_number_array_2, MaterialView,
8};
9use crate::{LayerSetDirection, LogicalValue, MaterialError, MaterialResult};
10
11borrowed_entity!(MaterialLayer, "IFCMATERIALLAYER");
12borrowed_entity!(MaterialLayerWithOffsets, "IFCMATERIALLAYERWITHOFFSETS");
13
14macro_rules! layer_accessors {
15    ($type:ident, $ifc_name:literal) => {
16        impl<'m> $type<'m> {
17            /// `Material`, the associated `IfcMaterial`, if given.
18            pub fn material_id(self) -> MaterialResult<Option<EntityId>> {
19                optional_ref($ifc_name, self.id(), self.entity(), 0, "Material")
20            }
21
22            /// `LayerThickness`. Required and must be non-negative.
23            pub fn thickness(self) -> MaterialResult<f64> {
24                let value =
25                    required_number($ifc_name, self.id(), self.entity(), 1, "LayerThickness")?;
26                if value < 0.0 {
27                    return Err(MaterialError::InvalidValue {
28                        entity: $ifc_name,
29                        id: self.id(),
30                        attribute: "LayerThickness",
31                        value: "expected a non-negative length".to_owned(),
32                    });
33                }
34                Ok(value)
35            }
36
37            /// `IsVentilated`, if given.
38            pub fn is_ventilated(self) -> MaterialResult<Option<LogicalValue>> {
39                optional_logical($ifc_name, self.id(), self.entity(), 2, "IsVentilated")
40            }
41
42            /// `Name`, if given.
43            pub fn name(self) -> MaterialResult<Option<&'m str>> {
44                optional_text($ifc_name, self.id(), self.entity(), 3, "Name")
45            }
46
47            /// `Description`, if given.
48            pub fn description(self) -> MaterialResult<Option<&'m str>> {
49                optional_text($ifc_name, self.id(), self.entity(), 4, "Description")
50            }
51
52            /// `Category`, if given.
53            pub fn category(self) -> MaterialResult<Option<&'m str>> {
54                optional_text($ifc_name, self.id(), self.entity(), 5, "Category")
55            }
56
57            /// `Priority`, if given. Must be in `0..=100`.
58            pub fn priority(self) -> MaterialResult<Option<i64>> {
59                let value = optional_integer($ifc_name, self.id(), self.entity(), 6, "Priority")?;
60                if value.is_some_and(|value| !(0..=100).contains(&value)) {
61                    return Err(MaterialError::InvalidValue {
62                        entity: $ifc_name,
63                        id: self.id(),
64                        attribute: "Priority",
65                        value: "expected an integer in 0..=100".to_owned(),
66                    });
67                }
68                Ok(value)
69            }
70        }
71    };
72}
73layer_accessors!(MaterialLayer, "IFCMATERIALLAYER");
74layer_accessors!(MaterialLayerWithOffsets, "IFCMATERIALLAYERWITHOFFSETS");
75
76impl MaterialLayerWithOffsets<'_> {
77    /// `IfcMaterialLayerWithOffsets.OffsetDirection`. Required.
78    pub fn offset_direction(self) -> MaterialResult<LayerSetDirection> {
79        let token = required_enum(
80            "IFCMATERIALLAYERWITHOFFSETS",
81            self.id(),
82            self.entity(),
83            7,
84            "OffsetDirection",
85        )?;
86        LayerSetDirection::parse(token).ok_or_else(|| MaterialError::InvalidValue {
87            entity: "IFCMATERIALLAYERWITHOFFSETS",
88            id: self.id(),
89            attribute: "OffsetDirection",
90            value: token.to_owned(),
91        })
92    }
93
94    /// `IfcMaterialLayerWithOffsets.OffsetValues`. Required, a 2-element
95    /// list.
96    pub fn offset_values(self) -> MaterialResult<[f64; 2]> {
97        required_number_array_2(
98            "IFCMATERIALLAYERWITHOFFSETS",
99            self.id(),
100            self.entity(),
101            8,
102            "OffsetValues",
103        )
104    }
105}
106
107impl<'m> MaterialView<'m> {
108    /// Iterates every `IfcMaterialLayer` instance in the model.
109    pub fn layers(self) -> impl Iterator<Item = MaterialLayer<'m>> + 'm {
110        self.model()
111            .of_type("IFCMATERIALLAYER")
112            .map(|(id, entity)| MaterialLayer::from_known(id, entity))
113    }
114
115    /// Iterates every `IfcMaterialLayerWithOffsets` instance in the model.
116    pub fn layers_with_offsets(self) -> impl Iterator<Item = MaterialLayerWithOffsets<'m>> + 'm {
117        self.model()
118            .of_type("IFCMATERIALLAYERWITHOFFSETS")
119            .map(|(id, entity)| MaterialLayerWithOffsets::from_known(id, entity))
120    }
121}