1use std::fmt::Display;
16use std::ops::{Add, AddAssign, Mul, Neg, Sub, SubAssign};
17
18use super::traits::SpatialVec;
19use super::{SVector, VEC3_ZERO, Vec3};
20use crate::exts::MatrixExt;
21use crate::{Real, Vec6};
22
23#[derive(Clone, Copy, Debug, Default, PartialEq)]
28pub struct Motion;
29
30#[derive(Clone, Copy, Debug, Default, PartialEq)]
35pub struct Force;
36
37#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
39#[derive(Clone, Copy, Debug, Default, PartialEq)]
40pub struct SpatialVector<T = ()> {
41 pub top: Vec3,
46
47 pub bottom: Vec3,
52
53 _phantom: std::marker::PhantomData<T>,
57}
58
59impl<T> Display for SpatialVector<T> {
60 #[inline]
61 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62 write!(
63 f,
64 "[{},{},{}, {},{},{}]",
65 self.top[0], self.top[1], self.top[2], self.bottom[0], self.bottom[1], self.bottom[2]
66 )
67 }
68}
69
70impl<T> From<SpatialVector<T>> for Vec6 {
71 #[inline]
72 fn from(v: SpatialVector<T>) -> Self {
73 v.into_vec6()
74 }
75}
76
77impl<T> SpatialVector<T> {
78 pub const ZERO: Self = Self {
83 top: VEC3_ZERO,
84 bottom: VEC3_ZERO,
85 _phantom: std::marker::PhantomData,
86 };
87
88 #[inline]
105 pub const fn from_array(array: [Real; 6]) -> Self {
106 Self {
107 top: Vec3::new(array[0], array[1], array[2]),
108 bottom: Vec3::new(array[3], array[4], array[5]),
109 _phantom: std::marker::PhantomData,
110 }
111 }
112
113 #[inline]
118 pub fn from_vec6(vec: SVector<6>) -> Self {
119 Self {
120 top: Vec3::new(vec[0], vec[1], vec[2]),
121 bottom: Vec3::new(vec[3], vec[4], vec[5]),
122 _phantom: std::marker::PhantomData,
123 }
124 }
125
126 #[inline]
136 pub const fn from_pair(top: Vec3, bottom: Vec3) -> Self {
137 Self {
138 top,
139 bottom,
140 _phantom: std::marker::PhantomData,
141 }
142 }
143
144 #[inline]
149 pub fn into_array(self) -> [Real; 6] {
150 [
151 self.top.x,
152 self.top.y,
153 self.top.z,
154 self.bottom.x,
155 self.bottom.y,
156 self.bottom.z,
157 ]
158 }
159
160 #[inline]
161 pub fn any_nan(&self) -> bool {
162 self.top.any_nan() || self.bottom.any_nan()
163 }
164
165 #[inline]
166 #[must_use]
167 pub fn add(&self, rhs: &Self) -> Self {
168 Self::from_pair(self.top + rhs.top, self.bottom + rhs.bottom)
169 }
170
171 #[inline]
172 #[must_use]
173 pub fn sub(&self, rhs: &Self) -> Self {
174 Self::from_pair(self.top - rhs.top, self.bottom - rhs.bottom)
175 }
176
177 #[inline]
178 #[must_use]
179 pub fn neg(&self) -> Self {
180 Self::from_pair(-self.top, -self.bottom)
181 }
182
183 #[inline]
184 #[must_use]
185 pub fn scale(&self, scalar: Real) -> Self {
186 Self::from_pair(self.top * scalar, self.bottom * scalar)
187 }
188
189 #[inline]
190 #[must_use]
191 pub fn cross(&self, rhs: &Self) -> Self {
192 Self::from_pair(
193 self.top.cross(&rhs.top),
194 self.top.cross(&rhs.bottom) + self.bottom.cross(&rhs.top),
195 )
196 }
197
198 #[inline]
199 pub fn into_vec6(self) -> SVector<6> {
200 SVector::from_iterator([
201 self.top.x,
202 self.top.y,
203 self.top.z,
204 self.bottom.x,
205 self.bottom.y,
206 self.bottom.z,
207 ])
208 }
209}
210
211impl<T> Add<Self> for SpatialVector<T> {
212 type Output = Self;
213
214 #[inline]
215 fn add(self, rhs: Self) -> Self::Output {
216 SpatialVector::add(&self, &rhs)
217 }
218}
219
220impl<T> AddAssign<Self> for SpatialVector<T> {
221 #[inline]
222 fn add_assign(&mut self, rhs: Self) {
223 *self = SpatialVector::add(&*self, &rhs);
224 }
225}
226
227impl<T> Sub<Self> for SpatialVector<T> {
228 type Output = Self;
229
230 #[inline]
231 fn sub(self, rhs: Self) -> Self::Output {
232 SpatialVector::sub(&self, &rhs)
233 }
234}
235
236impl<T> SubAssign<Self> for SpatialVector<T> {
237 #[inline]
238 fn sub_assign(&mut self, rhs: Self) {
239 *self = SpatialVector::sub(&*self, &rhs);
240 }
241}
242
243impl<T> Mul<Real> for SpatialVector<T> {
244 type Output = Self;
245
246 #[inline]
247 fn mul(self, rhs: Real) -> Self::Output {
248 self.scale(rhs)
249 }
250}
251
252impl<T> Neg for SpatialVector<T> {
253 type Output = Self;
254
255 #[inline]
256 fn neg(self) -> Self::Output {
257 Self::neg(&self)
258 }
259}
260
261pub type SpatialMotionVector = SpatialVector<Motion>;
262pub type SpatialForceVector = SpatialVector<Force>;
263
264impl SpatialVec for SpatialMotionVector {
265 type DualType = SpatialForceVector;
266
267 #[inline]
268 fn from_pair(top: Vec3, bottom: Vec3) -> Self {
269 Self::from_pair(top, bottom)
270 }
271
272 #[inline]
273 fn top(&self) -> Vec3 {
274 self.top
275 }
276
277 #[inline]
278 fn bottom(&self) -> Vec3 {
279 self.bottom
280 }
281}
282
283impl SpatialVec for SpatialForceVector {
284 type DualType = SpatialMotionVector;
285
286 #[inline]
287 fn from_pair(top: Vec3, bottom: Vec3) -> Self {
288 Self::from_pair(top, bottom)
289 }
290
291 #[inline]
292 fn top(&self) -> Vec3 {
293 self.top
294 }
295
296 #[inline]
297 fn bottom(&self) -> Vec3 {
298 self.bottom
299 }
300}
301
302#[cfg(feature = "approx")]
303mod approx_eq {
304 use crate::Real;
305
306 impl<T> approx::AbsDiffEq for super::SpatialVector<T>
307 where
308 T: PartialEq,
309 {
310 type Epsilon = Real;
311
312 fn default_epsilon() -> Self::Epsilon {
313 Real::EPSILON
314 }
315
316 fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
317 self.top.abs_diff_eq(&other.top, epsilon)
318 && self.bottom.abs_diff_eq(&other.bottom, epsilon)
319 }
320 }
321
322 impl<T> approx::RelativeEq for super::SpatialVector<T>
323 where
324 T: PartialEq,
325 {
326 fn default_max_relative() -> Self::Epsilon {
327 Real::EPSILON
328 }
329
330 fn relative_eq(
331 &self,
332 other: &Self,
333 epsilon: Self::Epsilon,
334 max_relative: Self::Epsilon,
335 ) -> bool {
336 self.top.relative_eq(&other.top, epsilon, max_relative)
337 && self
338 .bottom
339 .relative_eq(&other.bottom, epsilon, max_relative)
340 }
341 }
342}
343
344#[cfg(test)]
345mod tests {
346
347 use approx::assert_relative_eq;
348
349 use super::*;
350 use crate::vec3;
351
352 #[test]
353 fn test_construct() {
354 let v = SpatialVector::<()>::from_array([1., 2., 3., 4., 5., 6.]);
355 assert_eq!(v.top, vec3(1., 2., 3.));
356 assert_eq!(v.bottom, vec3(4., 5., 6.));
357
358 let v = SpatialVector::<()>::from_vec6(SVector::from_iterator([1., 2., 3., 4., 5., 6.]));
359 assert_eq!(v.top, vec3(1., 2., 3.));
360 assert_eq!(v.bottom, vec3(4., 5., 6.));
361 }
362
363 #[test]
364 fn test_add() {
365 let v1 = SpatialVector::<()>::from_array([1., 2., 3., 4., 5., 6.]);
366 let v2 = SpatialVector::<()>::from_array([7., 8., 9., 10., 11., 12.]);
367
368 let result = v1 + v2;
369
370 assert_eq!(result.top, vec3(8., 10., 12.));
371 assert_eq!(result.bottom, vec3(14., 16., 18.));
372 }
373
374 #[test]
375 fn test_add_assign() {
376 let mut v1 = SpatialVector::<()>::from_array([1., 2., 3., 4., 5., 6.]);
377 let v2 = SpatialVector::<()>::from_array([7., 8., 9., 10., 11., 12.]);
378
379 v1 += v2;
380
381 assert_eq!(v1.top, vec3(8., 10., 12.));
382 assert_eq!(v1.bottom, vec3(14., 16., 18.));
383 }
384
385 #[test]
386 fn test_sub() {
387 let v1 = SpatialVector::<()>::from_array([1., 2., 3., 4., 5., 6.]);
388 let v2 = SpatialVector::<()>::from_array([7., 8., 9., 10., 11., 12.]);
389
390 let result = v1 - v2;
391
392 assert_eq!(result.top, vec3(-6., -6., -6.));
393 assert_eq!(result.bottom, vec3(-6., -6., -6.));
394 }
395
396 #[test]
397 fn test_sub_assign() {
398 let mut v1 = SpatialVector::<()>::from_array([1., 2., 3., 4., 5., 6.]);
399 let v2 = SpatialVector::<()>::from_array([7., 8., 9., 10., 11., 12.]);
400
401 v1 -= v2;
402
403 assert_eq!(v1.top, vec3(-6., -6., -6.));
404 assert_eq!(v1.bottom, vec3(-6., -6., -6.));
405 }
406
407 #[test]
408 fn test_mul() {
409 let v = SpatialVector::<()>::from_array([1., 2., 3., 4., 5., 6.]);
410 let scalar = 2.0;
411
412 let result = v * scalar;
413
414 assert_eq!(result.top, vec3(2., 4., 6.));
415 assert_eq!(result.bottom, vec3(8., 10., 12.));
416 }
417
418 #[test]
419 fn test_neg() {
420 let v = SpatialVector::<()>::from_array([1., 2., 3., 4., 5., 6.]);
421
422 let result = v.neg();
423
424 assert_eq!(result.top, vec3(-1., -2., -3.));
425 assert_eq!(result.bottom, vec3(-4., -5., -6.));
426 }
427
428 #[test]
429 fn test_cross() {
430 let spatial_v1 = SpatialMotionVector::from_array([1., 2., 3., 4., 5., 6.]);
431 let spatial_v2 = SpatialMotionVector::from_array([7., 8., 9., 10., 11., 12.]);
432
433 let result = spatial_v1.cross(&spatial_v2);
434
435 let w1 = spatial_v1.top;
436 let v1 = spatial_v1.bottom;
437 let w2 = spatial_v2.top;
438 let v2 = spatial_v2.bottom;
439
440 assert_relative_eq!(result.top, w1.cross(&w2));
441 assert_relative_eq!(result.bottom, w1.cross(&v2) + v1.cross(&w2));
442 }
443}