melvin_ob/imaging/
camera_state.rs

1use fixed::types::I32F32;
2use std::{collections::HashMap, sync::LazyLock};
3use strum_macros::{Display, EnumIter};
4
5#[cfg(test)]
6use rand::prelude::Rng;
7
8/// Represents different camera angles supported by the system.
9///
10/// # Variants
11/// - `Narrow`: Indicates a narrow FOV for the camera.
12/// - `Normal`: Indicates a normal FOV for the camera.
13/// - `Wide`: Indicates a wide FOV for the camera.
14///
15/// These angles are associated with a specific square side length
16/// for image processing purposes, available in a pre-computed lookup table.
17#[derive(Debug, Display, PartialEq, Eq, Clone, Copy, Hash, EnumIter)]
18pub enum CameraAngle {
19    Narrow,
20    Normal,
21    Wide,
22}
23
24impl CameraAngle {
25    /// Returns the square side length (in pixels) associated with the given camera angle.
26    /// The value is retrieved from a pre-computed lookup table `CAMERA_SCALE_LOOKUP`.
27    ///
28    /// # Returns
29    /// A `u16` representing the side length of the square for the given camera angle.
30    pub fn get_square_side_length(self) -> u16 { CAMERA_SCALE_LOOKUP[&self] }
31
32    pub fn get_max_speed(self) -> I32F32 { CAMERA_MAX_SPEED_LOOKUP[&self] }
33    
34    #[cfg(test)]
35    pub fn random<R: Rng + ?Sized>(rng: &mut R) -> Self {
36        let variants = [CameraAngle::Narrow, CameraAngle::Normal, CameraAngle::Wide];
37        variants[rng.random_range(0..variants.len())]
38    }
39}
40
41impl From<&str> for CameraAngle {
42    /// Converts a string value into a `CameraAngle` enum.
43    ///
44    /// # Arguments
45    /// - `value`: A string slice representing the camera angle (`"narrow"`, `"normal"` or `"wide"`).
46    ///
47    /// # Returns
48    /// A `CameraAngle` converted from the input string.
49    /// If the input is an unknown string this defaults to `normal` and logs the error.
50    fn from(value: &str) -> Self {
51        match value.to_lowercase().as_str() {
52            "narrow" => CameraAngle::Narrow,
53            "wide" => CameraAngle::Wide,
54            "normal" => CameraAngle::Normal,
55            _ => panic!("Couldn't convert camera_angle string"),
56        }
57    }
58}
59
60impl From<CameraAngle> for &'static str {
61    /// Converts a `CameraAngle` enum back into a static string slice representation.
62    ///
63    /// # Arguments
64    /// - `value`: A `CameraAngle` variant to be converted.
65    ///
66    /// # Returns
67    /// A string slice representation of the camera angle (`"narrow"`, `"normal"`, `"wide"`).
68    fn from(value: CameraAngle) -> Self {
69        match value {
70            CameraAngle::Narrow => "narrow",
71            CameraAngle::Normal => "normal",
72            CameraAngle::Wide => "wide",
73        }
74    }
75}
76
77/// A pre-computed lookup table mapping `CameraAngle` variants to their
78/// associated square side lengths (in pixels).
79///
80/// The side length represents the size of an image square corresponding
81/// to a specific camera angle.
82static CAMERA_SCALE_LOOKUP: LazyLock<HashMap<CameraAngle, u16>> = LazyLock::new(|| {
83    let mut lookup = HashMap::new();
84    let transition_widths = vec![
85        (CameraAngle::Narrow, 600),
86        (CameraAngle::Normal, 800),
87        (CameraAngle::Wide, 1000),
88    ];
89
90    for (angle, square_side_length) in transition_widths {
91        lookup.insert(angle, square_side_length);
92    }
93    lookup
94});
95
96/// A pre-computed lookup table mapping `CameraAngle` variants to their
97/// associated maximum speed limit (in pixels/s).
98static CAMERA_MAX_SPEED_LOOKUP: LazyLock<HashMap<CameraAngle, I32F32>> = LazyLock::new(|| {
99    let mut lookup = HashMap::new();
100    let transition_widths = vec![
101        (CameraAngle::Narrow, I32F32::lit("10.0")),
102        (CameraAngle::Normal, I32F32::lit("50.0")),
103        (CameraAngle::Wide, I32F32::MAX),
104    ];
105
106    for (angle, square_side_length) in transition_widths {
107        lookup.insert(angle, square_side_length);
108    }
109    lookup
110});