From c044ce0d0830902b9ca86dcd9a725e5ef4f72eb1 Mon Sep 17 00:00:00 2001
From: Stanislaw Halik <sthalik@misaki.pl>
Date: Sun, 2 Nov 2014 06:04:05 +0100
Subject: use quaternions for centering

@KyokushinPL says was the only version that worked. Let's give it a try.

Goddamn issue: #63
---
 ftnoir_posewidget/glwidget.cpp | 23 +++++---------
 opentrack/quat.hpp             | 71 ++++++++++++++++++++++++++++++++++++++++++
 opentrack/tracker.cpp          | 32 ++++++++-----------
 opentrack/tracker.h            |  3 +-
 4 files changed, 93 insertions(+), 36 deletions(-)
 create mode 100644 opentrack/quat.hpp

diff --git a/ftnoir_posewidget/glwidget.cpp b/ftnoir_posewidget/glwidget.cpp
index bfb9190d..07445c8a 100644
--- a/ftnoir_posewidget/glwidget.cpp
+++ b/ftnoir_posewidget/glwidget.cpp
@@ -34,26 +34,17 @@ void GLWidget::paintEvent ( QPaintEvent * event ) {
 void GLWidget::rotateBy(double xAngle, double yAngle, double zAngle)
 {
 
-    double c1 = cos(zAngle / 57.295781);
-    double s1 = sin(zAngle / 57.295781);
+    double c1 = cos(yAngle / 57.295781);
+    double s1 = sin(yAngle / 57.295781);
     double c2 = cos(xAngle / 57.295781);
     double s2 = sin(xAngle / 57.295781);
-    double c3 = cos(yAngle / 57.295781);
-    double s3 = sin(yAngle / 57.295781);
+    double c3 = cos(zAngle / 57.295781);
+    double s3 = sin(zAngle / 57.295781);
 
     double foo[] = {
-        // z
-        c1 * c2,
-        c1 * s2 * s3 - c3 * s1,
-        s1 * s3 + c1 * c3 * s2,
-        // y
-        c2 * s1,
-        c1 * c3 + s1 * s2 * s3,
-        c3 * s1 * s2 - c1 * s3,
-        // x
-        -s2,
-        c2 * s3,
-        c2 * c3
+        c2*c3,  -c2*s3,  s2,
+        c1*s3+c3*s1*s2,  c1*c3-s1*s2*s3,  -c2*s1,
+        s1*s3-c1*c3*s2,  c3*s1+c1*s2*s3,  c1*c2,
     };
 
     for (int i = 0; i < 9; i++)
diff --git a/opentrack/quat.hpp b/opentrack/quat.hpp
new file mode 100644
index 00000000..e5e74df4
--- /dev/null
+++ b/opentrack/quat.hpp
@@ -0,0 +1,71 @@
+/* Copyright (c) 2012 Patrick Ruoff
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ */
+
+#pragma once
+#include <cmath>
+
+class Quat {
+private:
+    static constexpr double pi = 3.141592653;
+    static constexpr double r2d = 180./pi;
+    double a,b,c,d; // quaternion coefficients
+public:
+    Quat() : a(1.),b(0.),c(0.),d(0.) {}
+    Quat(double yaw, double pitch, double roll) { *this = from_euler_rads(yaw, pitch, roll); }
+    Quat(double a, double b, double c, double d) : a(a),b(b),c(c),d(d) {}
+
+    Quat inv() const
+    {
+        return Quat(a,-b,-c, -d);
+    }
+
+    // conversions
+    // see http://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles
+    static Quat from_euler_rads(double yaw, double pitch, double roll)
+    {
+
+        const double sin_phi = sin(roll/2.);
+        const double cos_phi = cos(roll/2.);
+        const double sin_the = sin(pitch/2.);
+        const double cos_the = cos(pitch/2.);
+        const double sin_psi = sin(yaw/2.);
+        const double cos_psi = cos(yaw/2.);
+
+        Quat q;
+
+        q.a = cos_phi*cos_the*cos_psi + sin_phi*sin_the*sin_psi;
+        q.b = sin_phi*cos_the*cos_psi - cos_phi*sin_the*sin_psi;
+        q.c = cos_phi*sin_the*cos_psi + sin_phi*cos_the*sin_psi;
+        q.d = cos_phi*cos_the*sin_psi - sin_phi*sin_the*cos_psi;
+
+        return q;
+    }
+
+    void to_euler_rads(double& yaw, double& pitch, double& roll) const
+    {
+        roll = atan2(2.*(a*b + c*d), 1. - 2.*(b*b + c*c));
+        pitch = asin(2.*(a*c - b*d));
+        yaw =  atan2(2.*(a*d + b*c), 1. - 2.*(c*c + d*d));
+    }
+
+    void to_euler_degrees(double& yaw, double& pitch, double& roll) const
+    {
+        to_euler_rads(yaw, pitch, roll);
+        yaw *= r2d;
+        pitch *= r2d;
+        roll *= r2d;
+    }
+
+    const Quat operator*(const Quat& B) const
+    {
+        const Quat& A = *this;
+        return Quat(A.a*B.a - A.b*B.b - A.c*B.c - A.d*B.d,	// quaternion multiplication
+                    A.a*B.b + A.b*B.a + A.c*B.d - A.d*B.c,
+                    A.a*B.c - A.b*B.d + A.c*B.a + A.d*B.b,
+                    A.a*B.d + A.b*B.c - A.c*B.b + A.d*B.a);
+    }
+};
diff --git a/opentrack/tracker.cpp b/opentrack/tracker.cpp
index 4a9be069..b9b43ee5 100644
--- a/opentrack/tracker.cpp
+++ b/opentrack/tracker.cpp
@@ -28,7 +28,6 @@ Tracker::Tracker(main_settings& s, Mappings &m, SelectedLibraries &libs) :
     enabledp(true),
     should_quit(false),
     libs(libs),
-    r_b (dmat<3, 3>::eye()),
     t_b {0,0,0}
 {
 }
@@ -48,17 +47,6 @@ double Tracker::map(double pos, bool invertp, Mapping& axis)
     return fc.getValue(pos) + axis.opts.zero;
 }
 
-// http://stackoverflow.com/a/18436193
-static dmat<3, 1> rmat_to_euler(const dmat<3, 3>& R)
-{
-    const double tmp[] = {
-        -atan2( -R(2,0), sqrt(R(2,1)*R(2,1) + R(2,2)*R(2,2)) ),
-        -atan2( R(2,1), R(2,2)),
-        -atan2( R(1,0), R(0,0)),
-    };
-    return dmat<3, 1>(tmp);
-}
-
 // tait-bryan angles, not euler
 static dmat<3, 3> euler_to_rmat(const double* input)
 {
@@ -124,26 +112,32 @@ void Tracker::logic()
     for (int i = 0; i < 6; i++)
         filtered_pose[i] *= inverts[i] ? -1. : 1.;
 
+    static constexpr double pi = 3.141592653;
+    static constexpr double d2r = pi / 180.;
+
     if (centerp)
     {
         centerp = false;
-        dmat<3, 1> tmp;
-        r_b = euler_to_rmat(&filtered_pose[Yaw]);
         for (int i = 0; i < 3; i++)
             t_b[i] = filtered_pose(i);
+        q_b = Quat::from_euler_rads(filtered_pose(Yaw) * d2r,
+                                    filtered_pose(Pitch) * d2r,
+                                    filtered_pose(Roll) * d2r);
     }
 
     Pose raw_centered;
 
     {
-        const dmat<3, 3> rmat = euler_to_rmat(&filtered_pose[Yaw]);
-        const dmat<3, 3> m_ = rmat * r_b.t();
-        const auto euler = rmat_to_euler(m_);
+        const Quat q(filtered_pose(Yaw)*d2r,
+                     filtered_pose(Pitch)*d2r,
+                     filtered_pose(Roll)*d2r);
+        const Quat q_ = q * q_b.inv();
+        double ypr[3];
+        q_.to_euler_degrees(ypr[0], ypr[1], ypr[2]);
         for (int i = 0; i < 3; i++)
         {
-            static constexpr double pi = 3.141592653;
             raw_centered(i) = filtered_pose(i) - t_b[i];
-            raw_centered(i+3) = euler(i, 0) * 180./pi;
+            raw_centered(i+3) = ypr[i];
         }
     }
 
diff --git a/opentrack/tracker.h b/opentrack/tracker.h
index 23939576..feb1f903 100644
--- a/opentrack/tracker.h
+++ b/opentrack/tracker.h
@@ -7,6 +7,7 @@
 #include "plugin-support.h"
 #include "mappings.hpp"
 #include "pose.hpp"
+#include "quat.hpp"
 #include "simple-mat.hpp"
 
 #include "../qfunctionconfigurator/functionconfig.h"
@@ -32,7 +33,7 @@ private:
     std::atomic<bool> should_quit;
     SelectedLibraries const& libs;
 
-    dmat<3, 3> r_b;
+    Quat q_b;
     double t_b[3];
 
     double map(double pos, bool invertp, Mapping& axis);
-- 
cgit v1.2.3