Skip to content

Commit

Permalink
Add example
Browse files Browse the repository at this point in the history
  • Loading branch information
lynn-lumen committed Mar 21, 2024
1 parent 36e898b commit 47a8d4a
Show file tree
Hide file tree
Showing 3 changed files with 186 additions and 0 deletions.
11 changes: 11 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -1013,6 +1013,17 @@ description = "Create and play an animation defined by code that operates on the
category = "Animation"
wasm = true

[[example]]
name = "color_animation"
path = "examples/animation/color_animation.rs"
doc-scrape-examples = true

[package.metadata.example.color_animation]
name = "Color animation"
description = "Bezier curve example showing squares changing colors using different color spaces"
category = "Animation"
wasm = true

[[example]]
name = "cubic_curve"
path = "examples/animation/cubic_curve.rs"
Expand Down
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ Example | Description
[Animated Fox](../examples/animation/animated_fox.rs) | Plays an animation from a skinned glTF
[Animated Transform](../examples/animation/animated_transform.rs) | Create and play an animation defined by code that operates on the `Transform` component
[Animation Graph](../examples/animation/animation_graph.rs) | Blends multiple animations together with a graph
[Color animation](../examples/animation/color_animation.rs) | Bezier curve example showing squares changing colors using different color spaces
[Cubic Curve](../examples/animation/cubic_curve.rs) | Bezier curve example showing a cube following a cubic curve
[Custom Skinned Mesh](../examples/animation/custom_skinned_mesh.rs) | Skinned mesh example with mesh and joints data defined in code
[Morph Targets](../examples/animation/morph_targets.rs) | Plays an animation from a glTF file with meshes with morph targets
Expand Down
174 changes: 174 additions & 0 deletions examples/animation/color_animation.rs
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

Check warning on line 20 in examples/animation/color_animation.rs

View workflow job for this annotation

GitHub Actions / typos

"copmonent" should be "component".
#[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);
}
}

0 comments on commit 47a8d4a

Please sign in to comment.