Skip to main content

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