lighthouse_protocol/utils/
direction.rs

1use std::{fmt::Debug, ops::Neg};
2
3use rand::{prelude::Distribution, distributions::Standard};
4use serde::{Deserialize, Serialize};
5
6use super::{Vec2, Unity, Zero};
7
8/// One of the four cardinal directions.
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
10pub enum Direction {
11    Up,
12    Down,
13    Left,
14    Right,
15}
16
17impl Direction {
18    pub fn approximate_from<T>(vec2: Vec2<T>) -> Option<Self> where T: Zero + Unity + PartialEq + Neg<Output = T> + PartialOrd + Copy {
19        if vec2 == Vec2::ZERO {
20            return None;
21        }
22
23        // See https://www.desmos.com/calculator/472pdoxzqa for visualization
24        // Note that the y-axis is flipped here, per computer graphics conventions,
25        // hence the sign flip (-y instead of y).
26        let Vec2 { x, y } = vec2;
27        let left_or_up = x < -y;
28        let right_or_up = -x < -y;
29        Some(
30            match (left_or_up, right_or_up) {
31                (true, true) => Direction::Up,
32                (true, false) => Direction::Left,
33                (false, true) => Direction::Right,
34                (false, false) => Direction::Down, 
35            }
36        )
37    }
38}
39
40impl<T> TryFrom<Vec2<T>> for Direction where T: Zero + Unity + PartialEq + Debug {
41    type Error = String;
42
43    fn try_from(vec2: Vec2<T>) -> Result<Self, Self::Error> {
44        if vec2 == Vec2::UP {
45            Ok(Direction::Up)
46        } else if vec2 == Vec2::DOWN {
47            Ok(Direction::Down)
48        } else if vec2 == Vec2::LEFT {
49            Ok(Direction::Left)
50        } else if vec2 == Vec2::RIGHT {
51            Ok(Direction::Right)
52        } else {
53            Err(format!("Not a direction: {:?}", vec2))
54        }
55    }
56}
57
58impl<T> From<Direction> for Vec2<T> where T: Zero + Unity {
59    fn from(direction: Direction) -> Self {
60        match direction {
61            Direction::Up => Vec2::UP,
62            Direction::Down => Vec2::DOWN,
63            Direction::Left => Vec2::LEFT,
64            Direction::Right => Vec2::RIGHT,
65        }
66    }
67}
68
69impl Distribution<Direction> for Standard {
70    fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> Direction {
71        match rng.gen_range(0..4) {
72            0 => Direction::Up,
73            1 => Direction::Down,
74            2 => Direction::Left,
75            3 => Direction::Right,
76            _ => unreachable!(),
77        }
78    }
79}