diff --git a/crates/bevy_color/src/laba.rs b/crates/bevy_color/src/laba.rs index 10decc050439e..a227cc271e5d2 100644 --- a/crates/bevy_color/src/laba.rs +++ b/crates/bevy_color/src/laba.rs @@ -1,6 +1,6 @@ use crate::{ - Alpha, ClampColor, Hsla, Hsva, Hwba, LinearRgba, Luminance, Mix, Oklaba, Srgba, StandardColor, - Xyza, + impl_componentwise_point, Alpha, ClampColor, Hsla, Hsva, Hwba, LinearRgba, Luminance, Mix, + Oklaba, Srgba, StandardColor, Xyza, }; use bevy_reflect::prelude::*; use serde::{Deserialize, Serialize}; @@ -25,6 +25,8 @@ pub struct Laba { impl StandardColor for Laba {} +impl_componentwise_point!(Laba, [lightness, a, b, alpha]); + impl Laba { /// Construct a new [`Laba`] color from components. /// diff --git a/crates/bevy_color/src/lib.rs b/crates/bevy_color/src/lib.rs index 27d92be1c46b2..937921503e7ca 100644 --- a/crates/bevy_color/src/lib.rs +++ b/crates/bevy_color/src/lib.rs @@ -65,6 +65,12 @@ //! types in this crate. This is useful when you need to store a color in a data structure //! that can't be generic over the color type. //! +//! Color types that are either physically or perceptually linear also implement `Add`, `Sub`, `Mul` and `Div` +//! allowing you to use them with splines. +//! +//! Please note that most often adding or subtracting colors is not what you may want. +//! Please have a look at other operations like blending, lightening or mixing colors using e.g. [`Mix`] or [`Luminance`] instead. +//! //! # Example //! //! ``` @@ -151,3 +157,61 @@ where Self: Alpha, { } + +macro_rules! impl_componentwise_point { + ($ty: ident, [$($element: ident),+]) => { + impl std::ops::Add for $ty { + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { + Self::Output { + $($element: self.$element + rhs.$element,)+ + } + } + } + + impl std::ops::Sub for $ty { + type Output = Self; + + fn sub(self, rhs: Self) -> Self::Output { + Self::Output { + $($element: self.$element - rhs.$element,)+ + } + } + } + + impl std::ops::Mul for $ty { + type Output = Self; + + fn mul(self, rhs: f32) -> Self::Output { + Self::Output { + $($element: self.$element * rhs,)+ + } + } + } + + impl std::ops::Mul<$ty> for f32 { + type Output = $ty; + + fn mul(self, rhs: $ty) -> Self::Output { + Self::Output { + $($element: self * rhs.$element,)+ + } + } + } + + impl std::ops::Div for $ty { + type Output = Self; + + fn div(self, rhs: f32) -> Self::Output { + Self::Output { + $($element: self.$element / rhs,)+ + } + } + } + + impl bevy_math::cubic_splines::Point for $ty {} + }; +} + +pub(crate) use impl_componentwise_point; diff --git a/crates/bevy_color/src/linear_rgba.rs b/crates/bevy_color/src/linear_rgba.rs index 864b402a28fe2..5e450af72f31c 100644 --- a/crates/bevy_color/src/linear_rgba.rs +++ b/crates/bevy_color/src/linear_rgba.rs @@ -1,7 +1,6 @@ -use std::ops::{Div, Mul}; - use crate::{ - color_difference::EuclideanDistance, Alpha, ClampColor, Luminance, Mix, StandardColor, + color_difference::EuclideanDistance, impl_componentwise_point, Alpha, ClampColor, Luminance, + Mix, StandardColor, }; use bevy_math::Vec4; use bevy_reflect::prelude::*; @@ -29,6 +28,8 @@ pub struct LinearRgba { impl StandardColor for LinearRgba {} +impl_componentwise_point!(LinearRgba, [red, green, blue, alpha]); + impl LinearRgba { /// A fully black color with full alpha. pub const BLACK: Self = Self { @@ -299,48 +300,6 @@ impl From for wgpu::Color { } } -/// All color channels are scaled directly, -/// but alpha is unchanged. -/// -/// Values are not clamped. -impl Mul for LinearRgba { - type Output = Self; - - fn mul(self, rhs: f32) -> Self { - Self { - red: self.red * rhs, - green: self.green * rhs, - blue: self.blue * rhs, - alpha: self.alpha, - } - } -} - -impl Mul for f32 { - type Output = LinearRgba; - - fn mul(self, rhs: LinearRgba) -> LinearRgba { - rhs * self - } -} - -/// All color channels are scaled directly, -/// but alpha is unchanged. -/// -/// Values are not clamped. -impl Div for LinearRgba { - type Output = Self; - - fn div(self, rhs: f32) -> Self { - Self { - red: self.red / rhs, - green: self.green / rhs, - blue: self.blue / rhs, - alpha: self.alpha, - } - } -} - // [`LinearRgba`] is intended to be used with shaders // So it's the only color type that implements [`ShaderType`] to make it easier to use inside shaders impl encase::ShaderType for LinearRgba { diff --git a/crates/bevy_color/src/oklaba.rs b/crates/bevy_color/src/oklaba.rs index 2ec202a2fa22b..4da4c805fb97c 100644 --- a/crates/bevy_color/src/oklaba.rs +++ b/crates/bevy_color/src/oklaba.rs @@ -1,6 +1,6 @@ use crate::{ - color_difference::EuclideanDistance, Alpha, ClampColor, Hsla, Hsva, Hwba, Lcha, LinearRgba, - Luminance, Mix, Srgba, StandardColor, Xyza, + color_difference::EuclideanDistance, impl_componentwise_point, Alpha, ClampColor, Hsla, Hsva, + Hwba, Lcha, LinearRgba, Luminance, Mix, Srgba, StandardColor, Xyza, }; use bevy_reflect::prelude::*; use serde::{Deserialize, Serialize}; @@ -25,6 +25,8 @@ pub struct Oklaba { impl StandardColor for Oklaba {} +impl_componentwise_point!(Oklaba, [lightness, a, b, alpha]); + impl Oklaba { /// Construct a new [`Oklaba`] color from components. /// diff --git a/crates/bevy_color/src/xyza.rs b/crates/bevy_color/src/xyza.rs index caee5e705cbd5..008b42fb12b24 100644 --- a/crates/bevy_color/src/xyza.rs +++ b/crates/bevy_color/src/xyza.rs @@ -1,4 +1,6 @@ -use crate::{Alpha, ClampColor, LinearRgba, Luminance, Mix, StandardColor}; +use crate::{ + impl_componentwise_point, Alpha, ClampColor, LinearRgba, Luminance, Mix, StandardColor, +}; use bevy_reflect::prelude::*; use serde::{Deserialize, Serialize}; @@ -22,6 +24,8 @@ pub struct Xyza { impl StandardColor for Xyza {} +impl_componentwise_point!(Xyza, [x, y, z, alpha]); + impl Xyza { /// Construct a new [`Xyza`] color from components. ///