vec3D/
lib.rs

1// Copyright 2019 Jared Vann
2
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9
10// The above copyright notice and this permission notice shall be included in
11// all copies or substantial portions of the Software.
12
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21//! A minimal 3D Vector library in Rust. Designed with a preference towards
22//! conventions from physics. Inspired by the CLHEP Hep3Vector class.
23//!
24//! # Conventions
25//!
26//! This module uses the convention for describing spherical coordinates as used
27//! in the physics community as follows:
28//!
29//!  - **r** - radial distance
30//!  - **theta** - polar angle
31//!  - **phi** - azimuthal angle
32//!
33//! ![spherical-coordinates-diagram](https://upload.wikimedia.org/wikipedia/commons/4/4f/3D_Spherical.svg)
34//!
35//! And cylindrical coordinates:
36//!
37//!  - **r** - radial distance
38//!  - **phi** - angle
39//!  - **z** - height along z-axis
40//!
41//!  All angles are in radians.
42//!
43//! # Examples
44//!
45//! ```
46//! use vec3D::Vec3D;
47//!
48//! fn main() {
49//!     // Simple Initialisation
50//!     let vec1 = Vec3D::new(1.0, 2.0, 3.0);
51//!     println!("{}", vec1); // Prints: "[1.0, 2.0, 3.0]"
52//!
53//!     // Operator overloads for clean code
54//!     let vec2 = Vec3D::new(3.0, 4.0, 5.0);
55//!     let vec3 = vec1 + vec2;
56//!     println!("{}", vec3); // Prints: "[4.0, 6.0, 8.0]"
57//!
58//!     let vec4 = vec3 * 2.0;
59//!     println!("{}", vec4); // Prints: "[8.0, 12.0, 16.0]"
60//!
61//!     // Common vector operations
62//!     let dot_product = vec3.dot(vec4);
63//!     println!("{}", dot_product); // Prints: "232"
64//!
65//!    let vec5 = Vec3D::new(1.0, 0.0, 0.0);
66//!    let vec6 = Vec3D::new(0.0, 1.0, 0.0);
67//!    let cross_product = vec5.cross(vec6);
68//!    println!("{}", cross_product); // Prints: "[0.0, 0.0, 1.0]"
69//!
70//!    // Plus initialisations from spherical/cylindrical coordinates, rotations and more
71//! }
72//! ```
73
74#[macro_use]
75extern crate nearly_eq;
76
77use std::cmp;
78use std::error;
79use std::f64::consts::PI;
80use std::fmt;
81use std::ops;
82
83use float_cmp::ApproxEq;
84
85#[derive(Copy, Clone, Debug, PartialEq)]
86pub enum Vec3DError {
87    RadiusLessThanZero,
88    ThetaNotWithinRange,
89}
90
91impl fmt::Display for Vec3DError {
92    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
93        match *self {
94            Vec3DError::RadiusLessThanZero => write!(f, "Vec3D error: given value for the radius is less than 0.0."),
95            Vec3DError::ThetaNotWithinRange => write!(f, "Vec3D error: given value for theta not within the range [0, PI]"),
96        }
97    }
98}
99
100impl error::Error for Vec3DError {}
101
102#[derive(Copy, Clone, Debug)]
103pub struct Vec3D {
104    pub x: f64,
105    pub y: f64,
106    pub z: f64,
107}
108
109impl Vec3D {
110    pub fn new(x: f64, y: f64, z: f64) -> Vec3D {
111        Vec3D { x, y, z }
112    }
113
114    /// Creates a new 3D vector from spherical coordinates
115    ///
116    ///  - **r** - radial distance
117    ///  - **theta** - polar angle
118    ///  - **phi** - azimuthal angle
119    ///
120    /// # Errors
121    ///
122    /// Will return a `Vec3DError` if:
123    ///  - `r < 0`
124    ///  - `theta < 0`
125    ///  - `theta > PI`
126    pub fn new_from_spherical_coordinates(r: f64, theta: f64, phi: f64) -> Result<Vec3D, Vec3DError> {
127        if r < 0.0 {
128            return Err(Vec3DError::RadiusLessThanZero);
129        };
130
131        if theta < 0.0 || theta > PI {
132            return Err(Vec3DError::ThetaNotWithinRange);
133        };
134
135        let (sin_theta, cos_theta) = theta.sin_cos();
136        let (sin_phi, cos_phi) = phi.sin_cos();
137        let rho = r * sin_theta;
138
139        Ok(Vec3D {
140            x: rho * cos_phi,
141            y: rho * sin_phi,
142            z: r * cos_theta,
143        })
144    }
145
146    /// Creates a new 3D vector from cylindrical coordinates
147    ///
148    ///  - **r** - radial distance
149    ///  - **phi** - angle
150    ///  - **z** - height along z-axis
151    ///
152    /// # Errors
153    ///
154    /// Will return a `Vec3DError` if:
155    ///  - `r < 0`
156    pub fn new_from_cylindrical_coordinates(r: f64, phi: f64, z: f64) -> Result<Vec3D, Vec3DError> {
157        if r < 0.0 {
158            return Err(Vec3DError::RadiusLessThanZero);
159        };
160
161        let (sin_phi, cos_phi) = phi.sin_cos();
162
163        Ok(Vec3D {
164            x: r * cos_phi,
165            y: r * sin_phi,
166            z,
167        })
168    }
169
170    /// Returns a new vector with a value of 0.0 in each axis
171    #[inline]
172    pub fn zeros() -> Vec3D {
173        Vec3D::new(0.0, 0.0, 0.0)
174    }
175
176    /// Returns a new vector with a value of 1.0 in each axis
177    #[inline]
178    pub fn ones() -> Vec3D {
179        Vec3D::new(1.0, 1.0, 1.0)
180    }
181
182    /// Returns the projection of the vector in the x-axis
183    #[inline]
184    pub fn x_proj(&self) -> Vec3D {
185        Vec3D::new(self.x, 0.0, 0.0)
186    }
187
188    /// Returns the projection of the vector in the y-axis
189    #[inline]
190    pub fn y_proj(&self) -> Vec3D {
191        Vec3D::new(0.0, self.y, 0.0)
192    }
193
194    /// Returns the projection of the vector in the z-axis
195    #[inline]
196    pub fn z_proj(&self) -> Vec3D {
197        Vec3D::new(0.0, 0.0, self.z)
198    }
199
200    /// Returns the vector's theta value in spherical coordinates
201    #[inline]
202    pub fn theta(&self) -> f64 {
203        if self.x == 0.0 && self.y == 0.0 && self.z == 0.0 {
204            0.0
205        } else {
206            (self.x * self.x + self.y * self.y).sqrt().atan2(self.z)
207        }
208    }
209
210    /// Returns the vector's phi value in spherical/cylindrical coordinates
211    #[inline]
212    pub fn phi(&self) -> f64 {
213        if self.x == 0.0 && self.y == 0.0 {
214            0.0
215        } else {
216            self.y.atan2(self.x)
217        }
218    }
219
220    /// Returns the vector's magnitude
221    #[inline]
222    pub fn mag(&self) -> f64 {
223        self.mag2().sqrt()
224    }
225
226    /// Returns the vector's magnitude^2
227    #[inline]
228    pub fn mag2(&self) -> f64 {
229        self.x * self.x + self.y * self.y + self.z * self.z
230    }
231
232    /// Returns a new vector of the current vector normalised
233    #[inline]
234    pub fn norm(&self) -> Vec3D {
235        let mag2 = self.mag2();
236
237        if mag2 == 0.0 {
238            Vec3D::new(0.0, 0.0, 0.0)
239        } else {
240            *self * 1.0 / mag2.sqrt()
241        }
242    }
243
244    /// Alias for `Vec3D::norm`
245    #[inline]
246    pub fn unit(&self) -> Vec3D {
247        self.norm()
248    }
249
250    /// Returns the inner product of this vector with another vector
251    #[inline]
252    pub fn inner_product(&self, other: Vec3D) -> f64 {
253        self.x * other.x + self.y * other.y + self.z * other.z
254    }
255
256    /// Alias for `Vec3D::inner_product`
257    #[inline]
258    pub fn dot(&self, other: Vec3D) -> f64 {
259        self.x * other.x + self.y * other.y + self.z * other.z
260    }
261
262    /// Returns the distance between this vector and another vector
263    #[inline]
264    pub fn distance_to(&self, other: Vec3D) -> f64 {
265        (*self - other).mag()
266    }
267
268    /// Returns the distance^2 between this vector and another vector
269    #[inline]
270    pub fn distance_to2(&self, other: Vec3D) -> f64 {
271        (*self - other).mag2()
272    }
273
274    /// Returns the cross product of this vector with another vector
275    #[inline]
276    pub fn cross(&self, other: Vec3D) -> Vec3D {
277        Vec3D::new(
278            self.y * other.z - other.y * self.z,
279            self.z * other.x - other.z * self.x,
280            self.x * other.y - other.x * self.y,
281        )
282    }
283
284    /// Returns the pseudo-rapidity of the vector w.r.t the z-axis
285    ///
286    /// See: [https://en.wikipedia.org/wiki/Pseudorapidity](https://en.wikipedia.org/wiki/Pseudorapidity)
287    pub fn pseudo_rapidity(&self) -> f64 {
288        let m = self.mag();
289
290        if m.approx_eq(&0.0, 2.0 * ::std::f64::EPSILON, 2) {
291            0.0
292        } else if m.approx_eq(&self.z, 2.0 * ::std::f64::EPSILON, 2) {
293            std::f64::INFINITY
294        } else if m.approx_eq(&(-self.z), 2.0 * ::std::f64::EPSILON, 2) {
295            std::f64::NEG_INFINITY
296        } else {
297            0.5 * ((m + self.z) / (m - self.z)).ln()
298        }
299    }
300
301    /// Rotates the vector around the x-axis
302    pub fn rotate_x(&mut self, angle: f64) {
303        let (sinphi, cosphi) = angle.sin_cos();
304
305        let ty = self.y * cosphi - self.z * sinphi;
306        self.z = self.z * cosphi + self.y * sinphi;
307        self.y = ty;
308    }
309
310    /// Returns a new vector of the current vector rotated around the x-axis
311    pub fn rotated_x(&self, angle: f64) -> Vec3D {
312        let (sinphi, cosphi) = angle.sin_cos();
313
314        Vec3D::new(self.x, self.y * cosphi - self.z * sinphi, self.z * cosphi + self.y * sinphi)
315    }
316
317    /// Rotates the vector around the y-axis
318    pub fn rotate_y(&mut self, angle: f64) {
319        let (sinphi, cosphi) = angle.sin_cos();
320
321        let tz = self.z * cosphi - self.x * sinphi;
322        self.x = self.x * cosphi + self.z * sinphi;
323        self.z = tz;
324    }
325
326    /// Returns a new vector of the current vector rotated around the y-axis
327    pub fn rotated_y(&self, angle: f64) -> Vec3D {
328        let (sinphi, cosphi) = angle.sin_cos();
329
330        Vec3D::new(self.x * cosphi + self.z * sinphi, self.y, self.z * cosphi - self.x * sinphi)
331    }
332
333    /// Rotates the vector around the z-axis
334    pub fn rotate_z(&mut self, angle: f64) {
335        let (sinphi, cosphi) = angle.sin_cos();
336
337        let tx = self.x * cosphi - self.y * sinphi;
338        self.y = self.y * cosphi + self.x * sinphi;
339        self.x = tx;
340    }
341
342    /// Returns a new vector of the current vector rotated around the z-axis
343    pub fn rotated_z(&self, angle: f64) -> Vec3D {
344        let (sinphi, cosphi) = angle.sin_cos();
345
346        Vec3D::new(self.x * cosphi - self.y * sinphi, self.y * cosphi + self.x * sinphi, self.z)
347    }
348
349    /// Returns true if points are approximately equal
350    pub fn approx_eq(self, other: Vec3D) -> bool {
351        self.x.approx_eq(&other.x, 2.0 * ::std::f64::EPSILON, 2)
352            && self.y.approx_eq(&other.y, 2.0 * ::std::f64::EPSILON, 2)
353            && self.z.approx_eq(&other.z, 2.0 * ::std::f64::EPSILON, 2)
354    }
355}
356
357impl fmt::Display for Vec3D {
358    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
359        write!(f, "[{:?}, {:?}, {:?}]", self.x, self.y, self.z)
360    }
361}
362
363impl cmp::PartialEq for Vec3D {
364    #[inline]
365    fn eq(&self, other: &Vec3D) -> bool {
366        self.x == other.x && self.y == other.y && self.z == other.z
367    }
368}
369impl Eq for Vec3D {}
370
371impl ops::Add<Vec3D> for Vec3D {
372    type Output = Vec3D;
373
374    #[inline]
375    fn add(self, other: Vec3D) -> Vec3D {
376        Vec3D::new(self.x + other.x, self.y + other.y, self.z + other.z)
377    }
378}
379
380impl ops::AddAssign<Vec3D> for Vec3D {
381    #[inline]
382    fn add_assign(&mut self, other: Vec3D) {
383        self.x += other.x;
384        self.y += other.y;
385        self.z += other.z;
386    }
387}
388
389impl ops::Sub<Vec3D> for Vec3D {
390    type Output = Vec3D;
391
392    #[inline]
393    fn sub(self, other: Vec3D) -> Vec3D {
394        Vec3D::new(self.x - other.x, self.y - other.y, self.z - other.z)
395    }
396}
397
398impl ops::SubAssign<Vec3D> for Vec3D {
399    #[inline]
400    fn sub_assign(&mut self, other: Vec3D) {
401        self.x -= other.x;
402        self.y -= other.y;
403        self.z -= other.z;
404    }
405}
406
407impl ops::Neg for Vec3D {
408    type Output = Vec3D;
409
410    #[inline]
411    fn neg(self) -> Vec3D {
412        Vec3D::new(-self.x, -self.y, -self.z)
413    }
414}
415
416impl ops::Mul<f64> for Vec3D {
417    type Output = Vec3D;
418
419    #[inline]
420    fn mul(self, other: f64) -> Vec3D {
421        Vec3D::new(self.x * other, self.y * other, self.z * other)
422    }
423}
424
425impl ops::MulAssign<f64> for Vec3D {
426    #[inline]
427    fn mul_assign(&mut self, other: f64) {
428        self.x *= other;
429        self.y *= other;
430        self.z *= other;
431    }
432}
433
434impl ops::Div<f64> for Vec3D {
435    type Output = Vec3D;
436
437    #[inline]
438    fn div(self, other: f64) -> Vec3D {
439        Vec3D::new(self.x / other, self.y / other, self.z / other)
440    }
441}
442
443impl ops::DivAssign<f64> for Vec3D {
444    #[inline]
445    fn div_assign(&mut self, other: f64) {
446        self.x /= other;
447        self.y /= other;
448        self.z /= other;
449    }
450}
451
452#[cfg(test)]
453mod tests {
454    use super::{Vec3D, Vec3DError};
455
456    const PI: f64 = std::f64::consts::PI;
457
458    #[test]
459    fn create_vector() {
460        // Test creation of basic vector
461        let vec = Vec3D::new(1.0, 2.0, 3.0);
462        assert_eq!(vec.x, 1.0);
463        assert_eq!(vec.y, 2.0);
464        assert_eq!(vec.z, 3.0);
465    }
466
467    #[test]
468    fn new_from_spherical_coordinates() {
469        // Test creation of vector from spherical coordinates
470        let vec = Vec3D::new_from_spherical_coordinates(10.0, PI / 4.0, PI / 4.0).unwrap();
471        assert_nearly_eq!(vec.x, 5.0);
472        assert_nearly_eq!(vec.y, 5.0);
473        assert_nearly_eq!(vec.z, 7.0710678118654752);
474
475        // Test failure condition 1
476        let vec = Vec3D::new_from_spherical_coordinates(-1.0, PI / 2.0, PI / 2.0);
477        assert!(vec.is_err());
478        assert_eq!(vec.err().unwrap(), Vec3DError::RadiusLessThanZero);
479
480        // Test failure condition 2
481        let vec = Vec3D::new_from_spherical_coordinates(1.0, PI * 2.0, PI / 2.0);
482        assert!(vec.is_err());
483        assert_eq!(vec.err().unwrap(), Vec3DError::ThetaNotWithinRange);
484    }
485
486    #[test]
487    fn new_from_cylindrical_coordinates() {
488        // Test creation of vector from spherical coordinates
489        let vec = Vec3D::new_from_cylindrical_coordinates(10.0, PI / 4.0, 20.0).unwrap();
490        assert_nearly_eq!(vec.x, 7.0710678118654752);
491        assert_nearly_eq!(vec.y, 7.0710678118654752);
492        assert_nearly_eq!(vec.z, 20.0);
493
494        // Test failure condition
495        let vec = Vec3D::new_from_cylindrical_coordinates(-1.0, PI / 2.0, 10.0);
496        assert!(vec.is_err());
497        assert_eq!(vec.err().unwrap(), Vec3DError::RadiusLessThanZero);
498    }
499
500    #[test]
501    fn x_proj() {
502        // Test calculation of vector's projection in x-axis
503        let vec = Vec3D::new(10.0, 10.0, 10.0);
504        assert_eq!(vec.x_proj(), Vec3D::new(10.0, 0.0, 0.0));
505    }
506
507    #[test]
508    fn y_proj() {
509        // Test calculation of vector's projection in y-axis
510        let vec = Vec3D::new(10.0, 10.0, 10.0);
511        assert_eq!(vec.y_proj(), Vec3D::new(0.0, 10.0, 0.0));
512    }
513
514    #[test]
515    fn z_proj() {
516        // Test calculation of vector's projection in z-axis
517        let vec = Vec3D::new(10.0, 10.0, 10.0);
518        assert_eq!(vec.z_proj(), Vec3D::new(0.0, 0.0, 10.0));
519    }
520
521    #[test]
522    fn theta() {
523        // Test calculation of theta values from vector
524        let vec = Vec3D::new(1.0, 0.0, 0.0);
525        assert_eq!(vec.theta(), PI / 2.0);
526
527        let vec = Vec3D::new(0.0, 1.0, 1.0);
528        assert_eq!(vec.theta(), PI / 4.0);
529
530        let vec = Vec3D::new(0.0, 0.0, 1.0);
531        assert_eq!(vec.theta(), 0.0);
532
533        for i in 1..8 {
534            let theta = PI / i as f64;
535
536            let vec = Vec3D::new_from_spherical_coordinates(1.0, theta, 0.0).unwrap();
537            assert_nearly_eq!(vec.theta(), theta);
538        }
539    }
540
541    #[test]
542    fn phi() {
543        // Test calculation of theta values from vector
544        let vec = Vec3D::new(1.0, 0.0, 0.0);
545        assert_eq!(vec.phi(), 0.0);
546
547        let vec = Vec3D::new(0.0, 1.0, 1.0);
548        assert_eq!(vec.phi(), PI / 2.0);
549
550        let vec = Vec3D::new(0.0, 0.0, 1.0);
551        assert_eq!(vec.phi(), 0.0);
552
553        for i in 2..8 {
554            let phi = (2.0 * PI) / i as f64;
555            let vec = Vec3D::new_from_spherical_coordinates(1.0, PI / 2.0, phi).unwrap();
556            assert_nearly_eq!(vec.phi(), phi);
557        }
558    }
559
560    #[test]
561    fn mag() {
562        // Test calculation of vector's magnitude
563        let vec = Vec3D::new(10.0, 10.0, 10.0);
564        assert_nearly_eq!(vec.mag(), 17.320508075688772);
565    }
566
567    #[test]
568    fn mag2() {
569        // Test calculation of vector's magnitude^2
570        let vec = Vec3D::new(10.0, 10.0, 10.0);
571        assert_eq!(vec.mag2(), 300.0);
572    }
573
574    #[test]
575    fn unit() {
576        // Test calculation of vector's unit vector
577        let vec = Vec3D::new(10.0, 20.0, 30.0).unit();
578        assert_nearly_eq!(vec.x, 0.2672612419124243);
579        assert_nearly_eq!(vec.y, 0.5345224838248487);
580        assert_nearly_eq!(vec.z, 0.8017837257372731);
581    }
582
583    #[test]
584    fn inner_product() {
585        // Test calculation of the inner product of two vectors
586        let vec1 = Vec3D::new(10.0, 20.0, 30.0);
587        let vec2 = Vec3D::new(40.0, 50.0, 60.0);
588        assert_eq!(vec1.inner_product(vec2), 3200.0);
589        assert_eq!(vec2.inner_product(vec1), 3200.0);
590    }
591
592    #[test]
593    fn dot() {
594        // Test calculation of the inner product of two vectors
595        let vec1 = Vec3D::new(10.0, 20.0, 30.0);
596        let vec2 = Vec3D::new(40.0, 50.0, 60.0);
597        assert_eq!(vec1.dot(vec2), 3200.0);
598        assert_eq!(vec2.dot(vec1), 3200.0);
599    }
600
601    #[test]
602    fn distance_to() {
603        // Test calculation of distance between two vectors
604        let vec1 = Vec3D::new(10.0, 20.0, 30.0);
605        let vec2 = Vec3D::new(40.0, 50.0, 60.0);
606        
607        assert_nearly_eq!(vec1.distance_to(vec1), 0.0);
608        assert_nearly_eq!(vec1.distance_to(vec2), 51.96152422706632);
609    }
610
611    #[test]
612    fn distance_to2() {
613        // Test calculation of distance^2 between two vectors
614        let vec1 = Vec3D::new(10.0, 20.0, 30.0);
615        let vec2 = Vec3D::new(40.0, 50.0, 60.0);
616        
617        assert_nearly_eq!(vec1.distance_to2(vec1), 0.0);
618        assert_nearly_eq!(vec1.distance_to2(vec2), 2700.0);
619    }
620
621    #[test]
622    fn cross() {
623        // Test calculation of the cross product of two vectors
624        let vec1 = Vec3D::new(10.0, 20.0, 30.0);
625        let vec2 = Vec3D::new(40.0, 50.0, 60.0);
626        assert_eq!(vec1.cross(vec2), Vec3D::new(-300.0, 600.0, -300.0));
627        assert_eq!(vec2.cross(vec1), Vec3D::new(300.0, -600.0, 300.0));
628    }
629
630    #[test]
631    fn pseudo_rapidity() {
632        // Test when theta is PI/2, pseudo-rapidity is 0
633        let vec1 = Vec3D::new(1.0, 1.0, 0.0);
634        assert_eq!(vec1.pseudo_rapidity(), 0.0);
635
636        // Test when theta is 0, pseudo-rapidity is inf
637        let vec2 = Vec3D::new(0.0, 0.0, 1.0);
638        assert_eq!(vec2.pseudo_rapidity(), std::f64::INFINITY);
639
640        // Test when theta is PI/4
641        let vec3 = Vec3D::new(1.0, 0.0, 1.0);
642        assert_nearly_eq!(vec3.pseudo_rapidity(), 0.881373587019543025232609);
643    }
644
645    #[test]
646    fn rotate_x() {
647        // Test on null vector, rotate 0
648        let vec = Vec3D::new(0.0, 0.0, 0.0);
649        let mut vec2 = vec;
650        vec2.rotate_x(0.0);
651        assert_eq!(vec, vec2);
652
653        // Test on null vector, rotate 180
654        let vec = Vec3D::new(0.0, 0.0, 0.0);
655        let mut vec2 = vec;
656        vec2.rotate_x(PI);
657        assert_eq!(vec, vec2);
658
659        // Test on y-dir unit vector, rotate PI
660        let mut vec = Vec3D::new(0.0, 1.0, 0.0);
661        vec.rotate_x(PI);
662        assert_nearly_eq!(vec.x, 0.0);
663        assert_nearly_eq!(vec.y, -1.0);
664        assert_nearly_eq!(vec.z, 0.0);
665
666        // Test on y-dir unit vector, rotate PI/2
667        let mut vec = Vec3D::new(0.0, 1.0, 0.0);
668        vec.rotate_x(PI / 2.0);
669        assert_nearly_eq!(vec.x, 0.0);
670        assert_nearly_eq!(vec.y, 0.0);
671        assert_nearly_eq!(vec.z, 1.0);
672
673        // Test on z-dir unit vector, rotate PI
674        let mut vec = Vec3D::new(0.0, 0.0, 1.0);
675        vec.rotate_x(PI);
676        assert_nearly_eq!(vec.x, 0.0);
677        assert_nearly_eq!(vec.y, 0.0);
678        assert_nearly_eq!(vec.z, -1.0);
679
680        // Test on z-dir unit vector, rotate PI/2
681        let mut vec = Vec3D::new(0.0, 0.0, 1.0);
682        vec.rotate_x(PI / 2.0);
683        assert_nearly_eq!(vec.x, 0.0);
684        assert_nearly_eq!(vec.y, -1.0);
685        assert_nearly_eq!(vec.z, 0.0);
686    }
687
688    #[test]
689    fn rotated_x() {
690        // Test on null vector, rotate 0
691        let vec1 = Vec3D::new(0.0, 0.0, 0.0);
692        let vec2 = vec1.rotated_x(0.0);
693        assert_eq!(vec1, vec2);
694
695        // Test on null vector, rotate 180
696        let vec1 = Vec3D::new(0.0, 0.0, 0.0);
697        let vec2 = vec1.rotated_x(PI);
698        assert_eq!(vec1, vec2);
699
700        // Test on y-dir unit vector, rotate PI
701        let vec1 = Vec3D::new(0.0, 1.0, 0.0);
702        let vec2 = vec1.rotated_x(PI);
703        assert_nearly_eq!(vec2.x, 0.0);
704        assert_nearly_eq!(vec2.y, -1.0);
705        assert_nearly_eq!(vec2.z, 0.0);
706
707        // Test on y-dir unit vector, rotate PI/2
708        let vec1 = Vec3D::new(0.0, 1.0, 0.0);
709        let vec2 = vec1.rotated_x(PI / 2.0);
710        assert_nearly_eq!(vec2.x, 0.0);
711        assert_nearly_eq!(vec2.y, 0.0);
712        assert_nearly_eq!(vec2.z, 1.0);
713
714        // Test on z-dir unit vector, rotate PI
715        let vec1 = Vec3D::new(0.0, 0.0, 1.0);
716        let vec2 = vec1.rotated_x(PI);
717        assert_nearly_eq!(vec2.x, 0.0);
718        assert_nearly_eq!(vec2.y, 0.0);
719        assert_nearly_eq!(vec2.z, -1.0);
720
721        // Test on z-dir unit vector, rotate PI/2
722        let vec1 = Vec3D::new(0.0, 0.0, 1.0);
723        let vec2 = vec1.rotated_x(PI / 2.0);
724        assert_nearly_eq!(vec2.x, 0.0);
725        assert_nearly_eq!(vec2.y, -1.0);
726        assert_nearly_eq!(vec2.z, 0.0);
727    }
728
729    #[test]
730    fn rotate_y() {
731        // Test on null vector, rotate 0
732        let vec = Vec3D::new(0.0, 0.0, 0.0);
733        let mut vec2 = vec;
734        vec2.rotate_y(0.0);
735        assert_eq!(vec, vec2);
736
737        // Test on null vector, rotate 180
738        let vec = Vec3D::new(0.0, 0.0, 0.0);
739        let mut vec2 = vec;
740        vec2.rotate_y(PI);
741        assert_eq!(vec, vec2);
742
743        // Test on x-dir unit vector, rotate PI
744        let mut vec = Vec3D::new(1.0, 0.0, 0.0);
745        vec.rotate_y(PI);
746        assert_nearly_eq!(vec.x, -1.0);
747        assert_nearly_eq!(vec.y, 0.0);
748        assert_nearly_eq!(vec.z, 0.0);
749
750        // Test on x-dir unit vector, rotate PI/2
751        let mut vec = Vec3D::new(1.0, 0.0, 0.0);
752        vec.rotate_y(PI / 2.0);
753        assert_nearly_eq!(vec.x, 0.0);
754        assert_nearly_eq!(vec.y, 0.0);
755        assert_nearly_eq!(vec.z, -1.0);
756
757        // Test on z-dir unit vector, rotate PI
758        let mut vec = Vec3D::new(0.0, 0.0, 1.0);
759        vec.rotate_y(PI);
760        assert_nearly_eq!(vec.x, 0.0);
761        assert_nearly_eq!(vec.y, 0.0);
762        assert_nearly_eq!(vec.z, -1.0);
763
764        // Test on z-dir unit vector, rotate PI/2
765        let mut vec = Vec3D::new(0.0, 0.0, 1.0);
766        vec.rotate_y(PI / 2.0);
767        assert_nearly_eq!(vec.x, 1.0);
768        assert_nearly_eq!(vec.y, 0.0);
769        assert_nearly_eq!(vec.z, 0.0);
770    }
771
772    #[test]
773    fn rotated_y() {
774        // Test on null vector, rotate 0
775        let vec1 = Vec3D::new(0.0, 0.0, 0.0);
776        let vec2 = vec1.rotated_y(0.0);
777        assert_eq!(vec1, vec2);
778
779        // Test on null vector, rotate 180
780        let vec1 = Vec3D::new(0.0, 0.0, 0.0);
781        let vec2 = vec1.rotated_y(PI);
782        assert_eq!(vec1, vec2);
783
784        // Test on x-dir unit vector, rotate PI
785        let vec1 = Vec3D::new(1.0, 0.0, 0.0);
786        let vec2 = vec1.rotated_y(PI);
787        assert_nearly_eq!(vec2.x, -1.0);
788        assert_nearly_eq!(vec2.y, 0.0);
789        assert_nearly_eq!(vec2.z, 0.0);
790
791        // Test on x-dir unit vector, rotate PI/2
792        let vec1 = Vec3D::new(1.0, 0.0, 0.0);
793        let vec2 = vec1.rotated_y(PI / 2.0);
794        assert_nearly_eq!(vec2.x, 0.0);
795        assert_nearly_eq!(vec2.y, 0.0);
796        assert_nearly_eq!(vec2.z, -1.0);
797
798        // Test on z-dir unit vector, rotate PI
799        let vec1 = Vec3D::new(0.0, 0.0, 1.0);
800        let vec2 = vec1.rotated_y(PI);
801        assert_nearly_eq!(vec2.x, 0.0);
802        assert_nearly_eq!(vec2.y, 0.0);
803        assert_nearly_eq!(vec2.z, -1.0);
804
805        // Test on z-dir unit vector, rotate PI/2
806        let vec1 = Vec3D::new(0.0, 0.0, 1.0);
807        let vec2 = vec1.rotated_y(PI / 2.0);
808        assert_nearly_eq!(vec2.x, 1.0);
809        assert_nearly_eq!(vec2.y, 0.0);
810        assert_nearly_eq!(vec2.z, 0.0);
811    }
812
813    #[test]
814    fn rotate_z() {
815        // Test on null vector, rotate 0
816        let vec = Vec3D::new(0.0, 0.0, 0.0);
817        let mut vec2 = vec;
818        vec2.rotate_z(0.0);
819        assert_eq!(vec, vec2);
820
821        // Test on null vector, rotate 180
822        let vec = Vec3D::new(0.0, 0.0, 0.0);
823        let mut vec2 = vec;
824        vec2.rotate_z(PI);
825        assert_eq!(vec, vec2);
826
827        // Test on x-dir unit vector, rotate PI
828        let mut vec = Vec3D::new(1.0, 0.0, 0.0);
829        vec.rotate_z(PI);
830        assert_nearly_eq!(vec.x, -1.0);
831        assert_nearly_eq!(vec.y, 0.0);
832        assert_nearly_eq!(vec.z, 0.0);
833
834        // Test on x-dir unit vector, rotate PI/2
835        let mut vec = Vec3D::new(1.0, 0.0, 0.0);
836        vec.rotate_z(PI / 2.0);
837        assert_nearly_eq!(vec.x, 0.0);
838        assert_nearly_eq!(vec.y, 1.0);
839        assert_nearly_eq!(vec.z, 0.0);
840
841        // Test on y-dir unit vector, rotate PI
842        let mut vec = Vec3D::new(0.0, 1.0, 0.0);
843        vec.rotate_z(PI);
844        assert_nearly_eq!(vec.x, 0.0);
845        assert_nearly_eq!(vec.y, -1.0);
846        assert_nearly_eq!(vec.z, 0.0);
847
848        // Test on y-dir unit vector, rotate PI/2
849        let mut vec = Vec3D::new(0.0, 1.0, 0.0);
850        vec.rotate_z(PI / 2.0);
851        assert_nearly_eq!(vec.x, -1.0);
852        assert_nearly_eq!(vec.y, 0.0);
853        assert_nearly_eq!(vec.z, 0.0);
854    }
855
856    #[test]
857    fn rotated_z() {
858        // Test on null vector, rotate 0
859        let vec1 = Vec3D::new(0.0, 0.0, 0.0);
860        let vec2 = vec1.rotated_z(0.0);
861        assert_eq!(vec1, vec2);
862
863        // Test on null vector, rotate 180
864        let vec1 = Vec3D::new(0.0, 0.0, 0.0);
865        let vec2 = vec1.rotated_z(PI);
866        assert_eq!(vec1, vec2);
867
868        // Test on x-dir unit vector, rotate PI
869        let vec1 = Vec3D::new(1.0, 0.0, 0.0);
870        let vec2 = vec1.rotated_z(PI);
871        assert_nearly_eq!(vec2.x, -1.0);
872        assert_nearly_eq!(vec2.y, 0.0);
873        assert_nearly_eq!(vec2.z, 0.0);
874
875        // Test on x-dir unit vector, rotate PI/2
876        let vec1 = Vec3D::new(1.0, 0.0, 0.0);
877        let vec2 = vec1.rotated_z(PI / 2.0);
878        assert_nearly_eq!(vec2.x, 0.0);
879        assert_nearly_eq!(vec2.y, 1.0);
880        assert_nearly_eq!(vec2.z, 0.0);
881
882        // Test on y-dir unit vector, rotate PI
883        let vec1 = Vec3D::new(0.0, 1.0, 0.0);
884        let vec2 = vec1.rotated_z(PI);
885        assert_nearly_eq!(vec2.x, 0.0);
886        assert_nearly_eq!(vec2.y, -1.0);
887        assert_nearly_eq!(vec2.z, 0.0);
888
889        // Test on y-dir unit vector, rotate PI/2
890        let vec1 = Vec3D::new(0.0, 1.0, 0.0);
891        let vec2 = vec1.rotated_z(PI / 2.0);
892        assert_nearly_eq!(vec2.x, -1.0);
893        assert_nearly_eq!(vec2.y, 0.0);
894        assert_nearly_eq!(vec2.z, 0.0);
895    }
896
897    #[test]
898    fn partial_eq() {
899        let vec1 = Vec3D::new(10.0, 10.0, 10.0);
900        let vec2 = Vec3D::new(10.0, 10.0, 10.0);
901        let vec3 = Vec3D::new(20.0, 20.0, 20.0);
902        assert_eq!(vec1, vec2);
903        assert_ne!(vec2, vec3);
904    }
905
906    #[test]
907    fn add() {
908        let vec1 = Vec3D::new(10.0, 10.0, 10.0);
909        let vec2 = Vec3D::new(20.0, 20.0, 20.0);
910        let vec3 = Vec3D::new(30.0, 30.0, 30.0);
911        assert_eq!(vec1 + vec2, vec3);
912    }
913
914    #[test]
915    fn add_assign() {
916        let mut vec1 = Vec3D::new(10.0, 10.0, 10.0);
917        let vec2 = Vec3D::new(20.0, 20.0, 20.0);
918        let vec3 = Vec3D::new(30.0, 30.0, 30.0);
919
920        vec1 += vec2;
921        assert_eq!(vec1, vec3);
922    }
923
924    #[test]
925    fn sub() {
926        let vec1 = Vec3D::new(10.0, 10.0, 10.0);
927        let vec2 = Vec3D::new(20.0, 20.0, 20.0);
928        let vec3 = Vec3D::new(30.0, 30.0, 30.0);
929        assert_eq!(vec3 - vec2, vec1);
930    }
931
932    #[test]
933    fn sub_assign() {
934        let vec1 = Vec3D::new(10.0, 10.0, 10.0);
935        let vec2 = Vec3D::new(20.0, 20.0, 20.0);
936        let mut vec3 = Vec3D::new(30.0, 30.0, 30.0);
937
938        vec3 -= vec2;
939        assert_eq!(vec1, vec3);
940    }
941
942    #[test]
943    fn neg() {
944        let vec1 = Vec3D::new(10.0, 10.0, 10.0);
945        let vec2 = Vec3D::new(-10.0, -10.0, -10.0);
946        assert_eq!(-vec1, vec2);
947        assert_eq!(vec1, -vec2);
948    }
949
950    #[test]
951    fn mul() {
952        let vec1 = Vec3D::new(10.0, 10.0, 10.0);
953        let vec2 = Vec3D::new(30.0, 30.0, 30.0);
954        assert_eq!(vec1 * 3.0, vec2);
955    }
956
957    #[test]
958    fn mul_assign() {
959        let mut vec1 = Vec3D::new(10.0, 10.0, 10.0);
960        let vec2 = Vec3D::new(30.0, 30.0, 30.0);
961
962        vec1 *= 3.0;
963        assert_eq!(vec1, vec2);
964    }
965
966    #[test]
967    fn div() {
968        let vec1 = Vec3D::new(30.0, 30.0, 30.0);
969        let vec2 = Vec3D::new(10.0, 10.0, 10.0);
970        assert_eq!(vec1 / 3.0, vec2);
971    }
972
973    #[test]
974    fn div_assign() {
975        let mut vec1 = Vec3D::new(30.0, 30.0, 30.0);
976        let vec2 = Vec3D::new(10.0, 10.0, 10.0);
977
978        vec1 /= 3.0;
979        assert_eq!(vec1, vec2);
980    }
981}