#[cfg(feature = "pyffi")]
use pyo3::prelude::*;
use super::ColorSpace;
use crate::core::{conversion::okxab_to_okxch, convert, FloatExt};
use crate::{Bits, Float};
#[macro_export]
macro_rules! assert_close_enough {
($f1:expr, $f2:expr $(,)?) => {
let (f1, f2) = ($f1, $f2);
let bits1 = $crate::to_eq_bits(f1);
let bits2 = $crate::to_eq_bits(f2);
assert_eq!(bits1, bits2, "quantities differ:\n{:?}\n{:?}", f1, f2);
};
}
#[cfg(test)]
macro_rules! assert_same_coordinates {
($space:expr , $cs1:expr , $cs2:expr $(,)?) => {
let (space, cs1, cs2) = ($space, $cs1, $cs2);
let bits1 = $crate::core::to_eq_coordinates(space, cs1);
let bits2 = $crate::core::to_eq_coordinates(space, cs2);
assert_eq!(
bits1, bits2,
"color coordinates differ:\n{:?}\n{:?}",
cs1, cs2
);
};
}
#[cfg(test)]
pub(crate) use assert_same_coordinates;
#[macro_export]
macro_rules! assert_same_color {
($c1:expr, $c2:expr $(,)?) => {
let (c1, c2) = ($c1, $c2);
if c1.space() != c2.space() {
assert_eq!(
c1,
c2,
"color spaces differ:\n{:?}\n{:?}",
c1.space(),
c2.space()
);
}
assert_eq!(
c1,
c2,
"color coordinates differ:\n{:?}\n{:?}",
c1.as_ref(),
c2.as_ref()
);
};
}
#[inline]
pub(crate) fn normalize(space: ColorSpace, coordinates: &[Float; 3]) -> [Float; 3] {
let [mut c1, mut c2, mut c3] = *coordinates;
if c1.is_nan() {
c1 = 0.0;
}
if c2.is_nan() {
c2 = 0.0;
}
if c3.is_nan() {
c3 = 0.0;
if space.is_polar() {
c2 = 0.0;
}
}
if space.is_ok() {
c1 = c1.clamp(0.0, 1.0);
if space.is_polar() {
c2 = c2.max(0.0);
}
}
[c1, c2, c3]
}
#[must_use = "function returns new color coordinates and does not mutate original value"]
pub(crate) fn to_eq_coordinates(space: ColorSpace, coordinates: &[Float; 3]) -> [Bits; 3] {
let [mut c1, mut c2, mut c3] = normalize(space, coordinates);
if space.is_polar() {
c3 = c3.rem_euclid(360.0) / 360.0
}
let factor = <Float as FloatExt>::ROUNDING_FACTOR;
c1 = (c1 * factor).round();
c2 = (c2 * factor).round();
c3 = (c3 * factor).round();
if c1 == -0.0 {
c1 = 0.0;
}
if c2 == -0.0 {
c2 = 0.0
}
if c3 == -0.0 {
c3 = 0.0
}
[c1.to_bits(), c2.to_bits(), c3.to_bits()]
}
#[cfg(feature = "pyffi")]
#[pyfunction]
pub fn close_enough(f1: Float, f2: Float) -> bool {
to_eq_bits(f1) == to_eq_bits(f2)
}
#[doc(hidden)]
#[inline]
pub fn to_eq_bits(f: Float) -> Bits {
let mut f = if f.is_nan() { 0.0 } else { f };
f = (<Float as FloatExt>::ROUNDING_FACTOR * f).round();
if f == -0.0 {
f = 0.0
}
f.to_bits()
}
pub(crate) fn is_achromatic(space: ColorSpace, coordinates: &[Float; 3], threshold: Float) -> bool {
let coordinates = match space {
ColorSpace::Oklch | ColorSpace::Oklrch => *coordinates,
ColorSpace::Oklrab => okxab_to_okxch(coordinates),
_ => convert(space, ColorSpace::Oklch, coordinates),
};
is_achromatic_chroma_hue(coordinates[1], coordinates[2], threshold)
}
#[inline]
pub(crate) fn is_achromatic_chroma_hue(chroma: Float, hue: Float, threshold: Float) -> bool {
hue.is_nan() || chroma <= threshold
}