use super::cielabcolor::CIELABColor;
use color::{Color, XYZColor};
use coord::Coord;
use illuminants::Illuminant;
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
pub struct CIELCHColor {
pub l: f64,
pub c: f64,
pub h: f64,
}
impl Color for CIELCHColor {
fn from_xyz(xyz: XYZColor) -> CIELCHColor {
let lab = CIELABColor::from_xyz(xyz);
let l = lab.l; let c = lab.b.hypot(lab.a);
let unbounded_h = lab.b.atan2(lab.a).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
};
CIELCHColor { l, c, h }
}
fn to_xyz(&self, illuminant: Illuminant) -> XYZColor {
let (sin, cos) = self.h.to_radians().sin_cos();
CIELABColor {
l: self.l,
a: self.c * cos,
b: self.c * sin,
}
.to_xyz(illuminant)
}
}
impl From<Coord> for CIELCHColor {
fn from(c: Coord) -> CIELCHColor {
CIELCHColor {
l: c.x,
c: c.y,
h: c.z,
}
}
}
impl From<CIELCHColor> for Coord {
fn from(val: CIELCHColor) -> 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_lch_xyz_conversion_same_illuminant() {
let xyz = XYZColor {
x: 0.2,
y: 0.42,
z: 0.23,
illuminant: Illuminant::D50,
};
let lch: CIELCHColor = xyz.convert();
let xyz2: XYZColor = lch.convert();
assert!(xyz2.approx_equal(&xyz));
assert!(xyz.distance(&xyz2) <= TEST_PRECISION);
}
#[test]
fn test_lch_xyz_conversion_different_illuminant() {
let xyz = XYZColor {
x: 0.2,
y: 0.42,
z: 0.23,
illuminant: Illuminant::D55,
};
let lch: CIELCHColor = xyz.convert();
let xyz2: XYZColor = lch.convert();
assert!(xyz2.approx_visually_equal(&xyz));
assert!(xyz.distance(&xyz2) <= TEST_PRECISION);
}
}