cnccoder/types/
vector.rs

1use std::{
2    f64::{self, consts::PI},
3    fmt,
4    ops::{Add, Div, Mul, Sub},
5};
6
7use serde::{Deserialize, Deserializer, Serialize, Serializer};
8
9use crate::utils::round_precision;
10
11// Used to deserialize a struct as a tuple.
12macro_rules! as_serde_tuple {
13    ($(#[$smeta:meta])*
14        $svis:vis struct $sname:ident {
15            $($fvis:vis $fname:ident : $ftype:ty,)*
16    }) => {
17        $(#[$smeta])*
18        $svis struct $sname {
19            $($fvis $fname : $ftype,)*
20        }
21
22        impl<'de> Deserialize<'de> for $sname {
23            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
24                where D: Deserializer<'de>
25            {
26                #[derive(Deserialize, Serialize)]
27                pub struct Array($(pub $ftype,)*);
28
29                Deserialize::deserialize(deserializer)
30                    .map(|Array($($fname,)*)| Self { $($fname,)* })
31            }
32        }
33
34        impl Serialize for $sname {
35            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
36            where
37                S: Serializer,
38            {
39                #[derive(Deserialize, Serialize)]
40                pub struct Array($(pub $ftype,)*);
41
42                (Array($(self.$fname.clone(),)*)).serialize(serializer)
43            }
44        }
45    }
46}
47
48as_serde_tuple! {
49    #[allow(missing_docs)]
50    /// Represents a 2D point in space.
51    #[derive(Default, Debug, PartialEq, Clone, Copy)]
52    pub struct Vector2 {
53        pub x: f64,
54        pub y: f64,
55
56    }
57}
58
59impl Vector2 {
60    /// An all zero `Vector2` value
61    pub const ZERO: Self = Self { x: 0.0, y: 0.0 };
62
63    /// Create a 2D point struct using `f64::MIN` for x, y and z coordinates.
64    pub const MIN: Self = Self {
65        x: f64::MIN,
66        y: f64::MIN,
67    };
68
69    /// Create a 2D point struct using `f64::MAX` for x, y and z coordinates.
70    pub const MAX: Self = Self {
71        x: f64::MAX,
72        y: f64::MAX,
73    };
74
75    /// Create a 2D point struct from x and y coordinates.
76    #[must_use]
77    pub fn new(x: f64, y: f64) -> Self {
78        Self { x, y }
79    }
80
81    /// Create a 2D point struct from a single value.
82    #[must_use]
83    pub fn splat(value: f64) -> Self {
84        Self { x: value, y: value }
85    }
86
87    /// Create a 2D point struct using `f64::MIN` for x and y coordinates.
88    #[must_use]
89    #[deprecated]
90    pub fn min() -> Self {
91        Self {
92            x: f64::MIN,
93            y: f64::MIN,
94        }
95    }
96
97    /// Create a 2D point struct using `f64::MAX` for x and y coordinates.
98    #[must_use]
99    #[deprecated]
100    pub fn max() -> Self {
101        Self {
102            x: f64::MAX,
103            y: f64::MAX,
104        }
105    }
106
107    /// Calculate the distance to another `Vector2` struct.
108    #[must_use]
109    pub fn distance_to(&self, to: Self) -> f64 {
110        ((self.x - to.x) * (self.x - to.x) + (self.y - to.y) * (self.y - to.y)).sqrt()
111    }
112
113    /// Computes the angle in radians with respect to the positive x-axis.
114    #[must_use]
115    pub fn angle(&self) -> f64 {
116        (-self.x).atan2(-self.y) + PI
117    }
118
119    /// Computes the angle in degrees with respect to the positive x-axis.
120    #[must_use]
121    pub fn angle_degrees(&self) -> f64 {
122        self.angle().to_degrees()
123    }
124
125    /// Returns a new `Vector2` incrementing the x coordinate by the given value.
126    #[must_use]
127    pub fn add_x(&self, value: f64) -> Self {
128        let mut vector = *self;
129        vector.x += value;
130        vector
131    }
132
133    /// Returns a new `Vector2` incrementing the y coordinate by the given value.
134    #[must_use]
135    pub fn add_y(&self, value: f64) -> Self {
136        let mut vector = *self;
137        vector.y += value;
138        vector
139    }
140
141    /// Returns a new `Vector2` setting the x coordinate to the given value.
142    #[must_use]
143    pub fn with_x(&self, value: f64) -> Self {
144        let mut vector = *self;
145        vector.x = value;
146        vector
147    }
148
149    /// Returns a new `Vector2` setting the y coordinate to the given value.
150    #[must_use]
151    pub fn with_y(&self, value: f64) -> Self {
152        let mut vector = *self;
153        vector.y = value;
154        vector
155    }
156}
157
158impl Add for Vector2 {
159    type Output = Vector2;
160
161    fn add(self, rhs: Vector2) -> Vector2 {
162        Vector2 {
163            x: self.x + rhs.x,
164            y: self.y + rhs.y,
165        }
166    }
167}
168
169impl Sub for Vector2 {
170    type Output = Vector2;
171
172    fn sub(self, rhs: Vector2) -> Vector2 {
173        Vector2 {
174            x: self.x - rhs.x,
175            y: self.y - rhs.y,
176        }
177    }
178}
179
180impl Mul for Vector2 {
181    type Output = Vector2;
182
183    fn mul(self, rhs: Vector2) -> Vector2 {
184        Vector2 {
185            x: self.x * rhs.x,
186            y: self.y * rhs.y,
187        }
188    }
189}
190
191impl Div for Vector2 {
192    type Output = Vector2;
193
194    fn div(self, rhs: Vector2) -> Vector2 {
195        Vector2 {
196            x: self.x / rhs.x,
197            y: self.y / rhs.y,
198        }
199    }
200}
201
202impl fmt::Display for Vector2 {
203    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
204        write!(
205            formatter,
206            "{{x: {}, y: {}}}",
207            round_precision(self.x),
208            round_precision(self.y)
209        )
210    }
211}
212
213#[cfg(feature = "glam")]
214impl From<glam::Vec2> for Vector2 {
215    fn from(value: glam::Vec2) -> Self {
216        Self {
217            x: value.x as f64,
218            y: value.y as f64,
219        }
220    }
221}
222
223#[cfg(feature = "glam")]
224impl From<Vector2> for glam::Vec2 {
225    fn from(value: Vector2) -> Self {
226        Self {
227            x: value.x as f32,
228            y: value.y as f32,
229        }
230    }
231}
232
233#[cfg(feature = "glam")]
234impl From<glam::DVec2> for Vector2 {
235    fn from(value: glam::DVec2) -> Self {
236        Self {
237            x: value.x,
238            y: value.y,
239        }
240    }
241}
242
243#[cfg(feature = "glam")]
244impl From<Vector2> for glam::DVec2 {
245    fn from(value: Vector2) -> Self {
246        Self {
247            x: value.x,
248            y: value.y,
249        }
250    }
251}
252
253#[cfg(feature = "nalgebra")]
254impl From<nalgebra::Point2<f32>> for Vector2 {
255    fn from(value: nalgebra::Point2<f32>) -> Self {
256        Self {
257            x: value.x as f64,
258            y: value.y as f64,
259        }
260    }
261}
262
263#[cfg(feature = "nalgebra")]
264impl From<Vector2> for nalgebra::Point2<f32> {
265    fn from(value: Vector2) -> Self {
266        Self::new(value.x as f32, value.y as f32)
267    }
268}
269
270#[cfg(feature = "nalgebra")]
271impl From<nalgebra::Point2<f64>> for Vector2 {
272    fn from(value: nalgebra::Point2<f64>) -> Self {
273        Self {
274            x: value.x,
275            y: value.y,
276        }
277    }
278}
279
280#[cfg(feature = "nalgebra")]
281impl From<Vector2> for nalgebra::Point2<f64> {
282    fn from(value: Vector2) -> Self {
283        Self::new(value.x, value.y)
284    }
285}
286
287as_serde_tuple! {
288    #[allow(missing_docs)]
289    /// Represents a 3D point in space.
290    #[derive(Default, Debug, PartialEq, Clone, Copy)]
291    pub struct Vector3 {
292        pub x: f64,
293        pub y: f64,
294        pub z: f64,
295    }
296}
297
298impl Vector3 {
299    /// An all zero `Vector3` value
300    pub const ZERO: Self = Self {
301        x: 0.0,
302        y: 0.0,
303        z: 0.0,
304    };
305
306    /// Create a 3D point struct using `f64::MIN` for x, y and z coordinates.
307    pub const MIN: Self = Self {
308        x: f64::MIN,
309        y: f64::MIN,
310        z: f64::MIN,
311    };
312
313    /// Create a 3D point struct using `f64::MAX` for x, y and z coordinates.
314    pub const MAX: Self = Self {
315        x: f64::MAX,
316        y: f64::MAX,
317        z: f64::MAX,
318    };
319
320    /// Create a 3D point struct from x, y and z coordinates.
321    #[must_use]
322    pub fn new(x: f64, y: f64, z: f64) -> Self {
323        Self { x, y, z }
324    }
325
326    /// Create a 3D point struct from a single value.
327    #[must_use]
328    pub fn splat(value: f64) -> Self {
329        Self {
330            x: value,
331            y: value,
332            z: value,
333        }
334    }
335
336    /// Create a 3D point struct using `f64::MIN` for x, y and z coordinates.
337    #[must_use]
338    #[deprecated]
339    pub fn min() -> Self {
340        Self {
341            x: f64::MIN,
342            y: f64::MIN,
343            z: f64::MIN,
344        }
345    }
346
347    /// Create a 3D point struct using `f64::MAX` for x, y and z coordinates.
348    #[must_use]
349    #[deprecated]
350    pub fn max() -> Self {
351        Self {
352            x: f64::MAX,
353            y: f64::MAX,
354            z: f64::MAX,
355        }
356    }
357
358    /// Calculate the distance to another `Vector3` struct.
359    #[must_use]
360    pub fn distance_to(&self, to: Self) -> f64 {
361        ((self.x - to.x) * (self.x - to.x)
362            + (self.y - to.y) * (self.y - to.y)
363            + (self.z - to.z) * (self.z - to.z))
364            .sqrt()
365    }
366
367    /// Returns a `Vector2` struct using the x and y coordinates from this `Vector3` struct.
368    #[must_use]
369    pub fn xy(&self) -> Vector2 {
370        Vector2::new(self.x, self.y)
371    }
372
373    /// Returns a `Vector2` struct using the x and z coordinates from this `Vector3` struct.
374    #[must_use]
375    pub fn xz(&self) -> Vector2 {
376        Vector2::new(self.x, self.z)
377    }
378
379    /// Returns a `Vector2` struct using the y and z coordinates from this `Vector3` struct.
380    #[must_use]
381    pub fn yz(&self) -> Vector2 {
382        Vector2::new(self.y, self.z)
383    }
384
385    /// Returns a new `Vector3` incrementing the x coordinate by the given value.
386    #[must_use]
387    pub fn add_x(&self, value: f64) -> Self {
388        let mut vector = *self;
389        vector.x += value;
390        vector
391    }
392
393    /// Returns a new `Vector3` incrementing the y coordinate by the given value.
394    #[must_use]
395    pub fn add_y(&self, value: f64) -> Self {
396        let mut vector = *self;
397        vector.y += value;
398        vector
399    }
400
401    /// Returns a new `Vector3` incrementing the z coordinate by the given value.
402    #[must_use]
403    pub fn add_z(&self, value: f64) -> Self {
404        let mut vector = *self;
405        vector.z += value;
406        vector
407    }
408
409    /// Returns a new `Vector3` setting the x coordinate to the given value.
410    #[must_use]
411    pub fn with_x(&self, value: f64) -> Self {
412        let mut vector = *self;
413        vector.x = value;
414        vector
415    }
416
417    /// Returns a new `Vector3` setting the y coordinate to the given value.
418    #[must_use]
419    pub fn with_y(&self, value: f64) -> Self {
420        let mut vector = *self;
421        vector.y = value;
422        vector
423    }
424
425    /// Returns a new `Vector3` setting the z coordinate to the given value.
426    #[must_use]
427    pub fn with_z(&self, value: f64) -> Self {
428        let mut vector = *self;
429        vector.z = value;
430        vector
431    }
432}
433
434impl Add for Vector3 {
435    type Output = Vector3;
436
437    fn add(self, rhs: Vector3) -> Vector3 {
438        Vector3 {
439            x: self.x + rhs.x,
440            y: self.y + rhs.y,
441            z: self.z + rhs.z,
442        }
443    }
444}
445
446impl Sub for Vector3 {
447    type Output = Vector3;
448
449    fn sub(self, rhs: Vector3) -> Vector3 {
450        Vector3 {
451            x: self.x - rhs.x,
452            y: self.y - rhs.y,
453            z: self.z - rhs.z,
454        }
455    }
456}
457
458impl Mul for Vector3 {
459    type Output = Vector3;
460
461    fn mul(self, rhs: Vector3) -> Vector3 {
462        Vector3 {
463            x: self.x * rhs.x,
464            y: self.y * rhs.y,
465            z: self.z * rhs.z,
466        }
467    }
468}
469
470impl Div for Vector3 {
471    type Output = Vector3;
472
473    fn div(self, rhs: Vector3) -> Vector3 {
474        Vector3 {
475            x: self.x / rhs.x,
476            y: self.y / rhs.y,
477            z: self.z / rhs.z,
478        }
479    }
480}
481
482impl fmt::Display for Vector3 {
483    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
484        write!(
485            formatter,
486            "{{x: {}, y: {}, z: {}}}",
487            round_precision(self.x),
488            round_precision(self.y),
489            round_precision(self.z)
490        )
491    }
492}
493
494#[cfg(feature = "glam")]
495impl From<glam::Vec3> for Vector3 {
496    fn from(value: glam::Vec3) -> Self {
497        Self {
498            x: value.x as f64,
499            y: value.y as f64,
500            z: value.z as f64,
501        }
502    }
503}
504
505#[cfg(feature = "glam")]
506impl From<Vector3> for glam::Vec3 {
507    fn from(value: Vector3) -> Self {
508        Self {
509            x: value.x as f32,
510            y: value.y as f32,
511            z: value.z as f32,
512        }
513    }
514}
515
516#[cfg(feature = "glam")]
517impl From<glam::DVec3> for Vector3 {
518    fn from(value: glam::DVec3) -> Self {
519        Self {
520            x: value.x,
521            y: value.y,
522            z: value.z,
523        }
524    }
525}
526
527#[cfg(feature = "glam")]
528impl From<Vector3> for glam::DVec3 {
529    fn from(value: Vector3) -> Self {
530        Self {
531            x: value.x,
532            y: value.y,
533            z: value.z,
534        }
535    }
536}
537
538#[cfg(feature = "nalgebra")]
539impl From<nalgebra::Point3<f32>> for Vector3 {
540    fn from(value: nalgebra::Point3<f32>) -> Self {
541        Self {
542            x: value.x as f64,
543            y: value.y as f64,
544            z: value.z as f64,
545        }
546    }
547}
548
549#[cfg(feature = "nalgebra")]
550impl From<Vector3> for nalgebra::Point3<f32> {
551    fn from(value: Vector3) -> Self {
552        Self::new(value.x as f32, value.y as f32, value.z as f32)
553    }
554}
555
556#[cfg(feature = "nalgebra")]
557impl From<nalgebra::Point3<f64>> for Vector3 {
558    fn from(value: nalgebra::Point3<f64>) -> Self {
559        Self {
560            x: value.x,
561            y: value.y,
562            z: value.z,
563        }
564    }
565}
566
567#[cfg(feature = "nalgebra")]
568impl From<Vector3> for nalgebra::Point3<f64> {
569    fn from(value: Vector3) -> Self {
570        Self::new(value.x, value.y, value.z)
571    }
572}
573
574#[cfg(test)]
575mod tests {
576    use super::*;
577
578    #[test]
579    fn test_vector3_min() {
580        let vector = Vector3::MIN;
581        assert!(vector.x == f64::MIN);
582        assert!(vector.y == f64::MIN);
583        assert!(vector.z == f64::MIN);
584    }
585
586    #[test]
587    fn test_vector3_max() {
588        let vector = Vector3::MAX;
589        assert!(vector.x == f64::MAX);
590        assert!(vector.y == f64::MAX);
591        assert!(vector.z == f64::MAX);
592    }
593
594    #[test]
595    fn test_vector3_xy_xz_yz() {
596        let vector = Vector3::new(1.0, 2.0, 3.0);
597        assert!(vector.xy() == Vector2::new(1.0, 2.0));
598        assert!(vector.xz() == Vector2::new(1.0, 3.0));
599        assert!(vector.yz() == Vector2::new(2.0, 3.0));
600    }
601
602    #[test]
603    fn test_vector3_add_xyz() {
604        let vector = Vector3::default();
605
606        let vector = vector.add_x(1.0);
607        assert!(vector.x == 1.0);
608        assert!(vector.y == 0.0);
609        assert!(vector.z == 0.0);
610
611        let vector = vector.add_y(-1.0);
612        assert!(vector.x == 1.0);
613        assert!(vector.y == -1.0);
614        assert!(vector.z == 0.0);
615
616        let vector = vector.add_z(3.0);
617        assert!(vector.x == 1.0);
618        assert!(vector.y == -1.0);
619        assert!(vector.z == 3.0);
620    }
621
622    #[test]
623    fn test_vector2_min() {
624        let vector = Vector2::MIN;
625        assert!(vector.x == f64::MIN);
626        assert!(vector.y == f64::MIN);
627    }
628
629    #[test]
630    fn test_vector2_max() {
631        let vector = Vector2::MAX;
632        assert!(vector.x == f64::MAX);
633        assert!(vector.y == f64::MAX);
634    }
635
636    #[test]
637    fn test_vector2_add_xyz() {
638        let vector = Vector2::default();
639
640        let vector = vector.add_x(1.0);
641        assert!(vector.x == 1.0);
642        assert!(vector.y == 0.0);
643
644        let vector = vector.add_y(-1.0);
645        assert!(vector.x == 1.0);
646        assert!(vector.y == -1.0);
647    }
648
649    #[test]
650    fn test_vector2_distance_to() {
651        let vector_a = Vector2::new(20.0, 40.0);
652        let vector_b = Vector2::new(20.0, 20.0);
653        assert!(vector_a.distance_to(vector_b) == 20.0);
654    }
655
656    #[test]
657    fn test_vector2_angle() {
658        let vector = Vector2::new(20.0, 0.0);
659        assert!(vector.angle() == f64::consts::PI / 2.0);
660    }
661
662    #[test]
663    fn test_vector2_angle_degree() {
664        let vector = Vector2::new(20.0, 20.0);
665        assert!(vector.angle_degrees() == 45.0);
666    }
667
668    #[cfg(feature = "glam")]
669    #[test]
670    fn test_glam_from_into() {
671        let v = Vector3::new(23.1, 5.0, 0.0);
672
673        let b: glam::DVec3 = v.into();
674        assert_eq!(b, glam::DVec3::new(23.1, 5.0, 0.0));
675
676        let c = Vector3::from(b);
677        assert_eq!(c, v);
678
679        let d: glam::Vec3 = c.into();
680        assert_eq!(d, glam::Vec3::new(23.1, 5.0, 0.0));
681
682        let v = Vector2::new(23.1, 5.0);
683
684        let b: glam::DVec2 = v.into();
685        assert_eq!(b, glam::DVec2::new(23.1, 5.0));
686
687        let c = Vector2::from(b);
688        assert_eq!(c, v);
689
690        let d: glam::Vec2 = c.into();
691        assert_eq!(d, glam::Vec2::new(23.1, 5.0));
692    }
693
694    #[cfg(feature = "nalgebra")]
695    #[test]
696    fn test_nalgebra_from_into() {
697        let v = Vector3::new(23.1, 5.0, 0.0);
698
699        let b: nalgebra::Point3<f64> = v.into();
700        assert_eq!(b, nalgebra::Point3::new(23.1, 5.0, 0.0));
701
702        let c = Vector3::from(b);
703        assert_eq!(c, v);
704
705        let d: nalgebra::Point3<f32> = c.into();
706        assert_eq!(d, nalgebra::Point3::new(23.1, 5.0, 0.0));
707
708        let v = Vector2::new(23.1, 5.0);
709
710        let b: nalgebra::Point2<f64> = v.into();
711        assert_eq!(b, nalgebra::Point2::new(23.1, 5.0));
712
713        let c = Vector2::from(b);
714        assert_eq!(c, v);
715
716        let d: nalgebra::Point2<f32> = c.into();
717        assert_eq!(d, nalgebra::Point2::new(23.1, 5.0));
718    }
719}