cat_box/
vec2.rs

1//! Types representing directions and locations in 2d and 3d space.
2//!
3//!
4//! This module contains 3 major types:
5//!  - [`Vec2`], a 2d float vector
6//!  - [`Vec2Int`], a 2d integer vector
7//!  - [`Direction`], a 2d cardinal direction
8//!
9//! All the types implement the expected [`From`]s and all the relevant operator traits.
10
11use std::{
12    fmt::Debug,
13    ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign},
14};
15
16use sdl2::rect::Point;
17
18// Direction
19/// A cardinal direction in a 2d plane.
20///
21/// Conversions to a [`Vec2`] or [`Vec2Int`] assume that East is positive-x and South is positive-y.
22#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
23pub enum Direction {
24    /// North, or Vec2::from((0, -1))
25    North,
26    /// North, or Vec2::from((0, 1))
27    South,
28    /// North, or Vec2::from((1, 0))
29    East,
30    /// North, or Vec2::from((-1, 0))
31    West,
32}
33
34#[allow(clippy::enum_glob_use)]
35impl Direction {
36    /// Flips this `Direction` around both the x- and y-axes.
37    #[must_use]
38    pub fn flipped(self) -> Self {
39        self.flip_x().flip_y()
40    }
41
42    /// Flips this `Direction` around the x-axis.
43    #[must_use]
44    pub fn flip_x(self) -> Self {
45        use Direction::*;
46        match self {
47            East => West,
48            West => East,
49            v => v,
50        }
51    }
52
53    /// Flips this `Direction` around the y-axis.
54    #[must_use]
55    pub fn flip_y(self) -> Self {
56        use Direction::*;
57        match self {
58            North => South,
59            South => North,
60            v => v,
61        }
62    }
63}
64
65// ...and related op impls
66impl Neg for Direction {
67    type Output = Self;
68
69    fn neg(self) -> Self::Output {
70        self.flipped()
71    }
72}
73
74#[allow(clippy::enum_glob_use)]
75impl From<Direction> for Vec2 {
76    fn from(v: Direction) -> Self {
77        use Direction::*;
78        match v {
79            North => (0.0, -1.0).into(),
80            South => (0.0, 1.0).into(),
81            East => (1.0, 0.0).into(),
82            West => (-1.0, 0.0).into(),
83        }
84    }
85}
86
87#[allow(clippy::enum_glob_use)]
88impl From<Direction> for Vec2Int {
89    fn from(v: Direction) -> Self {
90        use Direction::*;
91        match v {
92            North => (0, -1).into(),
93            South => (0, 1).into(),
94            East => (1, 0).into(),
95            West => (-1, 0).into(),
96        }
97    }
98}
99
100impl From<Point> for Vec2 {
101    fn from(p: Point) -> Self {
102        let x: (i32, i32) = p.into();
103        x.into()
104    }
105}
106
107impl From<Point> for Vec2Int {
108    fn from(p: Point) -> Self {
109        let x: (i32, i32) = p.into();
110        x.into()
111    }
112}
113
114impl Mul<f32> for Direction {
115    type Output = Vec2;
116
117    fn mul(self, rhs: f32) -> Self::Output {
118        Vec2::from(self) * rhs
119    }
120}
121
122impl Mul<i32> for Direction {
123    type Output = Vec2Int;
124
125    fn mul(self, rhs: i32) -> Self::Output {
126        Vec2Int::from(self) * rhs
127    }
128}
129
130// Vec2
131/// A set of 2 [`f32`]s representing a location or direction in the 2d plane.
132#[derive(Clone, Copy, Default, PartialEq)]
133pub struct Vec2 {
134    /// The x component of the vector.
135    pub x: f32,
136    /// The y component of the vector.
137    pub y: f32,
138}
139
140impl Debug for Vec2 {
141    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
142        f.debug_tuple("Vec2").field(&self.x).field(&self.y).finish()
143    }
144}
145
146impl Vec2 {
147    /// Creates a new `Vec2` with the given x- and y-values.
148    ///
149    /// It is often simpler, and preferred, to just write `(x, y).into()`.
150    #[must_use]
151    pub const fn new(x: f32, y: f32) -> Vec2 {
152        Self { x, y }
153    }
154
155    /// Gets the squared magnitude of the vector.
156    ///
157    /// Useful for comparisons as it is faster to calculate than `magnitude`.
158    #[must_use]
159    pub fn sq_magnitude(self) -> f32 {
160        self.x * self.x + self.y * self.y
161    }
162
163    /// Gets the magnitude of the vector.
164    #[must_use]
165    pub fn magnitude(self) -> f32 {
166        self.sq_magnitude().sqrt()
167    }
168
169    /// Gets the squared distance from this vector to `rhs`.
170    ///
171    /// Useful for comparisons as it is faster to calculate than `dist`.
172    #[must_use]
173    pub fn sq_dist(self, rhs: Self) -> f32 {
174        (self - rhs).sq_magnitude()
175    }
176
177    /// Gets the distance from this vector to `rhs`.
178    #[must_use]
179    pub fn dist(self, rhs: Self) -> f32 {
180        (self - rhs).magnitude()
181    }
182
183    /// Normalizes the vector, making its magnitude `1`.
184    #[must_use]
185    pub fn normalized(self) -> Self {
186        self / self.magnitude()
187    }
188
189    /// Rounds the vector to a [`Vec2Int`].
190    ///
191    /// This uses `as i32` under the hood, and as such comes with all the same unfortunate edge cases. Beware.
192    #[must_use]
193    pub fn rounded(self) -> Vec2Int {
194        #[allow(clippy::cast_possible_truncation)]
195        Vec2Int {
196            x: self.x as i32,
197            y: self.y as i32,
198        }
199    }
200}
201
202impl From<(i32, i32)> for Vec2 {
203    fn from(v: (i32, i32)) -> Self {
204        Vec2Int::from(v).to_f32()
205    }
206}
207
208impl From<(f32, f32)> for Vec2 {
209    fn from(v: (f32, f32)) -> Self {
210        Self { x: v.0, y: v.1 }
211    }
212}
213
214impl From<Vec2> for (f32, f32) {
215    fn from(v: Vec2) -> Self {
216        (v.x, v.y)
217    }
218}
219
220impl PartialEq<(i32, i32)> for Vec2 {
221    fn eq(&self, other: &(i32, i32)) -> bool {
222        self == &Self::from(*other)
223    }
224}
225
226impl PartialEq<(f32, f32)> for Vec2 {
227    fn eq(&self, other: &(f32, f32)) -> bool {
228        self == &Self::from(*other)
229    }
230}
231
232// ...and related op impls
233impl Neg for Vec2 {
234    type Output = Self;
235
236    fn neg(self) -> Self::Output {
237        self * -1.0
238    }
239}
240
241impl Add for Vec2 {
242    type Output = Self;
243
244    fn add(self, rhs: Self) -> Self::Output {
245        Self {
246            x: self.x + rhs.x,
247            y: self.y + rhs.y,
248        }
249    }
250}
251
252impl Add<Direction> for Vec2 {
253    type Output = Self;
254
255    fn add(self, rhs: Direction) -> Self::Output {
256        self + Self::from(rhs)
257    }
258}
259
260impl<T> AddAssign<T> for Vec2
261where
262    Vec2: Add<T, Output = Self>,
263{
264    fn add_assign(&mut self, rhs: T) {
265        *self = *self + rhs;
266    }
267}
268
269impl<T> Sub<T> for Vec2
270where
271    Vec2: Add<T, Output = Self>,
272{
273    type Output = Self;
274
275    fn sub(self, rhs: T) -> Self::Output {
276        -(-self + rhs)
277    }
278}
279
280impl<T> SubAssign<T> for Vec2
281where
282    Vec2: Sub<T, Output = Self>,
283{
284    fn sub_assign(&mut self, rhs: T) {
285        *self = *self - rhs;
286    }
287}
288
289impl Mul<f32> for Vec2 {
290    type Output = Self;
291
292    fn mul(self, rhs: f32) -> Self::Output {
293        Self {
294            x: self.x * rhs,
295            y: self.y * rhs,
296        }
297    }
298}
299
300impl Div<f32> for Vec2 {
301    type Output = Self;
302
303    fn div(self, rhs: f32) -> Self::Output {
304        Self {
305            x: self.x / rhs,
306            y: self.y / rhs,
307        }
308    }
309}
310
311impl MulAssign<f32> for Vec2 {
312    fn mul_assign(&mut self, rhs: f32) {
313        *self = *self * rhs;
314    }
315}
316
317impl DivAssign<f32> for Vec2 {
318    fn div_assign(&mut self, rhs: f32) {
319        *self = *self / rhs;
320    }
321}
322
323// Vec2Int
324/// A set of 2 [`i32`]s representing a location or direction in the 2d plane.
325#[derive(Clone, Copy, Default, PartialEq, Eq, Hash)]
326pub struct Vec2Int {
327    /// The x component of the vector.
328    pub x: i32,
329    /// The y component of the vector.
330    pub y: i32,
331}
332
333impl Debug for Vec2Int {
334    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
335        f.debug_tuple("Vec2Int")
336            .field(&self.x)
337            .field(&self.y)
338            .finish()
339    }
340}
341
342impl Vec2Int {
343    /// Creates a new `Vec2` with the given x- and y-values.
344    ///
345    /// It is often simpler, and preferred, to just write `(x, y).into()`.
346    #[must_use]
347    pub const fn new(x: i32, y: i32) -> Vec2Int {
348        Self { x, y }
349    }
350
351    /// Gets the squared magnitude of the vector.
352    ///
353    /// Useful for comparisons as it is faster to calculate than `magnitude`.
354    #[must_use]
355    pub fn sq_magnitude(self) -> i32 {
356        self.x * self.x + self.y * self.y
357    }
358
359    /// Gets the magnitude of the vector.
360    #[must_use]
361    pub fn magnitude(self) -> f32 {
362        #[allow(clippy::cast_precision_loss)]
363        (self.sq_magnitude() as f32).sqrt()
364    }
365
366    /// Gets the squared distance from this vector to `rhs`.
367    ///
368    /// Useful for comparisons as it is faster to calculate than `dist`.
369    #[must_use]
370    pub fn sq_dist(self, rhs: Self) -> i32 {
371        (self - rhs).sq_magnitude()
372    }
373
374    /// Gets the distance from this vector to `rhs`.
375    #[must_use]
376    pub fn dist(self, rhs: Self) -> f32 {
377        (self - rhs).magnitude()
378    }
379
380    /// Casts this vector to a [`Vec2`].
381    ///
382    /// This uses `as f32` under the hood, and as such comes with all the same unfortunate edge cases. Beware.
383    #[must_use]
384    pub fn to_f32(self) -> Vec2 {
385        #[allow(clippy::cast_precision_loss)]
386        Vec2 {
387            x: self.x as f32,
388            y: self.y as f32,
389        }
390    }
391}
392
393impl From<(i32, i32)> for Vec2Int {
394    fn from(v: (i32, i32)) -> Self {
395        Self { x: v.0, y: v.1 }
396    }
397}
398
399impl From<Vec2Int> for (i32, i32) {
400    fn from(v: Vec2Int) -> Self {
401        (v.x, v.y)
402    }
403}
404
405impl PartialEq<(i32, i32)> for Vec2Int {
406    fn eq(&self, other: &(i32, i32)) -> bool {
407        self == &Self::from(*other)
408    }
409}
410
411// ...and related op impls
412impl Neg for Vec2Int {
413    type Output = Self;
414
415    fn neg(self) -> Self::Output {
416        self * -1
417    }
418}
419
420impl Add for Vec2Int {
421    type Output = Self;
422
423    fn add(self, rhs: Self) -> Self::Output {
424        Self {
425            x: self.x + rhs.x,
426            y: self.y + rhs.y,
427        }
428    }
429}
430
431impl Add<Direction> for Vec2Int {
432    type Output = Self;
433
434    fn add(self, rhs: Direction) -> Self::Output {
435        self + Self::from(rhs)
436    }
437}
438
439impl<T> AddAssign<T> for Vec2Int
440where
441    Vec2Int: Add<T, Output = Self>,
442{
443    fn add_assign(&mut self, rhs: T) {
444        *self = *self + rhs;
445    }
446}
447
448impl<T> Sub<T> for Vec2Int
449where
450    Vec2Int: Add<T, Output = Self>,
451{
452    type Output = Self;
453
454    fn sub(self, rhs: T) -> Self::Output {
455        -(-self + rhs)
456    }
457}
458
459impl<T> SubAssign<T> for Vec2Int
460where
461    Vec2Int: Sub<T, Output = Self>,
462{
463    fn sub_assign(&mut self, rhs: T) {
464        *self = *self - rhs;
465    }
466}
467
468impl Mul<i32> for Vec2Int {
469    type Output = Self;
470
471    fn mul(self, rhs: i32) -> Self::Output {
472        Self {
473            x: self.x * rhs,
474            y: self.y * rhs,
475        }
476    }
477}
478
479impl Div<i32> for Vec2Int {
480    type Output = Self;
481
482    fn div(self, rhs: i32) -> Self::Output {
483        Self {
484            x: self.x / rhs,
485            y: self.y / rhs,
486        }
487    }
488}
489
490impl MulAssign<i32> for Vec2Int {
491    fn mul_assign(&mut self, rhs: i32) {
492        *self = *self * rhs;
493    }
494}
495
496impl DivAssign<i32> for Vec2Int {
497    fn div_assign(&mut self, rhs: i32) {
498        *self = *self / rhs;
499    }
500}