batbox_la/vec/
_2d.rs

1use super::*;
2
3/// 2 dimensional vector.
4#[allow(non_camel_case_types)]
5#[repr(C)]
6#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
7pub struct vec2<T>(pub T, pub T);
8
9impl<T: std::fmt::Display> std::fmt::Display for vec2<T> {
10    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
11        write!(fmt, "({}, {})", self.0, self.1)
12    }
13}
14
15impl<T> From<[T; 2]> for vec2<T> {
16    fn from(v: [T; 2]) -> vec2<T> {
17        let [x, y] = v;
18        vec2(x, y)
19    }
20}
21
22/// Data structure used to provide access to coordinates with the dot notation, e.g. `v.x`
23pub struct XY<T> {
24    #[allow(missing_docs)]
25    pub x: T,
26    #[allow(missing_docs)]
27    pub y: T,
28}
29
30impl<T> Deref for XY<T> {
31    type Target = [T; 2];
32    fn deref(&self) -> &[T; 2] {
33        unsafe { std::mem::transmute(self) }
34    }
35}
36
37impl<T> DerefMut for XY<T> {
38    fn deref_mut(&mut self) -> &mut [T; 2] {
39        unsafe { std::mem::transmute(self) }
40    }
41}
42
43impl<T> Deref for vec2<T> {
44    type Target = XY<T>;
45    fn deref(&self) -> &XY<T> {
46        unsafe { std::mem::transmute(self) }
47    }
48}
49
50impl<T> DerefMut for vec2<T> {
51    fn deref_mut(&mut self) -> &mut XY<T> {
52        unsafe { std::mem::transmute(self) }
53    }
54}
55
56impl<T> vec2<T> {
57    /// Extend into a 3-d vector.
58    ///
59    /// # Examples
60    /// ```
61    /// # use batbox_la::*;
62    /// assert_eq!(vec2(1, 2).extend(3), vec3(1, 2, 3));
63    /// ```
64    pub fn extend(self, z: T) -> vec3<T> {
65        vec3(self.0, self.1, z)
66    }
67
68    /// Map every component (coordinate)
69    pub fn map<U, F: Fn(T) -> U>(self, f: F) -> vec2<U> {
70        vec2(f(self.0), f(self.1))
71    }
72
73    /// Zip two vectors together
74    pub fn zip<U>(self, v: vec2<U>) -> vec2<(T, U)> {
75        vec2((self.0, v.0), (self.1, v.1))
76    }
77}
78
79impl<T: Clone> vec2<T> {
80    /// Construct a vector with all components set to specified value
81    pub fn splat(value: T) -> Self {
82        Self(value.clone(), value)
83    }
84}
85
86impl<T: UNum> vec2<T> {
87    /// A zero 2-d vector
88    pub const ZERO: Self = vec2(T::ZERO, T::ZERO);
89
90    /// A unit X
91    pub const UNIT_X: Self = Self(T::ONE, T::ZERO);
92
93    /// A unit Y
94    pub const UNIT_Y: Self = Self(T::ZERO, T::ONE);
95}
96
97impl<T: Num> vec2<T> {
98    /// Calculate dot product of two vectors.
99    ///
100    /// # Examples
101    /// ```
102    /// # use batbox_la::*;
103    /// assert_eq!(vec2::dot(vec2(1, 2), vec2(3, 4)), 11);
104    /// ```
105    pub fn dot(a: Self, b: Self) -> T {
106        a.x * b.x + a.y * b.y
107    }
108
109    /// Calculate skew product of two vectors.
110    ///
111    /// # Examples
112    /// ```
113    /// # use batbox_la::*;
114    /// assert_eq!(vec2::skew(vec2(1, 2), vec2(3, 4)), -2);
115    /// ```
116    pub fn skew(a: Self, b: Self) -> T {
117        a.x * b.y - a.y * b.x
118    }
119}
120
121impl<T: Neg<Output = T>> vec2<T> {
122    /// Rotate a vector by 90 degrees counter clockwise.
123    /// # Examples
124    /// ```
125    /// # use batbox_la::*;
126    /// let v = vec2(3.0, 4.0);
127    /// assert_eq!(v.rotate_90(), vec2(-4.0, 3.0));
128    /// ```
129    pub fn rotate_90(self) -> Self {
130        let vec2(x, y) = self;
131        vec2(-y, x)
132    }
133}
134
135impl<T: Float> vec2<T> {
136    /// Normalize a vector.
137    ///
138    /// # Examples
139    /// ```
140    /// # use batbox_la::*;
141    /// let v: vec2<f64> = vec2(1.0, 2.0);
142    /// assert!((v.normalize().len() - 1.0).abs() < 1e-5);
143    /// ```
144    pub fn normalize(self) -> Self {
145        self / self.len()
146    }
147
148    /// Normalizes a vector unless its length its approximately 0.
149    /// Can be used to avoid division by 0.
150    ///
151    /// # Examples
152    /// ```
153    /// # use batbox_la::*;
154    /// let v = vec2(1.0, 2.0);
155    /// assert_eq!(v.normalize_or_zero(), v.normalize());
156    /// let v = vec2(1e-10, 1e-10);
157    /// assert_eq!(v.normalize_or_zero(), vec2::ZERO);
158    /// ```
159    pub fn normalize_or_zero(self) -> Self {
160        let len = self.len();
161        if len.approx_eq(&T::ZERO) {
162            vec2::ZERO
163        } else {
164            self / len
165        }
166    }
167
168    /// Calculate length of a vector.
169    ///
170    /// # Examples
171    /// ```
172    /// # use batbox_la::*;
173    /// let v = vec2(3.0, 4.0);
174    /// assert_eq!(v.len(), 5.0);
175    /// ```
176    pub fn len(self) -> T {
177        T::sqrt(self.len_sqr())
178    }
179
180    /// Calculate squared length of this vector
181    pub fn len_sqr(self) -> T {
182        vec2::dot(self, self)
183    }
184
185    /// Rotate a vector by a given angle.
186    ///
187    /// # Examples
188    /// ```
189    /// # use batbox_la::*;
190    /// let v = vec2(1.0, 2.0);
191    /// assert!((v.rotate(Angle::from_radians(std::f32::consts::FRAC_PI_2)) - vec2(-2.0, 1.0)).len() < 1e-5);
192    /// ```
193    pub fn rotate(self, angle: Angle<T>) -> Self {
194        let (sin, cos) = angle.sin_cos();
195        Self(self.x * cos - self.y * sin, self.x * sin + self.y * cos)
196    }
197
198    /// Clamp vector's length. Note that the range must be inclusive.
199    ///
200    /// # Examples
201    /// ```
202    /// # use batbox_la::*;
203    /// let v = vec2(1.0, 2.0);
204    /// assert_eq!(v.clamp_len(..=1.0), v.normalize());
205    /// ```
206    pub fn clamp_len(self, len_range: impl FixedRangeBounds<T>) -> Self {
207        let len = self.len();
208        let target_len = len.clamp_range(len_range);
209        if len == target_len {
210            self
211        } else {
212            self * target_len / len
213        }
214    }
215
216    /// Clamp vector in range. Note the range must be inclusive.
217    ///
218    /// # Examples
219    /// ```
220    /// # use batbox_la::*;
221    /// let v = vec2(1.0, 2.0);
222    /// assert_eq!(v.clamp_coordinates(.., 0.0..=1.0), vec2(1.0, 1.0));
223    /// ```
224    pub fn clamp_coordinates(
225        self,
226        x_range: impl FixedRangeBounds<T>,
227        y_range: impl FixedRangeBounds<T>,
228    ) -> Self {
229        vec2(self.x.clamp_range(x_range), self.y.clamp_range(y_range))
230    }
231
232    /// Clamp vector by `aabb` corners.
233    ///
234    /// # Examples
235    /// ```
236    /// # use batbox_la::*;
237    /// let v = vec2(0.5, 2.0);
238    /// let min = vec2(0.0, 0.0);
239    /// let max = vec2(1.0, 1.0);
240    /// let aabb = Aabb2::from_corners(min, max);
241    /// assert_eq!(v.clamp_aabb(aabb), vec2(0.5, 1.0));
242    /// ```
243    pub fn clamp_aabb(self, aabb: Aabb2<T>) -> Self {
244        let start = aabb.bottom_left();
245        let end = aabb.top_right();
246        self.clamp_coordinates(start.x..=end.x, start.y..=end.y)
247    }
248
249    /// Get an angle between the positive direction of the x-axis.
250    ///
251    /// # Examples
252    /// ```
253    /// # use batbox_la::*;
254    /// let v = vec2(0.0, 1.0);
255    /// assert_eq!(v.arg().as_radians(), std::f32::consts::FRAC_PI_2);
256    /// ```
257    pub fn arg(self) -> Angle<T> {
258        Angle::atan2(self.y, self.x)
259    }
260
261    /// Apply transformation matrix
262    pub fn transform(self, transform: mat3<T>) -> Self {
263        (transform * self.extend(T::ONE)).into_2d()
264    }
265
266    /// Calculate aspect ratio (x / y)
267    pub fn aspect(self) -> T {
268        self.x / self.y
269    }
270}
271
272#[test]
273fn test_clamp_zero_len() {
274    vec2::ZERO.clamp_len(..=R64::ONE);
275}