#![deny(
warnings,
missing_copy_implementations,
trivial_casts,
trivial_numeric_casts,
unsafe_code,
unstable_features,
unused_import_braces,
unused_qualifications,
missing_docs
)]
use std::time::Duration;
#[cfg(feature = "ease_handle")]
use rand::Rng;
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)]
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, 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),
}
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),
}
}
}
pub struct EasingComponent<T> {
start: Option<EaseValue<T>>,
end: EaseValue<T>,
ease_function: EaseMethod,
timer: Timer,
pub state: EasingState,
paused: bool,
easing_type: EasingType,
#[cfg(feature = "ease_handle")]
id: i128,
direction: i16,
}
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> {
#[cfg(feature = "ease_handle")]
let mut rng = rand::thread_rng();
let next = EasingComponent {
start: None,
end: EaseValue(end),
ease_function: ease_function.into(),
timer: match easing_type {
EasingType::Once { duration } => Timer::new(duration, false),
EasingType::Loop { duration, .. } => Timer::new(duration, false),
EasingType::PingPong { duration, .. } => Timer::new(duration, false),
},
state: EasingState::Play,
paused: false,
easing_type,
#[cfg(feature = "ease_handle")]
id: rng.gen(),
direction: 1,
};
EasingChainComponent(vec![next, self])
}
}
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> {
#[cfg(feature = "ease_handle")]
let mut rng = rand::thread_rng();
let next = EasingComponent {
start: None,
end: EaseValue(end),
ease_function: ease_function.into(),
timer: match easing_type {
EasingType::Once { duration } => Timer::new(duration, false),
EasingType::Loop { duration, .. } => Timer::new(duration, false),
EasingType::PingPong { duration, .. } => Timer::new(duration, false),
},
state: EasingState::Play,
paused: false,
easing_type,
#[cfg(feature = "ease_handle")]
id: rng.gen(),
direction: 1,
};
self.0.insert(0, next);
self
}
}
pub trait Ease: Sized {
fn ease(
start: Option<Self>,
end: Self,
ease_function: impl Into<EaseMethod>,
easing_type: EasingType,
) -> EasingComponent<Self> {
#[cfg(feature = "ease_handle")]
let mut rng = rand::thread_rng();
EasingComponent {
start: start.map(EaseValue),
end: EaseValue(end),
ease_function: ease_function.into(),
timer: match easing_type {
EasingType::Once { duration } => Timer::new(duration, false),
EasingType::Loop { duration, .. } => Timer::new(duration, false),
EasingType::PingPong { duration, .. } => Timer::new(duration, false),
},
state: EasingState::Play,
paused: false,
easing_type,
#[cfg(feature = "ease_handle")]
id: rng.gen(),
direction: 1,
}
}
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 Handle<T> where EaseValue<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> {
#[cfg(feature = "ease_handle")]
let mut rng = rand::thread_rng();
EasingComponent {
start: start.map(EaseValue),
end: EaseValue(end),
ease_function: ease_function.into(),
timer: match easing_type {
EasingType::Once { duration } => Timer::new(duration, false),
EasingType::Loop { duration, .. } => Timer::new(duration, false),
EasingType::PingPong { duration, .. } => Timer::new(duration, false),
},
state: EasingState::Play,
paused: false,
easing_type,
#[cfg(feature = "ease_handle")]
id: rng.gen(),
direction: 1,
}
}
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> {}