npc_engine_utils/
direction.rs

1/*
2 *  SPDX-License-Identifier: Apache-2.0 OR MIT
3 *  © 2020-2022 ETH Zurich and other contributors, see AUTHORS.txt for details
4 */
5
6use serde::Serialize;
7use std::{fmt, marker::PhantomData};
8
9use crate::Coord2D;
10
11/// A helper trait that tells whether up and down are positive or negative.
12pub trait YUpDown {
13    /// Returns 1 or -1 depending on the up direction.
14    fn up() -> i32;
15    /// Returns 1 or -1 depending on the down direction.
16    fn down() -> i32;
17}
18
19/// Up is positive.
20pub struct YUp;
21impl YUpDown for YUp {
22    fn up() -> i32 {
23        1
24    }
25    fn down() -> i32 {
26        -1
27    }
28}
29
30/// Down is positive.
31pub struct YDown;
32impl YUpDown for YDown {
33    fn up() -> i32 {
34        -1
35    }
36    fn down() -> i32 {
37        1
38    }
39}
40
41/// A direction type.
42///
43/// Directions can be applied to [Coord2D] through the help of a [DirectionConverter],
44/// which decides whether up is positive or negative y.
45#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Serialize)]
46#[serde(rename_all = "kebab-case")]
47pub enum Direction {
48    Up,
49    Down,
50    Left,
51    Right,
52}
53
54impl fmt::Display for Direction {
55    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56        match self {
57            Direction::Up => write!(f, "Up"),
58            Direction::Down => write!(f, "Down"),
59            Direction::Left => write!(f, "Left"),
60            Direction::Right => write!(f, "Right"),
61        }
62    }
63}
64
65/// A helper struct to apply direction to coordinates.
66///
67/// If used as DirectionConverter<[YUp]>, y will be positive up,
68/// and if used as DirectionConverter<[YDown]>, y will be negative up.
69pub struct DirectionConverter<YDir: YUpDown> {
70    _phantom: PhantomData<YDir>,
71}
72impl<YDir: YUpDown> DirectionConverter<YDir> {
73    /// Moves `coord` by `direction`.
74    pub fn apply(direction: Direction, coord: Coord2D) -> Coord2D {
75        let Coord2D { x, y } = coord;
76        let (x, y) = match direction {
77            Direction::Up => (x, y + YDir::up()),
78            Direction::Down => (x, y + YDir::down()),
79            Direction::Left => (x - 1, y),
80            Direction::Right => (x + 1, y),
81        };
82        Coord2D::new(x, y)
83    }
84
85    /// Gets the direction between `start` and `end`, panics if they are not adjacent.
86    pub fn from(start: Coord2D, end: Coord2D) -> Direction {
87        let dx = end.x - start.x;
88        let dy = end.y - start.y;
89        match (dx, dy) {
90            (1, _) => Direction::Right,
91            (-1, _) => Direction::Left,
92            (_, 1) => match YDir::up() == 1 {
93                true => Direction::Up,
94                false => Direction::Down,
95            },
96            (_, -1) => match YDir::down() == -1 {
97                true => Direction::Down,
98                false => Direction::Up,
99            },
100            _ => panic!("start and end positions are not next to each others with 4-connectivity"),
101        }
102    }
103}
104
105/// All directions.
106pub const DIRECTIONS: [Direction; 4] = [
107    Direction::Up,
108    Direction::Right,
109    Direction::Down,
110    Direction::Left,
111];
112/// Apply direction to coordinates with up being positive.
113pub type DirectionConverterYUp = DirectionConverter<YUp>;
114/// Apply direction to coordinates with up being negative.
115pub type DirectionConverterYDown = DirectionConverter<YDown>;