kenshii 0.3.0

Disk usage analyzer written in Rust
use std::time::{Duration, Instant};

#[derive(Clone, Copy)]
pub struct Animation {
    start: Instant,
    duration: Duration,
}

impl Animation {
    pub fn started(duration_ms: u64) -> Self {
        Self {
            start: Instant::now(),
            duration: Duration::from_millis(duration_ms.max(1)),
        }
    }

    pub fn linear(&self) -> f32 {
        let t = self.start.elapsed().as_secs_f32() / self.duration.as_secs_f32();
        t.clamp(0.0, 1.0)
    }

    pub fn eased(&self) -> f32 {
        ease_out_cubic(self.linear())
    }

    pub fn eased_staggered(&self, stagger_ms: u64, total_duration_ms: u64) -> f32 {
        let elapsed = self.start.elapsed().as_millis() as i64 - stagger_ms as i64;
        if elapsed <= 0 {
            return 0.0;
        }
        let t = elapsed as f32 / total_duration_ms.max(1) as f32;
        ease_out_cubic(t.clamp(0.0, 1.0))
    }

    #[allow(dead_code)]
    pub fn is_done(&self) -> bool {
        self.start.elapsed() >= self.duration
    }
}

pub fn ease_out_cubic(t: f32) -> f32 {
    let t = t.clamp(0.0, 1.0);
    1.0 - (1.0 - t).powi(3)
}

#[derive(Clone, Copy)]
pub struct Pulse {
    start: Instant,
}

impl Pulse {
    pub fn new() -> Self {
        Self {
            start: Instant::now(),
        }
    }

    pub fn wave(&self, period_ms: u64) -> f32 {
        let t = self.start.elapsed().as_secs_f32();
        let period = (period_ms.max(1) as f32) / 1000.0;
        let phase = (t / period) * std::f32::consts::TAU;
        (phase.sin() * 0.5) + 0.5
    }
}