Skip to main content

animate_core/spring/
mod.rs

1mod alternate;
2mod cycle;
3mod once;
4mod distance;
5mod settled;
6
7pub use distance::Distance;
8pub use settled::Settled;
9
10use crate::{Animate, Mode, Once};
11use std::marker::PhantomData;
12
13pub struct Spring<T, I, M = Once>
14where
15    M: Mode,
16    T: SpringAnim,
17    I: Fn(&T, &T, &T::Velocity, SpringParams, f64) -> (T, T::Velocity),
18{
19    pub(crate) state: SpringState<T, I>,
20    _mode: PhantomData<M>,
21    pub(crate) advancing: bool,
22}
23
24impl<T, I, M> Spring<T, I, M>
25where
26    M: Mode,
27    T: SpringAnim,
28    I: Fn(&T, &T, &T::Velocity, SpringParams, f64) -> (T, T::Velocity),
29{
30    pub fn new(initial: T, params: SpringParams, interp: I) -> Self {
31        Self {
32            state: SpringState::new(initial, params, interp),
33            _mode: PhantomData,
34            advancing: true,
35        }
36    }
37
38    pub fn velocity(&self) -> f64
39    where
40        T::Velocity: Settled,
41    {
42        self.state.velocity.magnitude()
43    }
44}
45
46
47pub trait SpringAnim: Sized + Default {
48    fn spring(
49        current: &Self,
50        target: &Self,
51        velocity: &Self::Velocity,
52        params: SpringParams,
53        delta: f64,
54    ) -> (Self, Self::Velocity);
55
56    type Velocity: Default + std::fmt::Debug;
57}
58
59#[derive(Clone, Copy, Debug)]
60pub struct SpringParams {
61    pub stiffness: f32,
62    pub damping: f32,
63    pub mass: f32,
64    pub epsilon: f32,
65}
66
67impl Default for SpringParams {
68    fn default() -> Self {
69        Self {
70            stiffness: 100.0,
71            damping: 10.0,
72            mass: 1.0,
73            epsilon: 0.001,
74        }
75    }
76}
77
78impl SpringParams {
79    pub fn new(stiffness: f32, damping: f32, mass: f32) -> Self {
80        Self {
81            stiffness,
82            damping,
83            mass,
84            ..Default::default()
85        }
86    }
87}
88
89#[derive(Debug)]
90pub(crate) struct SpringState<T: SpringAnim, I>
91where
92    I: Fn(&T, &T, &T::Velocity, SpringParams, f64) -> (T, T::Velocity),
93{
94    pub current: T,
95    pub origin: T,
96    pub target: T,
97    pub velocity: T::Velocity,
98    pub active: bool,
99    pub pending: bool,
100    pub params: SpringParams,
101    pub interp: I,
102}
103
104impl<T, I> SpringState<T, I>
105where
106    T: SpringAnim,
107    I: Fn(&T, &T, &T::Velocity, SpringParams, f64) -> (T, T::Velocity),
108{
109    pub fn new(initial: T, params: SpringParams, interp: I) -> Self {
110        Self {
111            current: initial,
112            origin: T::default(),
113            target: T::default(),
114            velocity: T::Velocity::default(),
115            active: false,
116            pending: false,
117            params,
118            interp,
119        }
120    }
121}
122
123impl<T, I, M> std::ops::Deref for Spring<T, I, M>
124where
125    M: Mode,
126    T: SpringAnim,
127    I: Fn(&T, &T, &T::Velocity, SpringParams, f64) -> (T, T::Velocity),
128    Self: Animate<Value = T>,
129{
130    type Target = T;
131    fn deref(&self) -> &T {
132        Animate::get(self)
133    }
134}
135
136impl<T, I, M> std::fmt::Display for Spring<T, I, M>
137where
138    M: Mode,
139    T: SpringAnim + std::fmt::Display,
140    I: Fn(&T, &T, &T::Velocity, SpringParams, f64) -> (T, T::Velocity),
141    Self: Animate<Value = T>,
142{
143    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
144        Animate::get(self).fmt(f)
145    }
146}
147
148impl<T, I, M> std::ops::AddAssign<T> for Spring<T, I, M>
149where
150    M: Mode,
151    T: SpringAnim,
152    I: Fn(&T, &T, &T::Velocity, SpringParams, f64) -> (T, T::Velocity),
153    for<'b> &'b T: std::ops::Add<T, Output = T>,
154    Self: Animate<Value = T>,
155{
156    fn add_assign(&mut self, rhs: T) {
157        let v = Animate::target(self) + rhs;
158        Animate::set(self, v);
159    }
160}
161
162impl<T, I, M> std::ops::SubAssign<T> for Spring<T, I, M>
163where
164    M: Mode,
165    T: SpringAnim,
166    I: Fn(&T, &T, &T::Velocity, SpringParams, f64) -> (T, T::Velocity),
167    for<'b> &'b T: std::ops::Sub<T, Output = T>,
168    Self: Animate<Value = T>,
169{
170    fn sub_assign(&mut self, rhs: T) {
171        let v = Animate::target(self) - rhs;
172        Animate::set(self, v);
173    }
174}
175
176impl<T, I, M> std::ops::MulAssign<T> for Spring<T, I, M>
177where
178    M: Mode,
179    T: SpringAnim,
180    I: Fn(&T, &T, &T::Velocity, SpringParams, f64) -> (T, T::Velocity),
181    for<'b> &'b T: std::ops::Mul<T, Output = T>,
182    Self: Animate<Value = T>,
183{
184    fn mul_assign(&mut self, rhs: T) {
185        let v = Animate::target(self) * rhs;
186        Animate::set(self, v);
187    }
188}
189
190impl<T, I, M> std::ops::DivAssign<T> for Spring<T, I, M>
191where
192    M: Mode,
193    T: SpringAnim,
194    I: Fn(&T, &T, &T::Velocity, SpringParams, f64) -> (T, T::Velocity),
195    for<'b> &'b T: std::ops::Div<T, Output = T>,
196    Self: Animate<Value = T>,
197{
198    fn div_assign(&mut self, rhs: T) {
199        let v = Animate::target(self) / rhs;
200        Animate::set(self, v);
201    }
202}
203
204#[inline]
205fn has_settled<V: Settled>(delta: f64, velocity: &V, epsilon: f32) -> bool {
206    delta.abs() < epsilon as f64 && velocity.is_within_epsilon(epsilon)
207}