1use std::{
2 f64::consts::E,
3 thread,
4 time::{Duration, Instant},
5};
6
7pub struct Animator {
8 pub config: AnimatorConfig,
9}
10pub struct AnimatorConfig {
11 pub target_fps: u32,
12 pub speed: u32,
13}
14
15impl Animator {
16 pub fn new(config: Option<AnimatorConfig>) -> Self {
17 if let Some(config) = config {
18 Self { config }
19 } else {
20 Self {
21 config: AnimatorConfig {
22 target_fps: 1,
23 speed: 100,
24 },
25 }
26 }
27 }
28
29 fn run<F>(&self, mut on_tick: F)
30 where
31 F: FnMut(),
32 {
33 let target_fps = self.config.target_fps;
34 let speed = self.config.speed as f64 / 100_f64;
35 let actual_target_fps: u8 = ((target_fps as f64 * speed).floor() as f64) as u8;
36 println!("{} {}", actual_target_fps, speed);
37 let target_sleep_time = Duration::from_micros(1_000_000 / actual_target_fps.max(1) as u64);
38
39 loop {
40 on_tick();
41
42 let frame_start = Instant::now();
43 let frame_time = frame_start.elapsed();
44 if frame_time < target_sleep_time {
45 thread::sleep(target_sleep_time - frame_time);
46 }
47 }
48 }
49
50 pub fn oscillate<F>(&self, min: u8, max: u8, power: u8, mut on_tick: F)
52 where
53 F: FnMut(u8),
54 {
55 let mut current: f64 = min.into();
56 let mut x: f64 = 0.0;
57 let mut is_going_up = false;
58
59 Self::run(self, || {
60 let modo = Self::log(x, 1.0, 1.0, 0.5);
61
62 if !is_going_up && current > min.into() {
63 x -= 0.1;
64 } else if is_going_up && current < max.into() {
65 x += 0.1;
66 } else {
67 is_going_up = !is_going_up;
68 }
69
70 current = (max as f64 * modo).round();
71 on_tick(current as u8);
72 });
73 }
74
75 pub fn log(x: f64, L: f64, k: f64, x0: f64) -> f64 {
77 L / (1.0 + E.powf(-1.0 * k * (x - x0)))
78 }
79
80 pub fn clean_screen() {
81 print!("{esc}[2J{esc}[1;1H", esc = 27 as char);
82 }
83}