#ifndef STAR_QUATERNION_HPP #define STAR_QUATERNION_HPP #include "StarVector3.hpp" #include "StarMatrix3.hpp" namespace Star { template class Quaternion { public: // Default tolerance is 100 times type epsilon static T1 const Tolerance; static Quaternion slerp(Quaternion q, Quaternion r, T1 a); static T1 dot(Quaternion q1, Quaternion q2); static Quaternion fromRotMat(Matrix3 mat); static Quaternion fromAxisAngle(Vector3 axis, T1 angle); static Quaternion fromAngVel(Vector3 omega); T1 n; Vector3 v; Quaternion(); Quaternion(T1 w, T1 x, T1 y, T1 z); Quaternion(Vector3 axis, T1 angle); T1* ptr(); T1 const* ptr() const; template Quaternion(Quaternion const& q); template Quaternion& operator=(Quaternion const& q); template Quaternion& operator+=(Quaternion q); template Quaternion& operator-=(Quaternion q); Quaternion& operator*=(T1 s); Quaternion& operator/=(T1 s); template Quaternion operator+(Quaternion const& q) const; template Quaternion operator-(Quaternion const& q) const; template Quaternion operator*(Quaternion const& q) const; template Quaternion operator*(Vector3 const& q) const; Quaternion operator*(T1 const s) const; Quaternion operator/(T1 const s) const; Quaternion operator~() const; Quaternion operator-() const; T1 magnitude() const; Quaternion& normalize(); Vector3 axis() const; T1 angle() const; Matrix3 toRotMat() const; Quaternion rotate(Quaternion q2) const; Vector3 rotate(Vector3 v) const; }; typedef Quaternion QuatD; typedef Quaternion QuatF; template Quaternion::Quaternion() { n = 1; v[0] = 0; v[1] = 0; v[2] = 0; } template Quaternion::Quaternion(T w, T x, T y, T z) { n = w; v[0] = x; v[1] = y; v[2] = z; } template Quaternion::Quaternion(Vector3 axis, T1 angle) { T1 d = axis.magnitude(); if (d != 0) { T1 s = sin(0.5*angle) / d; v[0] = (T1) axis[0] * s; v[1] = (T1) axis[1] * s; v[2] = (T1) axis[2] * s; n = cos(0.5*angle); } else { v[0] = v[1] = v[2] = 0; n = 1; } } template template Quaternion::Quaternion(Quaternion const& q) { n = q.n; v = q.v; } template template Quaternion& Quaternion::operator=(Quaternion const& q) { n = q.n; v = q.v; return *this; } template T Quaternion::magnitude() const { return (T) sqrt(n*n + v[0]*v[0] + v[1]*v[1] + v[2]*v[2]); } template template Quaternion& Quaternion::operator+=(Quaternion q) { n += q.n; v[0] += q.v[0]; v[1] += q.v[1]; v[2] += q.v[2]; return *this; } template template Quaternion& Quaternion::operator-=(Quaternion q) { n -= q.n; v[0] -= q.v[0]; v[1] -= q.v[1]; v[2] -= q.v[2]; return *this; } template Quaternion& Quaternion::operator*=(T1 s) { n *= s; v[0] *= s; v[1] *= s; v[2] *= s; return *this; } template Quaternion Quaternion::operator/(T1 s) const { return Quaternion(n/s, v[0]/s, v[1]/s, v[2]/s); } template Quaternion& Quaternion::operator/=(T1 s) { n /= s; v /= s; return *this; } template Quaternion Quaternion::operator~() const { return Quaternion(n, -v[0], -v[1], -v[2]); } template Quaternion Quaternion::operator-() const { return Quaternion(-n, -v[0], -v[1], -v[2]); } template template Quaternion Quaternion::operator+( Quaternion const& q2) const { return Quaternion( n + q2.n, v[0] + q2.v[0], v[1] + q2.v[1], v[2] + q2.v[2]); } template template Quaternion Quaternion::operator-( Quaternion const& q2) const { return Quaternion( n - q2.n, v[0] - q2.v[0], v[1] - q2.v[1], v[2] - q2.v[2]); } template template Quaternion Quaternion::operator*( Quaternion const& q2) const { return Quaternion( n*q2.n - v[0]*q2.v[0] - v[1]*q2.v[1] - v[2]*q2.v[2], n*q2.v[0] + v[0]*q2.n + v[1]*q2.v[2] - v[2]*q2.v[1], n*q2.v[1] + v[1]*q2.n + v[2]*q2.v[0] - v[0]*q2.v[2], n*q2.v[2] + v[2]*q2.n + v[0]*q2.v[1] - v[1]*q2.v[0]); } template Quaternion Quaternion::operator*(T1 s) const { return Quaternion( n * s, v[0] * s, v[1] * s, v[2] * s ); } template template Quaternion Quaternion::operator*(Vector3 const& v2) const { return Quaternion( -(v[0]*v2[0] + v[1]*v2[1] + v[2]*v2[2]), n*v2[0] + v[1]*v2[2] - v[2]*v2[1], n*v2[1] + v[2]*v2[0] - v[0]*v2[2], n*v2[2] + v[0]*v2[1] - v[1]*v2[0]); } template T1 Quaternion::angle() const { return T1(2*acos(n)); } template Vector3 Quaternion::axis() const { T1 m = v.magnitude(); if (m <= Tolerance) return Vector3 Quaternion Quaternion::rotate(Quaternion q2) const { return (*this)*q2*(~(*this)); } template Vector3 Quaternion::rotate(Vector3 v) const { Quaternion t; t = (*this)*v*(~(*this)); return t.v; } template Quaternion Quaternion::slerp(Quaternion q, Quaternion r, T a) { q.normalize(); r.normalize(); T u, v, angle, s; T dot = qdot(q, r); if(dot < 0.0f) { q = -q; dot = qdot(q, r); } // acos gives NaN for dot slightly out of range if(dot > -(1.0 - Tolerance)) { if(dot < (1.0 - Tolerance)) { angle = acos(dot); s = sin(angle); } else { angle = 0; s = 0; } } else { angle = T(Constants::pi); s = 0; } if(s == 0) return angle Quaternion Quaternion::fromRotMat(Matrix3 mat) { // quicker, but still wrong in one case /* Quaternion q(1.0f, 0.0f, 0.0f, 0.0f); T t; if((t=0.25f*(1.0f + mat.r1[0] + mat.r2[1] + mat.r3[2])) > Tolerance) { q.n = sqrt(t); t = 4.0f*q.n; q.v[0] = (mat.r2[2]-mat.r3[1]) / t; q.v[1] = (mat.r3[0]-mat.r1[2]) / t; q.v[2] = (mat.r1[1]-mat.r2[0]) / t; } else if((t=-0.5f*(mat.r2[1]+mat.r3[2])) > Tolerance) { q.v[0] = sqrt(t); t=2.0f*q.v[0]; q.v[1] = mat.r1[1]/t; q.v[2] = mat.r1[2]/t; } else if((t=0.5f*(1-mat.r3[2])) > Tolerance) { q.v[1]=sqrt(t); q.v[2]=mat.r2[2]/(2.0f*q.v[1]); } return norm(q); */ Quaternion quat; T s; T tr = mat[0][0] + mat[1][1] + mat[2][2]; if(tr >= 0.0f) { s = sqrt(tr + 1.0f); quat.n = s * 0.5f; s = T(0.5) / s; quat.v[0] = (mat.r3[1] - mat.r2[2]) * s; quat.v[1] = (mat.r1[2] - mat.r3[0]) * s; quat.v[2] = (mat.r2[0] - mat.r1[1]) * s; } else { int i = 0; if(mat.r2[1] > mat.r1[0]) i = 1; if(mat.r3[2] > mat[i][i]) i = 2; switch(i) { case 0: s = sqrt(1.0f + mat.r1[0] - mat.r2[1] - mat.r3[2]); quat.v[0] = s*0.5f; s = 0.5f / s; quat.v[1] = (mat.r2[0] + mat.r1[1]) * s; quat.v[2] = (mat.r1[2] + mat.r3[0]) * s; quat.n = (mat.r3[1] - mat.r2[2]) * s; break; case 1: s = sqrt(1.0f + mat.r2[1] - mat.r1[0] - mat.r3[2]); quat.v[1] = s*0.5f; s = 0.5f / s; quat.v[2] = (mat.r3[1] + mat.r2[2]) * s; quat.v[0] = (mat.r2[0] + mat.r1[1]) * s; quat.n = (mat.r1[2] - mat.r3[0]) * s; break; case 2: s = sqrt(1.0f + mat.r3[2] - mat.r1[0] - mat.r2[1]); quat.v[2] = s*0.5f; s = 0.5f / s; quat.v[0] = (mat.r1[2] + mat.r3[0]) * s; quat.v[1] = (mat.r3[1] + mat.r2[2]) * s; quat.n = (mat.r2[0] - mat.r1[1]) * s; break; } } return norm(quat); } template Matrix3 Quaternion::toRotMat() const { Matrix3 mat; T twx = 2*n*v[0]; T twy = 2*n*v[1]; T twz = 2*n*v[2]; T txy = 2*v[0]*v[1]; T txz = 2*v[0]*v[2]; T tyz = 2*v[1]*v[2]; T tx2 = 2*v[0]*v[0]; T ty2 = 2*v[1]*v[1]; T tz2 = 2*v[2]*v[2]; mat.r1[0] = 1 - ty2 - tz2; mat.r2[0] = txy + twz; mat.r3[0] = txz - twy; mat.r1[1] = txy - twz; mat.r2[1] = 1 - tx2 - tz2; mat.r3[1] = tyz + twx; mat.r1[2] = txz + twy; mat.r2[2] = tyz - twx; mat.r3[2] = 1 - tx2 - ty2; return mat; } template Quaternion Quaternion::fromAxisAngle(Vector3 axis, T angle) { Quaternion ret; ret.n = cos(angle / 2); ret.v = norm(axis) * sin(angle / 2); return ret; } template Quaternion Quaternion::fromAngVel(Vector3 axis) { T mag = mag(axis); return fromAxisAngle(axis/mag, mag); } template T Quaternion::dot(Quaternion q, Quaternion r) { return q.n*r.n + (q.v * r.v); } template Quaternion& Quaternion::normalize() { operator/=(magnitude()); return *this; } template T qdot(Quaternion const& q, Quaternion const& r) { return Quaternion::dot(q, r); } template T mag(Quaternion const& q) { return q.magnitude(); } template Quaternion norm(Quaternion q) { return q.normalize(); } template Vector3 qv_rotate(Quaternion q, Vector3 v) { return q.rotate(v); } template Quaternion q_rotate(Quaternion q1, Quaternion q2) { return q1.rotate(q2); } template Quaternion operator*(Vector3 const& v, Quaternion const& q) { return q * v; } template Quaternion operator*(T1 s, Quaternion const& q) { return q * s; } template T1 const* Quaternion::ptr() const { return &n; } template T1* Quaternion::ptr() { return &n; } } #endif