1use super::interval::*;
4use super::Curve;
5
6use crate::ops;
7use crate::VectorSpace;
8use core::any::type_name;
9use core::fmt::{self, Debug};
10use core::marker::PhantomData;
11
12#[cfg(feature = "bevy_reflect")]
13use {
14 alloc::format,
15 bevy_reflect::{utility::GenericTypePathCell, FromReflect, Reflect, TypePath},
16};
17
18#[cfg(feature = "bevy_reflect")]
19mod paths {
20 pub(super) const THIS_MODULE: &str = "bevy_math::curve::adaptors";
21 pub(super) const THIS_CRATE: &str = "bevy_math";
22}
23
24#[expect(unused, reason = "imported just for doc links")]
25use super::CurveExt;
26
27#[derive(Clone, Copy, Debug)]
45#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
46#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
47pub struct ConstantCurve<T> {
48 pub(crate) domain: Interval,
49 pub(crate) value: T,
50}
51
52impl<T> ConstantCurve<T>
53where
54 T: Clone,
55{
56 pub fn new(domain: Interval, value: T) -> Self {
59 Self { domain, value }
60 }
61}
62
63impl<T> Curve<T> for ConstantCurve<T>
64where
65 T: Clone,
66{
67 #[inline]
68 fn domain(&self) -> Interval {
69 self.domain
70 }
71
72 #[inline]
73 fn sample_unchecked(&self, _t: f32) -> T {
74 self.value.clone()
75 }
76}
77
78#[derive(Clone)]
83#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
84#[cfg_attr(
85 feature = "bevy_reflect",
86 derive(Reflect),
87 reflect(where T: TypePath),
88 reflect(from_reflect = false, type_path = false),
89)]
90pub struct FunctionCurve<T, F> {
91 pub(crate) domain: Interval,
92 #[cfg_attr(feature = "bevy_reflect", reflect(ignore))]
93 pub(crate) f: F,
94 #[cfg_attr(feature = "bevy_reflect", reflect(ignore, clone))]
95 pub(crate) _phantom: PhantomData<fn() -> T>,
96}
97
98impl<T, F> Debug for FunctionCurve<T, F> {
99 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100 f.debug_struct("FunctionCurve")
101 .field("domain", &self.domain)
102 .field("f", &type_name::<F>())
103 .finish()
104 }
105}
106
107#[cfg(feature = "bevy_reflect")]
110impl<T, F> TypePath for FunctionCurve<T, F>
111where
112 T: TypePath,
113 F: 'static,
114{
115 fn type_path() -> &'static str {
116 static CELL: GenericTypePathCell = GenericTypePathCell::new();
117 CELL.get_or_insert::<Self, _>(|| {
118 format!(
119 "{}::FunctionCurve<{},{}>",
120 paths::THIS_MODULE,
121 T::type_path(),
122 type_name::<F>()
123 )
124 })
125 }
126
127 fn short_type_path() -> &'static str {
128 static CELL: GenericTypePathCell = GenericTypePathCell::new();
129 CELL.get_or_insert::<Self, _>(|| {
130 format!(
131 "FunctionCurve<{},{}>",
132 T::short_type_path(),
133 type_name::<F>()
134 )
135 })
136 }
137
138 fn type_ident() -> Option<&'static str> {
139 Some("FunctionCurve")
140 }
141
142 fn crate_name() -> Option<&'static str> {
143 Some(paths::THIS_CRATE)
144 }
145
146 fn module_path() -> Option<&'static str> {
147 Some(paths::THIS_MODULE)
148 }
149}
150
151impl<T, F> FunctionCurve<T, F>
152where
153 F: Fn(f32) -> T,
154{
155 pub fn new(domain: Interval, function: F) -> Self {
158 FunctionCurve {
159 domain,
160 f: function,
161 _phantom: PhantomData,
162 }
163 }
164}
165
166impl<T, F> Curve<T> for FunctionCurve<T, F>
167where
168 F: Fn(f32) -> T,
169{
170 #[inline]
171 fn domain(&self) -> Interval {
172 self.domain
173 }
174
175 #[inline]
176 fn sample_unchecked(&self, t: f32) -> T {
177 (self.f)(t)
178 }
179}
180
181#[derive(Clone)]
184#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
185#[cfg_attr(
186 feature = "bevy_reflect",
187 derive(Reflect),
188 reflect(where S: TypePath, T: TypePath, C: TypePath),
189 reflect(from_reflect = false, type_path = false),
190)]
191pub struct MapCurve<S, T, C, F> {
192 pub(crate) preimage: C,
193 #[cfg_attr(feature = "bevy_reflect", reflect(ignore))]
194 pub(crate) f: F,
195 #[cfg_attr(feature = "bevy_reflect", reflect(ignore, clone))]
196 pub(crate) _phantom: PhantomData<(fn() -> S, fn(S) -> T)>,
197}
198
199impl<S, T, C, F> Debug for MapCurve<S, T, C, F>
200where
201 C: Debug,
202{
203 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
204 f.debug_struct("MapCurve")
205 .field("preimage", &self.preimage)
206 .field("f", &type_name::<F>())
207 .finish()
208 }
209}
210
211#[cfg(feature = "bevy_reflect")]
214impl<S, T, C, F> TypePath for MapCurve<S, T, C, F>
215where
216 S: TypePath,
217 T: TypePath,
218 C: TypePath,
219 F: 'static,
220{
221 fn type_path() -> &'static str {
222 static CELL: GenericTypePathCell = GenericTypePathCell::new();
223 CELL.get_or_insert::<Self, _>(|| {
224 format!(
225 "{}::MapCurve<{},{},{},{}>",
226 paths::THIS_MODULE,
227 S::type_path(),
228 T::type_path(),
229 C::type_path(),
230 type_name::<F>()
231 )
232 })
233 }
234
235 fn short_type_path() -> &'static str {
236 static CELL: GenericTypePathCell = GenericTypePathCell::new();
237 CELL.get_or_insert::<Self, _>(|| {
238 format!(
239 "MapCurve<{},{},{},{}>",
240 S::type_path(),
241 T::type_path(),
242 C::type_path(),
243 type_name::<F>()
244 )
245 })
246 }
247
248 fn type_ident() -> Option<&'static str> {
249 Some("MapCurve")
250 }
251
252 fn crate_name() -> Option<&'static str> {
253 Some(paths::THIS_CRATE)
254 }
255
256 fn module_path() -> Option<&'static str> {
257 Some(paths::THIS_MODULE)
258 }
259}
260
261impl<S, T, C, F> Curve<T> for MapCurve<S, T, C, F>
262where
263 C: Curve<S>,
264 F: Fn(S) -> T,
265{
266 #[inline]
267 fn domain(&self) -> Interval {
268 self.preimage.domain()
269 }
270
271 #[inline]
272 fn sample_unchecked(&self, t: f32) -> T {
273 (self.f)(self.preimage.sample_unchecked(t))
274 }
275}
276
277#[derive(Clone)]
280#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
281#[cfg_attr(
282 feature = "bevy_reflect",
283 derive(Reflect),
284 reflect(where T: TypePath, C: TypePath),
285 reflect(from_reflect = false, type_path = false),
286)]
287pub struct ReparamCurve<T, C, F> {
288 pub(crate) domain: Interval,
289 pub(crate) base: C,
290 #[cfg_attr(feature = "bevy_reflect", reflect(ignore))]
291 pub(crate) f: F,
292 #[cfg_attr(feature = "bevy_reflect", reflect(ignore, clone))]
293 pub(crate) _phantom: PhantomData<fn() -> T>,
294}
295
296impl<T, C, F> Debug for ReparamCurve<T, C, F>
297where
298 C: Debug,
299{
300 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
301 f.debug_struct("ReparamCurve")
302 .field("domain", &self.domain)
303 .field("base", &self.base)
304 .field("f", &type_name::<F>())
305 .finish()
306 }
307}
308
309#[cfg(feature = "bevy_reflect")]
312impl<T, C, F> TypePath for ReparamCurve<T, C, F>
313where
314 T: TypePath,
315 C: TypePath,
316 F: 'static,
317{
318 fn type_path() -> &'static str {
319 static CELL: GenericTypePathCell = GenericTypePathCell::new();
320 CELL.get_or_insert::<Self, _>(|| {
321 format!(
322 "{}::ReparamCurve<{},{},{}>",
323 paths::THIS_MODULE,
324 T::type_path(),
325 C::type_path(),
326 type_name::<F>()
327 )
328 })
329 }
330
331 fn short_type_path() -> &'static str {
332 static CELL: GenericTypePathCell = GenericTypePathCell::new();
333 CELL.get_or_insert::<Self, _>(|| {
334 format!(
335 "ReparamCurve<{},{},{}>",
336 T::type_path(),
337 C::type_path(),
338 type_name::<F>()
339 )
340 })
341 }
342
343 fn type_ident() -> Option<&'static str> {
344 Some("ReparamCurve")
345 }
346
347 fn crate_name() -> Option<&'static str> {
348 Some(paths::THIS_CRATE)
349 }
350
351 fn module_path() -> Option<&'static str> {
352 Some(paths::THIS_MODULE)
353 }
354}
355
356impl<T, C, F> Curve<T> for ReparamCurve<T, C, F>
357where
358 C: Curve<T>,
359 F: Fn(f32) -> f32,
360{
361 #[inline]
362 fn domain(&self) -> Interval {
363 self.domain
364 }
365
366 #[inline]
367 fn sample_unchecked(&self, t: f32) -> T {
368 self.base.sample_unchecked((self.f)(t))
369 }
370}
371
372#[derive(Clone, Debug)]
375#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
376#[cfg_attr(
377 feature = "bevy_reflect",
378 derive(Reflect, FromReflect),
379 reflect(from_reflect = false)
380)]
381pub struct LinearReparamCurve<T, C> {
382 pub(crate) base: C,
384 pub(crate) new_domain: Interval,
386 #[cfg_attr(feature = "bevy_reflect", reflect(ignore, clone))]
387 pub(crate) _phantom: PhantomData<fn() -> T>,
388}
389
390impl<T, C> Curve<T> for LinearReparamCurve<T, C>
391where
392 C: Curve<T>,
393{
394 #[inline]
395 fn domain(&self) -> Interval {
396 self.new_domain
397 }
398
399 #[inline]
400 fn sample_unchecked(&self, t: f32) -> T {
401 let f = self.new_domain.linear_map_to(self.base.domain()).unwrap();
403 self.base.sample_unchecked(f(t))
404 }
405}
406
407#[derive(Clone, Debug)]
410#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
411#[cfg_attr(
412 feature = "bevy_reflect",
413 derive(Reflect, FromReflect),
414 reflect(from_reflect = false)
415)]
416pub struct CurveReparamCurve<T, C, D> {
417 pub(crate) base: C,
418 pub(crate) reparam_curve: D,
419 #[cfg_attr(feature = "bevy_reflect", reflect(ignore, clone))]
420 pub(crate) _phantom: PhantomData<fn() -> T>,
421}
422
423impl<T, C, D> Curve<T> for CurveReparamCurve<T, C, D>
424where
425 C: Curve<T>,
426 D: Curve<f32>,
427{
428 #[inline]
429 fn domain(&self) -> Interval {
430 self.reparam_curve.domain()
431 }
432
433 #[inline]
434 fn sample_unchecked(&self, t: f32) -> T {
435 let sample_time = self.reparam_curve.sample_unchecked(t);
436 self.base.sample_unchecked(sample_time)
437 }
438}
439
440#[derive(Clone, Debug)]
443#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
444#[cfg_attr(
445 feature = "bevy_reflect",
446 derive(Reflect, FromReflect),
447 reflect(from_reflect = false)
448)]
449pub struct GraphCurve<T, C> {
450 pub(crate) base: C,
451 #[cfg_attr(feature = "bevy_reflect", reflect(ignore, clone))]
452 pub(crate) _phantom: PhantomData<fn() -> T>,
453}
454
455impl<T, C> Curve<(f32, T)> for GraphCurve<T, C>
456where
457 C: Curve<T>,
458{
459 #[inline]
460 fn domain(&self) -> Interval {
461 self.base.domain()
462 }
463
464 #[inline]
465 fn sample_unchecked(&self, t: f32) -> (f32, T) {
466 (t, self.base.sample_unchecked(t))
467 }
468}
469
470#[derive(Clone, Debug)]
473#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
474#[cfg_attr(
475 feature = "bevy_reflect",
476 derive(Reflect, FromReflect),
477 reflect(from_reflect = false)
478)]
479pub struct ZipCurve<S, T, C, D> {
480 pub(crate) domain: Interval,
481 pub(crate) first: C,
482 pub(crate) second: D,
483 #[cfg_attr(feature = "bevy_reflect", reflect(ignore, clone))]
484 pub(crate) _phantom: PhantomData<fn() -> (S, T)>,
485}
486
487impl<S, T, C, D> Curve<(S, T)> for ZipCurve<S, T, C, D>
488where
489 C: Curve<S>,
490 D: Curve<T>,
491{
492 #[inline]
493 fn domain(&self) -> Interval {
494 self.domain
495 }
496
497 #[inline]
498 fn sample_unchecked(&self, t: f32) -> (S, T) {
499 (
500 self.first.sample_unchecked(t),
501 self.second.sample_unchecked(t),
502 )
503 }
504}
505
506#[derive(Clone, Debug)]
514#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
515#[cfg_attr(
516 feature = "bevy_reflect",
517 derive(Reflect, FromReflect),
518 reflect(from_reflect = false)
519)]
520pub struct ChainCurve<T, C, D> {
521 pub(crate) first: C,
522 pub(crate) second: D,
523 #[cfg_attr(feature = "bevy_reflect", reflect(ignore, clone))]
524 pub(crate) _phantom: PhantomData<fn() -> T>,
525}
526
527impl<T, C, D> Curve<T> for ChainCurve<T, C, D>
528where
529 C: Curve<T>,
530 D: Curve<T>,
531{
532 #[inline]
533 fn domain(&self) -> Interval {
534 Interval::new(
537 self.first.domain().start(),
538 self.first.domain().end() + self.second.domain().length(),
539 )
540 .unwrap()
541 }
542
543 #[inline]
544 fn sample_unchecked(&self, t: f32) -> T {
545 if t > self.first.domain().end() {
546 self.second.sample_unchecked(
547 t - self.first.domain().end() + self.second.domain().start(),
549 )
550 } else {
551 self.first.sample_unchecked(t)
552 }
553 }
554}
555
556#[derive(Clone, Debug)]
564#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
565#[cfg_attr(
566 feature = "bevy_reflect",
567 derive(Reflect, FromReflect),
568 reflect(from_reflect = false)
569)]
570pub struct ReverseCurve<T, C> {
571 pub(crate) curve: C,
572 #[cfg_attr(feature = "bevy_reflect", reflect(ignore, clone))]
573 pub(crate) _phantom: PhantomData<fn() -> T>,
574}
575
576impl<T, C> Curve<T> for ReverseCurve<T, C>
577where
578 C: Curve<T>,
579{
580 #[inline]
581 fn domain(&self) -> Interval {
582 self.curve.domain()
583 }
584
585 #[inline]
586 fn sample_unchecked(&self, t: f32) -> T {
587 self.curve
588 .sample_unchecked(self.domain().end() - (t - self.domain().start()))
589 }
590}
591
592#[derive(Clone, Debug)]
605#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
606#[cfg_attr(
607 feature = "bevy_reflect",
608 derive(Reflect, FromReflect),
609 reflect(from_reflect = false)
610)]
611pub struct RepeatCurve<T, C> {
612 pub(crate) domain: Interval,
613 pub(crate) curve: C,
614 #[cfg_attr(feature = "bevy_reflect", reflect(ignore, clone))]
615 pub(crate) _phantom: PhantomData<fn() -> T>,
616}
617
618impl<T, C> Curve<T> for RepeatCurve<T, C>
619where
620 C: Curve<T>,
621{
622 #[inline]
623 fn domain(&self) -> Interval {
624 self.domain
625 }
626
627 #[inline]
628 fn sample_unchecked(&self, t: f32) -> T {
629 let t = self.base_curve_sample_time(t);
630 self.curve.sample_unchecked(t)
631 }
632}
633
634impl<T, C> RepeatCurve<T, C>
635where
636 C: Curve<T>,
637{
638 #[inline]
639 pub(crate) fn base_curve_sample_time(&self, t: f32) -> f32 {
640 let d = self.curve.domain();
642 let cyclic_t = ops::rem_euclid(t - d.start(), d.length());
643 if t != d.start() && cyclic_t == 0.0 {
644 d.end()
645 } else {
646 d.start() + cyclic_t
647 }
648 }
649}
650
651#[derive(Clone, Debug)]
664#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
665#[cfg_attr(
666 feature = "bevy_reflect",
667 derive(Reflect, FromReflect),
668 reflect(from_reflect = false)
669)]
670pub struct ForeverCurve<T, C> {
671 pub(crate) curve: C,
672 #[cfg_attr(feature = "bevy_reflect", reflect(ignore, clone))]
673 pub(crate) _phantom: PhantomData<fn() -> T>,
674}
675
676impl<T, C> Curve<T> for ForeverCurve<T, C>
677where
678 C: Curve<T>,
679{
680 #[inline]
681 fn domain(&self) -> Interval {
682 Interval::EVERYWHERE
683 }
684
685 #[inline]
686 fn sample_unchecked(&self, t: f32) -> T {
687 let t = self.base_curve_sample_time(t);
688 self.curve.sample_unchecked(t)
689 }
690}
691
692impl<T, C> ForeverCurve<T, C>
693where
694 C: Curve<T>,
695{
696 #[inline]
697 pub(crate) fn base_curve_sample_time(&self, t: f32) -> f32 {
698 let d = self.curve.domain();
700 let cyclic_t = ops::rem_euclid(t - d.start(), d.length());
701 if t != d.start() && cyclic_t == 0.0 {
702 d.end()
703 } else {
704 d.start() + cyclic_t
705 }
706 }
707}
708
709#[derive(Clone, Debug)]
718#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
719#[cfg_attr(
720 feature = "bevy_reflect",
721 derive(Reflect, FromReflect),
722 reflect(from_reflect = false)
723)]
724pub struct PingPongCurve<T, C> {
725 pub(crate) curve: C,
726 #[cfg_attr(feature = "bevy_reflect", reflect(ignore, clone))]
727 pub(crate) _phantom: PhantomData<fn() -> T>,
728}
729
730impl<T, C> Curve<T> for PingPongCurve<T, C>
731where
732 C: Curve<T>,
733{
734 #[inline]
735 fn domain(&self) -> Interval {
736 Interval::new(
739 self.curve.domain().start(),
740 self.curve.domain().end() + self.curve.domain().length(),
741 )
742 .unwrap()
743 }
744
745 #[inline]
746 fn sample_unchecked(&self, t: f32) -> T {
747 let final_t = if t > self.curve.domain().end() {
749 self.curve.domain().end() * 2.0 - t
750 } else {
751 t
752 };
753 self.curve.sample_unchecked(final_t)
754 }
755}
756
757#[derive(Clone, Debug)]
772#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
773#[cfg_attr(
774 feature = "bevy_reflect",
775 derive(Reflect, FromReflect),
776 reflect(from_reflect = false)
777)]
778pub struct ContinuationCurve<T, C, D> {
779 pub(crate) first: C,
780 pub(crate) second: D,
781 pub(crate) offset: T,
783 #[cfg_attr(feature = "bevy_reflect", reflect(ignore, clone))]
784 pub(crate) _phantom: PhantomData<fn() -> T>,
785}
786
787impl<T, C, D> Curve<T> for ContinuationCurve<T, C, D>
788where
789 T: VectorSpace,
790 C: Curve<T>,
791 D: Curve<T>,
792{
793 #[inline]
794 fn domain(&self) -> Interval {
795 Interval::new(
798 self.first.domain().start(),
799 self.first.domain().end() + self.second.domain().length(),
800 )
801 .unwrap()
802 }
803
804 #[inline]
805 fn sample_unchecked(&self, t: f32) -> T {
806 if t > self.first.domain().end() {
807 self.second.sample_unchecked(
808 t - self.first.domain().end() + self.second.domain().start(),
810 ) + self.offset
811 } else {
812 self.first.sample_unchecked(t)
813 }
814 }
815}