#![warn(missing_docs)]
use std::cell::Cell;
#[repr(C, u32)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum EasingCurve {
Linear,
CubicBezier([f32; 4]),
}
impl Default for EasingCurve {
fn default() -> Self {
Self::Linear
}
}
#[repr(transparent)]
#[derive(Copy, Clone, Debug, Default, PartialEq, Ord, PartialOrd, Eq)]
pub struct Instant(pub u64);
impl core::ops::Sub<Instant> for Instant {
type Output = core::time::Duration;
fn sub(self, other: Self) -> core::time::Duration {
core::time::Duration::from_millis(self.0 - other.0)
}
}
impl core::ops::Sub<core::time::Duration> for Instant {
type Output = Instant;
fn sub(self, other: core::time::Duration) -> Instant {
Self(self.0 - other.as_millis() as u64)
}
}
impl core::ops::Add<core::time::Duration> for Instant {
type Output = Instant;
fn add(self, other: core::time::Duration) -> Instant {
Self(self.0 + other.as_millis() as u64)
}
}
impl core::ops::AddAssign<core::time::Duration> for Instant {
fn add_assign(&mut self, other: core::time::Duration) {
self.0 += other.as_millis() as u64;
}
}
impl core::ops::SubAssign<core::time::Duration> for Instant {
fn sub_assign(&mut self, other: core::time::Duration) {
self.0 += other.as_millis() as u64;
}
}
impl Instant {
pub fn duration_since(self, earlier: Instant) -> core::time::Duration {
self - earlier
}
}
pub struct AnimationDriver {
active_animations: Cell<bool>,
global_instant: core::pin::Pin<Box<crate::Property<Instant>>>,
initial_instant: instant::Instant,
}
impl Default for AnimationDriver {
fn default() -> Self {
AnimationDriver {
active_animations: Cell::default(),
global_instant: Box::pin(crate::Property::new(Instant::default())),
initial_instant: instant::Instant::now(),
}
}
}
impl AnimationDriver {
pub fn update_animations(&self, new_tick: Instant) {
if self.global_instant.as_ref().get() != new_tick {
self.active_animations.set(false);
self.global_instant.as_ref().set(new_tick);
}
}
pub fn has_active_animations(&self) -> bool {
self.active_animations.get()
}
pub fn set_has_active_animations(&self) {
self.active_animations.set(true);
}
pub fn current_tick(&self) -> Instant {
self.global_instant.as_ref().get()
}
}
thread_local!(pub(crate) static CURRENT_ANIMATION_DRIVER : AnimationDriver = AnimationDriver::default());
pub fn current_tick() -> Instant {
CURRENT_ANIMATION_DRIVER.with(|driver| driver.current_tick())
}
pub fn easing_curve(curve: &EasingCurve, value: f32) -> f32 {
match curve {
EasingCurve::Linear => value,
EasingCurve::CubicBezier([a, b, c, d]) => {
if !(0.0..=1.0).contains(a) && !(0.0..=1.0).contains(c) {
return value;
};
let curve = lyon::algorithms::geom::cubic_bezier::CubicBezierSegment {
from: (0., 0.).into(),
ctrl1: (*a, *b).into(),
ctrl2: (*c, *d).into(),
to: (1., 1.).into(),
};
let curve = curve.assume_monotonic();
curve.y(curve.solve_t_for_x(value, 0.0..1.0, 0.01))
}
}
}
pub(crate) fn update_animations() {
CURRENT_ANIMATION_DRIVER.with(|driver| {
let duration = instant::Instant::now() - driver.initial_instant;
let duration = match std::env::var("SIXTYFPS_SLOW_ANIMATIONS") {
Err(_) => duration,
Ok(val) => {
let factor = val.parse().unwrap_or(2);
duration / factor
}
};
driver.update_animations(Instant(duration.as_millis() as u64))
});
}