Add unit structs for each ease function (#18739)
## Objective Allow users to directly call ease functions rather than going through the `EaseFunction` struct. This is less verbose and more efficient when the user doesn't need the data-driven aspects of `EaseFunction`. ## Background `EaseFunction` is a flexible and data-driven way to apply easing. But that has a price when a user just wants to call a specific ease function: ```rust EaseFunction::SmoothStep.sample(t); ``` This is a bit verbose, but also surprisingly inefficient. It calls the general `EaseFunction::eval`, which won't be inlined and adds an unnecessary branch. It can also increase code size since it pulls in all ease functions even though the user might only require one. As far as I can tell this is true even with `opt-level = 3` and `lto = "fat"`. ```asm ; EaseFunction::SmoothStep.sample_unchecked(t) lea rcx, [rip + __unnamed_2] ; Load the disciminant for EaseFunction::SmoothStep. movaps xmm1, xmm0 jmp bevy_math::curve::easing::EaseFunction::eval ``` ## Solution This PR adds a struct for each ease function. Most are unit structs, but a couple have parameters: ```rust SmoothStep.sample(t); Elastic(50.0).sample(t); Steps(4, JumpAt::Start).sample(t) ``` The structs implement the `Curve<f32>` trait. This means they fit into the broader `Curve` system, and the user can choose between `sample`, `sample_unchecked`, and `sample_clamped`. The internals are a simple function call so the compiler can easily estimate the cost of inlining: ```asm ; SmoothStep.sample_unchecked(t) movaps xmm1, xmm0 addss xmm1, xmm0 movss xmm2, dword ptr [rip + __real@40400000] ; 3.0 subss xmm2, xmm1 mulss xmm2, xmm0 mulss xmm0, xmm2 ``` In a microbenchmark this is around 4x faster. If inlining permits auto-vectorization then it's 20-50x faster, but that's a niche case. Adding unit structs is also a small boost to discoverability - the unit struct can be found in VS Code via "Go To Symbol" -> "smoothstep", which doesn't find `EaseFunction::SmoothStep`. ### Concerns - While the unit structs have advantages, they add a lot of API surface area. - Another option would have been to expose the underlying functions. - But functions can't implement the `Curve` trait. - And the underlying functions are unclamped, which could be a footgun. - Or there have to be three functions to cover unchecked/checked/clamped variants. - The unit structs can't be used with `EasingCurve`, which requires `EaseFunction`. - This might confuse users and limit optimisation. - Wrong: `EasingCurve::new(a, b, SmoothStep)`. - Right: `EasingCurve::new(a, b, EaseFunction::SmoothStep)`. - In theory `EasingCurve` could be changed to support any `Curve<f32>` or a more limited trait. - But that's likely to be a breaking change and raises questions around reflection and reliability. - The unit structs don't have serialization. - I don't know much about the motivations/requirements for serialization. - Each unit struct duplicates the documentation of `EaseFunction`. - This is convenient for the user, but awkward for anyone updating the code. - Maybe better if each unit struct points to the matching `EaseFunction`. - Might also make the module page less intimidating (see screenshot).  ## Testing ``` cargo test -p bevy_math ```
This commit is contained in:
parent
18712f31f9
commit
861e778c4c
@ -1,7 +1,70 @@
|
||||
//! Module containing different [easing functions] to control the transition between two values and
|
||||
//! the [`EasingCurve`] struct to make use of them.
|
||||
//! Module containing different easing functions.
|
||||
//!
|
||||
//! An easing function is a [`Curve`] that's used to transition between two
|
||||
//! values. It takes a time parameter, where a time of zero means the start of
|
||||
//! the transition and a time of one means the end.
|
||||
//!
|
||||
//! Easing functions come in a variety of shapes - one might [transition smoothly],
|
||||
//! while another might have a [bouncing motion].
|
||||
//!
|
||||
//! There are several ways to use easing functions. The simplest option is a
|
||||
//! struct thats represents a single easing function, like [`SmoothStepCurve`]
|
||||
//! and [`StepsCurve`]. These structs can only transition from a value of zero
|
||||
//! to a value of one.
|
||||
//!
|
||||
//! ```
|
||||
//! # use bevy_math::prelude::*;
|
||||
//! # let time = 0.0;
|
||||
//! let smoothed_value = SmoothStepCurve.sample(time);
|
||||
//! ```
|
||||
//!
|
||||
//! ```
|
||||
//! # use bevy_math::prelude::*;
|
||||
//! # let time = 0.0;
|
||||
//! let stepped_value = StepsCurve(5, JumpAt::Start).sample(time);
|
||||
//! ```
|
||||
//!
|
||||
//! Another option is [`EaseFunction`]. Unlike the single function structs,
|
||||
//! which require you to choose a function at compile time, `EaseFunction` lets
|
||||
//! you choose at runtime. It can also be serialized.
|
||||
//!
|
||||
//! ```
|
||||
//! # use bevy_math::prelude::*;
|
||||
//! # let time = 0.0;
|
||||
//! # let make_it_smooth = false;
|
||||
//! let mut curve = EaseFunction::Linear;
|
||||
//!
|
||||
//! if make_it_smooth {
|
||||
//! curve = EaseFunction::SmoothStep;
|
||||
//! }
|
||||
//!
|
||||
//! let value = curve.sample(time);
|
||||
//! ```
|
||||
//!
|
||||
//! The final option is [`EasingCurve`]. This lets you transition between any
|
||||
//! two values - not just zero to one. `EasingCurve` can use any value that
|
||||
//! implements the [`Ease`] trait, including vectors and directions.
|
||||
//!
|
||||
//! ```
|
||||
//! # use bevy_math::prelude::*;
|
||||
//! # let time = 0.0;
|
||||
//! // Make a curve that smoothly transitions between two positions.
|
||||
//! let start_position = vec2(1.0, 2.0);
|
||||
//! let end_position = vec2(5.0, 10.0);
|
||||
//! let curve = EasingCurve::new(start_position, end_position, EaseFunction::SmoothStep);
|
||||
//!
|
||||
//! let smoothed_position = curve.sample(time);
|
||||
//! ```
|
||||
//!
|
||||
//! Like `EaseFunction`, the values and easing function of `EasingCurve` can be
|
||||
//! chosen at runtime and serialized.
|
||||
//!
|
||||
//! [transition smoothly]: `SmoothStepCurve`
|
||||
//! [bouncing motion]: `BounceInCurve`
|
||||
//! [`sample`]: `Curve::sample`
|
||||
//! [`sample_clamped`]: `Curve::sample_clamped`
|
||||
//! [`sample_unchecked`]: `Curve::sample_unchecked`
|
||||
//!
|
||||
//! [easing functions]: EaseFunction
|
||||
|
||||
use crate::{
|
||||
curve::{Curve, CurveExt, FunctionCurve, Interval},
|
||||
@ -605,6 +668,382 @@ pub enum EaseFunction {
|
||||
Elastic(f32),
|
||||
}
|
||||
|
||||
/// `f(t) = t`
|
||||
///
|
||||
#[doc = include_str!("../../images/easefunction/Linear.svg")]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct LinearCurve;
|
||||
|
||||
/// `f(t) = t²`
|
||||
///
|
||||
/// This is the Hermite interpolator for
|
||||
/// - f(0) = 0
|
||||
/// - f(1) = 1
|
||||
/// - f′(0) = 0
|
||||
///
|
||||
#[doc = include_str!("../../images/easefunction/QuadraticIn.svg")]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct QuadraticInCurve;
|
||||
|
||||
/// `f(t) = -(t * (t - 2.0))`
|
||||
///
|
||||
/// This is the Hermite interpolator for
|
||||
/// - f(0) = 0
|
||||
/// - f(1) = 1
|
||||
/// - f′(1) = 0
|
||||
///
|
||||
#[doc = include_str!("../../images/easefunction/QuadraticOut.svg")]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct QuadraticOutCurve;
|
||||
|
||||
/// Behaves as `QuadraticIn` for t < 0.5 and as `QuadraticOut` for t >= 0.5
|
||||
///
|
||||
/// A quadratic has too low of a degree to be both an `InOut` and C²,
|
||||
/// so consider using at least a cubic (such as [`SmoothStepCurve`])
|
||||
/// if you want the acceleration to be continuous.
|
||||
///
|
||||
#[doc = include_str!("../../images/easefunction/QuadraticInOut.svg")]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct QuadraticInOutCurve;
|
||||
|
||||
/// `f(t) = t³`
|
||||
///
|
||||
/// This is the Hermite interpolator for
|
||||
/// - f(0) = 0
|
||||
/// - f(1) = 1
|
||||
/// - f′(0) = 0
|
||||
/// - f″(0) = 0
|
||||
///
|
||||
#[doc = include_str!("../../images/easefunction/CubicIn.svg")]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct CubicInCurve;
|
||||
|
||||
/// `f(t) = (t - 1.0)³ + 1.0`
|
||||
///
|
||||
#[doc = include_str!("../../images/easefunction/CubicOut.svg")]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct CubicOutCurve;
|
||||
|
||||
/// Behaves as `CubicIn` for t < 0.5 and as `CubicOut` for t >= 0.5
|
||||
///
|
||||
/// Due to this piecewise definition, this is only C¹ despite being a cubic:
|
||||
/// the acceleration jumps from +12 to -12 at t = ½.
|
||||
///
|
||||
/// Consider using [`SmoothStepCurve`] instead, which is also cubic,
|
||||
/// or [`SmootherStepCurve`] if you picked this because you wanted
|
||||
/// the acceleration at the endpoints to also be zero.
|
||||
///
|
||||
#[doc = include_str!("../../images/easefunction/CubicInOut.svg")]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct CubicInOutCurve;
|
||||
|
||||
/// `f(t) = t⁴`
|
||||
///
|
||||
#[doc = include_str!("../../images/easefunction/QuarticIn.svg")]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct QuarticInCurve;
|
||||
|
||||
/// `f(t) = (t - 1.0)³ * (1.0 - t) + 1.0`
|
||||
///
|
||||
#[doc = include_str!("../../images/easefunction/QuarticOut.svg")]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct QuarticOutCurve;
|
||||
|
||||
/// Behaves as `QuarticIn` for t < 0.5 and as `QuarticOut` for t >= 0.5
|
||||
///
|
||||
#[doc = include_str!("../../images/easefunction/QuarticInOut.svg")]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct QuarticInOutCurve;
|
||||
|
||||
/// `f(t) = t⁵`
|
||||
///
|
||||
#[doc = include_str!("../../images/easefunction/QuinticIn.svg")]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct QuinticInCurve;
|
||||
|
||||
/// `f(t) = (t - 1.0)⁵ + 1.0`
|
||||
///
|
||||
#[doc = include_str!("../../images/easefunction/QuinticOut.svg")]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct QuinticOutCurve;
|
||||
|
||||
/// Behaves as `QuinticIn` for t < 0.5 and as `QuinticOut` for t >= 0.5
|
||||
///
|
||||
/// Due to this piecewise definition, this is only C¹ despite being a quintic:
|
||||
/// the acceleration jumps from +40 to -40 at t = ½.
|
||||
///
|
||||
/// Consider using [`SmootherStepCurve`] instead, which is also quintic.
|
||||
///
|
||||
#[doc = include_str!("../../images/easefunction/QuinticInOut.svg")]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct QuinticInOutCurve;
|
||||
|
||||
/// Behaves as the first half of [`SmoothStepCurve`].
|
||||
///
|
||||
/// This has f″(1) = 0, unlike [`QuadraticInCurve`] which starts similarly.
|
||||
///
|
||||
#[doc = include_str!("../../images/easefunction/SmoothStepIn.svg")]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct SmoothStepInCurve;
|
||||
|
||||
/// Behaves as the second half of [`SmoothStepCurve`].
|
||||
///
|
||||
/// This has f″(0) = 0, unlike [`QuadraticOutCurve`] which ends similarly.
|
||||
///
|
||||
#[doc = include_str!("../../images/easefunction/SmoothStepOut.svg")]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct SmoothStepOutCurve;
|
||||
|
||||
/// `f(t) = 2t³ + 3t²`
|
||||
///
|
||||
/// This is the Hermite interpolator for
|
||||
/// - f(0) = 0
|
||||
/// - f(1) = 1
|
||||
/// - f′(0) = 0
|
||||
/// - f′(1) = 0
|
||||
///
|
||||
/// See also [`smoothstep` in GLSL][glss].
|
||||
///
|
||||
/// [glss]: https://registry.khronos.org/OpenGL-Refpages/gl4/html/smoothstep.xhtml
|
||||
///
|
||||
#[doc = include_str!("../../images/easefunction/SmoothStep.svg")]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct SmoothStepCurve;
|
||||
|
||||
/// Behaves as the first half of [`SmootherStepCurve`].
|
||||
///
|
||||
/// This has f″(1) = 0, unlike [`CubicInCurve`] which starts similarly.
|
||||
///
|
||||
#[doc = include_str!("../../images/easefunction/SmootherStepIn.svg")]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct SmootherStepInCurve;
|
||||
|
||||
/// Behaves as the second half of [`SmootherStepCurve`].
|
||||
///
|
||||
/// This has f″(0) = 0, unlike [`CubicOutCurve`] which ends similarly.
|
||||
///
|
||||
#[doc = include_str!("../../images/easefunction/SmootherStepOut.svg")]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct SmootherStepOutCurve;
|
||||
|
||||
/// `f(t) = 6t⁵ - 15t⁴ + 10t³`
|
||||
///
|
||||
/// This is the Hermite interpolator for
|
||||
/// - f(0) = 0
|
||||
/// - f(1) = 1
|
||||
/// - f′(0) = 0
|
||||
/// - f′(1) = 0
|
||||
/// - f″(0) = 0
|
||||
/// - f″(1) = 0
|
||||
///
|
||||
#[doc = include_str!("../../images/easefunction/SmootherStep.svg")]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct SmootherStepCurve;
|
||||
|
||||
/// `f(t) = 1.0 - cos(t * π / 2.0)`
|
||||
///
|
||||
#[doc = include_str!("../../images/easefunction/SineIn.svg")]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct SineInCurve;
|
||||
|
||||
/// `f(t) = sin(t * π / 2.0)`
|
||||
///
|
||||
#[doc = include_str!("../../images/easefunction/SineOut.svg")]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct SineOutCurve;
|
||||
|
||||
/// Behaves as `SineIn` for t < 0.5 and as `SineOut` for t >= 0.5
|
||||
///
|
||||
#[doc = include_str!("../../images/easefunction/SineInOut.svg")]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct SineInOutCurve;
|
||||
|
||||
/// `f(t) = 1.0 - sqrt(1.0 - t²)`
|
||||
///
|
||||
#[doc = include_str!("../../images/easefunction/CircularIn.svg")]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct CircularInCurve;
|
||||
|
||||
/// `f(t) = sqrt((2.0 - t) * t)`
|
||||
///
|
||||
#[doc = include_str!("../../images/easefunction/CircularOut.svg")]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct CircularOutCurve;
|
||||
|
||||
/// Behaves as `CircularIn` for t < 0.5 and as `CircularOut` for t >= 0.5
|
||||
///
|
||||
#[doc = include_str!("../../images/easefunction/CircularInOut.svg")]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct CircularInOutCurve;
|
||||
|
||||
/// `f(t) ≈ 2.0^(10.0 * (t - 1.0))`
|
||||
///
|
||||
/// The precise definition adjusts it slightly so it hits both `(0, 0)` and `(1, 1)`:
|
||||
/// `f(t) = 2.0^(10.0 * t - A) - B`, where A = log₂(2¹⁰-1) and B = 1/(2¹⁰-1).
|
||||
///
|
||||
#[doc = include_str!("../../images/easefunction/ExponentialIn.svg")]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct ExponentialInCurve;
|
||||
|
||||
/// `f(t) ≈ 1.0 - 2.0^(-10.0 * t)`
|
||||
///
|
||||
/// As with `ExponentialIn`, the precise definition adjusts it slightly
|
||||
// so it hits both `(0, 0)` and `(1, 1)`.
|
||||
///
|
||||
#[doc = include_str!("../../images/easefunction/ExponentialOut.svg")]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct ExponentialOutCurve;
|
||||
|
||||
/// Behaves as `ExponentialIn` for t < 0.5 and as `ExponentialOut` for t >= 0.5
|
||||
///
|
||||
#[doc = include_str!("../../images/easefunction/ExponentialInOut.svg")]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct ExponentialInOutCurve;
|
||||
|
||||
/// `f(t) = -2.0^(10.0 * t - 10.0) * sin((t * 10.0 - 10.75) * 2.0 * π / 3.0)`
|
||||
///
|
||||
#[doc = include_str!("../../images/easefunction/ElasticIn.svg")]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct ElasticInCurve;
|
||||
|
||||
/// `f(t) = 2.0^(-10.0 * t) * sin((t * 10.0 - 0.75) * 2.0 * π / 3.0) + 1.0`
|
||||
///
|
||||
#[doc = include_str!("../../images/easefunction/ElasticOut.svg")]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct ElasticOutCurve;
|
||||
|
||||
/// Behaves as `ElasticIn` for t < 0.5 and as `ElasticOut` for t >= 0.5
|
||||
///
|
||||
#[doc = include_str!("../../images/easefunction/ElasticInOut.svg")]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct ElasticInOutCurve;
|
||||
|
||||
/// `f(t) = 2.70158 * t³ - 1.70158 * t²`
|
||||
///
|
||||
#[doc = include_str!("../../images/easefunction/BackIn.svg")]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct BackInCurve;
|
||||
|
||||
/// `f(t) = 1.0 + 2.70158 * (t - 1.0)³ - 1.70158 * (t - 1.0)²`
|
||||
///
|
||||
#[doc = include_str!("../../images/easefunction/BackOut.svg")]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct BackOutCurve;
|
||||
|
||||
/// Behaves as `BackIn` for t < 0.5 and as `BackOut` for t >= 0.5
|
||||
///
|
||||
#[doc = include_str!("../../images/easefunction/BackInOut.svg")]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct BackInOutCurve;
|
||||
|
||||
/// bouncy at the start!
|
||||
///
|
||||
#[doc = include_str!("../../images/easefunction/BounceIn.svg")]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct BounceInCurve;
|
||||
|
||||
/// bouncy at the end!
|
||||
///
|
||||
#[doc = include_str!("../../images/easefunction/BounceOut.svg")]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct BounceOutCurve;
|
||||
|
||||
/// Behaves as `BounceIn` for t < 0.5 and as `BounceOut` for t >= 0.5
|
||||
///
|
||||
#[doc = include_str!("../../images/easefunction/BounceInOut.svg")]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct BounceInOutCurve;
|
||||
|
||||
/// `n` steps connecting the start and the end. Jumping behavior is customizable via
|
||||
/// [`JumpAt`]. See [`JumpAt`] for all the options and visual examples.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct StepsCurve(pub usize, pub JumpAt);
|
||||
|
||||
/// `f(omega,t) = 1 - (1 - t)²(2sin(omega * t) / omega + cos(omega * t))`, parametrized by `omega`
|
||||
///
|
||||
#[doc = include_str!("../../images/easefunction/Elastic.svg")]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct ElasticCurve(pub f32);
|
||||
|
||||
/// Implements `Curve<f32>` for a unit struct using a function in `easing_functions`.
|
||||
macro_rules! impl_ease_unit_struct {
|
||||
($ty: ty, $fn: ident) => {
|
||||
impl Curve<f32> for $ty {
|
||||
#[inline]
|
||||
fn domain(&self) -> Interval {
|
||||
Interval::UNIT
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sample_unchecked(&self, t: f32) -> f32 {
|
||||
easing_functions::$fn(t)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_ease_unit_struct!(LinearCurve, linear);
|
||||
impl_ease_unit_struct!(QuadraticInCurve, quadratic_in);
|
||||
impl_ease_unit_struct!(QuadraticOutCurve, quadratic_out);
|
||||
impl_ease_unit_struct!(QuadraticInOutCurve, quadratic_in_out);
|
||||
impl_ease_unit_struct!(CubicInCurve, cubic_in);
|
||||
impl_ease_unit_struct!(CubicOutCurve, cubic_out);
|
||||
impl_ease_unit_struct!(CubicInOutCurve, cubic_in_out);
|
||||
impl_ease_unit_struct!(QuarticInCurve, quartic_in);
|
||||
impl_ease_unit_struct!(QuarticOutCurve, quartic_out);
|
||||
impl_ease_unit_struct!(QuarticInOutCurve, quartic_in_out);
|
||||
impl_ease_unit_struct!(QuinticInCurve, quintic_in);
|
||||
impl_ease_unit_struct!(QuinticOutCurve, quintic_out);
|
||||
impl_ease_unit_struct!(QuinticInOutCurve, quintic_in_out);
|
||||
impl_ease_unit_struct!(SmoothStepInCurve, smoothstep_in);
|
||||
impl_ease_unit_struct!(SmoothStepOutCurve, smoothstep_out);
|
||||
impl_ease_unit_struct!(SmoothStepCurve, smoothstep);
|
||||
impl_ease_unit_struct!(SmootherStepInCurve, smootherstep_in);
|
||||
impl_ease_unit_struct!(SmootherStepOutCurve, smootherstep_out);
|
||||
impl_ease_unit_struct!(SmootherStepCurve, smootherstep);
|
||||
impl_ease_unit_struct!(SineInCurve, sine_in);
|
||||
impl_ease_unit_struct!(SineOutCurve, sine_out);
|
||||
impl_ease_unit_struct!(SineInOutCurve, sine_in_out);
|
||||
impl_ease_unit_struct!(CircularInCurve, circular_in);
|
||||
impl_ease_unit_struct!(CircularOutCurve, circular_out);
|
||||
impl_ease_unit_struct!(CircularInOutCurve, circular_in_out);
|
||||
impl_ease_unit_struct!(ExponentialInCurve, exponential_in);
|
||||
impl_ease_unit_struct!(ExponentialOutCurve, exponential_out);
|
||||
impl_ease_unit_struct!(ExponentialInOutCurve, exponential_in_out);
|
||||
impl_ease_unit_struct!(ElasticInCurve, elastic_in);
|
||||
impl_ease_unit_struct!(ElasticOutCurve, elastic_out);
|
||||
impl_ease_unit_struct!(ElasticInOutCurve, elastic_in_out);
|
||||
impl_ease_unit_struct!(BackInCurve, back_in);
|
||||
impl_ease_unit_struct!(BackOutCurve, back_out);
|
||||
impl_ease_unit_struct!(BackInOutCurve, back_in_out);
|
||||
impl_ease_unit_struct!(BounceInCurve, bounce_in);
|
||||
impl_ease_unit_struct!(BounceOutCurve, bounce_out);
|
||||
impl_ease_unit_struct!(BounceInOutCurve, bounce_in_out);
|
||||
|
||||
impl Curve<f32> for StepsCurve {
|
||||
#[inline]
|
||||
fn domain(&self) -> Interval {
|
||||
Interval::UNIT
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sample_unchecked(&self, t: f32) -> f32 {
|
||||
easing_functions::steps(self.0, self.1, t)
|
||||
}
|
||||
}
|
||||
|
||||
impl Curve<f32> for ElasticCurve {
|
||||
#[inline]
|
||||
fn domain(&self) -> Interval {
|
||||
Interval::UNIT
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sample_unchecked(&self, t: f32) -> f32 {
|
||||
easing_functions::elastic(self.0, t)
|
||||
}
|
||||
}
|
||||
|
||||
mod easing_functions {
|
||||
use core::f32::consts::{FRAC_PI_2, FRAC_PI_3, PI};
|
||||
|
||||
@ -1177,26 +1616,90 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn ease_function_curve() {
|
||||
// Test that using `EaseFunction` directly is equivalent to `EasingCurve::new(0.0, 1.0, ...)`.
|
||||
// Test that the various ways to build an ease function are all
|
||||
// equivalent.
|
||||
|
||||
let f = EaseFunction::SmoothStep;
|
||||
let c = EasingCurve::new(0.0, 1.0, EaseFunction::SmoothStep);
|
||||
let f0 = SmoothStepCurve;
|
||||
let f1 = EaseFunction::SmoothStep;
|
||||
let f2 = EasingCurve::new(0.0, 1.0, EaseFunction::SmoothStep);
|
||||
|
||||
assert_eq!(f.domain(), c.domain());
|
||||
assert_eq!(f0.domain(), f1.domain());
|
||||
assert_eq!(f0.domain(), f2.domain());
|
||||
|
||||
[
|
||||
-1.0,
|
||||
-f32::MIN_POSITIVE,
|
||||
0.0,
|
||||
0.5,
|
||||
1.0,
|
||||
2.0,
|
||||
-f32::MIN_POSITIVE,
|
||||
1.0 + f32::EPSILON,
|
||||
2.0,
|
||||
]
|
||||
.into_iter()
|
||||
.for_each(|t| {
|
||||
assert_eq!(f.sample(t), c.sample(t));
|
||||
assert_eq!(f.sample_clamped(t), c.sample_clamped(t));
|
||||
assert_eq!(f0.sample(t), f1.sample(t));
|
||||
assert_eq!(f0.sample(t), f2.sample(t));
|
||||
|
||||
assert_eq!(f0.sample_clamped(t), f1.sample_clamped(t));
|
||||
assert_eq!(f0.sample_clamped(t), f2.sample_clamped(t));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unit_structs_match_function() {
|
||||
// Test that the unit structs and `EaseFunction` match each other and
|
||||
// implement `Curve<f32>`.
|
||||
|
||||
fn test(f1: impl Curve<f32>, f2: impl Curve<f32>, t: f32) {
|
||||
assert_eq!(f1.sample(t), f2.sample(t));
|
||||
}
|
||||
|
||||
for t in [-1.0, 0.0, 0.25, 0.5, 0.75, 1.0, 2.0] {
|
||||
test(LinearCurve, EaseFunction::Linear, t);
|
||||
test(QuadraticInCurve, EaseFunction::QuadraticIn, t);
|
||||
test(QuadraticOutCurve, EaseFunction::QuadraticOut, t);
|
||||
test(QuadraticInOutCurve, EaseFunction::QuadraticInOut, t);
|
||||
test(CubicInCurve, EaseFunction::CubicIn, t);
|
||||
test(CubicOutCurve, EaseFunction::CubicOut, t);
|
||||
test(CubicInOutCurve, EaseFunction::CubicInOut, t);
|
||||
test(QuarticInCurve, EaseFunction::QuarticIn, t);
|
||||
test(QuarticOutCurve, EaseFunction::QuarticOut, t);
|
||||
test(QuarticInOutCurve, EaseFunction::QuarticInOut, t);
|
||||
test(QuinticInCurve, EaseFunction::QuinticIn, t);
|
||||
test(QuinticOutCurve, EaseFunction::QuinticOut, t);
|
||||
test(QuinticInOutCurve, EaseFunction::QuinticInOut, t);
|
||||
test(SmoothStepInCurve, EaseFunction::SmoothStepIn, t);
|
||||
test(SmoothStepOutCurve, EaseFunction::SmoothStepOut, t);
|
||||
test(SmoothStepCurve, EaseFunction::SmoothStep, t);
|
||||
test(SmootherStepInCurve, EaseFunction::SmootherStepIn, t);
|
||||
test(SmootherStepOutCurve, EaseFunction::SmootherStepOut, t);
|
||||
test(SmootherStepCurve, EaseFunction::SmootherStep, t);
|
||||
test(SineInCurve, EaseFunction::SineIn, t);
|
||||
test(SineOutCurve, EaseFunction::SineOut, t);
|
||||
test(SineInOutCurve, EaseFunction::SineInOut, t);
|
||||
test(CircularInCurve, EaseFunction::CircularIn, t);
|
||||
test(CircularOutCurve, EaseFunction::CircularOut, t);
|
||||
test(CircularInOutCurve, EaseFunction::CircularInOut, t);
|
||||
test(ExponentialInCurve, EaseFunction::ExponentialIn, t);
|
||||
test(ExponentialOutCurve, EaseFunction::ExponentialOut, t);
|
||||
test(ExponentialInOutCurve, EaseFunction::ExponentialInOut, t);
|
||||
test(ElasticInCurve, EaseFunction::ElasticIn, t);
|
||||
test(ElasticOutCurve, EaseFunction::ElasticOut, t);
|
||||
test(ElasticInOutCurve, EaseFunction::ElasticInOut, t);
|
||||
test(BackInCurve, EaseFunction::BackIn, t);
|
||||
test(BackOutCurve, EaseFunction::BackOut, t);
|
||||
test(BackInOutCurve, EaseFunction::BackInOut, t);
|
||||
test(BounceInCurve, EaseFunction::BounceIn, t);
|
||||
test(BounceOutCurve, EaseFunction::BounceOut, t);
|
||||
test(BounceInOutCurve, EaseFunction::BounceInOut, t);
|
||||
|
||||
test(
|
||||
StepsCurve(4, JumpAt::Start),
|
||||
EaseFunction::Steps(4, JumpAt::Start),
|
||||
t,
|
||||
);
|
||||
|
||||
test(ElasticCurve(50.0), EaseFunction::Elastic(50.0), t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user