Skip to main content

ifc_material/layer/
set.rs

1//! Ordered `IfcMaterialLayerSet` composition and total thickness.
2
3use ifc_model::EntityId;
4
5use crate::view::{borrowed_entity, optional_text, required_number, required_refs, MaterialView};
6use crate::{MaterialError, MaterialResult};
7
8borrowed_entity!(MaterialLayerSet, "IFCMATERIALLAYERSET");
9
10impl<'m> MaterialLayerSet<'m> {
11    /// `IfcMaterialLayerSet.MaterialLayers`, in set order. Required and
12    /// must be non-empty.
13    pub fn layer_ids(self) -> MaterialResult<Vec<EntityId>> {
14        required_refs(
15            "IFCMATERIALLAYERSET",
16            self.id(),
17            self.entity(),
18            0,
19            "MaterialLayers",
20            1,
21        )
22    }
23
24    /// `IfcMaterialLayerSet.LayerSetName`, if given.
25    pub fn name(self) -> MaterialResult<Option<&'m str>> {
26        optional_text(
27            "IFCMATERIALLAYERSET",
28            self.id(),
29            self.entity(),
30            1,
31            "LayerSetName",
32        )
33    }
34
35    /// `IfcMaterialLayerSet.Description`, if given.
36    pub fn description(self) -> MaterialResult<Option<&'m str>> {
37        optional_text(
38            "IFCMATERIALLAYERSET",
39            self.id(),
40            self.entity(),
41            2,
42            "Description",
43        )
44    }
45}
46
47impl<'m> MaterialView<'m> {
48    /// Iterates every `IfcMaterialLayerSet` instance in the model.
49    pub fn layer_sets(self) -> impl Iterator<Item = MaterialLayerSet<'m>> + 'm {
50        self.model()
51            .of_type("IFCMATERIALLAYERSET")
52            .map(|(id, entity)| MaterialLayerSet::from_known(id, entity))
53    }
54
55    /// Evaluate the normative `IfcMlsTotalThickness` function.
56    pub fn total_thickness(self, set: MaterialLayerSet<'m>) -> MaterialResult<f64> {
57        let mut total = 0.0;
58        for layer_id in set.layer_ids()? {
59            let layer = self.entity(set.id(), layer_id)?;
60            if !layer.is_type("IFCMATERIALLAYER") && !layer.is_type("IFCMATERIALLAYERWITHOFFSETS") {
61                return Err(MaterialError::ReferenceType {
62                    source_id: set.id(),
63                    target: layer_id,
64                    expected: "IFCMATERIALLAYER",
65                    actual: layer.type_name.to_string(),
66                });
67            }
68            let thickness =
69                required_number("IFCMATERIALLAYER", layer_id, layer, 1, "LayerThickness")?;
70            if thickness < 0.0 {
71                return Err(MaterialError::InvalidValue {
72                    entity: "IFCMATERIALLAYER",
73                    id: layer_id,
74                    attribute: "LayerThickness",
75                    value: "expected a non-negative length".to_owned(),
76                });
77            }
78            total += thickness;
79            if !total.is_finite() {
80                return Err(MaterialError::InvalidValue {
81                    entity: "IFCMATERIALLAYERSET",
82                    id: set.id(),
83                    attribute: "TotalThickness",
84                    value: "finite layer thicknesses overflowed the aggregate".to_owned(),
85                });
86            }
87        }
88        Ok(total)
89    }
90}