veloren_common/util/
cardinal_directions.rs

1use std::ops::{Add, Sub};
2
3use rand::RngExt;
4use vek::{Aabb, Aabr, Mat3, Vec2, Vec3};
5
6/// A 2d cardinal direction.
7#[derive(Debug, enum_map::Enum, strum::EnumIter, enumset::EnumSetType)]
8pub enum Dir2 {
9    X,
10    Y,
11    NegX,
12    NegY,
13}
14
15impl Dir2 {
16    pub const ALL: [Dir2; 4] = [Dir2::X, Dir2::Y, Dir2::NegX, Dir2::NegY];
17
18    pub fn choose(rng: &mut impl RngExt) -> Dir2 {
19        match rng.random_range(0..4) {
20            0 => Dir2::X,
21            1 => Dir2::Y,
22            2 => Dir2::NegX,
23            _ => Dir2::NegY,
24        }
25    }
26
27    pub fn from_vec2(vec: Vec2<i32>) -> Dir2 {
28        if vec.x.abs() > vec.y.abs() {
29            if vec.x > 0 { Dir2::X } else { Dir2::NegX }
30        } else if vec.y > 0 {
31            Dir2::Y
32        } else {
33            Dir2::NegY
34        }
35    }
36
37    pub fn to_dir3(self) -> Dir3 { Dir3::from_dir(self) }
38
39    #[must_use]
40    pub fn opposite(self) -> Dir2 {
41        match self {
42            Dir2::X => Dir2::NegX,
43            Dir2::NegX => Dir2::X,
44            Dir2::Y => Dir2::NegY,
45            Dir2::NegY => Dir2::Y,
46        }
47    }
48
49    /// Rotate the direction anti clock wise
50    #[must_use]
51    pub fn rotated_ccw(self) -> Dir2 {
52        match self {
53            Dir2::X => Dir2::Y,
54            Dir2::NegX => Dir2::NegY,
55            Dir2::Y => Dir2::NegX,
56            Dir2::NegY => Dir2::X,
57        }
58    }
59
60    /// Rotate the direction clock wise
61    #[must_use]
62    pub fn rotated_cw(self) -> Dir2 { self.rotated_ccw().opposite() }
63
64    #[must_use]
65    pub fn orthogonal(self) -> Dir2 {
66        match self {
67            Dir2::X | Dir2::NegX => Dir2::Y,
68            Dir2::Y | Dir2::NegY => Dir2::X,
69        }
70    }
71
72    #[must_use]
73    pub fn abs(self) -> Dir2 {
74        match self {
75            Dir2::X | Dir2::NegX => Dir2::X,
76            Dir2::Y | Dir2::NegY => Dir2::Y,
77        }
78    }
79
80    #[must_use]
81    pub fn signum(self) -> i32 {
82        match self {
83            Dir2::X | Dir2::Y => 1,
84            Dir2::NegX | Dir2::NegY => -1,
85        }
86    }
87
88    pub fn to_vec2(self) -> Vec2<i32> {
89        match self {
90            Dir2::X => Vec2::new(1, 0),
91            Dir2::NegX => Vec2::new(-1, 0),
92            Dir2::Y => Vec2::new(0, 1),
93            Dir2::NegY => Vec2::new(0, -1),
94        }
95    }
96
97    /// The diagonal to the left of `self`, this is equal to this dir plus this
98    /// dir rotated counter clockwise.
99    pub fn diagonal(self) -> Vec2<i32> { self.to_vec2() + self.rotated_ccw().to_vec2() }
100
101    pub fn to_vec3(self) -> Vec3<i32> {
102        match self {
103            Dir2::X => Vec3::new(1, 0, 0),
104            Dir2::NegX => Vec3::new(-1, 0, 0),
105            Dir2::Y => Vec3::new(0, 1, 0),
106            Dir2::NegY => Vec3::new(0, -1, 0),
107        }
108    }
109
110    /// Create a vec2 where x is in the direction of `self`, and y is anti
111    /// clockwise of `self`.
112    pub fn vec2(self, x: i32, y: i32) -> Vec2<i32> {
113        match self {
114            Dir2::X => Vec2::new(x, y),
115            Dir2::NegX => Vec2::new(-x, -y),
116            Dir2::Y => Vec2::new(y, x),
117            Dir2::NegY => Vec2::new(-y, -x),
118        }
119    }
120
121    /// Create a vec2 where x is in the direction of `self`, and y is orthogonal
122    /// version of self.
123    pub fn vec2_abs<T>(self, x: T, y: T) -> Vec2<T> {
124        match self {
125            Dir2::X => Vec2::new(x, y),
126            Dir2::NegX => Vec2::new(x, y),
127            Dir2::Y => Vec2::new(y, x),
128            Dir2::NegY => Vec2::new(y, x),
129        }
130    }
131
132    /// Returns a 3x3 matrix that rotates Vec3(1, 0, 0) to the direction you get
133    /// in to_vec3. Inteded to be used with Primitive::Rotate.
134    ///
135    /// Example:
136    /// ```
137    /// use vek::Vec3;
138    /// use veloren_common::util::Dir2;
139    /// let dir = Dir2::X;
140    ///
141    /// assert_eq!(dir.to_mat3() * Vec3::new(1, 0, 0), dir.to_vec3());
142    ///
143    /// let dir = Dir2::NegX;
144    ///
145    /// assert_eq!(dir.to_mat3() * Vec3::new(1, 0, 0), dir.to_vec3());
146    ///
147    /// let dir = Dir2::Y;
148    ///
149    /// assert_eq!(dir.to_mat3() * Vec3::new(1, 0, 0), dir.to_vec3());
150    ///
151    /// let dir = Dir2::NegY;
152    ///
153    /// assert_eq!(dir.to_mat3() * Vec3::new(1, 0, 0), dir.to_vec3());
154    /// ```
155    pub fn to_mat3(self) -> Mat3<i32> {
156        match self {
157            Dir2::X => Mat3::new(1, 0, 0, 0, 1, 0, 0, 0, 1),
158            Dir2::NegX => Mat3::new(-1, 0, 0, 0, -1, 0, 0, 0, 1),
159            Dir2::Y => Mat3::new(0, -1, 0, 1, 0, 0, 0, 0, 1),
160            Dir2::NegY => Mat3::new(0, 1, 0, -1, 0, 0, 0, 0, 1),
161        }
162    }
163
164    /// Creates a matrix that tranforms an upwards facing vector to this
165    /// direction.
166    pub fn from_z_mat3(self) -> Mat3<i32> {
167        match self {
168            Dir2::X => Mat3::new(0, 0, -1, 0, 1, 0, 1, 0, 0),
169            Dir2::NegX => Mat3::new(0, 0, 1, 0, 1, 0, -1, 0, 0),
170            Dir2::Y => Mat3::new(1, 0, 0, 0, 0, -1, 0, 1, 0),
171            Dir2::NegY => Mat3::new(1, 0, 0, 0, 0, 1, 0, -1, 0),
172        }
173    }
174
175    /// Translates this direction to worldspace as if it was relative to the
176    /// other direction
177    #[must_use]
178    pub fn relative_to(self, other: Dir2) -> Dir2 {
179        match other {
180            Dir2::X => self,
181            Dir2::NegX => self.opposite(),
182            Dir2::Y => self.rotated_cw(),
183            Dir2::NegY => self.rotated_ccw(),
184        }
185    }
186
187    /// Is this direction parallel to x
188    pub fn is_x(self) -> bool { matches!(self, Dir2::X | Dir2::NegX) }
189
190    /// Is this direction parallel to y
191    pub fn is_y(self) -> bool { matches!(self, Dir2::Y | Dir2::NegY) }
192
193    pub fn is_positive(self) -> bool { matches!(self, Dir2::X | Dir2::Y) }
194
195    pub fn is_negative(self) -> bool { !self.is_positive() }
196
197    /// Returns the component that the direction is parallell to
198    pub fn select(self, vec: impl Into<Vec2<i32>>) -> i32 {
199        let vec = vec.into();
200        match self {
201            Dir2::X | Dir2::NegX => vec.x,
202            Dir2::Y | Dir2::NegY => vec.y,
203        }
204    }
205
206    /// Select one component the direction is parallel to from vec and select
207    /// the other component from other
208    pub fn select_with(self, vec: impl Into<Vec2<i32>>, other: impl Into<Vec2<i32>>) -> Vec2<i32> {
209        let vec = vec.into();
210        let other = other.into();
211        match self {
212            Dir2::X | Dir2::NegX => Vec2::new(vec.x, other.y),
213            Dir2::Y | Dir2::NegY => Vec2::new(other.x, vec.y),
214        }
215    }
216
217    /// Returns the side of an aabr that the direction is pointing to
218    pub fn select_aabr<T>(self, aabr: Aabr<T>) -> T {
219        match self {
220            Dir2::X => aabr.max.x,
221            Dir2::NegX => aabr.min.x,
222            Dir2::Y => aabr.max.y,
223            Dir2::NegY => aabr.min.y,
224        }
225    }
226
227    /// Select one component from the side the direction is pointing to from
228    /// aabr and select the other component from other
229    pub fn select_aabr_with<T>(self, aabr: Aabr<T>, other: impl Into<Vec2<T>>) -> Vec2<T> {
230        let other = other.into();
231        match self {
232            Dir2::X => Vec2::new(aabr.max.x, other.y),
233            Dir2::NegX => Vec2::new(aabr.min.x, other.y),
234            Dir2::Y => Vec2::new(other.x, aabr.max.y),
235            Dir2::NegY => Vec2::new(other.x, aabr.min.y),
236        }
237    }
238
239    /// The equivelant sprite direction of the direction
240    pub fn sprite_ori(self) -> u8 {
241        match self {
242            Dir2::X => 0,
243            Dir2::Y => 2,
244            Dir2::NegX => 4,
245            Dir2::NegY => 6,
246        }
247    }
248
249    /// Returns (Dir, rest)
250    ///
251    /// Returns None if `ori` isn't a valid sprite Ori.
252    pub fn from_sprite_ori(ori: u8) -> Option<(Dir2, u8)> {
253        let dir = match ori / 2 {
254            0 => Dir2::X,
255            1 => Dir2::Y,
256            2 => Dir2::NegX,
257            3 => Dir2::NegY,
258            _ => return None,
259        };
260        let rest = ori % 2;
261
262        Some((dir, rest))
263    }
264
265    /// Legacy version of `sprite_ori`, so prefer using that over this.
266    pub fn sprite_ori_legacy(self) -> u8 {
267        match self {
268            Dir2::X => 2,
269            Dir2::NegX => 6,
270            Dir2::Y => 4,
271            Dir2::NegY => 0,
272        }
273    }
274
275    pub fn split_aabr_offset<T>(self, aabr: Aabr<T>, offset: T) -> [Aabr<T>; 2]
276    where
277        T: Copy + PartialOrd + Add<T, Output = T> + Sub<T, Output = T>,
278    {
279        match self {
280            Dir2::X => aabr.split_at_x(aabr.min.x + offset),
281            Dir2::Y => aabr.split_at_y(aabr.min.y + offset),
282            Dir2::NegX => {
283                let res = aabr.split_at_x(aabr.max.x - offset);
284                [res[1], res[0]]
285            },
286            Dir2::NegY => {
287                let res = aabr.split_at_y(aabr.max.y - offset);
288                [res[1], res[0]]
289            },
290        }
291    }
292
293    pub fn trim_aabr(self, aabr: Aabr<i32>, amount: i32) -> Aabr<i32> {
294        (-self).extend_aabr(aabr, -amount)
295    }
296
297    pub fn extend_aabr(self, aabr: Aabr<i32>, amount: i32) -> Aabr<i32> {
298        let offset = self.to_vec2() * amount;
299        match self {
300            _ if self.is_positive() => Aabr {
301                min: aabr.min,
302                max: aabr.max + offset,
303            },
304            _ => Aabr {
305                min: aabr.min + offset,
306                max: aabr.max,
307            },
308        }
309    }
310}
311
312impl std::ops::Neg for Dir2 {
313    type Output = Dir2;
314
315    fn neg(self) -> Self::Output { self.opposite() }
316}
317
318/// A 3d direction.
319#[derive(Debug, enum_map::Enum, strum::EnumIter, enumset::EnumSetType)]
320pub enum Dir3 {
321    X,
322    Y,
323    Z,
324    NegX,
325    NegY,
326    NegZ,
327}
328
329impl Dir3 {
330    pub const ALL: [Dir2; 4] = [Dir2::X, Dir2::Y, Dir2::NegX, Dir2::NegY];
331
332    pub fn choose(rng: &mut impl RngExt) -> Dir3 {
333        match rng.random_range(0..6) {
334            0 => Dir3::X,
335            1 => Dir3::Y,
336            2 => Dir3::Z,
337            3 => Dir3::NegX,
338            4 => Dir3::NegY,
339            _ => Dir3::NegZ,
340        }
341    }
342
343    pub fn from_dir(dir: Dir2) -> Dir3 {
344        match dir {
345            Dir2::X => Dir3::X,
346            Dir2::Y => Dir3::Y,
347            Dir2::NegX => Dir3::NegX,
348            Dir2::NegY => Dir3::NegY,
349        }
350    }
351
352    pub fn to_dir(self) -> Option<Dir2> {
353        match self {
354            Dir3::X => Some(Dir2::X),
355            Dir3::Y => Some(Dir2::Y),
356            Dir3::NegX => Some(Dir2::NegX),
357            Dir3::NegY => Some(Dir2::NegY),
358            _ => None,
359        }
360    }
361
362    pub fn from_vec3(vec: Vec3<i32>) -> Dir3 {
363        if vec.x.abs() > vec.y.abs() && vec.x.abs() > vec.z.abs() {
364            if vec.x > 0 { Dir3::X } else { Dir3::NegX }
365        } else if vec.y.abs() > vec.z.abs() {
366            if vec.y > 0 { Dir3::Y } else { Dir3::NegY }
367        } else if vec.z > 0 {
368            Dir3::Z
369        } else {
370            Dir3::NegZ
371        }
372    }
373
374    #[must_use]
375    pub fn opposite(self) -> Dir3 {
376        match self {
377            Dir3::X => Dir3::NegX,
378            Dir3::NegX => Dir3::X,
379            Dir3::Y => Dir3::NegY,
380            Dir3::NegY => Dir3::Y,
381            Dir3::Z => Dir3::NegZ,
382            Dir3::NegZ => Dir3::Z,
383        }
384    }
385
386    /// Rotate counter clockwise around an axis by 90 degrees.
387    pub fn rotate_axis_ccw(self, axis: Dir3) -> Dir3 {
388        match axis {
389            Dir3::X | Dir3::NegX => match self {
390                Dir3::Y => Dir3::Z,
391                Dir3::NegY => Dir3::NegZ,
392                Dir3::Z => Dir3::NegY,
393                Dir3::NegZ => Dir3::Y,
394                x => x,
395            },
396            Dir3::Y | Dir3::NegY => match self {
397                Dir3::X => Dir3::Z,
398                Dir3::NegX => Dir3::NegZ,
399                Dir3::Z => Dir3::NegX,
400                Dir3::NegZ => Dir3::X,
401                y => y,
402            },
403            Dir3::Z | Dir3::NegZ => match self {
404                Dir3::X => Dir3::Y,
405                Dir3::NegX => Dir3::NegY,
406                Dir3::Y => Dir3::NegX,
407                Dir3::NegY => Dir3::X,
408                z => z,
409            },
410        }
411    }
412
413    /// Rotate clockwise around an axis by 90 degrees.
414    pub fn rotate_axis_cw(self, axis: Dir3) -> Dir3 { self.rotate_axis_ccw(axis).opposite() }
415
416    /// Get a direction that is orthogonal to both directions, always a positive
417    /// direction.
418    pub fn cross(self, other: Dir3) -> Dir3 {
419        match (self, other) {
420            (Dir3::X | Dir3::NegX, Dir3::Y | Dir3::NegY)
421            | (Dir3::Y | Dir3::NegY, Dir3::X | Dir3::NegX) => Dir3::Z,
422            (Dir3::X | Dir3::NegX, Dir3::Z | Dir3::NegZ)
423            | (Dir3::Z | Dir3::NegZ, Dir3::X | Dir3::NegX) => Dir3::Y,
424            (Dir3::Z | Dir3::NegZ, Dir3::Y | Dir3::NegY)
425            | (Dir3::Y | Dir3::NegY, Dir3::Z | Dir3::NegZ) => Dir3::X,
426            (Dir3::X | Dir3::NegX, Dir3::X | Dir3::NegX) => Dir3::Y,
427            (Dir3::Y | Dir3::NegY, Dir3::Y | Dir3::NegY) => Dir3::X,
428            (Dir3::Z | Dir3::NegZ, Dir3::Z | Dir3::NegZ) => Dir3::Y,
429        }
430    }
431
432    #[must_use]
433    pub fn abs(self) -> Dir3 {
434        match self {
435            Dir3::X | Dir3::NegX => Dir3::X,
436            Dir3::Y | Dir3::NegY => Dir3::Y,
437            Dir3::Z | Dir3::NegZ => Dir3::Z,
438        }
439    }
440
441    #[must_use]
442    pub fn signum(self) -> i32 {
443        match self {
444            Dir3::X | Dir3::Y | Dir3::Z => 1,
445            Dir3::NegX | Dir3::NegY | Dir3::NegZ => -1,
446        }
447    }
448
449    pub fn to_vec3(self) -> Vec3<i32> {
450        match self {
451            Dir3::X => Vec3::new(1, 0, 0),
452            Dir3::NegX => Vec3::new(-1, 0, 0),
453            Dir3::Y => Vec3::new(0, 1, 0),
454            Dir3::NegY => Vec3::new(0, -1, 0),
455            Dir3::Z => Vec3::new(0, 0, 1),
456            Dir3::NegZ => Vec3::new(0, 0, -1),
457        }
458    }
459
460    /// Is this direction parallel to x
461    pub fn is_x(self) -> bool { matches!(self, Dir3::X | Dir3::NegX) }
462
463    /// Is this direction parallel to y
464    pub fn is_y(self) -> bool { matches!(self, Dir3::Y | Dir3::NegY) }
465
466    /// Is this direction parallel to z
467    pub fn is_z(self) -> bool { matches!(self, Dir3::Z | Dir3::NegZ) }
468
469    pub fn is_positive(self) -> bool { matches!(self, Dir3::X | Dir3::Y | Dir3::Z) }
470
471    pub fn is_negative(self) -> bool { !self.is_positive() }
472
473    /// Returns the component that the direction is parallell to
474    pub fn select(self, vec: impl Into<Vec3<i32>>) -> i32 {
475        let vec = vec.into();
476        match self {
477            Dir3::X | Dir3::NegX => vec.x,
478            Dir3::Y | Dir3::NegY => vec.y,
479            Dir3::Z | Dir3::NegZ => vec.z,
480        }
481    }
482
483    /// Select one component the direction is parallel to from vec and select
484    /// the other components from other
485    pub fn select_with(self, vec: impl Into<Vec3<i32>>, other: impl Into<Vec3<i32>>) -> Vec3<i32> {
486        let vec = vec.into();
487        let other = other.into();
488        match self {
489            Dir3::X | Dir3::NegX => Vec3::new(vec.x, other.y, other.z),
490            Dir3::Y | Dir3::NegY => Vec3::new(other.x, vec.y, other.z),
491            Dir3::Z | Dir3::NegZ => Vec3::new(other.x, other.y, vec.z),
492        }
493    }
494
495    /// Returns the side of an aabb that the direction is pointing to
496    pub fn select_aabb<T>(self, aabb: Aabb<T>) -> T {
497        match self {
498            Dir3::X => aabb.max.x,
499            Dir3::NegX => aabb.min.x,
500            Dir3::Y => aabb.max.y,
501            Dir3::NegY => aabb.min.y,
502            Dir3::Z => aabb.max.z,
503            Dir3::NegZ => aabb.min.z,
504        }
505    }
506
507    /// Select one component from the side the direction is pointing to from
508    /// aabr and select the other components from other
509    pub fn select_aabb_with<T>(self, aabb: Aabb<T>, other: impl Into<Vec3<T>>) -> Vec3<T> {
510        let other = other.into();
511        match self {
512            Dir3::X => Vec3::new(aabb.max.x, other.y, other.z),
513            Dir3::NegX => Vec3::new(aabb.min.x, other.y, other.z),
514            Dir3::Y => Vec3::new(other.x, aabb.max.y, other.z),
515            Dir3::NegY => Vec3::new(other.x, aabb.min.y, other.z),
516            Dir3::Z => Vec3::new(other.x, other.y, aabb.max.z),
517            Dir3::NegZ => Vec3::new(other.x, other.y, aabb.min.z),
518        }
519    }
520
521    pub fn split_aabb_offset<T>(self, aabb: Aabb<T>, offset: T) -> [Aabb<T>; 2]
522    where
523        T: Copy + PartialOrd + Add<T, Output = T> + Sub<T, Output = T>,
524    {
525        match self {
526            Dir3::X => aabb.split_at_x(aabb.min.x + offset),
527            Dir3::NegX => {
528                let res = aabb.split_at_x(aabb.max.x - offset);
529                [res[1], res[0]]
530            },
531            Dir3::Y => aabb.split_at_y(aabb.min.y + offset),
532            Dir3::NegY => {
533                let res = aabb.split_at_y(aabb.max.y - offset);
534                [res[1], res[0]]
535            },
536            Dir3::Z => aabb.split_at_z(aabb.min.z + offset),
537            Dir3::NegZ => {
538                let res = aabb.split_at_z(aabb.max.z - offset);
539                [res[1], res[0]]
540            },
541        }
542    }
543
544    pub fn trim_aabb(self, aabb: Aabb<i32>, amount: i32) -> Aabb<i32> {
545        (-self).extend_aabb(aabb, -amount)
546    }
547
548    pub fn extend_aabb(self, aabb: Aabb<i32>, amount: i32) -> Aabb<i32> {
549        let offset = self.to_vec3() * amount;
550        match self {
551            _ if self.is_positive() => Aabb {
552                min: aabb.min,
553                max: aabb.max + offset,
554            },
555            _ => Aabb {
556                min: aabb.min + offset,
557                max: aabb.max,
558            },
559        }
560    }
561}
562impl std::ops::Neg for Dir3 {
563    type Output = Dir3;
564
565    fn neg(self) -> Self::Output { self.opposite() }
566}