From 7477928f1380ef8c56c00f184b7401c5afdcf678 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Wed, 13 Nov 2024 07:30:05 -0800 Subject: [PATCH] Use normal constructors for EasingCurve, FunctionCurve, ConstantCurve (#16367) # Objective We currently use special "floating" constructors for `EasingCurve`, `FunctionCurve`, and `ConstantCurve` (ex: `easing_curve`). This erases the type being created (and in general "what is happening" structurally), for very minimal ergonomics improvements. With rare exceptions, we prefer normal `X::new()` constructors over floating `x()` constructors in Bevy. I don't think this use case merits special casing here. ## Solution Add `EasingCurve::new()`, use normal constructors everywhere, and remove the floating constructors. I think this should land in 0.15 in the interest of not breaking people later. --- crates/bevy_animation/src/animation_curves.rs | 14 +-- crates/bevy_gizmos/src/curves.rs | 8 +- crates/bevy_gltf/src/loader.rs | 10 +- crates/bevy_math/src/curve/easing.rs | 45 ++++---- crates/bevy_math/src/curve/mod.rs | 109 ++++++++---------- examples/animation/eased_motion.rs | 4 +- examples/animation/easing_functions.rs | 2 +- examples/gizmos/2d_gizmos.rs | 2 +- examples/gizmos/3d_gizmos.rs | 2 +- 9 files changed, 90 insertions(+), 106 deletions(-) diff --git a/crates/bevy_animation/src/animation_curves.rs b/crates/bevy_animation/src/animation_curves.rs index fbe555ca3b..6ca1ec34c4 100644 --- a/crates/bevy_animation/src/animation_curves.rs +++ b/crates/bevy_animation/src/animation_curves.rs @@ -7,9 +7,9 @@ //! `Curve` that we want to use to animate something. That could be defined in //! a number of different ways, but let's imagine that we've defined it [using a function]: //! -//! # use bevy_math::curve::{Curve, Interval, function_curve}; +//! # use bevy_math::curve::{Curve, Interval, FunctionCurve}; //! # use bevy_math::vec3; -//! let wobble_curve = function_curve( +//! let wobble_curve = FunctionCurve::new( //! Interval::UNIT, //! |t| { vec3(t.cos(), 0.0, 0.0) }, //! ); @@ -25,10 +25,10 @@ //! the adaptor [`TranslationCurve`], which wraps any `Curve` and turns it into an //! [`AnimationCurve`] that will use the given curve to animate the entity's translation: //! -//! # use bevy_math::curve::{Curve, Interval, function_curve}; +//! # use bevy_math::curve::{Curve, Interval, FunctionCurve}; //! # use bevy_math::vec3; //! # use bevy_animation::animation_curves::*; -//! # let wobble_curve = function_curve( +//! # let wobble_curve = FunctionCurve::new( //! # Interval::UNIT, //! # |t| vec3(t.cos(), 0.0, 0.0) //! # ); @@ -37,11 +37,11 @@ //! And finally, this `AnimationCurve` needs to be added to an [`AnimationClip`] in order to //! actually animate something. This is what that looks like: //! -//! # use bevy_math::curve::{Curve, Interval, function_curve}; +//! # use bevy_math::curve::{Curve, Interval, FunctionCurve}; //! # use bevy_animation::{AnimationClip, AnimationTargetId, animation_curves::*}; //! # use bevy_core::Name; //! # use bevy_math::vec3; -//! # let wobble_curve = function_curve( +//! # let wobble_curve = FunctionCurve::new( //! # Interval::UNIT, //! # |t| { vec3(t.cos(), 0.0, 0.0) }, //! # ); @@ -71,7 +71,7 @@ //! Animation of arbitrary components can be accomplished using [`AnimatableProperty`] in //! conjunction with [`AnimatableCurve`]. See the documentation [there] for details. //! -//! [using a function]: bevy_math::curve::function_curve +//! [using a function]: bevy_math::curve::FunctionCurve //! [translation component of a `Transform`]: bevy_transform::prelude::Transform::translation //! [`AnimationClip`]: crate::AnimationClip //! [there]: AnimatableProperty diff --git a/crates/bevy_gizmos/src/curves.rs b/crates/bevy_gizmos/src/curves.rs index 4a7b1aec1e..be747626de 100644 --- a/crates/bevy_gizmos/src/curves.rs +++ b/crates/bevy_gizmos/src/curves.rs @@ -33,7 +33,7 @@ where /// # use bevy_color::palettes::basic::{RED}; /// fn system(mut gizmos: Gizmos) { /// let domain = Interval::UNIT; - /// let curve = function_curve(domain, |t| Vec2::from(t.sin_cos())); + /// let curve = FunctionCurve::new(domain, |t| Vec2::from(t.sin_cos())); /// gizmos.curve_2d(curve, (0..=100).map(|n| n as f32 / 100.0), RED); /// } /// # bevy_ecs::system::assert_is_system(system); @@ -67,7 +67,7 @@ where /// # use bevy_color::palettes::basic::{RED}; /// fn system(mut gizmos: Gizmos) { /// let domain = Interval::UNIT; - /// let curve = function_curve(domain, |t| { + /// let curve = FunctionCurve::new(domain, |t| { /// let (x,y) = t.sin_cos(); /// Vec3::new(x, y, t) /// }); @@ -104,7 +104,7 @@ where /// # use bevy_color::{Mix, palettes::basic::{GREEN, RED}}; /// fn system(mut gizmos: Gizmos) { /// let domain = Interval::UNIT; - /// let curve = function_curve(domain, |t| Vec2::from(t.sin_cos())); + /// let curve = FunctionCurve::new(domain, |t| Vec2::from(t.sin_cos())); /// gizmos.curve_gradient_2d( /// curve, /// (0..=100).map(|n| n as f32 / 100.0) @@ -147,7 +147,7 @@ where /// # use bevy_color::{Mix, palettes::basic::{GREEN, RED}}; /// fn system(mut gizmos: Gizmos) { /// let domain = Interval::UNIT; - /// let curve = function_curve(domain, |t| { + /// let curve = FunctionCurve::new(domain, |t| { /// let (x,y) = t.sin_cos(); /// Vec3::new(x, y, t) /// }); diff --git a/crates/bevy_gltf/src/loader.rs b/crates/bevy_gltf/src/loader.rs index d9d1571ce9..04e8f4a48e 100644 --- a/crates/bevy_gltf/src/loader.rs +++ b/crates/bevy_gltf/src/loader.rs @@ -275,7 +275,7 @@ async fn load_gltf<'a, 'b, 'c>( #[cfg(feature = "bevy_animation")] let (animations, named_animations, animation_roots) = { use bevy_animation::{animation_curves::*, gltf_curves::*, VariableCurve}; - use bevy_math::curve::{constant_curve, Interval, UnevenSampleAutoCurve}; + use bevy_math::curve::{ConstantCurve, Interval, UnevenSampleAutoCurve}; use bevy_math::{Quat, Vec4}; use gltf::animation::util::ReadOutputs; let mut animations = vec![]; @@ -313,7 +313,7 @@ async fn load_gltf<'a, 'b, 'c>( let translations: Vec = tr.map(Vec3::from).collect(); if keyframe_timestamps.len() == 1 { #[allow(clippy::unnecessary_map_on_constructor)] - Some(constant_curve(Interval::EVERYWHERE, translations[0])) + Some(ConstantCurve::new(Interval::EVERYWHERE, translations[0])) .map(TranslationCurve) .map(VariableCurve::new) } else { @@ -348,7 +348,7 @@ async fn load_gltf<'a, 'b, 'c>( rots.into_f32().map(Quat::from_array).collect(); if keyframe_timestamps.len() == 1 { #[allow(clippy::unnecessary_map_on_constructor)] - Some(constant_curve(Interval::EVERYWHERE, rotations[0])) + Some(ConstantCurve::new(Interval::EVERYWHERE, rotations[0])) .map(RotationCurve) .map(VariableCurve::new) } else { @@ -385,7 +385,7 @@ async fn load_gltf<'a, 'b, 'c>( let scales: Vec = scale.map(Vec3::from).collect(); if keyframe_timestamps.len() == 1 { #[allow(clippy::unnecessary_map_on_constructor)] - Some(constant_curve(Interval::EVERYWHERE, scales[0])) + Some(ConstantCurve::new(Interval::EVERYWHERE, scales[0])) .map(ScaleCurve) .map(VariableCurve::new) } else { @@ -419,7 +419,7 @@ async fn load_gltf<'a, 'b, 'c>( let weights: Vec = weights.into_f32().collect(); if keyframe_timestamps.len() == 1 { #[allow(clippy::unnecessary_map_on_constructor)] - Some(constant_curve(Interval::EVERYWHERE, weights)) + Some(ConstantCurve::new(Interval::EVERYWHERE, weights)) .map(WeightsCurve) .map(VariableCurve::new) } else { diff --git a/crates/bevy_math/src/curve/easing.rs b/crates/bevy_math/src/curve/easing.rs index 689ffb24f5..a31946239d 100644 --- a/crates/bevy_math/src/curve/easing.rs +++ b/crates/bevy_math/src/curve/easing.rs @@ -3,9 +3,10 @@ //! //! [easing functions]: EaseFunction -use crate::{Dir2, Dir3, Dir3A, Quat, Rot2, VectorSpace}; - -use super::{function_curve, Curve, Interval}; +use crate::{ + curve::{FunctionCurve, Interval}, + Curve, Dir2, Dir3, Dir3A, Quat, Rot2, VectorSpace, +}; // TODO: Think about merging `Ease` with `StableInterpolate` @@ -28,13 +29,13 @@ pub trait Ease: Sized { impl Ease for V { fn interpolating_curve_unbounded(start: Self, end: Self) -> impl Curve { - function_curve(Interval::EVERYWHERE, move |t| V::lerp(start, end, t)) + FunctionCurve::new(Interval::EVERYWHERE, move |t| V::lerp(start, end, t)) } } impl Ease for Rot2 { fn interpolating_curve_unbounded(start: Self, end: Self) -> impl Curve { - function_curve(Interval::EVERYWHERE, move |t| Rot2::slerp(start, end, t)) + FunctionCurve::new(Interval::EVERYWHERE, move |t| Rot2::slerp(start, end, t)) } } @@ -44,7 +45,7 @@ impl Ease for Quat { let end_adjusted = if dot < 0.0 { -end } else { end }; let difference = end_adjusted * start.inverse(); let (axis, angle) = difference.to_axis_angle(); - function_curve(Interval::EVERYWHERE, move |s| { + FunctionCurve::new(Interval::EVERYWHERE, move |s| { Quat::from_axis_angle(axis, angle * s) * start }) } @@ -52,7 +53,7 @@ impl Ease for Quat { impl Ease for Dir2 { fn interpolating_curve_unbounded(start: Self, end: Self) -> impl Curve { - function_curve(Interval::EVERYWHERE, move |t| Dir2::slerp(start, end, t)) + FunctionCurve::new(Interval::EVERYWHERE, move |t| Dir2::slerp(start, end, t)) } } @@ -71,20 +72,6 @@ impl Ease for Dir3A { } } -/// 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(start: T, end: T, ease_fn: EaseFunction) -> EasingCurve { - EasingCurve { - start, - end, - ease_fn, - } -} - /// A [`Curve`] that is defined by /// /// - an initial `start` sample value at `t = 0` @@ -104,6 +91,22 @@ pub struct EasingCurve { ease_fn: EaseFunction, } +impl EasingCurve { + /// 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 new(start: T, end: T, ease_fn: EaseFunction) -> Self { + Self { + start, + end, + ease_fn, + } + } +} + impl Curve for EasingCurve where T: Ease + Clone, diff --git a/crates/bevy_math/src/curve/mod.rs b/crates/bevy_math/src/curve/mod.rs index 851dddc564..34afaeca94 100644 --- a/crates/bevy_math/src/curve/mod.rs +++ b/crates/bevy_math/src/curve/mod.rs @@ -56,13 +56,13 @@ //! # use bevy_math::vec3; //! # use bevy_math::curve::*; //! // A sinusoid: -//! let sine_curve = function_curve(Interval::EVERYWHERE, f32::sin); +//! let sine_curve = FunctionCurve::new(Interval::EVERYWHERE, f32::sin); //! //! // A sawtooth wave: -//! let sawtooth_curve = function_curve(Interval::EVERYWHERE, |t| t % 1.0); +//! let sawtooth_curve = FunctionCurve::new(Interval::EVERYWHERE, |t| t % 1.0); //! //! // A helix: -//! let helix_curve = function_curve(Interval::EVERYWHERE, |theta| vec3(theta.sin(), theta, theta.cos())); +//! let helix_curve = FunctionCurve::new(Interval::EVERYWHERE, |theta| vec3(theta.sin(), theta, theta.cos())); //! ``` //! //! Sample-interpolated curves commonly arises in both rasterization and in animation, and this library @@ -127,7 +127,7 @@ //! # use bevy_math::{vec2, prelude::*}; //! # use std::f32::consts::TAU; //! // Our original curve, which may look something like this: -//! let ellipse_curve = function_curve( +//! let ellipse_curve = FunctionCurve::new( //! interval(0.0, TAU).unwrap(), //! |t| vec2(t.cos(), t.sin() * 2.0) //! ); @@ -141,7 +141,7 @@ //! ```rust //! # use bevy_math::{vec2, prelude::*}; //! # use std::f32::consts::TAU; -//! # let ellipse_curve = function_curve(interval(0.0, TAU).unwrap(), |t| vec2(t.cos(), t.sin() * 2.0)); +//! # let ellipse_curve = FunctionCurve::new(interval(0.0, TAU).unwrap(), |t| vec2(t.cos(), t.sin() * 2.0)); //! # let ellipse_motion_curve = ellipse_curve.map(|pos| pos.extend(0.0)); //! // Change the domain to `[0, 1]` instead of `[0, TAU]`: //! let final_curve = ellipse_motion_curve.reparametrize_linear(Interval::UNIT).unwrap(); @@ -155,7 +155,7 @@ //! // A line segment curve connecting two points in the plane: //! let start = vec2(-1.0, 1.0); //! let end = vec2(1.0, 1.0); -//! let segment = function_curve(Interval::UNIT, |t| start.lerp(end, t)); +//! let segment = FunctionCurve::new(Interval::UNIT, |t| start.lerp(end, t)); //! //! // Let's make a curve that goes back and forth along this line segment forever. //! // @@ -177,13 +177,13 @@ //! # use bevy_math::{vec2, prelude::*}; //! # use std::f32::consts::PI; //! // A line segment connecting `(-1, 0)` to `(0, 0)`: -//! let line_curve = function_curve( +//! let line_curve = FunctionCurve::new( //! Interval::UNIT, //! |t| vec2(-1.0, 0.0).lerp(vec2(0.0, 0.0), t) //! ); //! //! // A half-circle curve starting at `(0, 0)`: -//! let half_circle_curve = function_curve( +//! let half_circle_curve = FunctionCurve::new( //! interval(0.0, PI).unwrap(), //! |t| vec2(t.cos() * -1.0 + 1.0, t.sin()) //! ); @@ -198,10 +198,10 @@ //! ```rust //! # use bevy_math::{vec2, prelude::*}; //! // Some entity's position in 2D: -//! let position_curve = function_curve(Interval::UNIT, |t| vec2(t.cos(), t.sin())); +//! let position_curve = FunctionCurve::new(Interval::UNIT, |t| vec2(t.cos(), t.sin())); //! //! // The same entity's orientation, described as a rotation. (In this case it will be spinning.) -//! let orientation_curve = function_curve(Interval::UNIT, |t| Rot2::radians(5.0 * t)); +//! let orientation_curve = FunctionCurve::new(Interval::UNIT, |t| Rot2::radians(5.0 * t)); //! //! // Both in one curve with `(Vec2, Rot2)` output: //! let position_and_orientation = position_curve.zip(orientation_curve).unwrap(); @@ -220,7 +220,7 @@ //! ```rust //! # use bevy_math::{vec2, prelude::*}; //! // A curve that is not easily transported because it relies on evaluating a function: -//! let interesting_curve = function_curve(Interval::UNIT, |t| vec2(t * 3.0, t.exp())); +//! let interesting_curve = FunctionCurve::new(Interval::UNIT, |t| vec2(t * 3.0, t.exp())); //! //! // A rasterized form of the preceding curve which is just a `SampleAutoCurve`. Inside, this //! // just stores an `Interval` along with a buffer of sample data, so it's easy to serialize @@ -250,7 +250,7 @@ //! Here is a demonstration: //! ```rust //! # use bevy_math::prelude::*; -//! # let some_magic_constructor = || easing_curve(0.0, 1.0, EaseFunction::ElasticInOut).graph(); +//! # let some_magic_constructor = || EasingCurve::new(0.0, 1.0, EaseFunction::ElasticInOut).graph(); //! //`my_curve` is obtained somehow. It is a `Curve<(f32, f32)>`. //! let my_curve = some_magic_constructor(); //! @@ -272,7 +272,7 @@ //! [changing parametrizations]: Curve::reparametrize //! [mapping output]: Curve::map //! [rasterization]: Curve::resample -//! [functions]: function_curve +//! [functions]: FunctionCurve //! [sample interpolation]: SampleCurve //! [splines]: crate::cubic_splines //! [easings]: easing @@ -282,7 +282,7 @@ //! [`zip`]: Curve::zip //! [`resample`]: Curve::resample //! -//! [^footnote]: In fact, universal as well, in some sense: if `curve` is any curve, then `function_curve +//! [^footnote]: In fact, universal as well, in some sense: if `curve` is any curve, then `FunctionCurve::new //! (curve.domain(), |t| curve.sample_unchecked(t))` is an equivalent function curve. pub mod adaptors; @@ -414,7 +414,7 @@ pub trait Curve { /// factor rather than multiplying: /// ``` /// # use bevy_math::curve::*; - /// let my_curve = constant_curve(Interval::UNIT, 1.0); + /// let my_curve = ConstantCurve::new(Interval::UNIT, 1.0); /// let scaled_curve = my_curve.reparametrize(interval(0.0, 2.0).unwrap(), |t| t / 2.0); /// ``` /// This kind of linear remapping is provided by the convenience method @@ -425,12 +425,12 @@ pub trait Curve { /// // Reverse a curve: /// # use bevy_math::curve::*; /// # use bevy_math::vec2; - /// let my_curve = constant_curve(Interval::UNIT, 1.0); + /// let my_curve = ConstantCurve::new(Interval::UNIT, 1.0); /// let domain = my_curve.domain(); /// let reversed_curve = my_curve.reparametrize(domain, |t| domain.end() - (t - domain.start())); /// /// // Take a segment of a curve: - /// # let my_curve = constant_curve(Interval::UNIT, 1.0); + /// # let my_curve = ConstantCurve::new(Interval::UNIT, 1.0); /// let curve_segment = my_curve.reparametrize(interval(0.0, 0.5).unwrap(), |t| 0.5 + t); /// ``` #[must_use] @@ -712,7 +712,7 @@ pub trait Curve { /// ``` /// # use bevy_math::*; /// # use bevy_math::curve::*; - /// let quarter_rotation = function_curve(interval(0.0, 90.0).unwrap(), |t| Rot2::degrees(t)); + /// let quarter_rotation = FunctionCurve::new(interval(0.0, 90.0).unwrap(), |t| Rot2::degrees(t)); /// // A curve which only stores three data points and uses `nlerp` to interpolate them: /// let resampled_rotation = quarter_rotation.resample(3, |x, y, t| x.nlerp(*y, t)); /// ``` @@ -863,7 +863,7 @@ pub trait Curve { /// # Example /// ``` /// # use bevy_math::curve::*; - /// let my_curve = function_curve(Interval::UNIT, |t| t * t + 1.0); + /// let my_curve = FunctionCurve::new(Interval::UNIT, |t| t * t + 1.0); /// /// // Borrow `my_curve` long enough to resample a mapped version. Note that `map` takes /// // ownership of its input. @@ -976,27 +976,8 @@ pub enum ResamplingError { UnboundedDomain, } -/// Create a [`Curve`] that constantly takes the given `value` over the given `domain`. -pub fn constant_curve(domain: Interval, value: T) -> ConstantCurve { - ConstantCurve { domain, value } -} - -/// Convert the given function `f` into a [`Curve`] with the given `domain`, sampled by -/// evaluating the function. -pub fn function_curve(domain: Interval, f: F) -> FunctionCurve -where - F: Fn(f32) -> T, -{ - FunctionCurve { - domain, - f, - _phantom: PhantomData, - } -} - #[cfg(test)] mod tests { - use super::easing::*; use super::*; use crate::{ops, Quat}; use approx::{assert_abs_diff_eq, AbsDiffEq}; @@ -1005,7 +986,7 @@ mod tests { #[test] fn curve_can_be_made_into_an_object() { - let curve = constant_curve(Interval::UNIT, 42.0); + let curve = ConstantCurve::new(Interval::UNIT, 42.0); let curve: &dyn Curve = &curve; assert_eq!(curve.sample(1.0), Some(42.0)); @@ -1014,21 +995,21 @@ mod tests { #[test] fn constant_curves() { - let curve = constant_curve(Interval::EVERYWHERE, 5.0); + let curve = ConstantCurve::new(Interval::EVERYWHERE, 5.0); assert!(curve.sample_unchecked(-35.0) == 5.0); - let curve = constant_curve(Interval::UNIT, true); + let curve = ConstantCurve::new(Interval::UNIT, true); assert!(curve.sample_unchecked(2.0)); assert!(curve.sample(2.0).is_none()); } #[test] fn function_curves() { - let curve = function_curve(Interval::EVERYWHERE, |t| t * t); + let curve = FunctionCurve::new(Interval::EVERYWHERE, |t| t * t); assert!(curve.sample_unchecked(2.0).abs_diff_eq(&4.0, f32::EPSILON)); assert!(curve.sample_unchecked(-3.0).abs_diff_eq(&9.0, f32::EPSILON)); - let curve = function_curve(interval(0.0, f32::INFINITY).unwrap(), ops::log2); + let curve = FunctionCurve::new(interval(0.0, f32::INFINITY).unwrap(), ops::log2); assert_eq!(curve.sample_unchecked(3.5), ops::log2(3.5)); assert!(curve.sample_unchecked(-1.0).is_nan()); assert!(curve.sample(-1.0).is_none()); @@ -1038,7 +1019,7 @@ mod tests { fn linear_curve() { let start = Vec2::ZERO; let end = Vec2::new(1.0, 2.0); - let curve = easing_curve(start, end, EaseFunction::Linear); + let curve = EasingCurve::new(start, end, EaseFunction::Linear); let mid = (start + end) / 2.0; @@ -1054,7 +1035,7 @@ mod tests { let start = Vec2::ZERO; let end = Vec2::new(1.0, 2.0); - let curve = easing_curve(start, end, EaseFunction::Steps(4)); + let curve = EasingCurve::new(start, end, EaseFunction::Steps(4)); [ (0.0, start), (0.124, start), @@ -1078,7 +1059,7 @@ mod tests { let start = Vec2::ZERO; let end = Vec2::new(1.0, 2.0); - let curve = easing_curve(start, end, EaseFunction::QuadraticIn); + let curve = EasingCurve::new(start, end, EaseFunction::QuadraticIn); [ (0.0, start), (0.25, Vec2::new(0.0625, 0.125)), @@ -1093,7 +1074,7 @@ mod tests { #[test] fn mapping() { - let curve = function_curve(Interval::EVERYWHERE, |t| t * 3.0 + 1.0); + let curve = FunctionCurve::new(Interval::EVERYWHERE, |t| t * 3.0 + 1.0); let mapped_curve = curve.map(|x| x / 7.0); assert_eq!(mapped_curve.sample_unchecked(3.5), (3.5 * 3.0 + 1.0) / 7.0); assert_eq!( @@ -1102,7 +1083,7 @@ mod tests { ); assert_eq!(mapped_curve.domain(), Interval::EVERYWHERE); - let curve = function_curve(Interval::UNIT, |t| t * TAU); + let curve = FunctionCurve::new(Interval::UNIT, |t| t * TAU); let mapped_curve = curve.map(Quat::from_rotation_z); assert_eq!(mapped_curve.sample_unchecked(0.0), Quat::IDENTITY); assert!(mapped_curve.sample_unchecked(1.0).is_near_identity()); @@ -1111,7 +1092,7 @@ mod tests { #[test] fn reverse() { - let curve = function_curve(Interval::new(0.0, 1.0).unwrap(), |t| t * 3.0 + 1.0); + let curve = FunctionCurve::new(Interval::new(0.0, 1.0).unwrap(), |t| t * 3.0 + 1.0); let rev_curve = curve.reverse().unwrap(); assert_eq!(rev_curve.sample(-0.1), None); assert_eq!(rev_curve.sample(0.0), Some(1.0 * 3.0 + 1.0)); @@ -1119,7 +1100,7 @@ mod tests { assert_eq!(rev_curve.sample(1.0), Some(0.0 * 3.0 + 1.0)); assert_eq!(rev_curve.sample(1.1), None); - let curve = function_curve(Interval::new(-2.0, 1.0).unwrap(), |t| t * 3.0 + 1.0); + let curve = FunctionCurve::new(Interval::new(-2.0, 1.0).unwrap(), |t| t * 3.0 + 1.0); let rev_curve = curve.reverse().unwrap(); assert_eq!(rev_curve.sample(-2.1), None); assert_eq!(rev_curve.sample(-2.0), Some(1.0 * 3.0 + 1.0)); @@ -1130,7 +1111,7 @@ mod tests { #[test] fn repeat() { - let curve = function_curve(Interval::new(0.0, 1.0).unwrap(), |t| t * 3.0 + 1.0); + let curve = FunctionCurve::new(Interval::new(0.0, 1.0).unwrap(), |t| t * 3.0 + 1.0); let repeat_curve = curve.by_ref().repeat(1).unwrap(); assert_eq!(repeat_curve.sample(-0.1), None); assert_eq!(repeat_curve.sample(0.0), Some(0.0 * 3.0 + 1.0)); @@ -1159,7 +1140,7 @@ mod tests { #[test] fn ping_pong() { - let curve = function_curve(Interval::new(0.0, 1.0).unwrap(), |t| t * 3.0 + 1.0); + let curve = FunctionCurve::new(Interval::new(0.0, 1.0).unwrap(), |t| t * 3.0 + 1.0); let ping_pong_curve = curve.ping_pong().unwrap(); assert_eq!(ping_pong_curve.sample(-0.1), None); assert_eq!(ping_pong_curve.sample(0.0), Some(0.0 * 3.0 + 1.0)); @@ -1169,7 +1150,7 @@ mod tests { assert_eq!(ping_pong_curve.sample(2.0), Some(0.0 * 3.0 + 1.0)); assert_eq!(ping_pong_curve.sample(2.1), None); - let curve = function_curve(Interval::new(-2.0, 2.0).unwrap(), |t| t * 3.0 + 1.0); + let curve = FunctionCurve::new(Interval::new(-2.0, 2.0).unwrap(), |t| t * 3.0 + 1.0); let ping_pong_curve = curve.ping_pong().unwrap(); assert_eq!(ping_pong_curve.sample(-2.1), None); assert_eq!(ping_pong_curve.sample(-2.0), Some(-2.0 * 3.0 + 1.0)); @@ -1182,8 +1163,8 @@ mod tests { #[test] fn continue_chain() { - let first = function_curve(Interval::new(0.0, 1.0).unwrap(), |t| t * 3.0 + 1.0); - let second = function_curve(Interval::new(0.0, 1.0).unwrap(), |t| t * t); + let first = FunctionCurve::new(Interval::new(0.0, 1.0).unwrap(), |t| t * 3.0 + 1.0); + let second = FunctionCurve::new(Interval::new(0.0, 1.0).unwrap(), |t| t * t); let c0_chain_curve = first.chain_continue(second).unwrap(); assert_eq!(c0_chain_curve.sample(-0.1), None); assert_eq!(c0_chain_curve.sample(0.0), Some(0.0 * 3.0 + 1.0)); @@ -1196,7 +1177,7 @@ mod tests { #[test] fn reparameterization() { - let curve = function_curve(interval(1.0, f32::INFINITY).unwrap(), ops::log2); + let curve = FunctionCurve::new(interval(1.0, f32::INFINITY).unwrap(), ops::log2); let reparametrized_curve = curve .by_ref() .reparametrize(interval(0.0, f32::INFINITY).unwrap(), ops::exp2); @@ -1216,7 +1197,7 @@ mod tests { #[test] fn multiple_maps() { // Make sure these actually happen in the right order. - let curve = function_curve(Interval::UNIT, ops::exp2); + let curve = FunctionCurve::new(Interval::UNIT, ops::exp2); let first_mapped = curve.map(ops::log2); let second_mapped = first_mapped.map(|x| x * -2.0); assert_abs_diff_eq!(second_mapped.sample_unchecked(0.0), 0.0); @@ -1227,7 +1208,7 @@ mod tests { #[test] fn multiple_reparams() { // Make sure these happen in the right order too. - let curve = function_curve(Interval::UNIT, ops::exp2); + let curve = FunctionCurve::new(Interval::UNIT, ops::exp2); let first_reparam = curve.reparametrize(interval(1.0, 2.0).unwrap(), ops::log2); let second_reparam = first_reparam.reparametrize(Interval::UNIT, |t| t + 1.0); assert_abs_diff_eq!(second_reparam.sample_unchecked(0.0), 1.0); @@ -1237,7 +1218,7 @@ mod tests { #[test] fn resampling() { - let curve = function_curve(interval(1.0, 4.0).unwrap(), ops::log2); + let curve = FunctionCurve::new(interval(1.0, 4.0).unwrap(), ops::log2); // Need at least one segment to sample. let nice_try = curve.by_ref().resample_auto(0); @@ -1257,7 +1238,7 @@ mod tests { } // Another example. - let curve = function_curve(interval(0.0, TAU).unwrap(), ops::cos); + let curve = FunctionCurve::new(interval(0.0, TAU).unwrap(), ops::cos); let resampled_curve = curve.by_ref().resample_auto(1000).unwrap(); for test_pt in curve.domain().spaced_points(1001).unwrap() { let expected = curve.sample_unchecked(test_pt); @@ -1271,7 +1252,7 @@ mod tests { #[test] fn uneven_resampling() { - let curve = function_curve(interval(0.0, f32::INFINITY).unwrap(), ops::exp); + let curve = FunctionCurve::new(interval(0.0, f32::INFINITY).unwrap(), ops::exp); // Need at least two points to resample. let nice_try = curve.by_ref().resample_uneven_auto([1.0; 1]); @@ -1290,7 +1271,7 @@ mod tests { assert_abs_diff_eq!(resampled_curve.domain().end(), 9.9, epsilon = 1e-6); // Another example. - let curve = function_curve(interval(1.0, f32::INFINITY).unwrap(), ops::log2); + let curve = FunctionCurve::new(interval(1.0, f32::INFINITY).unwrap(), ops::log2); let sample_points = (0..10).map(|idx| ops::exp2(idx as f32)); let resampled_curve = curve.by_ref().resample_uneven_auto(sample_points).unwrap(); for idx in 0..10 { @@ -1306,7 +1287,7 @@ mod tests { fn sample_iterators() { let times = [-0.5, 0.0, 0.5, 1.0, 1.5]; - let curve = function_curve(Interval::EVERYWHERE, |t| t * 3.0 + 1.0); + let curve = FunctionCurve::new(Interval::EVERYWHERE, |t| t * 3.0 + 1.0); let samples = curve.sample_iter_unchecked(times).collect::>(); let [y0, y1, y2, y3, y4] = samples.try_into().unwrap(); @@ -1316,7 +1297,7 @@ mod tests { assert_eq!(y3, 1.0 * 3.0 + 1.0); assert_eq!(y4, 1.5 * 3.0 + 1.0); - let finite_curve = function_curve(Interval::new(0.0, 1.0).unwrap(), |t| t * 3.0 + 1.0); + let finite_curve = FunctionCurve::new(Interval::new(0.0, 1.0).unwrap(), |t| t * 3.0 + 1.0); let samples = finite_curve.sample_iter(times).collect::>(); let [y0, y1, y2, y3, y4] = samples.try_into().unwrap(); diff --git a/examples/animation/eased_motion.rs b/examples/animation/eased_motion.rs index cbe4c5d993..11220e92ad 100644 --- a/examples/animation/eased_motion.rs +++ b/examples/animation/eased_motion.rs @@ -106,7 +106,7 @@ impl AnimationInfo { // The easing curve is parametrized over [0, 1], so we reparametrize it and // then ping-pong, which makes it spend another 3 seconds on the return journey. - let translation_curve = easing_curve( + let translation_curve = EasingCurve::new( vec3(-6., 2., 0.), vec3(6., 2., 0.), EaseFunction::CubicInOut, @@ -119,7 +119,7 @@ impl AnimationInfo { // Something similar for rotation. The repetition here is an illusion caused // by the symmetry of the cube; it rotates on the forward journey and never // rotates back. - let rotation_curve = easing_curve( + let rotation_curve = EasingCurve::new( Quat::IDENTITY, Quat::from_rotation_y(FRAC_PI_2), EaseFunction::ElasticInOut, diff --git a/examples/animation/easing_functions.rs b/examples/animation/easing_functions.rs index 668c741c89..2cde2a4542 100644 --- a/examples/animation/easing_functions.rs +++ b/examples/animation/easing_functions.rs @@ -138,7 +138,7 @@ fn display_curves( ); // Draw the curve - let f = easing_curve(0.0, 1.0, *function); + let f = EasingCurve::new(0.0, 1.0, *function); let drawn_curve = f.by_ref().graph().map(|(x, y)| { Vec2::new( x * SIZE_PER_FUNCTION + transform.translation.x, diff --git a/examples/gizmos/2d_gizmos.rs b/examples/gizmos/2d_gizmos.rs index fa14904b2a..9877b06907 100644 --- a/examples/gizmos/2d_gizmos.rs +++ b/examples/gizmos/2d_gizmos.rs @@ -69,7 +69,7 @@ fn draw_example_collection( gizmos.cross_2d(Vec2::new(-160., 120.), 12., FUCHSIA); let domain = Interval::EVERYWHERE; - let curve = function_curve(domain, |t| Vec2::new(t, ops::sin(t / 25.0) * 100.0)); + let curve = FunctionCurve::new(domain, |t| Vec2::new(t, ops::sin(t / 25.0) * 100.0)); let resolution = ((ops::sin(time.elapsed_secs()) + 1.0) * 50.0) as usize; let times_and_colors = (0..=resolution) .map(|n| n as f32 / resolution as f32) diff --git a/examples/gizmos/3d_gizmos.rs b/examples/gizmos/3d_gizmos.rs index a62faee115..81fe527c9a 100644 --- a/examples/gizmos/3d_gizmos.rs +++ b/examples/gizmos/3d_gizmos.rs @@ -122,7 +122,7 @@ fn draw_example_collection( gizmos.cross(Vec3::new(-1., 1., 1.), 0.5, FUCHSIA); let domain = Interval::EVERYWHERE; - let curve = function_curve(domain, |t| { + let curve = FunctionCurve::new(domain, |t| { (Vec2::from(ops::sin_cos(t * 10.0))).extend(t - 6.0) }); let resolution = ((ops::sin(time.elapsed_secs()) + 1.0) * 100.0) as usize;