Add color conversions #13224 (#13276)

# Objective
fixes #13224
adds conversions for Vec3 and Vec4 since these appear so often

## Solution
added Covert trait (couldn't think of good name) for [f32; 4], [f32, 3],
Vec4, and Vec3 along with the symmetric implementation

## Changelog
added conversions between arrays and vector to colors and vice versa

#migration
LinearRgba appears to have already had implicit conversions for [f32;4]
and Vec4
This commit is contained in:
moonlightaria 2024-05-09 14:01:52 -04:00 committed by GitHub
parent d9d305dab5
commit 3f2cc244d7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 606 additions and 46 deletions

View file

@ -1,3 +1,5 @@
use bevy_math::{Vec3, Vec4};
/// Methods for changing the luminance of a color. Note that these methods are not
/// guaranteed to produce consistent results across color spaces,
/// but will be within a given space.
@ -97,6 +99,26 @@ pub trait ClampColor: Sized {
fn is_within_bounds(&self) -> bool;
}
/// Trait with methods for converting colors to non-color types
pub trait ColorToComponents {
/// Convert to an f32 array
fn to_f32_array(self) -> [f32; 4];
/// Convert to an f32 array without the alpha value
fn to_f32_array_no_alpha(self) -> [f32; 3];
/// Convert to a Vec4
fn to_vec4(self) -> Vec4;
/// Convert to a Vec3
fn to_vec3(self) -> Vec3;
/// Convert from an f32 array
fn from_f32_array(color: [f32; 4]) -> Self;
/// Convert from an f32 array without the alpha value
fn from_f32_array_no_alpha(color: [f32; 3]) -> Self;
/// Convert from a Vec4
fn from_vec4(color: Vec4) -> Self;
/// Convert from a Vec3
fn from_vec3(color: Vec3) -> Self;
}
/// Utility function for interpolating hue values. This ensures that the interpolation
/// takes the shortest path around the color wheel, and that the result is always between
/// 0 and 360.

View file

@ -1,7 +1,8 @@
use crate::{
Alpha, ClampColor, Hsva, Hue, Hwba, Lcha, LinearRgba, Luminance, Mix, Srgba, StandardColor,
Xyza,
Alpha, ClampColor, ColorToComponents, Hsva, Hue, Hwba, Lcha, LinearRgba, Luminance, Mix, Srgba,
StandardColor, Xyza,
};
use bevy_math::{Vec3, Vec4};
use bevy_reflect::prelude::*;
/// Color in Hue-Saturation-Lightness (HSL) color space with alpha.
@ -195,6 +196,60 @@ impl ClampColor for Hsla {
}
}
impl ColorToComponents for Hsla {
fn to_f32_array(self) -> [f32; 4] {
[self.hue, self.saturation, self.lightness, self.alpha]
}
fn to_f32_array_no_alpha(self) -> [f32; 3] {
[self.hue, self.saturation, self.lightness]
}
fn to_vec4(self) -> Vec4 {
Vec4::new(self.hue, self.saturation, self.lightness, self.alpha)
}
fn to_vec3(self) -> Vec3 {
Vec3::new(self.hue, self.saturation, self.lightness)
}
fn from_f32_array(color: [f32; 4]) -> Self {
Self {
hue: color[0],
saturation: color[1],
lightness: color[2],
alpha: color[3],
}
}
fn from_f32_array_no_alpha(color: [f32; 3]) -> Self {
Self {
hue: color[0],
saturation: color[1],
lightness: color[2],
alpha: 1.0,
}
}
fn from_vec4(color: Vec4) -> Self {
Self {
hue: color[0],
saturation: color[1],
lightness: color[2],
alpha: color[3],
}
}
fn from_vec3(color: Vec3) -> Self {
Self {
hue: color[0],
saturation: color[1],
lightness: color[2],
alpha: 1.0,
}
}
}
impl From<Hsla> for Hsva {
fn from(
Hsla {

View file

@ -1,4 +1,8 @@
use crate::{Alpha, ClampColor, Hue, Hwba, Lcha, LinearRgba, Mix, Srgba, StandardColor, Xyza};
use crate::{
Alpha, ClampColor, ColorToComponents, Hue, Hwba, Lcha, LinearRgba, Mix, Srgba, StandardColor,
Xyza,
};
use bevy_math::{Vec3, Vec4};
use bevy_reflect::prelude::*;
/// Color in Hue-Saturation-Value (HSV) color space with alpha.
@ -172,6 +176,60 @@ impl From<Hwba> for Hsva {
}
}
impl ColorToComponents for Hsva {
fn to_f32_array(self) -> [f32; 4] {
[self.hue, self.saturation, self.value, self.alpha]
}
fn to_f32_array_no_alpha(self) -> [f32; 3] {
[self.hue, self.saturation, self.value]
}
fn to_vec4(self) -> Vec4 {
Vec4::new(self.hue, self.saturation, self.value, self.alpha)
}
fn to_vec3(self) -> Vec3 {
Vec3::new(self.hue, self.saturation, self.value)
}
fn from_f32_array(color: [f32; 4]) -> Self {
Self {
hue: color[0],
saturation: color[1],
value: color[2],
alpha: color[3],
}
}
fn from_f32_array_no_alpha(color: [f32; 3]) -> Self {
Self {
hue: color[0],
saturation: color[1],
value: color[2],
alpha: 1.0,
}
}
fn from_vec4(color: Vec4) -> Self {
Self {
hue: color[0],
saturation: color[1],
value: color[2],
alpha: color[3],
}
}
fn from_vec3(color: Vec3) -> Self {
Self {
hue: color[0],
saturation: color[1],
value: color[2],
alpha: 1.0,
}
}
}
// Derived Conversions
impl From<Srgba> for Hsva {

View file

@ -2,7 +2,10 @@
//! 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, Hue, Lcha, LinearRgba, Mix, Srgba, StandardColor, Xyza};
use crate::{
Alpha, ClampColor, ColorToComponents, Hue, Lcha, LinearRgba, Mix, Srgba, StandardColor, Xyza,
};
use bevy_math::{Vec3, Vec4};
use bevy_reflect::prelude::*;
/// Color in Hue-Whiteness-Blackness (HWB) color space with alpha.
@ -142,6 +145,60 @@ impl ClampColor for Hwba {
}
}
impl ColorToComponents for Hwba {
fn to_f32_array(self) -> [f32; 4] {
[self.hue, self.whiteness, self.blackness, self.alpha]
}
fn to_f32_array_no_alpha(self) -> [f32; 3] {
[self.hue, self.whiteness, self.blackness]
}
fn to_vec4(self) -> Vec4 {
Vec4::new(self.hue, self.whiteness, self.blackness, self.alpha)
}
fn to_vec3(self) -> Vec3 {
Vec3::new(self.hue, self.whiteness, self.blackness)
}
fn from_f32_array(color: [f32; 4]) -> Self {
Self {
hue: color[0],
whiteness: color[1],
blackness: color[2],
alpha: color[3],
}
}
fn from_f32_array_no_alpha(color: [f32; 3]) -> Self {
Self {
hue: color[0],
whiteness: color[1],
blackness: color[2],
alpha: 1.0,
}
}
fn from_vec4(color: Vec4) -> Self {
Self {
hue: color[0],
whiteness: color[1],
blackness: color[2],
alpha: color[3],
}
}
fn from_vec3(color: Vec3) -> Self {
Self {
hue: color[0],
whiteness: color[1],
blackness: color[2],
alpha: 1.0,
}
}
}
impl From<Srgba> for Hwba {
fn from(
Srgba {

View file

@ -1,7 +1,8 @@
use crate::{
impl_componentwise_vector_space, Alpha, ClampColor, Hsla, Hsva, Hwba, LinearRgba, Luminance,
Mix, Oklaba, Srgba, StandardColor, Xyza,
impl_componentwise_vector_space, Alpha, ClampColor, ColorToComponents, Hsla, Hsva, Hwba,
LinearRgba, Luminance, Mix, Oklaba, Srgba, StandardColor, Xyza,
};
use bevy_math::{Vec3, Vec4};
use bevy_reflect::prelude::*;
/// Color in LAB color space, with alpha
@ -164,6 +165,60 @@ impl Luminance for Laba {
}
}
impl ColorToComponents for Laba {
fn to_f32_array(self) -> [f32; 4] {
[self.lightness, self.a, self.b, self.alpha]
}
fn to_f32_array_no_alpha(self) -> [f32; 3] {
[self.lightness, self.a, self.b]
}
fn to_vec4(self) -> Vec4 {
Vec4::new(self.lightness, self.a, self.b, self.alpha)
}
fn to_vec3(self) -> Vec3 {
Vec3::new(self.lightness, self.a, self.b)
}
fn from_f32_array(color: [f32; 4]) -> Self {
Self {
lightness: color[0],
a: color[1],
b: color[2],
alpha: color[3],
}
}
fn from_f32_array_no_alpha(color: [f32; 3]) -> Self {
Self {
lightness: color[0],
a: color[1],
b: color[2],
alpha: 1.0,
}
}
fn from_vec4(color: Vec4) -> Self {
Self {
lightness: color[0],
a: color[1],
b: color[2],
alpha: color[3],
}
}
fn from_vec3(color: Vec3) -> Self {
Self {
lightness: color[0],
a: color[1],
b: color[2],
alpha: 1.0,
}
}
}
impl From<Laba> for Xyza {
fn from(
Laba {

View file

@ -1,4 +1,8 @@
use crate::{Alpha, ClampColor, Hue, Laba, LinearRgba, Luminance, Mix, Srgba, StandardColor, Xyza};
use crate::{
Alpha, ClampColor, ColorToComponents, Hue, Laba, LinearRgba, Luminance, Mix, Srgba,
StandardColor, Xyza,
};
use bevy_math::{Vec3, Vec4};
use bevy_reflect::prelude::*;
/// Color in LCH color space, with alpha
@ -200,6 +204,60 @@ impl ClampColor for Lcha {
}
}
impl ColorToComponents for Lcha {
fn to_f32_array(self) -> [f32; 4] {
[self.lightness, self.chroma, self.hue, self.alpha]
}
fn to_f32_array_no_alpha(self) -> [f32; 3] {
[self.lightness, self.chroma, self.hue]
}
fn to_vec4(self) -> Vec4 {
Vec4::new(self.lightness, self.chroma, self.hue, self.alpha)
}
fn to_vec3(self) -> Vec3 {
Vec3::new(self.lightness, self.chroma, self.hue)
}
fn from_f32_array(color: [f32; 4]) -> Self {
Self {
lightness: color[0],
chroma: color[1],
hue: color[2],
alpha: color[3],
}
}
fn from_f32_array_no_alpha(color: [f32; 3]) -> Self {
Self {
lightness: color[0],
chroma: color[1],
hue: color[2],
alpha: 1.0,
}
}
fn from_vec4(color: Vec4) -> Self {
Self {
lightness: color[0],
chroma: color[1],
hue: color[2],
alpha: color[3],
}
}
fn from_vec3(color: Vec3) -> Self {
Self {
lightness: color[0],
chroma: color[1],
hue: color[2],
alpha: 1.0,
}
}
}
impl From<Lcha> for Laba {
fn from(
Lcha {

View file

@ -1,8 +1,8 @@
use crate::{
color_difference::EuclideanDistance, impl_componentwise_vector_space, Alpha, ClampColor,
Luminance, Mix, StandardColor,
ColorToComponents, Luminance, Mix, StandardColor,
};
use bevy_math::Vec4;
use bevy_math::{Vec3, Vec4};
use bevy_reflect::prelude::*;
use bytemuck::{Pod, Zeroable};
@ -281,15 +281,57 @@ impl ClampColor for LinearRgba {
}
}
impl From<LinearRgba> for [f32; 4] {
fn from(color: LinearRgba) -> Self {
[color.red, color.green, color.blue, color.alpha]
impl ColorToComponents for LinearRgba {
fn to_f32_array(self) -> [f32; 4] {
[self.red, self.green, self.blue, self.alpha]
}
}
impl From<LinearRgba> for Vec4 {
fn from(color: LinearRgba) -> Self {
Vec4::new(color.red, color.green, color.blue, color.alpha)
fn to_f32_array_no_alpha(self) -> [f32; 3] {
[self.red, self.green, self.blue]
}
fn to_vec4(self) -> Vec4 {
Vec4::new(self.red, self.green, self.blue, self.alpha)
}
fn to_vec3(self) -> Vec3 {
Vec3::new(self.red, self.green, self.blue)
}
fn from_f32_array(color: [f32; 4]) -> Self {
Self {
red: color[0],
green: color[1],
blue: color[2],
alpha: color[3],
}
}
fn from_f32_array_no_alpha(color: [f32; 3]) -> Self {
Self {
red: color[0],
green: color[1],
blue: color[2],
alpha: 1.0,
}
}
fn from_vec4(color: Vec4) -> Self {
Self {
red: color[0],
green: color[1],
blue: color[2],
alpha: color[3],
}
}
fn from_vec3(color: Vec3) -> Self {
Self {
red: color[0],
green: color[1],
blue: color[2],
alpha: 1.0,
}
}
}

View file

@ -1,7 +1,9 @@
use crate::{
color_difference::EuclideanDistance, impl_componentwise_vector_space, Alpha, ClampColor, Hsla,
Hsva, Hwba, Lcha, LinearRgba, Luminance, Mix, Srgba, StandardColor, Xyza,
color_difference::EuclideanDistance, impl_componentwise_vector_space, Alpha, ClampColor,
ColorToComponents, Hsla, Hsva, Hwba, Lcha, LinearRgba, Luminance, Mix, Srgba, StandardColor,
Xyza,
};
use bevy_math::{Vec3, Vec4};
use bevy_reflect::prelude::*;
/// Color in Oklab color space, with alpha
@ -173,6 +175,60 @@ impl ClampColor for Oklaba {
}
}
impl ColorToComponents for Oklaba {
fn to_f32_array(self) -> [f32; 4] {
[self.lightness, self.a, self.b, self.alpha]
}
fn to_f32_array_no_alpha(self) -> [f32; 3] {
[self.lightness, self.a, self.b]
}
fn to_vec4(self) -> Vec4 {
Vec4::new(self.lightness, self.a, self.b, self.alpha)
}
fn to_vec3(self) -> Vec3 {
Vec3::new(self.lightness, self.a, self.b)
}
fn from_f32_array(color: [f32; 4]) -> Self {
Self {
lightness: color[0],
a: color[1],
b: color[2],
alpha: color[3],
}
}
fn from_f32_array_no_alpha(color: [f32; 3]) -> Self {
Self {
lightness: color[0],
a: color[1],
b: color[2],
alpha: 1.0,
}
}
fn from_vec4(color: Vec4) -> Self {
Self {
lightness: color[0],
a: color[1],
b: color[2],
alpha: color[3],
}
}
fn from_vec3(color: Vec3) -> Self {
Self {
lightness: color[0],
a: color[1],
b: color[2],
alpha: 1.0,
}
}
}
#[allow(clippy::excessive_precision)]
impl From<LinearRgba> for Oklaba {
fn from(value: LinearRgba) -> Self {

View file

@ -1,7 +1,8 @@
use crate::{
color_difference::EuclideanDistance, Alpha, ClampColor, Hsla, Hsva, Hue, Hwba, Laba, Lcha,
LinearRgba, Luminance, Mix, Oklaba, Srgba, StandardColor, Xyza,
color_difference::EuclideanDistance, Alpha, ClampColor, ColorToComponents, Hsla, Hsva, Hue,
Hwba, Laba, Lcha, LinearRgba, Luminance, Mix, Oklaba, Srgba, StandardColor, Xyza,
};
use bevy_math::{Vec3, Vec4};
use bevy_reflect::prelude::*;
/// Color in Oklch color space, with alpha
@ -190,6 +191,60 @@ impl EuclideanDistance for Oklcha {
}
}
impl ColorToComponents for Oklcha {
fn to_f32_array(self) -> [f32; 4] {
[self.lightness, self.chroma, self.hue, self.alpha]
}
fn to_f32_array_no_alpha(self) -> [f32; 3] {
[self.lightness, self.chroma, self.hue]
}
fn to_vec4(self) -> Vec4 {
Vec4::new(self.lightness, self.chroma, self.hue, self.alpha)
}
fn to_vec3(self) -> Vec3 {
Vec3::new(self.lightness, self.chroma, self.hue)
}
fn from_f32_array(color: [f32; 4]) -> Self {
Self {
lightness: color[0],
chroma: color[1],
hue: color[2],
alpha: color[3],
}
}
fn from_f32_array_no_alpha(color: [f32; 3]) -> Self {
Self {
lightness: color[0],
chroma: color[1],
hue: color[2],
alpha: 1.0,
}
}
fn from_vec4(color: Vec4) -> Self {
Self {
lightness: color[0],
chroma: color[1],
hue: color[2],
alpha: color[3],
}
}
fn from_vec3(color: Vec3) -> Self {
Self {
lightness: color[0],
chroma: color[1],
hue: color[2],
alpha: 1.0,
}
}
}
impl From<Oklaba> for Oklcha {
fn from(
Oklaba {

View file

@ -1,9 +1,9 @@
use crate::color_difference::EuclideanDistance;
use crate::{
impl_componentwise_vector_space, Alpha, ClampColor, LinearRgba, Luminance, Mix, StandardColor,
Xyza,
impl_componentwise_vector_space, Alpha, ClampColor, ColorToComponents, LinearRgba, Luminance,
Mix, StandardColor, Xyza,
};
use bevy_math::Vec4;
use bevy_math::{Vec3, Vec4};
use bevy_reflect::prelude::*;
use thiserror::Error;
@ -332,6 +332,60 @@ impl ClampColor for Srgba {
}
}
impl ColorToComponents for Srgba {
fn to_f32_array(self) -> [f32; 4] {
[self.red, self.green, self.blue, self.alpha]
}
fn to_f32_array_no_alpha(self) -> [f32; 3] {
[self.red, self.green, self.blue]
}
fn to_vec4(self) -> Vec4 {
Vec4::new(self.red, self.green, self.blue, self.alpha)
}
fn to_vec3(self) -> Vec3 {
Vec3::new(self.red, self.green, self.blue)
}
fn from_f32_array(color: [f32; 4]) -> Self {
Self {
red: color[0],
green: color[1],
blue: color[2],
alpha: color[3],
}
}
fn from_f32_array_no_alpha(color: [f32; 3]) -> Self {
Self {
red: color[0],
green: color[1],
blue: color[2],
alpha: 1.0,
}
}
fn from_vec4(color: Vec4) -> Self {
Self {
red: color[0],
green: color[1],
blue: color[2],
alpha: color[3],
}
}
fn from_vec3(color: Vec3) -> Self {
Self {
red: color[0],
green: color[1],
blue: color[2],
alpha: 1.0,
}
}
}
impl From<LinearRgba> for Srgba {
#[inline]
fn from(value: LinearRgba) -> Self {
@ -356,18 +410,6 @@ impl From<Srgba> for LinearRgba {
}
}
impl From<Srgba> for [f32; 4] {
fn from(color: Srgba) -> Self {
[color.red, color.green, color.blue, color.alpha]
}
}
impl From<Srgba> for Vec4 {
fn from(color: Srgba) -> Self {
Vec4::new(color.red, color.green, color.blue, color.alpha)
}
}
// Derived Conversions
impl From<Xyza> for Srgba {

View file

@ -1,6 +1,8 @@
use crate::{
impl_componentwise_vector_space, Alpha, ClampColor, LinearRgba, Luminance, Mix, StandardColor,
impl_componentwise_vector_space, Alpha, ClampColor, ColorToComponents, LinearRgba, Luminance,
Mix, StandardColor,
};
use bevy_math::{Vec3, Vec4};
use bevy_reflect::prelude::*;
/// [CIE 1931](https://en.wikipedia.org/wiki/CIE_1931_color_space) color space, also known as XYZ, with an alpha channel.
@ -160,6 +162,60 @@ impl ClampColor for Xyza {
}
}
impl ColorToComponents for Xyza {
fn to_f32_array(self) -> [f32; 4] {
[self.x, self.y, self.z, self.alpha]
}
fn to_f32_array_no_alpha(self) -> [f32; 3] {
[self.x, self.y, self.z]
}
fn to_vec4(self) -> Vec4 {
Vec4::new(self.x, self.y, self.z, self.alpha)
}
fn to_vec3(self) -> Vec3 {
Vec3::new(self.x, self.y, self.z)
}
fn from_f32_array(color: [f32; 4]) -> Self {
Self {
x: color[0],
y: color[1],
z: color[2],
alpha: color[3],
}
}
fn from_f32_array_no_alpha(color: [f32; 3]) -> Self {
Self {
x: color[0],
y: color[1],
z: color[2],
alpha: 1.0,
}
}
fn from_vec4(color: Vec4) -> Self {
Self {
x: color[0],
y: color[1],
z: color[2],
alpha: color[3],
}
}
fn from_vec3(color: Vec3) -> Self {
Self {
x: color[0],
y: color[1],
z: color[2],
alpha: 1.0,
}
}
}
impl From<LinearRgba> for Xyza {
fn from(
LinearRgba {

View file

@ -1509,7 +1509,7 @@ mod tests {
);
}
#[cfg(feature = "multi-threaded")]
#[cfg(feature = "multi_threaded")]
#[test]
fn test_events_par_iter() {
use std::{collections::HashSet, sync::mpsc};

View file

@ -1,6 +1,6 @@
use bevy_app::{App, Plugin};
use bevy_asset::{load_internal_asset, Handle};
use bevy_color::LinearRgba;
use bevy_color::{ColorToComponents, LinearRgba};
use bevy_ecs::prelude::*;
use bevy_math::{Vec3, Vec4};
use bevy_render::{
@ -66,24 +66,27 @@ pub fn prepare_fog(
match &fog.falloff {
FogFalloff::Linear { start, end } => GpuFog {
mode: GPU_FOG_MODE_LINEAR,
base_color: LinearRgba::from(fog.color).into(),
directional_light_color: LinearRgba::from(fog.directional_light_color).into(),
base_color: LinearRgba::from(fog.color).to_vec4(),
directional_light_color: LinearRgba::from(fog.directional_light_color)
.to_vec4(),
directional_light_exponent: fog.directional_light_exponent,
be: Vec3::new(*start, *end, 0.0),
..Default::default()
},
FogFalloff::Exponential { density } => GpuFog {
mode: GPU_FOG_MODE_EXPONENTIAL,
base_color: LinearRgba::from(fog.color).into(),
directional_light_color: LinearRgba::from(fog.directional_light_color).into(),
base_color: LinearRgba::from(fog.color).to_vec4(),
directional_light_color: LinearRgba::from(fog.directional_light_color)
.to_vec4(),
directional_light_exponent: fog.directional_light_exponent,
be: Vec3::new(*density, 0.0, 0.0),
..Default::default()
},
FogFalloff::ExponentialSquared { density } => GpuFog {
mode: GPU_FOG_MODE_EXPONENTIAL_SQUARED,
base_color: LinearRgba::from(fog.color).into(),
directional_light_color: LinearRgba::from(fog.directional_light_color).into(),
base_color: LinearRgba::from(fog.color).to_vec4(),
directional_light_color: LinearRgba::from(fog.directional_light_color)
.to_vec4(),
directional_light_exponent: fog.directional_light_exponent,
be: Vec3::new(*density, 0.0, 0.0),
..Default::default()
@ -93,8 +96,9 @@ pub fn prepare_fog(
inscattering,
} => GpuFog {
mode: GPU_FOG_MODE_ATMOSPHERIC,
base_color: LinearRgba::from(fog.color).into(),
directional_light_color: LinearRgba::from(fog.directional_light_color).into(),
base_color: LinearRgba::from(fog.color).to_vec4(),
directional_light_color: LinearRgba::from(fog.directional_light_color)
.to_vec4(),
directional_light_exponent: fog.directional_light_exponent,
be: *extinction,
bi: *inscattering,