#![warn(
missing_copy_implementations,
trivial_casts,
trivial_numeric_casts,
unsafe_code,
unstable_features,
unused_import_braces,
unused_qualifications,
missing_docs
)]
#![doc = include_str!("../README.md")]
use std::time::Duration;
use bevy::prelude::*;
use interpolation::Ease as IEase;
pub use interpolation::EaseFunction;
pub use interpolation::Lerp;
mod plugin;
pub use plugin::{custom_ease_system, EasingsPlugin};
mod implemented;
#[derive(Debug, Clone, Copy)]
pub struct EaseValue<T>(pub T);
#[derive(Clone, Copy)]
pub enum EasingType {
Once {
duration: Duration,
},
Loop {
duration: Duration,
pause: Option<Duration>,
},
PingPong {
duration: Duration,
pause: Option<Duration>,
},
}
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
pub enum EasingState {
Play,
Paused,
}
impl std::ops::Not for EasingState {
type Output = EasingState;
fn not(self) -> Self::Output {
match self {
EasingState::Paused => EasingState::Play,
EasingState::Play => EasingState::Paused,
}
}
}
#[derive(Clone, Copy)]
pub enum EaseMethod {
EaseFunction(EaseFunction),
Linear,
Discrete,
CustomFunction(fn(f32) -> f32),
}
#[allow(clippy::from_over_into)]
impl Into<EaseMethod> for EaseFunction {
fn into(self) -> EaseMethod {
EaseMethod::EaseFunction(self)
}
}
trait MyEaser {
fn compute(self, function: EaseMethod) -> Self;
}
impl MyEaser for f32 {
fn compute(self, function: EaseMethod) -> f32 {
match function {
EaseMethod::EaseFunction(function) => self.calc(function),
EaseMethod::Linear => {
let delta = 0.01;
if self < 0. + delta {
0.
} else if self > 1. - delta {
1.
} else {
self
}
}
EaseMethod::Discrete => {
if self > 0.5 {
1.
} else {
0.
}
}
EaseMethod::CustomFunction(function) => function(self),
}
}
}
#[derive(Component, Clone)]
pub struct EasingComponent<T> {
start: Option<EaseValue<T>>,
end: EaseValue<T>,
ease_function: EaseMethod,
timer: Timer,
pub state: EasingState,
paused: bool,
easing_type: EasingType,
direction: EasingDirection,
}
#[derive(PartialEq, Eq, Clone, Copy)]
pub enum EasingDirection {
Forward = 1,
Backward = -1,
}
impl EasingDirection {
fn reverse(&mut self) {
*self = match self {
EasingDirection::Backward => EasingDirection::Forward,
EasingDirection::Forward => EasingDirection::Backward,
};
}
}
impl<T> EasingComponent<T> {
pub fn direction(&self) -> EasingDirection {
self.direction
}
}
impl<T: std::fmt::Debug> std::fmt::Debug for EasingComponent<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("EasingComponent")
.field("start", &self.start)
.field("end", &self.end)
.field("state", &self.state)
.finish()
}
}
impl<T: Default> EasingComponent<T> {
pub fn ease_to(
self,
end: T,
ease_function: impl Into<EaseMethod>,
easing_type: EasingType,
) -> EasingChainComponent<T> {
let next = EasingComponent {
start: None,
end: EaseValue(end),
ease_function: ease_function.into(),
timer: match easing_type {
EasingType::Once { duration }
| EasingType::Loop { duration, .. }
| EasingType::PingPong { duration, .. } => Timer::new(duration, TimerMode::Once),
},
state: EasingState::Play,
paused: false,
easing_type,
direction: EasingDirection::Forward,
};
EasingChainComponent(vec![next, self])
}
}
#[derive(Component)]
pub struct EasingChainComponent<T>(Vec<EasingComponent<T>>);
impl<T: Default> EasingChainComponent<T> {
pub fn ease_to(
mut self,
end: T,
ease_function: impl Into<EaseMethod>,
easing_type: EasingType,
) -> EasingChainComponent<T> {
let next = EasingComponent {
start: None,
end: EaseValue(end),
ease_function: ease_function.into(),
timer: match easing_type {
EasingType::Once { duration }
| EasingType::Loop { duration, .. }
| EasingType::PingPong { duration, .. } => Timer::new(duration, TimerMode::Once),
},
state: EasingState::Play,
paused: false,
easing_type,
direction: EasingDirection::Forward,
};
self.0.insert(0, next);
self
}
pub fn repeat(mut self, n: usize) -> EasingChainComponent<T>
where
EasingComponent<T>: Clone,
{
let mut tmp = self.0.clone();
for _ in 1..n {
tmp.extend(self.0.clone());
}
self.0 = tmp;
self
}
}
pub trait Ease: Sized {
fn ease(
start: Option<Self>,
end: Self,
ease_function: impl Into<EaseMethod>,
easing_type: EasingType,
) -> EasingComponent<Self> {
EasingComponent {
start: start.map(EaseValue),
end: EaseValue(end),
ease_function: ease_function.into(),
timer: match easing_type {
EasingType::Once { duration }
| EasingType::Loop { duration, .. }
| EasingType::PingPong { duration, .. } => Timer::new(duration, TimerMode::Once),
},
state: EasingState::Play,
paused: false,
easing_type,
direction: EasingDirection::Forward,
}
}
fn ease_to(
self,
target: Self,
ease_function: impl Into<EaseMethod>,
easing_type: EasingType,
) -> EasingComponent<Self> {
Self::ease(Some(self), target, ease_function, easing_type)
}
}
impl<T> Ease for EaseValue<T> where T: Lerp<Scalar = f32> {}
impl<T> Ease for T where EaseValue<T>: Lerp<Scalar = f32> {}
impl<T> Default for EaseValue<T>
where
T: Default,
{
fn default() -> Self {
EaseValue(T::default())
}
}
trait IntermediateLerp: Sized {
fn lerp(start: &EaseValue<&Self>, end: &EaseValue<&Self>, scalar: f32) -> Self;
}
pub trait CustomComponentEase: Sized {
fn ease(
start: Option<Self>,
end: Self,
ease_function: impl Into<EaseMethod>,
easing_type: EasingType,
) -> EasingComponent<Self> {
EasingComponent {
start: start.map(EaseValue),
end: EaseValue(end),
ease_function: ease_function.into(),
timer: match easing_type {
EasingType::Once { duration }
| EasingType::Loop { duration, .. }
| EasingType::PingPong { duration, .. } => Timer::new(duration, TimerMode::Once),
},
state: EasingState::Play,
paused: false,
easing_type,
direction: EasingDirection::Forward,
}
}
fn ease_to(
self,
target: Self,
ease_function: impl Into<EaseMethod>,
easing_type: EasingType,
) -> EasingComponent<Self> {
Self::ease(Some(self), target, ease_function, easing_type)
}
}
impl<T> CustomComponentEase for T where T: Lerp<Scalar = f32> {}