pix_engine/vector.rs
1//! A [Euclidean] `Vector` in N-dimensional space.
2//!
3//! Each [Vector] represents a N-dimensional [Euclidean] (or geometric) vector with a magnitude
4//! and a direction. The [Vector] `struct`, however, contains N values for each dimensional
5//! coordinate. The magnitude and direction are retrieved with the [`Vector::mag`] and
6//! [`Vector::heading`] methods.
7//!
8//! Some example uses of a [Vector] include modeling a position, velocity, or acceleration of an
9//! object or particle in 2D or 3D space.
10//!
11//! # Examples
12//!
13//! You can create a [Vector] using [`Vector::new`]:
14//!
15//! ```
16//! # use pix_engine::prelude::*;
17//! let v = Vector::new([10.0, 20.0, 15.0]);
18//! ```
19//! ...or by using the [vector!] macro:
20//!
21//! ```
22//! # use pix_engine::prelude::*;
23//! let v: Vector<f64, 3> = vector!(); // vector at the origin (0, 0, 0) with no direction or magnitude
24//! assert_eq!(v.coords(), [0.0, 0.0, 0.0]);
25//!
26//! let v = vector!(5.0); // 1D vector on the x-axis with magnitude 5
27//! assert_eq!(v.coords(), [5.0]);
28//!
29//! let v = vector!(5.0, 10.0); // 2D vector in the x/y-plane
30//! assert_eq!(v.coords(), [5.0, 10.0]);
31//!
32//! let v = vector!(-1.5, 3.0, 2.2); // 3D vector
33//! assert_eq!(v.coords(), [-1.5, 3.0, 2.2]);
34//! ```
35//!
36//! You can also create random `Vector`s using [`Vector::random`] which create unit vectors with
37//! magnitudes in the range `-1.0..=1.0`.
38//!
39//! ```
40//! use pix_engine::prelude::*;
41//!
42//! let v: Vector<f64, 1> = Vector::random();
43//! // `v.coords()` will return something like:
44//! // [-0.9993116191591512, 0.03709835324533284, 0.0]
45//! assert!(v.x() >= -1.0 && v.x() <= 1.0);
46//!
47//! let v: Vector<f64, 2> = Vector::random();
48//! // `v.coords()` will return something like:
49//! // [-0.9993116191591512, 0.03709835324533284, 0.0]
50//! assert!(v.x() >= -1.0 && v.x() <= 1.0);
51//! assert!(v.y() >= -1.0 && v.y() <= 1.0);
52//!
53//! let v: Vector<f64, 3> = Vector::random();
54//! // `v.coords()` will return something like:
55//! // [-0.40038099206441835, 0.8985763512414204, 0.17959844705110184]
56//! assert!(v.x() >= -1.0 && v.x() <= 1.0);
57//! assert!(v.y() >= -1.0 && v.y() <= 1.0);
58//! assert!(v.z() >= -1.0 && v.z() <= 1.0);
59//! ```
60//!
61//! [Euclidean]: https://en.wikipedia.org/wiki/Euclidean_vector
62
63use crate::prelude::*;
64#[cfg(feature = "serde")]
65use crate::serialize::arrays;
66use num_traits::Signed;
67use rand::distributions::uniform::SampleUniform;
68#[cfg(feature = "serde")]
69use serde::{de::DeserializeOwned, Deserialize, Serialize};
70use std::{fmt, ops::MulAssign};
71
72/// A [Euclidean] `Vector` in N-dimensional space.
73///
74/// Also known as a geometric vector. A `Vector` has both a magnitude and a direction. The [Vector]
75/// struct, however, contains N values for each dimensional coordinate.
76///
77/// The magnitude and direction are retrieved with the [mag] and [heading] methods.
78///
79/// Some example uses of a [Vector] include modeling a position, velocity, or acceleration of an
80/// object or particle.
81///
82/// [Vector]s can be combined using [vector math][vecmath], so for example two [Vector]s can be added together
83/// to form a new [Vector] using `let v3 = v1 + v2` or you can add one [Vector] to another by calling
84/// `v1 += v2`.
85///
86/// Please see the [module-level documentation] for examples.
87///
88/// [Euclidean]: https://en.wikipedia.org/wiki/Euclidean_vector
89/// [mag]: Vector::mag
90/// [heading]: Vector::heading
91/// [vecmath]: https://en.wikipedia.org/wiki/Vector_(mathematics_and_p.y()sics)
92/// [module-level documentation]: mod@crate::vector
93#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
94#[repr(transparent)]
95#[must_use]
96#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
97#[cfg_attr(feature = "serde", serde(bound = "T: Serialize + DeserializeOwned"))]
98pub struct Vector<T = f64, const N: usize = 2>(
99 #[cfg_attr(feature = "serde", serde(with = "arrays"))] pub(crate) [T; N],
100);
101
102/// Constructs a [Vector].
103///
104/// # Examples
105///
106/// ```
107/// # use pix_engine::prelude::*;
108/// let v: Vector<f64, 3> = vector!();
109/// assert_eq!(v.coords(), [0.0, 0.0, 0.0]);
110///
111/// let v = vector!(1.0);
112/// assert_eq!(v.coords(), [1.0]);
113///
114/// let v = vector!(1.0, 2.0);
115/// assert_eq!(v.coords(), [1.0, 2.0]);
116///
117/// let v = vector!(1.0, -2.0, 1.0);
118/// assert_eq!(v.coords(), [1.0, -2.0, 1.0]);
119/// ```
120#[macro_export]
121macro_rules! vector {
122 () => {
123 $crate::prelude::Vector::origin()
124 };
125 ($x:expr) => {
126 $crate::prelude::Vector::from_x($x)
127 };
128 ($x:expr, $y:expr$(,)?) => {
129 $crate::prelude::Vector::from_xy($x, $y)
130 };
131 ($x:expr, $y:expr, $z:expr$(,)?) => {
132 $crate::prelude::Vector::from_xyz($x, $y, $z)
133 };
134}
135
136impl<T, const N: usize> Vector<T, N> {
137 /// Constructs a `Vector` from `[T; N]` coordinates.
138 ///
139 /// # Examples
140 ///
141 /// ```
142 /// # use pix_engine::prelude::*;
143 /// let v = Vector::new([2.1]);
144 /// assert_eq!(v.coords(), [2.1]);
145 ///
146 /// let v = Vector::new([2.1, 3.5]);
147 /// assert_eq!(v.coords(), [2.1, 3.5]);
148 ///
149 /// let v = Vector::new([2.1, 3.5, 1.0]);
150 /// assert_eq!(v.coords(), [2.1, 3.5, 1.0]);
151 /// ```
152 #[inline]
153 pub const fn new(coords: [T; N]) -> Self {
154 Self(coords)
155 }
156
157 /// Constructs a `Vector` at the origin.
158 ///
159 /// # Example
160 ///
161 /// ```
162 /// # use pix_engine::prelude::*;
163 /// let v: Vector<f64, 3> = Vector::origin();
164 /// assert_eq!(v.coords(), [0.0, 0.0, 0.0]);
165 /// ```
166 #[inline]
167 pub fn origin() -> Self
168 where
169 T: Default,
170 {
171 Self::new([(); N].map(|_| T::default()))
172 }
173}
174
175impl<T> Vector<T, 1> {
176 /// Constructs a `Vector` from an individual x coordinate.
177 #[inline]
178 pub const fn from_x(x: T) -> Self {
179 Self([x])
180 }
181}
182
183impl<T> Vector<T> {
184 /// Constructs a `Vector` from individual x/y coordinates.
185 #[inline]
186 pub const fn from_xy(x: T, y: T) -> Self {
187 Self([x, y])
188 }
189}
190
191impl<T> Vector<T, 3> {
192 /// Constructs a `Vector` from individual x/y/z coordinates.
193 #[inline]
194 pub const fn from_xyz(x: T, y: T, z: T) -> Self {
195 Self([x, y, z])
196 }
197}
198
199impl<T: Num + Float> Vector<T> {
200 /// Constructs a `Vector` from another `Vector`, rotated by an `angle`.
201 ///
202 /// # Example
203 ///
204 /// ```
205 /// # use pix_engine::prelude::*;
206 /// use pix_engine::math::FRAC_PI_2;
207 /// let v1 = Vector::new([10.0, 20.0]);
208 /// let v2 = Vector::rotated(v1, FRAC_PI_2);
209 /// assert!(v2.approx_eq(vector![-20.0, 10.0], 1e-4));
210 /// ```
211 pub fn rotated<V>(v: V, angle: T) -> Self
212 where
213 V: Into<Vector<T>>,
214 {
215 let mut v = v.into();
216 v.rotate(angle);
217 v
218 }
219
220 /// Constructs a 2D unit `Vector` in the XY plane from a given angle. Angle is given as
221 /// radians and is unaffected by [`AngleMode`].
222 ///
223 /// # Example
224 ///
225 /// ```
226 /// # use pix_engine::prelude::*;
227 /// use pix_engine::math::FRAC_PI_4;
228 /// let v = Vector::from_angle(FRAC_PI_4, 15.0);
229 /// assert!(v.approx_eq(vector!(10.6066, 10.6066), 1e-4));
230 /// ```
231 pub fn from_angle(angle: T, length: T) -> Self {
232 let (sin, cos) = angle.sin_cos();
233 Self::new([length * cos, length * sin])
234 }
235
236 /// Returns the 2D angular direction of the `Vector`.
237 ///
238 /// # Example
239 ///
240 /// ```
241 /// # use pix_engine::prelude::*;
242 /// let v = vector!(10.0, 10.0);
243 /// let heading: f64 = v.heading();
244 /// assert_eq!(heading.to_degrees(), 45.0);
245 /// ```
246 pub fn heading(&self) -> T {
247 self.y().atan2(self.x())
248 }
249
250 /// Rotate a 2D `Vector` by an angle in radians, magnitude remains the same. Unaffected by
251 /// [`AngleMode`].
252 ///
253 /// # Example
254 ///
255 /// ```
256 /// # use pix_engine::prelude::*;
257 /// use pix_engine::math::FRAC_PI_2;
258 /// let mut v = vector!(10.0, 20.0);
259 /// v.rotate(FRAC_PI_2);
260 /// assert!(v.approx_eq(vector![-20.0, 10.0], 1e-4));
261 /// ```
262 pub fn rotate(&mut self, angle: T) {
263 let new_heading = self.heading() + angle;
264 let mag = self.mag();
265 let (sin, cos) = new_heading.sin_cos();
266 self.set_x(cos * mag);
267 self.set_y(sin * mag);
268 }
269}
270
271impl<T: Num + Float> Vector<T, 3> {
272 /// Returns the [cross product](https://en.wikipedia.org/wiki/Cross_product) between two
273 /// `Vector`s. Only defined for 3D `Vector`s.
274 ///
275 /// # Example
276 ///
277 /// ```
278 /// # use pix_engine::prelude::*;
279 /// let v1 = vector!(1.0, 2.0, 3.0);
280 /// let v2 = vector!(1.0, 2.0, 3.0);
281 /// let cross = v1.cross(v2);
282 /// assert_eq!(cross.coords(), [0.0, 0.0, 0.0]);
283 /// ```
284 pub fn cross<V>(&self, v: V) -> Self
285 where
286 V: Into<Vector<T, 3>>,
287 {
288 let v = v.into();
289 Self::new([
290 self.y() * v.z() - self.z() * v.y(),
291 self.z() * v.x() - self.x() * v.z(),
292 self.x() * v.y() - self.y() * v.x(),
293 ])
294 }
295
296 /// Returns the angle between two 3D `Vector`s in radians.
297 ///
298 /// # Example
299 ///
300 /// ```
301 /// # use pix_engine::prelude::*;
302 /// let v1 = vector!(1.0, 0.0, 0.0);
303 /// let v2 = vector!(0.0, 1.0, 0.0);
304 /// let angle = v1.angle_between(v2);
305 /// assert_eq!(angle, std::f64::consts::FRAC_PI_2);
306 /// ```
307 pub fn angle_between<V>(&self, v: V) -> T
308 where
309 V: Into<Vector<T, 3>>,
310 {
311 let v = v.into();
312 // This should range from -1.0 to 1.0, inclusive but could possibly land outside this range
313 // due to floating-point rounding, so we'll need to clamp it to the correct range.
314 let dot_mag_product =
315 num_traits::clamp(self.dot(v) / (self.mag() * v.mag()), -T::one(), T::one());
316 dot_mag_product.acos() * self.cross(v).z().signum()
317 }
318}
319
320impl<T: Copy, const N: usize> Vector<T, N> {
321 /// Constructs a `Vector` from a [Point].
322 ///
323 /// # Example
324 ///
325 /// ```
326 /// # use pix_engine::prelude::*;
327 /// let p = point!(1.0, 2.0);
328 /// let v = Vector::from_point(p);
329 /// assert_eq!(v.coords(), [1.0, 2.0]);
330 /// ```
331 #[inline]
332 pub fn from_point(p: Point<T, N>) -> Self {
333 Self::new(p.coords())
334 }
335
336 /// Returns the `x-coordinate`.
337 ///
338 /// # Panics
339 ///
340 /// If `Vector` has zero dimensions.
341 ///
342 /// # Example
343 ///
344 /// ```
345 /// # use pix_engine::prelude::*;
346 /// let v = vector!(1.0, 2.0);
347 /// assert_eq!(v.x(), 1.0);
348 /// ```
349 #[inline]
350 pub fn x(&self) -> T {
351 self.0[0]
352 }
353
354 /// Sets the `x-magnitude`.
355 ///
356 /// # Panics
357 ///
358 /// If `Vector` has zero dimensions.
359 ///
360 /// # Example
361 ///
362 /// ```
363 /// # use pix_engine::prelude::*;
364 /// let mut v = vector!(1.0, 2.0);
365 /// v.set_x(3.0);
366 /// assert_eq!(v.coords(), [3.0, 2.0]);
367 /// ```
368 #[inline]
369 pub fn set_x(&mut self, x: T) {
370 self.0[0] = x;
371 }
372
373 /// Returns the `y-magnitude`.
374 ///
375 /// # Panics
376 ///
377 /// If `Vector` has less than 2 dimensions.
378 ///
379 /// # Example
380 ///
381 /// ```
382 /// # use pix_engine::prelude::*;
383 /// let v = vector!(1.0, 2.0);
384 /// assert_eq!(v.y(), 2.0);
385 /// ```
386 #[inline]
387 pub fn y(&self) -> T {
388 self.0[1]
389 }
390
391 /// Sets the `y-magnitude`.
392 ///
393 /// # Panics
394 ///
395 /// If `Vector` has less than 2 dimensions.
396 ///
397 /// # Example
398 ///
399 /// ```
400 /// # use pix_engine::prelude::*;
401 /// let mut v = vector!(1.0, 2.0);
402 /// v.set_y(3.0);
403 /// assert_eq!(v.coords(), [1.0, 3.0]);
404 /// ```
405 #[inline]
406 pub fn set_y(&mut self, y: T) {
407 self.0[1] = y;
408 }
409
410 /// Returns the `z-magnitude`.
411 ///
412 /// # Panics
413 ///
414 /// If `Vector` has less than 3 dimensions.
415 ///
416 /// # Example
417 ///
418 /// ```
419 /// # use pix_engine::prelude::*;
420 /// let v = vector!(1.0, 2.0, 2.5);
421 /// assert_eq!(v.z(), 2.5);
422 /// ```
423 #[inline]
424 pub fn z(&self) -> T {
425 self.0[2]
426 }
427
428 /// Sets the `z-magnitude`.
429 ///
430 /// # Panics
431 ///
432 /// If `Vector` has less than 3 dimensions.
433 ///
434 /// # Example
435 ///
436 /// ```
437 /// # use pix_engine::prelude::*;
438 /// let mut v = vector!(1.0, 2.0, 1.0);
439 /// v.set_z(3.0);
440 /// assert_eq!(v.coords(), [1.0, 2.0, 3.0]);
441 /// ```
442 #[inline]
443 pub fn set_z(&mut self, z: T) {
444 self.0[2] = z;
445 }
446
447 /// Get `Vector` coordinates as `[T; N]`.
448 ///
449 /// # Example
450 ///
451 /// ```
452 /// # use pix_engine::prelude::*;
453 /// let v = vector!(2.0, 1.0, 3.0);
454 /// assert_eq!(v.coords(), [2.0, 1.0, 3.0]);
455 /// ```
456 #[inline]
457 pub fn coords(&self) -> [T; N] {
458 self.0
459 }
460
461 /// Get `Vector` coordinates as a mutable slice `&[T; N]`.
462 ///
463 /// # Example
464 ///
465 /// ```
466 /// # use pix_engine::prelude::*;
467 /// let mut vector = vector!(2.0, 1.0, 3.0);
468 /// for v in vector.coords_mut() {
469 /// *v *= 2.0;
470 /// }
471 /// assert_eq!(vector.coords(), [4.0, 2.0, 6.0]);
472 /// ```
473 #[inline]
474 pub fn coords_mut(&mut self) -> &mut [T; N] {
475 &mut self.0
476 }
477
478 /// Returns `Vector` as a [Vec].
479 ///
480 /// # Example
481 ///
482 /// ```
483 /// # use pix_engine::prelude::*;
484 /// let v = vector!(1.0, 1.0, 0.0);
485 /// assert_eq!(v.to_vec(), vec![1.0, 1.0, 0.0]);
486 /// ```
487 #[inline]
488 pub fn to_vec(self) -> Vec<T> {
489 self.0.to_vec()
490 }
491}
492
493impl<T: Num, const N: usize> Vector<T, N> {
494 /// Constructs a `Vector` by shifting coordinates by given amount.
495 ///
496 /// # Examples
497 ///
498 /// ```
499 /// # use pix_engine::prelude::*;
500 /// let mut v = vector!(2.0, 3.0, 1.5);
501 /// v.offset([2.0, -4.0]);
502 /// assert_eq!(v.coords(), [4.0, -1.0, 1.5]);
503 /// ```
504 #[inline]
505 pub fn offset<V, const M: usize>(&mut self, offsets: V)
506 where
507 V: Into<Vector<T, M>>,
508 {
509 let offsets = offsets.into();
510 for (v, o) in self.iter_mut().zip(offsets) {
511 *v += o;
512 }
513 }
514
515 /// Offsets the `x-coordinate` of the point by a given amount.
516 ///
517 /// # Panics
518 ///
519 /// If `Point` has zero dimensions.
520 #[inline]
521 pub fn offset_x(&mut self, offset: T) {
522 self.0[0] += offset;
523 }
524
525 /// Offsets the `y-coordinate` of the point by a given amount.
526 ///
527 /// # Panics
528 ///
529 /// If `Vector` has less than 2 dimensions.
530 #[inline]
531 pub fn offset_y(&mut self, offset: T) {
532 self.0[1] += offset;
533 }
534
535 /// Offsets the `z-coordinate` of the point by a given amount.
536 ///
537 /// # Panics
538 ///
539 /// If `Vector` has less than 3 dimensions.
540 #[inline]
541 pub fn offset_z(&mut self, offset: T) {
542 self.0[2] += offset;
543 }
544
545 /// Constructs a `Vector` by multiplying it by the given scale factor.
546 ///
547 /// # Examples
548 ///
549 /// ```
550 /// # use pix_engine::prelude::*;
551 /// let mut v = vector!(2.0, 3.0, 1.5);
552 /// v.scale(2.0);
553 /// assert_eq!(v.coords(), [4.0, 6.0, 3.0]);
554 /// ```
555 pub fn scale<U>(&mut self, s: U)
556 where
557 T: MulAssign<U>,
558 U: Num,
559 {
560 *self *= s;
561 }
562
563 /// Wraps `Vector` around the given `[T; N]`, and size (radius).
564 ///
565 /// # Examples
566 ///
567 /// ```
568 /// # use pix_engine::prelude::*;
569 /// let mut v = vector!(200.0, 300.0);
570 /// v.wrap([150.0, 400.0], 10.0);
571 /// assert_eq!(v.coords(), [-10.0, 300.0]);
572 ///
573 /// let mut v = vector!(-100.0, 300.0);
574 /// v.wrap([150.0, 400.0], 10.0);
575 /// assert_eq!(v.coords(), [160.0, 300.0]);
576 /// ```
577 pub fn wrap(&mut self, wrap: [T; N], size: T)
578 where
579 T: Signed,
580 {
581 for (v, w) in self.iter_mut().zip(wrap) {
582 let w = w + size;
583 if *v > w {
584 *v = -size;
585 } else if *v < -size {
586 *v = w;
587 }
588 }
589 }
590
591 /// Constructs a random unit `Vector` in 1D space.
592 ///
593 /// # Example
594 ///
595 /// ```
596 /// # use pix_engine::prelude::*;
597 /// let v: Vector<f64, 3> = Vector::random();
598 /// assert!(v.x() > -1.0 && v.x() < 1.0);
599 /// assert!(v.y() > -1.0 && v.y() < 1.0);
600 /// assert!(v.z() > -1.0 && v.z() < 1.0);
601 ///
602 /// // May make v's (x, y, z) values something like:
603 /// // (0.61554617, 0.0, 0.0) or
604 /// // (-0.4695841, 0.0, 0.0) or
605 /// // (0.6091097, 0.0, 0.0)
606 /// ```
607 pub fn random() -> Self
608 where
609 T: SampleUniform,
610 {
611 let mut coords = [T::zero(); N];
612 for coord in &mut coords {
613 *coord = random!(T::one());
614 }
615 Self::new(coords)
616 }
617}
618
619impl<T: Num + Float, const N: usize> Vector<T, N> {
620 /// Constructs a `Vector` from a reflection about a normal to a line in 2D space or a plane in 3D
621 /// space.
622 ///
623 /// # Example
624 ///
625 /// ```
626 /// # use pix_engine::prelude::*;
627 /// let v1 = Vector::new([1.0, 1.0, 0.0]);
628 /// let normal = Vector::new([0.0, 1.0, 0.0]);
629 /// let v2 = Vector::reflection(v1, normal);
630 /// assert_eq!(v2.coords(), [-1.0, 1.0, 0.0]);
631 /// ```
632 pub fn reflection<V>(v: V, normal: V) -> Self
633 where
634 V: Into<Vector<T, N>>,
635 {
636 let mut v = v.into();
637 v.reflect(normal);
638 v
639 }
640
641 /// Constructs a unit `Vector` of length `1` from another `Vector`.
642 ///
643 /// # Example
644 ///
645 /// ```
646 /// # use pix_engine::prelude::*;
647 /// let v1 = Vector::new([0.0, 5.0, 0.0]);
648 /// let v2 = Vector::normalized(v1);
649 /// assert_eq!(v2.coords(), [0.0, 1.0, 0.0]);
650 /// ```
651 pub fn normalized<V>(v: V) -> Self
652 where
653 V: Into<Vector<T, N>>,
654 {
655 let mut v = v.into();
656 v.normalize();
657 v
658 }
659
660 /// Returns the magnitude (length) of the `Vector`.
661 ///
662 /// The formula used for 2D is `sqrt(x*x + y*y)`.
663 /// The formula used for 3D is `sqrt(x*x + y*y + z*z)`.
664 ///
665 /// # Example
666 ///
667 /// ```
668 /// # use pix_engine::prelude::*;
669 /// let v = vector!(1.0, 2.0, 3.0);
670 /// let abs_difference = (v.mag() as f64 - 3.7416).abs();
671 /// assert!(abs_difference <= 1e-4);
672 /// ```
673 pub fn mag(&self) -> T {
674 self.mag_sq().sqrt()
675 }
676
677 /// Returns the squared magnitude (length) of the `Vector`. This is faster if the real length
678 /// is not required in the case of comparing vectors.
679 ///
680 /// The formula used for 2D is `x*x + y*y`.
681 /// The formula used for 3D is `x*x + y*y + z*z`.
682 ///
683 /// # Example
684 ///
685 /// ```
686 /// # use pix_engine::prelude::*;
687 /// let v = vector!(1.0, 2.0, 3.0);
688 /// assert_eq!(v.mag_sq(), 14.0);
689 /// ```
690 pub fn mag_sq(&self) -> T {
691 let mut sum = T::zero();
692 for &v in self.iter() {
693 sum += v * v;
694 }
695 sum
696 }
697
698 /// Returns the [dot product](https://en.wikipedia.org/wiki/Dot_product) betwen two `Vector`s.
699 ///
700 /// # Example
701 ///
702 /// ```
703 /// # use pix_engine::prelude::*;
704 /// let v1 = vector!(1.0, 2.0, 3.0);
705 /// let v2 = vector!(2.0, 3.0, 4.0);
706 /// let dot_product = v1.dot(v2);
707 /// assert_eq!(dot_product, 20.0);
708 /// ```
709 pub fn dot<V>(&self, o: V) -> T
710 where
711 V: Into<Vector<T, N>>,
712 {
713 let o = o.into();
714 let mut sum = T::zero();
715 for (&v, o) in self.iter().zip(o) {
716 sum += v * o;
717 }
718 sum
719 }
720
721 /// Reflect `Vector` about a normal to a line in 2D space or a plane in 3D space.
722 ///
723 /// # Example
724 ///
725 /// ```
726 /// # use pix_engine::prelude::*;
727 /// let mut v = vector!(4.0, 6.0); // Vector heading right and down
728 /// let n = vector!(0.0, 1.0); // Surface normal facing up
729 /// v.reflect(n); // Reflect about the surface normal (e.g. the x-axis)
730 /// assert_eq!(v.x(), -4.0);
731 /// assert_eq!(v.y(), 6.0);
732 /// ```
733 pub fn reflect<V>(&mut self, normal: V)
734 where
735 V: Into<Vector<T, N>>,
736 {
737 let normal = Self::normalized(normal);
738 *self = normal * ((T::one() + T::one()) * self.dot(normal)) - *self;
739 }
740
741 /// Set the magnitude (length) of the `Vector`.
742 ///
743 /// # Examples
744 ///
745 /// ```
746 /// # use pix_engine::prelude::*;
747 /// let mut v = vector!(10.0, 20.0, 2.0);
748 /// v.set_mag(10.0);
749 /// assert!(v.approx_eq(vector![4.4543, 8.9087, 0.8908], 1e-4));
750 /// ```
751 pub fn set_mag(&mut self, mag: T) {
752 self.normalize();
753 *self *= mag;
754 }
755
756 /// Returns the Euclidean distance between two `Vector`s.
757 ///
758 /// # Example
759 ///
760 /// ```
761 /// # use pix_engine::prelude::*;
762 /// let v1 = vector!(1.0, 0.0, 0.0);
763 /// let v2 = vector!(0.0, 1.0, 0.0);
764 /// let dist = v1.dist(v2);
765 /// let abs_difference: f64 = (dist - std::f64::consts::SQRT_2).abs();
766 /// assert!(abs_difference <= 1e-4);
767 /// ```
768 pub fn dist<V>(&self, v: V) -> T
769 where
770 V: Into<Vector<T, N>>,
771 {
772 (*self - v.into()).mag()
773 }
774
775 /// Normalize the `Vector` to length `1` making it a unit vector.
776 ///
777 /// # Example
778 ///
779 /// ```
780 /// # use pix_engine::prelude::*;
781 /// let mut v = vector!(10.0, 20.0, 2.0);
782 /// v.normalize();
783 /// assert!(v.approx_eq(vector!(0.4454, 0.8908, 0.0890), 1e-4));
784 /// ```
785 pub fn normalize(&mut self) {
786 let len = self.mag();
787 if len != T::zero() {
788 // Multiply by the reciprocol so we don't duplicate a div by zero check
789 *self *= len.recip();
790 }
791 }
792
793 /// Clamp the magnitude (length) of `Vector` to the value given by `max`.
794 ///
795 /// # Example
796 ///
797 /// ```
798 /// # use pix_engine::prelude::*;
799 /// let mut v = vector!(10.0, 20.0, 2.0);
800 /// v.limit(5.0);
801 /// assert!(v.approx_eq(vector!(2.2271, 4.4543, 0.4454), 1e-4));
802 /// ```
803 pub fn limit(&mut self, max: T) {
804 let mag_sq = self.mag_sq();
805 if mag_sq > max * max {
806 *self /= mag_sq.sqrt();
807 *self *= max;
808 }
809 }
810
811 /// Constructs a `Vector` by linear interpolating between two `Vector`s by a given amount
812 /// between `0.0` and `1.0`.
813 ///
814 /// # Example
815 ///
816 /// ```
817 /// # use pix_engine::prelude::*;
818 /// let v1 = vector!(1.0, 1.0, 0.0);
819 /// let v2 = vector!(3.0, 3.0, 0.0);
820 /// let v3 = v1.lerp(v2, 0.5);
821 /// assert_eq!(v3.coords(), [2.0, 2.0, 0.0]);
822 /// ```
823 pub fn lerp<V>(&self, o: V, amt: T) -> Self
824 where
825 V: Into<Vector<T, N>>,
826 {
827 let o = o.into();
828 let lerp = |start, stop, amt| amt * (stop - start) + start;
829 let amt = num_traits::clamp(amt, T::zero(), T::one());
830 let mut coords = [T::zero(); N];
831 for ((c, &v), o) in coords.iter_mut().zip(self.iter()).zip(o) {
832 *c = lerp(v, o, amt);
833 }
834 Self::new(coords)
835 }
836
837 /// Returns whether two `Vector`s are approximately equal.
838 ///
839 /// # Example
840 ///
841 /// ```
842 /// # use pix_engine::prelude::*;
843 /// let v1 = vector!(10.0, 20.0, 2.0);
844 /// let v2 = vector!(10.0001, 20.0, 2.0);
845 /// assert!(v1.approx_eq(v2, 1e-3));
846 /// ```
847 pub fn approx_eq<V>(&self, other: V, epsilon: T) -> bool
848 where
849 V: Into<Vector<T, N>>,
850 {
851 let other = other.into();
852 let mut approx_eq = true;
853 for (&v, o) in self.iter().zip(other) {
854 approx_eq &= (v - o).abs() < epsilon;
855 }
856 approx_eq
857 }
858}
859
860impl<T: Default, const N: usize> Default for Vector<T, N> {
861 /// Return default `Vector` as origin.
862 fn default() -> Self {
863 Self::origin()
864 }
865}
866
867impl<T, const N: usize> fmt::Display for Vector<T, N>
868where
869 [T; N]: fmt::Debug,
870{
871 /// Display [Vector] as a string of coordinates.
872 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
873 write!(f, "{:?}", self.0)
874 }
875}
876
877impl<T: Copy, const N: usize> From<Point<T, N>> for Vector<T, N> {
878 fn from(p: Point<T, N>) -> Self {
879 Self::from_point(p)
880 }
881}
882
883impl<T: Copy, const N: usize> From<&Point<T, N>> for Vector<T, N> {
884 fn from(p: &Point<T, N>) -> Self {
885 Self::from_point(*p)
886 }
887}
888
889impl<T: Copy, const N: usize> From<Vector<T, N>> for Point<T, N> {
890 fn from(v: Vector<T, N>) -> Self {
891 Self::from_vector(v)
892 }
893}
894
895impl<T: Copy, const N: usize> From<&Vector<T, N>> for Point<T, N> {
896 fn from(v: &Vector<T, N>) -> Self {
897 Self::from_vector(*v)
898 }
899}