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}