1use std::io::{self, Cursor};
6
7use crate::{axis::Axis, codec::VarInt, serial::ReadFrom, types::BlockPos};
8
9#[derive(Clone, Copy, Debug)]
11#[derive_const(PartialEq)]
12pub enum Direction {
13 Down,
15 Up,
17 North,
19 South,
21 West,
23 East,
25}
26
27impl ReadFrom for Direction {
28 fn read(data: &mut Cursor<&[u8]>) -> io::Result<Self> {
29 let id = VarInt::read(data)?.0;
30 match id {
31 0 => Ok(Direction::Down),
32 1 => Ok(Direction::Up),
33 2 => Ok(Direction::North),
34 3 => Ok(Direction::South),
35 4 => Ok(Direction::West),
36 5 => Ok(Direction::East),
37 _ => Err(io::Error::other("Invalid Direction id")),
38 }
39 }
40}
41
42impl Direction {
43 #[must_use]
45 pub const fn offset(self) -> (i32, i32, i32) {
46 match self {
47 Direction::Down => (0, -1, 0),
48 Direction::Up => (0, 1, 0),
49 Direction::North => (0, 0, -1),
50 Direction::South => (0, 0, 1),
51 Direction::West => (-1, 0, 0),
52 Direction::East => (1, 0, 0),
53 }
54 }
55
56 #[must_use]
58 pub const fn relative(self, pos: BlockPos) -> BlockPos {
59 let (dx, dy, dz) = self.offset();
60 pos.offset(dx, dy, dz)
61 }
62
63 #[must_use]
65 pub const fn get_axis(self) -> Axis {
66 match self {
67 Direction::Down | Direction::Up => Axis::Y,
68 Direction::North | Direction::South => Axis::Z,
69 Direction::West | Direction::East => Axis::X,
70 }
71 }
72
73 #[must_use]
75 pub const fn opposite(self) -> Direction {
76 match self {
77 Direction::Down => Direction::Up,
78 Direction::Up => Direction::Down,
79 Direction::North => Direction::South,
80 Direction::South => Direction::North,
81 Direction::West => Direction::East,
82 Direction::East => Direction::West,
83 }
84 }
85
86 #[must_use]
94 pub fn from_yaw(yaw: f32) -> Direction {
95 let adjusted = yaw.rem_euclid(360.0);
96 match adjusted {
97 y if !(45.0..315.0).contains(&y) => Direction::South,
98 y if y < 135.0 => Direction::West,
99 y if y < 225.0 => Direction::North,
100 _ => Direction::East,
101 }
102 }
103
104 #[must_use]
109 pub const fn to_yaw(self) -> f32 {
110 match self {
111 Direction::North => 180.0,
112 Direction::South | Direction::Up | Direction::Down => 0.0,
113 Direction::West => 90.0,
114 Direction::East => 270.0,
115 }
116 }
117
118 #[must_use]
120 pub const fn axis(self) -> Axis {
121 self.get_axis()
122 }
123
124 #[must_use]
126 pub const fn is_horizontal(self) -> bool {
127 matches!(
128 self,
129 Direction::North | Direction::South | Direction::East | Direction::West
130 )
131 }
132
133 #[must_use]
137 pub const fn rotate_y_clockwise(self) -> Direction {
138 match self {
139 Direction::North => Direction::East,
140 Direction::East => Direction::South,
141 Direction::South => Direction::West,
142 Direction::West => Direction::North,
143 other => other,
144 }
145 }
146
147 #[must_use]
151 pub const fn rotate_y_counter_clockwise(self) -> Direction {
152 match self {
153 Direction::North => Direction::West,
154 Direction::West => Direction::South,
155 Direction::South => Direction::East,
156 Direction::East => Direction::North,
157 other => other,
158 }
159 }
160
161 pub const UPDATE_SHAPE_ORDER: [Direction; 6] = [
164 Direction::West,
165 Direction::East,
166 Direction::North,
167 Direction::South,
168 Direction::Down,
169 Direction::Up,
170 ];
171
172 pub const FLOW_NEIGHBOR_CHECK: [Direction; 5] = [
175 Direction::Up,
176 Direction::North,
177 Direction::South,
178 Direction::West,
179 Direction::East,
180 ];
181
182 pub const HORIZONTAL: [Direction; 4] = [
184 Direction::North,
185 Direction::South,
186 Direction::West,
187 Direction::East,
188 ];
189
190 pub const ALL: [Direction; 6] = [
192 Direction::North,
193 Direction::South,
194 Direction::West,
195 Direction::East,
196 Direction::Down,
197 Direction::Up,
198 ];
199
200 #[must_use]
206 pub fn ordered_by_nearest(yaw: f32, pitch: f32) -> [Direction; 6] {
207 let pitch_rad = pitch.to_radians();
209 let yaw_rad = (-yaw).to_radians();
210
211 let pitch_sin = pitch_rad.sin();
212 let pitch_cos = pitch_rad.cos();
213 let yaw_sin = yaw_rad.sin();
214 let yaw_cos = yaw_rad.cos();
215
216 let x_pos = yaw_sin > 0.0;
218 let y_pos = pitch_sin < 0.0; let z_pos = yaw_cos > 0.0;
220
221 let x_yaw = if x_pos { yaw_sin } else { -yaw_sin };
223 let y_mag = if y_pos { -pitch_sin } else { pitch_sin };
224 let z_yaw = if z_pos { yaw_cos } else { -yaw_cos };
225 let x_mag = x_yaw * pitch_cos;
226 let z_mag = z_yaw * pitch_cos;
227
228 let axis_x = if x_pos {
230 Direction::East
231 } else {
232 Direction::West
233 };
234 let axis_y = if y_pos {
235 Direction::Up
236 } else {
237 Direction::Down
238 };
239 let axis_z = if z_pos {
240 Direction::South
241 } else {
242 Direction::North
243 };
244
245 if x_yaw > z_yaw {
247 if y_mag > x_mag {
248 Self::make_direction_array(axis_y, axis_x, axis_z)
249 } else if z_mag > y_mag {
250 Self::make_direction_array(axis_x, axis_z, axis_y)
251 } else {
252 Self::make_direction_array(axis_x, axis_y, axis_z)
253 }
254 } else if y_mag > z_mag {
255 Self::make_direction_array(axis_y, axis_z, axis_x)
256 } else if x_mag > y_mag {
257 Self::make_direction_array(axis_z, axis_x, axis_y)
258 } else {
259 Self::make_direction_array(axis_z, axis_y, axis_x)
260 }
261 }
262
263 const fn make_direction_array(
268 axis1: Direction,
269 axis2: Direction,
270 axis3: Direction,
271 ) -> [Direction; 6] {
272 [
273 axis1,
274 axis2,
275 axis3,
276 axis3.opposite(),
277 axis2.opposite(),
278 axis1.opposite(),
279 ]
280 }
281
282 #[must_use]
284 pub const fn as_str(&self) -> &str {
285 match self {
286 Direction::Down => "down",
287 Direction::Up => "up",
288 Direction::North => "north",
289 Direction::South => "south",
290 Direction::West => "west",
291 Direction::East => "east",
292 }
293 }
294}