mod cdf;
mod wavelength;
use rand_distr::num_traits::{real::Real, Float, FromPrimitive, ToPrimitive};
use self::cdf::{BLACKBODY_CDF_DATA, BLACKBODY_CDF_TEMP};
pub(crate) use self::wavelength::{FIRST_WAVELENGTH, LAST_WAVELENGTH, WAVELENGTH_TO_RGB};
pub fn wavelength_to_colour<P>(nm: f64) -> (P, P, P)
where
P: Copy + Real,
{
if nm == 0.0 {
return (
P::from(8192).unwrap(),
P::from(8192).unwrap(),
P::from(8192).unwrap(),
);
}
if !nm.is_normal() {
panic!("nm (= {:?}) is not normal", nm);
}
if nm < FIRST_WAVELENGTH || nm > LAST_WAVELENGTH {
return (P::zero(), P::zero(), P::zero());
}
let fp_index: f32 = nm as f32 - FIRST_WAVELENGTH as f32;
let index: usize = fp_index.floor() as usize;
let frac = P::from(fp_index.fract()).unwrap();
let inv = P::from(1.0).unwrap() - frac;
let c1: (i16, i16, i16) = WAVELENGTH_TO_RGB[index];
let c2: (i16, i16, i16) = WAVELENGTH_TO_RGB[index + 1];
let r: P = inv * P::from(c1.0).unwrap() + frac * P::from(c2.0).unwrap();
let g: P = inv * P::from(c1.1).unwrap() + frac * P::from(c2.1).unwrap();
let b: P = inv * P::from(c1.2).unwrap() + frac * P::from(c2.2).unwrap();
return (r, g, b);
}
pub fn blackbody_wavelength<T>(temp: T, noise: T) -> T
where
T: Float + ToPrimitive + FromPrimitive + From<f32>,
{
let index: usize = (1..BLACKBODY_CDF_DATA.len())
.find(|x| BLACKBODY_CDF_DATA[*x] >= noise.to_f64().unwrap())
.expect("Blackbody Index out of range");
let lower: T = (BLACKBODY_CDF_DATA[index - 1] as f32).into();
let upper: T = (BLACKBODY_CDF_DATA[index] as f32).into();
let mut lerp: T =
T::from_usize(index).unwrap() + (noise - lower.into()) / (upper - lower).into();
if lerp.is_nan() {
lerp = 0.0.into();
}
return lerp * (T::from_f64(BLACKBODY_CDF_TEMP).unwrap() / temp);
}
#[cfg(test)]
mod tests {
use std::fmt::Debug;
use rand_distr::num_traits::{real::Real, NumCast};
use super::wavelength_to_colour;
fn match_colours<T: Copy + Real + Debug + NumCast>() {
let (r, g, b) = wavelength_to_colour::<T>(635.0);
assert_eq!(r, T::from(11654).unwrap());
assert_eq!(g, T::from(-967).unwrap());
assert_eq!(b, T::from(-115).unwrap());
let (r, g, b) = wavelength_to_colour::<T>(530.0);
assert_eq!(r, T::from(-6634).unwrap());
assert_eq!(g, T::from(11947).unwrap());
assert_eq!(b, T::from(-1000).unwrap());
let (r, g, b) = wavelength_to_colour::<T>(458.0);
assert_eq!(r, T::from(392).unwrap());
assert_eq!(g, T::from(-981).unwrap());
assert_eq!(b, T::from(14817).unwrap());
}
#[test]
fn match_colours_32() {
match_colours::<f32>()
}
#[test]
fn match_colours_64() {
match_colours::<f64>()
}
}