Skip to main content

ifc_style/light/
source.rs

1//! The `IfcLightSource` supertype and its two attribute-free/minimal subtypes.
2
3use ifc_model::EntityId;
4
5use crate::error::StyleResult;
6use crate::view::Record;
7
8/// Which concrete `IfcLightSource` subtype an entity declares.
9///
10/// Resolution walks the schema's inheritance graph, so a subtype introduced by
11/// a later schema resolves to its nearest known ancestor rather than being
12/// mistaken for an unrelated family. `Spot` is checked before `Positional`
13/// because `IfcLightSourceSpot` *is a* `IfcLightSourcePositional`.
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15#[non_exhaustive]
16pub enum LightSourceKind {
17    /// `IfcLightSourceAmbient`: non-directional fill light.
18    Ambient,
19    /// `IfcLightSourceDirectional`: parallel rays, no position.
20    Directional,
21    /// `IfcLightSourceGoniometric`: photometric light with a distribution curve.
22    Goniometric,
23    /// `IfcLightSourcePositional`: point light with distance attenuation.
24    Positional,
25    /// `IfcLightSourceSpot`: positional light narrowed to a cone.
26    Spot,
27}
28
29/// Borrowed projection of `IfcLightSource`: the colour and intensity every
30/// light shares, independent of how it is positioned or aimed.
31///
32/// `AmbientIntensity` and `Intensity` are both OPTIONAL in all three bundled
33/// schemas. An absent value is reported as `None`, never defaulted to `1.0`:
34/// choosing a fallback is a renderer policy, and inventing one here would make
35/// an unauthored file indistinguishable from one that authored full intensity.
36#[derive(Debug, Clone, Copy)]
37pub struct LightSource<'m, 's> {
38    record: Record<'m, 's>,
39}
40
41impl<'m, 's> LightSource<'m, 's> {
42    pub(crate) fn from_record(record: Record<'m, 's>) -> Self {
43        Self { record }
44    }
45
46    /// The concrete subtype this entity declares.
47    pub fn kind(&self) -> StyleResult<LightSourceKind> {
48        let declared = self.record.type_name();
49        for (name, kind) in [
50            ("IfcLightSourceSpot", LightSourceKind::Spot),
51            ("IfcLightSourceGoniometric", LightSourceKind::Goniometric),
52            ("IfcLightSourcePositional", LightSourceKind::Positional),
53            ("IfcLightSourceDirectional", LightSourceKind::Directional),
54            ("IfcLightSourceAmbient", LightSourceKind::Ambient),
55        ] {
56            if self.record.is_a(declared, name) {
57                return Ok(kind);
58            }
59        }
60        Err(self.record.wrong_type("a concrete IfcLightSource subtype"))
61    }
62
63    /// The `Name` attribute, when authored.
64    pub fn name(&self) -> StyleResult<Option<&'m str>> {
65        self.record.optional_text("Name")
66    }
67
68    /// The `LightColour` attribute: a mandatory `IfcColourRgb` reference.
69    pub fn light_colour(&self) -> StyleResult<EntityId> {
70        self.record.required_ref("LightColour", "IfcColourRgb")
71    }
72
73    /// The `AmbientIntensity` attribute, normalized to `[0, 1]` when authored.
74    pub fn ambient_intensity(&self) -> StyleResult<Option<f64>> {
75        self.record.optional_normalized("AmbientIntensity")
76    }
77
78    /// The `Intensity` attribute, normalized to `[0, 1]` when authored.
79    pub fn intensity(&self) -> StyleResult<Option<f64>> {
80        self.record.optional_normalized("Intensity")
81    }
82}
83
84/// Borrowed projection of `IfcLightSourceAmbient`.
85///
86/// The subtype adds no attributes in any bundled schema; it exists so a file
87/// can say "this is ambient fill" rather than leaving a reader to infer it.
88/// The type is still distinct from [`LightSource`] so that a caller matching
89/// on [`LightSourceKind`] gets a value it cannot accidentally aim or position.
90#[derive(Debug, Clone, Copy)]
91pub struct LightSourceAmbient<'m, 's> {
92    record: Record<'m, 's>,
93}
94
95impl<'m, 's> LightSourceAmbient<'m, 's> {
96    pub(crate) fn from_record(record: Record<'m, 's>) -> Self {
97        Self { record }
98    }
99
100    /// The inherited `IfcLightSource` colour and intensity attributes.
101    pub fn light_source(&self) -> LightSource<'m, 's> {
102        LightSource::from_record(self.record)
103    }
104}
105
106/// Borrowed projection of `IfcLightSourceDirectional`: parallel rays with an
107/// orientation but no position.
108#[derive(Debug, Clone, Copy)]
109pub struct LightSourceDirectional<'m, 's> {
110    record: Record<'m, 's>,
111}
112
113impl<'m, 's> LightSourceDirectional<'m, 's> {
114    pub(crate) fn from_record(record: Record<'m, 's>) -> Self {
115        Self { record }
116    }
117
118    /// The inherited `IfcLightSource` colour and intensity attributes.
119    pub fn light_source(&self) -> LightSource<'m, 's> {
120        LightSource::from_record(self.record)
121    }
122
123    /// The `Orientation` attribute: a mandatory `IfcDirection` reference.
124    ///
125    /// Returned as an id, not a vector. Resolving and normalizing a direction
126    /// is geometry-resource work; this crate references representation items
127    /// without importing geometry types.
128    pub fn orientation(&self) -> StyleResult<EntityId> {
129        self.record.required_ref("Orientation", "IfcDirection")
130    }
131}