==== main.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 ====
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 ====
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 ====
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:
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 }