color_space 0.5.4

library for converting between color spaces and comparing colors
Documentation
use crate::{ Rgb, FromRgb, ToRgb, Xyz, approx };

/// A CIELUV color (luminance, )
#[derive(Copy, Clone, Debug, Default)]
pub struct Luv {
    pub l: f64,
    pub u: f64,
    pub v: f64,
}

impl Luv {
    /// Create a new CIELUV color.
    /// 
    /// `l`: luminance component (0 to 100).
    /// 
    /// `u`: coordinate (-134 to 220).
    /// 
    /// `v`: coordinate (-140 to 122).
    #[inline]
    pub fn new(l: f64, u: f64, v: f64) -> Self {
        Self { l, u, v }
    }
}

impl PartialEq for Luv {
    fn eq(&self, other: &Self) -> bool {
        approx(self.l, other.l) &&
        approx(self.u, other.u) &&
        approx(self.v, other.v)
    }
}

const EPS: f64 = 216.0 / 24389.0;
const KAPPA: f64 = 24389.0 / 27.0;
const WHITE: Xyz = Xyz{ x: 95.047, y: 100.000, z: 108.883 };

impl FromRgb for Luv {
    fn from_rgb(rgb: &Rgb) -> Self {

        let xyz = Xyz::from_rgb(rgb);
        let y = xyz.y / WHITE.y;
        let temp = xyz.x + 15.0 * xyz.y + 3.0 * xyz.z;
        let tempr = WHITE.x + 15.0 * WHITE.y + 3.0 * WHITE.z;
        
        let l = match y > EPS {
            true => 116.0 * y.cbrt() - 16.0,
            false => KAPPA * y
        };

        let (u, v) = match temp > 1e-3 {
            true => (xyz.x / temp, xyz.y / temp),
            false => (0.0, 0.0),
        };

        Self::new(
            l,
            52.0 * l * (u - WHITE.x / tempr),
            117.0 * l * (v - WHITE.y / tempr)
        )
    }
}

impl ToRgb for Luv {
    fn to_rgb(&self) -> Rgb {
        let y = match self.l > EPS * KAPPA {
            true => ((self.l + 16.0) / 116.0).powf(3.0),
            false => self.l / KAPPA,
        };
        let tempr = WHITE.x + 15.0 * WHITE.y + 3.0 * WHITE.z;
        let up = 4.0 * WHITE.x / tempr;
        let vp = 9.0 * WHITE.y / tempr;
        let a = 1.0 / 3.0 * (52.0 * self.l / (self.u + 13.0 * self.l * up) - 1.0);
        let b = y * -5.0;
        let x = (y * (39.0 * self.l / (self.v + 13.0 * self.l * vp) - 5.0) - b) / (a + 1.0 / 3.0);
        let z = x * a + b;
        Xyz::new(x * 100.0, y * 100.0, z * 100.0).to_rgb()
    }
}