Skip to main content

animato_spring/
config.rs

1//! [`SpringConfig`] — parameters that control spring behaviour.
2
3/// Configuration for a damped harmonic oscillator spring.
4///
5/// Use one of the named presets for common feels, or tune the parameters directly.
6///
7/// # Presets
8///
9/// | Preset | Stiffness | Damping | Feel |
10/// |--------|-----------|---------|------|
11/// | `gentle()` | 60 | 14 | Slow, soft |
12/// | `wobbly()` | 180 | 12 | Bouncy, playful |
13/// | `stiff()` | 210 | 20 | Fast, firm |
14/// | `slow()` | 37 | 14 | Very slow, lazy |
15/// | `snappy()` | 300 | 30 | Near-instant |
16///
17/// # Example
18///
19/// ```rust
20/// use animato_spring::SpringConfig;
21///
22/// let cfg = SpringConfig::wobbly();
23/// assert_eq!(cfg.stiffness, 180.0);
24/// ```
25#[derive(Clone, Debug)]
26#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
27pub struct SpringConfig {
28    /// Restoring force. Higher = snappier. Default: `100.0`.
29    pub stiffness: f32,
30    /// Resistance to motion. Higher = less bouncy. Default: `10.0`.
31    pub damping: f32,
32    /// Mass of the simulated object. Default: `1.0`.
33    pub mass: f32,
34    /// Settle threshold — spring is considered at rest when both
35    /// `|position - target| < epsilon` and `|velocity| < epsilon`. Default: `0.001`.
36    pub epsilon: f32,
37}
38
39impl Default for SpringConfig {
40    fn default() -> Self {
41        Self {
42            stiffness: 100.0,
43            damping: 10.0,
44            mass: 1.0,
45            epsilon: 0.001,
46        }
47    }
48}
49
50impl SpringConfig {
51    /// Create a critically damped spring for the given stiffness.
52    ///
53    /// Critical damping uses `damping = 2 * sqrt(stiffness * mass)` with
54    /// `mass = 1.0`, producing fast movement without intentional overshoot.
55    pub fn critically_damped(stiffness: f32) -> Self {
56        let stiffness = stiffness.max(0.0);
57        let mass = 1.0;
58        Self {
59            stiffness,
60            damping: 2.0 * animato_core::math::sqrt(stiffness * mass),
61            mass,
62            epsilon: 0.001,
63        }
64    }
65
66    /// Create an overdamped spring with damping ratio above `1.0`.
67    ///
68    /// Ratios at or below `1.0` are clamped to a small overdamped margin.
69    pub fn overdamped(stiffness: f32, ratio: f32) -> Self {
70        let mut config = Self::critically_damped(stiffness);
71        config.damping *= ratio.max(1.0001);
72        config
73    }
74
75    /// Create an underdamped spring with damping ratio below `1.0`.
76    ///
77    /// Ratios are clamped to `[0.0, 1.0]`; lower values produce more bounce.
78    pub fn underdamped(stiffness: f32, ratio: f32) -> Self {
79        let mut config = Self::critically_damped(stiffness);
80        config.damping *= ratio.clamp(0.0, 1.0);
81        config
82    }
83
84    /// Slow, soft spring — good for subtle UI elements.
85    pub fn gentle() -> Self {
86        Self {
87            stiffness: 60.0,
88            damping: 14.0,
89            mass: 1.0,
90            epsilon: 0.001,
91        }
92    }
93
94    /// Bouncy, playful spring — great for icons and interactive elements.
95    pub fn wobbly() -> Self {
96        Self {
97            stiffness: 180.0,
98            damping: 12.0,
99            mass: 1.0,
100            epsilon: 0.001,
101        }
102    }
103
104    /// Fast, firm spring — good for panels and drawers.
105    pub fn stiff() -> Self {
106        Self {
107            stiffness: 210.0,
108            damping: 20.0,
109            mass: 1.0,
110            epsilon: 0.001,
111        }
112    }
113
114    /// Very slow, lazy spring — good for background animations.
115    pub fn slow() -> Self {
116        Self {
117            stiffness: 37.0,
118            damping: 14.0,
119            mass: 1.0,
120            epsilon: 0.001,
121        }
122    }
123
124    /// Near-instant response — for time-critical feedback.
125    pub fn snappy() -> Self {
126        Self {
127            stiffness: 300.0,
128            damping: 30.0,
129            mass: 1.0,
130            epsilon: 0.001,
131        }
132    }
133}