yuno 0.1.1

A declarative UI layout and rendering framework powered by Skia.
use std::time::{Duration, Instant};

pub mod easing {
    use std::f64::consts::PI;

    #[allow(unused)]
    pub fn cubic_fly_hover_out(t: f64) -> f64 {
        let t_in = 500.0 / 4500.0;
        let t_out = 4000.0 / 4500.0;

        if t <= t_in {
            let u = t / t_in;
            let inv = 1.0 - u;
            1.0 - inv * inv * inv
        } else if t <= t_out {
            1.0
        } else {
            let u = (t - t_out) / (1.0 - t_out);
            1.0 - u * u * u
        }
    }

    #[allow(unused)]
    pub fn linear(t: f64) -> f64 {
        t
    }

    #[allow(unused)]
    pub fn quad_in(t: f64) -> f64 {
        t * t
    }

    #[allow(unused)]
    pub fn quad_out(t: f64) -> f64 {
        t * (2.0 - t)
    }

    #[allow(unused)]
    pub fn quad_in_out(t: f64) -> f64 {
        if t < 0.5 {
            2.0 * t * t
        } else {
            -1.0 + (4.0 - 2.0 * t) * t
        }
    }

    #[allow(unused)]
    pub fn sine_in(t: f64) -> f64 {
        1.0 - (t * PI / 2.0).cos()
    }

    #[allow(unused)]
    pub fn sine_out(t: f64) -> f64 {
        (t * PI / 2.0).sin()
    }

    #[allow(unused)]
    pub fn sine_in_out(t: f64) -> f64 {
        -0.5 * ((t * PI).cos() - 1.0)
    }

    #[allow(unused)]
    pub fn cubic_in_out(t: f64) -> f64 {
        if t < 0.5 {
            4.0 * t * t * t
        } else {
            let f = 2.0 * t - 2.0;
            0.5 * f * f * f + 1.0
        }
    }
}

pub struct StoryBoard {
    beginning: Instant,
    start: f64,
    end: f64,
    duration: f64,        // ms
    conv: fn(f64) -> f64, // Easing Functions [0, 1] -> [0, 1]
    r#loop: bool,
}

impl StoryBoard {
    pub fn init(start: f64, end: f64, duration: f64, r#loop: bool, f: fn(f64) -> f64) -> Self {
        Self {
            beginning: Instant::now(),
            start,
            end,
            duration,
            conv: f,
            r#loop,
        }
    }

    pub fn set_loop(&mut self, r#loop: bool) {
        self.r#loop = r#loop;
    }

    pub fn restart(&mut self) {
        self.beginning = Instant::now();
    }

    pub fn reverse(&mut self) {
        (self.start, self.end) = (self.end, self.start)
    }

    pub fn get_progress(&self) -> f64 {
        if self.duration <= 0.0 {
            return 1.0;
        }

        let pasted = Instant::now().duration_since(self.beginning).as_millis() as f64;
        let ratio = pasted / self.duration;

        if self.r#loop {
            ratio.rem_euclid(1.0)
        } else {
            ratio.clamp(0.0, 1.0)
        }
    }

    pub fn seek(&mut self, progress: f64) {
        if self.duration <= 0.0 {
            return;
        }

        let target_progress = if self.r#loop {
            progress.rem_euclid(1.0)
        } else {
            progress.clamp(0.0, 1.0)
        };

        let pasted_ms = target_progress * self.duration;
        self.beginning = Instant::now() - Duration::from_secs_f64(pasted_ms / 1000.0);
    }
}

impl From<&StoryBoard> for f64 {
    fn from(value: &StoryBoard) -> Self {
        if value.duration <= 0.0 {
            return value.end;
        }

        let r = value.get_progress();
        let x = (value.conv)(r);

        x * (value.end - value.start) + value.start
    }
}