Skip to main content

argui_animation/
decay.rs

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}