#![allow(dead_code)]
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct FollowThroughMorph {
pub target: f32,
pub current: f32,
pub velocity: f32,
pub follow_factor: f32,
}
#[allow(dead_code)]
pub fn new_follow_through_morph(follow_factor: f32) -> FollowThroughMorph {
FollowThroughMorph { target: 0.0, current: 0.0, velocity: 0.0, follow_factor }
}
#[allow(dead_code)]
pub fn ft_set_target(m: &mut FollowThroughMorph, target: f32) {
let dir = if target > m.current { 1.0 } else { -1.0 };
m.target = target;
m.velocity = dir * m.follow_factor;
}
#[allow(dead_code)]
pub fn ft_step(m: &mut FollowThroughMorph, dt: f32) {
let stiffness: f32 = 8.0;
let damping = 2.0 * stiffness.sqrt();
let force = stiffness * (m.target - m.current) - damping * m.velocity;
m.velocity += force * dt;
m.current += m.velocity * dt;
}
#[allow(dead_code)]
pub fn ft_value(m: &FollowThroughMorph) -> f32 {
m.current
}
#[allow(dead_code)]
pub fn ft_follow_factor(m: &FollowThroughMorph) -> f32 {
m.follow_factor
}
#[allow(dead_code)]
pub fn ft_reset(m: &mut FollowThroughMorph) {
m.current = 0.0;
m.velocity = 0.0;
m.target = 0.0;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_moves_past_target_initially() {
let mut m = new_follow_through_morph(2.0);
ft_set_target(&mut m, 1.0);
ft_step(&mut m, 0.1);
assert!(ft_value(&m) > 0.0);
}
#[test]
fn test_follow_factor_getter() {
let m = new_follow_through_morph(0.6);
assert!((ft_follow_factor(&m) - 0.6).abs() < 1e-6);
}
#[test]
fn test_reset_clears_state() {
let mut m = new_follow_through_morph(1.0);
ft_set_target(&mut m, 1.0);
ft_step(&mut m, 0.2);
ft_reset(&mut m);
assert_eq!(ft_value(&m), 0.0);
assert_eq!(m.velocity, 0.0);
assert_eq!(m.target, 0.0);
}
#[test]
fn test_step_no_crash() {
let mut m = new_follow_through_morph(0.5);
ft_step(&mut m, 0.016);
assert!(ft_value(&m).is_finite());
}
#[test]
fn test_initial_velocity_nonzero_after_set_target() {
let mut m = new_follow_through_morph(1.5);
ft_set_target(&mut m, 1.0);
assert!(m.velocity.abs() > 0.0);
}
#[test]
fn test_converges_over_time() {
let mut m = new_follow_through_morph(0.1);
ft_set_target(&mut m, 1.0);
for _ in 0..200 {
ft_step(&mut m, 0.05);
}
assert!((ft_value(&m) - 1.0).abs() < 0.1);
}
#[test]
fn test_new_initial_state() {
let m = new_follow_through_morph(1.0);
assert_eq!(m.current, 0.0);
assert_eq!(m.velocity, 0.0);
}
#[test]
fn test_negative_target() {
let mut m = new_follow_through_morph(1.0);
ft_set_target(&mut m, -1.0);
assert!(m.velocity < 0.0);
}
}