vector3d/
lib.rs

1// Copyright 2018,2020 David Roundy <roundyd@physics.oregonstate.edu>
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9#![cfg_attr(feature = "strict", deny(warnings))]
10#![deny(missing_docs)]
11
12//! This crates provides a single structure `Vector3d`, which is a
13//! generic three-dimensional vector type, which should work well with
14//! `dimensioned`.
15//!
16//! Features: serde1, auto-args, clapme
17
18#[cfg(feature = "serde1")]
19#[macro_use]
20extern crate serde_derive;
21
22#[cfg(feature = "auto-args")]
23use auto_args::AutoArgs;
24#[cfg(feature = "clapme")]
25use clapme::ClapMe;
26
27use std::fmt::Alignment;
28
29/// A 3D vector.
30#[derive(Clone, Copy, Debug, PartialEq, Default)]
31#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
32#[cfg_attr(feature = "clapme", derive(ClapMe))]
33#[cfg_attr(feature = "auto-args", derive(AutoArgs))]
34pub struct Vector3d<T> {
35    /// The x component of the vector.
36    pub x: T,
37    /// The y component of the vector.
38    pub y: T,
39    /// The z component of the vector.
40    pub z: T,
41}
42
43impl<T> Vector3d<T> {
44    /// Create a new `Vector3d`.
45    pub fn new(x: T, y: T, z: T) -> Vector3d<T> {
46        Vector3d { x, y, z }
47    }
48    /// The dot product of two vectors.  Note that we assume that the
49    /// vector components have commutative multiplication.
50    pub fn dot<U: Mul<T, Output = X>, X: Add<Output = X>>(self, rhs: Vector3d<U>) -> X {
51        rhs.x * self.x + rhs.y * self.y + rhs.z * self.z
52    }
53}
54
55// impl Vector3d<f64> {
56//     pub fn ran(scale: f64) -> Vector3d<f64> {
57//         unsafe {
58//             let mut x = 2.0 * RAN.ran() - 1.0;
59//             let mut y = 2.0 * RAN.ran() - 1.0;
60//             let mut r2 = x * x + y * y;
61//             while r2 >= 1.0 || r2 == 0.0 {
62//                 x = 2.0 * RAN.ran() - 1.0;
63//                 y = 2.0 * RAN.ran() - 1.0;
64//                 r2 = x * x + y * y;
65//             }
66//             let mut fac = scale * (-2.0 * r2.ln() / r2).sqrt();
67//             let mut out = Vector3d {
68//                 x: x * fac,
69//                 y: y * fac,
70//                 z: 0.0,
71//             };
72
73//             x = 2.0 * RAN.ran() - 1.0;
74//             y = 2.0 * RAN.ran() - 1.0;
75//             r2 = x * x + y * y;
76//             while r2 >= 1.0 || r2 == 0.0 {
77//                 x = 2.0 * RAN.ran() - 1.0;
78//                 y = 2.0 * RAN.ran() - 1.0;
79//                 r2 = x * x + y * y;
80//             }
81//             fac = scale * (-2.0 * r2.ln() / r2).sqrt();
82//             out[2] = x * fac;
83//             out
84//         }
85//     }
86// }
87
88/// These three operators (`Add`, `Sub`, and `Neg`) do not change
89/// units, and so we can implement them expecting type `T` to not
90/// change. We could be more generic, and implement them similarly to
91/// how we will do `Mul`, but that is added complication with no known
92/// practical gain.
93use std::ops::Add;
94impl<T: Add<T, Output = T>> Add<Vector3d<T>> for Vector3d<T> {
95    type Output = Vector3d<T>;
96    fn add(self, rhs: Vector3d<T>) -> Self::Output {
97        Vector3d::new(self.x + rhs.x, self.y + rhs.y, self.z + rhs.z)
98    }
99}
100
101use std::ops::Sub;
102impl<T: Sub<T, Output = T>> Sub<Vector3d<T>> for Vector3d<T> {
103    type Output = Vector3d<T>;
104    fn sub(self, rhs: Vector3d<T>) -> Self::Output {
105        Vector3d::new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z)
106    }
107}
108
109use std::iter::Sum;
110impl<T: Add<T, Output = T> + Sum<T>> Sum<Vector3d<T>> for Vector3d<T> {
111    fn sum<I: Iterator<Item = Vector3d<T>>>(mut iter: I) -> Vector3d<T> {
112        if let Some(first) = iter.next() {
113            iter.fold(first, |a, b| a + b)
114        } else {
115            // There has got to be a more elegant way to do this, but
116            // if so I don't see it.
117            let x: Option<T> = None;
118            let zero_x: T = x.into_iter().sum();
119            let y: Option<T> = None;
120            let zero_y: T = y.into_iter().sum();
121            let z: Option<T> = None;
122            let zero_z: T = z.into_iter().sum();
123            Vector3d::new(zero_x, zero_y, zero_z)
124        }
125    }
126}
127impl<'a, T: 'a + Add<T, Output = T> + Sum<T> + Clone> Sum<&'a Vector3d<T>> for Vector3d<T> {
128    fn sum<I: Iterator<Item = &'a Vector3d<T>>>(iter: I) -> Vector3d<T> {
129        iter.cloned().sum()
130    }
131}
132
133#[test]
134fn sum_f64() {
135    let x: f64 = [0.0, 0.0, 0.1].iter().cloned().sum();
136    assert_eq!(x, 0.1f64);
137    let total: Vector3d<f64> = [Vector3d::new(0.0, 0.0, 0.1), Vector3d::new(0.0, 0.2, 0.0)]
138        .iter()
139        .cloned()
140        .sum();
141    assert_eq!(total, Vector3d::new(0.0, 0.2, 0.1));
142    let total: Vector3d<f64> = [Vector3d::new(0.0, 0.0, 0.1), Vector3d::new(0.0, 0.2, 0.0)]
143        .iter()
144        .sum();
145    assert_eq!(total, Vector3d::new(0.0, 0.2, 0.1));
146}
147
148use std::ops::Neg;
149impl<T: Neg<Output = T>> Neg for Vector3d<T> {
150    type Output = Vector3d<T>;
151    fn neg(self) -> Self::Output {
152        Vector3d::new(-self.x, -self.y, -self.z)
153    }
154}
155
156use std::ops::Mul;
157impl<S: Clone, X, T: Mul<S, Output = X>> Mul<S> for Vector3d<T> {
158    type Output = Vector3d<X>;
159    fn mul(self, rhs: S) -> Self::Output {
160        Vector3d::new(self.x * rhs.clone(), self.y * rhs.clone(), self.z * rhs)
161    }
162}
163
164use std::ops::Div;
165impl<S: Clone, X, T: Div<S, Output = X>> Div<S> for Vector3d<T> {
166    type Output = Vector3d<X>;
167    fn div(self, rhs: S) -> Self::Output {
168        Vector3d::new(self.x / rhs.clone(), self.y / rhs.clone(), self.z / rhs)
169    }
170}
171
172impl<T: Clone> Vector3d<T> {
173    /// The cross product of two vectors.  Note that we assume that
174    /// the components of both vector types have commutative
175    /// multiplication.
176    pub fn cross<U: Clone + Mul<T, Output = X>, X: Add<Output = X> + Sub<Output = X>>(
177        self,
178        rhs: Vector3d<U>,
179    ) -> Vector3d<X> {
180        Vector3d::new(
181            rhs.z.clone() * self.y.clone() - rhs.y.clone() * self.z.clone(),
182            rhs.x.clone() * self.z.clone() - rhs.z * self.x.clone(),
183            rhs.y * self.x - rhs.x * self.y,
184        )
185    }
186}
187
188impl<T: Clone + Mul<T, Output = X>, X: Add<Output = X>> Vector3d<T> {
189    /// The square of the vector.
190    pub fn norm2(self) -> X {
191        self.x.clone() * self.x + self.y.clone() * self.y + self.z.clone() * self.z
192    }
193}
194
195use std::ops::Index;
196impl<T> Index<usize> for Vector3d<T> {
197    type Output = T;
198    fn index(&self, index: usize) -> &T {
199        match index {
200            0 => &self.x,
201            1 => &self.y,
202            2 => &self.z,
203            _ => panic!("Invalid index"),
204        }
205    }
206}
207
208use std::ops::IndexMut;
209impl<T> IndexMut<usize> for Vector3d<T> {
210    fn index_mut(&mut self, index: usize) -> &mut T {
211        match index {
212            0 => &mut self.x,
213            1 => &mut self.y,
214            2 => &mut self.z,
215            _ => panic!("Invalid index"),
216        }
217    }
218}
219
220use std::fmt;
221impl<T: fmt::Display> fmt::Display for Vector3d<T> {
222    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
223        if let Some(decimals) = f.precision() {
224            let s = format!(
225                "({:.decimals$}, {:.decimals$}, {:.decimals$})",
226                self.x,
227                self.y,
228                self.z,
229                decimals = decimals
230            );
231            if let Some(width) = f.width() {
232                match f.align().unwrap_or(Alignment::Left) {
233                    Alignment::Left => write!(f, "{:<width$}", s, width = width),
234                    Alignment::Right => write!(f, "{:>width$}", s, width = width),
235                    Alignment::Center => write!(f, "{:^width$}", s, width = width),
236                }
237            } else {
238                f.write_str(&s)
239            }
240        } else {
241            let string = format!("({}, {}, {})", self.x, self.y, self.z);
242            f.pad(&string)
243        }
244    }
245}
246
247#[test]
248fn padding_works() {
249    let v = Vector3d::new(0, 0, 0);
250    assert_eq!(&format!("{}", v), "(0, 0, 0)");
251    assert_eq!(&format!("{:10}", v), "(0, 0, 0) ");
252    assert_eq!(&format!("{:<10}", v), "(0, 0, 0) ");
253    assert_eq!(&format!("{:>10}", v), " (0, 0, 0)");
254    assert_eq!(&format!("{:^11}", v), " (0, 0, 0) ");
255    assert_eq!(&format!("{:>11}", v), "  (0, 0, 0)");
256
257    let v = Vector3d::new(0., 0., 0.);
258    assert_eq!(&format!("{}", v), "(0, 0, 0)");
259    assert_eq!(&format!("{:.2}", v), "(0.00, 0.00, 0.00)");
260    assert_eq!(&format!("{:19.2}", v), "(0.00, 0.00, 0.00) ");
261    assert_eq!(&format!("{:<19.2}", v), "(0.00, 0.00, 0.00) ");
262    assert_eq!(&format!("{:>19.2}", v), " (0.00, 0.00, 0.00)");
263    assert_eq!(&format!("{:^20.2}", v), " (0.00, 0.00, 0.00) ");
264}