Skip to main content

pax_runtime_api/math/
vector.rs

1use std::{
2    f64::consts::PI,
3    marker::PhantomData,
4    ops::{Add, Div, Mul, Neg, Sub},
5};
6
7use crate::{Interpolatable, Numeric, Rotation};
8
9use super::{Generic, Point2, Space};
10
11/// A representation of a vector in 2D space.
12pub struct Vector2<W = Generic> {
13    /// Horizontal component.
14    pub x: f64,
15    /// Vertical component.
16    pub y: f64,
17    _phantom: PhantomData<W>,
18}
19
20// Implement Clone, Copy, PartialEq, etc manually, as
21// to not require the Space to implement these.
22impl<W: Space> Interpolatable for Vector2<W> {}
23
24impl<W: Space> std::fmt::Debug for Vector2<W> {
25    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26        write!(f, "<{} {}>", self.x, self.y)
27    }
28}
29
30impl<W: Space> Clone for Vector2<W> {
31    fn clone(&self) -> Self {
32        Self {
33            x: self.x,
34            y: self.y,
35            _phantom: PhantomData,
36        }
37    }
38}
39
40impl<W: Space> Copy for Vector2<W> {}
41
42impl<W: Space> PartialEq for Vector2<W> {
43    fn eq(&self, other: &Self) -> bool {
44        self.x == other.x && self.y == other.y
45    }
46}
47
48impl<W: Space> Default for Vector2<W> {
49    fn default() -> Self {
50        Self::new(0.0, 0.0)
51    }
52}
53
54impl<W: Space> Vector2<W> {
55    /// Constructs a vector from x and y components.
56    pub fn new(x: f64, y: f64) -> Self {
57        Self {
58            x,
59            y,
60            _phantom: PhantomData,
61        }
62    }
63
64    /// Unit vector along the x axis.
65    pub fn x() -> Self {
66        Self::new(1.0, 0.0)
67    }
68
69    /// Unit vector along the y axis.
70    pub fn y() -> Self {
71        Self::new(0.0, 1.0)
72    }
73
74    /// Returns the left-handed normal.
75    pub fn normal(&self) -> Self {
76        Self::new(-self.y, self.x)
77    }
78
79    /// Returns this vector scaled to unit length.
80    pub fn normalize(self) -> Self {
81        self / self.length()
82    }
83
84    /// Returns squared vector length.
85    pub fn length_squared(&self) -> f64 {
86        self.x * self.x + self.y * self.y
87    }
88
89    /// Returns vector length.
90    pub fn length(&self) -> f64 {
91        self.length_squared().sqrt()
92    }
93
94    /// Returns a vector with absolute component values.
95    pub fn coord_abs(&self) -> Self {
96        Self::new(self.x.abs(), self.y.abs())
97    }
98
99    /// Projects this vector onto `axis`.
100    pub fn project_onto(self, axis: Self) -> Self {
101        let dot_product = self * axis;
102        axis * dot_product / axis.length_squared()
103    }
104
105    // Projects this vector onto an axis-aligned frame described by `other`.
106    pub fn project_axis_aligned(self, other: Self) -> Self {
107        let v = self.coord_abs();
108        let o = other.coord_abs().normalize();
109        o.to_signums_of(self) * (v.x / o.x).max(v.y / o.y)
110    }
111
112    /// Returns the counter-clockwise angle from this vector to `other`.
113    pub fn angle_to(self, other: Self) -> Rotation {
114        let dot = self.x * other.x + self.y * other.y; //Dot product between [x1, y1] and [x2, y2]
115        let det = self.x * other.y - self.y * other.x; //Determinant
116        let angle = det.atan2(dot).rem_euclid(2.0 * PI); //atan2(y, x) or atan2(sin, cos)
117        Rotation::Radians(Numeric::from(angle))
118    }
119
120    /// Returns the z component of the 3D cross product, assuming both vectors have z value 0.0.
121    pub fn cross(self, other: Self) -> f64 {
122        self.x * other.y - self.y * other.x
123    }
124
125    /// Copies this vector's magnitudes onto the component signs of `other`.
126    pub fn to_signums_of(&self, other: Self) -> Self {
127        Self::new(
128            self.x.abs() * other.x.signum(),
129            self.y.abs() * other.y.signum(),
130        )
131    }
132
133    /// Reinterprets this vector as a point.
134    pub fn to_point(&self) -> Point2<W> {
135        Point2::new(self.x, self.y)
136    }
137
138    /// Casts this vector into another phantom coordinate space.
139    pub fn cast_space<WNew: Space>(&self) -> Vector2<WNew> {
140        Vector2::new(self.x, self.y)
141    }
142
143    /// Rotates this vector by `angle`.
144    pub fn rotate(&self, angle: Rotation) -> Self {
145        let (s, c) = angle.get_as_radians().sin_cos();
146        let x = self.x * c - self.y * s;
147        let y = self.x * s + self.y * c;
148        Self::new(x, y)
149    }
150
151    /// Rotates this vector 90 degrees clockwise.
152    pub fn rotate90(self) -> Self {
153        Self::new(self.y, -self.x)
154    }
155
156    /// Multiplies component-wise by `other`.
157    pub fn mult(&self, other: Self) -> Vector2<W> {
158        Vector2::new(self.x * other.x, self.y * other.y)
159    }
160}
161
162impl<W: Space> Mul for Vector2<W> {
163    type Output = f64;
164
165    fn mul(self, rhs: Vector2<W>) -> Self::Output {
166        self.x * rhs.x + self.y * rhs.y
167    }
168}
169
170impl<W: Space> Mul<f64> for Vector2<W> {
171    type Output = Self;
172
173    fn mul(self, rhs: f64) -> Self::Output {
174        Vector2::new(self.x * rhs, self.y * rhs)
175    }
176}
177impl<W: Space> Mul<Vector2<W>> for f64 {
178    type Output = Vector2<W>;
179
180    fn mul(self, rhs: Vector2<W>) -> Self::Output {
181        Vector2::new(rhs.x * self, rhs.y * self)
182    }
183}
184
185impl<W: Space> Add for Vector2<W> {
186    type Output = Vector2<W>;
187
188    fn add(self, rhs: Vector2<W>) -> Self::Output {
189        Self::Output::new(self.x + rhs.x, self.y + rhs.y)
190    }
191}
192
193impl<W: Space> Neg for Vector2<W> {
194    type Output = Vector2<W>;
195
196    fn neg(self) -> Self::Output {
197        Self::Output::new(-self.x, -self.y)
198    }
199}
200
201impl<W: Space> Sub for Vector2<W> {
202    type Output = Vector2<W>;
203    fn sub(self, rhs: Vector2<W>) -> Self::Output {
204        Self::Output::new(self.x - rhs.x, self.y - rhs.y)
205    }
206}
207
208impl<W: Space> Sub<f64> for Vector2<W> {
209    type Output = Vector2<W>;
210    fn sub(self, rhs: f64) -> Self::Output {
211        Self::Output::new(self.x - rhs, self.y - rhs)
212    }
213}
214
215impl<W: Space> Add<f64> for Vector2<W> {
216    type Output = Vector2<W>;
217    fn add(self, rhs: f64) -> Self::Output {
218        Self::Output::new(self.x + rhs, self.y + rhs)
219    }
220}
221
222impl<W: Space> Div<f64> for Vector2<W> {
223    type Output = Vector2<W>;
224    fn div(self, rhs: f64) -> Self::Output {
225        Self::Output::new(self.x / rhs, self.y / rhs)
226    }
227}
228
229impl<W: Space> Div for Vector2<W> {
230    type Output = Vector2<W>;
231    fn div(self, rhs: Vector2<W>) -> Self::Output {
232        Self::Output::new(self.x / rhs.x, self.y / rhs.y)
233    }
234}