Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_math/src/curve/derivatives/mod.rs
6598 views
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
25
pub mod adaptor_impls;
26
27
use crate::{
28
common_traits::{HasTangent, WithDerivative, WithTwoDerivatives},
29
curve::{Curve, Interval},
30
};
31
use core::ops::Deref;
32
33
#[cfg(feature = "bevy_reflect")]
34
use 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`].
40
pub trait CurveWithDerivative<T>: SampleDerivative<T> + Sized
41
where
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`].
54
pub trait CurveWithTwoDerivatives<T>: SampleTwoDerivatives<T> + Sized
55
where
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
71
pub trait SampleDerivative<T>: Curve<T>
72
where
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
100
impl<T, C, D> SampleDerivative<T> for D
101
where
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
118
pub trait SampleTwoDerivatives<T>: Curve<T>
119
where
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
)]
155
pub struct SampleDerivativeWrapper<C>(C);
156
157
impl<T, C> Curve<WithDerivative<T>> for SampleDerivativeWrapper<C>
158
where
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
)]
188
pub struct SampleTwoDerivativesWrapper<C>(C);
189
190
impl<T, C> Curve<WithTwoDerivatives<T>> for SampleTwoDerivativesWrapper<C>
191
where
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
212
impl<T, C> CurveWithDerivative<T> for C
213
where
214
T: HasTangent,
215
C: SampleDerivative<T>,
216
{
217
fn with_derivative(self) -> SampleDerivativeWrapper<Self> {
218
SampleDerivativeWrapper(self)
219
}
220
}
221
222
impl<T, C> CurveWithTwoDerivatives<T> for C
223
where
224
T: HasTangent,
225
C: SampleTwoDerivatives<T> + CurveWithDerivative<T>,
226
{
227
fn with_two_derivatives(self) -> SampleTwoDerivativesWrapper<Self> {
228
SampleTwoDerivativesWrapper(self)
229
}
230
}
231
232