spatial_math/
traits.rs

1// Copyright (C) 2020-2025 spatial-math authors. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use super::Vec3;
16use crate::Real;
17
18/// Trait defining the core interface for spatial vectors in Featherstone's spatial algebra.
19///
20/// # Spatial Vector Duality
21///
22/// In Featherstone's formulation, spatial vectors come in dual pairs:
23/// - **Motion vectors** (velocity, acceleration): [ω, v] where ω is angular velocity and v is
24///   linear velocity
25/// - **Force vectors** (force, torque): [n, f] where n is moment/torque and f is force
26///
27/// This duality is fundamental to spatial algebra:
28/// - Motion and force vectors are duals of each other
29/// - The dot product between a motion vector and force vector gives power
30/// - Cross products have specific meanings for each combination
31///
32/// # Mathematical Conventions
33///
34/// ```text
35/// Motion Vector:  v = [ω]  (angular velocity)
36///                      [v]  (linear velocity)
37///
38/// Force Vector:   f = [n]  (moment/torque)
39///                      [f]  (force)
40///
41/// Power:          P = v ⋅ f = ω·n + v·f
42/// ```
43pub trait SpatialVec: Sized {
44    /// The dual type of this spatial vector.
45    ///
46    /// For `SpatialMotionVector`, this is `SpatialForceVector`.
47    /// For `SpatialForceVector`, this is `SpatialMotionVector`.
48    /// This enforces type safety at compile time to prevent mixing
49    /// incompatible vector types in operations.
50    type DualType: SpatialVec;
51
52    /// Create a spatial vector from its top and bottom 3D components.
53    ///
54    /// # Arguments
55    ///
56    /// * `top` - The angular component (ω for motion, n for force)
57    /// * `bottom` - The linear component (v for motion, f for force)
58    fn from_pair(top: Vec3, bottom: Vec3) -> Self;
59
60    /// Get the top (angular) 3D component of the spatial vector.
61    ///
62    /// - For motion vectors: returns angular velocity ω
63    /// - For force vectors: returns moment/torque n
64    fn top(&self) -> Vec3;
65
66    /// Get the bottom (linear) 3D component of the spatial vector.
67    ///
68    /// - For motion vectors: returns linear velocity v
69    /// - For force vectors: returns force f
70    fn bottom(&self) -> Vec3;
71
72    /// Calculate the dot product with the dual vector type.
73    ///
74    /// This computes the scalar product between a motion vector and force vector,
75    /// which represents power in physics: P = ω·n + v·f
76    ///
77    /// # Mathematical Meaning
78    ///
79    /// For motion vector v = [ω, v] and force vector f = [n, f]:
80    /// ```text
81    /// v ⋅ f = ω·n + v·f
82    /// ```
83    /// where:
84    /// - ω·n is the power from rotational motion
85    /// - v·f is the power from linear motion
86    #[inline]
87    fn dot(&self, rhs: &Self::DualType) -> Real {
88        self.top().dot(&rhs.top()) + self.bottom().dot(&rhs.bottom())
89    }
90
91    /// Calculate the cross product with the dual vector type.
92    ///
93    /// This operation represents the spatial cross product between motion and force
94    /// vectors, following Featherstone's spatial algebra conventions.
95    ///
96    /// # Mathematical Formulation
97    ///
98    /// For motion vector m = [ω, v] and force vector f = [n, f]:
99    /// ```text
100    /// m ×* f = [ω×n + v×f]
101    ///         [ω×f      ]
102    /// ```
103    /// where ×* denotes the spatial cross product.
104    ///
105    /// This operation is used in dynamics calculations, particularly for
106    /// transforming forces between rotating reference frames.
107    #[inline]
108    fn cross_dual(&self, rhs: &Self::DualType) -> Self::DualType {
109        Self::DualType::from_pair(
110            self.top().cross(&rhs.top()) + self.bottom().cross(&rhs.bottom()),
111            self.top().cross(&rhs.bottom()),
112        )
113    }
114
115    /// Compute the transpose (dual) of the spatial vector.
116    ///
117    /// In spatial algebra, the transpose operation converts between motion
118    /// and force vectors, effectively changing the vector's interpretation
119    /// while preserving its components.
120    ///
121    /// # Mathematical Meaning
122    ///
123    /// This is equivalent to the adjoint operation in spatial algebra,
124    /// which is used for coordinate transformations and energy calculations.
125    #[inline]
126    fn transpose(&self) -> Self::DualType {
127        Self::DualType::from_pair(self.top(), self.bottom())
128    }
129}
130
131#[cfg(test)]
132mod tests {
133
134    use approx::assert_relative_eq;
135
136    use super::*;
137    use crate::{SpatialForceVector, SpatialMotionVector};
138
139    #[test]
140    fn test_cross_dual() {
141        let spatial_v = SpatialMotionVector::from_array([1., 2., 3., 4., 5., 6.]);
142        let spatial_f = SpatialForceVector::from_array([7., 8., 9., 10., 11., 12.]);
143
144        let result = spatial_v.cross_dual(&spatial_f);
145
146        let w = spatial_v.top;
147        let v = spatial_v.bottom;
148        let n = spatial_f.top;
149        let f = spatial_f.bottom;
150
151        assert_relative_eq!(result.top, w.cross(&n) + v.cross(&f));
152        assert_relative_eq!(result.bottom, w.cross(&f));
153    }
154
155    #[test]
156    fn test_dot() {
157        let spatial_v = SpatialMotionVector::from_array([1., 2., 3., 4., 5., 6.]);
158        let spatial_f = SpatialForceVector::from_array([7., 8., 9., 10., 11., 12.]);
159
160        let result = spatial_v.dot(&spatial_f);
161
162        let w = spatial_v.top;
163        let v = spatial_v.bottom;
164        let n = spatial_f.top;
165        let f = spatial_f.bottom;
166
167        assert_relative_eq!(result, w.dot(&n) + v.dot(&f));
168    }
169}