use color::{Color, XYZColor};
use coord::Coord;
use illuminants::Illuminant;
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
pub struct CIELUVColor {
pub l: f64,
pub u: f64,
pub v: f64,
}
impl Color for CIELUVColor {
fn from_xyz(xyz: XYZColor) -> CIELUVColor {
let xyz_c = xyz.color_adapt(Illuminant::D50);
let wp = XYZColor::white_point(Illuminant::D50);
let denom = |color: XYZColor| color.x + 15.0 * color.y + 3.0 * color.z;
let u_func = |color: XYZColor| 4.0 * color.x / denom(color);
let v_func = |color: XYZColor| 9.0 * color.y / denom(color);
let u_prime_n = u_func(wp);
let v_prime_n = v_func(wp);
let u_prime = u_func(xyz_c);
let v_prime = v_func(xyz_c);
let delta: f64 = 6.0 / 29.0;
let y_scaled = xyz_c.y / wp.y; let l = if y_scaled <= delta.powf(3.0) {
(2.0 / delta).powf(3.0) * y_scaled
} else {
116.0 * y_scaled.powf(1.0 / 3.0) - 16.0
};
let u = 13.0 * l * (u_prime - u_prime_n);
let v = 13.0 * l * (v_prime - v_prime_n);
CIELUVColor { l, u, v }
}
fn to_xyz(&self, illuminant: Illuminant) -> XYZColor {
let wp = XYZColor::white_point(Illuminant::D50);
let denom = |color: XYZColor| color.x + 15.0 * color.y + 3.0 * color.z;
let u_func = |color: XYZColor| 4.0 * color.x / denom(color);
let v_func = |color: XYZColor| 9.0 * color.y / denom(color);
let u_prime_n = u_func(wp);
let v_prime_n = v_func(wp);
let u_prime = self.u / (13.0 * self.l) + u_prime_n;
let v_prime = self.v / (13.0 * self.l) + v_prime_n;
let delta: f64 = 6.0 / 29.0;
let y = if self.l <= 8.0 {
wp.y * self.l * (delta / 2.0).powf(3.0)
} else {
wp.y * ((self.l + 16.0) / 116.0).powf(3.0)
};
let x = y * 9.0 * u_prime / (4.0 * v_prime);
let z = y * (12.0 - 3.0 * u_prime - 20.0 * v_prime) / (4.0 * v_prime);
XYZColor {
x,
y,
z,
illuminant: Illuminant::D50,
}
.color_adapt(illuminant)
}
}
impl From<Coord> for CIELUVColor {
fn from(c: Coord) -> CIELUVColor {
CIELUVColor {
l: c.x,
u: c.y,
v: c.z,
}
}
}
impl From<CIELUVColor> for Coord {
fn from(val: CIELUVColor) -> Self {
Coord {
x: val.l,
y: val.u,
z: val.v,
}
}
}
#[cfg(test)]
mod tests {
#[allow(unused_imports)]
use super::*;
use consts::TEST_PRECISION;
#[test]
fn test_cieluv_xyz_conversion_d50() {
let xyz = XYZColor {
x: 0.3,
y: 0.53,
z: 0.65,
illuminant: Illuminant::D50,
};
let luv: CIELUVColor = xyz.convert();
let xyz2: XYZColor = luv.convert();
assert!(xyz2.approx_equal(&xyz));
assert!(xyz.distance(&xyz2) <= TEST_PRECISION);
}
#[test]
fn test_cieluv_xyz_conversion_d65() {
let xyz = XYZColor {
x: 0.3,
y: 0.53,
z: 0.65,
illuminant: Illuminant::D65,
};
let luv: CIELUVColor = xyz.convert();
let xyz2: XYZColor = luv.convert();
assert!(xyz2.approx_visually_equal(&xyz));
assert!(xyz.distance(&xyz2) <= TEST_PRECISION);
}
}