#[derive(Clone, Debug)]
pub struct Spring {
value: f64,
target: f64,
velocity: f64,
stiffness: f64,
damping: f64,
mass: f64,
threshold: f64,
}
impl Spring {
pub fn new(initial: f64, target: f64) -> Self {
Self {
value: initial,
target,
velocity: 0.0,
stiffness: 180.0,
damping: 12.0,
mass: 1.0,
threshold: 0.01,
}
}
pub fn at(value: f64) -> Self {
Self::new(value, value)
}
pub fn stiffness(mut self, stiffness: f64) -> Self {
self.stiffness = stiffness.max(0.1);
self
}
pub fn damping(mut self, damping: f64) -> Self {
self.damping = damping.max(0.1);
self
}
pub fn mass(mut self, mass: f64) -> Self {
self.mass = mass.max(0.01);
self
}
pub fn threshold(mut self, threshold: f64) -> Self {
self.threshold = threshold.max(0.0001);
self
}
pub fn snappy() -> Self {
Self::at(0.0).stiffness(400.0).damping(30.0)
}
pub fn gentle() -> Self {
Self::at(0.0).stiffness(100.0).damping(15.0)
}
pub fn bouncy() -> Self {
Self::at(0.0).stiffness(200.0).damping(8.0)
}
pub fn slow() -> Self {
Self::at(0.0).stiffness(50.0).damping(10.0)
}
pub fn set_target(&mut self, target: f64) {
self.target = target;
}
pub fn set_value(&mut self, value: f64) {
self.value = value;
self.velocity = 0.0;
}
pub fn value(&self) -> f64 {
self.value
}
pub fn target(&self) -> f64 {
self.target
}
pub fn velocity(&self) -> f64 {
self.velocity
}
pub fn is_settled(&self) -> bool {
(self.value - self.target).abs() < self.threshold && self.velocity.abs() < self.threshold
}
pub fn update(&mut self, dt: f64) -> f64 {
if self.is_settled() {
self.value = self.target;
self.velocity = 0.0;
return self.value;
}
let displacement = self.value - self.target;
let spring_force = -self.stiffness * displacement;
let damping_force = -self.damping * self.velocity;
let acceleration = (spring_force + damping_force) / self.mass;
self.velocity += acceleration * dt;
self.value += self.velocity * dt;
self.value
}
pub fn tick(&mut self) -> f64 {
self.update(1.0 / 60.0)
}
}