-
-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
36e898b
commit 47a8d4a
Showing
3 changed files
with
186 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
//! Documents how to animate colors in different color spaces. | ||
|
||
use bevy::{ | ||
math::cubic_splines::{CubicCurve, Point}, | ||
prelude::*, | ||
}; | ||
|
||
// We define this trait so we can reuse the same code for multiple color types that may be implemented using curves. | ||
trait CurveColor: Point + Into<Color> + Send + Sync + 'static {} | ||
impl CurveColor for LinearRgba {} | ||
impl CurveColor for Oklaba {} | ||
impl CurveColor for Xyza {} | ||
|
||
// We define this trait so we can reuse the same code for multiple color types that may be implemented using mixing. | ||
trait MixedColor: Mix + Into<Color> + Send + Sync + 'static {} | ||
impl MixedColor for Oklcha {} | ||
impl MixedColor for Srgba {} | ||
impl MixedColor for Hsla {} | ||
|
||
// This copmonent is used to | ||
#[derive(Debug, Component)] | ||
struct Curve<T: CurveColor>(CubicCurve<T>); | ||
|
||
#[derive(Debug, Component)] | ||
struct Mixed<T: MixedColor>([T; 4]); | ||
|
||
fn main() { | ||
App::new() | ||
.add_plugins(DefaultPlugins) | ||
.add_systems(Startup, setup) | ||
.add_systems( | ||
Update, | ||
( | ||
animate_curve::<LinearRgba>, | ||
animate_curve::<Oklaba>, | ||
animate_curve::<Xyza>, | ||
animate_mixed::<Hsla>, | ||
animate_mixed::<Srgba>, | ||
animate_mixed::<Oklcha>, | ||
), | ||
) | ||
.run(); | ||
} | ||
|
||
fn setup(mut commands: Commands) { | ||
commands.spawn(Camera2dBundle::default()); | ||
|
||
// The color spaces `Oklaba`, `Laba`, `LinearRgba` and `Xyza` all are either perceptually or physically linear. | ||
// This property allows us to define curves, e.g. bezier curves through these spaces. | ||
|
||
// Define the control points for the curve. | ||
// For more information, please see the cubic curve example. | ||
let points = [ | ||
LinearRgba::WHITE, | ||
LinearRgba::rgb(1., 1., 0.), // Yellow | ||
LinearRgba::RED, | ||
LinearRgba::BLACK, | ||
]; | ||
// Spawn a sprite using the provided control points. | ||
spawn_curve_sprite(&mut commands, 275., points); | ||
|
||
let points = [ | ||
Xyza::xyz(1., 1., 1.), // White | ||
Xyza::xyz(0.76998, 0.92781, 0.13853), // Yellow | ||
Xyza::xyz(0.41239, 0.21264, 0.01933), // Red | ||
Xyza::xyz(0., 0., 0.), // Black | ||
]; | ||
spawn_curve_sprite(&mut commands, 175., points); | ||
|
||
let points = [ | ||
Oklaba::lab(1., -0.00002, -0.00012), // White | ||
Oklaba::lab(0.96798, -0.07141, 0.19848), // Yellow | ||
Oklaba::lab(0.62795, 0.22483, 0.12579), // Red | ||
Oklaba::lab(0., 0., 0.), // Black | ||
]; | ||
spawn_curve_sprite(&mut commands, 75., points); | ||
|
||
// Other color spaces like `Srgba` or `Hsva` are neither perceptually nor physically linear. | ||
// As such, we cannot use curves in these spaces. | ||
// However, we can still mix these colours and animate that way. In fact, mixing colors works in any color space. | ||
|
||
// Define the colors that should be mixed. | ||
let colors = [ | ||
Hsla::hsl(0., 0., 1.), // White | ||
Hsla::hsl(60., 1., 0.5), // Yellow | ||
Hsla::hsl(0., 1., 0.5), // Red | ||
Hsla::hsl(0., 0., 0.), // Black | ||
]; | ||
// Spawn a spritre using the provided colors for mixing. | ||
spawn_mixed_sprite(&mut commands, -75., colors); | ||
|
||
let colors = [ | ||
Srgba::WHITE, | ||
Srgba::rgb(1., 1., 0.), // Yellow | ||
Srgba::RED, | ||
Srgba::BLACK, | ||
]; | ||
spawn_mixed_sprite(&mut commands, -175., colors); | ||
|
||
let colors = [ | ||
Oklcha::lch(1., 0., 0.), // White | ||
Oklcha::lch(0.96798, 0.21094, 109.78745), // Yellow | ||
Oklcha::lch(0.62795, 0.25763, 29.22714), // Red | ||
Oklcha::lch(0., 0., 0.), // Black | ||
]; | ||
spawn_mixed_sprite(&mut commands, -275., colors); | ||
} | ||
|
||
fn spawn_curve_sprite<T: CurveColor>(commands: &mut Commands, y: f32, points: [T; 4]) { | ||
commands.spawn(( | ||
SpriteBundle { | ||
transform: Transform::from_xyz(0., y, 0.), | ||
sprite: Sprite { | ||
custom_size: Some(Vec2::new(75., 75.)), | ||
..Default::default() | ||
}, | ||
..Default::default() | ||
}, | ||
Curve(CubicBezier::new([points]).to_curve()), | ||
)); | ||
} | ||
|
||
fn spawn_mixed_sprite<T: MixedColor>(commands: &mut Commands, y: f32, colors: [T; 4]) { | ||
commands.spawn(( | ||
SpriteBundle { | ||
transform: Transform::from_xyz(0., y, 0.), | ||
sprite: Sprite { | ||
custom_size: Some(Vec2::new(75., 75.)), | ||
..Default::default() | ||
}, | ||
..Default::default() | ||
}, | ||
Mixed(colors), | ||
)); | ||
} | ||
|
||
fn animate_curve<T: CurveColor>( | ||
time: Res<Time>, | ||
mut query: Query<(&mut Transform, &mut Sprite, &Curve<T>)>, | ||
) { | ||
let t = (time.elapsed_seconds().sin() + 1.) / 2.; | ||
|
||
for (mut transform, mut sprite, cubic_curve) in &mut query { | ||
// position takes a point from the curve where 0 is the initial point | ||
// and 1 is the last point | ||
sprite.color = cubic_curve.0.position(t).into(); | ||
transform.translation.x = 600. * (t - 0.5); | ||
} | ||
} | ||
|
||
fn animate_mixed<T: MixedColor>( | ||
time: Res<Time>, | ||
mut query: Query<(&mut Transform, &mut Sprite, &Mixed<T>)>, | ||
) { | ||
let t = (time.elapsed_seconds().sin() + 1.) / 2.; | ||
|
||
for (mut transform, mut sprite, mixed) in &mut query { | ||
sprite.color = { | ||
// First, we determine the amount of intervals between colors. | ||
// For four colors, there are three intervals between those colors; | ||
let intervals = (mixed.0.len() - 1) as f32; | ||
|
||
// Next we determine the index of the first of the two colorts to mix. | ||
let start_i = (t * intervals).floor().min(intervals - 1.); | ||
|
||
// Lastly we determine the 'local' value of t in this interval. | ||
let local_t = (t * intervals) - start_i; | ||
|
||
let color = mixed.0[start_i as usize].mix(&mixed.0[start_i as usize + 1], local_t); | ||
color.into() | ||
}; | ||
transform.translation.x = 600. * (t - 0.5); | ||
} | ||
} |