1use crate::{Duration, MotionValue, PhysicsError};
2
3#[derive(Clone, Copy, Debug, PartialEq)]
4pub struct DecayConfig {
5 pub rate: f64,
6 pub rest_speed: f64,
7}
8
9impl Default for DecayConfig {
10 fn default() -> Self {
11 Self {
12 rate: 5.0,
13 rest_speed: 0.01,
14 }
15 }
16}
17
18impl DecayConfig {
19 pub fn validate(self) -> Result<Self, PhysicsError> {
20 if !self.rate.is_finite() || self.rate <= 0.0 {
21 Err(PhysicsError::InvalidDecay)
22 } else if !self.rest_speed.is_finite() || self.rest_speed < 0.0 {
23 Err(PhysicsError::InvalidRestThreshold)
24 } else {
25 Ok(self)
26 }
27 }
28}
29
30#[derive(Clone, Copy, Debug, PartialEq)]
31pub struct Decay<T> {
32 value: T,
33 velocity: T,
34 config: DecayConfig,
35 active: bool,
36}
37
38impl<T: MotionValue> Decay<T> {
39 pub fn new(value: T, velocity: T, config: DecayConfig) -> Result<Self, PhysicsError> {
40 let config = config.validate()?;
41 let active = velocity.magnitude() > config.rest_speed;
42 Ok(Self {
43 value,
44 velocity,
45 config,
46 active,
47 })
48 }
49
50 #[must_use]
51 pub const fn value(&self) -> T {
52 self.value
53 }
54
55 #[must_use]
56 pub const fn velocity(&self) -> T {
57 self.velocity
58 }
59
60 #[must_use]
61 pub const fn is_active(&self) -> bool {
62 self.active
63 }
64
65 pub fn kick(&mut self, velocity: T) {
66 self.velocity = velocity;
67 self.active = velocity.magnitude() > self.config.rest_speed;
68 }
69
70 pub fn advance(&mut self, elapsed: Duration) -> bool {
71 if !self.active || elapsed == Duration::ZERO {
72 return false;
73 }
74 let decay = (-self.config.rate * elapsed.as_secs_f64()).exp();
75 let distance = self.velocity.scale((1.0 - decay) / self.config.rate);
76 self.value = self.value.add(distance);
77 self.velocity = self.velocity.scale(decay);
78 if self.velocity.magnitude() <= self.config.rest_speed {
79 self.velocity = T::zero();
80 self.active = false;
81 }
82 true
83 }
84}