cmtool_core/coordinates/
vec3.rs1use crate::coordinates::{CartesianCoordinates, Coords3};
4
5#[derive(Clone, Copy, Debug)]
6pub struct CartesianVec3(pub Coords3);
7
8#[derive(Clone, Copy, Debug)]
9pub struct CylindricalVec3(pub Coords3, pub f64);
10
11pub trait Vec3 {
12 fn sub(&self, other: &Self) -> Self;
13 fn add(&self, other: &Self) -> Self;
14 fn cross(&self, other: &Self) -> Self;
15 fn dot(&self, other: &Self) -> f64;
16 fn norm(&self) -> f64;
17 fn normalized(&self) -> Self;
18 fn scale(&self, lambda: f64) -> Self;
19}
20
21impl CartesianVec3 {
22 pub fn from_point(a: CartesianCoordinates, b: CartesianCoordinates) -> Self {
23 Self([b.0[0] - a.0[0], b.0[1] - a.0[1], b.0[2] - a.0[2]])
24 }
25
26 pub fn from_point_origin(a: CartesianCoordinates) -> Self {
27 Self(a.0)
28 }
29}
30
31impl Vec3 for CartesianVec3 {
32 fn add(&self, other: &Self) -> Self {
33 Self([
34 self.0[0] + other.0[0],
35 self.0[1] + other.0[1],
36 self.0[2] + other.0[2],
37 ])
38 }
39
40 fn sub(&self, other: &Self) -> Self {
41 Self([
42 self.0[0] - other.0[0],
43 self.0[1] - other.0[1],
44 self.0[2] - other.0[2],
45 ])
46 }
47
48 fn scale(&self, lambda: f64) -> Self {
49 Self(self.0.map(|v| v * lambda))
50 }
51
52 fn cross(&self, other: &Self) -> Self {
53 Self([
54 self.0[1] * other.0[2] - self.0[2] * other.0[1],
55 self.0[2] * other.0[0] - self.0[0] * other.0[2],
56 self.0[0] * other.0[1] - self.0[1] * other.0[0],
57 ])
58 }
59
60 fn dot(&self, other: &Self) -> f64 {
61 self.0[0] * other.0[0] + self.0[1] * other.0[1] + self.0[2] * other.0[2]
62 }
63
64 fn norm(&self) -> f64 {
65 f64::sqrt(self.0[0] * self.0[0] + self.0[1] * self.0[1] + self.0[2] * self.0[2])
66 }
67
68 fn normalized(&self) -> Self {
69 let _norm = self.norm();
70 self.scale(1. / _norm)
71 }
72}
73
74impl CylindricalVec3 {
76 pub fn from_cartesian_vec(value: CartesianVec3, base_theta: f64) -> Self {
77 let vx = value.0[0];
78 let vy = value.0[1];
79 let vz = value.0[2];
80
81 let vr = vx * base_theta.cos() + vy * base_theta.sin();
82 let vtheta = -vx * base_theta.sin() + vy * base_theta.cos();
83
84 CylindricalVec3([vr, vtheta, vz], base_theta)
85 }
86
87 pub fn to_cartesian_vec(&self) -> CartesianVec3 {
88 let vr = self.0[0];
89 let vtheta = self.0[1];
90 let vz = self.0[2];
91 let base_theta = self.1;
92
93 let vx = vr * base_theta.cos() - vtheta * base_theta.sin();
94 let vy = vr * base_theta.sin() + vtheta * base_theta.cos();
95
96 CartesianVec3([vx, vy, vz])
97 }
98}
99
100impl CartesianVec3 {
101 pub fn from_cylindrical_vec(value: CylindricalVec3) -> Self {
102 let vr = value.0[0];
103 let vtheta = value.0[1];
104 let vz = value.0[2];
105 let base_theta = value.1;
106
107 let vx = vr * base_theta.cos() - vtheta * base_theta.sin();
108 let vy = vr * base_theta.sin() + vtheta * base_theta.cos();
109
110 CartesianVec3([vx, vy, vz])
111 }
112
113 pub fn to_cylindrical_vec(&self, base_theta: f64) -> CylindricalVec3 {
114 let vx = self.0[0];
115 let vy = self.0[1];
116 let vz = self.0[2];
117
118 let vr = vx * base_theta.cos() + vy * base_theta.sin();
119 let vtheta = -vx * base_theta.sin() + vy * base_theta.cos();
120
121 CylindricalVec3([vr, vtheta, vz], base_theta)
122 }
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128 #[test]
129 fn test_cartesian_cylindrical_round_trip() {
130 let original = CartesianVec3([3.0, 4.0, 5.0]);
131 let cyl = original.to_cylindrical_vec(0.);
132 let converted = cyl.to_cartesian_vec();
133
134 for i in 0..3 {
135 assert!((converted.0[i] - original.0[i]).abs() < 1e-10);
136 }
137 }
138
139 #[test]
140 fn test_cartesian_cylindrical_1() {
141 let base_theta = 0.0;
142
143 let cart = CartesianVec3([3.0, 4.0, 5.0]);
144 let cyl = cart.to_cylindrical_vec(base_theta);
145
146 let vr_expected = 3.0;
147 let vtheta_expected = 4.0;
148 let vz_expected = 5.0;
149
150 assert!((cyl.0[0] - vr_expected).abs() < 1e-10);
151 assert!((cyl.0[1] - vtheta_expected).abs() < 1e-10);
152 assert!((cyl.0[2] - vz_expected).abs() < 1e-10);
153 assert!((cyl.1 - base_theta).abs() < 1e-10);
154
155 let cart_converted = cyl.to_cartesian_vec();
157
158 for i in 0..3 {
159 assert!((cart_converted.0[i] - cart.0[i]).abs() < 1e-10);
160 }
161 }
162
163 #[test]
164 fn test_cartesian_to_cylindrical() {
165 let base_theta = std::f64::consts::FRAC_PI_4;
166
167 let cart = CartesianVec3([1.0, 0.0, 2.0]);
168 let cyl = cart.to_cylindrical_vec(base_theta);
169
170 let vr_expected = 1.0 * base_theta.cos() + 0.0 * base_theta.sin();
171 let vtheta_expected = -base_theta.sin() + 0.0 * base_theta.cos();
172 let vz_expected = 2.0;
173
174 assert!((cyl.0[0] - vr_expected).abs() < 1e-10);
175 assert!((cyl.0[1] - vtheta_expected).abs() < 1e-10);
176 assert!((cyl.0[2] - vz_expected).abs() < 1e-10);
177
178 let cart_converted = cyl.to_cartesian_vec();
180
181 for i in 0..3 {
182 assert!((cart_converted.0[i] - cart.0[i]).abs() < 1e-10);
183 }
184 }
185
186 #[test]
187 fn test_cylindrical_to_cartesian_base_theta_0() {
188 let r = 2.0_f64.sqrt();
189 let theta = std::f64::consts::FRAC_PI_4;
190 let z = 3.0;
191 let base_theta = 0.0;
192
193 let cyl = CylindricalVec3([r, theta, z], base_theta);
194 let cart = cyl.to_cartesian_vec();
195 let x_expected = r;
198 let y_expected = theta;
199 let z_expected = 3.0;
200
201 assert!((cart.0[0] - x_expected).abs() < 1e-10);
202 assert!((cart.0[1] - y_expected).abs() < 1e-10);
203 assert!((cart.0[2] - z_expected).abs() < 1e-10);
204 }
205 #[test]
206 fn test_sub() {
207 let v1 = CartesianVec3([3.0, 4.0, 5.0]);
208 let v2 = CartesianVec3([1.0, 1.0, 1.0]);
209 let result = v1.sub(&v2);
210 assert_eq!(result.0, [2.0, 3.0, 4.0]);
211 }
212
213 #[test]
214 fn test_scale() {
215 let v = CartesianVec3([1.0, -2.0, 3.0]);
216 let result = v.scale(2.0);
217 assert_eq!(result.0, [2.0, -4.0, 6.0]);
218 }
219
220 #[test]
221 fn test_dot() {
222 let v1 = CartesianVec3([1.0, 2.0, 3.0]);
223 let v2 = CartesianVec3([4.0, -5.0, 6.0]);
224 let result = v1.dot(&v2);
225 assert_eq!(result, 12.0); }
227
228 #[test]
229 fn test_cross() {
230 let v1 = CartesianVec3([1.0, 0.0, 0.0]);
231 let v2 = CartesianVec3([0.0, 1.0, 0.0]);
232 let result = v1.cross(&v2);
233 assert_eq!(result.0, [0.0, 0.0, 1.0]);
234 }
235
236 #[test]
237 fn test_norm() {
238 let v = CartesianVec3([3.0, 4.0, 0.0]);
239 let result = v.norm();
240 assert!((result - 5.0).abs() < 1e-10);
241 }
242
243 #[allow(clippy::needless_range_loop)]
244 #[test]
245 fn test_normalized() {
246 let v = CartesianVec3([0.0, 3.0, 4.0]);
247 let normed = v.normalized();
248 let expected = [0.0, 0.6, 0.8];
249 for i in 0..3 {
250 assert!((normed.0[i] - expected[i]).abs() < 1e-10);
251 }
252 }
253}