quaternion_core/
generics.rs

1//! 四元数と純虚四元数で共通する処理をまとめる
2
3use super::{Vector3, Quaternion, Float, pfs::mul_add};
4
5
6/// This trait provides operations common to Quaternions and Pure Quaternions (Vector3).
7/// 
8/// This is to allow functions such as `add`, `dot` and `negate` to take
9/// both `Vector3` and `Quaternion` types as arguments. 
10/// You can use this trait directly, but it is easier to see if you use
11/// the one provided as a function (i.e., write `add(a, b)` instead of `a.add(b)`).
12pub trait QuaternionOps<T>: Copy {
13    fn sum(self) -> T;
14    /// `self + rhs`
15    fn add(self, rhs: Self) -> Self;
16    /// `self - rhs`
17    fn sub(self, rhs: Self) -> Self;
18    /// `s * self`
19    fn scale(self, s: T) -> Self;
20    /// `s * self + b`
21    fn scale_add(self, s: T, b: Self) -> Self;
22    /// `self ∘ rhs`
23    fn hadamard(self, rhs: Self) -> Self;
24    /// `self ∘ b + c`
25    fn hadamard_add(self, b: Self, c: Self) -> Self;
26    fn dot(self, b: Self) -> T;
27    fn norm(self) -> T;
28    /// `-self`
29    fn negate(self) -> Self;
30    /// `self * rhs`
31    fn mul(self, rhs: Self) -> Quaternion<T>;
32    fn inv(self) -> Self;
33    fn exp(self) -> Quaternion<T>;
34}
35
36impl<T: Float> QuaternionOps<T> for Vector3<T> {
37    #[inline]
38    fn sum(self) -> T {
39        self[0] + self[1] + self[2]
40    }
41
42    #[inline]
43    fn add(self, rhs: Vector3<T>) -> Vector3<T> {
44        [
45            self[0] + rhs[0], 
46            self[1] + rhs[1], 
47            self[2] + rhs[2] 
48        ]
49    }
50
51    #[inline]
52    fn sub(self, rhs: Vector3<T>) -> Vector3<T> {
53        [
54            self[0] - rhs[0], 
55            self[1] - rhs[1], 
56            self[2] - rhs[2]
57        ]
58    }
59
60    #[inline]
61    fn scale(self, s: T) -> Vector3<T> {
62        [
63            s * self[0],
64            s * self[1],
65            s * self[2]
66        ]
67    }
68
69    #[inline]
70    fn scale_add(self, s: T, b: Vector3<T>) -> Vector3<T> {
71        [
72            mul_add(s, self[0], b[0]),
73            mul_add(s, self[1], b[1]),
74            mul_add(s, self[2], b[2]),
75        ]
76    }
77
78    #[inline]
79    fn hadamard(self, rhs: Vector3<T>) -> Vector3<T> {
80        [
81            self[0] * rhs[0],
82            self[1] * rhs[1],
83            self[2] * rhs[2]
84        ]
85    }
86
87    #[inline]
88    fn hadamard_add(self, b: Vector3<T>, c: Vector3<T>) -> Vector3<T> {
89        [
90            mul_add(self[0], b[0], c[0]),
91            mul_add(self[1], b[1], c[1]),
92            mul_add(self[2], b[2], c[2]),
93        ]
94    }
95
96    #[inline]
97    fn dot(self, b: Vector3<T>) -> T {
98        #[cfg(feature = "fma")]
99        {
100            mul_add(self[0], b[0], mul_add(self[1], b[1], self[2] * b[2]))
101        }
102
103        #[cfg(not(feature = "fma"))]
104        {
105            self.hadamard(b).sum()
106        }
107    }
108
109    #[inline]
110    fn norm(self) -> T {
111        #[cfg(feature = "norm-sqrt")]
112        {
113            super::dot(self, self).sqrt()
114        }
115
116        #[cfg(not(feature = "norm-sqrt"))]
117        {
118            let mut s = self[0];
119            for val in self.iter().skip(1) {
120                s = s.hypot(*val);
121            }
122            s
123        }
124    }
125
126    #[inline]
127    fn negate(self) -> Vector3<T> {
128        [ -self[0], -self[1], -self[2] ]
129    }
130
131    // Product of Pure Quaternions
132    #[inline]
133    fn mul(self, rhs: Vector3<T>) -> Quaternion<T> {
134        ( -super::dot(self, rhs), super::cross(self, rhs) )
135    }
136
137    // 零ベクトルで特異点
138    #[inline]
139    fn inv(self) -> Vector3<T> {
140        self.negate().scale( super::dot(self, self).recip() )
141    }
142
143    #[inline]
144    fn exp(self) -> Quaternion<T> {
145        let norm_v = self.norm();
146        let (sin, cos) = norm_v.sin_cos();
147        ( cos, self.scale(sin / norm_v) )
148    }
149}
150
151impl<T: Float> QuaternionOps<T> for Quaternion<T> {
152    #[inline]
153    fn sum(self) -> T {
154        self.0 + self.1.sum()
155    }
156
157    #[inline]
158    fn add(self, rhs: Quaternion<T>) -> Quaternion<T> {
159        ( self.0 + rhs.0, self.1.add(rhs.1) )
160    }
161
162    #[inline]
163    fn sub(self, rhs: Quaternion<T>) -> Quaternion<T> {
164        ( self.0 - rhs.0,  self.1.sub(rhs.1) )
165    }
166
167    #[inline]
168    fn scale(self, s: T) -> Quaternion<T> {
169        ( s * self.0, self.1.scale(s) )
170    }
171
172    #[inline]
173    fn scale_add(self, s: T, b: Quaternion<T>) -> Quaternion<T> {
174        ( mul_add(s, self.0, b.0), self.1.scale_add(s, b.1) )
175    }
176
177    #[inline]
178    fn hadamard(self, rhs: Quaternion<T>) -> Quaternion<T> {
179        ( self.0 * rhs.0, self.1.hadamard(rhs.1) )
180    }
181
182    #[inline]
183    fn hadamard_add(self, b: Quaternion<T>, c: Quaternion<T>) -> Quaternion<T> {
184        ( mul_add(self.0, b.0, c.0), self.1.hadamard_add(b.1, c.1) )
185    }
186
187    #[inline]
188    fn dot(self, b: Quaternion<T>) -> T {
189        #[cfg(feature = "fma")]
190        {
191            mul_add(self.0, b.0, self.1.dot(b.1))
192        }
193
194        #[cfg(not(feature = "fma"))]
195        {
196            self.hadamard(b).sum()
197        }
198    }
199
200    #[inline]
201    fn norm(self) -> T {
202        #[cfg(feature = "norm-sqrt")]
203        {
204            super::dot(self, self).sqrt()
205        }
206
207        #[cfg(not(feature = "norm-sqrt"))]
208        {
209            let mut s = self.0;
210            for val in self.1 {
211                s = s.hypot(val)
212            }
213            s
214        }
215    }
216
217    #[inline]
218    fn negate(self) -> Quaternion<T> {
219        ( -self.0, self.1.negate() )
220    }
221
222    #[inline]
223    fn mul(self, rhs: Quaternion<T>) -> Quaternion<T> {
224        let self0_b = rhs.scale(self.0);
225        (
226            self0_b.0 - super::dot(self.1, rhs.1),
227            self.1.scale_add(rhs.0, self0_b.1).add( super::cross(self.1, rhs.1) )
228        )
229    }
230
231    #[inline]
232    fn inv(self) -> Quaternion<T> {
233        super::conj(self).scale( super::dot(self, self).recip() )
234    }
235
236    #[inline]
237    fn exp(self) -> Quaternion<T> {
238        let norm_v = self.1.norm();
239        let (sin, cos) = norm_v.sin_cos();
240        let coef = self.0.exp();
241        ( coef * cos, self.1.scale((coef * sin) / norm_v) )
242    }
243}