Skip to content

Commit 0950348

Browse files
oyasumi731pablo-luaalice-i-cecile
authored
Add hue traits (#12399)
# Objective Fixes #12200 . ## Solution I added a Hue Trait with the rotate_hue method to enable hue rotation. Additionally, I modified the implementation of animations in the animated_material sample. --- ## Changelog - Added a `Hue` trait to `bevy_color/src/color_ops.rs`. - Added the `Hue` trait implementation to `Hsla`, `Hsva`, `Hwba`, `Lcha`, and `Oklcha`. - Updated animated_material sample. ## Migration Guide Users of Oklcha need to change their usage to use the with_hue method instead of the with_h method. --------- Co-authored-by: Pablo Reinhardt <126117294+pablo-lua@users.noreply.github.com> Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
1 parent 887bc27 commit 0950348

File tree

7 files changed

+137
-37
lines changed

7 files changed

+137
-37
lines changed

crates/bevy_color/src/color_ops.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,24 @@ pub trait Alpha: Sized {
6262
}
6363
}
6464

65+
/// Trait for manipulating the hue of a color.
66+
pub trait Hue: Sized {
67+
/// Return a new version of this color with the hue channel set to the given value.
68+
fn with_hue(&self, hue: f32) -> Self;
69+
70+
/// Return the hue of this color [0.0, 360.0].
71+
fn hue(&self) -> f32;
72+
73+
/// Sets the hue of this color.
74+
fn set_hue(&mut self, hue: f32);
75+
76+
/// Return a new version of this color with the hue channel rotated by the given degrees.
77+
fn rotate_hue(&self, degrees: f32) -> Self {
78+
let rotated_hue = (self.hue() + degrees).rem_euclid(360.);
79+
self.with_hue(rotated_hue)
80+
}
81+
}
82+
6583
/// Trait with methods for asserting a colorspace is within bounds.
6684
///
6785
/// During ordinary usage (e.g. reading images from disk, rendering images, picking colors for UI), colors should always be within their ordinary bounds (such as 0 to 1 for RGB colors).
@@ -78,3 +96,21 @@ pub trait ClampColor: Sized {
7896
/// Are all the fields of this color in bounds?
7997
fn is_within_bounds(&self) -> bool;
8098
}
99+
100+
#[cfg(test)]
101+
mod tests {
102+
use super::*;
103+
use crate::Hsla;
104+
105+
#[test]
106+
fn test_rotate_hue() {
107+
let hsla = Hsla::hsl(180.0, 1.0, 0.5);
108+
assert_eq!(hsla.rotate_hue(90.0), Hsla::hsl(270.0, 1.0, 0.5));
109+
assert_eq!(hsla.rotate_hue(-90.0), Hsla::hsl(90.0, 1.0, 0.5));
110+
assert_eq!(hsla.rotate_hue(180.0), Hsla::hsl(0.0, 1.0, 0.5));
111+
assert_eq!(hsla.rotate_hue(-180.0), Hsla::hsl(0.0, 1.0, 0.5));
112+
assert_eq!(hsla.rotate_hue(0.0), hsla);
113+
assert_eq!(hsla.rotate_hue(360.0), hsla);
114+
assert_eq!(hsla.rotate_hue(-360.0), hsla);
115+
}
116+
}

crates/bevy_color/src/hsla.rs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::{
2-
Alpha, ClampColor, Hsva, Hwba, Lcha, LinearRgba, Luminance, Mix, Srgba, StandardColor, Xyza,
2+
Alpha, ClampColor, Hsva, Hue, Hwba, Lcha, LinearRgba, Luminance, Mix, Srgba, StandardColor,
3+
Xyza,
34
};
45
use bevy_reflect::prelude::*;
56
use serde::{Deserialize, Serialize};
@@ -54,11 +55,6 @@ impl Hsla {
5455
Self::new(hue, saturation, lightness, 1.0)
5556
}
5657

57-
/// Return a copy of this color with the hue channel set to the given value.
58-
pub const fn with_hue(self, hue: f32) -> Self {
59-
Self { hue, ..self }
60-
}
61-
6258
/// Return a copy of this color with the saturation channel set to the given value.
6359
pub const fn with_saturation(self, saturation: f32) -> Self {
6460
Self { saturation, ..self }
@@ -143,6 +139,23 @@ impl Alpha for Hsla {
143139
}
144140
}
145141

142+
impl Hue for Hsla {
143+
#[inline]
144+
fn with_hue(&self, hue: f32) -> Self {
145+
Self { hue, ..*self }
146+
}
147+
148+
#[inline]
149+
fn hue(&self) -> f32 {
150+
self.hue
151+
}
152+
153+
#[inline]
154+
fn set_hue(&mut self, hue: f32) {
155+
self.hue = hue;
156+
}
157+
}
158+
146159
impl Luminance for Hsla {
147160
#[inline]
148161
fn with_luminance(&self, lightness: f32) -> Self {

crates/bevy_color/src/hsva.rs

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::{Alpha, ClampColor, Hwba, Lcha, LinearRgba, Srgba, StandardColor, Xyza};
1+
use crate::{Alpha, ClampColor, Hue, Hwba, Lcha, LinearRgba, Srgba, StandardColor, Xyza};
22
use bevy_reflect::prelude::*;
33
use serde::{Deserialize, Serialize};
44

@@ -52,11 +52,6 @@ impl Hsva {
5252
Self::new(hue, saturation, value, 1.0)
5353
}
5454

55-
/// Return a copy of this color with the hue channel set to the given value.
56-
pub const fn with_hue(self, hue: f32) -> Self {
57-
Self { hue, ..self }
58-
}
59-
6055
/// Return a copy of this color with the saturation channel set to the given value.
6156
pub const fn with_saturation(self, saturation: f32) -> Self {
6257
Self { saturation, ..self }
@@ -91,6 +86,23 @@ impl Alpha for Hsva {
9186
}
9287
}
9388

89+
impl Hue for Hsva {
90+
#[inline]
91+
fn with_hue(&self, hue: f32) -> Self {
92+
Self { hue, ..*self }
93+
}
94+
95+
#[inline]
96+
fn hue(&self) -> f32 {
97+
self.hue
98+
}
99+
100+
#[inline]
101+
fn set_hue(&mut self, hue: f32) {
102+
self.hue = hue;
103+
}
104+
}
105+
94106
impl ClampColor for Hsva {
95107
fn clamped(&self) -> Self {
96108
Self {

crates/bevy_color/src/hwba.rs

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//! in [_HWB - A More Intuitive Hue-Based Color Model_] by _Smith et al_.
33
//!
44
//! [_HWB - A More Intuitive Hue-Based Color Model_]: https://web.archive.org/web/20240226005220/http://alvyray.com/Papers/CG/HWB_JGTv208.pdf
5-
use crate::{Alpha, ClampColor, Lcha, LinearRgba, Srgba, StandardColor, Xyza};
5+
use crate::{Alpha, ClampColor, Hue, Lcha, LinearRgba, Srgba, StandardColor, Xyza};
66
use bevy_reflect::prelude::*;
77
use serde::{Deserialize, Serialize};
88

@@ -56,11 +56,6 @@ impl Hwba {
5656
Self::new(hue, whiteness, blackness, 1.0)
5757
}
5858

59-
/// Return a copy of this color with the hue channel set to the given value.
60-
pub const fn with_hue(self, hue: f32) -> Self {
61-
Self { hue, ..self }
62-
}
63-
6459
/// Return a copy of this color with the whiteness channel set to the given value.
6560
pub const fn with_whiteness(self, whiteness: f32) -> Self {
6661
Self { whiteness, ..self }
@@ -95,6 +90,23 @@ impl Alpha for Hwba {
9590
}
9691
}
9792

93+
impl Hue for Hwba {
94+
#[inline]
95+
fn with_hue(&self, hue: f32) -> Self {
96+
Self { hue, ..*self }
97+
}
98+
99+
#[inline]
100+
fn hue(&self) -> f32 {
101+
self.hue
102+
}
103+
104+
#[inline]
105+
fn set_hue(&mut self, hue: f32) {
106+
self.hue = hue;
107+
}
108+
}
109+
98110
impl ClampColor for Hwba {
99111
fn clamped(&self) -> Self {
100112
Self {

crates/bevy_color/src/lcha.rs

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::{Alpha, ClampColor, Laba, LinearRgba, Luminance, Mix, Srgba, StandardColor, Xyza};
1+
use crate::{Alpha, ClampColor, Hue, Laba, LinearRgba, Luminance, Mix, Srgba, StandardColor, Xyza};
22
use bevy_reflect::prelude::*;
33
use serde::{Deserialize, Serialize};
44

@@ -56,11 +56,6 @@ impl Lcha {
5656
}
5757
}
5858

59-
/// Return a copy of this color with the hue channel set to the given value.
60-
pub const fn with_hue(self, hue: f32) -> Self {
61-
Self { hue, ..self }
62-
}
63-
6459
/// Return a copy of this color with the chroma channel set to the given value.
6560
pub const fn with_chroma(self, chroma: f32) -> Self {
6661
Self { chroma, ..self }
@@ -137,6 +132,23 @@ impl Alpha for Lcha {
137132
}
138133
}
139134

135+
impl Hue for Lcha {
136+
#[inline]
137+
fn with_hue(&self, hue: f32) -> Self {
138+
Self { hue, ..*self }
139+
}
140+
141+
#[inline]
142+
fn hue(&self) -> f32 {
143+
self.hue
144+
}
145+
146+
#[inline]
147+
fn set_hue(&mut self, hue: f32) {
148+
self.hue = hue;
149+
}
150+
}
151+
140152
impl Luminance for Lcha {
141153
#[inline]
142154
fn with_luminance(&self, lightness: f32) -> Self {

crates/bevy_color/src/oklcha.rs

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::{
2-
color_difference::EuclideanDistance, Alpha, ClampColor, Hsla, Hsva, Hwba, Laba, Lcha,
2+
color_difference::EuclideanDistance, Alpha, ClampColor, Hsla, Hsva, Hue, Hwba, Laba, Lcha,
33
LinearRgba, Luminance, Mix, Oklaba, Srgba, StandardColor, Xyza,
44
};
55
use bevy_reflect::prelude::*;
@@ -65,11 +65,6 @@ impl Oklcha {
6565
Self { chroma, ..self }
6666
}
6767

68-
/// Return a copy of this color with the 'hue' channel set to the given value.
69-
pub const fn with_hue(self, hue: f32) -> Self {
70-
Self { hue, ..self }
71-
}
72-
7368
/// Generate a deterministic but [quasi-randomly distributed](https://en.wikipedia.org/wiki/Low-discrepancy_sequence)
7469
/// color from a provided `index`.
7570
///
@@ -136,6 +131,23 @@ impl Alpha for Oklcha {
136131
}
137132
}
138133

134+
impl Hue for Oklcha {
135+
#[inline]
136+
fn with_hue(&self, hue: f32) -> Self {
137+
Self { hue, ..*self }
138+
}
139+
140+
#[inline]
141+
fn hue(&self) -> f32 {
142+
self.hue
143+
}
144+
145+
#[inline]
146+
fn set_hue(&mut self, hue: f32) {
147+
self.hue = hue;
148+
}
149+
}
150+
139151
impl Luminance for Oklcha {
140152
#[inline]
141153
fn with_luminance(&self, lightness: f32) -> Self {

examples/3d/animated_material.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,19 @@ fn setup(
3030
));
3131

3232
let cube = meshes.add(Cuboid::new(0.5, 0.5, 0.5));
33+
34+
const GOLDEN_ANGLE: f32 = 137.507_77;
35+
36+
let mut hsla = Hsla::hsl(0.0, 1.0, 0.5);
3337
for x in -1..2 {
3438
for z in -1..2 {
3539
commands.spawn(PbrBundle {
3640
mesh: cube.clone(),
37-
material: materials.add(Color::WHITE),
41+
material: materials.add(Color::from(hsla)),
3842
transform: Transform::from_translation(Vec3::new(x as f32, 0.0, z as f32)),
3943
..default()
4044
});
45+
hsla = hsla.rotate_hue(GOLDEN_ANGLE);
4146
}
4247
}
4348
}
@@ -47,13 +52,11 @@ fn animate_materials(
4752
time: Res<Time>,
4853
mut materials: ResMut<Assets<StandardMaterial>>,
4954
) {
50-
for (i, material_handle) in material_handles.iter().enumerate() {
55+
for material_handle in material_handles.iter() {
5156
if let Some(material) = materials.get_mut(material_handle) {
52-
material.base_color = Color::hsl(
53-
((i as f32 * 2.345 + time.elapsed_seconds_wrapped()) * 100.0) % 360.0,
54-
1.0,
55-
0.5,
56-
);
57+
if let Color::Hsla(ref mut hsla) = material.base_color {
58+
*hsla = hsla.rotate_hue(time.delta_seconds() * 100.0);
59+
}
5760
}
5861
}
5962
}

0 commit comments

Comments
 (0)