egui-map 0.0.19

Visual component to draw a map on screen.
Documentation
use crate::map::{Error, objects::RawPoint};
use egui::{Color32, Painter, Shape, epaint::CircleShape};
use std::time::Instant;

pub(crate) struct Animation {}

impl Animation {
    pub(crate) fn pulse(
        painter: &Painter,
        center: RawPoint,
        zoom: f32,
        initial_time: Instant,
        color: Color32,
    ) -> Result<bool, Error> {
        let current_instant = Instant::now();
        let mut result = false;
        let time_diff = current_instant.duration_since(initial_time);
        let secs_played = time_diff.as_secs_f32();
        let radius = (4.00 + (40.00 * secs_played)) * zoom;
        let mut transparency = 1.00 - (secs_played / 3.50).abs();
        if transparency < 0.00 {
            transparency = 0.00;
        }
        let corrected_color = Color32::from_rgba_unmultiplied(
            color.r(),
            color.g(),
            color.b(),
            (255.00 * transparency).round() as u8,
        );
        let circle = Shape::Circle(CircleShape::filled(center.into(), radius, corrected_color));
        painter.extend(vec![circle]);
        if secs_played < 3.50 {
            result = true;
        }
        Ok(result)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use egui::{Context, LayerId, Pos2, Rect, Vec2};
    use std::time::Duration;

    fn headless_painter() -> Painter {
        Painter::new(
            Context::default(),
            LayerId::background(),
            Rect::from_min_size(Pos2::ZERO, Vec2::new(100.0, 100.0)),
        )
    }

    #[test]
    fn pulse_returns_true_while_running() {
        let painter = headless_painter();
        let result = Animation::pulse(
            &painter,
            RawPoint::default(),
            1.0,
            Instant::now(),
            Color32::RED,
        );
        assert!(matches!(result, Ok(true)));
    }

    #[test]
    fn pulse_returns_false_when_finished() {
        let painter = headless_painter();
        // la animación dura 3.5 segundos; 4 segundos después ya terminó
        let initial_time = Instant::now() - Duration::from_secs(4);
        let result = Animation::pulse(
            &painter,
            RawPoint::default(),
            1.0,
            initial_time,
            Color32::RED,
        );
        assert!(matches!(result, Ok(false)));
    }
}