Skip to main content

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}