quaternion_averager/
lib.rs1use nalgebra::{
2 geometry::Quaternion, geometry::UnitQuaternion, linalg::SymmetricEigen, Matrix4, RealField,
3};
4
5pub struct QuaternionAverager<T: RealField + Copy + PartialEq> {
8 matrix: Matrix4<T>,
9 weight_sum: T,
10}
11
12impl<T: RealField + Copy + PartialEq> Default for QuaternionAverager<T> {
13 fn default() -> Self {
14 Self::new()
15 }
16}
17
18impl<T: RealField + Copy + PartialEq> QuaternionAverager<T> {
19 pub fn new() -> QuaternionAverager<T> {
29 QuaternionAverager {
30 matrix: Matrix4::zeros(),
31 weight_sum: T::from_f32(0f32).unwrap(),
32 }
33 }
34
35 pub fn add_quaternion(&mut self, quaternion: &UnitQuaternion<T>) {
51 let q = quaternion.coords * quaternion.coords.transpose();
52 self.matrix += q;
53 let w = T::from_f32(1f32).unwrap();
54 self.weight_sum += w;
55 }
56
57 pub fn add_quaternion_weighted(&mut self, quaternion: &UnitQuaternion<T>, weight: T) {
73 let q = quaternion.coords * quaternion.coords.transpose();
74 let q = q / weight;
75 self.matrix += q;
76 self.weight_sum += weight;
77 }
78
79 pub fn calc_average(&self) -> UnitQuaternion<T> {
100 let m = self.matrix / self.weight_sum;
101 let decomp = SymmetricEigen::new(m);
102 let i = decomp.eigenvalues.imax();
103 let q = decomp.eigenvectors.column(i);
104 let q = Quaternion::new(q[3], q[0], q[1], q[2]);
105
106 UnitQuaternion::from_quaternion(q)
107 }
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113 use approx::*;
114
115 #[test]
116 fn avg2_32() {
117 let mut avg = QuaternionAverager::new();
118 let q1 = Quaternion::new(0.9961947f32, 0.0871557f32, 0f32, 0f32);
119 let q1 = UnitQuaternion::from_quaternion(q1);
120 let q2 = Quaternion::new(0.9848078f32, 0.1736482f32, 0f32, 0f32);
121 let q2 = UnitQuaternion::from_quaternion(q2);
122 avg.add_quaternion(&q1);
123 avg.add_quaternion(&q2);
124
125 let qr = Quaternion::new(0.9914449f32, 0.1305262f32, 0f32, 0f32);
126 let qr = UnitQuaternion::from_quaternion(qr);
127 let q = avg.calc_average();
128
129 assert_relative_eq!(q, qr, max_relative = 0.00001);
130 }
131
132 #[test]
133 fn avg2_64() {
134 let mut avg = QuaternionAverager::new();
135 let q1 = Quaternion::new(0.9961947f64, 0.0871557f64, 0f64, 0f64);
136 let q1 = UnitQuaternion::from_quaternion(q1);
137 let q2 = Quaternion::new(0.9848078f64, 0.1736482f64, 0f64, 0f64);
138 let q2 = UnitQuaternion::from_quaternion(q2);
139 avg.add_quaternion(&q1);
140 avg.add_quaternion(&q2);
141
142 let qr = Quaternion::new(0.9914449f64, 0.1305262f64, 0f64, 0f64);
143 let qr = UnitQuaternion::from_quaternion(qr);
144 let q = avg.calc_average();
145
146 assert_relative_eq!(q, qr, max_relative = 0.000001);
147 }
148}