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` 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}