use crate::spaces::{Lab, Rgb, Xyz65};
use crate::traits::ColorSpace;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Lch {
pub l: f64,
pub c: f64,
pub h: f64,
pub alpha: Option<f64>,
}
#[inline]
fn normalize_hue(h: f64) -> f64 {
let h = h % 360.0;
if h < 0.0 {
h + 360.0
} else {
h
}
}
impl ColorSpace for Lch {
const MODE: &'static str = "lch";
const CHANNELS: &'static [&'static str] = &["l", "c", "h"];
fn alpha(&self) -> Option<f64> {
self.alpha
}
fn with_alpha(self, alpha: Option<f64>) -> Self {
Self { alpha, ..self }
}
fn to_xyz65(&self) -> Xyz65 {
Lab::from(*self).to_xyz65()
}
fn from_xyz65(xyz: Xyz65) -> Self {
Lab::from_xyz65(xyz).into()
}
}
impl From<Lab> for Lch {
fn from(c: Lab) -> Self {
let chroma = (c.a * c.a + c.b * c.b).sqrt();
let h = if chroma == 0.0 {
f64::NAN
} else {
normalize_hue(c.b.atan2(c.a).to_degrees())
};
Self {
l: c.l,
c: chroma,
h,
alpha: c.alpha,
}
}
}
impl From<Rgb> for Lch {
fn from(c: Rgb) -> Self {
Lch::from(Lab::from(c))
}
}
impl From<Lch> for Lab {
fn from(c: Lch) -> Self {
let h = if c.h.is_nan() { 0.0 } else { c.h };
let (a, b) = if c.c == 0.0 {
(0.0, 0.0)
} else {
let theta = h.to_radians();
(c.c * theta.cos(), c.c * theta.sin())
};
Self {
l: c.l,
a,
b,
alpha: c.alpha,
}
}
}