utils/
geometry.rs

1//! 2D, 3D and 4D vector implementations.
2
3use crate::number::{Integer, Number, Signed, UnsignedInteger};
4use std::fmt::Debug;
5use std::ops::{Add, AddAssign, Mul, MulAssign, Not, Sub, SubAssign};
6
7macro_rules! vec_impl {
8    ($n:literal, $tuple:tt =>
9        $(#[$m:meta])* $v:vis struct $s:ident{$($i:tt => $f:ident),+}
10    ) => {
11        #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
12        $(#[$m])* $v struct $s<T: Number> {
13            $(pub $f: T,)+
14        }
15
16        impl<T: Number> $s<T> {
17            pub const ORIGIN: Self = Self{$($f: T::ZERO),+};
18
19            #[inline]
20            #[must_use]
21            pub const fn new($($f: T),+) -> Self {
22                Self{$($f),+}
23            }
24
25            /// Returns a vector with all components set to the provided value.
26            #[inline]
27            #[must_use]
28            pub const fn splat(v: T) -> Self {
29                Self{$($f: v),+}
30            }
31
32            /// Returns the manhattan distance from the origin.
33            #[inline]
34            #[must_use]
35            pub fn manhattan_distance(self) -> T::Unsigned
36            where
37                T: Integer
38            {
39                T::Unsigned::ZERO $(+ self.$f.unsigned_abs())+
40            }
41
42            /// Returns the manhattan distance to the specified point.
43            #[inline]
44            #[must_use]
45            pub fn manhattan_distance_to(self, rhs: Self) -> T::Unsigned
46            where
47                T: Integer
48            {
49                T::Unsigned::ZERO $(+ self.$f.abs_diff(rhs.$f))+
50            }
51
52            /// Returns the manhattan distance to the specified axis-aligned bounding box.
53            #[inline]
54            #[must_use]
55            pub fn manhattan_distance_to_aabb(self, min: Self, max: Self) -> T::Unsigned
56            where
57                T: Integer
58            {
59                T::Unsigned::ZERO $(
60                    + min.$f.saturating_sub_0(self.$f)
61                    + self.$f.saturating_sub_0(max.$f)
62                )+
63            }
64
65            /// Add the provided signed vector, wrapping on overflow.
66            ///
67            /// Useful for adding a signed direction onto an unsigned position.
68            #[inline]
69            #[must_use]
70            pub fn wrapping_add_signed(self, rhs: $s<T::Signed>) -> Self
71            where
72                T: UnsignedInteger,
73            {
74                Self{
75                    $($f: self.$f.wrapping_add_signed(rhs.$f),)+
76                }
77            }
78        }
79
80        impl<T: Number> Add for $s<T> {
81            type Output = Self;
82
83            #[inline]
84            fn add(self, rhs: Self) -> Self {
85                Self{
86                    $($f: self.$f + rhs.$f,)+
87                }
88            }
89        }
90
91        impl<T: Number> AddAssign for $s<T> {
92            #[inline]
93            fn add_assign(&mut self, rhs: Self) {
94                $(self.$f += rhs.$f;)+
95            }
96        }
97
98        impl<T: Number> Mul<T> for $s<T> {
99            type Output = Self;
100
101            #[inline]
102            fn mul(self, rhs: T) -> Self {
103                Self{
104                    $($f: self.$f * rhs,)+
105                }
106            }
107        }
108
109        impl<T: Number> MulAssign<T> for $s<T> {
110            #[inline]
111            fn mul_assign(&mut self, rhs: T) {
112                $(self.$f *= rhs;)+
113            }
114        }
115
116        impl<T: Number> Sub for $s<T> {
117            type Output = Self;
118
119            #[inline]
120            fn sub(self, rhs: Self) -> Self {
121                Self{
122                    $($f: self.$f - rhs.$f,)+
123                }
124            }
125        }
126
127        impl<T: Number> SubAssign for $s<T> {
128            #[inline]
129            fn sub_assign(&mut self, rhs: Self) {
130                $(self.$f -= rhs.$f;)+
131            }
132        }
133
134        impl<T: Number> From<[T; $n]> for $s<T> {
135            #[inline]
136            fn from(arr: [T; $n]) -> Self {
137                Self{$(
138                    $f: arr[$i],
139                )+}
140            }
141        }
142
143        impl<T: Number> From<$tuple> for $s<T> {
144            #[inline]
145            fn from(arr: $tuple) -> Self {
146                Self{$(
147                    $f: arr.$i,
148                )+}
149            }
150        }
151
152        impl<T: Number> From<$s<T>> for [T; $n] {
153            #[inline]
154            fn from(value: $s<T>) -> Self {
155                [$(value.$f),+]
156            }
157        }
158
159        impl<T: Number> From<$s<T>> for $tuple {
160            #[inline]
161            fn from(value: $s<T>) -> Self {
162                ($(value.$f),+)
163            }
164        }
165    };
166}
167
168vec_impl! {2, (T, T) =>
169    /// Struct representing a 2D vector or point.
170    #[doc(alias("Vector", "Vector2", "Point", "Point2", "Point2D"))]
171    pub struct Vec2{0 => x, 1 => y}
172}
173
174impl<T: Signed> Vec2<T> {
175    pub const UP: Self = Self {
176        x: T::ZERO,
177        y: T::ONE,
178    };
179    pub const RIGHT: Self = Self {
180        x: T::ONE,
181        y: T::ZERO,
182    };
183    pub const DOWN: Self = Self {
184        x: T::ZERO,
185        y: T::MINUS_ONE,
186    };
187    pub const LEFT: Self = Self {
188        x: T::MINUS_ONE,
189        y: T::ZERO,
190    };
191    pub const DIRECTIONS: [Self; 4] = [Self::UP, Self::RIGHT, Self::DOWN, Self::LEFT];
192
193    /// Rotate this vector 90 degrees clockwise.
194    #[inline]
195    #[must_use]
196    pub fn turn_right(self) -> Self {
197        Self {
198            x: self.y,
199            y: -self.x,
200        }
201    }
202
203    /// Rotate this vector 90 degrees counterclockwise.
204    #[inline]
205    #[must_use]
206    pub fn turn_left(self) -> Self {
207        Self {
208            x: -self.y,
209            y: self.x,
210        }
211    }
212}
213
214vec_impl! {3, (T, T, T) =>
215    /// Struct representing a 3D vector or point.
216    #[doc(alias("Vector3", "Point3", "Point3D"))]
217    pub struct Vec3{0 => x, 1 => y, 2 => z}
218}
219
220vec_impl! {4, (T, T, T, T) =>
221    /// Struct representing a 4D vector or point.
222    #[doc(alias("Vector4", "Point4", "Point4D"))]
223    pub struct Vec4{0 => x, 1 => y, 2 => z, 3 => w}
224}
225
226/// Enum representing the four cardinal directions.
227#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
228#[repr(u8)]
229pub enum Direction {
230    #[default]
231    Up = 0,
232    Right,
233    Down,
234    Left,
235}
236
237impl Direction {
238    /// Rotate this direction by the provided turn.
239    #[inline]
240    #[must_use]
241    pub fn turn(self, turn: Turn) -> Self {
242        Self::from((self as u8).wrapping_add_signed(turn as i8))
243    }
244
245    /// Rotate this direction 90 degrees anticlockwise.
246    #[inline]
247    #[must_use]
248    pub fn turn_left(self) -> Self {
249        self.turn(Turn::Left)
250    }
251
252    /// Rotate this direction 90 degrees clockwise.
253    #[inline]
254    #[must_use]
255    pub fn turn_right(self) -> Self {
256        self.turn(Turn::Right)
257    }
258}
259
260impl From<u8> for Direction {
261    #[inline]
262    fn from(value: u8) -> Self {
263        match value % 4 {
264            0 => Direction::Up,
265            1 => Direction::Right,
266            2 => Direction::Down,
267            3 => Direction::Left,
268            _ => unreachable!(),
269        }
270    }
271}
272
273impl Not for Direction {
274    type Output = Self;
275
276    #[inline]
277    fn not(self) -> Self::Output {
278        Self::from((self as u8).wrapping_add(2))
279    }
280}
281
282impl<T: Signed> From<Direction> for Vec2<T> {
283    #[inline]
284    fn from(value: Direction) -> Self {
285        Vec2::DIRECTIONS[value as usize]
286    }
287}
288
289impl<T: Signed> Add<Direction> for Vec2<T> {
290    type Output = Self;
291
292    #[inline]
293    fn add(self, rhs: Direction) -> Self::Output {
294        self + Vec2::from(rhs)
295    }
296}
297
298impl<T: Signed> Sub<Direction> for Vec2<T> {
299    type Output = Self;
300
301    #[inline]
302    fn sub(self, rhs: Direction) -> Self::Output {
303        self - Vec2::from(rhs)
304    }
305}
306
307/// Enum representing possible turns.
308#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
309#[repr(i8)]
310pub enum Turn {
311    #[doc(alias("Anticlockwise"))]
312    Left = -1,
313    #[doc(alias("Straight"))]
314    #[default]
315    None = 0,
316    #[doc(alias("Clockwise"))]
317    Right = 1,
318}