ifc_material/profile/
usage.rs1use ifc_model::EntityId;
4
5use crate::view::{borrowed_entity, optional_integer, optional_number, required_ref, MaterialView};
6use crate::{CardinalPointReference, MaterialError, MaterialResult};
7
8borrowed_entity!(
12 MaterialProfileSetUsage,
13 "IFCMATERIALPROFILESETUSAGE",
14 ["IFCMATERIALPROFILESETUSAGETAPERING"]
15);
16borrowed_entity!(
17 MaterialProfileSetUsageTapering,
18 "IFCMATERIALPROFILESETUSAGETAPERING"
19);
20
21fn cardinal(
22 entity_type: &'static str,
23 id: EntityId,
24 entity: &ifc_model::Entity,
25 slot: usize,
26 attribute: &'static str,
27) -> MaterialResult<Option<CardinalPointReference>> {
28 let Some(value) = optional_integer(entity_type, id, entity, slot, attribute)? else {
29 return Ok(None);
30 };
31 CardinalPointReference::new(value)
32 .map(Some)
33 .ok_or_else(|| MaterialError::InvalidValue {
34 entity: entity_type,
35 id,
36 attribute,
37 value: value.to_string(),
38 })
39}
40
41fn positive_extent(
42 entity_type: &'static str,
43 id: EntityId,
44 entity: &ifc_model::Entity,
45 slot: usize,
46) -> MaterialResult<Option<f64>> {
47 let value = optional_number(entity_type, id, entity, slot, "ReferenceExtent")?;
48 if value.is_some_and(|value| value <= 0.0) {
49 return Err(MaterialError::InvalidValue {
50 entity: entity_type,
51 id,
52 attribute: "ReferenceExtent",
53 value: "expected a positive length".to_owned(),
54 });
55 }
56 Ok(value)
57}
58
59macro_rules! usage_accessors {
60 ($type:ident, $ifc_name:literal) => {
61 impl $type<'_> {
62 pub fn profile_set_id(self) -> MaterialResult<EntityId> {
64 required_ref(
65 $ifc_name,
66 self.id(),
67 self.entity(),
68 self.slot("ForProfileSet")?,
69 "ForProfileSet",
70 )
71 }
72
73 pub fn cardinal_point(self) -> MaterialResult<Option<CardinalPointReference>> {
76 cardinal(
77 $ifc_name,
78 self.id(),
79 self.entity(),
80 self.slot("CardinalPoint")?,
81 "CardinalPoint",
82 )
83 }
84
85 pub fn reference_extent(self) -> MaterialResult<Option<f64>> {
88 positive_extent(
89 $ifc_name,
90 self.id(),
91 self.entity(),
92 self.slot("ReferenceExtent")?,
93 )
94 }
95 }
96 };
97}
98usage_accessors!(MaterialProfileSetUsage, "IFCMATERIALPROFILESETUSAGE");
99usage_accessors!(
100 MaterialProfileSetUsageTapering,
101 "IFCMATERIALPROFILESETUSAGETAPERING"
102);
103
104impl<'m> MaterialProfileSetUsage<'m> {
105 pub fn tapering(self) -> Option<MaterialProfileSetUsageTapering<'m>> {
109 self.entity()
110 .is_type("IFCMATERIALPROFILESETUSAGETAPERING")
111 .then(|| {
112 MaterialProfileSetUsageTapering::from_known(
113 self.id(),
114 self.entity(),
115 self.release(),
116 )
117 })
118 }
119}
120
121impl MaterialProfileSetUsageTapering<'_> {
122 pub fn end_profile_set_id(self) -> MaterialResult<EntityId> {
124 required_ref(
125 "IFCMATERIALPROFILESETUSAGETAPERING",
126 self.id(),
127 self.entity(),
128 self.slot("ForProfileEndSet")?,
129 "ForProfileEndSet",
130 )
131 }
132
133 pub fn cardinal_end_point(self) -> MaterialResult<Option<CardinalPointReference>> {
136 cardinal(
137 "IFCMATERIALPROFILESETUSAGETAPERING",
138 self.id(),
139 self.entity(),
140 self.slot("CardinalEndPoint")?,
141 "CardinalEndPoint",
142 )
143 }
144}
145
146impl<'m> MaterialView<'m> {
147 pub fn profile_set_usages(self) -> impl Iterator<Item = MaterialProfileSetUsage<'m>> + 'm {
156 let release = self.release();
157 let mut usages: Vec<_> = self
158 .model()
159 .of_type("IFCMATERIALPROFILESETUSAGE")
160 .chain(self.model().of_type("IFCMATERIALPROFILESETUSAGETAPERING"))
161 .collect();
162 usages.sort_unstable_by_key(|(id, _)| *id);
163 usages
164 .into_iter()
165 .map(move |(id, entity)| MaterialProfileSetUsage::from_known(id, entity, release))
166 }
167
168 pub fn tapering_profile_set_usages(
171 self,
172 ) -> impl Iterator<Item = MaterialProfileSetUsageTapering<'m>> + 'm {
173 let release = self.release();
174 self.model()
175 .of_type("IFCMATERIALPROFILESETUSAGETAPERING")
176 .map(move |(id, entity)| {
177 MaterialProfileSetUsageTapering::from_known(id, entity, release)
178 })
179 }
180}