Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Solve some oklaba inconsistencies #12526

Merged
merged 12 commits into from
Mar 17, 2024
13 changes: 9 additions & 4 deletions crates/bevy_color/src/color.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,14 +266,19 @@ impl Color {
}

/// Creates a new [`Color`] object storing a [`Oklaba`] color.
pub const fn oklaba(l: f32, a: f32, b: f32, alpha: f32) -> Self {
Self::Oklaba(Oklaba { l, a, b, alpha })
pub const fn oklaba(lightness: f32, a: f32, b: f32, alpha: f32) -> Self {
Self::Oklaba(Oklaba {
lightness,
a,
b,
alpha,
})
}

/// Creates a new [`Color`] object storing a [`Oklaba`] color with an alpha of 1.0.
pub const fn oklab(l: f32, a: f32, b: f32) -> Self {
pub const fn oklab(lightness: f32, a: f32, b: f32) -> Self {
Self::Oklaba(Oklaba {
l,
lightness,
a,
b,
alpha: 1.0,
Expand Down
66 changes: 44 additions & 22 deletions crates/bevy_color/src/oklaba.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use serde::{Deserialize, Serialize};
#[reflect(PartialEq, Serialize, Deserialize)]
pub struct Oklaba {
/// The 'l' channel. [0.0, 1.0]
pub l: f32,
pub lightness: f32,
/// The 'a' channel. [-1.0, 1.0]
pub a: f32,
/// The 'b' channel. [-1.0, 1.0]
Expand All @@ -30,33 +30,38 @@ impl Oklaba {
///
/// # Arguments
///
/// * `l` - Lightness channel. [0.0, 1.0]
/// * `lightness` - Lightness channel. [0.0, 1.0]
/// * `a` - Green-red channel. [-1.0, 1.0]
/// * `b` - Blue-yellow channel. [-1.0, 1.0]
/// * `alpha` - Alpha channel. [0.0, 1.0]
pub const fn new(l: f32, a: f32, b: f32, alpha: f32) -> Self {
Self { l, a, b, alpha }
pub const fn new(lightness: f32, a: f32, b: f32, alpha: f32) -> Self {
Self {
lightness,
a,
b,
alpha,
}
}

/// Construct a new [`Oklaba`] color from (l, a, b) components, with the default alpha (1.0).
///
/// # Arguments
///
/// * `l` - Lightness channel. [0.0, 1.0]
/// * `lightness` - Lightness channel. [0.0, 1.0]
/// * `a` - Green-red channel. [-1.0, 1.0]
/// * `b` - Blue-yellow channel. [-1.0, 1.0]
pub const fn lch(l: f32, a: f32, b: f32) -> Self {
pub const fn lab(lightness: f32, a: f32, b: f32) -> Self {
Self {
l,
lightness,
a,
b,
alpha: 1.0,
}
}

/// Return a copy of this color with the 'l' channel set to the given value.
pub const fn with_l(self, l: f32) -> Self {
Self { l, ..self }
pub const fn with_lightness(self, lightness: f32) -> Self {
Self { lightness, ..self }
}

/// Return a copy of this color with the 'a' channel set to the given value.
Expand All @@ -81,7 +86,7 @@ impl Mix for Oklaba {
fn mix(&self, other: &Self, factor: f32) -> Self {
let n_factor = 1.0 - factor;
Self {
l: self.l * n_factor + other.l * factor,
lightness: self.lightness * n_factor + other.lightness * factor,
a: self.a * n_factor + other.a * factor,
b: self.b * n_factor + other.b * factor,
alpha: self.alpha * n_factor + other.alpha * factor,
Expand All @@ -108,27 +113,39 @@ impl Alpha for Oklaba {

impl Luminance for Oklaba {
#[inline]
fn with_luminance(&self, l: f32) -> Self {
Self { l, ..*self }
fn with_luminance(&self, lightness: f32) -> Self {
Self { lightness, ..*self }
}

fn luminance(&self) -> f32 {
self.l
self.lightness
}

fn darker(&self, amount: f32) -> Self {
Self::new((self.l - amount).max(0.), self.a, self.b, self.alpha)
Self::new(
(self.lightness - amount).max(0.),
self.a,
self.b,
self.alpha,
)
}

fn lighter(&self, amount: f32) -> Self {
Self::new((self.l + amount).min(1.), self.a, self.b, self.alpha)
Self::new(
(self.lightness + amount).min(1.),
self.a,
self.b,
self.alpha,
)
}
}

impl EuclideanDistance for Oklaba {
#[inline]
fn distance_squared(&self, other: &Self) -> f32 {
(self.l - other.l).powi(2) + (self.a - other.a).powi(2) + (self.b - other.b).powi(2)
(self.lightness - other.lightness).powi(2)
+ (self.a - other.a).powi(2)
+ (self.b - other.b).powi(2)
}
}

Expand Down Expand Up @@ -158,12 +175,17 @@ impl From<LinearRgba> for Oklaba {
#[allow(clippy::excessive_precision)]
impl From<Oklaba> for LinearRgba {
fn from(value: Oklaba) -> Self {
let Oklaba { l, a, b, alpha } = value;
let Oklaba {
lightness,
a,
b,
alpha,
} = value;

// From https://github.com/Ogeon/palette/blob/e75eab2fb21af579353f51f6229a510d0d50a311/palette/src/oklab.rs#L312-L332
let l_ = l + 0.3963377774 * a + 0.2158037573 * b;
let m_ = l - 0.1055613458 * a - 0.0638541728 * b;
let s_ = l - 0.0894841775 * a - 1.2914855480 * b;
let l_ = lightness + 0.3963377774 * a + 0.2158037573 * b;
let m_ = lightness - 0.1055613458 * a - 0.0638541728 * b;
let s_ = lightness - 0.0894841775 * a - 1.2914855480 * b;

let l = l_ * l_ * l_;
let m = m_ * m_ * m_;
Expand Down Expand Up @@ -266,7 +288,7 @@ mod tests {
let oklaba = Oklaba::new(0.5, 0.5, 0.5, 1.0);
let srgba: Srgba = oklaba.into();
let oklaba2: Oklaba = srgba.into();
assert_approx_eq!(oklaba.l, oklaba2.l, 0.001);
assert_approx_eq!(oklaba.lightness, oklaba2.lightness, 0.001);
assert_approx_eq!(oklaba.a, oklaba2.a, 0.001);
assert_approx_eq!(oklaba.b, oklaba2.b, 0.001);
assert_approx_eq!(oklaba.alpha, oklaba2.alpha, 0.001);
Expand Down Expand Up @@ -299,7 +321,7 @@ mod tests {
let oklaba = Oklaba::new(0.5, 0.5, 0.5, 1.0);
let linear: LinearRgba = oklaba.into();
let oklaba2: Oklaba = linear.into();
assert_approx_eq!(oklaba.l, oklaba2.l, 0.001);
assert_approx_eq!(oklaba.lightness, oklaba2.lightness, 0.001);
assert_approx_eq!(oklaba.a, oklaba2.a, 0.001);
assert_approx_eq!(oklaba.b, oklaba2.b, 0.001);
assert_approx_eq!(oklaba.alpha, oklaba2.alpha, 0.001);
Expand Down
10 changes: 8 additions & 2 deletions crates/bevy_color/src/oklcha.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,14 @@ impl EuclideanDistance for Oklcha {
}

impl From<Oklaba> for Oklcha {
fn from(Oklaba { l, a, b, alpha }: Oklaba) -> Self {
let lightness = l;
fn from(
Oklaba {
lightness,
a,
b,
alpha,
}: Oklaba,
) -> Self {
let chroma = a.hypot(b);
let hue = b.atan2(a).to_degrees();

Expand Down