use std::time::Duration;
use crate::animated::DEFAULT_DURATION;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Motion {
pub damping: f32,
pub response: Duration,
}
impl Motion {
pub const SMOOTH: Self = Self {
damping: 1.0,
response: DEFAULT_DURATION,
};
pub const SNAPPY: Self = Self {
damping: 0.85,
response: DEFAULT_DURATION,
};
pub const BOUNCY: Self = Self {
damping: 0.7,
response: DEFAULT_DURATION,
};
pub const INSTANT: Self = Self {
damping: 1.0,
response: Duration::ZERO,
};
pub fn with_duration(mut self, duration: Duration) -> Self {
self.response = duration;
self
}
pub fn with_damping(mut self, damping: f32) -> Self {
self.damping = damping;
self
}
pub fn very_quick(self) -> Self {
self.with_duration(Duration::from_millis(100))
}
pub fn quick(self) -> Self {
self.with_duration(Duration::from_millis(200))
}
pub fn slow(self) -> Self {
self.with_duration(Duration::from_millis(400))
}
pub fn very_slow(self) -> Self {
self.with_duration(Duration::from_millis(500))
}
pub fn duration(&self) -> Duration {
self.response
}
pub fn damping(&self) -> f32 {
self.damping
}
pub fn applied_stiffness(&self) -> f32 {
let duration_fraction = self.duration().as_secs_f32();
39.478_416 / duration_fraction.powi(2)
}
pub fn applied_damping(&self) -> f32 {
let duration = self.duration().as_secs_f32();
self.damping() * 12.566_371 / duration
}
}
impl Default for Motion {
fn default() -> Self {
Self::SMOOTH
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_is_smooth() {
assert_eq!(Motion::default(), Motion::SMOOTH);
}
#[test]
fn with_duration() {
let motion = Motion::SMOOTH.with_duration(Duration::from_millis(300));
assert_eq!(
motion,
Motion {
response: Duration::from_millis(300),
damping: Motion::SMOOTH.damping()
}
);
}
#[test]
fn with_damping() {
let motion = Motion::SMOOTH.with_damping(0.5);
assert_eq!(
motion,
Motion {
response: Motion::SMOOTH.duration(),
damping: 0.5
}
);
}
#[test]
fn test_spring_motion_duration() {
assert_eq!(Motion::BOUNCY.duration(), DEFAULT_DURATION);
assert_eq!(Motion::SMOOTH.duration(), DEFAULT_DURATION);
assert_eq!(Motion::SNAPPY.duration(), DEFAULT_DURATION);
assert_eq!(
Motion {
response: Duration::from_millis(300),
damping: 0.5
}
.duration(),
Duration::from_millis(300)
);
}
#[test]
fn test_spring_motion_damping() {
assert_eq!(Motion::SMOOTH.damping(), 1.0);
assert_eq!(Motion::SNAPPY.damping(), 0.85);
assert_eq!(Motion::BOUNCY.damping(), 0.7);
assert_eq!(
Motion {
response: Duration::from_millis(300),
damping: 0.5
}
.damping(),
0.5
);
}
#[test]
fn smooth_applied_forces() {
let motion = Motion {
response: Duration::from_millis(500),
damping: 1.0,
};
assert_eq!(motion.applied_damping().trunc(), 25.0);
assert_eq!(motion.applied_stiffness().trunc(), 157.0);
}
#[test]
fn snappy_applied_forces() {
assert_eq!(Motion::SNAPPY.applied_damping().trunc(), 21.0);
assert_eq!(Motion::SNAPPY.applied_stiffness().trunc(), 157.0);
}
#[test]
fn bouncy_applied_forces() {
assert_eq!(Motion::BOUNCY.applied_damping().trunc(), 17.0);
assert_eq!(Motion::BOUNCY.applied_stiffness().trunc(), 157.0);
}
#[test]
fn custom_applied_forces() {
let motion = Motion {
response: Duration::from_millis(250),
damping: 0.75,
};
assert_eq!(motion.applied_damping().trunc(), 37.0);
assert_eq!(motion.applied_stiffness().trunc(), 631.0);
}
#[test]
fn instant() {
assert_eq!(Motion::INSTANT.duration(), Duration::ZERO);
}
}