use std::{any::Any, borrow::Cow, path::PathBuf, sync::Arc, time::Duration};
use zng_app_context::{app_local, context_local};
use zng_time::{DInstant, Deadline};
use zng_txt::Txt;
use zng_unit::{
euclid, AngleDegree, AngleGradian, AngleRadian, AngleTurn, ByteLength, CornerRadius2D, Dip, Factor, FactorPercent, FactorUnits,
Orientation2D, Px, Rgba,
};
use crate::{
animation::{easing::EasingStep, Transition, Transitionable},
impl_from_and_into_var,
};
impl Transitionable for f64 {
fn lerp(self, to: &Self, step: EasingStep) -> Self {
self + (*to - self) * step.0 as f64
}
}
impl Transitionable for f32 {
fn lerp(self, to: &Self, step: EasingStep) -> Self {
self + (*to - self) * step.0
}
}
macro_rules! impl_transitionable {
($FT:ident => $($T:ty,)+) => {$(
impl Transitionable for $T {
fn lerp(self, to: &Self, step: EasingStep) -> Self {
$FT::lerp(self as $FT, &((*to) as $FT), step).round() as _
}
}
)+}
}
impl_transitionable! {
f32 => i8, u8, i16, u16, i32, u32,
}
impl_transitionable! {
f64 => u64, i64, u128, i128, isize, usize,
}
impl Transitionable for Px {
fn lerp(self, to: &Self, step: EasingStep) -> Self {
Px(self.0.lerp(&to.0, step))
}
}
impl Transitionable for Dip {
fn lerp(self, to: &Self, step: EasingStep) -> Self {
Dip::new_f32(self.to_f32().lerp(&to.to_f32(), step))
}
}
impl<T, U> Transitionable for euclid::Point2D<T, U>
where
T: Transitionable,
U: Send + Sync + Any,
{
fn lerp(self, to: &Self, step: EasingStep) -> Self {
euclid::point2(self.x.lerp(&to.x, step), self.y.lerp(&to.y, step))
}
}
impl<T, U> Transitionable for euclid::Box2D<T, U>
where
T: Transitionable,
U: Send + Sync + Any,
{
fn lerp(self, to: &Self, step: EasingStep) -> Self {
euclid::Box2D::new(self.min.lerp(&to.min, step), self.max.lerp(&to.max, step))
}
}
impl<T, U> Transitionable for euclid::Point3D<T, U>
where
T: Transitionable,
U: Send + Sync + Any,
{
fn lerp(self, to: &Self, step: EasingStep) -> Self {
euclid::point3(self.x.lerp(&to.x, step), self.y.lerp(&to.y, step), self.z.lerp(&to.z, step))
}
}
impl<T, U> Transitionable for euclid::Box3D<T, U>
where
T: Transitionable,
U: Send + Sync + Any,
{
fn lerp(self, to: &Self, step: EasingStep) -> Self {
euclid::Box3D::new(self.min.lerp(&to.min, step), self.max.lerp(&to.max, step))
}
}
impl<T, U> Transitionable for euclid::Length<T, U>
where
T: Transitionable,
U: Send + Sync + Any,
{
fn lerp(self, to: &Self, step: EasingStep) -> Self {
euclid::Length::new(self.get().lerp(&to.clone().get(), step))
}
}
impl<T, U> Transitionable for euclid::Size2D<T, U>
where
T: Transitionable,
U: Send + Sync + Any,
{
fn lerp(self, to: &Self, step: EasingStep) -> Self {
euclid::size2(self.width.lerp(&to.width, step), self.height.lerp(&to.height, step))
}
}
impl<T, U> Transitionable for euclid::Size3D<T, U>
where
T: Transitionable,
U: Send + Sync + Any,
{
fn lerp(self, to: &Self, step: EasingStep) -> Self {
euclid::size3(
self.width.lerp(&to.width, step),
self.height.lerp(&to.height, step),
self.depth.lerp(&to.depth, step),
)
}
}
impl<T, U> Transitionable for euclid::Rect<T, U>
where
T: Transitionable,
U: Send + Sync + Any,
{
fn lerp(self, to: &Self, step: EasingStep) -> Self {
euclid::Rect::new(self.origin.lerp(&to.origin, step), self.size.lerp(&to.size, step))
}
}
impl<T, U> Transitionable for euclid::Vector2D<T, U>
where
T: Transitionable,
U: Send + Sync + Any,
{
fn lerp(self, to: &Self, step: EasingStep) -> Self {
euclid::vec2(self.x.lerp(&to.x, step), self.y.lerp(&to.y, step))
}
}
impl<T, U> Transitionable for euclid::Vector3D<T, U>
where
T: Transitionable,
U: Send + Sync + Any,
{
fn lerp(self, to: &Self, step: EasingStep) -> Self {
euclid::vec3(self.x.lerp(&to.x, step), self.y.lerp(&to.y, step), self.z.lerp(&to.z, step))
}
}
impl Transitionable for Factor {
fn lerp(self, to: &Self, step: EasingStep) -> Self {
Factor(self.0.lerp(&to.0, step))
}
}
impl<T, U> Transitionable for euclid::SideOffsets2D<T, U>
where
T: Transitionable,
U: Send + Sync + Any,
{
fn lerp(self, to: &Self, step: EasingStep) -> Self {
euclid::SideOffsets2D::new(
self.top.lerp(&to.top, step),
self.right.lerp(&to.right, step),
self.bottom.lerp(&to.bottom, step),
self.left.lerp(&to.left, step),
)
}
}
impl Transitionable for bool {
fn lerp(self, to: &Self, step: EasingStep) -> Self {
if step >= 1.fct() {
*to
} else {
self
}
}
}
impl<T, U> Transitionable for CornerRadius2D<T, U>
where
T: Transitionable,
U: Send + Sync + Any,
{
fn lerp(self, to: &Self, step: EasingStep) -> Self {
Self {
top_left: self.top_left.lerp(&to.top_left, step),
top_right: self.top_right.lerp(&to.top_right, step),
bottom_right: self.bottom_right.lerp(&to.bottom_right, step),
bottom_left: self.bottom_left.lerp(&to.bottom_left, step),
}
}
}
impl Transitionable for ByteLength {
fn lerp(self, to: &Self, step: EasingStep) -> Self {
Self(self.0.lerp(&to.0, step))
}
}
impl_from_and_into_var! {
fn from(s: &'static str) -> Txt;
fn from(s: String) -> Txt;
fn from(s: Cow<'static, str>) -> Txt;
fn from(c: char) -> Txt;
fn from(t: Txt) -> PathBuf;
fn from(t: Txt) -> String;
fn from(t: Txt) -> Cow<'static, str>;
fn from(f: f32) -> Factor;
fn from(one_or_zero: bool) -> Factor;
fn from(f: FactorPercent) -> Factor;
fn from(f: Factor) -> FactorPercent;
fn from(d: DInstant) -> Deadline;
fn from(d: Duration) -> Deadline;
fn from(b: usize) -> ByteLength;
fn from(rad: AngleRadian) -> AngleTurn;
fn from(grad: AngleGradian) -> AngleTurn;
fn from(deg: AngleDegree) -> AngleTurn;
fn from(grad: AngleGradian) -> AngleRadian;
fn from(deg: AngleDegree) -> AngleRadian;
fn from(turn: AngleTurn) -> AngleRadian;
fn from(rad: AngleRadian) -> AngleGradian;
fn from(deg: AngleDegree) -> AngleGradian;
fn from(turn: AngleTurn) -> AngleGradian;
fn from(rad: AngleRadian) -> AngleDegree;
fn from(grad: AngleGradian) -> AngleDegree;
fn from(turn: AngleTurn) -> AngleDegree;
}
macro_rules! impl_into_var_option {
(
$($T:ty),* $(,)?
) => {
impl_from_and_into_var! { $(
fn from(some: $T) -> Option<$T>;
)* }
}
}
impl_into_var_option! {
i8, i16, i32, i64, i128, isize,
u8, u16, u32, u64, u128, usize,
f32, f64,
char, bool,
Orientation2D,
}
pub fn slerp_sampler<T: Transitionable>(t: &Transition<T>, step: EasingStep) -> T {
slerp_enabled(true, || t.sample(step))
}
pub fn is_slerp_enabled() -> bool {
SLERP_ENABLED.get_clone()
}
pub fn slerp_enabled<R>(enabled: bool, f: impl FnOnce() -> R) -> R {
SLERP_ENABLED.with_context(&mut Some(Arc::new(enabled)), f)
}
context_local! {
static SLERP_ENABLED: bool = false;
}
impl Transitionable for AngleRadian {
fn lerp(self, to: &Self, step: EasingStep) -> Self {
match is_slerp_enabled() {
false => self.lerp(*to, step),
true => self.slerp(*to, step),
}
}
}
impl Transitionable for AngleGradian {
fn lerp(self, to: &Self, step: EasingStep) -> Self {
match is_slerp_enabled() {
false => self.lerp(*to, step),
true => self.slerp(*to, step),
}
}
}
impl Transitionable for AngleDegree {
fn lerp(self, to: &Self, step: EasingStep) -> Self {
match is_slerp_enabled() {
false => self.lerp(*to, step),
true => self.slerp(*to, step),
}
}
}
impl Transitionable for AngleTurn {
fn lerp(self, to: &Self, step: EasingStep) -> Self {
match is_slerp_enabled() {
false => self.lerp(*to, step),
true => self.slerp(*to, step),
}
}
}
impl Transitionable for Rgba {
fn lerp(self, to: &Self, step: EasingStep) -> Self {
let lerp = *RGBA_LERP.read();
lerp(self, *to, step)
}
}
app_local! {
static RGBA_LERP: fn(Rgba, Rgba, EasingStep) -> Rgba = const { lerp_rgba_linear };
}
fn lerp_rgba_linear(mut from: Rgba, to: Rgba, factor: Factor) -> Rgba {
from.red = from.red.lerp(&to.red, factor);
from.green = from.green.lerp(&to.green, factor);
from.blue = from.blue.lerp(&to.blue, factor);
from.alpha = from.alpha.lerp(&to.alpha, factor);
from
}
#[expect(non_camel_case_types)]
pub struct TRANSITIONABLE_APP;
impl TRANSITIONABLE_APP {
pub fn init_rgba_lerp(&self, lerp: fn(Rgba, Rgba, EasingStep) -> Rgba) {
*RGBA_LERP.write() = lerp;
}
}