/**************************************************************************** * VCGLib o o * * Visual and Computer Graphics Library o o * * _ O _ * * Copyright(C) 2004-2016 \/)\/ * * Visual Computing Lab /\/| * * ISTI - Italian National Research Council | * * \ * * All rights reserved. * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License (http://www.gnu.org/licenses/gpl.txt) * * for more details. * * * ****************************************************************************/ #ifndef QUATERNION_H #define QUATERNION_H #include #include #include #include #include namespace vcg { /** Class quaternion. A quaternion is a point in the unit sphere in four dimension: all rotations in three-dimensional space can be represented by a quaternion. */ template class Quaternion: public Point4 { public: Quaternion() {} Quaternion(const S v0, const S v1, const S v2, const S v3): Point4(v0,v1,v2,v3){} Quaternion(const Point4 p) : Point4(p) {} Quaternion(const S phi, const Point3 &a); Quaternion operator*(const S &s) const; //Quaternion &operator*=(S d); Quaternion operator*(const Quaternion &q) const; Quaternion &operator*=(const Quaternion &q); void Invert(); Quaternion Inverse() const; void SetIdentity(); void FromAxis(const S phi, const Point3 &a); void ToAxis(S &phi, Point3 &a ) const; ///warning m must be a rotation matrix, result is unpredictable otherwise void FromMatrix(const Matrix44 &m); void FromMatrix(const Matrix33 &m); void ToMatrix(Matrix44 &m) const; void ToMatrix(Matrix33 &m) const; void ToEulerAngles(S &alpha, S &beta, S &gamma) const; void FromEulerAngles(S alpha, S beta, S gamma); Point3 Rotate(const Point3 vec) const; //duplicated ... because of gcc new confoming to ISO template derived classes //do no 'see' parent members (unless explicitly specified) const S & V ( const int i ) const { assert(i>=0 && i<4); return Point4::V(i); } S & V ( const int i ) { assert(i>=0 && i<4); return Point4::V(i); } /// constuctor that imports from different Quaternion types template static inline Quaternion Construct( const Quaternion & b ) { return Quaternion(S(b[0]),S(b[1]),S(b[2]),S(b[3])); } private: }; /*template void QuaternionToMatrix(Quaternion &s, M &m); template void MatrixtoQuaternion(M &m, Quaternion &s);*/ template Quaternion Interpolate( Quaternion a, Quaternion b, double t); template Quaternion &Invert(Quaternion &q); template Quaternion Inverse(const Quaternion &q); //Implementation template void Quaternion::SetIdentity(){ FromAxis(0, Point3(1, 0, 0)); } template Quaternion::Quaternion(const S phi, const Point3 &a) { FromAxis(phi, a); } template Quaternion Quaternion::operator*(const S &s) const { return (Quaternion(V(0)*s,V(1)*s,V(2)*s,V(3)*s)); } template Quaternion Quaternion::operator*(const Quaternion &q) const { Point3 t1(V(1), V(2), V(3)); Point3 t2(q.V(1), q.V(2), q.V(3)); S d = t2.dot(t1); Point3 t3 = t1 ^ t2; t1 *= q.V(0); t2 *= V(0); Point3 tf = t1 + t2 +t3; Quaternion t; t.V(0) = V(0) * q.V(0) - d; t.V(1) = tf[0]; t.V(2) = tf[1]; t.V(3) = tf[2]; return t; } template Quaternion &Quaternion::operator*=(const Quaternion &q) { S ww = V(0) * q.V(0) - V(1) * q.V(1) - V(2) * q.V(2) - V(3) * q.V(3); S xx = V(0) * q.V(1) + V(1) * q.V(0) + V(2) * q.V(3) - V(3) * q.V(2); S yy = V(0) * q.V(2) - V(1) * q.V(3) + V(2) * q.V(0) + V(3) * q.V(1); V(0) = ww; V(1) = xx; V(2) = yy; V(3) = V(0) * q.V(3) + V(1) * q.V(2) - V(2) * q.V(1) + V(3) * q.V(0); return *this; } template void Quaternion::Invert() { V(1)*=-1; V(2)*=-1; V(3)*=-1; } template Quaternion Quaternion::Inverse() const{ return Quaternion( V(0), -V(1), -V(2), -V(3) ); } template void Quaternion::FromAxis(const S phi, const Point3 &a) { Point3 b = a; b.Normalize(); S s = math::Sin(phi/(S(2.0))); V(0) = math::Cos(phi/(S(2.0))); V(1) = b[0]*s; V(2) = b[1]*s; V(3) = b[2]*s; } template void Quaternion::ToAxis(S &phi, Point3 &a) const { S s = math::Asin(V(0))*S(2.0); phi = math::Acos(V(0))*S(2.0); if(s < 0) phi = - phi; a.V(0) = V(1); a.V(1) = V(2); a.V(2) = V(3); a.Normalize(); } template Point3 Quaternion::Rotate(const Point3 p) const { Quaternion co = *this; co.Invert(); Quaternion tmp(0, p.V(0), p.V(1), p.V(2)); tmp = (*this) * tmp * co; return Point3(tmp.V(1), tmp.V(2), tmp.V(3)); } template void QuaternionToMatrix(const Quaternion &q, M &m) { typedef typename M::ScalarType MScalarType; MScalarType x2 = q.V(1) + q.V(1); MScalarType y2 = q.V(2) + q.V(2); MScalarType z2 = q.V(3) + q.V(3); { MScalarType xx2 = q.V(1) * x2; MScalarType yy2 = q.V(2) * y2; MScalarType zz2 = q.V(3) * z2; m[0][0] = 1.0f - yy2 - zz2; m[1][1] = 1.0f - xx2 - zz2; m[2][2] = 1.0f - xx2 - yy2; } { MScalarType yz2 = q.V(2) * z2; MScalarType wx2 = q.V(0) * x2; m[1][2] = yz2 - wx2; m[2][1] = yz2 + wx2; } { MScalarType xy2 = q.V(1) * y2; MScalarType wz2 = q.V(0) * z2; m[0][1] = xy2 - wz2; m[1][0] = xy2 + wz2; } { MScalarType xz2 = q.V(1) * z2; MScalarType wy2 = q.V(0) * y2; m[2][0] = xz2 - wy2; m[0][2] = xz2 + wy2; } } template void Quaternion::ToMatrix(Matrix44 &m) const { QuaternionToMatrix >(*this, m); m[0][3] = (S)0.0; m[1][3] = (S)0.0; m[2][3] = (S)0.0; m[3][0] = (S)0.0; m[3][1] = (S)0.0; m[3][2] = (S)0.0; m[3][3] = (S)1.0; } template void Quaternion::ToMatrix(Matrix33 &m) const { QuaternionToMatrix >(*this, m); } template void MatrixToQuaternion(const M &m, Quaternion &q) { if ( m[0][0] + m[1][1] + m[2][2] > 0.0f ) { S t = m[0][0] + m[1][1] + m[2][2] + 1.0f; S s = (S)0.5 / math::Sqrt(t); q.V(0) = s * t; q.V(3) = ( m[1][0] - m[0][1] ) * s; q.V(2) = ( m[0][2] - m[2][0] ) * s; q.V(1) = ( m[2][1] - m[1][2] ) * s; } else if ( m[0][0] > m[1][1] && m[0][0] > m[2][2] ) { S t = m[0][0] - m[1][1] - m[2][2] + 1.0f; S s = (S)0.5 / math::Sqrt(t); q.V(1) = s * t; q.V(2) = ( m[1][0] + m[0][1] ) * s; q.V(3) = ( m[0][2] + m[2][0] ) * s; q.V(0) = ( m[2][1] - m[1][2] ) * s; } else if ( m[1][1] > m[2][2] ) { S t = - m[0][0] + m[1][1] - m[2][2] + 1.0f; S s = (S)0.5 / math::Sqrt(t); q.V(2) = s * t; q.V(1) = ( m[1][0] + m[0][1] ) * s; q.V(0) = ( m[0][2] - m[2][0] ) * s; q.V(3) = ( m[2][1] + m[1][2] ) * s; } else { S t = - m[0][0] - m[1][1] + m[2][2] + 1.0f; S s = (S)0.5 / math::Sqrt(t); q.V(3) = s * t; q.V(0) = ( m[1][0] - m[0][1] ) * s; q.V(1) = ( m[0][2] + m[2][0] ) * s; q.V(2) = ( m[2][1] + m[1][2] ) * s; } } template void Quaternion::FromMatrix(const Matrix44 &m) { MatrixToQuaternion >(m, *this); } template void Quaternion::FromMatrix(const Matrix33 &m) { MatrixToQuaternion >(m, *this); } template void Quaternion::ToEulerAngles(S &alpha, S &beta, S &gamma) const { #define P(a,b,c,d) (2*(V(a)*V(b)+V(c)*V(d))) #define M(a,b,c,d) (2*(V(a)*V(b)-V(c)*V(d))) alpha = math::Atan2( P(0,1,2,3) , 1-P(1,1,2,2) ); beta = math::Asin ( M(0,2,3,1) ); gamma = math::Atan2( P(0,3,1,2) , 1-P(2,2,3,3) ); #undef P #undef M } template void Quaternion::FromEulerAngles(S alpha, S beta, S gamma) { S cosalpha = math::Cos(alpha / 2.0); S cosbeta = math::Cos(beta / 2.0); S cosgamma = math::Cos(gamma / 2.0); S sinalpha = math::Sin(alpha / 2.0); S sinbeta = math::Sin(beta / 2.0); S singamma = math::Sin(gamma / 2.0); V(0) = cosalpha * cosbeta * cosgamma + sinalpha * sinbeta * singamma; V(1) = sinalpha * cosbeta * cosgamma - cosalpha * sinbeta * singamma; V(2) = cosalpha * sinbeta * cosgamma + sinalpha * cosbeta * singamma; V(3) = cosalpha * cosbeta * singamma - sinalpha * sinbeta * cosgamma; } template Quaternion &Invert(Quaternion &m) { m.Invert(); return m; } template Quaternion Inverse(const Quaternion &m) { Quaternion a = m; a.Invert(); return a; } template Quaternion Interpolate( Quaternion a , Quaternion b , double t) { double v = a.V(0) * b.V(0) + a.V(1) * b.V(1) + a.V(2) * b.V(2) + a.V(3) * b.V(3); double phi = math::Acos(v); if(phi > 0.01) { a = a * (math::Sin(phi *(1-t))/math::Sin(phi)); b = b * (math::Sin(phi * t)/math::Sin(phi)); } Quaternion c; c.V(0) = a.V(0) + b.V(0); c.V(1) = a.V(1) + b.V(1); c.V(2) = a.V(2) + b.V(2); c.V(3) = a.V(3) + b.V(3); if(v < -0.999) { //almost opposite double d = t * (1 - t); if(c.V(0) == 0) c.V(0) += d; else c.V(1) += d; } c.Normalize(); return c; } typedef Quaternion Quaternionf; typedef Quaternion Quaterniond; } // end namespace #endif