use crate::Float; use crate::core::color::{RGB, XYZ}; use enum_dispatch::enum_dispatch; pub use crate::spectra::*; #[enum_dispatch] pub trait SpectrumTrait: Copy { fn evaluate(&self, lambda: Float) -> Float; fn sample(&self, lambda: &SampledWavelengths) -> SampledSpectrum { SampledSpectrum::from_fn(|i| self.evaluate(lambda[i])) } fn max_value(&self) -> Float; } #[repr(C)] #[derive(Debug, Clone, Copy)] pub struct StandardSpectra { pub x: DenselySampledSpectrum, pub y: DenselySampledSpectrum, pub z: DenselySampledSpectrum, pub d65: DenselySampledSpectrum, } unsafe impl Send for StandardSpectra {} unsafe impl Sync for StandardSpectra {} #[repr(C)] #[enum_dispatch(SpectrumTrait)] #[derive(Debug, Clone, Copy)] pub enum Spectrum { Constant(ConstantSpectrum), Dense(DenselySampledSpectrum), Piecewise(PiecewiseLinearSpectrum), Blackbody(BlackbodySpectrum), RGBAlbedo(RGBAlbedoSpectrum), RGBIlluminant(RGBIlluminantSpectrum), RGBUnbounded(RGBUnboundedSpectrum), } impl Spectrum { pub fn std_illuminant_d65() -> Self { unimplemented!("Use crate::spectra::default_illuminant() on host") } pub fn to_xyz(&self, std: &StandardSpectra) -> XYZ { let x = self.inner_product(&Spectrum::Dense(std.x)); let y = self.inner_product(&Spectrum::Dense(std.y)); let z = self.inner_product(&Spectrum::Dense(std.z)); XYZ::new(x, y, z) / CIE_Y_INTEGRAL } pub fn to_rgb(&self, cs: &RGBColorSpace, std: &StandardSpectra) -> RGB { let xyz = self.to_xyz(std); cs.to_rgb(xyz) } pub fn inner_product(&self, other: &Spectrum) -> Float { let mut integral = 0.0; for lambda in LAMBDA_MIN..=LAMBDA_MAX { let l = lambda as Float; integral += self.evaluate(l) * other.evaluate(l); } integral } pub fn is_constant(&self) -> bool { matches!(self, Spectrum::Constant(_)) } }