mirror of
https://github.com/bevyengine/bevy
synced 2024-11-25 06:00:20 +00:00
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>
This commit is contained in:
parent
887bc27a6f
commit
0950348916
7 changed files with 137 additions and 37 deletions
|
@ -62,6 +62,24 @@ pub trait Alpha: Sized {
|
|||
}
|
||||
}
|
||||
|
||||
/// Trait for manipulating the hue of a color.
|
||||
pub trait Hue: Sized {
|
||||
/// Return a new version of this color with the hue channel set to the given value.
|
||||
fn with_hue(&self, hue: f32) -> Self;
|
||||
|
||||
/// Return the hue of this color [0.0, 360.0].
|
||||
fn hue(&self) -> f32;
|
||||
|
||||
/// Sets the hue of this color.
|
||||
fn set_hue(&mut self, hue: f32);
|
||||
|
||||
/// Return a new version of this color with the hue channel rotated by the given degrees.
|
||||
fn rotate_hue(&self, degrees: f32) -> Self {
|
||||
let rotated_hue = (self.hue() + degrees).rem_euclid(360.);
|
||||
self.with_hue(rotated_hue)
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait with methods for asserting a colorspace is within bounds.
|
||||
///
|
||||
/// 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 {
|
|||
/// Are all the fields of this color in bounds?
|
||||
fn is_within_bounds(&self) -> bool;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::Hsla;
|
||||
|
||||
#[test]
|
||||
fn test_rotate_hue() {
|
||||
let hsla = Hsla::hsl(180.0, 1.0, 0.5);
|
||||
assert_eq!(hsla.rotate_hue(90.0), Hsla::hsl(270.0, 1.0, 0.5));
|
||||
assert_eq!(hsla.rotate_hue(-90.0), Hsla::hsl(90.0, 1.0, 0.5));
|
||||
assert_eq!(hsla.rotate_hue(180.0), Hsla::hsl(0.0, 1.0, 0.5));
|
||||
assert_eq!(hsla.rotate_hue(-180.0), Hsla::hsl(0.0, 1.0, 0.5));
|
||||
assert_eq!(hsla.rotate_hue(0.0), hsla);
|
||||
assert_eq!(hsla.rotate_hue(360.0), hsla);
|
||||
assert_eq!(hsla.rotate_hue(-360.0), hsla);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use crate::{
|
||||
Alpha, ClampColor, Hsva, Hwba, Lcha, LinearRgba, Luminance, Mix, Srgba, StandardColor, Xyza,
|
||||
Alpha, ClampColor, Hsva, Hue, Hwba, Lcha, LinearRgba, Luminance, Mix, Srgba, StandardColor,
|
||||
Xyza,
|
||||
};
|
||||
use bevy_reflect::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -54,11 +55,6 @@ impl Hsla {
|
|||
Self::new(hue, saturation, lightness, 1.0)
|
||||
}
|
||||
|
||||
/// Return a copy of this color with the hue channel set to the given value.
|
||||
pub const fn with_hue(self, hue: f32) -> Self {
|
||||
Self { hue, ..self }
|
||||
}
|
||||
|
||||
/// Return a copy of this color with the saturation channel set to the given value.
|
||||
pub const fn with_saturation(self, saturation: f32) -> Self {
|
||||
Self { saturation, ..self }
|
||||
|
@ -143,6 +139,23 @@ impl Alpha for Hsla {
|
|||
}
|
||||
}
|
||||
|
||||
impl Hue for Hsla {
|
||||
#[inline]
|
||||
fn with_hue(&self, hue: f32) -> Self {
|
||||
Self { hue, ..*self }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn hue(&self) -> f32 {
|
||||
self.hue
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_hue(&mut self, hue: f32) {
|
||||
self.hue = hue;
|
||||
}
|
||||
}
|
||||
|
||||
impl Luminance for Hsla {
|
||||
#[inline]
|
||||
fn with_luminance(&self, lightness: f32) -> Self {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{Alpha, ClampColor, Hwba, Lcha, LinearRgba, Srgba, StandardColor, Xyza};
|
||||
use crate::{Alpha, ClampColor, Hue, Hwba, Lcha, LinearRgba, Srgba, StandardColor, Xyza};
|
||||
use bevy_reflect::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
@ -52,11 +52,6 @@ impl Hsva {
|
|||
Self::new(hue, saturation, value, 1.0)
|
||||
}
|
||||
|
||||
/// Return a copy of this color with the hue channel set to the given value.
|
||||
pub const fn with_hue(self, hue: f32) -> Self {
|
||||
Self { hue, ..self }
|
||||
}
|
||||
|
||||
/// Return a copy of this color with the saturation channel set to the given value.
|
||||
pub const fn with_saturation(self, saturation: f32) -> Self {
|
||||
Self { saturation, ..self }
|
||||
|
@ -91,6 +86,23 @@ impl Alpha for Hsva {
|
|||
}
|
||||
}
|
||||
|
||||
impl Hue for Hsva {
|
||||
#[inline]
|
||||
fn with_hue(&self, hue: f32) -> Self {
|
||||
Self { hue, ..*self }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn hue(&self) -> f32 {
|
||||
self.hue
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_hue(&mut self, hue: f32) {
|
||||
self.hue = hue;
|
||||
}
|
||||
}
|
||||
|
||||
impl ClampColor for Hsva {
|
||||
fn clamped(&self) -> Self {
|
||||
Self {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
//! in [_HWB - A More Intuitive Hue-Based Color Model_] by _Smith et al_.
|
||||
//!
|
||||
//! [_HWB - A More Intuitive Hue-Based Color Model_]: https://web.archive.org/web/20240226005220/http://alvyray.com/Papers/CG/HWB_JGTv208.pdf
|
||||
use crate::{Alpha, ClampColor, Lcha, LinearRgba, Srgba, StandardColor, Xyza};
|
||||
use crate::{Alpha, ClampColor, Hue, Lcha, LinearRgba, Srgba, StandardColor, Xyza};
|
||||
use bevy_reflect::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
@ -56,11 +56,6 @@ impl Hwba {
|
|||
Self::new(hue, whiteness, blackness, 1.0)
|
||||
}
|
||||
|
||||
/// Return a copy of this color with the hue channel set to the given value.
|
||||
pub const fn with_hue(self, hue: f32) -> Self {
|
||||
Self { hue, ..self }
|
||||
}
|
||||
|
||||
/// Return a copy of this color with the whiteness channel set to the given value.
|
||||
pub const fn with_whiteness(self, whiteness: f32) -> Self {
|
||||
Self { whiteness, ..self }
|
||||
|
@ -95,6 +90,23 @@ impl Alpha for Hwba {
|
|||
}
|
||||
}
|
||||
|
||||
impl Hue for Hwba {
|
||||
#[inline]
|
||||
fn with_hue(&self, hue: f32) -> Self {
|
||||
Self { hue, ..*self }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn hue(&self) -> f32 {
|
||||
self.hue
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_hue(&mut self, hue: f32) {
|
||||
self.hue = hue;
|
||||
}
|
||||
}
|
||||
|
||||
impl ClampColor for Hwba {
|
||||
fn clamped(&self) -> Self {
|
||||
Self {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{Alpha, ClampColor, Laba, LinearRgba, Luminance, Mix, Srgba, StandardColor, Xyza};
|
||||
use crate::{Alpha, ClampColor, Hue, Laba, LinearRgba, Luminance, Mix, Srgba, StandardColor, Xyza};
|
||||
use bevy_reflect::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
@ -56,11 +56,6 @@ impl Lcha {
|
|||
}
|
||||
}
|
||||
|
||||
/// Return a copy of this color with the hue channel set to the given value.
|
||||
pub const fn with_hue(self, hue: f32) -> Self {
|
||||
Self { hue, ..self }
|
||||
}
|
||||
|
||||
/// Return a copy of this color with the chroma channel set to the given value.
|
||||
pub const fn with_chroma(self, chroma: f32) -> Self {
|
||||
Self { chroma, ..self }
|
||||
|
@ -137,6 +132,23 @@ impl Alpha for Lcha {
|
|||
}
|
||||
}
|
||||
|
||||
impl Hue for Lcha {
|
||||
#[inline]
|
||||
fn with_hue(&self, hue: f32) -> Self {
|
||||
Self { hue, ..*self }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn hue(&self) -> f32 {
|
||||
self.hue
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_hue(&mut self, hue: f32) {
|
||||
self.hue = hue;
|
||||
}
|
||||
}
|
||||
|
||||
impl Luminance for Lcha {
|
||||
#[inline]
|
||||
fn with_luminance(&self, lightness: f32) -> Self {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
color_difference::EuclideanDistance, Alpha, ClampColor, Hsla, Hsva, Hwba, Laba, Lcha,
|
||||
color_difference::EuclideanDistance, Alpha, ClampColor, Hsla, Hsva, Hue, Hwba, Laba, Lcha,
|
||||
LinearRgba, Luminance, Mix, Oklaba, Srgba, StandardColor, Xyza,
|
||||
};
|
||||
use bevy_reflect::prelude::*;
|
||||
|
@ -65,11 +65,6 @@ impl Oklcha {
|
|||
Self { chroma, ..self }
|
||||
}
|
||||
|
||||
/// Return a copy of this color with the 'hue' channel set to the given value.
|
||||
pub const fn with_hue(self, hue: f32) -> Self {
|
||||
Self { hue, ..self }
|
||||
}
|
||||
|
||||
/// Generate a deterministic but [quasi-randomly distributed](https://en.wikipedia.org/wiki/Low-discrepancy_sequence)
|
||||
/// color from a provided `index`.
|
||||
///
|
||||
|
@ -136,6 +131,23 @@ impl Alpha for Oklcha {
|
|||
}
|
||||
}
|
||||
|
||||
impl Hue for Oklcha {
|
||||
#[inline]
|
||||
fn with_hue(&self, hue: f32) -> Self {
|
||||
Self { hue, ..*self }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn hue(&self) -> f32 {
|
||||
self.hue
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_hue(&mut self, hue: f32) {
|
||||
self.hue = hue;
|
||||
}
|
||||
}
|
||||
|
||||
impl Luminance for Oklcha {
|
||||
#[inline]
|
||||
fn with_luminance(&self, lightness: f32) -> Self {
|
||||
|
|
|
@ -30,14 +30,19 @@ fn setup(
|
|||
));
|
||||
|
||||
let cube = meshes.add(Cuboid::new(0.5, 0.5, 0.5));
|
||||
|
||||
const GOLDEN_ANGLE: f32 = 137.507_77;
|
||||
|
||||
let mut hsla = Hsla::hsl(0.0, 1.0, 0.5);
|
||||
for x in -1..2 {
|
||||
for z in -1..2 {
|
||||
commands.spawn(PbrBundle {
|
||||
mesh: cube.clone(),
|
||||
material: materials.add(Color::WHITE),
|
||||
material: materials.add(Color::from(hsla)),
|
||||
transform: Transform::from_translation(Vec3::new(x as f32, 0.0, z as f32)),
|
||||
..default()
|
||||
});
|
||||
hsla = hsla.rotate_hue(GOLDEN_ANGLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,13 +52,11 @@ fn animate_materials(
|
|||
time: Res<Time>,
|
||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||
) {
|
||||
for (i, material_handle) in material_handles.iter().enumerate() {
|
||||
for material_handle in material_handles.iter() {
|
||||
if let Some(material) = materials.get_mut(material_handle) {
|
||||
material.base_color = Color::hsl(
|
||||
((i as f32 * 2.345 + time.elapsed_seconds_wrapped()) * 100.0) % 360.0,
|
||||
1.0,
|
||||
0.5,
|
||||
);
|
||||
if let Color::Hsla(ref mut hsla) = material.base_color {
|
||||
*hsla = hsla.rotate_hue(time.delta_seconds() * 100.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue