2024-10-07 18:31:43 +00:00
|
|
|
//! Demonstrates the behavior of the built-in easing functions.
|
|
|
|
|
|
|
|
use bevy::{prelude::*, sprite::Anchor};
|
|
|
|
|
|
|
|
#[derive(Component)]
|
Simplified easing curves (#15711)
# Objective
Simplify the API surrounding easing curves. Broaden the base of types
that support easing.
## Solution
There is now a single library function, `easing_curve`, which constructs
a unit-parametrized easing curve between two values based on an
`EaseFunction`:
```rust
/// Given a `start` and `end` value, create a curve parametrized over [the unit interval]
/// that connects them, using the given [ease function] to determine the form of the
/// curve in between.
///
/// [the unit interval]: Interval::UNIT
/// [ease function]: EaseFunction
pub fn easing_curve<T: Ease>(start: T, end: T, ease_fn: EaseFunction) -> EasingCurve<T> { //... }
```
As this shows, the type of the output curve is generic only in `T`. In
particular, as long as `T` is `Reflect` (and `FromReflect` etc. — i.e.,
a standard "well-behaved" reflectable type), `EasingCurve<T>` is also
`Reflect`, and there is no special field handling nonsense. Therefore,
`EasingCurve` is the kind of thing that would be able to be easily
changed in an editor. This is made possible by storing the actual
`EaseFunction` on `EasingCurve<T>` instead of indirecting through some
kind of function type (which generally leads to issues with reflection).
The types that can be eased are those that implement a trait `Ease`:
```rust
/// A type whose values can be eased between.
///
/// This requires the construction of an interpolation curve that actually extends
/// beyond the curve segment that connects two values, because an easing curve may
/// extrapolate before the starting value and after the ending value. This is
/// especially common in easing functions that mimic elastic or springlike behavior.
pub trait Ease: Sized {
/// Given `start` and `end` values, produce a curve with [unlimited domain]
/// that:
/// - takes a value equivalent to `start` at `t = 0`
/// - takes a value equivalent to `end` at `t = 1`
/// - has constant speed everywhere, including outside of `[0, 1]`
///
/// [unlimited domain]: Interval::EVERYWHERE
fn interpolating_curve_unbounded(start: &Self, end: &Self) -> impl Curve<Self>;
}
```
(I know, I know, yet *another* interpolation trait. See 'Future
direction'.)
The other existing easing functions from the previous version of this
module have also become new members of `EaseFunction`: `Linear`,
`Steps`, and `Elastic` (which maybe needs a different name). The latter
two are parametrized.
## Testing
Tested using the `easing_functions` example. I also axed the
`cubic_curve` example which was of questionable value and replaced it
with `eased_motion`, which uses this API in the context of animation:
https://github.com/user-attachments/assets/3c802992-6b9b-4b56-aeb1-a47501c29ce2
---
## Future direction
Morally speaking, `Ease` is incredibly similar to `StableInterpolate`.
Probably, we should just merge `StableInterpolate` into `Ease`, and then
make `SmoothNudge` an automatic extension trait of `Ease`. The reason I
didn't do that is that `StableInterpolate` is not implemented for
`VectorSpace` because of concerns about the `Color` types, and I wanted
to avoid controversy. I think that may be a good idea though.
As Alice mentioned before, we should also probably get rid of the
`interpolation` dependency.
The parametrized `Elastic` variant probably also needs some additional
work (e.g. renaming, in/out/in-out variants, etc.) if we want to keep
it.
2024-10-08 19:45:13 +00:00
|
|
|
struct SelectedEaseFunction(EaseFunction, Color);
|
2024-10-07 18:31:43 +00:00
|
|
|
|
|
|
|
fn main() {
|
|
|
|
App::new()
|
|
|
|
.add_plugins(DefaultPlugins)
|
|
|
|
.add_systems(Startup, setup)
|
|
|
|
.add_systems(Update, display_curves)
|
|
|
|
.run();
|
|
|
|
}
|
|
|
|
|
|
|
|
fn setup(mut commands: Commands) {
|
|
|
|
commands.spawn(Camera2d);
|
|
|
|
|
|
|
|
let text_style = TextStyle {
|
|
|
|
font_size: 10.0,
|
|
|
|
..default()
|
|
|
|
};
|
|
|
|
|
|
|
|
for (i, functions) in [
|
Simplified easing curves (#15711)
# Objective
Simplify the API surrounding easing curves. Broaden the base of types
that support easing.
## Solution
There is now a single library function, `easing_curve`, which constructs
a unit-parametrized easing curve between two values based on an
`EaseFunction`:
```rust
/// Given a `start` and `end` value, create a curve parametrized over [the unit interval]
/// that connects them, using the given [ease function] to determine the form of the
/// curve in between.
///
/// [the unit interval]: Interval::UNIT
/// [ease function]: EaseFunction
pub fn easing_curve<T: Ease>(start: T, end: T, ease_fn: EaseFunction) -> EasingCurve<T> { //... }
```
As this shows, the type of the output curve is generic only in `T`. In
particular, as long as `T` is `Reflect` (and `FromReflect` etc. — i.e.,
a standard "well-behaved" reflectable type), `EasingCurve<T>` is also
`Reflect`, and there is no special field handling nonsense. Therefore,
`EasingCurve` is the kind of thing that would be able to be easily
changed in an editor. This is made possible by storing the actual
`EaseFunction` on `EasingCurve<T>` instead of indirecting through some
kind of function type (which generally leads to issues with reflection).
The types that can be eased are those that implement a trait `Ease`:
```rust
/// A type whose values can be eased between.
///
/// This requires the construction of an interpolation curve that actually extends
/// beyond the curve segment that connects two values, because an easing curve may
/// extrapolate before the starting value and after the ending value. This is
/// especially common in easing functions that mimic elastic or springlike behavior.
pub trait Ease: Sized {
/// Given `start` and `end` values, produce a curve with [unlimited domain]
/// that:
/// - takes a value equivalent to `start` at `t = 0`
/// - takes a value equivalent to `end` at `t = 1`
/// - has constant speed everywhere, including outside of `[0, 1]`
///
/// [unlimited domain]: Interval::EVERYWHERE
fn interpolating_curve_unbounded(start: &Self, end: &Self) -> impl Curve<Self>;
}
```
(I know, I know, yet *another* interpolation trait. See 'Future
direction'.)
The other existing easing functions from the previous version of this
module have also become new members of `EaseFunction`: `Linear`,
`Steps`, and `Elastic` (which maybe needs a different name). The latter
two are parametrized.
## Testing
Tested using the `easing_functions` example. I also axed the
`cubic_curve` example which was of questionable value and replaced it
with `eased_motion`, which uses this API in the context of animation:
https://github.com/user-attachments/assets/3c802992-6b9b-4b56-aeb1-a47501c29ce2
---
## Future direction
Morally speaking, `Ease` is incredibly similar to `StableInterpolate`.
Probably, we should just merge `StableInterpolate` into `Ease`, and then
make `SmoothNudge` an automatic extension trait of `Ease`. The reason I
didn't do that is that `StableInterpolate` is not implemented for
`VectorSpace` because of concerns about the `Color` types, and I wanted
to avoid controversy. I think that may be a good idea though.
As Alice mentioned before, we should also probably get rid of the
`interpolation` dependency.
The parametrized `Elastic` variant probably also needs some additional
work (e.g. renaming, in/out/in-out variants, etc.) if we want to keep
it.
2024-10-08 19:45:13 +00:00
|
|
|
EaseFunction::SineIn,
|
|
|
|
EaseFunction::SineOut,
|
|
|
|
EaseFunction::SineInOut,
|
|
|
|
EaseFunction::QuadraticIn,
|
|
|
|
EaseFunction::QuadraticOut,
|
|
|
|
EaseFunction::QuadraticInOut,
|
|
|
|
EaseFunction::CubicIn,
|
|
|
|
EaseFunction::CubicOut,
|
|
|
|
EaseFunction::CubicInOut,
|
|
|
|
EaseFunction::QuarticIn,
|
|
|
|
EaseFunction::QuarticOut,
|
|
|
|
EaseFunction::QuarticInOut,
|
|
|
|
EaseFunction::QuinticIn,
|
|
|
|
EaseFunction::QuinticOut,
|
|
|
|
EaseFunction::QuinticInOut,
|
|
|
|
EaseFunction::CircularIn,
|
|
|
|
EaseFunction::CircularOut,
|
|
|
|
EaseFunction::CircularInOut,
|
|
|
|
EaseFunction::ExponentialIn,
|
|
|
|
EaseFunction::ExponentialOut,
|
|
|
|
EaseFunction::ExponentialInOut,
|
|
|
|
EaseFunction::ElasticIn,
|
|
|
|
EaseFunction::ElasticOut,
|
|
|
|
EaseFunction::ElasticInOut,
|
|
|
|
EaseFunction::BackIn,
|
|
|
|
EaseFunction::BackOut,
|
|
|
|
EaseFunction::BackInOut,
|
|
|
|
EaseFunction::BounceIn,
|
|
|
|
EaseFunction::BounceOut,
|
|
|
|
EaseFunction::BounceInOut,
|
|
|
|
EaseFunction::Linear,
|
|
|
|
EaseFunction::Steps(4),
|
|
|
|
EaseFunction::Elastic(50.0),
|
2024-10-07 18:31:43 +00:00
|
|
|
]
|
|
|
|
.chunks(3)
|
|
|
|
.enumerate()
|
|
|
|
{
|
|
|
|
for (j, function) in functions.iter().enumerate() {
|
Simplified easing curves (#15711)
# Objective
Simplify the API surrounding easing curves. Broaden the base of types
that support easing.
## Solution
There is now a single library function, `easing_curve`, which constructs
a unit-parametrized easing curve between two values based on an
`EaseFunction`:
```rust
/// Given a `start` and `end` value, create a curve parametrized over [the unit interval]
/// that connects them, using the given [ease function] to determine the form of the
/// curve in between.
///
/// [the unit interval]: Interval::UNIT
/// [ease function]: EaseFunction
pub fn easing_curve<T: Ease>(start: T, end: T, ease_fn: EaseFunction) -> EasingCurve<T> { //... }
```
As this shows, the type of the output curve is generic only in `T`. In
particular, as long as `T` is `Reflect` (and `FromReflect` etc. — i.e.,
a standard "well-behaved" reflectable type), `EasingCurve<T>` is also
`Reflect`, and there is no special field handling nonsense. Therefore,
`EasingCurve` is the kind of thing that would be able to be easily
changed in an editor. This is made possible by storing the actual
`EaseFunction` on `EasingCurve<T>` instead of indirecting through some
kind of function type (which generally leads to issues with reflection).
The types that can be eased are those that implement a trait `Ease`:
```rust
/// A type whose values can be eased between.
///
/// This requires the construction of an interpolation curve that actually extends
/// beyond the curve segment that connects two values, because an easing curve may
/// extrapolate before the starting value and after the ending value. This is
/// especially common in easing functions that mimic elastic or springlike behavior.
pub trait Ease: Sized {
/// Given `start` and `end` values, produce a curve with [unlimited domain]
/// that:
/// - takes a value equivalent to `start` at `t = 0`
/// - takes a value equivalent to `end` at `t = 1`
/// - has constant speed everywhere, including outside of `[0, 1]`
///
/// [unlimited domain]: Interval::EVERYWHERE
fn interpolating_curve_unbounded(start: &Self, end: &Self) -> impl Curve<Self>;
}
```
(I know, I know, yet *another* interpolation trait. See 'Future
direction'.)
The other existing easing functions from the previous version of this
module have also become new members of `EaseFunction`: `Linear`,
`Steps`, and `Elastic` (which maybe needs a different name). The latter
two are parametrized.
## Testing
Tested using the `easing_functions` example. I also axed the
`cubic_curve` example which was of questionable value and replaced it
with `eased_motion`, which uses this API in the context of animation:
https://github.com/user-attachments/assets/3c802992-6b9b-4b56-aeb1-a47501c29ce2
---
## Future direction
Morally speaking, `Ease` is incredibly similar to `StableInterpolate`.
Probably, we should just merge `StableInterpolate` into `Ease`, and then
make `SmoothNudge` an automatic extension trait of `Ease`. The reason I
didn't do that is that `StableInterpolate` is not implemented for
`VectorSpace` because of concerns about the `Color` types, and I wanted
to avoid controversy. I think that may be a good idea though.
As Alice mentioned before, we should also probably get rid of the
`interpolation` dependency.
The parametrized `Elastic` variant probably also needs some additional
work (e.g. renaming, in/out/in-out variants, etc.) if we want to keep
it.
2024-10-08 19:45:13 +00:00
|
|
|
let color = Hsla::hsl(i as f32 / 11.0 * 360.0, 0.8, 0.75).into();
|
2024-10-07 18:31:43 +00:00
|
|
|
commands
|
|
|
|
.spawn((
|
|
|
|
Text2dBundle {
|
|
|
|
text: Text::from_section(
|
|
|
|
format!("{:?}", function),
|
|
|
|
TextStyle {
|
|
|
|
color,
|
|
|
|
..text_style.clone()
|
|
|
|
},
|
|
|
|
),
|
|
|
|
transform: Transform::from_xyz(
|
Simplified easing curves (#15711)
# Objective
Simplify the API surrounding easing curves. Broaden the base of types
that support easing.
## Solution
There is now a single library function, `easing_curve`, which constructs
a unit-parametrized easing curve between two values based on an
`EaseFunction`:
```rust
/// Given a `start` and `end` value, create a curve parametrized over [the unit interval]
/// that connects them, using the given [ease function] to determine the form of the
/// curve in between.
///
/// [the unit interval]: Interval::UNIT
/// [ease function]: EaseFunction
pub fn easing_curve<T: Ease>(start: T, end: T, ease_fn: EaseFunction) -> EasingCurve<T> { //... }
```
As this shows, the type of the output curve is generic only in `T`. In
particular, as long as `T` is `Reflect` (and `FromReflect` etc. — i.e.,
a standard "well-behaved" reflectable type), `EasingCurve<T>` is also
`Reflect`, and there is no special field handling nonsense. Therefore,
`EasingCurve` is the kind of thing that would be able to be easily
changed in an editor. This is made possible by storing the actual
`EaseFunction` on `EasingCurve<T>` instead of indirecting through some
kind of function type (which generally leads to issues with reflection).
The types that can be eased are those that implement a trait `Ease`:
```rust
/// A type whose values can be eased between.
///
/// This requires the construction of an interpolation curve that actually extends
/// beyond the curve segment that connects two values, because an easing curve may
/// extrapolate before the starting value and after the ending value. This is
/// especially common in easing functions that mimic elastic or springlike behavior.
pub trait Ease: Sized {
/// Given `start` and `end` values, produce a curve with [unlimited domain]
/// that:
/// - takes a value equivalent to `start` at `t = 0`
/// - takes a value equivalent to `end` at `t = 1`
/// - has constant speed everywhere, including outside of `[0, 1]`
///
/// [unlimited domain]: Interval::EVERYWHERE
fn interpolating_curve_unbounded(start: &Self, end: &Self) -> impl Curve<Self>;
}
```
(I know, I know, yet *another* interpolation trait. See 'Future
direction'.)
The other existing easing functions from the previous version of this
module have also become new members of `EaseFunction`: `Linear`,
`Steps`, and `Elastic` (which maybe needs a different name). The latter
two are parametrized.
## Testing
Tested using the `easing_functions` example. I also axed the
`cubic_curve` example which was of questionable value and replaced it
with `eased_motion`, which uses this API in the context of animation:
https://github.com/user-attachments/assets/3c802992-6b9b-4b56-aeb1-a47501c29ce2
---
## Future direction
Morally speaking, `Ease` is incredibly similar to `StableInterpolate`.
Probably, we should just merge `StableInterpolate` into `Ease`, and then
make `SmoothNudge` an automatic extension trait of `Ease`. The reason I
didn't do that is that `StableInterpolate` is not implemented for
`VectorSpace` because of concerns about the `Color` types, and I wanted
to avoid controversy. I think that may be a good idea though.
As Alice mentioned before, we should also probably get rid of the
`interpolation` dependency.
The parametrized `Elastic` variant probably also needs some additional
work (e.g. renaming, in/out/in-out variants, etc.) if we want to keep
it.
2024-10-08 19:45:13 +00:00
|
|
|
i as f32 * 113.0 - 1280.0 / 2.0 + 25.0,
|
2024-10-07 18:31:43 +00:00
|
|
|
-100.0 - ((j as f32 * 250.0) - 300.0),
|
|
|
|
0.0,
|
|
|
|
),
|
|
|
|
text_anchor: Anchor::TopLeft,
|
|
|
|
..default()
|
|
|
|
},
|
|
|
|
SelectedEaseFunction(*function, color),
|
|
|
|
))
|
|
|
|
.with_children(|p| {
|
|
|
|
p.spawn(SpriteBundle {
|
|
|
|
sprite: Sprite {
|
|
|
|
custom_size: Some(Vec2::new(5.0, 5.0)),
|
|
|
|
color,
|
|
|
|
..default()
|
|
|
|
},
|
2024-10-08 22:40:40 +00:00
|
|
|
transform: Transform::from_xyz(SIZE_PER_FUNCTION + 5.0, 15.0, 0.0),
|
2024-10-07 18:31:43 +00:00
|
|
|
..default()
|
|
|
|
});
|
|
|
|
p.spawn(SpriteBundle {
|
|
|
|
sprite: Sprite {
|
|
|
|
custom_size: Some(Vec2::new(4.0, 4.0)),
|
|
|
|
color,
|
|
|
|
..default()
|
|
|
|
},
|
|
|
|
transform: Transform::from_xyz(0.0, 0.0, 0.0),
|
|
|
|
..default()
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
commands.spawn(
|
|
|
|
TextBundle::from_section("", TextStyle::default()).with_style(Style {
|
|
|
|
position_type: PositionType::Absolute,
|
|
|
|
bottom: Val::Px(12.0),
|
|
|
|
left: Val::Px(12.0),
|
|
|
|
..default()
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2024-10-08 22:40:40 +00:00
|
|
|
const SIZE_PER_FUNCTION: f32 = 95.0;
|
|
|
|
|
2024-10-07 18:31:43 +00:00
|
|
|
fn display_curves(
|
|
|
|
mut gizmos: Gizmos,
|
|
|
|
ease_functions: Query<(&SelectedEaseFunction, &Transform, &Children)>,
|
|
|
|
mut transforms: Query<&mut Transform, Without<SelectedEaseFunction>>,
|
|
|
|
mut ui: Query<&mut Text, With<Node>>,
|
|
|
|
time: Res<Time>,
|
|
|
|
) {
|
|
|
|
let samples = 100;
|
|
|
|
let duration = 2.5;
|
|
|
|
let time_margin = 0.5;
|
|
|
|
|
|
|
|
let now = ((time.elapsed_seconds() % (duration + time_margin * 2.0) - time_margin) / duration)
|
|
|
|
.clamp(0.0, 1.0);
|
|
|
|
|
|
|
|
ui.single_mut().sections[0].value = format!("Progress: {:.2}", now);
|
|
|
|
|
|
|
|
for (SelectedEaseFunction(function, color), transform, children) in &ease_functions {
|
|
|
|
// Draw a box around the curve
|
|
|
|
gizmos.linestrip_2d(
|
|
|
|
[
|
|
|
|
Vec2::new(transform.translation.x, transform.translation.y + 15.0),
|
|
|
|
Vec2::new(
|
2024-10-08 22:40:40 +00:00
|
|
|
transform.translation.x + SIZE_PER_FUNCTION,
|
2024-10-07 18:31:43 +00:00
|
|
|
transform.translation.y + 15.0,
|
|
|
|
),
|
|
|
|
Vec2::new(
|
2024-10-08 22:40:40 +00:00
|
|
|
transform.translation.x + SIZE_PER_FUNCTION,
|
|
|
|
transform.translation.y + 15.0 + SIZE_PER_FUNCTION,
|
2024-10-07 18:31:43 +00:00
|
|
|
),
|
|
|
|
Vec2::new(
|
|
|
|
transform.translation.x,
|
2024-10-08 22:40:40 +00:00
|
|
|
transform.translation.y + 15.0 + SIZE_PER_FUNCTION,
|
2024-10-07 18:31:43 +00:00
|
|
|
),
|
|
|
|
Vec2::new(transform.translation.x, transform.translation.y + 15.0),
|
|
|
|
],
|
|
|
|
color.darker(0.4),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Draw the curve
|
Simplified easing curves (#15711)
# Objective
Simplify the API surrounding easing curves. Broaden the base of types
that support easing.
## Solution
There is now a single library function, `easing_curve`, which constructs
a unit-parametrized easing curve between two values based on an
`EaseFunction`:
```rust
/// Given a `start` and `end` value, create a curve parametrized over [the unit interval]
/// that connects them, using the given [ease function] to determine the form of the
/// curve in between.
///
/// [the unit interval]: Interval::UNIT
/// [ease function]: EaseFunction
pub fn easing_curve<T: Ease>(start: T, end: T, ease_fn: EaseFunction) -> EasingCurve<T> { //... }
```
As this shows, the type of the output curve is generic only in `T`. In
particular, as long as `T` is `Reflect` (and `FromReflect` etc. — i.e.,
a standard "well-behaved" reflectable type), `EasingCurve<T>` is also
`Reflect`, and there is no special field handling nonsense. Therefore,
`EasingCurve` is the kind of thing that would be able to be easily
changed in an editor. This is made possible by storing the actual
`EaseFunction` on `EasingCurve<T>` instead of indirecting through some
kind of function type (which generally leads to issues with reflection).
The types that can be eased are those that implement a trait `Ease`:
```rust
/// A type whose values can be eased between.
///
/// This requires the construction of an interpolation curve that actually extends
/// beyond the curve segment that connects two values, because an easing curve may
/// extrapolate before the starting value and after the ending value. This is
/// especially common in easing functions that mimic elastic or springlike behavior.
pub trait Ease: Sized {
/// Given `start` and `end` values, produce a curve with [unlimited domain]
/// that:
/// - takes a value equivalent to `start` at `t = 0`
/// - takes a value equivalent to `end` at `t = 1`
/// - has constant speed everywhere, including outside of `[0, 1]`
///
/// [unlimited domain]: Interval::EVERYWHERE
fn interpolating_curve_unbounded(start: &Self, end: &Self) -> impl Curve<Self>;
}
```
(I know, I know, yet *another* interpolation trait. See 'Future
direction'.)
The other existing easing functions from the previous version of this
module have also become new members of `EaseFunction`: `Linear`,
`Steps`, and `Elastic` (which maybe needs a different name). The latter
two are parametrized.
## Testing
Tested using the `easing_functions` example. I also axed the
`cubic_curve` example which was of questionable value and replaced it
with `eased_motion`, which uses this API in the context of animation:
https://github.com/user-attachments/assets/3c802992-6b9b-4b56-aeb1-a47501c29ce2
---
## Future direction
Morally speaking, `Ease` is incredibly similar to `StableInterpolate`.
Probably, we should just merge `StableInterpolate` into `Ease`, and then
make `SmoothNudge` an automatic extension trait of `Ease`. The reason I
didn't do that is that `StableInterpolate` is not implemented for
`VectorSpace` because of concerns about the `Color` types, and I wanted
to avoid controversy. I think that may be a good idea though.
As Alice mentioned before, we should also probably get rid of the
`interpolation` dependency.
The parametrized `Elastic` variant probably also needs some additional
work (e.g. renaming, in/out/in-out variants, etc.) if we want to keep
it.
2024-10-08 19:45:13 +00:00
|
|
|
let f = easing_curve(0.0, 1.0, *function);
|
|
|
|
let drawn_curve = f.by_ref().graph().map(|(x, y)| {
|
|
|
|
Vec2::new(
|
2024-10-08 22:40:40 +00:00
|
|
|
x * SIZE_PER_FUNCTION + transform.translation.x,
|
|
|
|
y * SIZE_PER_FUNCTION + transform.translation.y + 15.0,
|
Simplified easing curves (#15711)
# Objective
Simplify the API surrounding easing curves. Broaden the base of types
that support easing.
## Solution
There is now a single library function, `easing_curve`, which constructs
a unit-parametrized easing curve between two values based on an
`EaseFunction`:
```rust
/// Given a `start` and `end` value, create a curve parametrized over [the unit interval]
/// that connects them, using the given [ease function] to determine the form of the
/// curve in between.
///
/// [the unit interval]: Interval::UNIT
/// [ease function]: EaseFunction
pub fn easing_curve<T: Ease>(start: T, end: T, ease_fn: EaseFunction) -> EasingCurve<T> { //... }
```
As this shows, the type of the output curve is generic only in `T`. In
particular, as long as `T` is `Reflect` (and `FromReflect` etc. — i.e.,
a standard "well-behaved" reflectable type), `EasingCurve<T>` is also
`Reflect`, and there is no special field handling nonsense. Therefore,
`EasingCurve` is the kind of thing that would be able to be easily
changed in an editor. This is made possible by storing the actual
`EaseFunction` on `EasingCurve<T>` instead of indirecting through some
kind of function type (which generally leads to issues with reflection).
The types that can be eased are those that implement a trait `Ease`:
```rust
/// A type whose values can be eased between.
///
/// This requires the construction of an interpolation curve that actually extends
/// beyond the curve segment that connects two values, because an easing curve may
/// extrapolate before the starting value and after the ending value. This is
/// especially common in easing functions that mimic elastic or springlike behavior.
pub trait Ease: Sized {
/// Given `start` and `end` values, produce a curve with [unlimited domain]
/// that:
/// - takes a value equivalent to `start` at `t = 0`
/// - takes a value equivalent to `end` at `t = 1`
/// - has constant speed everywhere, including outside of `[0, 1]`
///
/// [unlimited domain]: Interval::EVERYWHERE
fn interpolating_curve_unbounded(start: &Self, end: &Self) -> impl Curve<Self>;
}
```
(I know, I know, yet *another* interpolation trait. See 'Future
direction'.)
The other existing easing functions from the previous version of this
module have also become new members of `EaseFunction`: `Linear`,
`Steps`, and `Elastic` (which maybe needs a different name). The latter
two are parametrized.
## Testing
Tested using the `easing_functions` example. I also axed the
`cubic_curve` example which was of questionable value and replaced it
with `eased_motion`, which uses this API in the context of animation:
https://github.com/user-attachments/assets/3c802992-6b9b-4b56-aeb1-a47501c29ce2
---
## Future direction
Morally speaking, `Ease` is incredibly similar to `StableInterpolate`.
Probably, we should just merge `StableInterpolate` into `Ease`, and then
make `SmoothNudge` an automatic extension trait of `Ease`. The reason I
didn't do that is that `StableInterpolate` is not implemented for
`VectorSpace` because of concerns about the `Color` types, and I wanted
to avoid controversy. I think that may be a good idea though.
As Alice mentioned before, we should also probably get rid of the
`interpolation` dependency.
The parametrized `Elastic` variant probably also needs some additional
work (e.g. renaming, in/out/in-out variants, etc.) if we want to keep
it.
2024-10-08 19:45:13 +00:00
|
|
|
)
|
|
|
|
});
|
|
|
|
gizmos.curve_2d(
|
|
|
|
&drawn_curve,
|
|
|
|
drawn_curve.domain().spaced_points(samples).unwrap(),
|
2024-10-07 18:31:43 +00:00
|
|
|
*color,
|
|
|
|
);
|
|
|
|
|
|
|
|
// Show progress along the curve for the current time
|
2024-10-08 22:40:40 +00:00
|
|
|
let y = f.sample(now).unwrap() * SIZE_PER_FUNCTION + 15.0;
|
2024-10-07 18:31:43 +00:00
|
|
|
transforms.get_mut(children[0]).unwrap().translation.y = y;
|
2024-10-08 22:40:40 +00:00
|
|
|
transforms.get_mut(children[1]).unwrap().translation =
|
|
|
|
Vec3::new(now * SIZE_PER_FUNCTION, y, 0.0);
|
2024-10-07 18:31:43 +00:00
|
|
|
gizmos.linestrip_2d(
|
|
|
|
[
|
|
|
|
Vec2::new(transform.translation.x, transform.translation.y + y),
|
2024-10-08 22:40:40 +00:00
|
|
|
Vec2::new(
|
|
|
|
transform.translation.x + SIZE_PER_FUNCTION,
|
|
|
|
transform.translation.y + y,
|
|
|
|
),
|
2024-10-07 18:31:43 +00:00
|
|
|
],
|
|
|
|
color.darker(0.2),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|