Skip to main content

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