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}