1#![allow(clippy::excessive_precision)]
14
15use core::marker::PhantomData;
16
17use crate::model::WavelengthGrid;
18use crate::observer::{Cie1931, Cie1964, StandardObserver};
19
20pub trait Illuminant: 'static {
26 type Observer: StandardObserver;
28
29 const WHITE_POINT_XY: [f32; 2];
31
32 const WHITE_POINT_XYZ: [f32; 3];
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
41pub struct D65<O: StandardObserver = Cie1931>(PhantomData<O>);
42
43impl Illuminant for D65<Cie1931> {
44 type Observer = Cie1931;
45 const WHITE_POINT_XY: [f32; 2] = [0.3127, 0.3290];
46 const WHITE_POINT_XYZ: [f32; 3] = [0.95047, 1.00000, 1.08883];
47}
48
49impl Illuminant for D65<Cie1964> {
50 type Observer = Cie1964;
51 const WHITE_POINT_XY: [f32; 2] = [0.3138, 0.3310];
53 const WHITE_POINT_XYZ: [f32; 3] = [0.94811, 1.00000, 1.07304];
55}
56
57impl D65<Cie1931> {
58 #[rustfmt::skip]
61 pub const SPD_5NM: &'static [f32; 81] = &[
62 0.499755, 0.523118, 0.546482, 0.687015, 0.827549, 0.871204, 0.914860, 0.924589, 0.934318, 0.900570,
63 0.866823, 0.957736, 1.048650, 1.109360, 1.170080, 1.174100, 1.178120, 1.163360, 1.148610, 1.153920,
64 1.159230, 1.123670, 1.088110, 1.090820, 1.093540, 1.085780, 1.078020, 1.062960, 1.047900, 1.062390,
65 1.076890, 1.060470, 1.044050, 1.042250, 1.040460, 1.020230, 1.000000, 0.981671, 0.963342, 0.960611,
66 0.957880, 0.922368, 0.886856, 0.893459, 0.900062, 0.898026, 0.895991, 0.886489, 0.876987, 0.854936,
67 0.832886, 0.834939, 0.836992, 0.818630, 0.800268, 0.801207, 0.802146, 0.812462, 0.822778, 0.802810,
68 0.782842, 0.740027, 0.697213, 0.706652, 0.716091, 0.729790, 0.743490, 0.679765, 0.616040, 0.657448,
69 0.698856, 0.724862, 0.750869, 0.693398, 0.635927, 0.550054, 0.464182, 0.566118, 0.668054, 0.650942,
70 0.633830,
71 ];
72
73 pub const fn spd<const BANDS: usize, G: WavelengthGrid<BANDS>>() -> [f32; BANDS] {
78 let mut out = [0.0f32; BANDS];
79 let mut i = 0;
80 while i < BANDS {
81 let nm = G::START_NM + i as f32 * G::STEP_NM;
82 if nm >= 380.0 && nm <= 780.0 {
83 let t = (nm - 380.0) / 5.0;
84 let lo = t as usize;
85 let hi = if lo < 80 { lo + 1 } else { 80 };
86 let frac = t - lo as f32;
87 out[i] = Self::SPD_5NM[lo] * (1.0 - frac) + Self::SPD_5NM[hi] * frac;
88 }
89 i += 1;
90 }
91 out
92 }
93}
94
95#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
101pub struct D50<O: StandardObserver = Cie1931>(PhantomData<O>);
102
103impl Illuminant for D50<Cie1931> {
104 type Observer = Cie1931;
105 const WHITE_POINT_XY: [f32; 2] = [0.3457, 0.3585];
106 const WHITE_POINT_XYZ: [f32; 3] = [0.96422, 1.00000, 0.82521];
107}
108
109impl Illuminant for D50<Cie1964> {
110 type Observer = Cie1964;
111 const WHITE_POINT_XY: [f32; 2] = [0.3477, 0.3595];
113 const WHITE_POINT_XYZ: [f32; 3] = [0.96720, 1.00000, 0.81427];
115}
116
117impl D50<Cie1931> {
118 #[rustfmt::skip]
121 pub const SPD_5NM: &'static [f32; 81] = &[
122 0.266024, 0.314300, 0.333033, 0.443867, 0.588167, 0.664700, 0.741233, 0.771244, 0.801267, 0.769711,
123 0.738156, 0.865411, 0.992667, 1.056422, 1.120189, 1.116278, 1.112367, 1.127200, 1.142033, 1.139233,
124 1.136433, 1.111322, 1.086211, 1.089200, 1.092189, 1.083844, 1.075489, 1.064311, 1.053144, 1.067956,
125 1.082767, 1.064311, 1.045833, 1.049189, 1.052544, 1.026278, 1.000000, 0.994489, 0.988978, 0.986311,
126 0.983644, 0.949211, 0.914778, 0.924544, 0.934311, 0.926900, 0.919478, 0.909322, 0.899167, 0.877911,
127 0.856656, 0.858400, 0.860144, 0.844711, 0.829278, 0.829922, 0.830567, 0.839733, 0.848900, 0.830722,
128 0.812533, 0.770511, 0.728478, 0.738467, 0.748456, 0.763511, 0.778578, 0.715111, 0.651644, 0.703344,
129 0.755044, 0.791544, 0.828056, 0.765333, 0.702611, 0.611144, 0.519689, 0.638233, 0.756789, 0.738189,
130 0.719600,
131 ];
132
133 pub const fn spd<const BANDS: usize, G: WavelengthGrid<BANDS>>() -> [f32; BANDS] {
138 let mut out = [0.0f32; BANDS];
139 let mut i = 0;
140 while i < BANDS {
141 let nm = G::START_NM + i as f32 * G::STEP_NM;
142 if nm >= 380.0 && nm <= 780.0 {
143 let t = (nm - 380.0) / 5.0;
144 let lo = t as usize;
145 let hi = if lo < 80 { lo + 1 } else { 80 };
146 let frac = t - lo as f32;
147 out[i] = Self::SPD_5NM[lo] * (1.0 - frac) + Self::SPD_5NM[hi] * frac;
148 }
149 i += 1;
150 }
151 out
152 }
153}
154
155#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
160pub struct D60<O: StandardObserver = Cie1931>(PhantomData<O>);
161
162impl Illuminant for D60<Cie1931> {
163 type Observer = Cie1931;
164 const WHITE_POINT_XY: [f32; 2] = [0.32163, 0.33774];
165 const WHITE_POINT_XYZ: [f32; 3] = [0.95230, 1.00000, 1.00856];
166}
167
168impl Illuminant for D60<Cie1964> {
169 type Observer = Cie1964;
170 const WHITE_POINT_XY: [f32; 2] = [0.3223, 0.3348];
172 const WHITE_POINT_XYZ: [f32; 3] = [0.95002, 1.00000, 1.00350];
174}
175
176#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
181pub struct DciWhite;
182
183impl Illuminant for DciWhite {
184 type Observer = Cie1931;
185 const WHITE_POINT_XY: [f32; 2] = [0.3140, 0.3510];
186 const WHITE_POINT_XYZ: [f32; 3] = [0.89459, 1.00000, 0.95442];
187}
188
189#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
194pub struct AcesWhitePoint;
195
196impl Illuminant for AcesWhitePoint {
197 type Observer = Cie1931;
198 const WHITE_POINT_XY: [f32; 2] = [0.32168, 0.33767];
199 const WHITE_POINT_XYZ: [f32; 3] = [0.95265, 1.00000, 1.00883];
200}