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}