Skip to main content

ling_graphics/
animation.rs

1use glam::{Vec2, Vec3, Vec4, Quat};
2use crate::color::Color;
3use crate::scene::Transform;
4
5// ── Lerp trait ────────────────────────────────────────────────────────────────
6
7pub trait Lerp: Clone + Send + Sync + 'static {
8    fn lerp_by(&self, other: &Self, t: f32) -> Self;
9}
10
11impl Lerp for f32  { fn lerp_by(&self, o: &Self, t: f32) -> Self { self + (o - self) * t } }
12impl Lerp for f64  { fn lerp_by(&self, o: &Self, t: f32) -> Self { self + (o - self) * t as f64 } }
13impl Lerp for Vec2 { fn lerp_by(&self, o: &Self, t: f32) -> Self { self.lerp(*o, t) } }
14impl Lerp for Vec3 { fn lerp_by(&self, o: &Self, t: f32) -> Self { self.lerp(*o, t) } }
15impl Lerp for Vec4 { fn lerp_by(&self, o: &Self, t: f32) -> Self { self.lerp(*o, t) } }
16impl Lerp for Quat { fn lerp_by(&self, o: &Self, t: f32) -> Self { self.slerp(*o, t) } }
17impl Lerp for Color {
18    fn lerp_by(&self, o: &Self, t: f32) -> Self { self.lerp(*o, t) }
19}
20impl Lerp for Transform {
21    fn lerp_by(&self, o: &Self, t: f32) -> Self { self.lerp(o, t) }
22}
23
24// ── Ease functions ────────────────────────────────────────────────────────────
25
26#[derive(Debug, Clone, Copy, PartialEq, serde::Serialize, serde::Deserialize)]
27pub enum EaseFunction {
28    Linear,
29    Step,
30    QuadIn, QuadOut, QuadInOut,
31    CubicIn, CubicOut, CubicInOut,
32    SineIn, SineOut, SineInOut,
33    ExpoIn, ExpoOut, ExpoInOut,
34    ElasticIn, ElasticOut,
35    BackIn, BackOut, BackInOut,
36    BounceOut, BounceIn,
37}
38
39impl EaseFunction {
40    pub fn apply(self, t: f32) -> f32 {
41        let t = t.clamp(0.0, 1.0);
42        match self {
43            EaseFunction::Linear => t,
44            EaseFunction::Step   => if t < 1.0 { 0.0 } else { 1.0 },
45            EaseFunction::QuadIn    => t * t,
46            EaseFunction::QuadOut   => t * (2.0 - t),
47            EaseFunction::QuadInOut => if t < 0.5 { 2.0*t*t } else { -1.0+(4.0-2.0*t)*t },
48            EaseFunction::CubicIn   => t * t * t,
49            EaseFunction::CubicOut  => { let s = t-1.0; s*s*s+1.0 }
50            EaseFunction::CubicInOut => if t < 0.5 { 4.0*t*t*t } else { (t-1.0)*(2.0*t-2.0)*(2.0*t-2.0)+1.0 },
51            EaseFunction::SineIn    => 1.0 - ((t * std::f32::consts::FRAC_PI_2).cos()),
52            EaseFunction::SineOut   => (t * std::f32::consts::FRAC_PI_2).sin(),
53            EaseFunction::SineInOut => 0.5 * (1.0 - (std::f32::consts::PI * t).cos()),
54            EaseFunction::ExpoIn    => if t == 0.0 { 0.0 } else { (2.0_f32).powf(10.0*t - 10.0) },
55            EaseFunction::ExpoOut   => if t == 1.0 { 1.0 } else { 1.0 - (2.0_f32).powf(-10.0*t) },
56            EaseFunction::ExpoInOut => {
57                if t == 0.0 { return 0.0; }
58                if t == 1.0 { return 1.0; }
59                if t < 0.5 { (2.0_f32).powf(20.0*t-10.0)/2.0 }
60                else { (2.0 - (2.0_f32).powf(-20.0*t+10.0)) / 2.0 }
61            }
62            EaseFunction::ElasticIn  => {
63                let c = 2.0 * std::f32::consts::PI / 3.0;
64                if t == 0.0 { 0.0 }
65                else if t == 1.0 { 1.0 }
66                else { -(2.0_f32).powf(10.0*t-10.0) * ((10.0*t-10.75)*c).sin() }
67            }
68            EaseFunction::ElasticOut => {
69                let c = 2.0 * std::f32::consts::PI / 3.0;
70                if t == 0.0 { 0.0 }
71                else if t == 1.0 { 1.0 }
72                else { (2.0_f32).powf(-10.0*t) * ((10.0*t-0.75)*c).sin() + 1.0 }
73            }
74            EaseFunction::BackIn  => { let c1=1.70158; let c3=c1+1.0; c3*t*t*t - c1*t*t }
75            EaseFunction::BackOut => { let c1=1.70158; let c3=c1+1.0; 1.0+c3*(t-1.0).powi(3)+c1*(t-1.0).powi(2) }
76            EaseFunction::BackInOut => {
77                let c2 = 1.70158 * 1.525;
78                if t < 0.5 { ((2.0*t).powi(2)*((c2+1.0)*2.0*t - c2)) / 2.0 }
79                else { ((2.0*t-2.0).powi(2)*((c2+1.0)*(2.0*t-2.0)+c2)+2.0) / 2.0 }
80            }
81            EaseFunction::BounceOut => bounce_out(t),
82            EaseFunction::BounceIn  => 1.0 - bounce_out(1.0 - t),
83        }
84    }
85}
86
87fn bounce_out(t: f32) -> f32 {
88    let n1 = 7.5625;
89    let d1 = 2.75;
90    if t < 1.0/d1 { n1*t*t }
91    else if t < 2.0/d1 { let t=t-1.5/d1; n1*t*t+0.75 }
92    else if t < 2.5/d1 { let t=t-2.25/d1; n1*t*t+0.9375 }
93    else { let t=t-2.625/d1; n1*t*t+0.984375 }
94}
95
96// ── Keyframe & Track ──────────────────────────────────────────────────────────
97
98#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
99pub struct Keyframe<T: Lerp> {
100    pub time: f32,
101    pub value: T,
102    pub ease: EaseFunction,
103}
104
105pub struct Track<T: Lerp> {
106    pub keyframes: Vec<Keyframe<T>>,
107}
108
109impl<T: Lerp> Track<T> {
110    pub fn new() -> Self { Self { keyframes: Vec::new() } }
111
112    pub fn add(mut self, time: f32, value: T, ease: EaseFunction) -> Self {
113        self.keyframes.push(Keyframe { time, value, ease });
114        self.keyframes.sort_by(|a, b| a.time.partial_cmp(&b.time).unwrap());
115        self
116    }
117
118    pub fn sample(&self, t: f32) -> Option<T> {
119        if self.keyframes.is_empty() { return None; }
120        if t <= self.keyframes[0].time { return Some(self.keyframes[0].value.clone()); }
121        let last = self.keyframes.last().unwrap();
122        if t >= last.time { return Some(last.value.clone()); }
123        for i in 0..self.keyframes.len()-1 {
124            let kf0 = &self.keyframes[i];
125            let kf1 = &self.keyframes[i+1];
126            if t >= kf0.time && t <= kf1.time {
127                let local = (t - kf0.time) / (kf1.time - kf0.time);
128                let eased = kf0.ease.apply(local);
129                return Some(kf0.value.lerp_by(&kf1.value, eased));
130            }
131        }
132        None
133    }
134
135    pub fn duration(&self) -> f32 {
136        self.keyframes.last().map(|k| k.time).unwrap_or(0.0)
137    }
138}
139
140impl<T: Lerp> Default for Track<T> { fn default() -> Self { Self::new() } }
141
142// ── Timeline ──────────────────────────────────────────────────────────────────
143
144/// Named animation clips, played by name.
145#[derive(Debug, Default)]
146pub struct Timeline {
147    pub time: f32,
148    pub playing: bool,
149    pub looping: bool,
150    pub speed: f32,
151    duration: f32,
152}
153
154impl Timeline {
155    pub fn new(duration: f32) -> Self {
156        Self { time: 0.0, playing: false, looping: false, speed: 1.0, duration }
157    }
158
159    pub fn play(&mut self)  { self.playing = true; }
160    pub fn pause(&mut self) { self.playing = false; }
161    pub fn stop(&mut self)  { self.playing = false; self.time = 0.0; }
162
163    pub fn tick(&mut self, dt: f32) {
164        if !self.playing { return; }
165        self.time += dt * self.speed;
166        if self.time >= self.duration {
167            if self.looping { self.time = self.time % self.duration; }
168            else { self.time = self.duration; self.playing = false; }
169        }
170    }
171
172    pub fn normalized_time(&self) -> f32 {
173        if self.duration <= 0.0 { return 0.0; }
174        (self.time / self.duration).clamp(0.0, 1.0)
175    }
176}