Skip to main content

ferroui_base/animation/easings/
easing.rs

1use crate::animation::easings::*;
2use crate::animation::KeySpline;
3use crate::utilities::FormatError;
4use std::fmt;
5use std::rc::Rc;
6use std::str::FromStr;
7
8/// An easing function: the shared, type-erased handle that animations and
9/// transitions hold.
10///
11/// Any [`IEasing`] converts into an `Easing` (`LinearEasing::new().into()`).
12/// To keep mutating an easing after handing it out (for example the control
13/// points of a [`SplineEasing`]), create it behind an `Rc` and use
14/// [`from_rc`](Self::from_rc). Handles compare by identity.
15#[derive(Clone)]
16pub struct Easing(Rc<dyn IEasing>);
17
18impl Easing {
19    /// Wraps an easing function.
20    pub fn new(easing: impl IEasing) -> Self {
21        Self(Rc::new(easing))
22    }
23
24    /// Wraps a shared easing function.
25    pub fn from_rc(easing: Rc<dyn IEasing>) -> Self {
26        Self(easing)
27    }
28
29    /// The wrapped easing function.
30    pub fn as_rc(&self) -> &Rc<dyn IEasing> {
31        &self.0
32    }
33
34    /// Returns the transformed progress for a linear progress.
35    #[inline]
36    pub fn ease(&self, progress: f64) -> f64 {
37        self.0.ease(progress)
38    }
39
40    /// Parses an easing: either the four comma-separated control points of a
41    /// spline easing, or the name of one of the easing types of this module.
42    pub fn parse(e: &str) -> Result<Easing, FormatError> {
43        if e.contains(',') {
44            return Ok(Easing::new(SplineEasing::with_key_spline(KeySpline::parse(e)?)));
45        }
46
47        Ok(match e {
48            "BackEaseIn" => Easing::new(BackEaseIn::new()),
49            "BackEaseInOut" => Easing::new(BackEaseInOut::new()),
50            "BackEaseOut" => Easing::new(BackEaseOut::new()),
51            "BounceEaseIn" => Easing::new(BounceEaseIn::new()),
52            "BounceEaseInOut" => Easing::new(BounceEaseInOut::new()),
53            "BounceEaseOut" => Easing::new(BounceEaseOut::new()),
54            "CircularEaseIn" => Easing::new(CircularEaseIn::new()),
55            "CircularEaseInOut" => Easing::new(CircularEaseInOut::new()),
56            "CircularEaseOut" => Easing::new(CircularEaseOut::new()),
57            "CubicEaseIn" => Easing::new(CubicEaseIn::new()),
58            "CubicEaseInOut" => Easing::new(CubicEaseInOut::new()),
59            "CubicEaseOut" => Easing::new(CubicEaseOut::new()),
60            "ElasticEaseIn" => Easing::new(ElasticEaseIn::new()),
61            "ElasticEaseInOut" => Easing::new(ElasticEaseInOut::new()),
62            "ElasticEaseOut" => Easing::new(ElasticEaseOut::new()),
63            "ExponentialEaseIn" => Easing::new(ExponentialEaseIn::new()),
64            "ExponentialEaseInOut" => Easing::new(ExponentialEaseInOut::new()),
65            "ExponentialEaseOut" => Easing::new(ExponentialEaseOut::new()),
66            "LinearEasing" => Easing::new(LinearEasing::new()),
67            "QuadraticEaseIn" => Easing::new(QuadraticEaseIn::new()),
68            "QuadraticEaseInOut" => Easing::new(QuadraticEaseInOut::new()),
69            "QuadraticEaseOut" => Easing::new(QuadraticEaseOut::new()),
70            "QuarticEaseIn" => Easing::new(QuarticEaseIn::new()),
71            "QuarticEaseInOut" => Easing::new(QuarticEaseInOut::new()),
72            "QuarticEaseOut" => Easing::new(QuarticEaseOut::new()),
73            "QuinticEaseIn" => Easing::new(QuinticEaseIn::new()),
74            "QuinticEaseInOut" => Easing::new(QuinticEaseInOut::new()),
75            "QuinticEaseOut" => Easing::new(QuinticEaseOut::new()),
76            "SineEaseIn" => Easing::new(SineEaseIn::new()),
77            "SineEaseInOut" => Easing::new(SineEaseInOut::new()),
78            "SineEaseOut" => Easing::new(SineEaseOut::new()),
79            "SplineEasing" => Easing::new(SplineEasing::new()),
80            "SpringEasing" => Easing::new(SpringEasing::new()),
81            _ => {
82                return Err(FormatError::from_string(format!(
83                    "Easing \"{e}\" was not found in the easings module."
84                )))
85            }
86        })
87    }
88}
89
90impl IEasing for Easing {
91    fn to_shared(&self) -> std::sync::Arc<crate::animation::easings::SharedEasing> {
92        self.0.to_shared()
93    }
94
95    #[inline]
96    fn ease(&self, progress: f64) -> f64 {
97        self.0.ease(progress)
98    }
99
100    #[inline]
101    fn derives_from_easing(&self) -> bool {
102        self.0.derives_from_easing()
103    }
104}
105
106impl Default for Easing {
107    /// The linear easing.
108    fn default() -> Self {
109        Easing::new(LinearEasing::new())
110    }
111}
112
113impl PartialEq for Easing {
114    fn eq(&self, other: &Self) -> bool {
115        std::ptr::addr_eq(Rc::as_ptr(&self.0), Rc::as_ptr(&other.0))
116    }
117}
118
119impl fmt::Debug for Easing {
120    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121        f.write_str("Easing")
122    }
123}
124
125impl FromStr for Easing {
126    type Err = FormatError;
127    fn from_str(s: &str) -> Result<Self, Self::Err> {
128        Easing::parse(s)
129    }
130}
131
132macro_rules! easing_from {
133    ($($name:ident),+ $(,)?) => {
134        $(impl From<$name> for Easing {
135            fn from(value: $name) -> Self {
136                Easing::new(value)
137            }
138        })+
139    };
140}
141
142easing_from!(
143    BackEaseIn, BackEaseInOut, BackEaseOut, BounceEaseIn, BounceEaseInOut, BounceEaseOut, CircularEaseIn, CircularEaseInOut, CircularEaseOut, CubicEaseIn, CubicEaseInOut, CubicEaseOut, ElasticEaseIn, ElasticEaseInOut, ElasticEaseOut, ExponentialEaseIn, ExponentialEaseInOut, ExponentialEaseOut, LinearEasing, QuadraticEaseIn, QuadraticEaseInOut, QuadraticEaseOut, QuarticEaseIn, QuarticEaseInOut, QuarticEaseOut, QuinticEaseIn, QuinticEaseInOut, QuinticEaseOut, SineEaseIn, SineEaseInOut, SineEaseOut, SplineEasing, SpringEasing
144);