azalea_core/
direction.rs

1use azalea_buf::AzBuf;
2
3use crate::position::{BlockPos, Vec3};
4
5#[derive(
6    Clone, Copy, Debug, AzBuf, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize,
7)]
8pub enum Direction {
9    #[default]
10    Down = 0,
11    Up,
12    North,
13    South,
14    West,
15    East,
16}
17
18impl Direction {
19    pub const HORIZONTAL: [Direction; 4] = [
20        Direction::North,
21        Direction::East,
22        Direction::South,
23        Direction::West,
24    ];
25    pub const VERTICAL: [Direction; 2] = [Direction::Down, Direction::Up];
26
27    pub fn nearest(vec: Vec3) -> Direction {
28        let mut best_direction = Direction::North;
29        let mut best_direction_amount = 0.0;
30
31        for dir in [
32            Direction::Down,
33            Direction::Up,
34            Direction::North,
35            Direction::South,
36            Direction::West,
37            Direction::East,
38        ]
39        .iter()
40        {
41            let amount = dir.normal_vec3().dot(vec);
42            if amount > best_direction_amount {
43                best_direction = *dir;
44                best_direction_amount = amount;
45            }
46        }
47
48        best_direction
49    }
50
51    #[inline]
52    pub fn normal(self) -> BlockPos {
53        match self {
54            Direction::Down => BlockPos::new(0, -1, 0),
55            Direction::Up => BlockPos::new(0, 1, 0),
56            Direction::North => BlockPos::new(0, 0, -1),
57            Direction::South => BlockPos::new(0, 0, 1),
58            Direction::West => BlockPos::new(-1, 0, 0),
59            Direction::East => BlockPos::new(1, 0, 0),
60        }
61    }
62
63    #[inline]
64    pub fn normal_vec3(self) -> Vec3 {
65        self.normal().to_vec3_floored()
66    }
67
68    pub fn opposite(self) -> Direction {
69        match self {
70            Direction::Down => Direction::Up,
71            Direction::Up => Direction::Down,
72            Direction::North => Direction::South,
73            Direction::South => Direction::North,
74            Direction::West => Direction::East,
75            Direction::East => Direction::West,
76        }
77    }
78
79    pub fn x(self) -> i32 {
80        self.normal().x
81    }
82    pub fn y(self) -> i32 {
83        self.normal().y
84    }
85    pub fn z(self) -> i32 {
86        self.normal().z
87    }
88}
89
90/// The four cardinal directions.
91///
92/// Note that azalea_block has a similar enum named `FacingCardinal` that is
93/// used for block states.
94#[derive(Clone, Copy, Debug, AzBuf, PartialEq, Eq, Hash, serde::Deserialize, serde::Serialize)]
95pub enum CardinalDirection {
96    North,
97    South,
98    West,
99    East,
100}
101
102/// A 3D axis like x, y, z.
103#[derive(Clone, Copy, Debug)]
104pub enum Axis {
105    X = 0,
106    Y = 1,
107    Z = 2,
108}
109
110#[derive(Clone, Copy, Debug)]
111pub enum AxisCycle {
112    None = 0,
113    Forward = 1,
114    Backward = 2,
115}
116
117impl CardinalDirection {
118    #[inline]
119    pub fn x(self) -> i16 {
120        match self {
121            CardinalDirection::East => 1,
122            CardinalDirection::West => -1,
123            _ => 0,
124        }
125    }
126    #[inline]
127    pub fn z(self) -> i16 {
128        match self {
129            CardinalDirection::South => 1,
130            CardinalDirection::North => -1,
131            _ => 0,
132        }
133    }
134
135    pub fn iter() -> impl Iterator<Item = CardinalDirection> {
136        [
137            CardinalDirection::North,
138            CardinalDirection::South,
139            CardinalDirection::West,
140            CardinalDirection::East,
141        ]
142        .iter()
143        .copied()
144    }
145
146    #[inline]
147    pub fn right(self) -> CardinalDirection {
148        match self {
149            CardinalDirection::North => CardinalDirection::East,
150            CardinalDirection::South => CardinalDirection::West,
151            CardinalDirection::West => CardinalDirection::North,
152            CardinalDirection::East => CardinalDirection::South,
153        }
154    }
155    #[inline]
156    pub fn left(self) -> CardinalDirection {
157        match self {
158            CardinalDirection::North => CardinalDirection::West,
159            CardinalDirection::South => CardinalDirection::East,
160            CardinalDirection::West => CardinalDirection::South,
161            CardinalDirection::East => CardinalDirection::North,
162        }
163    }
164}
165impl From<CardinalDirection> for Direction {
166    fn from(value: CardinalDirection) -> Self {
167        match value {
168            CardinalDirection::North => Direction::North,
169            CardinalDirection::South => Direction::South,
170            CardinalDirection::West => Direction::West,
171            CardinalDirection::East => Direction::East,
172        }
173    }
174}
175
176impl Axis {
177    /// Pick x, y, or z from the arguments depending on the axis.
178    #[inline]
179    pub fn choose<T>(&self, x: T, y: T, z: T) -> T {
180        match self {
181            Axis::X => x,
182            Axis::Y => y,
183            Axis::Z => z,
184        }
185    }
186
187    pub fn from_ordinal(ordinal: u32) -> Self {
188        match ordinal {
189            0 => Axis::X,
190            1 => Axis::Y,
191            2 => Axis::Z,
192            _ => panic!("Invalid ordinal {ordinal}"),
193        }
194    }
195}
196
197impl AxisCycle {
198    pub fn from_ordinal(ordinal: u32) -> Self {
199        match ordinal {
200            0 => Self::None,
201            1 => Self::Forward,
202            2 => Self::Backward,
203            _ => panic!("invalid ordinal"),
204        }
205    }
206    pub fn between(axis0: Axis, axis1: Axis) -> Self {
207        Self::from_ordinal(i32::rem_euclid(axis1 as i32 - axis0 as i32, 3) as u32)
208    }
209    pub fn inverse(self) -> Self {
210        match self {
211            Self::None => Self::None,
212            Self::Forward => Self::Backward,
213            Self::Backward => Self::Forward,
214        }
215    }
216    pub fn cycle(self, axis: Axis) -> Axis {
217        match self {
218            Self::None => axis,
219            Self::Forward => Axis::from_ordinal(i32::rem_euclid(axis as i32 + 1, 3) as u32),
220            Self::Backward => Axis::from_ordinal(i32::rem_euclid(axis as i32 - 1, 3) as u32),
221        }
222    }
223    pub fn cycle_xyz(self, x: i32, y: i32, z: i32, axis: Axis) -> i32 {
224        match self {
225            Self::None => axis.choose(x, y, z),
226            Self::Forward => axis.choose(z, x, y),
227            Self::Backward => axis.choose(y, z, x),
228        }
229    }
230}