use super::cieluvcolor::CIELUVColor;
use color::{Color, XYZColor};
use coord::Coord;
use illuminants::Illuminant;
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
pub struct CIELCHuvColor {
pub l: f64,
pub c: f64,
pub h: f64,
}
impl Color for CIELCHuvColor {
fn from_xyz(xyz: XYZColor) -> CIELCHuvColor {
let luv = CIELUVColor::from_xyz(xyz);
let unbounded_h = luv.v.atan2(luv.u).to_degrees();
let h = if unbounded_h < 0.0 {
unbounded_h + 360.0
} else if unbounded_h > 360.0 {
unbounded_h - 360.0
} else {
unbounded_h
};
let c = luv.v.hypot(luv.u);
CIELCHuvColor { l: luv.l, c, h }
}
fn to_xyz(&self, illuminant: Illuminant) -> XYZColor {
let rad_h = self.h.to_radians();
let u = self.c * rad_h.cos();
let v = self.c * rad_h.sin();
CIELUVColor { l: self.l, u, v }.to_xyz(illuminant)
}
}
impl From<Coord> for CIELCHuvColor {
fn from(c: Coord) -> CIELCHuvColor {
CIELCHuvColor {
l: c.x,
c: c.y,
h: c.z,
}
}
}
impl From<CIELCHuvColor> for Coord {
fn from(val: CIELCHuvColor) -> Self {
Coord {
x: val.l,
y: val.c,
z: val.h,
}
}
}
#[cfg(test)]
mod tests {
#[allow(unused_imports)]
use super::*;
use consts::TEST_PRECISION;
#[test]
fn test_cielchuv_xyz_conversion_d50() {
let xyz = XYZColor {
x: 0.4,
y: 0.6,
z: 0.2,
illuminant: Illuminant::D50,
};
let lchuv: CIELCHuvColor = xyz.convert();
let xyz2: XYZColor = lchuv.convert();
assert!(xyz.approx_visually_equal(&xyz2));
assert!(xyz.distance(&xyz2) <= TEST_PRECISION);
}
#[test]
fn test_cielchuv_xyz_conversion_d65() {
let xyz = XYZColor {
x: 0.4,
y: 0.6,
z: 0.2,
illuminant: Illuminant::D65,
};
let lchuv: CIELCHuvColor = xyz.convert();
let xyz2: XYZColor = lchuv.convert();
assert!(xyz.approx_visually_equal(&xyz2));
assert!(xyz.distance(&xyz2) <= TEST_PRECISION);
}
}