ifc_geometry/input/profile/types.rs
1//! The public shape of a profile read in SI units.
2//!
3//! Every length is in metres and every plane angle in radians. The reader in
4//! [`super`] converts each value exactly once, so a caller never applies
5//! [`crate::units::UnitScale`] to these numbers again.
6//!
7//! Optional IFC attributes stay `Option`: the schema gives some of them a
8//! default (`IfcLShapeProfileDef.Width` defaults to `Depth`,
9//! `IfcAsymmetricIShapeProfileDef.TopFlangeThickness` to the bottom flange),
10//! and reporting the default as if it were authored would hide what the file
11//! actually says. Each field names its default where the schema has one.
12
13use ifc_model::EntityId;
14
15/// One `IfcProfileDef`, resolved into its family parameters.
16///
17/// Produced by [`super::describe_profile`]. `lower::profile` builds its exact
18/// neutral profile from this value, so the numbers a rule check reads are the
19/// numbers the kernel receives.
20#[derive(Debug, Clone, PartialEq)]
21#[non_exhaustive]
22pub struct ProfileDescription {
23 /// The `IfcProfileDef` entity.
24 pub entity: EntityId,
25 /// Its concrete IFC type in upper case, e.g. `IFCISHAPEPROFILEDEF`.
26 pub type_name: String,
27 /// `ProfileName`, often a catalogue designation such as `HEA300`.
28 pub name: Option<String>,
29 /// `IfcParameterizedProfileDef.Position`, in metres.
30 ///
31 /// `None` when the file omits it (the identity) and for the families
32 /// that have no such attribute (arbitrary, composite, derived).
33 pub position: Option<ProfilePosition>,
34 /// The family and its parameters.
35 pub parameters: ProfileParameters,
36}
37
38impl ProfileDescription {
39 /// Does this profile bound an area?
40 ///
41 /// `false` when the profile, or any member or parent it is built from, is
42 /// an `IfcArbitraryOpenProfileDef`. An open curve bounds nothing, so it
43 /// cannot be the cross section of a swept solid.
44 pub fn bounds_area(&self) -> bool {
45 match &self.parameters {
46 ProfileParameters::ArbitraryOpen { .. } => false,
47 ProfileParameters::Composite { profiles, .. } => {
48 profiles.iter().all(ProfileDescription::bounds_area)
49 }
50 ProfileParameters::Derived { parent, .. }
51 | ProfileParameters::Mirrored { parent, .. } => parent.bounds_area(),
52 _ => true,
53 }
54 }
55}
56
57/// `IfcParameterizedProfileDef.Position`: where the parameterised section sits
58/// in the profile plane.
59#[derive(Debug, Clone, Copy, PartialEq)]
60#[non_exhaustive]
61pub struct ProfilePosition {
62 /// `Location`, in metres.
63 pub origin: [f64; 2],
64 /// `RefDirection`, normalised; `[1, 0]` when omitted.
65 pub x_axis: [f64; 2],
66}
67
68impl ProfilePosition {
69 /// The local Y axis: X rotated a quarter turn counter-clockwise
70 /// (`IfcOrthogonalComplement`).
71 pub fn y_axis(&self) -> [f64; 2] {
72 [-self.x_axis[1], self.x_axis[0]]
73 }
74}
75
76/// `IfcDerivedProfileDef.Operator`: a 2D affine map applied to the parent.
77///
78/// The axes carry the operator's scale, so they are not necessarily unit
79/// length. The translation is in metres.
80#[derive(Debug, Clone, Copy, PartialEq)]
81#[non_exhaustive]
82pub struct ProfileOperator {
83 /// Image of the parent's X axis.
84 pub x_axis: [f64; 2],
85 /// Image of the parent's Y axis.
86 pub y_axis: [f64; 2],
87 /// Translation, in metres.
88 pub origin: [f64; 2],
89}
90
91/// The parameters of one profile family, in metres and radians.
92///
93/// Names follow the IFC attributes in snake case. Variants are
94/// `#[non_exhaustive]`, so a match must end in `..`; a future attribute can be
95/// added without breaking a consumer.
96#[derive(Debug, Clone, PartialEq)]
97#[non_exhaustive]
98pub enum ProfileParameters {
99 /// `IfcRectangleProfileDef`.
100 #[non_exhaustive]
101 Rectangle {
102 /// `XDim`.
103 x_dim: f64,
104 /// `YDim`.
105 y_dim: f64,
106 },
107 /// `IfcRoundedRectangleProfileDef`.
108 #[non_exhaustive]
109 RoundedRectangle {
110 /// `XDim`.
111 x_dim: f64,
112 /// `YDim`.
113 y_dim: f64,
114 /// `RoundingRadius`.
115 rounding_radius: f64,
116 },
117 /// `IfcRectangleHollowProfileDef`.
118 #[non_exhaustive]
119 RectangleHollow {
120 /// `XDim`.
121 x_dim: f64,
122 /// `YDim`.
123 y_dim: f64,
124 /// `WallThickness`.
125 wall_thickness: f64,
126 /// `InnerFilletRadius`.
127 inner_fillet_radius: Option<f64>,
128 /// `OuterFilletRadius`.
129 outer_fillet_radius: Option<f64>,
130 },
131 /// `IfcCircleProfileDef`.
132 #[non_exhaustive]
133 Circle {
134 /// `Radius`.
135 radius: f64,
136 },
137 /// `IfcCircleHollowProfileDef`.
138 #[non_exhaustive]
139 CircleHollow {
140 /// `Radius`, to the outside of the wall.
141 radius: f64,
142 /// `WallThickness`.
143 wall_thickness: f64,
144 },
145 /// `IfcEllipseProfileDef`.
146 #[non_exhaustive]
147 Ellipse {
148 /// `SemiAxis1`, along the position's X axis.
149 semi_axis_1: f64,
150 /// `SemiAxis2`, along the position's Y axis.
151 semi_axis_2: f64,
152 },
153 /// `IfcIShapeProfileDef`.
154 #[non_exhaustive]
155 IShape {
156 /// `OverallWidth`.
157 overall_width: f64,
158 /// `OverallDepth`.
159 overall_depth: f64,
160 /// `WebThickness`.
161 web_thickness: f64,
162 /// `FlangeThickness`.
163 flange_thickness: f64,
164 /// `FilletRadius`.
165 fillet_radius: Option<f64>,
166 /// `FlangeEdgeRadius`.
167 flange_edge_radius: Option<f64>,
168 /// `FlangeSlope`, in radians.
169 flange_slope: Option<f64>,
170 },
171 /// `IfcAsymmetricIShapeProfileDef`.
172 #[non_exhaustive]
173 AsymmetricIShape {
174 /// `BottomFlangeWidth` (`OverallWidth` in IFC2X3).
175 bottom_flange_width: f64,
176 /// `OverallDepth`.
177 overall_depth: f64,
178 /// `WebThickness`.
179 web_thickness: f64,
180 /// `BottomFlangeThickness` (`FlangeThickness` in IFC2X3).
181 bottom_flange_thickness: f64,
182 /// `BottomFlangeFilletRadius` (`FilletRadius` in IFC2X3).
183 bottom_flange_fillet_radius: Option<f64>,
184 /// `TopFlangeWidth`.
185 top_flange_width: f64,
186 /// `TopFlangeThickness`; defaults to the bottom flange thickness.
187 top_flange_thickness: Option<f64>,
188 /// `TopFlangeFilletRadius`.
189 top_flange_fillet_radius: Option<f64>,
190 /// `BottomFlangeEdgeRadius`; not declared in IFC2X3.
191 bottom_flange_edge_radius: Option<f64>,
192 /// `BottomFlangeSlope`, in radians; not declared in IFC2X3.
193 bottom_flange_slope: Option<f64>,
194 /// `TopFlangeEdgeRadius`; not declared in IFC2X3.
195 top_flange_edge_radius: Option<f64>,
196 /// `TopFlangeSlope`, in radians; not declared in IFC2X3.
197 top_flange_slope: Option<f64>,
198 },
199 /// `IfcLShapeProfileDef`.
200 #[non_exhaustive]
201 LShape {
202 /// `Depth`.
203 depth: f64,
204 /// `Width`; defaults to `Depth` (an equal-leg angle).
205 width: Option<f64>,
206 /// `Thickness`.
207 thickness: f64,
208 /// `FilletRadius`.
209 fillet_radius: Option<f64>,
210 /// `EdgeRadius`.
211 edge_radius: Option<f64>,
212 /// `LegSlope`, in radians.
213 leg_slope: Option<f64>,
214 },
215 /// `IfcTShapeProfileDef`.
216 #[non_exhaustive]
217 TShape {
218 /// `Depth`.
219 depth: f64,
220 /// `FlangeWidth`.
221 flange_width: f64,
222 /// `WebThickness`.
223 web_thickness: f64,
224 /// `FlangeThickness`.
225 flange_thickness: f64,
226 /// `FilletRadius`.
227 fillet_radius: Option<f64>,
228 /// `FlangeEdgeRadius`.
229 flange_edge_radius: Option<f64>,
230 /// `WebEdgeRadius`.
231 web_edge_radius: Option<f64>,
232 /// `WebSlope`, in radians.
233 web_slope: Option<f64>,
234 /// `FlangeSlope`, in radians.
235 flange_slope: Option<f64>,
236 },
237 /// `IfcUShapeProfileDef`.
238 #[non_exhaustive]
239 UShape {
240 /// `Depth`.
241 depth: f64,
242 /// `FlangeWidth`.
243 flange_width: f64,
244 /// `WebThickness`.
245 web_thickness: f64,
246 /// `FlangeThickness`.
247 flange_thickness: f64,
248 /// `FilletRadius`.
249 fillet_radius: Option<f64>,
250 /// `EdgeRadius`.
251 edge_radius: Option<f64>,
252 /// `FlangeSlope`, in radians.
253 flange_slope: Option<f64>,
254 },
255 /// `IfcCShapeProfileDef`.
256 #[non_exhaustive]
257 CShape {
258 /// `Depth`.
259 depth: f64,
260 /// `Width`.
261 width: f64,
262 /// `WallThickness`.
263 wall_thickness: f64,
264 /// `Girth`: the returned lip.
265 girth: f64,
266 /// `InternalFilletRadius`.
267 internal_fillet_radius: Option<f64>,
268 },
269 /// `IfcZShapeProfileDef`.
270 #[non_exhaustive]
271 ZShape {
272 /// `Depth`.
273 depth: f64,
274 /// `FlangeWidth`.
275 flange_width: f64,
276 /// `WebThickness`.
277 web_thickness: f64,
278 /// `FlangeThickness`.
279 flange_thickness: f64,
280 /// `FilletRadius`.
281 fillet_radius: Option<f64>,
282 /// `EdgeRadius`.
283 edge_radius: Option<f64>,
284 },
285 /// `IfcTrapeziumProfileDef`.
286 #[non_exhaustive]
287 Trapezium {
288 /// `BottomXDim`.
289 bottom_x_dim: f64,
290 /// `TopXDim`.
291 top_x_dim: f64,
292 /// `YDim`.
293 y_dim: f64,
294 /// `TopXOffset`; may be negative.
295 top_x_offset: f64,
296 },
297 /// `IfcArbitraryClosedProfileDef`: an area bounded by a curve.
298 ///
299 /// The boundary is reported by reference; reading its geometry is a curve
300 /// question, not a profile one.
301 #[non_exhaustive]
302 ArbitraryClosed {
303 /// `OuterCurve`.
304 outer_curve: EntityId,
305 },
306 /// `IfcArbitraryProfileDefWithVoids`.
307 #[non_exhaustive]
308 ArbitraryWithVoids {
309 /// `OuterCurve`.
310 outer_curve: EntityId,
311 /// `InnerCurves`, one per void.
312 inner_curves: Vec<EntityId>,
313 },
314 /// `IfcArbitraryOpenProfileDef`: an open curve. Bounds no area.
315 #[non_exhaustive]
316 ArbitraryOpen {
317 /// `Curve`.
318 curve: EntityId,
319 },
320 /// `IfcCenterLineProfileDef`: a path thickened symmetrically.
321 #[non_exhaustive]
322 CenterLine {
323 /// `Curve`, the centre line.
324 curve: EntityId,
325 /// `Thickness`: the FULL width across the path.
326 thickness: f64,
327 },
328 /// `IfcCompositeProfileDef`: an ordered set of member profiles.
329 #[non_exhaustive]
330 Composite {
331 /// `Profiles`, in authored order.
332 profiles: Vec<ProfileDescription>,
333 /// `Label`.
334 label: Option<String>,
335 },
336 /// `IfcDerivedProfileDef`: a parent profile under a 2D operator.
337 #[non_exhaustive]
338 Derived {
339 /// `ParentProfile`.
340 parent: Box<ProfileDescription>,
341 /// `Operator`, with its translation in metres.
342 operator: ProfileOperator,
343 /// `Label`.
344 label: Option<String>,
345 },
346 /// `IfcMirroredProfileDef`: the parent mirrored about its local Y axis.
347 ///
348 /// The schema derives the operator, so the file carries none; the mirror
349 /// is implied by the type alone.
350 #[non_exhaustive]
351 Mirrored {
352 /// `ParentProfile`.
353 parent: Box<ProfileDescription>,
354 /// `Label`.
355 label: Option<String>,
356 },
357}