Skip to main content

ifc_style/
view.rs

1//! Shared schema-resolved borrowed view primitives.
2
3mod record;
4
5pub(crate) use record::Record;
6
7use ifc_model::{EntityId, Model};
8use ifc_schema::Schema;
9
10use crate::error::{StyleError, StyleResult};
11use crate::{
12    Annotation, AnnotationFillArea, BlobTexture, ColourRgb, CurveStyle, CurveStyleFont,
13    CurveStyleFontPattern, FillAreaStyle, FillAreaStyleHatching, FillAreaStyleTiles, ImageTexture,
14    IndexedTextureMap, LightDistributionData, LightIntensityDistribution, LightSource,
15    LightSourceAmbient, LightSourceDirectional, LightSourceGoniometric, LightSourcePositional,
16    LightSourceSpot, PixelTexture, PlanarBox, PlanarExtent, PresentationLayer,
17    PresentationStyleAssignment, StyledItem, SurfaceStyle, SurfaceStyleLighting,
18    SurfaceStyleRefraction, SurfaceStyleRendering, SurfaceStyleShading, SurfaceStyleWithTextures,
19    SurfaceTexture, TextLiteral, TextLiteralWithExtent, TextStyle, TextStyleFontModel,
20    TextureCoordinate, TextureVertex, TextureVertexList,
21};
22
23/// Entry point for strict presentation and annotation projections.
24#[derive(Debug, Clone, Copy)]
25pub struct StyleView<'m, 's> {
26    pub(crate) model: &'m Model,
27    pub(crate) schema: &'s Schema,
28}
29
30impl<'m, 's> StyleView<'m, 's> {
31    /// Bind a schema-resolved view over `model` using `schema`'s attribute
32    /// layout and type hierarchy.
33    #[must_use]
34    pub fn new(model: &'m Model, schema: &'s Schema) -> Self {
35        Self { model, schema }
36    }
37
38    /// Project entity `id` as an `IfcAnnotation`.
39    pub fn annotation(&self, id: EntityId) -> StyleResult<Annotation<'m, 's>> {
40        Annotation::from_record(self.record(id, "IfcAnnotation")?)
41    }
42
43    /// Project entity `id` as an `IfcTextLiteral`.
44    pub fn text_literal(&self, id: EntityId) -> StyleResult<TextLiteral<'m, 's>> {
45        TextLiteral::from_record(self.record(id, "IfcTextLiteral")?)
46    }
47
48    /// Project entity `id` as an `IfcTextLiteralWithExtent`.
49    pub fn text_literal_with_extent(
50        &self,
51        id: EntityId,
52    ) -> StyleResult<TextLiteralWithExtent<'m, 's>> {
53        TextLiteralWithExtent::from_record(self.record(id, "IfcTextLiteralWithExtent")?)
54    }
55
56    /// Project entity `id` as an `IfcAnnotationFillArea`.
57    pub fn annotation_fill_area(&self, id: EntityId) -> StyleResult<AnnotationFillArea<'m, 's>> {
58        AnnotationFillArea::from_record(self.record(id, "IfcAnnotationFillArea")?)
59    }
60
61    /// Project entity `id` as an `IfcColourRgb`.
62    pub fn colour_rgb(&self, id: EntityId) -> StyleResult<ColourRgb<'m, 's>> {
63        Ok(ColourRgb::from_record(self.record(id, "IfcColourRgb")?))
64    }
65
66    /// Project entity `id` as an `IfcSurfaceStyleShading`.
67    pub fn surface_style_shading(&self, id: EntityId) -> StyleResult<SurfaceStyleShading<'m, 's>> {
68        Ok(SurfaceStyleShading::from_record(
69            self.record(id, "IfcSurfaceStyleShading")?,
70        ))
71    }
72
73    /// Project entity `id` as an `IfcSurfaceStyle`.
74    pub fn surface_style(&self, id: EntityId) -> StyleResult<SurfaceStyle<'m, 's>> {
75        Ok(SurfaceStyle::from_record(
76            self.record(id, "IfcSurfaceStyle")?,
77        ))
78    }
79
80    /// Project entity `id` as an `IfcStyledItem`.
81    pub fn styled_item(&self, id: EntityId) -> StyleResult<StyledItem<'m, 's>> {
82        Ok(StyledItem::from_record(self.record(id, "IfcStyledItem")?))
83    }
84
85    /// Project entity `id` as an `IfcPresentationLayerAssignment`.
86    pub fn presentation_layer(&self, id: EntityId) -> StyleResult<PresentationLayer<'m, 's>> {
87        Ok(PresentationLayer::from_record(
88            self.record(id, "IfcPresentationLayerAssignment")?,
89        ))
90    }
91
92    /// Project entity `id` as an `IfcSurfaceStyleRendering`.
93    pub fn surface_style_rendering(
94        &self,
95        id: EntityId,
96    ) -> StyleResult<SurfaceStyleRendering<'m, 's>> {
97        Ok(SurfaceStyleRendering::from_record(
98            self.record(id, "IfcSurfaceStyleRendering")?,
99        ))
100    }
101
102    /// Project entity `id` as an `IfcCurveStyle`.
103    pub fn curve_style(&self, id: EntityId) -> StyleResult<CurveStyle<'m, 's>> {
104        Ok(CurveStyle::from_record(self.record(id, "IfcCurveStyle")?))
105    }
106
107    /// Project entity `id` as an `IfcFillAreaStyle`.
108    pub fn fill_area_style(&self, id: EntityId) -> StyleResult<FillAreaStyle<'m, 's>> {
109        Ok(FillAreaStyle::from_record(
110            self.record(id, "IfcFillAreaStyle")?,
111        ))
112    }
113
114    /// Project entity `id` as an `IfcFillAreaStyleHatching`.
115    pub fn fill_area_style_hatching(
116        &self,
117        id: EntityId,
118    ) -> StyleResult<FillAreaStyleHatching<'m, 's>> {
119        Ok(FillAreaStyleHatching::from_record(
120            self.record(id, "IfcFillAreaStyleHatching")?,
121        ))
122    }
123
124    /// Project entity `id` as an `IfcFillAreaStyleTiles`.
125    pub fn fill_area_style_tiles(&self, id: EntityId) -> StyleResult<FillAreaStyleTiles<'m, 's>> {
126        Ok(FillAreaStyleTiles::from_record(
127            self.record(id, "IfcFillAreaStyleTiles")?,
128        ))
129    }
130
131    /// Project entity `id` as an `IfcSurfaceTexture`.
132    pub fn surface_texture(&self, id: EntityId) -> StyleResult<SurfaceTexture<'m, 's>> {
133        Ok(SurfaceTexture::from_record(
134            self.record(id, "IfcSurfaceTexture")?,
135        ))
136    }
137
138    /// Project entity `id` as an `IfcImageTexture`.
139    pub fn image_texture(&self, id: EntityId) -> StyleResult<ImageTexture<'m, 's>> {
140        Ok(ImageTexture::from_record(
141            self.record(id, "IfcImageTexture")?,
142        ))
143    }
144
145    /// Project entity `id` as an `IfcTextureCoordinate`.
146    pub fn texture_coordinate(&self, id: EntityId) -> StyleResult<TextureCoordinate<'m, 's>> {
147        Ok(TextureCoordinate::from_record(
148            self.record(id, "IfcTextureCoordinate")?,
149        ))
150    }
151
152    /// Project entity `id` as an `IfcIndexedTextureMap`.
153    pub fn indexed_texture_map(&self, id: EntityId) -> StyleResult<IndexedTextureMap<'m, 's>> {
154        Ok(IndexedTextureMap::from_record(
155            self.record(id, "IfcIndexedTextureMap")?,
156        ))
157    }
158
159    /// Project entity `id` as an `IfcTextStyle`.
160    pub fn text_style(&self, id: EntityId) -> StyleResult<TextStyle<'m, 's>> {
161        Ok(TextStyle::from_record(self.record(id, "IfcTextStyle")?))
162    }
163
164    /// Project entity `id` as an `IfcTextStyleFontModel`.
165    pub fn text_style_font_model(&self, id: EntityId) -> StyleResult<TextStyleFontModel<'m, 's>> {
166        Ok(TextStyleFontModel::from_record(
167            self.record(id, "IfcTextStyleFontModel")?,
168        ))
169    }
170
171    /// Project entity `id` as an IFC2x3 `IfcPresentationStyleAssignment`.
172    pub fn presentation_style_assignment(
173        &self,
174        id: EntityId,
175    ) -> StyleResult<PresentationStyleAssignment<'m, 's>> {
176        Ok(PresentationStyleAssignment::from_record(
177            self.record(id, "IfcPresentationStyleAssignment")?,
178        ))
179    }
180
181    /// Project entity `id` as an `IfcSurfaceStyleLighting`.
182    pub fn surface_style_lighting(
183        &self,
184        id: EntityId,
185    ) -> StyleResult<SurfaceStyleLighting<'m, 's>> {
186        Ok(SurfaceStyleLighting::from_record(
187            self.record(id, "IfcSurfaceStyleLighting")?,
188        ))
189    }
190
191    /// Project entity `id` as an `IfcSurfaceStyleRefraction`.
192    pub fn surface_style_refraction(
193        &self,
194        id: EntityId,
195    ) -> StyleResult<SurfaceStyleRefraction<'m, 's>> {
196        Ok(SurfaceStyleRefraction::from_record(
197            self.record(id, "IfcSurfaceStyleRefraction")?,
198        ))
199    }
200
201    /// Project entity `id` as an `IfcSurfaceStyleWithTextures`.
202    pub fn surface_style_with_textures(
203        &self,
204        id: EntityId,
205    ) -> StyleResult<SurfaceStyleWithTextures<'m, 's>> {
206        Ok(SurfaceStyleWithTextures::from_record(
207            self.record(id, "IfcSurfaceStyleWithTextures")?,
208        ))
209    }
210
211    /// Project entity `id` as an `IfcCurveStyleFont`.
212    pub fn curve_style_font(&self, id: EntityId) -> StyleResult<CurveStyleFont<'m, 's>> {
213        Ok(CurveStyleFont::from_record(
214            self.record(id, "IfcCurveStyleFont")?,
215        ))
216    }
217
218    /// Project entity `id` as an `IfcCurveStyleFontPattern`.
219    pub fn curve_style_font_pattern(
220        &self,
221        id: EntityId,
222    ) -> StyleResult<CurveStyleFontPattern<'m, 's>> {
223        Ok(CurveStyleFontPattern::from_record(
224            self.record(id, "IfcCurveStyleFontPattern")?,
225        ))
226    }
227
228    /// Project entity `id` as an `IfcBlobTexture`.
229    pub fn blob_texture(&self, id: EntityId) -> StyleResult<BlobTexture<'m, 's>> {
230        Ok(BlobTexture::from_record(self.record(id, "IfcBlobTexture")?))
231    }
232
233    /// Project entity `id` as an `IfcPixelTexture`.
234    pub fn pixel_texture(&self, id: EntityId) -> StyleResult<PixelTexture<'m, 's>> {
235        Ok(PixelTexture::from_record(
236            self.record(id, "IfcPixelTexture")?,
237        ))
238    }
239
240    /// Project entity `id` as an `IfcTextureVertex`.
241    pub fn texture_vertex(&self, id: EntityId) -> StyleResult<TextureVertex<'m, 's>> {
242        Ok(TextureVertex::from_record(
243            self.record(id, "IfcTextureVertex")?,
244        ))
245    }
246
247    /// Project entity `id` as an `IfcTextureVertexList`.
248    pub fn texture_vertex_list(&self, id: EntityId) -> StyleResult<TextureVertexList<'m, 's>> {
249        Ok(TextureVertexList::from_record(
250            self.record(id, "IfcTextureVertexList")?,
251        ))
252    }
253
254    /// Project entity `id` as an `IfcLightSource`, whatever its subtype.
255    ///
256    /// Use this when only the shared colour/intensity attributes are needed;
257    /// call [`LightSource::kind`] to decide which concrete projection below
258    /// to reach for.
259    pub fn light_source(&self, id: EntityId) -> StyleResult<LightSource<'m, 's>> {
260        Ok(LightSource::from_record(self.record(id, "IfcLightSource")?))
261    }
262
263    /// Project entity `id` as an `IfcLightSourceAmbient`.
264    pub fn light_source_ambient(&self, id: EntityId) -> StyleResult<LightSourceAmbient<'m, 's>> {
265        Ok(LightSourceAmbient::from_record(
266            self.record(id, "IfcLightSourceAmbient")?,
267        ))
268    }
269
270    /// Project entity `id` as an `IfcLightSourceDirectional`.
271    pub fn light_source_directional(
272        &self,
273        id: EntityId,
274    ) -> StyleResult<LightSourceDirectional<'m, 's>> {
275        Ok(LightSourceDirectional::from_record(
276            self.record(id, "IfcLightSourceDirectional")?,
277        ))
278    }
279
280    /// Project entity `id` as an `IfcLightSourceGoniometric`.
281    pub fn light_source_goniometric(
282        &self,
283        id: EntityId,
284    ) -> StyleResult<LightSourceGoniometric<'m, 's>> {
285        Ok(LightSourceGoniometric::from_record(
286            self.record(id, "IfcLightSourceGoniometric")?,
287        ))
288    }
289
290    /// Project entity `id` as an `IfcLightSourcePositional`.
291    ///
292    /// Accepts an `IfcLightSourceSpot` too, since a spot *is a* positional
293    /// light; use [`Self::light_source_spot`] to reach the cone attributes.
294    pub fn light_source_positional(
295        &self,
296        id: EntityId,
297    ) -> StyleResult<LightSourcePositional<'m, 's>> {
298        Ok(LightSourcePositional::from_record(
299            self.record(id, "IfcLightSourcePositional")?,
300        ))
301    }
302
303    /// Project entity `id` as an `IfcLightSourceSpot`.
304    pub fn light_source_spot(&self, id: EntityId) -> StyleResult<LightSourceSpot<'m, 's>> {
305        Ok(LightSourceSpot::from_record(
306            self.record(id, "IfcLightSourceSpot")?,
307        ))
308    }
309
310    /// Project entity `id` as an `IfcLightIntensityDistribution`.
311    pub fn light_intensity_distribution(
312        &self,
313        id: EntityId,
314    ) -> StyleResult<LightIntensityDistribution<'m, 's>> {
315        Ok(LightIntensityDistribution::from_record(
316            self.record(id, "IfcLightIntensityDistribution")?,
317        ))
318    }
319
320    /// Project entity `id` as an `IfcLightDistributionData`.
321    pub fn light_distribution_data(
322        &self,
323        id: EntityId,
324    ) -> StyleResult<LightDistributionData<'m, 's>> {
325        Ok(LightDistributionData::from_record(
326            self.record(id, "IfcLightDistributionData")?,
327        ))
328    }
329
330    /// Project entity `id` as an `IfcPlanarExtent`.
331    ///
332    /// Accepts an `IfcPlanarBox` too, since a box *is a* planar extent; use
333    /// [`Self::planar_box`] to reach its placement.
334    pub fn planar_extent(&self, id: EntityId) -> StyleResult<PlanarExtent<'m, 's>> {
335        Ok(PlanarExtent::from_record(
336            self.record(id, "IfcPlanarExtent")?,
337        ))
338    }
339
340    /// Project entity `id` as an `IfcPlanarBox`.
341    pub fn planar_box(&self, id: EntityId) -> StyleResult<PlanarBox<'m, 's>> {
342        Ok(PlanarBox::from_record(self.record(id, "IfcPlanarBox")?))
343    }
344
345    pub(crate) fn record(
346        &self,
347        id: EntityId,
348        expected: &'static str,
349    ) -> StyleResult<Record<'m, 's>> {
350        if self.schema.entity(expected).is_none() {
351            return Err(StyleError::UnsupportedEntity {
352                schema: self.schema.name().to_owned(),
353                entity: expected,
354            });
355        }
356        let entity = self.model.get(id).ok_or(StyleError::UnknownEntity { id })?;
357        if !self.schema.is_a(&entity.type_name, expected) {
358            return Err(StyleError::WrongEntityType {
359                id,
360                expected,
361                actual: entity.type_name.to_string(),
362            });
363        }
364        Ok(Record {
365            id,
366            entity,
367            model: self.model,
368            schema: self.schema,
369        })
370    }
371}