use crate::lab::CieLCh;
use crate::observer::Observer;
use crate::rgb::RgbSpace;
use crate::traits::Light;
use crate::xyz::XYZ;
const CONVERGENCE_THRESHOLD: f64 = 1e-5;
const CHROMA_HIGH_MAX: f64 = 500.0;
pub struct CieLChGamut {
rgb_space: RgbSpace,
white_point: XYZ,
}
impl CieLChGamut {
pub fn new(observer: Observer, rgb_space: RgbSpace) -> Self {
let white_point = rgb_space.white().white_point(observer);
CieLChGamut {
white_point,
rgb_space,
}
}
pub fn oberver(&self) -> Observer {
self.white_point.observer()
}
pub fn max_chroma(&self, l: f64, h: f64) -> Option<CieLCh> {
let mut c_low = 0.0;
let mut c_high = CHROMA_HIGH_MAX;
let mut c = CHROMA_HIGH_MAX / 2.0; for _ in 0..20 {
let cielch = CieLCh::new([l, c, h], self.white_point);
let rgb = cielch.rgb(self.rgb_space);
if rgb.to_array().iter().all(|&v| v < 1.0) {
c_low = c; } else {
c_high = c; }
if (c_high - c_low).abs() < CONVERGENCE_THRESHOLD {
break;
}
c = (c_low + c_high) / 2.0; }
let cielch = CieLCh::new([l, c_low, h], self.white_point);
let xy = cielch.rxyz().xyz().chromaticity();
if cielch.observer().spectral_locus().contains(xy.to_array()) {
Some(cielch)
} else {
None
}
}
pub fn max_chroma_in_gamut(&self, l: f64, h: f64) -> Option<CieLCh> {
let cielch = self.max_chroma(l, h)?;
let rgb = cielch.rgb(self.rgb_space);
if rgb.to_array().iter().all(|&v| (0.0..=1.0).contains(&v)) {
Some(cielch)
} else {
None
}
}
pub fn rgb_space(&self) -> RgbSpace {
self.rgb_space
}
pub fn white_point(&self) -> XYZ {
self.white_point
}
}
#[cfg(test)]
mod tests {}