use std::{
borrow::Cow,
iter::Sum,
ops::{Deref, Mul},
};
use crate::{observer::Observer, rgb::Rgb, spectrum::Spectrum, traits::Light};
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Stimulus(pub(crate) Spectrum);
impl Deref for Stimulus {
type Target = Spectrum;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Stimulus {
pub const fn new(spectrum: Spectrum) -> Self {
Self(spectrum)
}
pub fn set_luminance(mut self, obs: Observer, luminance: f64) -> Self {
let y = obs.y_from_spectrum(self.as_ref());
let l = luminance / y;
self.0 .0.iter_mut().for_each(|v| *v *= l);
self
}
pub fn from_srgb(r_u8: u8, g_u8: u8, b_u8: u8) -> Self {
let rgb = Rgb::from_u8(
r_u8,
g_u8,
b_u8,
Some(crate::observer::Observer::Cie1931),
Some(crate::rgb::RgbSpace::SRGB),
);
rgb.into()
}
pub fn from_rgb(rgb: Rgb) -> Self {
rgb.into()
}
}
impl Light for Stimulus {
fn spectrum(&self) -> Cow<'_, Spectrum> {
Cow::Borrowed(self)
}
}
impl Sum for Stimulus {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
let mut s = Spectrum::default();
iter.for_each(|si| s += si.0);
Stimulus(s)
}
}
impl From<Rgb> for Stimulus {
fn from(rgb: Rgb) -> Self {
let prim = rgb.space.primaries();
let rgb2xyz = rgb.observer.rgb2xyz_matrix(rgb.space);
let yrgb = rgb2xyz.row(1);
rgb.rgb
.iter()
.zip(yrgb.iter())
.zip(prim.iter())
.map(|((v, w), s)| *v * *w * s.clone())
.sum()
}
}
impl Mul<f64> for Stimulus {
type Output = Self;
fn mul(self, rhs: f64) -> Self::Output {
Self(self.0 * rhs)
}
}
impl Mul<Stimulus> for f64 {
type Output = Stimulus;
fn mul(self, rhs: Stimulus) -> Self::Output {
Stimulus(self * rhs.0)
}
}
impl AsRef<Spectrum> for Stimulus {
fn as_ref(&self) -> &Spectrum {
&self.0
}
}