1use bevy_math::{
4 curve::{cores::*, iterable::IterableCurve, *},
5 vec4, Quat, Vec4, VectorSpace,
6};
7use bevy_reflect::Reflect;
8use either::Either;
9use thiserror::Error;
10
11#[derive(Debug, Clone, Reflect)]
13pub struct SteppedKeyframeCurve<T> {
14 core: UnevenCore<T>,
15}
16
17impl<T> Curve<T> for SteppedKeyframeCurve<T>
18where
19 T: Clone,
20{
21 #[inline]
22 fn domain(&self) -> Interval {
23 self.core.domain()
24 }
25
26 #[inline]
27 fn sample_clamped(&self, t: f32) -> T {
28 self.core
29 .sample_with(t, |x, y, t| if t >= 1.0 { y.clone() } else { x.clone() })
30 }
31
32 #[inline]
33 fn sample_unchecked(&self, t: f32) -> T {
34 self.sample_clamped(t)
35 }
36}
37
38impl<T> SteppedKeyframeCurve<T> {
39 #[inline]
42 pub fn new(timed_samples: impl IntoIterator<Item = (f32, T)>) -> Result<Self, UnevenCoreError> {
43 Ok(Self {
44 core: UnevenCore::new(timed_samples)?,
45 })
46 }
47}
48
49#[derive(Debug, Clone, Reflect)]
51pub struct CubicKeyframeCurve<T> {
52 core: ChunkedUnevenCore<T>,
54}
55
56impl<V> Curve<V> for CubicKeyframeCurve<V>
57where
58 V: VectorSpace<Scalar = f32>,
59{
60 #[inline]
61 fn domain(&self) -> Interval {
62 self.core.domain()
63 }
64
65 #[inline]
66 fn sample_clamped(&self, t: f32) -> V {
67 match self.core.sample_interp_timed(t) {
68 InterpolationDatum::Exact((_, v))
70 | InterpolationDatum::LeftTail((_, v))
71 | InterpolationDatum::RightTail((_, v)) => v[1],
72
73 InterpolationDatum::Between((t0, u), (t1, v), s) => {
74 cubic_spline_interpolation(u[1], u[2], v[0], v[1], s, t1 - t0)
75 }
76 }
77 }
78
79 #[inline]
80 fn sample_unchecked(&self, t: f32) -> V {
81 self.sample_clamped(t)
82 }
83}
84
85impl<T> CubicKeyframeCurve<T> {
86 #[inline]
98 pub fn new(
99 times: impl IntoIterator<Item = f32>,
100 values: impl IntoIterator<Item = T>,
101 ) -> Result<Self, ChunkedUnevenCoreError> {
102 Ok(Self {
103 core: ChunkedUnevenCore::new(times, values, 3)?,
104 })
105 }
106}
107
108#[derive(Debug, Clone, Reflect)]
114#[reflect(Clone)]
115pub struct CubicRotationCurve {
116 core: ChunkedUnevenCore<Vec4>,
118}
119
120impl Curve<Quat> for CubicRotationCurve {
121 #[inline]
122 fn domain(&self) -> Interval {
123 self.core.domain()
124 }
125
126 #[inline]
127 fn sample_clamped(&self, t: f32) -> Quat {
128 let vec = match self.core.sample_interp_timed(t) {
129 InterpolationDatum::Exact((_, v))
131 | InterpolationDatum::LeftTail((_, v))
132 | InterpolationDatum::RightTail((_, v)) => v[1],
133
134 InterpolationDatum::Between((t0, u), (t1, v), s) => {
135 cubic_spline_interpolation(u[1], u[2], v[0], v[1], s, t1 - t0)
136 }
137 };
138 Quat::from_vec4(vec.normalize())
139 }
140
141 #[inline]
142 fn sample_unchecked(&self, t: f32) -> Quat {
143 self.sample_clamped(t)
144 }
145}
146
147impl CubicRotationCurve {
148 pub fn new(
163 times: impl IntoIterator<Item = f32>,
164 values: impl IntoIterator<Item = Vec4>,
165 ) -> Result<Self, ChunkedUnevenCoreError> {
166 Ok(Self {
167 core: ChunkedUnevenCore::new(times, values, 3)?,
168 })
169 }
170}
171
172#[derive(Debug, Clone, Reflect)]
175pub struct WideLinearKeyframeCurve<T> {
176 core: ChunkedUnevenCore<T>,
178}
179
180impl<T> IterableCurve<T> for WideLinearKeyframeCurve<T>
181where
182 T: VectorSpace<Scalar = f32>,
183{
184 #[inline]
185 fn domain(&self) -> Interval {
186 self.core.domain()
187 }
188
189 #[inline]
190 fn sample_iter_clamped(&self, t: f32) -> impl Iterator<Item = T> {
191 match self.core.sample_interp(t) {
192 InterpolationDatum::Exact(v)
193 | InterpolationDatum::LeftTail(v)
194 | InterpolationDatum::RightTail(v) => Either::Left(v.iter().copied()),
195
196 InterpolationDatum::Between(u, v, s) => {
197 let interpolated = u.iter().zip(v.iter()).map(move |(x, y)| x.lerp(*y, s));
198 Either::Right(interpolated)
199 }
200 }
201 }
202
203 #[inline]
204 fn sample_iter_unchecked(&self, t: f32) -> impl Iterator<Item = T> {
205 self.sample_iter_clamped(t)
206 }
207}
208
209impl<T> WideLinearKeyframeCurve<T> {
210 #[inline]
216 pub fn new(
217 times: impl IntoIterator<Item = f32>,
218 values: impl IntoIterator<Item = T>,
219 ) -> Result<Self, WideKeyframeCurveError> {
220 Ok(Self {
221 core: ChunkedUnevenCore::new_width_inferred(times, values)?,
222 })
223 }
224}
225
226#[derive(Debug, Clone, Reflect)]
229pub struct WideSteppedKeyframeCurve<T> {
230 core: ChunkedUnevenCore<T>,
232}
233
234impl<T> IterableCurve<T> for WideSteppedKeyframeCurve<T>
235where
236 T: Clone,
237{
238 #[inline]
239 fn domain(&self) -> Interval {
240 self.core.domain()
241 }
242
243 #[inline]
244 fn sample_iter_clamped(&self, t: f32) -> impl Iterator<Item = T> {
245 match self.core.sample_interp(t) {
246 InterpolationDatum::Exact(v)
247 | InterpolationDatum::LeftTail(v)
248 | InterpolationDatum::RightTail(v) => Either::Left(v.iter().cloned()),
249
250 InterpolationDatum::Between(u, v, s) => {
251 let interpolated =
252 u.iter()
253 .zip(v.iter())
254 .map(move |(x, y)| if s >= 1.0 { y.clone() } else { x.clone() });
255 Either::Right(interpolated)
256 }
257 }
258 }
259
260 #[inline]
261 fn sample_iter_unchecked(&self, t: f32) -> impl Iterator<Item = T> {
262 self.sample_iter_clamped(t)
263 }
264}
265
266impl<T> WideSteppedKeyframeCurve<T> {
267 #[inline]
273 pub fn new(
274 times: impl IntoIterator<Item = f32>,
275 values: impl IntoIterator<Item = T>,
276 ) -> Result<Self, WideKeyframeCurveError> {
277 Ok(Self {
278 core: ChunkedUnevenCore::new_width_inferred(times, values)?,
279 })
280 }
281}
282
283#[derive(Debug, Clone, Reflect)]
286pub struct WideCubicKeyframeCurve<T> {
287 core: ChunkedUnevenCore<T>,
288}
289
290impl<T> IterableCurve<T> for WideCubicKeyframeCurve<T>
291where
292 T: VectorSpace<Scalar = f32>,
293{
294 #[inline]
295 fn domain(&self) -> Interval {
296 self.core.domain()
297 }
298
299 fn sample_iter_clamped(&self, t: f32) -> impl Iterator<Item = T> {
300 match self.core.sample_interp_timed(t) {
301 InterpolationDatum::Exact((_, v))
302 | InterpolationDatum::LeftTail((_, v))
303 | InterpolationDatum::RightTail((_, v)) => {
304 let width = self.core.width();
307 Either::Left(v[width..(width * 2)].iter().copied())
308 }
309
310 InterpolationDatum::Between((t0, u), (t1, v), s) => Either::Right(
311 cubic_spline_interpolate_slices(self.core.width() / 3, u, v, s, t1 - t0),
312 ),
313 }
314 }
315
316 #[inline]
317 fn sample_iter_unchecked(&self, t: f32) -> impl Iterator<Item = T> {
318 self.sample_iter_clamped(t)
319 }
320}
321
322#[derive(Debug, Error)]
324#[error("unable to construct a curve using this data")]
325pub enum WideKeyframeCurveError {
326 #[error("number of values ({values_given}) is not divisible by {divisor}")]
328 LengthMismatch {
329 values_given: usize,
331 divisor: usize,
333 },
334 #[error(transparent)]
336 CoreError(#[from] ChunkedUnevenCoreError),
337}
338
339impl<T> WideCubicKeyframeCurve<T> {
340 #[inline]
348 pub fn new(
349 times: impl IntoIterator<Item = f32>,
350 values: impl IntoIterator<Item = T>,
351 ) -> Result<Self, WideKeyframeCurveError> {
352 let times: Vec<f32> = times.into_iter().collect();
353 let values: Vec<T> = values.into_iter().collect();
354 let divisor = times.len() * 3;
355
356 if !values.len().is_multiple_of(divisor) {
357 return Err(WideKeyframeCurveError::LengthMismatch {
358 values_given: values.len(),
359 divisor,
360 });
361 }
362
363 Ok(Self {
364 core: ChunkedUnevenCore::new_width_inferred(times, values)?,
365 })
366 }
367}
368
369#[derive(Debug, Clone, Reflect)]
378#[reflect(Clone)]
379pub enum WeightsCurve {
380 Constant(ConstantCurve<Vec<f32>>),
383
384 Linear(WideLinearKeyframeCurve<f32>),
386
387 Step(WideSteppedKeyframeCurve<f32>),
389
390 CubicSpline(WideCubicKeyframeCurve<f32>),
393}
394
395fn cubic_spline_interpolation<T>(
401 value_start: T,
402 tangent_out_start: T,
403 tangent_in_end: T,
404 value_end: T,
405 lerp: f32,
406 step_duration: f32,
407) -> T
408where
409 T: VectorSpace<Scalar = f32>,
410{
411 let coeffs = (vec4(2.0, 1.0, -2.0, 1.0) * lerp + vec4(-3.0, -2.0, 3.0, -1.0)) * lerp;
412 value_start * (coeffs.x * lerp + 1.0)
413 + tangent_out_start * step_duration * lerp * (coeffs.y + 1.0)
414 + value_end * lerp * coeffs.z
415 + tangent_in_end * step_duration * lerp * coeffs.w
416}
417
418fn cubic_spline_interpolate_slices<'a, T: VectorSpace<Scalar = f32>>(
419 width: usize,
420 first: &'a [T],
421 second: &'a [T],
422 s: f32,
423 step_between: f32,
424) -> impl Iterator<Item = T> + 'a {
425 (0..width).map(move |idx| {
426 cubic_spline_interpolation(
427 first[idx + width],
428 first[idx + (width * 2)],
429 second[idx + width],
430 second[idx],
431 s,
432 step_between,
433 )
434 })
435}