#[derive(Clone, Debug)]
pub struct SnapBackAnimation {
pub separator_idx: usize,
pub done: bool,
elapsed: f64,
initial_offset: f64,
spring: crate::animation::Spring,
}
impl SnapBackAnimation {
pub fn new(separator_idx: usize, initial_offset: f32) -> Self {
let done = initial_offset.abs() < 0.5;
let spring = crate::animation::Spring::new()
.stiffness(300.0)
.damping(20.0)
.mass(1.0)
.initial_velocity(0.0);
Self {
separator_idx,
done,
elapsed: 0.0,
initial_offset: initial_offset as f64,
spring,
}
}
pub fn update(&mut self, dt: f32) {
if self.done {
return;
}
self.elapsed += dt as f64;
let (position, velocity) = self.spring.evaluate(self.elapsed);
let pixel_offset = position * self.initial_offset;
if pixel_offset.abs() < 0.5 && velocity.abs() < 0.5 {
self.done = true;
}
}
pub fn offset(&self) -> f32 {
if self.done {
return 0.0;
}
let (position, _velocity) = self.spring.evaluate(self.elapsed);
(position * self.initial_offset) as f32
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_snap_back_creation() {
let anim = SnapBackAnimation::new(0, 50.0);
assert_eq!(anim.separator_idx, 0);
assert!(!anim.done);
let offset = anim.offset();
assert!((offset - 50.0).abs() < 1.0);
}
#[test]
fn test_snap_back_convergence() {
let mut anim = SnapBackAnimation::new(0, 100.0);
let dt = 1.0 / 60.0;
for _ in 0..120 {
anim.update(dt);
if anim.done {
break;
}
}
assert!(anim.done);
assert_eq!(anim.offset(), 0.0);
}
#[test]
fn test_snap_back_decreases() {
let mut anim = SnapBackAnimation::new(0, 100.0);
let initial_offset = anim.offset().abs();
let dt = 1.0 / 60.0;
for _ in 0..10 {
anim.update(dt);
}
let current_offset = anim.offset().abs();
assert!(current_offset < initial_offset);
}
#[test]
fn test_snap_back_zero_offset() {
let mut anim = SnapBackAnimation::new(0, 0.0);
anim.update(1.0 / 60.0);
for _ in 0..5 {
if anim.done {
break;
}
anim.update(1.0 / 60.0);
}
assert!(anim.done);
assert_eq!(anim.offset(), 0.0);
}
#[test]
fn test_snap_back_done_stays_zero() {
let mut anim = SnapBackAnimation::new(0, 50.0);
let dt = 1.0 / 60.0;
for _ in 0..200 {
anim.update(dt);
if anim.done {
break;
}
}
assert!(anim.done);
for _ in 0..10 {
anim.update(dt);
assert_eq!(anim.offset(), 0.0);
}
}
}