#![allow(dead_code)]
use std::time::{Duration, Instant};
#[derive(Debug)]
pub struct Timer {
last_update: Instant,
start_time: Instant,
}
impl Timer {
pub fn new() -> Self {
let now = Instant::now();
Self {
last_update: now,
start_time: now,
}
}
pub fn delta(&mut self) -> f64 {
let now = Instant::now();
let delta = now.duration_since(self.last_update);
self.last_update = now;
delta.as_secs_f64()
}
pub fn elapsed(&self) -> f64 {
Instant::now().duration_since(self.start_time).as_secs_f64()
}
pub fn reset(&mut self) {
let now = Instant::now();
self.last_update = now;
self.start_time = now;
}
}
impl Default for Timer {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct Timeline {
duration: f64,
elapsed: f64,
looping: bool,
}
impl Timeline {
pub fn new(duration: f64) -> Self {
Self {
duration,
elapsed: 0.0,
looping: false,
}
}
pub fn looping(mut self, looping: bool) -> Self {
self.looping = looping;
self
}
pub fn update(&mut self, delta_time: f64) -> bool {
self.elapsed += delta_time;
if self.elapsed >= self.duration {
if self.looping {
self.elapsed %= self.duration;
true
} else {
self.elapsed = self.duration;
false
}
} else {
true
}
}
pub fn progress(&self) -> f32 {
if self.duration <= 0.0 {
1.0
} else {
(self.elapsed / self.duration).min(1.0) as f32
}
}
pub fn is_complete(&self) -> bool {
!self.looping && self.elapsed >= self.duration
}
pub fn reset(&mut self) {
self.elapsed = 0.0;
}
pub fn duration(&self) -> f64 {
self.duration
}
pub fn elapsed(&self) -> f64 {
self.elapsed
}
}
#[derive(Debug)]
pub struct FrameLimiter {
target_fps: u32,
frame_duration: Duration,
last_frame: Instant,
}
impl FrameLimiter {
pub fn new(target_fps: u32) -> Self {
let frame_duration = Duration::from_nanos(1_000_000_000 / target_fps as u64);
Self {
target_fps,
frame_duration,
last_frame: Instant::now(),
}
}
pub fn limit(&mut self) -> bool {
let now = Instant::now();
let elapsed = now.duration_since(self.last_frame);
if elapsed < self.frame_duration {
let sleep_duration = self.frame_duration - elapsed;
std::thread::sleep(sleep_duration);
self.last_frame = Instant::now();
true
} else {
self.last_frame = now;
false
}
}
pub fn target_fps(&self) -> u32 {
self.target_fps
}
pub fn set_target_fps(&mut self, fps: u32) {
self.target_fps = fps;
self.frame_duration = Duration::from_nanos(1_000_000_000 / fps as u64);
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::thread;
use std::time::Duration;
#[test]
fn test_timer_creation() {
let timer = Timer::new();
assert!(timer.elapsed() >= 0.0);
}
#[test]
fn test_timer_delta() {
let mut timer = Timer::new();
thread::sleep(Duration::from_millis(10));
let delta = timer.delta();
assert!(delta > 0.0);
assert!(delta < 1.0); }
#[test]
fn test_timer_reset() {
let mut timer = Timer::new();
thread::sleep(Duration::from_millis(10));
timer.reset();
let elapsed = timer.elapsed();
assert!(elapsed < 0.01); }
#[test]
fn test_timeline_creation() {
let timeline = Timeline::new(2.0);
assert_eq!(timeline.duration(), 2.0);
assert_eq!(timeline.elapsed(), 0.0);
assert_eq!(timeline.progress(), 0.0);
assert!(!timeline.is_complete());
}
#[test]
fn test_timeline_progress() {
let mut timeline = Timeline::new(2.0);
timeline.update(1.0);
assert_eq!(timeline.progress(), 0.5);
assert!(!timeline.is_complete());
timeline.update(1.0);
assert_eq!(timeline.progress(), 1.0);
assert!(timeline.is_complete());
}
#[test]
fn test_timeline_looping() {
let mut timeline = Timeline::new(2.0).looping(true);
let still_active = timeline.update(3.0);
assert!(still_active);
assert!(!timeline.is_complete());
assert_eq!(timeline.progress(), 0.5);
}
#[test]
fn test_timeline_non_looping() {
let mut timeline = Timeline::new(2.0);
let still_active = timeline.update(3.0);
assert!(!still_active);
assert!(timeline.is_complete());
assert_eq!(timeline.progress(), 1.0);
}
#[test]
fn test_timeline_reset() {
let mut timeline = Timeline::new(2.0);
timeline.update(1.5);
timeline.reset();
assert_eq!(timeline.elapsed(), 0.0);
assert_eq!(timeline.progress(), 0.0);
assert!(!timeline.is_complete());
}
#[test]
fn test_timeline_zero_duration() {
let timeline = Timeline::new(0.0);
assert_eq!(timeline.progress(), 1.0);
}
#[test]
fn test_frame_limiter_creation() {
let limiter = FrameLimiter::new(60);
assert_eq!(limiter.target_fps(), 60);
}
#[test]
fn test_frame_limiter_set_fps() {
let mut limiter = FrameLimiter::new(60);
limiter.set_target_fps(30);
assert_eq!(limiter.target_fps(), 30);
}
#[test]
fn test_frame_limiter_limit() {
let mut limiter = FrameLimiter::new(1000);
let _did_wait = limiter.limit();
}
mod property_tests {
use super::*;
use proptest::prelude::*;
proptest! {
#[test]
fn test_timeline_progress_bounds(duration in 0.1f64..100.0, elapsed_time in 0.0f64..200.0) {
let mut timeline = Timeline::new(duration);
timeline.update(elapsed_time);
let progress = timeline.progress();
assert!((0.0..=1.0).contains(&progress));
}
#[test]
fn test_timeline_looping_never_completes(
duration in 0.1f64..10.0,
update_times in prop::collection::vec(0.0f64..5.0, 1..10)
) {
let mut timeline = Timeline::new(duration).looping(true);
for update_time in update_times {
timeline.update(update_time);
assert!(!timeline.is_complete());
}
}
#[test]
fn test_frame_limiter_fps_range(fps in 1u32..1000) {
let limiter = FrameLimiter::new(fps);
assert_eq!(limiter.target_fps(), fps);
}
}
}
}