bevy_math/curve/
mod.rs

1//! The [`Curve`] trait, providing a domain-agnostic description of curves.
2//!
3//! ## Overview
4//!
5//! At a high level, [`Curve`] is a trait that abstracts away the implementation details of curves,
6//! which comprise any kind of data parametrized by a single continuous variable. For example, that
7//! variable could represent time, in which case a curve would represent a value that changes over
8//! time, as in animation; on the other hand, it could represent something like displacement or
9//! distance, as in graphs, gradients, and curves in space.
10//!
11//! The trait itself has two fundamental components: a curve must have a [domain], which is a nonempty
12//! range of `f32` values, and it must be able to be [sampled] on every one of those values, producing
13//! output of some fixed type.
14//!
15//! A primary goal of the trait is to allow interfaces to simply accept `impl Curve<T>` as input
16//! rather than requiring for input curves to be defined in data in any particular way. This is
17//! supported by a number of interface methods which allow [changing parametrizations], [mapping output],
18//! and [rasterization].
19//!
20//! ## Analogy with `Iterator`
21//!
22//! The `Curve` API behaves, in many ways, like a continuous counterpart to [`Iterator`]. The analogy
23//! looks something like this with some of the common methods:
24//!
25//! |    Iterators     |     Curves      |
26//! | :--------------- | :-------------- |
27//! | `map`            | `map`           |
28//! | `skip`/`step_by` | `reparametrize` |
29//! | `enumerate`      | `graph`         |
30//! | `chain`          | `chain`         |
31//! | `zip`            | `zip`           |
32//! | `rev`            | `reverse`       |
33//! | `by_ref`         | `by_ref`        |
34//!
35//! Of course, there are very important differences, as well. For instance, the continuous nature of
36//! curves means that many iterator methods make little sense in the context of curves, or at least
37//! require numerical techniques. For example, the analogue of `sum` would be an integral, approximated
38//! by something like Riemann summation.
39//!
40//! Furthermore, the two also differ greatly in their orientation to borrowing and mutation:
41//! iterators are mutated by being iterated, and by contrast, all curve methods are immutable. More
42//! information on the implications of this can be found [below](self#Ownership-and-borrowing).
43//!
44//! ## Defining curves
45//!
46//! Curves may be defined in a number of ways. The following are common:
47//! - using [functions];
48//! - using [sample interpolation];
49//! - using [splines];
50//! - using [easings].
51//!
52//! Among these, the first is the most versatile[^footnote]: the domain and the sampling output are just
53//! specified directly in the construction. For this reason, function curves are a reliable go-to for
54//! simple one-off constructions and procedural uses, where flexibility is desirable. For example:
55//! ```rust
56//! # use bevy_math::vec3;
57//! # use bevy_math::curve::*;
58//! // A sinusoid:
59//! let sine_curve = FunctionCurve::new(Interval::EVERYWHERE, f32::sin);
60//!
61//! // A sawtooth wave:
62//! let sawtooth_curve = FunctionCurve::new(Interval::EVERYWHERE, |t| t % 1.0);
63//!
64//! // A helix:
65//! let helix_curve = FunctionCurve::new(Interval::EVERYWHERE, |theta| vec3(theta.sin(), theta, theta.cos()));
66//! ```
67//!
68//! Sample-interpolated curves commonly arises in both rasterization and in animation, and this library
69//! has support for producing them in both fashions. See [below](self#Resampling-and-rasterization) for
70//! more information about rasterization. Here is what an explicit sample-interpolated curve might look like:
71//! ```rust
72//! # use bevy_math::prelude::*;
73//! # use std::f32::consts::FRAC_PI_2;
74//! // A list of angles that we want to traverse:
75//! let angles = [
76//!     0.0,
77//!     -FRAC_PI_2,
78//!     0.0,
79//!     FRAC_PI_2,
80//!     0.0
81//! ];
82//!
83//! // Make each angle into a rotation by that angle:
84//! let rotations = angles.map(|angle| Rot2::radians(angle));
85//!
86//! // Interpolate these rotations with a `Rot2`-valued curve:
87//! let rotation_curve = SampleAutoCurve::new(interval(0.0, 4.0).unwrap(), rotations).unwrap();
88//! ```
89//!
90//! For more information on [spline curves] and [easing curves], see their respective modules.
91//!
92//! And, of course, you are also free to define curve types yourself, implementing the trait directly.
93//! For custom sample-interpolated curves, the [`cores`] submodule provides machinery to avoid having to
94//! reimplement interpolation logic yourself. In many other cases, implementing the trait directly is
95//! often quite straightforward:
96//! ```rust
97//! # use bevy_math::prelude::*;
98//! struct ExponentialCurve {
99//!     exponent: f32,
100//! }
101//!
102//! impl Curve<f32> for ExponentialCurve {
103//!     fn domain(&self) -> Interval {
104//!         Interval::EVERYWHERE
105//!     }
106//!
107//!     fn sample_unchecked(&self, t: f32) -> f32 {
108//!         f32::exp(self.exponent * t)
109//!     }
110//!
111//!     // All other trait methods can be inferred from these.
112//! }
113//! ```
114//!
115//! ## Transforming curves
116//!
117//! The API provides a few key ways of transforming one curve into another. These are often useful when
118//! you would like to make use of an interface that requires a curve that bears some logical relationship
119//! to one that you already have access to, but with different requirements or expectations. For example,
120//! the output type of the curves may differ, or the domain may be expected to be different. The `map`
121//! and `reparametrize` methods can help address this.
122//!
123//! As a simple example of the kind of thing that arises in practice, let's imagine that we have a
124//! `Curve<Vec2>` that we want to use to describe the motion of some object over time, but the interface
125//! for animation expects a `Curve<Vec3>`, since the object will move in three dimensions:
126//! ```rust
127//! # use bevy_math::{vec2, prelude::*};
128//! # use std::f32::consts::TAU;
129//! // Our original curve, which may look something like this:
130//! let ellipse_curve = FunctionCurve::new(
131//!     interval(0.0, TAU).unwrap(),
132//!     |t| vec2(t.cos(), t.sin() * 2.0)
133//! );
134//!
135//! // Use `map` to situate this in 3D as a Curve<Vec3>; in this case, it will be in the xy-plane:
136//! let ellipse_motion_curve = ellipse_curve.map(|pos| pos.extend(0.0));
137//! ```
138//!
139//! We might imagine further still that the interface expects the curve to have domain `[0, 1]`. The
140//! `reparametrize` methods can address this:
141//! ```rust
142//! # use bevy_math::{vec2, prelude::*};
143//! # use std::f32::consts::TAU;
144//! # let ellipse_curve = FunctionCurve::new(interval(0.0, TAU).unwrap(), |t| vec2(t.cos(), t.sin() * 2.0));
145//! # let ellipse_motion_curve = ellipse_curve.map(|pos| pos.extend(0.0));
146//! // Change the domain to `[0, 1]` instead of `[0, TAU]`:
147//! let final_curve = ellipse_motion_curve.reparametrize_linear(Interval::UNIT).unwrap();
148//! ```
149//!
150//! Of course, there are many other ways of using these methods. In general, `map` is used for transforming
151//! the output and using it to drive something else, while `reparametrize` preserves the curve's shape but
152//! changes the speed and direction in which it is traversed. For instance:
153//! ```rust
154//! # use bevy_math::{vec2, prelude::*};
155//! // A line segment curve connecting two points in the plane:
156//! let start = vec2(-1.0, 1.0);
157//! let end = vec2(1.0, 1.0);
158//! let segment = FunctionCurve::new(Interval::UNIT, |t| start.lerp(end, t));
159//!
160//! // Let's make a curve that goes back and forth along this line segment forever.
161//! //
162//! // Start by stretching the line segment in parameter space so that it travels along its length
163//! // from `-1` to `1` instead of `0` to `1`:
164//! let stretched_segment = segment.reparametrize_linear(interval(-1.0, 1.0).unwrap()).unwrap();
165//!
166//! // Now, the *output* of `f32::sin` in `[-1, 1]` corresponds to the *input* interval of
167//! // `stretched_segment`; the sinusoid output is mapped to the input parameter and controls how
168//! // far along the segment we are:
169//! let back_and_forth_curve = stretched_segment.reparametrize(Interval::EVERYWHERE, f32::sin);
170//! ```
171//!
172//! ## Combining curves
173//!
174//! Curves become more expressive when used together. For example, maybe you want to combine two
175//! curves end-to-end:
176//! ```rust
177//! # use bevy_math::{vec2, prelude::*};
178//! # use std::f32::consts::PI;
179//! // A line segment connecting `(-1, 0)` to `(0, 0)`:
180//! let line_curve = FunctionCurve::new(
181//!     Interval::UNIT,
182//!     |t| vec2(-1.0, 0.0).lerp(vec2(0.0, 0.0), t)
183//! );
184//!
185//! // A half-circle curve starting at `(0, 0)`:
186//! let half_circle_curve = FunctionCurve::new(
187//!     interval(0.0, PI).unwrap(),
188//!     |t| vec2(t.cos() * -1.0 + 1.0, t.sin())
189//! );
190//!
191//! // A curve that traverses `line_curve` and then `half_circle_curve` over the interval
192//! // from `0` to `PI + 1`:
193//! let combined_curve = line_curve.chain(half_circle_curve).unwrap();
194//! ```
195//!
196//! Or, instead, maybe you want to combine two curves the *other* way, producing a single curve
197//! that combines their output in a tuple:
198//! ```rust
199//! # use bevy_math::{vec2, prelude::*};
200//! // Some entity's position in 2D:
201//! let position_curve = FunctionCurve::new(Interval::UNIT, |t| vec2(t.cos(), t.sin()));
202//!
203//! // The same entity's orientation, described as a rotation. (In this case it will be spinning.)
204//! let orientation_curve = FunctionCurve::new(Interval::UNIT, |t| Rot2::radians(5.0 * t));
205//!
206//! // Both in one curve with `(Vec2, Rot2)` output:
207//! let position_and_orientation = position_curve.zip(orientation_curve).unwrap();
208//! ```
209//!
210//! See the documentation on [`chain`] and [`zip`] for more details on how these methods work.
211//!
212//! ## <a name="Resampling-and-rasterization"></a>Resampling and rasterization
213//!
214//! Sometimes, for reasons of portability, performance, or otherwise, it can be useful to ensure that
215//! curves of various provenance all actually share the same concrete type. This is the purpose of the
216//! [`resample`] family of functions: they allow a curve to be replaced by an approximate version of
217//! itself defined by interpolation over samples from the original curve.
218//!
219//! In effect, this allows very different curves to be rasterized and treated uniformly. For example:
220//! ```rust
221//! # use bevy_math::{vec2, prelude::*};
222//! // A curve that is not easily transported because it relies on evaluating a function:
223//! let interesting_curve = FunctionCurve::new(Interval::UNIT, |t| vec2(t * 3.0, t.exp()));
224//!
225//! // A rasterized form of the preceding curve which is just a `SampleAutoCurve`. Inside, this
226//! // just stores an `Interval` along with a buffer of sample data, so it's easy to serialize
227//! // and deserialize:
228//! let resampled_curve = interesting_curve.resample_auto(100).unwrap();
229//!
230//! // The rasterized form can be seamlessly used as a curve itself:
231//! let some_value = resampled_curve.sample(0.5).unwrap();
232//! ```
233//!
234//! ## <a name="Ownership-and-borrowing"></a>Ownership and borrowing
235//!
236//! It can be easy to get tripped up by how curves specifically interact with Rust's ownership semantics.
237//! First of all, it's worth noting that the API never uses `&mut self` — every method either takes
238//! ownership of the original curve or uses a shared reference.
239//!
240//! Because of the methods that take ownership, it is useful to be aware of the following:
241//! - If `curve` is a curve, then `&curve` is also a curve with the same output. For convenience,
242//!   `&curve` can be written as `curve.by_ref()` for use in method chaining.
243//! - However, `&curve` cannot outlive `curve`. In general, it is not `'static`.
244//!
245//! In other words, `&curve` can be used to perform temporary operations without consuming `curve` (for
246//! example, to effectively pass `curve` into an API which expects an `impl Curve<T>`), but it *cannot*
247//! be used in situations where persistence is necessary (e.g. when the curve itself must be stored
248//! for later use).
249//!
250//! Here is a demonstration:
251//! ```rust
252//! # use bevy_math::prelude::*;
253//! # let some_magic_constructor = || EasingCurve::new(0.0, 1.0, EaseFunction::ElasticInOut).graph();
254//! //`my_curve` is obtained somehow. It is a `Curve<(f32, f32)>`.
255//! let my_curve = some_magic_constructor();
256//!
257//! // Now, we want to sample a mapped version of `my_curve`.
258//!
259//! // let samples: Vec<f32> = my_curve.map(|(x, y)| y).samples(50).unwrap().collect();
260//! // ^ This would work, but it would also invalidate `my_curve`, since `map` takes ownership.
261//!
262//! // Instead, we pass a borrowed version of `my_curve` to `map`. It lives long enough that we
263//! // can extract samples:
264//! let samples: Vec<f32> = my_curve.by_ref().map(|(x, y)| y).samples(50).unwrap().collect();
265//!
266//! // This way, we retain the ability to use `my_curve` later:
267//! let new_curve = my_curve.map(|(x,y)| x + y);
268//! ```
269//!
270//! [domain]: Curve::domain
271//! [sampled]: Curve::sample
272//! [changing parametrizations]: CurveExt::reparametrize
273//! [mapping output]: CurveExt::map
274//! [rasterization]: CurveResampleExt::resample
275//! [functions]: FunctionCurve
276//! [sample interpolation]: SampleCurve
277//! [splines]: crate::cubic_splines
278//! [easings]: easing
279//! [spline curves]: crate::cubic_splines
280//! [easing curves]: easing
281//! [`chain`]: CurveExt::chain
282//! [`zip`]: CurveExt::zip
283//! [`resample`]: CurveResampleExt::resample
284//!
285//! [^footnote]: In fact, universal as well, in some sense: if `curve` is any curve, then `FunctionCurve::new
286//! (curve.domain(), |t| curve.sample_unchecked(t))` is an equivalent function curve.
287
288pub mod adaptors;
289pub mod cores;
290pub mod derivatives;
291pub mod easing;
292pub mod interval;
293pub mod iterable;
294
295#[cfg(feature = "alloc")]
296pub mod sample_curves;
297
298// bevy_math::curve re-exports all commonly-needed curve-related items.
299pub use adaptors::*;
300pub use easing::*;
301pub use interval::{interval, Interval};
302
303#[cfg(feature = "alloc")]
304pub use {
305    cores::{EvenCore, UnevenCore},
306    sample_curves::*,
307};
308
309use crate::VectorSpace;
310use core::{marker::PhantomData, ops::Deref};
311use interval::InvalidIntervalError;
312use thiserror::Error;
313
314#[cfg(feature = "alloc")]
315use {crate::StableInterpolate, itertools::Itertools};
316
317/// A trait for a type that can represent values of type `T` parametrized over a fixed interval.
318///
319/// Typical examples of this are actual geometric curves where `T: VectorSpace`, but other kinds
320/// of output data can be represented as well. See the [module-level documentation] for details.
321///
322/// [module-level documentation]: self
323pub trait Curve<T> {
324    /// The interval over which this curve is parametrized.
325    ///
326    /// This is the range of values of `t` where we can sample the curve and receive valid output.
327    fn domain(&self) -> Interval;
328
329    /// Sample a point on this curve at the parameter value `t`, extracting the associated value.
330    /// This is the unchecked version of sampling, which should only be used if the sample time `t`
331    /// is already known to lie within the curve's domain.
332    ///
333    /// Values sampled from outside of a curve's domain are generally considered invalid; data which
334    /// is nonsensical or otherwise useless may be returned in such a circumstance, and extrapolation
335    /// beyond a curve's domain should not be relied upon.
336    fn sample_unchecked(&self, t: f32) -> T;
337
338    /// Sample a point on this curve at the parameter value `t`, returning `None` if the point is
339    /// outside of the curve's domain.
340    fn sample(&self, t: f32) -> Option<T> {
341        match self.domain().contains(t) {
342            true => Some(self.sample_unchecked(t)),
343            false => None,
344        }
345    }
346
347    /// Sample a point on this curve at the parameter value `t`, clamping `t` to lie inside the
348    /// domain of the curve.
349    fn sample_clamped(&self, t: f32) -> T {
350        let t = self.domain().clamp(t);
351        self.sample_unchecked(t)
352    }
353}
354
355impl<T, C, D> Curve<T> for D
356where
357    C: Curve<T> + ?Sized,
358    D: Deref<Target = C>,
359{
360    fn domain(&self) -> Interval {
361        <C as Curve<T>>::domain(self)
362    }
363
364    fn sample_unchecked(&self, t: f32) -> T {
365        <C as Curve<T>>::sample_unchecked(self, t)
366    }
367}
368
369/// Extension trait implemented by [curves], allowing access to a number of adaptors and
370/// convenience methods.
371///
372/// This trait is automatically implemented for all curves that are `Sized`. In particular,
373/// it is implemented for types like `Box<dyn Curve<T>>`. `CurveExt` is not dyn-compatible
374/// itself.
375///
376/// For more information, see the [module-level documentation].
377///
378/// [curves]: Curve
379/// [module-level documentation]: self
380pub trait CurveExt<T>: Curve<T> + Sized {
381    /// Sample a collection of `n >= 0` points on this curve at the parameter values `t_n`,
382    /// returning `None` if the point is outside of the curve's domain.
383    ///
384    /// The samples are returned in the same order as the parameter values `t_n` were provided and
385    /// will include all results. This leaves the responsibility for things like filtering and
386    /// sorting to the user for maximum flexibility.
387    fn sample_iter(&self, iter: impl IntoIterator<Item = f32>) -> impl Iterator<Item = Option<T>> {
388        iter.into_iter().map(|t| self.sample(t))
389    }
390
391    /// Sample a collection of `n >= 0` points on this curve at the parameter values `t_n`,
392    /// extracting the associated values. This is the unchecked version of sampling, which should
393    /// only be used if the sample times `t_n` are already known to lie within the curve's domain.
394    ///
395    /// Values sampled from outside of a curve's domain are generally considered invalid; data
396    /// which is nonsensical or otherwise useless may be returned in such a circumstance, and
397    /// extrapolation beyond a curve's domain should not be relied upon.
398    ///
399    /// The samples are returned in the same order as the parameter values `t_n` were provided and
400    /// will include all results. This leaves the responsibility for things like filtering and
401    /// sorting to the user for maximum flexibility.
402    fn sample_iter_unchecked(
403        &self,
404        iter: impl IntoIterator<Item = f32>,
405    ) -> impl Iterator<Item = T> {
406        iter.into_iter().map(|t| self.sample_unchecked(t))
407    }
408
409    /// Sample a collection of `n >= 0` points on this curve at the parameter values `t_n`,
410    /// clamping `t_n` to lie inside the domain of the curve.
411    ///
412    /// The samples are returned in the same order as the parameter values `t_n` were provided and
413    /// will include all results. This leaves the responsibility for things like filtering and
414    /// sorting to the user for maximum flexibility.
415    fn sample_iter_clamped(&self, iter: impl IntoIterator<Item = f32>) -> impl Iterator<Item = T> {
416        iter.into_iter().map(|t| self.sample_clamped(t))
417    }
418
419    /// Create a new curve by mapping the values of this curve via a function `f`; i.e., if the
420    /// sample at time `t` for this curve is `x`, the value at time `t` on the new curve will be
421    /// `f(x)`.
422    #[must_use]
423    fn map<S, F>(self, f: F) -> MapCurve<T, S, Self, F>
424    where
425        F: Fn(T) -> S,
426    {
427        MapCurve {
428            preimage: self,
429            f,
430            _phantom: PhantomData,
431        }
432    }
433
434    /// Create a new [`Curve`] whose parameter space is related to the parameter space of this curve
435    /// by `f`. For each time `t`, the sample from the new curve at time `t` is the sample from
436    /// this curve at time `f(t)`. The given `domain` will be the domain of the new curve. The
437    /// function `f` is expected to take `domain` into `self.domain()`.
438    ///
439    /// Note that this is the opposite of what one might expect intuitively; for example, if this
440    /// curve has a parameter domain of `[0, 1]`, then stretching the parameter domain to
441    /// `[0, 2]` would be performed as follows, dividing by what might be perceived as the scaling
442    /// factor rather than multiplying:
443    /// ```
444    /// # use bevy_math::curve::*;
445    /// let my_curve = ConstantCurve::new(Interval::UNIT, 1.0);
446    /// let scaled_curve = my_curve.reparametrize(interval(0.0, 2.0).unwrap(), |t| t / 2.0);
447    /// ```
448    /// This kind of linear remapping is provided by the convenience method
449    /// [`CurveExt::reparametrize_linear`], which requires only the desired domain for the new curve.
450    ///
451    /// # Examples
452    /// ```
453    /// // Reverse a curve:
454    /// # use bevy_math::curve::*;
455    /// # use bevy_math::vec2;
456    /// let my_curve = ConstantCurve::new(Interval::UNIT, 1.0);
457    /// let domain = my_curve.domain();
458    /// let reversed_curve = my_curve.reparametrize(domain, |t| domain.end() - (t - domain.start()));
459    ///
460    /// // Take a segment of a curve:
461    /// # let my_curve = ConstantCurve::new(Interval::UNIT, 1.0);
462    /// let curve_segment = my_curve.reparametrize(interval(0.0, 0.5).unwrap(), |t| 0.5 + t);
463    /// ```
464    #[must_use]
465    fn reparametrize<F>(self, domain: Interval, f: F) -> ReparamCurve<T, Self, F>
466    where
467        F: Fn(f32) -> f32,
468    {
469        ReparamCurve {
470            domain,
471            base: self,
472            f,
473            _phantom: PhantomData,
474        }
475    }
476
477    /// Linearly reparametrize this [`Curve`], producing a new curve whose domain is the given
478    /// `domain` instead of the current one. This operation is only valid for curves with bounded
479    /// domains.
480    ///
481    /// # Errors
482    ///
483    /// If either this curve's domain or the given `domain` is unbounded, an error is returned.
484    fn reparametrize_linear(
485        self,
486        domain: Interval,
487    ) -> Result<LinearReparamCurve<T, Self>, LinearReparamError> {
488        if !self.domain().is_bounded() {
489            return Err(LinearReparamError::SourceCurveUnbounded);
490        }
491
492        if !domain.is_bounded() {
493            return Err(LinearReparamError::TargetIntervalUnbounded);
494        }
495
496        Ok(LinearReparamCurve {
497            base: self,
498            new_domain: domain,
499            _phantom: PhantomData,
500        })
501    }
502
503    /// Reparametrize this [`Curve`] by sampling from another curve.
504    ///
505    /// The resulting curve samples at time `t` by first sampling `other` at time `t`, which produces
506    /// another sample time `s` which is then used to sample this curve. The domain of the resulting
507    /// curve is the domain of `other`.
508    #[must_use]
509    fn reparametrize_by_curve<C>(self, other: C) -> CurveReparamCurve<T, Self, C>
510    where
511        C: Curve<f32>,
512    {
513        CurveReparamCurve {
514            base: self,
515            reparam_curve: other,
516            _phantom: PhantomData,
517        }
518    }
519
520    /// Create a new [`Curve`] which is the graph of this one; that is, its output echoes the sample
521    /// time as part of a tuple.
522    ///
523    /// For example, if this curve outputs `x` at time `t`, then the produced curve will produce
524    /// `(t, x)` at time `t`. In particular, if this curve is a `Curve<T>`, the output of this method
525    /// is a `Curve<(f32, T)>`.
526    #[must_use]
527    fn graph(self) -> GraphCurve<T, Self> {
528        GraphCurve {
529            base: self,
530            _phantom: PhantomData,
531        }
532    }
533
534    /// Create a new [`Curve`] by zipping this curve together with another.
535    ///
536    /// The sample at time `t` in the new curve is `(x, y)`, where `x` is the sample of `self` at
537    /// time `t` and `y` is the sample of `other` at time `t`. The domain of the new curve is the
538    /// intersection of the domains of its constituents.
539    ///
540    /// # Errors
541    ///
542    /// If the domain intersection would be empty, an error is returned instead.
543    fn zip<S, C>(self, other: C) -> Result<ZipCurve<T, S, Self, C>, InvalidIntervalError>
544    where
545        C: Curve<S> + Sized,
546    {
547        let domain = self.domain().intersect(other.domain())?;
548        Ok(ZipCurve {
549            domain,
550            first: self,
551            second: other,
552            _phantom: PhantomData,
553        })
554    }
555
556    /// Create a new [`Curve`] by composing this curve end-to-start with another, producing another curve
557    /// with outputs of the same type. The domain of the other curve is translated so that its start
558    /// coincides with where this curve ends.
559    ///
560    /// # Errors
561    ///
562    /// A [`ChainError`] is returned if this curve's domain doesn't have a finite end or if
563    /// `other`'s domain doesn't have a finite start.
564    fn chain<C>(self, other: C) -> Result<ChainCurve<T, Self, C>, ChainError>
565    where
566        C: Curve<T>,
567    {
568        if !self.domain().has_finite_end() {
569            return Err(ChainError::FirstEndInfinite);
570        }
571        if !other.domain().has_finite_start() {
572            return Err(ChainError::SecondStartInfinite);
573        }
574        Ok(ChainCurve {
575            first: self,
576            second: other,
577            _phantom: PhantomData,
578        })
579    }
580
581    /// Create a new [`Curve`] inverting this curve on the x-axis, producing another curve with
582    /// outputs of the same type, effectively playing backwards starting at `self.domain().end()`
583    /// and transitioning over to `self.domain().start()`. The domain of the new curve is still the
584    /// same.
585    ///
586    /// # Errors
587    ///
588    /// A [`ReverseError`] is returned if this curve's domain isn't bounded.
589    fn reverse(self) -> Result<ReverseCurve<T, Self>, ReverseError> {
590        self.domain()
591            .is_bounded()
592            .then(|| ReverseCurve {
593                curve: self,
594                _phantom: PhantomData,
595            })
596            .ok_or(ReverseError::SourceDomainEndInfinite)
597    }
598
599    /// Create a new [`Curve`] repeating this curve `N` times, producing another curve with outputs
600    /// of the same type. The domain of the new curve will be bigger by a factor of `n + 1`.
601    ///
602    /// # Notes
603    ///
604    /// - this doesn't guarantee a smooth transition from one occurrence of the curve to its next
605    ///   iteration. The curve will make a jump if `self.domain().start() != self.domain().end()`!
606    /// - for `count == 0` the output of this adaptor is basically identical to the previous curve
607    /// - the value at the transitioning points (`domain.end() * n` for `n >= 1`) in the results is the
608    ///   value at `domain.end()` in the original curve
609    ///
610    /// # Errors
611    ///
612    /// A [`RepeatError`] is returned if this curve's domain isn't bounded.
613    fn repeat(self, count: usize) -> Result<RepeatCurve<T, Self>, RepeatError> {
614        self.domain()
615            .is_bounded()
616            .then(|| {
617                // This unwrap always succeeds because `curve` has a valid Interval as its domain and the
618                // length of `curve` cannot be NAN. It's still fine if it's infinity.
619                let domain = Interval::new(
620                    self.domain().start(),
621                    self.domain().end() + self.domain().length() * count as f32,
622                )
623                .unwrap();
624                RepeatCurve {
625                    domain,
626                    curve: self,
627                    _phantom: PhantomData,
628                }
629            })
630            .ok_or(RepeatError::SourceDomainUnbounded)
631    }
632
633    /// Create a new [`Curve`] repeating this curve forever, producing another curve with
634    /// outputs of the same type. The domain of the new curve will be unbounded.
635    ///
636    /// # Notes
637    ///
638    /// - this doesn't guarantee a smooth transition from one occurrence of the curve to its next
639    ///   iteration. The curve will make a jump if `self.domain().start() != self.domain().end()`!
640    /// - the value at the transitioning points (`domain.end() * n` for `n >= 1`) in the results is the
641    ///   value at `domain.end()` in the original curve
642    ///
643    /// # Errors
644    ///
645    /// A [`RepeatError`] is returned if this curve's domain isn't bounded.
646    fn forever(self) -> Result<ForeverCurve<T, Self>, RepeatError> {
647        self.domain()
648            .is_bounded()
649            .then(|| ForeverCurve {
650                curve: self,
651                _phantom: PhantomData,
652            })
653            .ok_or(RepeatError::SourceDomainUnbounded)
654    }
655
656    /// Create a new [`Curve`] chaining the original curve with its inverse, producing
657    /// another curve with outputs of the same type. The domain of the new curve will be twice as
658    /// long. The transition point is guaranteed to not make any jumps.
659    ///
660    /// # Errors
661    ///
662    /// A [`PingPongError`] is returned if this curve's domain isn't right-finite.
663    fn ping_pong(self) -> Result<PingPongCurve<T, Self>, PingPongError> {
664        self.domain()
665            .has_finite_end()
666            .then(|| PingPongCurve {
667                curve: self,
668                _phantom: PhantomData,
669            })
670            .ok_or(PingPongError::SourceDomainEndInfinite)
671    }
672
673    /// Create a new [`Curve`] by composing this curve end-to-start with another, producing another
674    /// curve with outputs of the same type. The domain of the other curve is translated so that
675    /// its start coincides with where this curve ends.
676    ///
677    ///
678    /// Additionally the transition of the samples is guaranteed to make no sudden jumps. This is
679    /// useful if you really just know about the shapes of your curves and don't want to deal with
680    /// stitching them together properly when it would just introduce useless complexity. It is
681    /// realized by translating the other curve so that its start sample point coincides with the
682    /// current curves' end sample point.
683    ///
684    /// # Errors
685    ///
686    /// A [`ChainError`] is returned if this curve's domain doesn't have a finite end or if
687    /// `other`'s domain doesn't have a finite start.
688    fn chain_continue<C>(self, other: C) -> Result<ContinuationCurve<T, Self, C>, ChainError>
689    where
690        T: VectorSpace,
691        C: Curve<T>,
692    {
693        if !self.domain().has_finite_end() {
694            return Err(ChainError::FirstEndInfinite);
695        }
696        if !other.domain().has_finite_start() {
697            return Err(ChainError::SecondStartInfinite);
698        }
699
700        let offset = self.sample_unchecked(self.domain().end())
701            - other.sample_unchecked(self.domain().start());
702
703        Ok(ContinuationCurve {
704            first: self,
705            second: other,
706            offset,
707            _phantom: PhantomData,
708        })
709    }
710
711    /// Extract an iterator over evenly-spaced samples from this curve.
712    ///
713    /// # Errors
714    ///
715    /// If `samples` is less than 2 or if this curve has unbounded domain, a [`ResamplingError`]
716    /// is returned.
717    fn samples(&self, samples: usize) -> Result<impl Iterator<Item = T>, ResamplingError> {
718        if samples < 2 {
719            return Err(ResamplingError::NotEnoughSamples(samples));
720        }
721        if !self.domain().is_bounded() {
722            return Err(ResamplingError::UnboundedDomain);
723        }
724
725        // Unwrap on `spaced_points` always succeeds because its error conditions are handled
726        // above.
727        Ok(self
728            .domain()
729            .spaced_points(samples)
730            .unwrap()
731            .map(|t| self.sample_unchecked(t)))
732    }
733
734    /// Borrow this curve rather than taking ownership of it. This is essentially an alias for a
735    /// prefix `&`; the point is that intermediate operations can be performed while retaining
736    /// access to the original curve.
737    ///
738    /// # Example
739    /// ```
740    /// # use bevy_math::curve::*;
741    /// let my_curve = FunctionCurve::new(Interval::UNIT, |t| t * t + 1.0);
742    ///
743    /// // Borrow `my_curve` long enough to resample a mapped version. Note that `map` takes
744    /// // ownership of its input.
745    /// let samples = my_curve.by_ref().map(|x| x * 2.0).resample_auto(100).unwrap();
746    ///
747    /// // Do something else with `my_curve` since we retained ownership:
748    /// let new_curve = my_curve.reparametrize_linear(interval(-1.0, 1.0).unwrap()).unwrap();
749    /// ```
750    fn by_ref(&self) -> &Self {
751        self
752    }
753
754    /// Flip this curve so that its tuple output is arranged the other way.
755    #[must_use]
756    fn flip<U, V>(self) -> impl Curve<(V, U)>
757    where
758        Self: CurveExt<(U, V)>,
759    {
760        self.map(|(u, v)| (v, u))
761    }
762}
763
764impl<C, T> CurveExt<T> for C where C: Curve<T> {}
765
766/// Extension trait implemented by [curves], allowing access to generic resampling methods as
767/// well as those based on [stable interpolation].
768///
769/// This trait is automatically implemented for all curves.
770///
771/// For more information, see the [module-level documentation].
772///
773/// [curves]: Curve
774/// [stable interpolation]: crate::StableInterpolate
775/// [module-level documentation]: self
776#[cfg(feature = "alloc")]
777pub trait CurveResampleExt<T>: Curve<T> {
778    /// Resample this [`Curve`] to produce a new one that is defined by interpolation over equally
779    /// spaced sample values, using the provided `interpolation` to interpolate between adjacent samples.
780    /// The curve is interpolated on `segments` segments between samples. For example, if `segments` is 1,
781    /// only the start and end points of the curve are used as samples; if `segments` is 2, a sample at
782    /// the midpoint is taken as well, and so on.
783    ///
784    /// The interpolation takes two values by reference together with a scalar parameter and
785    /// produces an owned value. The expectation is that `interpolation(&x, &y, 0.0)` and
786    /// `interpolation(&x, &y, 1.0)` are equivalent to `x` and `y` respectively.
787    ///
788    /// # Errors
789    ///
790    /// If `segments` is zero or if this curve has unbounded domain, then a [`ResamplingError`] is
791    /// returned.
792    ///
793    /// # Example
794    /// ```
795    /// # use bevy_math::*;
796    /// # use bevy_math::curve::*;
797    /// let quarter_rotation = FunctionCurve::new(interval(0.0, 90.0).unwrap(), |t| Rot2::degrees(t));
798    /// // A curve which only stores three data points and uses `nlerp` to interpolate them:
799    /// let resampled_rotation = quarter_rotation.resample(3, |x, y, t| x.nlerp(*y, t));
800    /// ```
801    fn resample<I>(
802        &self,
803        segments: usize,
804        interpolation: I,
805    ) -> Result<SampleCurve<T, I>, ResamplingError>
806    where
807        I: Fn(&T, &T, f32) -> T,
808    {
809        let samples = self.samples(segments + 1)?.collect_vec();
810        Ok(SampleCurve {
811            core: EvenCore {
812                domain: self.domain(),
813                samples,
814            },
815            interpolation,
816        })
817    }
818
819    /// Resample this [`Curve`] to produce a new one that is defined by interpolation over equally
820    /// spaced sample values, using [automatic interpolation] to interpolate between adjacent samples.
821    /// The curve is interpolated on `segments` segments between samples. For example, if `segments` is 1,
822    /// only the start and end points of the curve are used as samples; if `segments` is 2, a sample at
823    /// the midpoint is taken as well, and so on.
824    ///
825    /// # Errors
826    ///
827    /// If `segments` is zero or if this curve has unbounded domain, a [`ResamplingError`] is returned.
828    ///
829    /// [automatic interpolation]: crate::common_traits::StableInterpolate
830    fn resample_auto(&self, segments: usize) -> Result<SampleAutoCurve<T>, ResamplingError>
831    where
832        T: StableInterpolate,
833    {
834        let samples = self.samples(segments + 1)?.collect_vec();
835        Ok(SampleAutoCurve {
836            core: EvenCore {
837                domain: self.domain(),
838                samples,
839            },
840        })
841    }
842
843    /// Resample this [`Curve`] to produce a new one that is defined by interpolation over samples
844    /// taken at a given set of times. The given `interpolation` is used to interpolate adjacent
845    /// samples, and the `sample_times` are expected to contain at least two valid times within the
846    /// curve's domain interval.
847    ///
848    /// Redundant sample times, non-finite sample times, and sample times outside of the domain
849    /// are filtered out. With an insufficient quantity of data, a [`ResamplingError`] is
850    /// returned.
851    ///
852    /// The domain of the produced curve stretches between the first and last sample times of the
853    /// iterator.
854    ///
855    /// The interpolation takes two values by reference together with a scalar parameter and
856    /// produces an owned value. The expectation is that `interpolation(&x, &y, 0.0)` and
857    /// `interpolation(&x, &y, 1.0)` are equivalent to `x` and `y` respectively.
858    ///
859    /// # Errors
860    ///
861    /// If `sample_times` doesn't contain at least two distinct times after filtering, a
862    /// [`ResamplingError`] is returned.
863    fn resample_uneven<I>(
864        &self,
865        sample_times: impl IntoIterator<Item = f32>,
866        interpolation: I,
867    ) -> Result<UnevenSampleCurve<T, I>, ResamplingError>
868    where
869        I: Fn(&T, &T, f32) -> T,
870    {
871        let domain = self.domain();
872        let mut times = sample_times
873            .into_iter()
874            .filter(|t| t.is_finite() && domain.contains(*t))
875            .collect_vec();
876        times.sort_by(f32::total_cmp);
877        times.dedup();
878        if times.len() < 2 {
879            return Err(ResamplingError::NotEnoughSamples(times.len()));
880        }
881        let samples = times.iter().map(|t| self.sample_unchecked(*t)).collect();
882        Ok(UnevenSampleCurve {
883            core: UnevenCore { times, samples },
884            interpolation,
885        })
886    }
887
888    /// Resample this [`Curve`] to produce a new one that is defined by [automatic interpolation] over
889    /// samples taken at the given set of times. The given `sample_times` are expected to contain at least
890    /// two valid times within the curve's domain interval.
891    ///
892    /// Redundant sample times, non-finite sample times, and sample times outside of the domain
893    /// are simply filtered out. With an insufficient quantity of data, a [`ResamplingError`] is
894    /// returned.
895    ///
896    /// The domain of the produced [`UnevenSampleAutoCurve`] stretches between the first and last
897    /// sample times of the iterator.
898    ///
899    /// # Errors
900    ///
901    /// If `sample_times` doesn't contain at least two distinct times after filtering, a
902    /// [`ResamplingError`] is returned.
903    ///
904    /// [automatic interpolation]: crate::common_traits::StableInterpolate
905    fn resample_uneven_auto(
906        &self,
907        sample_times: impl IntoIterator<Item = f32>,
908    ) -> Result<UnevenSampleAutoCurve<T>, ResamplingError>
909    where
910        T: StableInterpolate,
911    {
912        let domain = self.domain();
913        let mut times = sample_times
914            .into_iter()
915            .filter(|t| t.is_finite() && domain.contains(*t))
916            .collect_vec();
917        times.sort_by(f32::total_cmp);
918        times.dedup();
919        if times.len() < 2 {
920            return Err(ResamplingError::NotEnoughSamples(times.len()));
921        }
922        let samples = times.iter().map(|t| self.sample_unchecked(*t)).collect();
923        Ok(UnevenSampleAutoCurve {
924            core: UnevenCore { times, samples },
925        })
926    }
927}
928
929#[cfg(feature = "alloc")]
930impl<C, T> CurveResampleExt<T> for C where C: Curve<T> + ?Sized {}
931
932/// An error indicating that a linear reparameterization couldn't be performed because of
933/// malformed inputs.
934#[derive(Debug, Error)]
935#[error("Could not build a linear function to reparametrize this curve")]
936pub enum LinearReparamError {
937    /// The source curve that was to be reparametrized had unbounded domain.
938    #[error("This curve has unbounded domain")]
939    SourceCurveUnbounded,
940
941    /// The target interval for reparameterization was unbounded.
942    #[error("The target interval for reparameterization is unbounded")]
943    TargetIntervalUnbounded,
944}
945
946/// An error indicating that a reversion of a curve couldn't be performed because of
947/// malformed inputs.
948#[derive(Debug, Error)]
949#[error("Could not reverse this curve")]
950pub enum ReverseError {
951    /// The source curve that was to be reversed had unbounded domain end.
952    #[error("This curve has an unbounded domain end")]
953    SourceDomainEndInfinite,
954}
955
956/// An error indicating that a repetition of a curve couldn't be performed because of malformed
957/// inputs.
958#[derive(Debug, Error)]
959#[error("Could not repeat this curve")]
960pub enum RepeatError {
961    /// The source curve that was to be repeated had unbounded domain.
962    #[error("This curve has an unbounded domain")]
963    SourceDomainUnbounded,
964}
965
966/// An error indicating that a ping ponging of a curve couldn't be performed because of
967/// malformed inputs.
968#[derive(Debug, Error)]
969#[error("Could not ping pong this curve")]
970pub enum PingPongError {
971    /// The source curve that was to be ping ponged had unbounded domain end.
972    #[error("This curve has an unbounded domain end")]
973    SourceDomainEndInfinite,
974}
975
976/// An error indicating that an end-to-end composition couldn't be performed because of
977/// malformed inputs.
978#[derive(Debug, Error)]
979#[error("Could not compose these curves together")]
980pub enum ChainError {
981    /// The right endpoint of the first curve was infinite.
982    #[error("The first curve's domain has an infinite end")]
983    FirstEndInfinite,
984
985    /// The left endpoint of the second curve was infinite.
986    #[error("The second curve's domain has an infinite start")]
987    SecondStartInfinite,
988}
989
990/// An error indicating that a resampling operation could not be performed because of
991/// malformed inputs.
992#[derive(Debug, Error)]
993#[error("Could not resample from this curve because of bad inputs")]
994pub enum ResamplingError {
995    /// This resampling operation was not provided with enough samples to have well-formed output.
996    #[error("Not enough unique samples to construct resampled curve")]
997    NotEnoughSamples(usize),
998
999    /// This resampling operation failed because of an unbounded interval.
1000    #[error("Could not resample because this curve has unbounded domain")]
1001    UnboundedDomain,
1002}
1003
1004#[cfg(test)]
1005mod tests {
1006    use super::*;
1007    use crate::{ops, Quat};
1008    use alloc::vec::Vec;
1009    use approx::{assert_abs_diff_eq, AbsDiffEq};
1010    use core::f32::consts::TAU;
1011    use glam::*;
1012
1013    #[test]
1014    fn curve_can_be_made_into_an_object() {
1015        let curve = ConstantCurve::new(Interval::UNIT, 42.0);
1016        let curve: &dyn Curve<f64> = &curve;
1017
1018        assert_eq!(curve.sample(1.0), Some(42.0));
1019        assert_eq!(curve.sample(2.0), None);
1020    }
1021
1022    #[test]
1023    fn constant_curves() {
1024        let curve = ConstantCurve::new(Interval::EVERYWHERE, 5.0);
1025        assert!(curve.sample_unchecked(-35.0) == 5.0);
1026
1027        let curve = ConstantCurve::new(Interval::UNIT, true);
1028        assert!(curve.sample_unchecked(2.0));
1029        assert!(curve.sample(2.0).is_none());
1030    }
1031
1032    #[test]
1033    fn function_curves() {
1034        let curve = FunctionCurve::new(Interval::EVERYWHERE, |t| t * t);
1035        assert!(curve.sample_unchecked(2.0).abs_diff_eq(&4.0, f32::EPSILON));
1036        assert!(curve.sample_unchecked(-3.0).abs_diff_eq(&9.0, f32::EPSILON));
1037
1038        let curve = FunctionCurve::new(interval(0.0, f32::INFINITY).unwrap(), ops::log2);
1039        assert_eq!(curve.sample_unchecked(3.5), ops::log2(3.5));
1040        assert!(curve.sample_unchecked(-1.0).is_nan());
1041        assert!(curve.sample(-1.0).is_none());
1042    }
1043
1044    #[test]
1045    fn linear_curve() {
1046        let start = Vec2::ZERO;
1047        let end = Vec2::new(1.0, 2.0);
1048        let curve = EasingCurve::new(start, end, EaseFunction::Linear);
1049
1050        let mid = (start + end) / 2.0;
1051
1052        [(0.0, start), (0.5, mid), (1.0, end)]
1053            .into_iter()
1054            .for_each(|(t, x)| {
1055                assert!(curve.sample_unchecked(t).abs_diff_eq(x, f32::EPSILON));
1056            });
1057    }
1058
1059    #[test]
1060    fn easing_curves_step() {
1061        let start = Vec2::ZERO;
1062        let end = Vec2::new(1.0, 2.0);
1063
1064        let curve = EasingCurve::new(start, end, EaseFunction::Steps(4, JumpAt::End));
1065        [
1066            (0.0, start),
1067            (0.249, start),
1068            (0.250, Vec2::new(0.25, 0.5)),
1069            (0.499, Vec2::new(0.25, 0.5)),
1070            (0.500, Vec2::new(0.5, 1.0)),
1071            (0.749, Vec2::new(0.5, 1.0)),
1072            (0.750, Vec2::new(0.75, 1.5)),
1073            (1.0, end),
1074        ]
1075        .into_iter()
1076        .for_each(|(t, x)| {
1077            assert!(curve.sample_unchecked(t).abs_diff_eq(x, f32::EPSILON));
1078        });
1079    }
1080
1081    #[test]
1082    fn easing_curves_quadratic() {
1083        let start = Vec2::ZERO;
1084        let end = Vec2::new(1.0, 2.0);
1085
1086        let curve = EasingCurve::new(start, end, EaseFunction::QuadraticIn);
1087        [
1088            (0.0, start),
1089            (0.25, Vec2::new(0.0625, 0.125)),
1090            (0.5, Vec2::new(0.25, 0.5)),
1091            (1.0, end),
1092        ]
1093        .into_iter()
1094        .for_each(|(t, x)| {
1095            assert!(curve.sample_unchecked(t).abs_diff_eq(x, f32::EPSILON),);
1096        });
1097    }
1098
1099    #[test]
1100    fn mapping() {
1101        let curve = FunctionCurve::new(Interval::EVERYWHERE, |t| t * 3.0 + 1.0);
1102        let mapped_curve = curve.map(|x| x / 7.0);
1103        assert_eq!(mapped_curve.sample_unchecked(3.5), (3.5 * 3.0 + 1.0) / 7.0);
1104        assert_eq!(
1105            mapped_curve.sample_unchecked(-1.0),
1106            (-1.0 * 3.0 + 1.0) / 7.0
1107        );
1108        assert_eq!(mapped_curve.domain(), Interval::EVERYWHERE);
1109
1110        let curve = FunctionCurve::new(Interval::UNIT, |t| t * TAU);
1111        let mapped_curve = curve.map(Quat::from_rotation_z);
1112        assert_eq!(mapped_curve.sample_unchecked(0.0), Quat::IDENTITY);
1113        assert!(mapped_curve.sample_unchecked(1.0).is_near_identity());
1114        assert_eq!(mapped_curve.domain(), Interval::UNIT);
1115    }
1116
1117    #[test]
1118    fn reverse() {
1119        let curve = FunctionCurve::new(Interval::new(0.0, 1.0).unwrap(), |t| t * 3.0 + 1.0);
1120        let rev_curve = curve.reverse().unwrap();
1121        assert_eq!(rev_curve.sample(-0.1), None);
1122        assert_eq!(rev_curve.sample(0.0), Some(1.0 * 3.0 + 1.0));
1123        assert_eq!(rev_curve.sample(0.5), Some(0.5 * 3.0 + 1.0));
1124        assert_eq!(rev_curve.sample(1.0), Some(0.0 * 3.0 + 1.0));
1125        assert_eq!(rev_curve.sample(1.1), None);
1126
1127        let curve = FunctionCurve::new(Interval::new(-2.0, 1.0).unwrap(), |t| t * 3.0 + 1.0);
1128        let rev_curve = curve.reverse().unwrap();
1129        assert_eq!(rev_curve.sample(-2.1), None);
1130        assert_eq!(rev_curve.sample(-2.0), Some(1.0 * 3.0 + 1.0));
1131        assert_eq!(rev_curve.sample(-0.5), Some(-0.5 * 3.0 + 1.0));
1132        assert_eq!(rev_curve.sample(1.0), Some(-2.0 * 3.0 + 1.0));
1133        assert_eq!(rev_curve.sample(1.1), None);
1134    }
1135
1136    #[test]
1137    fn repeat() {
1138        let curve = FunctionCurve::new(Interval::new(0.0, 1.0).unwrap(), |t| t * 3.0 + 1.0);
1139        let repeat_curve = curve.by_ref().repeat(1).unwrap();
1140        assert_eq!(repeat_curve.sample(-0.1), None);
1141        assert_eq!(repeat_curve.sample(0.0), Some(0.0 * 3.0 + 1.0));
1142        assert_eq!(repeat_curve.sample(0.5), Some(0.5 * 3.0 + 1.0));
1143        assert_eq!(repeat_curve.sample(0.99), Some(0.99 * 3.0 + 1.0));
1144        assert_eq!(repeat_curve.sample(1.0), Some(1.0 * 3.0 + 1.0));
1145        assert_eq!(repeat_curve.sample(1.01), Some(0.01 * 3.0 + 1.0));
1146        assert_eq!(repeat_curve.sample(1.5), Some(0.5 * 3.0 + 1.0));
1147        assert_eq!(repeat_curve.sample(1.99), Some(0.99 * 3.0 + 1.0));
1148        assert_eq!(repeat_curve.sample(2.0), Some(1.0 * 3.0 + 1.0));
1149        assert_eq!(repeat_curve.sample(2.01), None);
1150
1151        let repeat_curve = curve.by_ref().repeat(3).unwrap();
1152        assert_eq!(repeat_curve.sample(2.0), Some(1.0 * 3.0 + 1.0));
1153        assert_eq!(repeat_curve.sample(3.0), Some(1.0 * 3.0 + 1.0));
1154        assert_eq!(repeat_curve.sample(4.0), Some(1.0 * 3.0 + 1.0));
1155        assert_eq!(repeat_curve.sample(5.0), None);
1156
1157        let repeat_curve = curve.by_ref().forever().unwrap();
1158        assert_eq!(repeat_curve.sample(-1.0), Some(1.0 * 3.0 + 1.0));
1159        assert_eq!(repeat_curve.sample(2.0), Some(1.0 * 3.0 + 1.0));
1160        assert_eq!(repeat_curve.sample(3.0), Some(1.0 * 3.0 + 1.0));
1161        assert_eq!(repeat_curve.sample(4.0), Some(1.0 * 3.0 + 1.0));
1162        assert_eq!(repeat_curve.sample(5.0), Some(1.0 * 3.0 + 1.0));
1163    }
1164
1165    #[test]
1166    fn ping_pong() {
1167        let curve = FunctionCurve::new(Interval::new(0.0, 1.0).unwrap(), |t| t * 3.0 + 1.0);
1168        let ping_pong_curve = curve.ping_pong().unwrap();
1169        assert_eq!(ping_pong_curve.sample(-0.1), None);
1170        assert_eq!(ping_pong_curve.sample(0.0), Some(0.0 * 3.0 + 1.0));
1171        assert_eq!(ping_pong_curve.sample(0.5), Some(0.5 * 3.0 + 1.0));
1172        assert_eq!(ping_pong_curve.sample(1.0), Some(1.0 * 3.0 + 1.0));
1173        assert_eq!(ping_pong_curve.sample(1.5), Some(0.5 * 3.0 + 1.0));
1174        assert_eq!(ping_pong_curve.sample(2.0), Some(0.0 * 3.0 + 1.0));
1175        assert_eq!(ping_pong_curve.sample(2.1), None);
1176
1177        let curve = FunctionCurve::new(Interval::new(-2.0, 2.0).unwrap(), |t| t * 3.0 + 1.0);
1178        let ping_pong_curve = curve.ping_pong().unwrap();
1179        assert_eq!(ping_pong_curve.sample(-2.1), None);
1180        assert_eq!(ping_pong_curve.sample(-2.0), Some(-2.0 * 3.0 + 1.0));
1181        assert_eq!(ping_pong_curve.sample(-0.5), Some(-0.5 * 3.0 + 1.0));
1182        assert_eq!(ping_pong_curve.sample(2.0), Some(2.0 * 3.0 + 1.0));
1183        assert_eq!(ping_pong_curve.sample(4.5), Some(-0.5 * 3.0 + 1.0));
1184        assert_eq!(ping_pong_curve.sample(6.0), Some(-2.0 * 3.0 + 1.0));
1185        assert_eq!(ping_pong_curve.sample(6.1), None);
1186    }
1187
1188    #[test]
1189    fn continue_chain() {
1190        let first = FunctionCurve::new(Interval::new(0.0, 1.0).unwrap(), |t| t * 3.0 + 1.0);
1191        let second = FunctionCurve::new(Interval::new(0.0, 1.0).unwrap(), |t| t * t);
1192        let c0_chain_curve = first.chain_continue(second).unwrap();
1193        assert_eq!(c0_chain_curve.sample(-0.1), None);
1194        assert_eq!(c0_chain_curve.sample(0.0), Some(0.0 * 3.0 + 1.0));
1195        assert_eq!(c0_chain_curve.sample(0.5), Some(0.5 * 3.0 + 1.0));
1196        assert_eq!(c0_chain_curve.sample(1.0), Some(1.0 * 3.0 + 1.0));
1197        assert_eq!(c0_chain_curve.sample(1.5), Some(1.0 * 3.0 + 1.0 + 0.25));
1198        assert_eq!(c0_chain_curve.sample(2.0), Some(1.0 * 3.0 + 1.0 + 1.0));
1199        assert_eq!(c0_chain_curve.sample(2.1), None);
1200    }
1201
1202    #[test]
1203    fn reparameterization() {
1204        let curve = FunctionCurve::new(interval(1.0, f32::INFINITY).unwrap(), ops::log2);
1205        let reparametrized_curve = curve
1206            .by_ref()
1207            .reparametrize(interval(0.0, f32::INFINITY).unwrap(), ops::exp2);
1208        assert_abs_diff_eq!(reparametrized_curve.sample_unchecked(3.5), 3.5);
1209        assert_abs_diff_eq!(reparametrized_curve.sample_unchecked(100.0), 100.0);
1210        assert_eq!(
1211            reparametrized_curve.domain(),
1212            interval(0.0, f32::INFINITY).unwrap()
1213        );
1214
1215        let reparametrized_curve = curve.by_ref().reparametrize(Interval::UNIT, |t| t + 1.0);
1216        assert_abs_diff_eq!(reparametrized_curve.sample_unchecked(0.0), 0.0);
1217        assert_abs_diff_eq!(reparametrized_curve.sample_unchecked(1.0), 1.0);
1218        assert_eq!(reparametrized_curve.domain(), Interval::UNIT);
1219    }
1220
1221    #[test]
1222    fn multiple_maps() {
1223        // Make sure these actually happen in the right order.
1224        let curve = FunctionCurve::new(Interval::UNIT, ops::exp2);
1225        let first_mapped = curve.map(ops::log2);
1226        let second_mapped = first_mapped.map(|x| x * -2.0);
1227        assert_abs_diff_eq!(second_mapped.sample_unchecked(0.0), 0.0);
1228        assert_abs_diff_eq!(second_mapped.sample_unchecked(0.5), -1.0);
1229        assert_abs_diff_eq!(second_mapped.sample_unchecked(1.0), -2.0);
1230    }
1231
1232    #[test]
1233    fn multiple_reparams() {
1234        // Make sure these happen in the right order too.
1235        let curve = FunctionCurve::new(Interval::UNIT, ops::exp2);
1236        let first_reparam = curve.reparametrize(interval(1.0, 2.0).unwrap(), ops::log2);
1237        let second_reparam = first_reparam.reparametrize(Interval::UNIT, |t| t + 1.0);
1238        assert_abs_diff_eq!(second_reparam.sample_unchecked(0.0), 1.0);
1239        assert_abs_diff_eq!(second_reparam.sample_unchecked(0.5), 1.5);
1240        assert_abs_diff_eq!(second_reparam.sample_unchecked(1.0), 2.0);
1241    }
1242
1243    #[test]
1244    fn resampling() {
1245        let curve = FunctionCurve::new(interval(1.0, 4.0).unwrap(), ops::log2);
1246
1247        // Need at least one segment to sample.
1248        let nice_try = curve.by_ref().resample_auto(0);
1249        assert!(nice_try.is_err());
1250
1251        // The values of a resampled curve should be very close at the sample points.
1252        // Because of denominators, it's not literally equal.
1253        // (This is a tradeoff against O(1) sampling.)
1254        let resampled_curve = curve.by_ref().resample_auto(100).unwrap();
1255        for test_pt in curve.domain().spaced_points(101).unwrap() {
1256            let expected = curve.sample_unchecked(test_pt);
1257            assert_abs_diff_eq!(
1258                resampled_curve.sample_unchecked(test_pt),
1259                expected,
1260                epsilon = 1e-6
1261            );
1262        }
1263
1264        // Another example.
1265        let curve = FunctionCurve::new(interval(0.0, TAU).unwrap(), ops::cos);
1266        let resampled_curve = curve.by_ref().resample_auto(1000).unwrap();
1267        for test_pt in curve.domain().spaced_points(1001).unwrap() {
1268            let expected = curve.sample_unchecked(test_pt);
1269            assert_abs_diff_eq!(
1270                resampled_curve.sample_unchecked(test_pt),
1271                expected,
1272                epsilon = 1e-6
1273            );
1274        }
1275    }
1276
1277    #[test]
1278    fn uneven_resampling() {
1279        let curve = FunctionCurve::new(interval(0.0, f32::INFINITY).unwrap(), ops::exp);
1280
1281        // Need at least two points to resample.
1282        let nice_try = curve.by_ref().resample_uneven_auto([1.0; 1]);
1283        assert!(nice_try.is_err());
1284
1285        // Uneven sampling should produce literal equality at the sample points.
1286        // (This is part of what you get in exchange for O(log(n)) sampling.)
1287        let sample_points = (0..100).map(|idx| idx as f32 * 0.1);
1288        let resampled_curve = curve.by_ref().resample_uneven_auto(sample_points).unwrap();
1289        for idx in 0..100 {
1290            let test_pt = idx as f32 * 0.1;
1291            let expected = curve.sample_unchecked(test_pt);
1292            assert_eq!(resampled_curve.sample_unchecked(test_pt), expected);
1293        }
1294        assert_abs_diff_eq!(resampled_curve.domain().start(), 0.0);
1295        assert_abs_diff_eq!(resampled_curve.domain().end(), 9.9, epsilon = 1e-6);
1296
1297        // Another example.
1298        let curve = FunctionCurve::new(interval(1.0, f32::INFINITY).unwrap(), ops::log2);
1299        let sample_points = (0..10).map(|idx| ops::exp2(idx as f32));
1300        let resampled_curve = curve.by_ref().resample_uneven_auto(sample_points).unwrap();
1301        for idx in 0..10 {
1302            let test_pt = ops::exp2(idx as f32);
1303            let expected = curve.sample_unchecked(test_pt);
1304            assert_eq!(resampled_curve.sample_unchecked(test_pt), expected);
1305        }
1306        assert_abs_diff_eq!(resampled_curve.domain().start(), 1.0);
1307        assert_abs_diff_eq!(resampled_curve.domain().end(), 512.0);
1308    }
1309
1310    #[test]
1311    fn sample_iterators() {
1312        let times = [-0.5, 0.0, 0.5, 1.0, 1.5];
1313
1314        let curve = FunctionCurve::new(Interval::EVERYWHERE, |t| t * 3.0 + 1.0);
1315        let samples = curve.sample_iter_unchecked(times).collect::<Vec<_>>();
1316        let [y0, y1, y2, y3, y4] = samples.try_into().unwrap();
1317
1318        assert_eq!(y0, -0.5 * 3.0 + 1.0);
1319        assert_eq!(y1, 0.0 * 3.0 + 1.0);
1320        assert_eq!(y2, 0.5 * 3.0 + 1.0);
1321        assert_eq!(y3, 1.0 * 3.0 + 1.0);
1322        assert_eq!(y4, 1.5 * 3.0 + 1.0);
1323
1324        let finite_curve = FunctionCurve::new(Interval::new(0.0, 1.0).unwrap(), |t| t * 3.0 + 1.0);
1325        let samples = finite_curve.sample_iter(times).collect::<Vec<_>>();
1326        let [y0, y1, y2, y3, y4] = samples.try_into().unwrap();
1327
1328        assert_eq!(y0, None);
1329        assert_eq!(y1, Some(0.0 * 3.0 + 1.0));
1330        assert_eq!(y2, Some(0.5 * 3.0 + 1.0));
1331        assert_eq!(y3, Some(1.0 * 3.0 + 1.0));
1332        assert_eq!(y4, None);
1333
1334        let samples = finite_curve.sample_iter_clamped(times).collect::<Vec<_>>();
1335        let [y0, y1, y2, y3, y4] = samples.try_into().unwrap();
1336
1337        assert_eq!(y0, 0.0 * 3.0 + 1.0);
1338        assert_eq!(y1, 0.0 * 3.0 + 1.0);
1339        assert_eq!(y2, 0.5 * 3.0 + 1.0);
1340        assert_eq!(y3, 1.0 * 3.0 + 1.0);
1341        assert_eq!(y4, 1.0 * 3.0 + 1.0);
1342    }
1343}