melvin_ob/util/math/vec2d.rs
1use fixed::prelude::{FromFixed, ToFixed};
2use fixed::types::{I32F32, I64F64, I96F32};
3use fixed::{
4 traits::{Fixed, FixedSigned},
5 types::I32F0,
6};
7use num::traits::{Num, NumAssignOps};
8use std::{
9 cmp::Ordering,
10 fmt::Display,
11 ops::{Add, Deref, Div, Mul, Rem, Sub},
12};
13use strum_macros::Display;
14
15/// A 2D vector generic over any numeric type.
16///
17/// This struct represents a 2D point or vector in space and provides common
18/// mathematical operations such as addition, normalization, rotation, and distance calculations.
19///
20/// # Type Parameters
21/// * `T` - The functionality for the vector depends on traits implemented by `T`.
22#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, serde::Serialize, serde::Deserialize)]
23pub struct Vec2D<T> {
24 /// The x-component of the vector.
25 x: T,
26 /// The y-component of the vector.
27 y: T,
28}
29
30#[derive(Debug, Copy, Clone, Display)]
31pub enum VecAxis {
32 X,
33 Y,
34}
35
36/// A 2D vector wrapper with fixed-size wrapping capabilities.
37///
38/// This struct is generic over a numeric type `T` and two constants `X` and `Y`,
39/// which represent the fixed size of the 2D wrapping area.
40///
41/// # Type Parameters
42/// * `T` - Any numeric type that implements the `Fixed` trait.
43/// * `X` - The maximum bound for the x-axis.
44/// * `Y` - The maximum bound for the y-axis.
45pub struct Wrapped2D<T, const X: u32, const Y: u32>(Vec2D<T>);
46
47impl<T, const X: u32, const Y: u32> Wrapped2D<T, X, Y>
48where T: Fixed
49{
50 /// Wraps the coordinates of the vector around the bounds defined by `X` and `Y`.
51 ///
52 /// # Returns
53 /// A new `Wrapped2D` instance with its coordinates wrapped within the bounds.
54 pub fn wrap_around_map(&self) -> Self {
55 Wrapped2D(Vec2D::new(
56 Self::wrap_coordinate(self.0.x, T::from_num(X)),
57 Self::wrap_coordinate(self.0.y, T::from_num(Y)),
58 ))
59 }
60
61 /// Wraps a single coordinate value around a maximum value.
62 ///
63 /// # Arguments
64 /// * `value` - The coordinate value to be wrapped.
65 /// * `max_value` - The maximum bound for the coordinate.
66 ///
67 /// # Returns
68 /// The wrapped coordinate value.
69 pub fn wrap_coordinate(value: T, max_value: T) -> T {
70 ((value % max_value) + max_value) % max_value
71 }
72}
73
74impl<T, const X: u32, const Y: u32> Deref for Wrapped2D<T, X, Y> {
75 type Target = Vec2D<T>;
76
77 /// Dereferences the `Wrapped2D` wrapper to access its inner `Vec2D` value.
78 ///
79 /// # Returns
80 /// A reference to the inner `Vec2D`.
81 fn deref(&self) -> &Self::Target { &self.0 }
82}
83
84impl<T> Display for Vec2D<T>
85where T: Display
86{
87 /// Formats the `Vec2D` as a string in the format `[x, y]`.
88 ///
89 /// # Arguments
90 /// * `f` - A mutable reference to the formatter.
91 ///
92 /// # Returns
93 /// A `Result` indicating the success of the formatting operation.
94 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
95 match f.precision() {
96 Some(p) => write!(f, "[{:.prec$}, {:.prec$}]", self.x, self.y, prec=p),
97 None => write!(f, "[{}, {}]", self.x, self.y),
98 }
99 }
100}
101
102/// A trait providing a method to define the size of a 2D map.
103///
104/// This is used to determine the dimensions of the map for wrapping operations.
105pub trait MapSize {
106 /// The output type of the map dimensions.
107 type Output;
108
109 /// Returns the size of the map as a `Vec2D` object.
110 ///
111 /// # Returns
112 /// A `Vec2D` representing the width (`x`) and height (`y`) of the map.
113 fn map_size() -> Vec2D<Self::Output>;
114}
115
116/// Implementation of the `MapSize` trait for the `I32F32` fixed-point number type.
117impl MapSize for I32F32 {
118 type Output = I32F32;
119
120 /// Defines the size of the map as a `Vec2D` with dimensions 21600.0 x 10800.0.
121 ///
122 /// # Returns
123 /// A `Vec2D` with fixed-point components representing the map dimensions.
124 fn map_size() -> Vec2D<I32F32> {
125 Vec2D { x: I32F32::from_num(21600.0), y: I32F32::from_num(10800.0) }
126 }
127}
128
129/// Implementation of the `MapSize` trait for the `I96F32` fixed-point number type.
130impl MapSize for I96F32 {
131 type Output = I96F32;
132
133 /// Defines the size of the map as a `Vec2D` with dimensions 21600.0 x 10800.0.
134 ///
135 /// # Returns
136 /// A `Vec2D` with fixed-point components representing the map dimensions.
137 fn map_size() -> Vec2D<I96F32> {
138 Vec2D { x: I96F32::from_num(21600.0), y: I96F32::from_num(10800.0) }
139 }
140}
141
142impl MapSize for f64 {
143 type Output = f64;
144 /// Defines the size of the map as a `Vec2D` with dimensions 21600.0 x 10800.0.
145 ///
146 /// # Returns
147 /// A `Vec2D` with floating-point components representing the map dimensions.
148 fn map_size() -> Vec2D<f64> { Vec2D { x: 21600.0, y: 10800.0 } }
149}
150
151/// Implementation of the `MapSize` trait for the `I32F0` fixed-point number type.
152impl MapSize for I32F0 {
153 type Output = I32F0;
154
155 /// Defines the size of the map as a `Vec2D` with dimensions 21600 x 10800.
156 ///
157 /// # Returns
158 /// A `Vec2D` with fixed-point integer components representing the map dimensions.
159 fn map_size() -> Vec2D<I32F0> { Vec2D { x: I32F0::from_num(21600), y: I32F0::from_num(10800) } }
160}
161
162/// Implementation of the `MapSize` trait for the `u32` type.
163impl MapSize for u32 {
164 type Output = u32;
165
166 /// Defines the size of the map as a `Vec2D` with dimensions 21600 x 10800.
167 ///
168 /// # Returns
169 /// A `Vec2D` with unsigned 32-bit integer components representing the map dimensions.
170 fn map_size() -> Vec2D<u32> { Vec2D { x: 21600, y: 10800 } }
171}
172
173/// Implementation of the `MapSize` trait for the `i32` type.
174impl MapSize for i32 {
175 type Output = i32;
176
177 /// Defines the size of the map as a `Vec2D` with dimensions 21600 x 10800.
178 ///
179 /// # Returns
180 /// A `Vec2D` with signed 32-bit integer components representing the map dimensions.
181 fn map_size() -> Vec2D<i32> { Vec2D { x: 21600, y: 10800 } }
182}
183
184/// Implementation of the `MapSize` trait for a `Vec2D` type with components
185/// that also implement the `MapSize` trait.
186impl<T> MapSize for Vec2D<T>
187where T: MapSize<Output = T>
188{
189 type Output = T;
190
191 /// Defines the size of the map by delegating to the `map_size` implementation of `T`.
192 ///
193 /// # Returns
194 /// A `Vec2D` with components of type `T` representing the map dimensions.
195 fn map_size() -> Vec2D<Self::Output> { T::map_size() }
196}
197
198impl<T> Vec2D<T>
199where T: FixedSigned + NumAssignOps
200{
201 /// Computes the magnitude (absolute value) of the vector.
202 ///
203 /// # Returns
204 /// The magnitude of the vector as a scalar of type `T`.
205 pub fn abs(&self) -> T { (self.x * self.x + self.y * self.y).sqrt() }
206
207 pub fn abs_sq(&self) -> T { self.x * self.x + self.y * self.y }
208
209 pub fn round(&self) -> Self { Self { x: self.x.round(), y: self.y.round() } }
210
211 pub fn round_to_2(&self) -> Self {
212 let factor = T::from_num(100);
213 let new_x = (self.x * factor).round() / factor;
214 let new_y = (self.y * factor).round() / factor;
215 Self { x: new_x, y: new_y }
216 }
217
218 pub fn floor(&self) -> Self { Vec2D::new(self.x.floor(), self.y.floor()) }
219
220 pub fn from_real<R>(&other: &Vec2D<R>) -> Self
221 where R: Copy + ToFixed {
222 Self { x: T::from_num(other.x()), y: T::from_num(other.y()) }
223 }
224
225 /// Creates a vector pointing from the current vector (`self`) to another vector (`other`).
226 ///
227 /// # Arguments
228 /// * `other` - The target vector.
229 ///
230 /// # Returns
231 /// A new vector representing the direction from `self` to `other`.
232 pub fn to(&self, other: &Self) -> Self { Vec2D::new(other.x - self.x, other.y - self.y) }
233
234 pub fn to_num<R: FromFixed + Copy>(self) -> Vec2D<R> {
235 Vec2D::new(self.x.to_num::<R>(), self.y.to_num::<R>())
236 }
237
238 pub fn project_overboundary_fw(&self, dir: &Self) -> Self {
239 let map_size = I32F32::map_size().to_num::<T>();
240 let t_x = if dir.x().is_positive() {
241 (map_size.x() - self.x()) / dir.x()
242 } else if dir.x().is_negative() {
243 (T::ZERO - self.x()) / dir.x()
244 } else {
245 T::MAX
246 };
247
248 // intersection times with horizontal boundaries
249 let t_y = if dir.y().is_positive() {
250 (map_size.y() - self.y()) / dir.y()
251 } else if dir.y().is_negative() {
252 (T::ZERO - self.y()) / dir.y()
253 } else {
254 T::MAX // no intersection vertically, moving horizontally
255 };
256 let t_adjust = t_x.min(t_y).ceil();
257 *self + *dir * t_adjust
258 }
259
260 pub fn project_overboundary_bw(&self, dir: &Self) -> Self {
261 let map_size = I32F32::map_size().to_num::<T>();
262 let t_x = if dir.x().is_positive() {
263 self.x() / dir.x()
264 } else if dir.x().is_negative() {
265 (self.x() - map_size.x()) / dir.x()
266 } else {
267 T::MAX
268 };
269
270 let t_y = if dir.y().is_positive() {
271 self.y() / dir.y()
272 } else if dir.y().is_negative() {
273 (self.y() - map_size.y()) / dir.y()
274 } else {
275 T::MAX
276 };
277
278 let t_adjust = t_x.min(t_y).ceil();
279 *self + *dir * (t_adjust * T::from_num(-1.0))
280 }
281
282 /// Computes an "unwrapped" vector pointing from the current vector (`self`) to another vector (`other`).
283 ///
284 /// This method considers potential wrapping around a 2D map (based on the map size) and calculates
285 /// the smallest vector that connects `self` to `other`. The wrapping allows for efficient navigation
286 /// across boundaries.
287 ///
288 /// # Arguments
289 /// * `other` - The target vector to which the direction is computed.
290 ///
291 /// # Returns
292 /// A `Vec2D` representing the shortest unwrapped direction from `self` to `other`.
293 pub fn unwrapped_to(&self, other: &Self) -> Self {
294 let options = self.get_projected_in_range(other, (&[1, 0, -1], &[1, 0, -1]));
295 options.into_iter().min_by(|a, b| a.1.cmp(&b.1)).unwrap().0
296 }
297
298 pub fn unwrap_all(&self) -> [Self; 9] {
299 let options = self.get_projected_in_range(self, (&[1, 0, -1], &[1, 0, -1]));
300 options.into_iter().take(9).map(|x| x.0 + *self).collect::<Vec<_>>().try_into().unwrap()
301 }
302
303 pub fn unwrapped_to_top_right(&self, other: &Self) -> Self {
304 let options = self.get_projected_in_range(other, (&[1, 0], &[1, 0]));
305 options.into_iter().min_by(|a, b| a.1.cmp(&b.1)).unwrap().0
306 }
307
308 pub fn unwrapped_to_bottom_right(&self, other: &Self) -> Self {
309 let options = self.get_projected_in_range(other, (&[1, 0], &[-1, 0]));
310 options.into_iter().min_by(|a, b| a.1.cmp(&b.1)).unwrap().0
311 }
312
313 fn get_projected_in_range(&self, to: &Self, range: (&[i8], &[i8])) -> Vec<(Self, I64F64)> {
314 let mut options = Vec::new();
315 for x_sign in range.0 {
316 for y_sign in range.1 {
317 let target: Vec2D<T> = Vec2D::new(
318 to.x + T::from_num(u32::map_size().x()) * T::from_num(*x_sign),
319 to.y + T::from_num(u32::map_size().y()) * T::from_num(*y_sign),
320 );
321 let to_target = self.to(&target);
322 let tt_scale =
323 Vec2D::new(I64F64::from_num(to_target.x), I64F64::from_num(to_target.y));
324 let to_target_abs_sq = tt_scale.abs_sq();
325 options.push((to_target, to_target_abs_sq));
326 }
327 }
328 options
329 }
330
331 /// Computes a perpendicular unit vector pointing to another vector (`other`).
332 ///
333 /// The direction of the perpendicular vector depends on whether `self` is clockwise
334 /// or counterclockwise to `other`.
335 ///
336 /// # Arguments
337 /// * `other` - The target vector to compare directions with.
338 ///
339 /// # Returns
340 /// A unit `Vec2D` perpendicular to `self` pointing towards `other`.
341 /// Returns a zero vector if `self` and `other` are collinear.
342 pub fn perp_unit_to(&self, other: &Self) -> Self {
343 match self.is_clockwise_to(other) {
344 Some(dir) => self.perp_unit(dir),
345 None => Self::zero(),
346 }
347 }
348
349 /// Computes a perpendicular unit vector to the current vector.
350 ///
351 /// The direction of the perpendicular vector depends on the `clockwise` parameter.
352 ///
353 /// # Arguments
354 /// * `clockwise` - A boolean indicating the direction. `true` for clockwise, `false` for counterclockwise.
355 ///
356 /// # Returns
357 /// A normalized perpendicular `Vec2D`.
358 pub fn perp_unit(&self, clockwise: bool) -> Self {
359 let perp = if clockwise { Self::new(self.y, -self.x) } else { Self::new(-self.y, self.x) };
360 perp.normalize()
361 }
362
363 /// Computes a flipped collinear unit vector to the current vector.
364 ///
365 /// This is equivalent to rotating the vector 180 degrees.
366 ///
367 /// # Returns
368 /// A normalized flipped collinear `Vec2D`.
369 pub fn flip_unit(&self) -> Self {
370 let flip = Self::new(-self.y, -self.x);
371 flip.normalize()
372 }
373
374 /// Determines whether the current vector is clockwise relative to another vector (`other`).
375 ///
376 /// This is determined using the cross product:
377 /// * `Some(true)` if `self` is clockwise to `other`.
378 /// * `Some(false)` if `self` is counterclockwise to `other`.
379 /// * `None` if `self` and `other` are collinear.
380 ///
381 /// # Arguments
382 /// * `other` - The vector to compare relative direction with.
383 ///
384 /// # Returns
385 /// An `Option<bool>` indicating the relative direction.
386 pub fn is_clockwise_to(&self, other: &Self) -> Option<bool> {
387 let cross = self.cross(other);
388 match cross.partial_cmp(&T::zero()) {
389 Some(Ordering::Less) => Some(true),
390 Some(Ordering::Greater) => Some(false),
391 _ => None,
392 }
393 }
394
395 /// Computes the angle (in degrees) between the current vector and another vector (`other`).
396 ///
397 /// The method calculates the cosine of the angle using the dot product, clamps it to
398 /// the valid range of `[-1, 1]`, and then computes the angle in degrees.
399 ///
400 /// # Arguments
401 /// * `other` - The target vector to compute the angle to.
402 ///
403 /// # Returns
404 /// The angle in degrees as a scalar of type `T`.
405 pub fn angle_to(&self, other: &Self) -> T {
406 let dot = self.dot(other);
407
408 let a_abs = self.abs();
409 let b_abs = other.abs();
410
411 if a_abs == 0.0 || b_abs == 0.0 {
412 return T::zero();
413 }
414 let cos_theta = dot / (a_abs * b_abs);
415 let clamped_cos_theta = cos_theta.clamp(T::from_num(-1.0), T::from_num(1.0));
416 let angle_radians = T::from_num(clamped_cos_theta.to_num::<f64>().acos());
417 angle_radians * T::from_num(180.0) / T::PI()
418 }
419
420 /// Normalizes the vector to have a magnitude of 1.
421 /// If the magnitude is zero, the original vector is returned unmodified.
422 ///
423 /// # Returns
424 /// A normalized vector.
425 pub fn normalize(self) -> Self {
426 let magnitude = self.abs();
427 if magnitude.is_zero() { self } else { Self::new(self.x / magnitude, self.y / magnitude) }
428 }
429
430 /// Rotates the vector by a given angle in degrees.
431 ///
432 /// # Arguments
433 /// * `angle_degrees` - The angle to rotate by, in degrees.
434 pub fn rotate_by(&mut self, angle_degrees: T) {
435 let angle_radians = angle_degrees.to_num::<f64>().to_radians();
436 let sin = T::from_num(angle_radians.sin());
437 let cos = T::from_num(angle_radians.cos());
438 let new_x = self.x * cos - self.y * sin;
439 self.y = self.x * sin + self.y * cos;
440 self.x = new_x;
441 }
442
443 pub fn is_eq_signum(&self, other: &Self) -> bool {
444 self.x().signum() == other.x().signum() && self.y().signum() == other.y().signum()
445 }
446
447 /// Computes the Euclidean distance between the current vector and another vector.
448 ///
449 /// # Arguments
450 /// * `other` - The other vector to compute the distance to.
451 ///
452 /// # Returns
453 /// The Euclidean distance as a scalar of type `T`.
454 pub fn euclid_distance(&self, other: &Self) -> T { self.euclid_distance_sq(other).sqrt() }
455
456 /// Computes the Euclidean distance squared.
457 ///
458 /// # Arguments
459 /// * `other` - The other vector to compute the distance to.
460 ///
461 /// # Returns
462 /// The Euclidean distance squared as a scalar of type `T`.
463 pub fn euclid_distance_sq(&self, other: &Self) -> T {
464 (self.x - other.x) * (self.x - other.x) + (self.y - other.y) * (self.y - other.y)
465 }
466
467 pub fn from_axis_and_val(axis: VecAxis, val: T) -> Self {
468 match axis {
469 VecAxis::X => Self { x: val, y: T::zero() },
470 VecAxis::Y => Self { x: T::zero(), y: val },
471 }
472 }
473}
474
475impl<T: Copy> Vec2D<T> {
476 /// Creates a new vector with the given x and y components.
477 ///
478 /// # Arguments
479 /// * `x` - The x-component of the vector.
480 /// * `y` - The y-component of the vector.
481 ///
482 /// # Returns
483 /// A new `Vec2D` object.
484 pub const fn new(x: T, y: T) -> Self { Self { x, y } }
485
486 /// Returns the x-component of the vector.
487 ///
488 /// # Returns
489 /// The `x` value of type `T`.
490 pub const fn x(&self) -> T { self.x }
491
492 /// Returns the y-component of the vector.
493 ///
494 /// # Returns
495 /// The `y` value of type `T`.
496 pub const fn y(&self) -> T { self.y }
497
498 pub const fn get(&self, axis: VecAxis) -> T {
499 match axis {
500 VecAxis::X => self.x,
501 VecAxis::Y => self.y,
502 }
503 }
504}
505
506impl<T: Fixed + Copy> Vec2D<T> {
507 /// Computes the dot product of the current vector with another vector.
508 /// The dot product is defined as:
509 ///
510 /// ```text
511 /// dot_product = (x1 * x2) + (y1 * y2)
512 /// ```
513 ///
514 /// # Arguments
515 /// * `other` - Another `Vec2D` vector to compute the dot product with.
516 ///
517 /// # Returns
518 /// A scalar value of type `T` that represents the dot product of the two vectors.
519 pub fn dot(self, other: &Self) -> T { self.x * other.x + self.y * other.y }
520
521 /// Computes the cross product of the current vector with another vector.
522 ///
523 /// The cross product is defined as:
524 ///
525 /// ```text
526 /// cross_product = (x1 * y2) - (y1 * x2)
527 /// ```
528 ///
529 /// # Arguments
530 /// * `other` - Another `Vec2D` vector to compute the cross product with.
531 ///
532 /// # Returns
533 /// A scalar value of type `T` that represents the cross product of the two vectors.
534 pub fn cross(self, other: &Self) -> T { self.x * other.y - self.y * other.x }
535
536 /// Computes the magnitude (absolute value) of the vector as an `f64`.
537 /// This enables magnitude calculation for integer types `T`.
538 ///
539 /// # Returns
540 /// The magnitude of the vector as an `f64`.
541 pub fn abs_f64(self) -> f64 { (self.x * self.x + self.y * self.y).to_f64().unwrap().sqrt() }
542
543 /// Creates a zero vector (x = 0, y = 0).
544 ///
545 /// # Returns
546 /// A zero-initialized `Vec2D` with member type `T`.
547 pub fn zero() -> Self { Self::new(T::zero(), T::zero()) }
548
549 pub fn is_zero(&self) -> bool { self.x.is_zero() && self.y.is_zero() }
550}
551
552impl Vec2D<i32> {
553 /// Converts the vector to an unsigned equivalent.
554 ///
555 /// This method casts both the x and y components of the vector from `i32` to `u32`.
556 ///
557 /// # Returns
558 /// A `Vec2D<u32>` with the unsigned components of the original vector.
559 ///
560 /// # Note
561 /// The conversion may cause loss of sign. Negative values will wrap around.
562 #[allow(clippy::cast_sign_loss)]
563 pub fn to_unsigned(self) -> Vec2D<u32> { Vec2D { x: self.x as u32, y: self.y as u32 } }
564}
565
566pub enum WrapDirection {
567 None,
568 WrapX,
569 WrapY,
570 Both,
571}
572
573impl<T> Vec2D<T>
574where T: Add<Output = T> + Rem<Output = T> + Copy + MapSize<Output = T> + PartialEq
575{
576 /// Wraps the vector around a predefined 2D map.
577 ///
578 /// This method ensures the vector’s coordinates do not exceed the boundaries
579 /// of the map defined by `map_size()`. If coordinates go beyond these boundaries,
580 /// they are wrapped to remain within valid values.
581 pub fn wrap_around_map(&self) -> Self {
582 let map_size_x = T::map_size().x;
583 let map_size_y = T::map_size().y;
584
585 Vec2D::new(
586 Self::wrap_coordinate(self.x, map_size_x),
587 Self::wrap_coordinate(self.y, map_size_y),
588 )
589 }
590
591 pub fn wraps(&self) -> WrapDirection {
592 let map_size_x = T::map_size().x;
593 let map_size_y = T::map_size().y;
594 let wrapped_y = Self::wrap_coordinate(self.y, map_size_y);
595 let wrapped_x = Self::wrap_coordinate(self.x, map_size_x);
596 if wrapped_x == self.x && wrapped_y == self.y {
597 WrapDirection::None
598 } else if wrapped_x == self.x {
599 WrapDirection::WrapY
600 } else if wrapped_y == self.y {
601 WrapDirection::WrapX
602 } else {
603 WrapDirection::Both
604 }
605 }
606
607 pub fn wrap_by(&self, dir: &WrapDirection) -> Self {
608 match dir {
609 WrapDirection::None => *self,
610 WrapDirection::WrapX => {
611 let map_size_x = T::map_size().x;
612 let wrapped_x = Self::wrap_coordinate(self.x, map_size_x);
613 Vec2D::new(wrapped_x, self.y)
614 }
615 WrapDirection::WrapY => {
616 let map_size_y = T::map_size().y;
617 let wrapped_y = Self::wrap_coordinate(self.y, map_size_y);
618 Vec2D::new(self.x, wrapped_y)
619 }
620 WrapDirection::Both => self.wrap_around_map(),
621 }
622 }
623
624 /// Wraps a single coordinate around a specific maximum value.
625 ///
626 /// # Arguments
627 /// * `value` - The value to wrap.
628 /// * `max_value` - The maximum value to wrap around.
629 ///
630 /// # Returns
631 /// The wrapped coordinate as type `T`.
632 pub fn wrap_coordinate(value: T, max_value: T) -> T {
633 ((value % max_value) + max_value) % max_value
634 }
635}
636
637impl<T> Add for Vec2D<T>
638where T: Add<Output = T>
639{
640 type Output = Vec2D<T>;
641
642 /// Implements the `+` operator for two `Vec2D` objects.
643 ///
644 /// # Arguments
645 /// * `rhs` - The vector to add.
646 ///
647 /// # Returns
648 /// A new `Vec2D` representing the sum of the vectors.
649 fn add(self, rhs: Vec2D<T>) -> Self::Output {
650 Self::Output { x: self.x + rhs.x, y: self.y + rhs.y }
651 }
652}
653
654impl<T, TMul> Mul<TMul> for Vec2D<T>
655where
656 T: Fixed,
657 TMul: Fixed + Copy,
658{
659 type Output = Vec2D<T>;
660
661 /// Implements the `*` operator for a [`Vec2D`] and a scalar.
662 ///
663 /// # Arguments
664 /// * `rhs` - The scalar value to multiply by.
665 ///
666 /// # Returns
667 /// A new scaled vector.
668 fn mul(self, rhs: TMul) -> Self::Output {
669 Self::Output { x: self.x * T::from_num(rhs), y: self.y * T::from_num(rhs) }
670 }
671}
672
673impl<T> Div<T> for Vec2D<T>
674where T: Div<T, Output = T> + Copy
675{
676 type Output = Vec2D<T>;
677
678 /// Implements the `/` operator for a `Vec2D` and a scalar.
679 ///
680 /// # Arguments
681 /// * `rhs` - The scalar value to divide by.
682 ///
683 /// # Returns
684 /// A new scaled vector.
685 fn div(self, rhs: T) -> Self::Output { Self::Output { x: self.x / rhs, y: self.y / rhs } }
686}
687
688impl<T, TSub> Sub<Vec2D<TSub>> for Vec2D<T>
689where
690 T: FixedSigned,
691 TSub: Fixed,
692{
693 type Output = Vec2D<T>;
694
695 /// Implements the `-` operator for two `Vec2D`.
696 ///
697 /// # Arguments
698 /// * `rhs` - The `Vec2D` to subtract.
699 ///
700 /// # Returns
701 /// A new vector.
702 fn sub(self, rhs: Vec2D<TSub>) -> Self::Output {
703 Self::Output { x: self.x - T::from_num(rhs.x), y: self.y - T::from_num(rhs.y) }
704 }
705}
706
707impl<T: Num> From<(T, T)> for Vec2D<T> {
708 /// Creates a `Vec2D` from a tuple of (x, y) values.
709 ///
710 /// # Arguments
711 /// * `tuple` - A tuple representing the x and y values.
712 ///
713 /// # Returns
714 /// A new `Vec2D` created from the tuple.
715 fn from(tuple: (T, T)) -> Self { Vec2D { x: tuple.0, y: tuple.1 } }
716}
717
718impl<T: Num> From<Vec2D<T>> for (T, T) {
719 /// Creates a tuple from a `Vec2D` of (x, y) values.
720 ///
721 /// # Arguments
722 /// * `tuple` - A tuple representing the x and y values.
723 ///
724 /// # Returns
725 /// A new `Vec2D` created from the tuple.
726 fn from(value: Vec2D<T>) -> Self { (value.x, value.y) }
727}
728
729impl<T> From<&[T; 2]> for Vec2D<T>
730where T: Copy
731{
732 /// Creates a `Vec2D` from a slice of (x, y) values.
733 ///
734 /// # Arguments
735 /// * `slice` - A slice representing the x and y values.
736 ///
737 /// # Returns
738 /// A new `Vec2D` created from the slice.
739 fn from(slice: &[T; 2]) -> Self { Self { x: slice[0], y: slice[1] } }
740}
741
742impl<T> From<Vec2D<T>> for [T; 2]
743where T: Copy
744{
745 /// Creates a slice [x, y] from a `Vec2D`.
746 ///
747 /// # Arguments
748 /// * `vec` - A `Vec2D` representing the x and y values.
749 ///
750 /// # Returns
751 /// A new slice created from the `Vec2D`.
752 fn from(vec: Vec2D<T>) -> Self { [vec.x(), vec.y()] }
753}