pub(crate) mod color;
pub(crate) mod geometry;
pub(crate) mod shadow;
#[cfg(test)]
mod tests;
pub(crate) trait Animatable: Sized {
fn interpolate(from: Self, to: Self, progress: f32) -> Self {
Self::interpolate_progress(from, to, InterpolationProgress::new(progress))
}
fn interpolate_progress(from: Self, to: Self, progress: InterpolationProgress) -> Self;
}
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub(crate) struct InterpolationProgress(f32);
impl InterpolationProgress {
#[must_use]
pub(crate) fn new(progress: f32) -> Self {
if progress.is_nan() {
Self(0.0)
} else {
Self(progress.clamp(0.0, 1.0))
}
}
#[must_use]
pub(crate) fn value(self) -> f32 {
self.0
}
#[must_use]
pub(crate) fn is_start(self) -> bool {
self.0 == 0.0
}
#[must_use]
pub(crate) fn is_end(self) -> bool {
self.0 >= 1.0
}
}
impl From<f32> for InterpolationProgress {
fn from(progress: f32) -> Self {
Self::new(progress)
}
}
impl Animatable for f32 {
fn interpolate_progress(from: Self, to: Self, progress: InterpolationProgress) -> Self {
interpolate_with_progress(from, to, progress, |from, to, progress| {
from + (to - from) * progress.value()
})
}
}
impl Animatable for f64 {
fn interpolate_progress(from: Self, to: Self, progress: InterpolationProgress) -> Self {
interpolate_with_progress(from, to, progress, |from, to, progress| {
let progress = f64::from(progress.value());
from + (to - from) * progress
})
}
}
macro_rules! impl_interpolate_integer {
($($ty:ty),+ $(,)?) => {
$(
impl Animatable for $ty {
fn interpolate_progress(
from: Self,
to: Self,
progress: InterpolationProgress,
) -> Self {
interpolate_with_progress(from, to, progress, |from, to, progress| {
let from = f64::from(from);
let to = f64::from(to);
let progress = f64::from(progress.value());
#[allow(
clippy::cast_possible_truncation,
reason= "The interpolated value is rounded back to the integer type. Endpoints are returned before this branch.")]
#[allow(
clippy::cast_sign_loss,
reason= "The interpolated value is rounded back to the integer type. Endpoints are returned before this branch.")]
{
(from + (to - from) * progress).round() as Self
}
})
}
}
)+
};
}
impl_interpolate_integer!(u8, i8, u16, i16, u32, i32);
fn interpolate_with_progress<T>(
from: T,
to: T,
progress: InterpolationProgress,
interpolate_between: impl FnOnce(T, T, InterpolationProgress) -> T,
) -> T {
if progress.is_start() {
from
} else if progress.is_end() {
to
} else {
interpolate_between(from, to, progress)
}
}
macro_rules! impl_interpolate_tuple {
($($name:ident : $index:tt),+) => {
impl<$($name),+> Animatable for ($($name,)+)
where
$($name: Animatable),+
{
fn interpolate_progress(
from: Self,
to: Self,
progress: InterpolationProgress,
) -> Self {
interpolate_with_progress(from, to, progress, |from, to, progress| {
(
$(
$name::interpolate_progress(from.$index, to.$index, progress),
)+
)
})
}
}
};
}
impl_interpolate_tuple!(A: 0, B: 1);
impl_interpolate_tuple!(A: 0, B: 1, C: 2);
impl_interpolate_tuple!(A: 0, B: 1, C: 2, D: 3);
#[inline]
fn lerp_f32_raw(from: f32, to: f32, t: f32) -> f32 {
from + (to - from) * t
}