egui_map/map/
animation.rs1use crate::map::{Error, objects::RawPoint};
5use egui::{Color32, Painter, Shape, epaint::CircleShape};
6use std::time::Instant;
7
8pub(crate) struct Animation {}
10
11impl Animation {
12 pub(crate) fn pulse(
20 painter: &Painter,
21 center: RawPoint,
22 zoom: f32,
23 initial_time: Instant,
24 color: Color32,
25 ) -> Result<bool, Error> {
26 let current_instant = Instant::now();
27 let mut result = false;
28 let time_diff = current_instant.duration_since(initial_time);
29 let secs_played = time_diff.as_secs_f32();
30 let radius = (4.00 + (40.00 * secs_played)) * zoom;
31 let mut transparency = 1.00 - (secs_played / 3.50).abs();
32 if transparency < 0.00 {
33 transparency = 0.00;
34 }
35 let corrected_color = Color32::from_rgba_unmultiplied(
36 color.r(),
37 color.g(),
38 color.b(),
39 (255.00 * transparency).round() as u8,
40 );
41 let circle = Shape::Circle(CircleShape::filled(center.into(), radius, corrected_color));
42 painter.extend(vec![circle]);
43 if secs_played < 3.50 {
44 result = true;
45 }
46 Ok(result)
47 }
48}
49
50#[cfg(test)]
51mod tests {
52 use super::*;
53 use egui::{Context, LayerId, Pos2, Rect, Vec2};
54 use std::time::Duration;
55
56 fn headless_painter() -> Painter {
57 Painter::new(
58 Context::default(),
59 LayerId::background(),
60 Rect::from_min_size(Pos2::ZERO, Vec2::new(100.0, 100.0)),
61 )
62 }
63
64 #[test]
65 fn pulse_returns_true_while_running() {
66 let painter = headless_painter();
67 let result = Animation::pulse(
68 &painter,
69 RawPoint::default(),
70 1.0,
71 Instant::now(),
72 Color32::RED,
73 );
74 assert!(matches!(result, Ok(true)));
75 }
76
77 #[test]
78 fn pulse_returns_false_when_finished() {
79 let painter = headless_painter();
80 let initial_time = Instant::now() - Duration::from_secs(4);
82 let result = Animation::pulse(
83 &painter,
84 RawPoint::default(),
85 1.0,
86 initial_time,
87 Color32::RED,
88 );
89 assert!(matches!(result, Ok(false)));
90 }
91}