1use std::io::{self, Cursor};
6
7use glam::IVec3;
8
9use crate::{axis::Axis, codec::VarInt, serial::ReadFrom, types::BlockPos};
10
11#[derive(Clone, Copy, Debug)]
13#[derive_const(PartialEq)]
14pub enum Direction {
15 Down,
17 Up,
19 North,
21 South,
23 West,
25 East,
27}
28
29impl ReadFrom for Direction {
30 fn read(data: &mut Cursor<&[u8]>) -> io::Result<Self> {
31 let id = VarInt::read(data)?.0;
32 match id {
33 0 => Ok(Direction::Down),
34 1 => Ok(Direction::Up),
35 2 => Ok(Direction::North),
36 3 => Ok(Direction::South),
37 4 => Ok(Direction::West),
38 5 => Ok(Direction::East),
39 _ => Err(io::Error::other("Invalid Direction id")),
40 }
41 }
42}
43
44impl Direction {
45 #[must_use]
47 pub const fn offset(self) -> (i32, i32, i32) {
48 match self {
49 Direction::Down => (0, -1, 0),
50 Direction::Up => (0, 1, 0),
51 Direction::North => (0, 0, -1),
52 Direction::South => (0, 0, 1),
53 Direction::West => (-1, 0, 0),
54 Direction::East => (1, 0, 0),
55 }
56 }
57
58 #[must_use]
62 pub const fn offset_xz(self) -> (i32, i32) {
63 match self {
64 Direction::Down | Direction::Up => (0, 0),
65 Direction::North => (0, -1),
66 Direction::South => (0, 1),
67 Direction::West => (-1, 0),
68 Direction::East => (1, 0),
69 }
70 }
71
72 #[must_use]
74 pub const fn offset_vec(self) -> IVec3 {
75 let (x, y, z) = self.offset();
76 IVec3::new(x, y, z)
77 }
78
79 #[must_use]
81 pub fn relative(self, pos: BlockPos) -> BlockPos {
82 BlockPos(pos.0 + self.offset_vec())
83 }
84
85 #[must_use]
87 pub const fn get_axis(self) -> Axis {
88 match self {
89 Direction::Down | Direction::Up => Axis::Y,
90 Direction::North | Direction::South => Axis::Z,
91 Direction::West | Direction::East => Axis::X,
92 }
93 }
94
95 #[must_use]
97 pub const fn opposite(self) -> Direction {
98 match self {
99 Direction::Down => Direction::Up,
100 Direction::Up => Direction::Down,
101 Direction::North => Direction::South,
102 Direction::South => Direction::North,
103 Direction::West => Direction::East,
104 Direction::East => Direction::West,
105 }
106 }
107
108 #[must_use]
116 pub fn from_yaw(yaw: f32) -> Direction {
117 let adjusted = yaw.rem_euclid(360.0);
118 match adjusted {
119 y if !(45.0..315.0).contains(&y) => Direction::South,
120 y if y < 135.0 => Direction::West,
121 y if y < 225.0 => Direction::North,
122 _ => Direction::East,
123 }
124 }
125
126 #[must_use]
131 pub const fn to_yaw(self) -> f32 {
132 match self {
133 Direction::North => 180.0,
134 Direction::South | Direction::Up | Direction::Down => 0.0,
135 Direction::West => 90.0,
136 Direction::East => 270.0,
137 }
138 }
139
140 #[must_use]
142 pub const fn axis(self) -> Axis {
143 self.get_axis()
144 }
145
146 #[must_use]
148 pub const fn positive_for_axis(axis: Axis) -> Direction {
149 match axis {
150 Axis::X => Direction::East,
151 Axis::Y => Direction::Up,
152 Axis::Z => Direction::South,
153 }
154 }
155
156 #[must_use]
158 pub const fn is_horizontal(self) -> bool {
159 matches!(
160 self,
161 Direction::North | Direction::South | Direction::East | Direction::West
162 )
163 }
164
165 #[must_use]
169 pub const fn rotate_y_clockwise(self) -> Direction {
170 match self {
171 Direction::North => Direction::East,
172 Direction::East => Direction::South,
173 Direction::South => Direction::West,
174 Direction::West => Direction::North,
175 other => other,
176 }
177 }
178
179 #[must_use]
183 pub const fn rotate_y_counter_clockwise(self) -> Direction {
184 match self {
185 Direction::North => Direction::West,
186 Direction::West => Direction::South,
187 Direction::South => Direction::East,
188 Direction::East => Direction::North,
189 other => other,
190 }
191 }
192
193 pub const UPDATE_SHAPE_ORDER: [Direction; 6] = [
196 Direction::West,
197 Direction::East,
198 Direction::North,
199 Direction::South,
200 Direction::Down,
201 Direction::Up,
202 ];
203
204 pub const FLOW_NEIGHBOR_CHECK: [Direction; 5] = [
207 Direction::Up,
208 Direction::North,
209 Direction::South,
210 Direction::West,
211 Direction::East,
212 ];
213
214 pub const HORIZONTAL: [Direction; 4] = [
216 Direction::North,
217 Direction::East,
218 Direction::South,
219 Direction::West,
220 ];
221
222 pub const ALL: [Direction; 6] = [
224 Direction::Down,
225 Direction::Up,
226 Direction::North,
227 Direction::South,
228 Direction::West,
229 Direction::East,
230 ];
231
232 #[must_use]
238 pub fn ordered_by_nearest(yaw: f32, pitch: f32) -> [Direction; 6] {
239 let pitch_rad = pitch.to_radians();
241 let yaw_rad = (-yaw).to_radians();
242
243 let pitch_sin = pitch_rad.sin();
244 let pitch_cos = pitch_rad.cos();
245 let yaw_sin = yaw_rad.sin();
246 let yaw_cos = yaw_rad.cos();
247
248 let x_pos = yaw_sin > 0.0;
250 let y_pos = pitch_sin < 0.0; let z_pos = yaw_cos > 0.0;
252
253 let x_yaw = if x_pos { yaw_sin } else { -yaw_sin };
255 let y_mag = if y_pos { -pitch_sin } else { pitch_sin };
256 let z_yaw = if z_pos { yaw_cos } else { -yaw_cos };
257 let x_mag = x_yaw * pitch_cos;
258 let z_mag = z_yaw * pitch_cos;
259
260 let axis_x = if x_pos {
262 Direction::East
263 } else {
264 Direction::West
265 };
266 let axis_y = if y_pos {
267 Direction::Up
268 } else {
269 Direction::Down
270 };
271 let axis_z = if z_pos {
272 Direction::South
273 } else {
274 Direction::North
275 };
276
277 if x_yaw > z_yaw {
279 if y_mag > x_mag {
280 Self::make_direction_array(axis_y, axis_x, axis_z)
281 } else if z_mag > y_mag {
282 Self::make_direction_array(axis_x, axis_z, axis_y)
283 } else {
284 Self::make_direction_array(axis_x, axis_y, axis_z)
285 }
286 } else if y_mag > z_mag {
287 Self::make_direction_array(axis_y, axis_z, axis_x)
288 } else if x_mag > y_mag {
289 Self::make_direction_array(axis_z, axis_x, axis_y)
290 } else {
291 Self::make_direction_array(axis_z, axis_y, axis_x)
292 }
293 }
294
295 const fn make_direction_array(
300 axis1: Direction,
301 axis2: Direction,
302 axis3: Direction,
303 ) -> [Direction; 6] {
304 [
305 axis1,
306 axis2,
307 axis3,
308 axis3.opposite(),
309 axis2.opposite(),
310 axis1.opposite(),
311 ]
312 }
313
314 #[must_use]
316 pub const fn as_str(&self) -> &str {
317 match self {
318 Direction::Down => "down",
319 Direction::Up => "up",
320 Direction::North => "north",
321 Direction::South => "south",
322 Direction::West => "west",
323 Direction::East => "east",
324 }
325 }
326
327 #[must_use]
329 pub fn random() -> Self {
330 match rand::random_range(0..6) {
331 1 => Self::Up,
332 2 => Self::North,
333 3 => Self::South,
334 4 => Self::West,
335 5 => Self::East,
336 _ => Self::Down,
337 }
338 }
339}
340
341#[cfg(test)]
342mod tests {
343 use crate::axis::Axis;
344
345 use super::Direction;
346
347 #[test]
348 fn horizontal_dirs_matches_vanilla_plane_horizontal_order() {
349 assert_eq!(
350 Direction::HORIZONTAL,
351 [
352 Direction::North,
353 Direction::East,
354 Direction::South,
355 Direction::West,
356 ]
357 );
358 }
359
360 #[test]
361 fn all_dirs_matches_vanilla_values_order() {
362 assert_eq!(
363 Direction::ALL,
364 [
365 Direction::Down,
366 Direction::Up,
367 Direction::North,
368 Direction::South,
369 Direction::West,
370 Direction::East,
371 ]
372 );
373 }
374
375 #[test]
376 fn positive_for_axis_matches_vanilla_axis_direction_positive() {
377 assert_eq!(Direction::positive_for_axis(Axis::X), Direction::East);
378 assert_eq!(Direction::positive_for_axis(Axis::Y), Direction::Up);
379 assert_eq!(Direction::positive_for_axis(Axis::Z), Direction::South);
380 }
381}