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