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` or an `IfcOpenCrossProfileDef`. An open
43 /// curve bounds nothing, so it cannot be the cross section of a swept
44 /// solid.
45 pub fn bounds_area(&self) -> bool {
46 match &self.parameters {
47 ProfileParameters::ArbitraryOpen { .. } | ProfileParameters::OpenCross { .. } => false,
48 ProfileParameters::Composite { profiles, .. } => {
49 profiles.iter().all(ProfileDescription::bounds_area)
50 }
51 ProfileParameters::Derived { parent, .. }
52 | ProfileParameters::Mirrored { parent, .. } => parent.bounds_area(),
53 _ => true,
54 }
55 }
56}
57
58/// `IfcParameterizedProfileDef.Position`: where the parameterised section sits
59/// in the profile plane.
60#[derive(Debug, Clone, Copy, PartialEq)]
61#[non_exhaustive]
62pub struct ProfilePosition {
63 /// `Location`, in metres.
64 pub origin: [f64; 2],
65 /// `RefDirection`, normalised; `[1, 0]` when omitted.
66 pub x_axis: [f64; 2],
67}
68
69impl ProfilePosition {
70 /// The local Y axis: X rotated a quarter turn counter-clockwise
71 /// (`IfcOrthogonalComplement`).
72 pub fn y_axis(&self) -> [f64; 2] {
73 [-self.x_axis[1], self.x_axis[0]]
74 }
75}
76
77/// `IfcDerivedProfileDef.Operator`: a 2D affine map applied to the parent.
78///
79/// The axes carry the operator's scale, so they are not necessarily unit
80/// length. The translation is in metres.
81#[derive(Debug, Clone, Copy, PartialEq)]
82#[non_exhaustive]
83pub struct ProfileOperator {
84 /// Image of the parent's X axis.
85 pub x_axis: [f64; 2],
86 /// Image of the parent's Y axis.
87 pub y_axis: [f64; 2],
88 /// Translation, in metres.
89 pub origin: [f64; 2],
90}
91
92/// The parameters of one profile family, in metres and radians.
93///
94/// Names follow the IFC attributes in snake case. Variants are
95/// `#[non_exhaustive]`, so a match must end in `..`; a future attribute can be
96/// added without breaking a consumer.
97#[derive(Debug, Clone, PartialEq)]
98#[non_exhaustive]
99pub enum ProfileParameters {
100 /// `IfcRectangleProfileDef`.
101 #[non_exhaustive]
102 Rectangle {
103 /// `XDim`.
104 x_dim: f64,
105 /// `YDim`.
106 y_dim: f64,
107 },
108 /// `IfcRoundedRectangleProfileDef`.
109 #[non_exhaustive]
110 RoundedRectangle {
111 /// `XDim`.
112 x_dim: f64,
113 /// `YDim`.
114 y_dim: f64,
115 /// `RoundingRadius`.
116 rounding_radius: f64,
117 },
118 /// `IfcRectangleHollowProfileDef`.
119 #[non_exhaustive]
120 RectangleHollow {
121 /// `XDim`.
122 x_dim: f64,
123 /// `YDim`.
124 y_dim: f64,
125 /// `WallThickness`.
126 wall_thickness: f64,
127 /// `InnerFilletRadius`.
128 inner_fillet_radius: Option<f64>,
129 /// `OuterFilletRadius`.
130 outer_fillet_radius: Option<f64>,
131 },
132 /// `IfcCircleProfileDef`.
133 #[non_exhaustive]
134 Circle {
135 /// `Radius`.
136 radius: f64,
137 },
138 /// `IfcCircleHollowProfileDef`.
139 #[non_exhaustive]
140 CircleHollow {
141 /// `Radius`, to the outside of the wall.
142 radius: f64,
143 /// `WallThickness`.
144 wall_thickness: f64,
145 },
146 /// `IfcEllipseProfileDef`.
147 #[non_exhaustive]
148 Ellipse {
149 /// `SemiAxis1`, along the position's X axis.
150 semi_axis_1: f64,
151 /// `SemiAxis2`, along the position's Y axis.
152 semi_axis_2: f64,
153 },
154 /// `IfcIShapeProfileDef`.
155 #[non_exhaustive]
156 IShape {
157 /// `OverallWidth`.
158 overall_width: f64,
159 /// `OverallDepth`.
160 overall_depth: f64,
161 /// `WebThickness`.
162 web_thickness: f64,
163 /// `FlangeThickness`.
164 flange_thickness: f64,
165 /// `FilletRadius`.
166 fillet_radius: Option<f64>,
167 /// `FlangeEdgeRadius`.
168 flange_edge_radius: Option<f64>,
169 /// `FlangeSlope`, in radians.
170 flange_slope: Option<f64>,
171 },
172 /// `IfcAsymmetricIShapeProfileDef`.
173 #[non_exhaustive]
174 AsymmetricIShape {
175 /// `BottomFlangeWidth` (`OverallWidth` in IFC2X3).
176 bottom_flange_width: f64,
177 /// `OverallDepth`.
178 overall_depth: f64,
179 /// `WebThickness`.
180 web_thickness: f64,
181 /// `BottomFlangeThickness` (`FlangeThickness` in IFC2X3).
182 bottom_flange_thickness: f64,
183 /// `BottomFlangeFilletRadius` (`FilletRadius` in IFC2X3).
184 bottom_flange_fillet_radius: Option<f64>,
185 /// `TopFlangeWidth`.
186 top_flange_width: f64,
187 /// `TopFlangeThickness`; defaults to the bottom flange thickness.
188 top_flange_thickness: Option<f64>,
189 /// `TopFlangeFilletRadius`.
190 top_flange_fillet_radius: Option<f64>,
191 /// `BottomFlangeEdgeRadius`; not declared in IFC2X3.
192 bottom_flange_edge_radius: Option<f64>,
193 /// `BottomFlangeSlope`, in radians; not declared in IFC2X3.
194 bottom_flange_slope: Option<f64>,
195 /// `TopFlangeEdgeRadius`; not declared in IFC2X3.
196 top_flange_edge_radius: Option<f64>,
197 /// `TopFlangeSlope`, in radians; not declared in IFC2X3.
198 top_flange_slope: Option<f64>,
199 },
200 /// `IfcLShapeProfileDef`.
201 #[non_exhaustive]
202 LShape {
203 /// `Depth`.
204 depth: f64,
205 /// `Width`; defaults to `Depth` (an equal-leg angle).
206 width: Option<f64>,
207 /// `Thickness`.
208 thickness: f64,
209 /// `FilletRadius`.
210 fillet_radius: Option<f64>,
211 /// `EdgeRadius`.
212 edge_radius: Option<f64>,
213 /// `LegSlope`, in radians.
214 leg_slope: Option<f64>,
215 },
216 /// `IfcTShapeProfileDef`.
217 #[non_exhaustive]
218 TShape {
219 /// `Depth`.
220 depth: f64,
221 /// `FlangeWidth`.
222 flange_width: f64,
223 /// `WebThickness`.
224 web_thickness: f64,
225 /// `FlangeThickness`.
226 flange_thickness: f64,
227 /// `FilletRadius`.
228 fillet_radius: Option<f64>,
229 /// `FlangeEdgeRadius`.
230 flange_edge_radius: Option<f64>,
231 /// `WebEdgeRadius`.
232 web_edge_radius: Option<f64>,
233 /// `WebSlope`, in radians.
234 web_slope: Option<f64>,
235 /// `FlangeSlope`, in radians.
236 flange_slope: Option<f64>,
237 },
238 /// `IfcUShapeProfileDef`.
239 #[non_exhaustive]
240 UShape {
241 /// `Depth`.
242 depth: f64,
243 /// `FlangeWidth`.
244 flange_width: f64,
245 /// `WebThickness`.
246 web_thickness: f64,
247 /// `FlangeThickness`.
248 flange_thickness: f64,
249 /// `FilletRadius`.
250 fillet_radius: Option<f64>,
251 /// `EdgeRadius`.
252 edge_radius: Option<f64>,
253 /// `FlangeSlope`, in radians.
254 flange_slope: Option<f64>,
255 },
256 /// `IfcCShapeProfileDef`.
257 #[non_exhaustive]
258 CShape {
259 /// `Depth`.
260 depth: f64,
261 /// `Width`.
262 width: f64,
263 /// `WallThickness`.
264 wall_thickness: f64,
265 /// `Girth`: the returned lip.
266 girth: f64,
267 /// `InternalFilletRadius`.
268 internal_fillet_radius: Option<f64>,
269 },
270 /// `IfcZShapeProfileDef`.
271 #[non_exhaustive]
272 ZShape {
273 /// `Depth`.
274 depth: f64,
275 /// `FlangeWidth`.
276 flange_width: f64,
277 /// `WebThickness`.
278 web_thickness: f64,
279 /// `FlangeThickness`.
280 flange_thickness: f64,
281 /// `FilletRadius`.
282 fillet_radius: Option<f64>,
283 /// `EdgeRadius`.
284 edge_radius: Option<f64>,
285 },
286 /// `IfcTrapeziumProfileDef`.
287 #[non_exhaustive]
288 Trapezium {
289 /// `BottomXDim`.
290 bottom_x_dim: f64,
291 /// `TopXDim`.
292 top_x_dim: f64,
293 /// `YDim`.
294 y_dim: f64,
295 /// `TopXOffset`; may be negative.
296 top_x_offset: f64,
297 },
298 /// `IfcArbitraryClosedProfileDef`: an area bounded by a curve.
299 ///
300 /// The boundary is reported by reference; reading its geometry is a curve
301 /// question, not a profile one.
302 #[non_exhaustive]
303 ArbitraryClosed {
304 /// `OuterCurve`.
305 outer_curve: EntityId,
306 },
307 /// `IfcArbitraryProfileDefWithVoids`.
308 #[non_exhaustive]
309 ArbitraryWithVoids {
310 /// `OuterCurve`.
311 outer_curve: EntityId,
312 /// `InnerCurves`, one per void.
313 inner_curves: Vec<EntityId>,
314 },
315 /// `IfcArbitraryOpenProfileDef`: an open curve. Bounds no area.
316 #[non_exhaustive]
317 ArbitraryOpen {
318 /// `Curve`.
319 curve: EntityId,
320 },
321 /// `IfcOpenCrossProfileDef` (IFC4X3): an open chain of straight segments,
322 /// each a width and a slope, starting at `offset_point`. Bounds no area.
323 ///
324 /// The values are the authored ones in SI units; the lowering derives the
325 /// chain's vertices from them and documents that construction.
326 #[non_exhaustive]
327 OpenCross {
328 /// `HorizontalWidths`: `true` when each width is the horizontal run
329 /// of its segment, `false` when it is the length along the slope.
330 horizontal_widths: bool,
331 /// `Widths`, in metres, one per segment.
332 widths: Vec<f64>,
333 /// `Slopes`, in radians, one per segment; positive rises along +X.
334 slopes: Vec<f64>,
335 /// `Tags`, one per point (one more than there are segments), when
336 /// authored. They pair points across consecutive sections of a
337 /// sectioned sweep and do not change this profile's shape.
338 tags: Option<Vec<String>>,
339 /// `OffsetPoint`, in metres. `None` means the chain starts at the
340 /// profile origin.
341 offset_point: Option<[f64; 2]>,
342 },
343 /// `IfcCenterLineProfileDef`: a path thickened symmetrically.
344 #[non_exhaustive]
345 CenterLine {
346 /// `Curve`, the centre line.
347 curve: EntityId,
348 /// `Thickness`: the FULL width across the path.
349 thickness: f64,
350 },
351 /// `IfcCompositeProfileDef`: an ordered set of member profiles.
352 #[non_exhaustive]
353 Composite {
354 /// `Profiles`, in authored order.
355 profiles: Vec<ProfileDescription>,
356 /// `Label`.
357 label: Option<String>,
358 },
359 /// `IfcDerivedProfileDef`: a parent profile under a 2D operator.
360 #[non_exhaustive]
361 Derived {
362 /// `ParentProfile`.
363 parent: Box<ProfileDescription>,
364 /// `Operator`, with its translation in metres.
365 operator: ProfileOperator,
366 /// `Label`.
367 label: Option<String>,
368 },
369 /// `IfcMirroredProfileDef`: the parent mirrored about its local Y axis.
370 ///
371 /// The schema derives the operator, so the file carries none; the mirror
372 /// is implied by the type alone.
373 #[non_exhaustive]
374 Mirrored {
375 /// `ParentProfile`.
376 parent: Box<ProfileDescription>,
377 /// `Label`.
378 label: Option<String>,
379 },
380}