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}