bevy_math/curve/derivatives/
mod.rs

1//! This module holds traits related to extracting derivatives from curves. In
2//! applications, the derivatives of interest are chiefly the first and second;
3//! in this module, these are provided by the traits [`CurveWithDerivative`]
4//! and [`CurveWithTwoDerivatives`].
5//!
6//! These take ownership of the curve they are used on by default, so that
7//! the resulting output may be used in more durable contexts. For example,
8//! `CurveWithDerivative<T>` is not dyn-compatible, but `Curve<WithDerivative<T>>`
9//! is, so if such a curve needs to be stored in a dynamic context, calling
10//! [`with_derivative`] and then placing the result in a
11//! `Box<Curve<WithDerivative<T>>>` is sensible.
12//!
13//! On the other hand, in more transient contexts, consuming a value merely to
14//! sample derivatives is inconvenient, and in these cases, it is recommended
15//! to use [`by_ref`] when possible to create a referential curve first, retaining
16//! liveness of the original.
17//!
18//! This module also holds the [`SampleDerivative`] and [`SampleTwoDerivatives`]
19//! traits, which can be used to easily implement `CurveWithDerivative` and its
20//! counterpart.
21//!
22//! [`with_derivative`]: CurveWithDerivative::with_derivative
23//! [`by_ref`]: crate::curve::CurveExt::by_ref
24
25pub mod adaptor_impls;
26
27use crate::{
28    common_traits::{HasTangent, WithDerivative, WithTwoDerivatives},
29    curve::{Curve, Interval},
30};
31use core::ops::Deref;
32
33#[cfg(feature = "bevy_reflect")]
34use bevy_reflect::{FromReflect, Reflect};
35
36/// Trait for curves that have a well-defined notion of derivative, allowing for
37/// derivatives to be extracted along with values.
38///
39/// This is implemented by implementing [`SampleDerivative`].
40pub trait CurveWithDerivative<T>: SampleDerivative<T> + Sized
41where
42    T: HasTangent,
43{
44    /// This curve, but with its first derivative included in sampling.
45    ///
46    /// Notably, the output type is a `Curve<WithDerivative<T>>`.
47    fn with_derivative(self) -> SampleDerivativeWrapper<Self>;
48}
49
50/// Trait for curves that have a well-defined notion of second derivative,
51/// allowing for two derivatives to be extracted along with values.
52///
53/// This is implemented by implementing [`SampleTwoDerivatives`].
54pub trait CurveWithTwoDerivatives<T>: SampleTwoDerivatives<T> + Sized
55where
56    T: HasTangent,
57{
58    /// This curve, but with its first two derivatives included in sampling.
59    ///
60    /// Notably, the output type is a `Curve<WithTwoDerivatives<T>>`.
61    fn with_two_derivatives(self) -> SampleTwoDerivativesWrapper<Self>;
62}
63
64/// A trait for curves that can sample derivatives in addition to values.
65///
66/// Types that implement this trait automatically implement [`CurveWithDerivative`];
67/// the curve produced by [`with_derivative`] uses the sampling defined in the trait
68/// implementation.
69///
70/// [`with_derivative`]: CurveWithDerivative::with_derivative
71pub trait SampleDerivative<T>: Curve<T>
72where
73    T: HasTangent,
74{
75    /// Sample this curve at the parameter value `t`, extracting the associated value
76    /// in addition to its derivative. This is the unchecked version of sampling, which
77    /// should only be used if the sample time `t` is already known to lie within the
78    /// curve's domain.
79    ///
80    /// See [`Curve::sample_unchecked`] for more information.
81    fn sample_with_derivative_unchecked(&self, t: f32) -> WithDerivative<T>;
82
83    /// Sample this curve's value and derivative at the parameter value `t`, returning
84    /// `None` if the point is outside of the curve's domain.
85    fn sample_with_derivative(&self, t: f32) -> Option<WithDerivative<T>> {
86        match self.domain().contains(t) {
87            true => Some(self.sample_with_derivative_unchecked(t)),
88            false => None,
89        }
90    }
91
92    /// Sample this curve's value and derivative at the parameter value `t`, clamping `t`
93    /// to lie inside the domain of the curve.
94    fn sample_with_derivative_clamped(&self, t: f32) -> WithDerivative<T> {
95        let t = self.domain().clamp(t);
96        self.sample_with_derivative_unchecked(t)
97    }
98}
99
100impl<T, C, D> SampleDerivative<T> for D
101where
102    T: HasTangent,
103    C: SampleDerivative<T> + ?Sized,
104    D: Deref<Target = C>,
105{
106    fn sample_with_derivative_unchecked(&self, t: f32) -> WithDerivative<T> {
107        <C as SampleDerivative<T>>::sample_with_derivative_unchecked(self, t)
108    }
109}
110
111/// A trait for curves that can sample two derivatives in addition to values.
112///
113/// Types that implement this trait automatically implement [`CurveWithTwoDerivatives`];
114/// the curve produced by [`with_two_derivatives`] uses the sampling defined in the trait
115/// implementation.
116///
117/// [`with_two_derivatives`]: CurveWithTwoDerivatives::with_two_derivatives
118pub trait SampleTwoDerivatives<T>: Curve<T>
119where
120    T: HasTangent,
121{
122    /// Sample this curve at the parameter value `t`, extracting the associated value
123    /// in addition to two derivatives. This is the unchecked version of sampling, which
124    /// should only be used if the sample time `t` is already known to lie within the
125    /// curve's domain.
126    ///
127    /// See [`Curve::sample_unchecked`] for more information.
128    fn sample_with_two_derivatives_unchecked(&self, t: f32) -> WithTwoDerivatives<T>;
129
130    /// Sample this curve's value and two derivatives at the parameter value `t`, returning
131    /// `None` if the point is outside of the curve's domain.
132    fn sample_with_two_derivatives(&self, t: f32) -> Option<WithTwoDerivatives<T>> {
133        match self.domain().contains(t) {
134            true => Some(self.sample_with_two_derivatives_unchecked(t)),
135            false => None,
136        }
137    }
138
139    /// Sample this curve's value and two derivatives at the parameter value `t`, clamping `t`
140    /// to lie inside the domain of the curve.
141    fn sample_with_two_derivatives_clamped(&self, t: f32) -> WithTwoDerivatives<T> {
142        let t = self.domain().clamp(t);
143        self.sample_with_two_derivatives_unchecked(t)
144    }
145}
146
147/// A wrapper that uses a [`SampleDerivative<T>`] curve to produce a `Curve<WithDerivative<T>>`.
148#[derive(Copy, Clone, Debug, Default, PartialEq)]
149#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
150#[cfg_attr(
151    feature = "bevy_reflect",
152    derive(Reflect, FromReflect),
153    reflect(from_reflect = false)
154)]
155pub struct SampleDerivativeWrapper<C>(C);
156
157impl<T, C> Curve<WithDerivative<T>> for SampleDerivativeWrapper<C>
158where
159    T: HasTangent,
160    C: SampleDerivative<T>,
161{
162    fn domain(&self) -> Interval {
163        self.0.domain()
164    }
165
166    fn sample_unchecked(&self, t: f32) -> WithDerivative<T> {
167        self.0.sample_with_derivative_unchecked(t)
168    }
169
170    fn sample(&self, t: f32) -> Option<WithDerivative<T>> {
171        self.0.sample_with_derivative(t)
172    }
173
174    fn sample_clamped(&self, t: f32) -> WithDerivative<T> {
175        self.0.sample_with_derivative_clamped(t)
176    }
177}
178
179/// A wrapper that uses a [`SampleTwoDerivatives<T>`] curve to produce a
180/// `Curve<WithTwoDerivatives<T>>`.
181#[derive(Copy, Clone, Debug, Default, PartialEq)]
182#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
183#[cfg_attr(
184    feature = "bevy_reflect",
185    derive(Reflect, FromReflect),
186    reflect(from_reflect = false)
187)]
188pub struct SampleTwoDerivativesWrapper<C>(C);
189
190impl<T, C> Curve<WithTwoDerivatives<T>> for SampleTwoDerivativesWrapper<C>
191where
192    T: HasTangent,
193    C: SampleTwoDerivatives<T>,
194{
195    fn domain(&self) -> Interval {
196        self.0.domain()
197    }
198
199    fn sample_unchecked(&self, t: f32) -> WithTwoDerivatives<T> {
200        self.0.sample_with_two_derivatives_unchecked(t)
201    }
202
203    fn sample(&self, t: f32) -> Option<WithTwoDerivatives<T>> {
204        self.0.sample_with_two_derivatives(t)
205    }
206
207    fn sample_clamped(&self, t: f32) -> WithTwoDerivatives<T> {
208        self.0.sample_with_two_derivatives_clamped(t)
209    }
210}
211
212impl<T, C> CurveWithDerivative<T> for C
213where
214    T: HasTangent,
215    C: SampleDerivative<T>,
216{
217    fn with_derivative(self) -> SampleDerivativeWrapper<Self> {
218        SampleDerivativeWrapper(self)
219    }
220}
221
222impl<T, C> CurveWithTwoDerivatives<T> for C
223where
224    T: HasTangent,
225    C: SampleTwoDerivatives<T> + CurveWithDerivative<T>,
226{
227    fn with_two_derivatives(self) -> SampleTwoDerivativesWrapper<Self> {
228        SampleTwoDerivativesWrapper(self)
229    }
230}