use utilities::prelude::cgmath::{InnerSpace, Quaternion, Vector3}; use std::time::Duration; #[derive(Clone, Debug)] pub struct Translation { time: Duration, translation: Vector3, } #[derive(Clone, Debug)] pub struct Rotation { time: Duration, rotation: Quaternion, } #[derive(Clone, Debug)] pub struct Scale { time: Duration, scale: Vector3, } pub(crate) trait LinearInterpolation { type Inner; fn new(time: Duration, value: Self::Inner) -> Self; fn time(&self) -> Duration; fn linear_interpolation(&self, other: &Self, now: Duration) -> Self; fn to_inner(&self) -> Self::Inner; fn almost_equal(&self, other: &Self) -> bool; } impl LinearInterpolation for Translation { type Inner = Vector3; fn new(time: Duration, translation: Vector3) -> Self { Translation { time, translation } } fn time(&self) -> Duration { self.time } fn linear_interpolation(&self, other: &Self, now: Duration) -> Self { debug_assert!(self.time > other.time); let interpolation_value = (now.as_secs_f32() - other.time.as_secs_f32()) / (self.time.as_secs_f32() - other.time.as_secs_f32()); let translation = other.translation + interpolation_value * (self.translation - other.translation); Translation { time: now, translation, } } fn to_inner(&self) -> Vector3 { self.translation } fn almost_equal(&self, other: &Self) -> bool { almost_equal_f32(self.translation.x, other.translation.x) && almost_equal_f32(self.translation.y, other.translation.y) && almost_equal_f32(self.translation.z, other.translation.z) } } impl LinearInterpolation for Rotation { type Inner = Quaternion; fn new(time: Duration, rotation: Quaternion) -> Self { Rotation { time, rotation } } fn time(&self) -> Duration { self.time } fn linear_interpolation(&self, other: &Self, now: Duration) -> Self { debug_assert!(self.time > other.time); let interpolation_value = (now.as_secs_f32() - other.time.as_secs_f32()) / (self.time.as_secs_f32() - other.time.as_secs_f32()); let norm_me = self.rotation.normalize(); let norm_other = other.rotation.normalize(); let rotation = slerp(norm_other, norm_me, interpolation_value); Rotation { time: now, rotation, } } fn to_inner(&self) -> Quaternion { self.rotation } fn almost_equal(&self, other: &Self) -> bool { almost_equal_f32(self.rotation.s, other.rotation.s) && almost_equal_f32(self.rotation.v.x, other.rotation.v.x) && almost_equal_f32(self.rotation.v.y, other.rotation.v.y) && almost_equal_f32(self.rotation.v.z, other.rotation.v.z) } } #[inline] fn slerp(p: Quaternion, q: Quaternion, t: f32) -> Quaternion { let dot = p.dot(q); if dot < 0.0 { return slerp(-1.0 * p, q, t); } let angle = dot.acos(); let sin = angle.sin(); if almost_equal_f32(sin, 0.0) { return p; } let first = p * ((1.0 - t) * angle).sin(); let second = q * (t * angle).sin(); (first + second) / sin } impl LinearInterpolation for Scale { type Inner = Vector3; fn new(time: Duration, scale: Vector3) -> Self { Scale { time, scale } } fn time(&self) -> Duration { self.time } fn linear_interpolation(&self, other: &Self, now: Duration) -> Self { debug_assert!(self.time > other.time); let interpolation_value = (now.as_secs_f32() - other.time.as_secs_f32()) / (self.time.as_secs_f32() - other.time.as_secs_f32()); let scale = other.scale + interpolation_value * (self.scale - other.scale); Scale { time: now, scale } } fn to_inner(&self) -> Vector3 { self.scale } fn almost_equal(&self, other: &Self) -> bool { almost_equal_f32(self.scale.x, other.scale.x) && almost_equal_f32(self.scale.y, other.scale.y) && almost_equal_f32(self.scale.z, other.scale.z) } } #[inline] fn almost_equal_f32(f1: f32, f2: f32) -> bool { (f1 - f2).abs() < 0.000001 } #[derive(Clone, Debug)] pub enum Transform { Translation(Vector3), Rotation(Quaternion), Scale(Vector3), }