use std::time::Duration;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct SleipnirParams {
pub stiffness: f32,
pub damping: f32,
pub mass: f32,
}
impl Default for SleipnirParams {
fn default() -> Self {
Self {
stiffness: 170.0,
damping: 26.0,
mass: 1.0,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Animation {
Ginnungagap,
Linear { duration: Duration },
Sleipnir(SleipnirParams),
BifrostFade { duration: Duration },
MjolnirSlice { duration: Duration },
MjolnirShatter { duration: Duration, pieces: u32, force: f32 },
}
impl Animation {
pub fn sleipnir() -> Self {
Animation::Sleipnir(SleipnirParams::default())
}
pub fn bifrost_fade(duration: Duration) -> Self {
Animation::BifrostFade { duration }
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
struct SolverState {
x: f32,
v: f32,
}
pub struct SleipnirSolver {
params: SleipnirParams,
target: f32,
state: SolverState,
}
impl SleipnirSolver {
pub fn new(params: SleipnirParams, target: f32, current: f32) -> Self {
Self {
params,
target,
state: SolverState { x: current, v: 0.0 },
}
}
pub fn tick(&mut self, dt: f32) -> f32 {
let a = self.evaluate(self.state, 0.0, SolverState { x: 0.0, v: 0.0 });
let b = self.evaluate(self.state, dt * 0.5, a);
let c = self.evaluate(self.state, dt * 0.5, b);
let d = self.evaluate(self.state, dt, c);
let dxdt = 1.0 / 6.0 * (a.x + 2.0 * (b.x + c.x) + d.x);
let dvdt = 1.0 / 6.0 * (a.v + 2.0 * (b.v + c.v) + d.v);
self.state.x += dxdt * dt;
self.state.v += dvdt * dt;
self.state.x
}
fn evaluate(&self, initial: SolverState, dt: f32, d: SolverState) -> SolverState {
let state = SolverState {
x: initial.x + d.x * dt,
v: initial.v + d.v * dt,
};
let force = -self.params.stiffness * (state.x - self.target) - self.params.damping * state.v;
let acceleration = force / self.params.mass;
SolverState {
x: state.v,
v: acceleration,
}
}
pub fn is_settled(&self) -> bool {
(self.state.x - self.target).abs() < 0.001 && self.state.v.abs() < 0.001
}
}
pub trait AnimationValue: Sized + Clone + PartialEq {
fn lerp(&self, other: &Self, t: f32) -> Self;
fn distance(&self, other: &Self) -> f32;
}
impl AnimationValue for f32 {
fn lerp(&self, other: &Self, t: f32) -> Self {
self + (other - self) * t
}
fn distance(&self, other: &Self) -> f32 {
(self - other).abs()
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct NiflColor {
pub r: f32,
pub g: f32,
pub b: f32,
pub a: f32,
}
impl AnimationValue for NiflColor {
fn lerp(&self, other: &Self, t: f32) -> Self {
Self {
r: self.r + (other.r - self.r) * t,
g: self.g + (other.g - self.g) * t,
b: self.b + (other.b - self.b) * t,
a: self.a + (other.a - self.a) * t,
}
}
fn distance(&self, other: &Self) -> f32 {
((self.r - other.r).powi(2) + (self.g - other.g).powi(2) + (self.b - other.b).powi(2)).sqrt()
}
}