#pragma once #include "fvec.h" #include "mat.h" namespace cgv { namespace math { #define EPSILON 1e-6 /** implements a quaternion.*/ template class quaternion : public fvec { public: /**@name{\large a) preliminaries}*/ //@{ /// base class type typedef fvec base_type; /// coordinate type typedef T coord_type; /// enumeration of the three coordinate axes enum AxisEnum { X_AXIS, Y_AXIS, Z_AXIS }; /// type of 3d axis typedef fvec vec_type; /// A frame typedef mat mat_type; //@} /**@name{\large b) construction}*/ //@{ /// empty standard constructor quaternion() {} /// copy constructor quaternion(const quaternion& quat) : fvec(quat) {} /// assignement operator quaternion& operator=(const quaternion& quat) { base_type::operator = (quat); return *this; } /// construct quaternion from coordinate axis and rotation angle quaternion(AxisEnum axis, coord_type angle) { set(axis, angle); } /// construct quaternion from axis and rotation angle quaternion(const vec_type& axis, coord_type angle) { set(axis, angle); } /// construct quaternion from 3x3 rotation matrix quaternion(const coord_type* matrix) { set(matrix); } /// construct quaternion directly quaternion(coord_type w,coord_type x, coord_type y,coord_type z) { set(w,x,y,z); } /// construct quaternion from real part and vector quaternion(coord_type re, const vec_type& im) { set(re, im); } base_type& vec() { return *this; } const base_type& vec() const { return *this; } //@} /**@name{\large c) initialization}*/ //@{ /// initialize quaternion from coordinate axis and rotation angle void set(AxisEnum axis, coord_type angle) { vec_type v(0,0,0); v((int)axis) = 1; set(v, angle); } /// initialize quaternion from axis and rotation angle void set(const vec_type& axis, coord_type angle) { angle *= (coord_type)0.5; set(cos(angle), sin(angle)*axis); } /// initialize quaternion from 3x3 rotation matrix void set(const coord_type* M) { coord_type t = M[0]+M[4]+M[8]+1; if (t > 0) { coord_type s = (coord_type) (0.5/sqrt(t)); set((coord_type) (0.25/s), s*(M[7]-M[5]), s*(M[2]-M[6]), s*(M[3] - M[1])); } else { if ( (M[0] > M[4]) && (M[0] > M[8]) ) { coord_type s = (coord_type) (0.5/sqrt( 1.0 + M[0] - M[4] - M[8] )); set((M[5]+M[7])*s, (coord_type) (0.5*s), (M[1]+M[3])*s, (M[2]+M[6])*s); } else if ( M[4] > M[8] ) { coord_type s = (coord_type) (0.5/sqrt( 1.0 + M[4] - M[0] - M[8] )); set((M[2]+M[6])*s, (M[1]+M[3])*s, (coord_type) (0.5*s), (M[5] + M[7])*s); } else { coord_type s = (coord_type) (0.5/sqrt( 1.0 + M[8] - M[0] - M[4] ) ); set((M[1]+M[3])*s, (M[2]+M[6])*s, (M[5]+M[7])*s, (coord_type) (0.5*s)); } } } /// initialize quaternion directly void set(coord_type re, coord_type ix, coord_type iy, coord_type iz) { this->x() = ix; this->y() = iy; this->z() = iz; this->w() = re; } /// initialize quaternion from real part and vector void set(coord_type re, const vec_type& im) { set(re, im.x(), im.y(), im.z()); } //@} /**@name{\large d) conversions and application}*/ //@{ /// compute equivalent 3x3 rotation matrix void put_matrix(coord_type* M) const { M[0] = 1-2*this->y()*this->y()-2*this->z()*this->z(); M[1] = 2*this->x()*this->y()-2*this->w()*this->z(); M[2] = 2*this->x()*this->z()+2*this->w()*this->y(); M[3] = 2*this->x()*this->y()+2*this->w()*this->z(); M[4] = 1-2*this->x()*this->x()-2*this->z()*this->z(); M[5] = 2*this->y()*this->z()-2*this->w()*this->x(); M[6] = 2*this->x()*this->z()-2*this->w()*this->y(); M[7] = 2*this->y()*this->z()+2*this->w()*this->x(); M[8] = 1-2*this->x()*this->x()-2*this->y()*this->y(); } /// compute equivalent homogeneous 4x4 rotation matrix void put_homogeneous_matrix(coord_type* M) const { M[0] = 1-2*this->y()*this->y()-2*this->z()*this->z(); M[1] = 2*this->x()*this->y()-2*this->w()*this->z(); M[2] = 2*this->x()*this->z()+2*this->w()*this->y(); M[3] = 0; M[4] = 2*this->x()*this->y()+2*this->w()*this->z(); M[5] = 1-2*this->x()*this->x()-2*this->z()*this->z(); M[6] = 2*this->y()*this->z()-2*this->w()*this->x(); M[7] = 0; M[8] = 2*this->x()*this->z()-2*this->w()*this->y(); M[9] = 2*this->y()*this->z()+2*this->w()*this->x(); M[10] = 1-2*this->x()*this->x()-2*this->y()*this->y(); M[11] = M[12] = M[13] = M[14] = 0; M[15] = 1; } /// initialize quaternion from normal vector void set_normal(const vec_type& n) { coord_type cosfac = 1-n.x(); if (fabs(cosfac)y()*this->y()+ this->z()*this->z()); n[1] = 2*(this->w()*this->z() + this->x()*this->y()); n[2] = 2*(this->x()*this->z() - this->w()*this->y()); } /// rotate preimage according to quaternion into image void put_image(const vec_type& preimage, vec_type& image) const { image = cross(im(), preimage); image = dot(preimage,im())*im() + re()*(re()*preimage + (coord_type)2*image) + cross(im(),image); } /// rotate vector according to quaternion void rotate(vec_type& v) const { vec_type tmp; put_image(v, tmp); v = tmp; } /// return rotated vector vec_type get_rotated(const vec_type& v) const { vec_type tmp; put_image(v, tmp); return tmp; } /// Rotate a frame according to quaternion. void rotate(mat_type& m) const { m.set_col(0, get_rotated(m.col(0))); m.set_col(1, get_rotated(m.col(1))); m.set_col(2, get_rotated(m.col(2))); } /// Rotate source frame s into destination frame d. mat_type get_rotated(const mat_type& M) const { mat_type tmp = M; rotate(tmp); return tmp; } /// rotate image according to quaternion into preimage void put_preimage(const vec_type& image, vec_type& preimage) const { preimage = cross(-im(), image); preimage = dot(image,-im())*(-im()) + re()*(re()*image + (coord_type)2*preimage) + cross(-im(),preimage); } /// rotate vector according to the inverse quaternion void inverse_rotate(vec_type& image) const { vec_type tmp; put_preimage(image, tmp); image = tmp; } /// rotate vector according to quaternion vec_type apply(const vec_type& v) const { vec_type tmp; put_image(v, tmp); return tmp; } //@} /**@name{\large e) operations}*/ //@{ /// return the conjugate quaternion conj() const { return quaternion(re(), -im()); } /// return the negated quaternion quaternion negated() const { return quaternion(-re(), -im()); } /// negate the quaternion void negate() { conjugate(); re() = -re(); } /// compute conjugate void conjugate() { this->x() = -this->x(); this->y() = -this->y(); this->z() = -this->z(); } /// return the inverse quaternion inverse() const { quaternion tmp(*this); tmp.invert(); return tmp; } /// compute inverse void invert() { conjugate(); coord_type sn = this->sqr_length(); if (sn < EPSILON*EPSILON) base_type::operator *= ((coord_type) 1e14); else base_type::operator /= (sn); } /// compute affin combination with angular interpolation void affin(const quaternion& p, coord_type t, const quaternion& q) { coord_type omega, cosom, sinom, sclp, sclq; *this = q*p; cosom = this->sqr_length(); if ( ( 1 + cosom) > EPSILON ) { if ( ( 1 - cosom) > EPSILON ) { omega = acos( cosom ); sinom = sin ( omega ); sclp = sin ( (1-t) * omega ) / sinom; sclq = sin ( t*omega ) / sinom; } else { sclp = 1 - t; sclq = t; } set(sclp*p.w() + sclq*q.w(), sclp*p.x() + sclq*q.x(), sclp*p.y() + sclq*q.y(), sclp*p.z() + sclq*q.z()); } else { sclp = (T)sin ( (1-t)*M_PI/2 ); sclq = (T)sin ( t * M_PI/2 ); set(p.z(), sclp*p.x() - sclq*p.y(), sclp*p.y() + sclq*p.x(), sclp*p.z() - sclq*p.w()); } } /// compute affin combination with angular interpolation void affin(coord_type s, const quaternion& q) { quaternion tmp; tmp.affin(*this, s, q); *this = tmp; } /// negation operator quaternion operator - () const { return negated(); } /// field multiplication quaternion& operator*=(const quaternion& q) { // Fastmul-Alg. siehe Seidel-Paper p.4 coord_type s[9], t; s[0] = (this->z()-this->y())*(q.y()-q.z()); s[1] = (this->w()+this->x())*(q.w()+q.x()); s[2] = (this->w()-this->x())*(q.y()+q.z()); s[3] = (this->z()+this->y())*(q.w()-q.x()); s[4] = (this->z()-this->x())*(q.x()-q.y()); s[5] = (this->z()+this->x())*(q.x()+q.y()); s[6] = (this->w()+this->y())*(q.w()-q.z()); s[7] = (this->w()-this->y())*(q.w()+q.z()); s[8] = s[5]+s[6]+s[7]; t = (s[4] +s[8])/2; set(s[0]+t-s[5], s[1]+t-s[8], s[2]+t-s[7], s[3]+t-s[6]); return *this; } /// field multiplication quaternion operator * (const quaternion& q) const { quaternion tmp(*this); tmp*=q; return tmp; } ///in place multiplication with s quaternion& operator *= (const T& s) { for (unsigned i=0;i<4;++i) this->v[i] *= s; return *this; } ///multiplication with scalar s quaternion operator * (const T& s) const { quaternion r = *this; r *= s; return r; } //@} /**@name{\large f) access to members}*/ //@{ /// return real part coord_type re() const { return this->w(); } /// return reference to real part coord_type& re() { return this->w(); } /// put imaginary part void put_im(vec_type& vector) const { vector.x() = this->x(); vector.y() = this->y(); vector.z() = this->z(); } /// return this as vector vec_type im() const { return vec_type(this->x(),this->y(),this->z()); } /// put rotation axis and return rotation angle coord_type put_axis(vec_type& v) const { if (re() > 1) { quaternion q(*this); q.normalize(); return q.put_axis(v); } coord_type angle = 2 * acos(re()); coord_type s = sqrt(1 - re()*re()); if (s < (coord_type)EPSILON) { v = vec_type(0,0,1); return 0; } v = im()/s; /* if (2*angle > M_PI) { angle -= (coord_type)(2*M_PI); v = -v; }*/ return angle; } /// exponential map quaternion exp() const { T m = im().length(); quaternion quat(cos(m), im()*(sin(m) / m)); (typename quaternion::base_type&) quat *= exp(re()); return quat; } /// logarithmic map quaternion log() const { T R = this->length(); typename quaternion::vec_type v(im()); T sinTimesR = v.length(); if (sinTimesR < EPSILON) v /= sinTimesR; else { v = typename quaternion::vec_type(0, 0, 0); sinTimesR = 0; } return quaternion(::log(R), atan2(sinTimesR, re()*v)); } //@} }; ///returns the product of a scalar s and vector v template quaternion operator * (const T& s, const quaternion& v) { quaternion r = v; r *= s; return r; } } }