Created
July 5, 2025 12:29
-
-
Save Staninna/9f13b1c503fe7edc3de3dfc360475884 to your computer and use it in GitHub Desktop.
Revisions
-
Staninna created this gist
Jul 5, 2025 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,246 @@ ==== main.rs ==== ```rs mod functions; mod interpolated; mod vec2; use crate::{functions::TransitionFunction, interpolated::Interpolated, vec2::Vec2}; use std::time::Duration; fn main() { let init = Vec2::new(-35.0, 10.0); let end = Vec2::new(100.0, 50.0); let dur = 1.5; let trans = TransitionFunction::EaseInOutExponential; let mut pos = Interpolated::new(init); pos.set_duration(Duration::from_secs_f32(dur)); pos.transition = trans; pos.set(end); while !pos.is_finished() { let v = pos.value(); println!("position = {:+.1?}", v); std::thread::sleep(Duration::from_millis(100)); } } ``` ==== interpolated.rs ==== ```rs use crate::functions::TransitionFunction; use std::ops::{Add, Mul, Sub}; use std::time::{Duration, Instant}; pub struct Interpolated<T> { start: T, end: T, start_time: Instant, pub speed: f32, pub transition: TransitionFunction, } impl<T> Interpolated<T> where T: Copy + Add<Output = T> + Sub<Output = T> + Mul<f32, Output = T>, { pub fn new(initial: T) -> Self { let now = Instant::now(); Interpolated { start: initial, end: initial, start_time: now, speed: 1.0, transition: TransitionFunction::Linear, } } pub fn set_duration(&mut self, dur: Duration) { self.speed = 1.0 / dur.as_secs_f32(); } pub fn set(&mut self, value: T) { self.start = self.value(); self.end = value; self.start_time = Instant::now(); } pub fn value(&self) -> T { let elapsed_secs = (Instant::now() - self.start_time).as_secs_f32() * self.speed; if elapsed_secs >= 1.0 { return self.end; } let r = self.transition.ratio(elapsed_secs); self.start + (self.end - self.start) * r } pub fn is_finished(&self) -> bool { let elapsed = (Instant::now() - self.start_time).as_secs_f32() * self.speed; elapsed >= 1.0 } } ``` ==== vec2.rs ==== ```rs use std::ops::{Add, Mul, Sub}; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Vec2<T> { pub x: T, pub y: T, } impl<T> Vec2<T> { pub fn new(x: T, y: T) -> Self { Self { x, y } } pub fn zero() -> Self where T: Default + Copy, { Self::new(T::default(), T::default()) } } impl<T> Add for Vec2<T> where T: Add<Output = T>, { type Output = Vec2<T>; fn add(self, rhs: Vec2<T>) -> Vec2<T> { Vec2 { x: self.x + rhs.x, y: self.y + rhs.y, } } } impl<T> Sub for Vec2<T> where T: Sub<Output = T>, { type Output = Vec2<T>; fn sub(self, rhs: Vec2<T>) -> Vec2<T> { Vec2 { x: self.x - rhs.x, y: self.y - rhs.y, } } } impl<T> Mul<T> for Vec2<T> where T: Mul<Output = T> + Copy, { type Output = Vec2<T>; fn mul(self, scalar: T) -> Vec2<T> { Vec2 { x: self.x * scalar, y: self.y * scalar, } } } ``` ==== functions.rs ==== ```rs use std::f32::consts::PI; fn ease_none(_: f32) -> f32 { 1.0 } fn linear(t: f32) -> f32 { t } fn expo_in_out(t: f32) -> f32 { if t < 0.5 { 0.5 * (20.0 * t - 10.0).exp2() } else { 1.0 - 0.5 * (10.0 - 20.0 * t).exp2() } } fn ease_out_back(t: f32) -> f32 { const C1: f32 = 1.70158; const C3: f32 = C1 + 1.0; 1.0 + C3 * (t - 1.0).powi(3) + C1 * (t - 1.0).powi(2) } fn ease_in_back(t: f32) -> f32 { const C1: f32 = 1.70158; const C3: f32 = C1 + 1.0; C3 * t * t * t - C1 * t * t } fn ease_out_elastic(t: f32) -> f32 { const TWO_PI: f32 = 2.0 * PI; const C4: f32 = TWO_PI / 3.0; if t == 0.0 { return 0.0; } if t == 1.0 { return 1.0; } (-10.0 * t).exp2() * ((t * 10.0 - 0.75) * C4).sin() + 1.0 } const EASINGS: [fn(f32) -> f32; 6] = [ ease_none, linear, expo_in_out, ease_out_back, ease_in_back, ease_out_elastic, ]; #[repr(u8)] #[derive(Copy, Clone, Debug)] pub enum TransitionFunction { None = 0, Linear = 1, EaseInOutExponential = 2, EaseOutBack = 3, EaseInBack = 4, EaseOutElastic = 5, } impl TransitionFunction { #[inline] pub fn ratio(self, t: f32) -> f32 { let t = if t < 0.0 { 0.0 } else if t > 1.0 { 1.0 } else { t }; EASINGS[self as u8 as usize](t) } } ``` Output: ```txt position = Vec2 { x: -34.9, y: +10.0 } position = Vec2 { x: -34.8, y: +10.0 } position = Vec2 { x: -34.6, y: +10.1 } position = Vec2 { x: -33.9, y: +10.3 } position = Vec2 { x: -32.3, y: +10.8 } position = Vec2 { x: -28.2, y: +12.0 } position = Vec2 { x: -17.8, y: +15.1 } position = Vec2 { x: +8.4, y: +22.9 } position = Vec2 { x: +58.5, y: +37.7 } position = Vec2 { x: +83.6, y: +45.1 } position = Vec2 { x: +93.5, y: +48.1 } position = Vec2 { x: +97.4, y: +49.2 } position = Vec2 { x: +99.0, y: +49.7 } position = Vec2 { x: +99.6, y: +49.9 } position = Vec2 { x: +99.8, y: +50.0 } ```