use std::time::Duration;
use crate::Animation;
use super::spring::Spring;
pub struct SpringBridge {
spring: Spring,
duration: Duration,
last_delta: f32,
elapsed: f32,
}
impl SpringBridge {
pub fn new(spring: Spring, duration: Duration) -> Self {
Self {
spring,
duration,
last_delta: 0.0,
elapsed: 0.0,
}
}
pub fn from_animation(spring: Spring, animation: &Animation) -> Self {
Self::new(spring, animation.duration)
}
pub fn advance(&mut self, delta: f32) -> bool {
let delta = delta.clamp(0.0, 1.0);
let dt = (delta - self.last_delta) * self.duration.as_secs_f32();
self.last_delta = delta;
self.elapsed += dt;
self.spring.tick(dt)
}
pub fn spring(&mut self) -> &mut Spring {
&mut self.spring
}
pub fn elapsed(&self) -> f32 {
self.elapsed
}
pub fn is_at_rest(&self) -> bool {
self.spring.is_at_rest()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::animation::spring::Spring;
use crate::linear;
#[test]
fn test_bridge_spring_reaches_target_with_animation() {
let duration = Duration::from_millis(500);
let animation = Animation::new(duration).with_easing(linear);
let mut bridge =
SpringBridge::from_animation(Spring::gentle().with_target(100.0), &animation);
let frame_dt = 1.0 / 60.0;
let mut elapsed = 0.0;
while elapsed < duration.as_secs_f32() {
let delta = elapsed / duration.as_secs_f32();
bridge.advance(delta);
elapsed += frame_dt;
}
bridge.advance(1.0);
assert!((bridge.elapsed() - duration.as_secs_f32()).abs() < 1e-3);
let mut frames = 0;
while !bridge.is_at_rest() && frames < 500 {
bridge.spring().tick(frame_dt);
frames += 1;
}
assert!(
bridge.is_at_rest(),
"spring not at rest after {frames} frames"
);
assert!((bridge.spring().position - 100.0).abs() < 0.01);
}
#[test]
fn test_bridge_delta_maps_to_dt() {
let duration = Duration::from_millis(400);
let mut bridge = SpringBridge::new(Spring::gentle().with_target(10.0), duration);
bridge.advance(0.0);
assert_eq!(bridge.elapsed(), 0.0);
bridge.advance(0.5);
assert!((bridge.elapsed() - 0.2).abs() < 1e-6);
bridge.advance(1.0);
assert!((bridge.elapsed() - 0.4).abs() < 1e-6);
}
}