material-colors 0.4.2

Up-to-date material-color-utilities port
Documentation
#[cfg(all(not(feature = "std"), feature = "libm"))]
#[allow(unused_imports)]
use crate::utils::no_std::FloatExt;
use crate::{
    color::Argb,
    hct::{Cam16, Hct},
    utils::math::{difference_degrees, rotate_direction, sanitize_degrees_double},
};

pub fn harmonize(design_color: Argb, source_color: Argb) -> Argb {
    let from_hct: Hct = design_color.into();
    let to_hct: Hct = source_color.into();

    let difference_degrees = difference_degrees(from_hct.get_hue(), to_hct.get_hue());
    let rotation_degrees = (difference_degrees * 0.5).min(15.0);

    let output_hue = sanitize_degrees_double(rotation_degrees.mul_add(
        rotate_direction(from_hct.get_hue(), to_hct.get_hue()),
        from_hct.get_hue(),
    ));

    Hct::from(output_hue, from_hct.get_chroma(), from_hct.get_tone()).into()
}

pub fn hct_hue(from: Argb, to: Argb, amount: f64) -> Argb {
    let ucs = cam16_ucs(from, to, amount);

    let ucs_cam = Cam16::from(ucs);
    let from_cam = Cam16::from(from);

    let blended = Hct::from(ucs_cam.hue, from_cam.chroma, from.as_lstar());

    blended.into()
}

pub fn cam16_ucs(from: Argb, to: Argb, amount: f64) -> Argb {
    let from_cam = Cam16::from(from);
    let to_cam = Cam16::from(to);

    let from_j = from_cam.jstar;
    let from_a = from_cam.astar;
    let from_b = from_cam.bstar;

    let to_j = to_cam.jstar;
    let to_a = to_cam.astar;
    let to_b = to_cam.bstar;

    let (jstar, astar, bstar) = (
        (to_j - from_j).mul_add(amount, from_j),
        (to_a - from_a).mul_add(amount, from_a),
        (to_b - from_b).mul_add(amount, from_b),
    );

    Cam16::from_ucs(jstar, astar, bstar).into()
}

#[cfg(test)]
mod tests {
    use super::hct_hue;
    use crate::color::Argb;
    use core::str::FromStr;

    #[test]
    fn test_red_to_blue() {
        let blended = hct_hue(
            Argb::from_str("ff0000").unwrap(),
            Argb::from_str("0000ff").unwrap(),
            0.8,
        );

        assert_eq!(blended.to_hex(), "905eff");
    }
}