C++ utility library for linear algebra, etc.

quat.h

Committer:
gltest26
Date:
2012-09-04
Revision:
2:83ae9739d636
Parent:
0:189617a75df4

File content as of revision 2:83ae9739d636:

#ifndef CPPLIB_QUAT_H
#define CPPLIB_QUAT_H
#include "vec3.h"
#include <assert.h>
#include <math.h> // sqrt
#include <float.h> // FLT_EPSILON, DBL_EPSILON
/** \file
 * \brief OpenGL-friendly Quaternion implementation template class.
 */

namespace cpplib{

template<typename T> class Vec3;
template<typename T> class Mat4;

/// Quaternion class with its component type as template argument.
template<typename T> class Quat{
    typedef Quat<T> tt; ///< This type
    T a[4];
public:
    typedef T intype[4]; ///< Internal type
    typedef cpplib::Vec3<T> Vec3;
    typedef cpplib::Mat4<T> Mat4;
    Quat(){}
    Quat(T x, T y, T z, T w){a[0] = x, a[1] = y, a[2] = z, a[3] = w;}
    Quat(const Vec3 &o){a[0] = o[0], a[1] = o[1], a[2] = o[2], a[3] = 0;}
    void clear(){a[0] = a[1] = a[2] = a[3] = 0;} ///< Assign zero to all elements
    const T &re()const{return a[3];} T &re(){return a[3];} ///< Returns real part
    const T &i()const{return a[0];} T &i(){return a[0];} ///< Returns imaginary 'i' part
    const T &j()const{return a[1];} T &j(){return a[1];} ///< Returns imaginary 'j' part
    const T &k()const{return a[2];} T &k(){return a[2];} ///< Returns imaginary 'k' part
    tt scale(T s)const{
        return tt(a[0] * s, a[1] * s, a[2] * s, a[3] * s);
    }
    tt &scalein(T s){
        a[0] *= s; a[1] *= s; a[2] *= s; a[3] *= s;
        return *this;
    }
    T len()const{
        return ::sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2] + a[3] * a[3]);
    }
    T slen()const{
        return a[0] * a[0] + a[1] * a[1] + a[2] * a[2] + a[3] * a[3];
    }
    tt norm()const{
        double s = len();
        return tt(a[0]/s,a[1]/s,a[2]/s,a[3]/s);
    }
    tt &normin(){
        double s = len(); a[0] /= s, a[1] /= s, a[2] /= s, a[3] /= s;
        return *this;
    }
    tt mul(const tt &o)const; ///< Multiplication
    tt imul(const tt &o)const{
        return mul(o.cnj());
    }
    tt operator*(const tt &o)const{return mul(o);}
    double sp(const tt &o)const{return a[0] * o.a[0] + a[1] * o.a[1] + a[2] * o.a[2] + a[3] * o.a[3];}
    T &operator[](int i){
        assert(0 <= i && i < 4);
        return a[i];
    }
    const T &operator[](int i)const{
        assert(0 <= i && i < 4);
        return a[i];
    }
    tt &operator*=(const tt &o);
    operator const intype*()const{
        return &a;
    }
    operator const intype&()const{
        return a;
    }
    operator intype*(){
        return &a;
    }
    operator intype&(){
        return a;
    }
    operator Vec3&(){
        return *reinterpret_cast<Vec3*>(this);
    }
    tt &addin(const tt &o){
        a[0] += o.a[0], a[1] += o.a[1], a[2] += o.a[2], a[3] += o.a[3];
        return *this;
    }
    tt add(const tt &o)const{
        return tt(a[0] + o.a[0], a[1] + o.a[1], a[2] + o.a[2], a[3] + o.a[3]);
    }
    tt &operator+=(const tt &o){return addin(o);}
    tt operator+(const tt &o)const{return add(o);}
    bool operator==(const tt &o)const;
    bool operator!=(const tt &o)const{return !operator==(o);}
    tt cnj()const{
        return tt(-a[0], -a[1], -a[2], a[3]);
    }
    Vec3 trans(const Vec3 &src)const; ///< Transforms given vector
    Vec3 itrans(const Vec3 &src)const{
        return cnj().trans(src);
    }
    tt quatrotquat(const Vec3 &v)const; ///< Creates a quaternion rotated by given angular velocity times delta-time

    static tt rotation(T p, T sx, T sy, T sz);
    static tt rotation(T p, const Vec3 &vec);
    tt rotate(T p, T sx, T sy, T sz)const;
    tt rotate(T p, const Vec3 &vec)const;

    Mat4 tomat4()const{
        return Mat4(trans(Vec3(1, 0, 0)), trans(Vec3(0, 1, 0)), trans(Vec3(0, 0, 1)));
    }
    static tt direction(const Vec3 &dir);
    static tt slerp(const tt &q, const tt &r, const double t); ///< Spheric Linear Interpolation

    /// Converting elements to a given type requires explicit call.
    template<typename T2> Quat<T2> cast()const;

    /// Square root of epsilon. This value depends on T, so we explicitly instanciate it as necessary.
    static T sqrtepsilon(){return ::sqrt(DBL_EPSILON);}
};

// Type definitions for common element types.
typedef Quat<double> Quatd;
typedef Quat<float> Quatf;
typedef Quat<int> Quati;

// Constant value definitions. This conversion operator trick was not necessary back in clib aquat_t.
class quat_0_t{ public: operator const Quatd &()const; };
extern const quat_0_t quat_0;
class quat_u_t{ public: operator const Quatd &()const; };
extern const quat_u_t quat_u;



//-----------------------------------------------------------------------------
// Implementation
//-----------------------------------------------------------------------------

template<typename T> inline    Quat<T> Quat<T>::mul(const tt &o)const{
    const T *qa = this->a, *qb = o.a;
    return tt((qa)[1]*(qb)[2]-(qa)[2]*(qb)[1]+(qa)[0]*(qb)[3]+(qa)[3]*(qb)[0],\
        (qa)[2]*(qb)[0]-(qa)[0]*(qb)[2]+(qa)[1]*(qb)[3]+(qa)[3]*(qb)[1],\
        (qa)[0]*(qb)[1]-(qa)[1]*(qb)[0]+(qa)[2]*(qb)[3]+(qa)[3]*(qb)[2],\
        -(qa)[0]*(qb)[0]-(qa)[1]*(qb)[1]-(qa)[2]*(qb)[2]+(qa)[3]*(qb)[3]);
}


template<typename T> inline Quat<T> &Quat<T>::operator*=(const tt &o){
    // There should be certainly better algorithm out there, but I rely on the optimizer for this one.
    *this = *this * o;
    return *this;
}

template<typename T> inline bool Quat<T>::operator ==(const tt &o)const{
    return a[0] == o.a[0] && a[1] == o.a[1] && a[2] == o.a[2] && a[3] == o.a[3];
}

template<typename T> inline Vec3<T> Quat<T>::trans(const Vec3 &src)const{
    tt r, qr;
    tt qc, qret;
    qc = cnj();
    r = src;
    qr = mul(r);
    qret = qr * qc;
    return Vec3(qret[0], qret[1], qret[2]);
}

template<typename T> inline Quat<T> Quat<T>::quatrotquat(const Vec3 &v)const{
    tt q, qr;
    q[0] = v[0];
    q[1] = v[1];
    q[2] = v[2];
    q[3] = 0;
    qr = q * *this;
    qr += *this;
    return qr.norm();
}

/// \brief Returns rotation represented as a quaternion, defined by angle and vector.
///
/// Note that the vector must be normalized.
/// \param p Angle in radians
/// \param sx,sy,sz Components of axis vector, must be normalized
template<typename T> inline Quat<T> Quat<T>::rotation(T p, T sx, T sy, T sz){
    T len = sin(p / 2.);
    return Quatd(len * sx, len * sy, len * sz, cos(p / 2.));
}

/// \brief Returns rotation represented as a quaternion, defined by angle and vector.
///
/// Note that the vector must be normalized.
/// \param p Angle in radians
/// \param vec Axis vector of rotation, must be normalized
template<typename T> inline Quat<T> Quat<T>::rotation(T p, const Vec3 &vec){
    return rotation(p, vec[0], vec[1], vec[2]);
}

/// \brief Returns rotated version of this quaternion with given angle around vector components.
///
/// Note that the vector must be normalized.
/// \param p Angle in radians
/// \param sx,sy,sz Components of axis vector, must be normalized
template<typename T> inline Quat<T> Quat<T>::rotate(T p, T sx, T sy, T sz)const{
    return *this * rotation(p, sx, sy, sz);
}

/// \brief Returns rotated version of this quaternion with given angle around vector.
///
/// Note that the vector must be normalized.
/// \param p Angle in radians
/// \param vec Axis vector of rotation, must be normalized
template<typename T> inline Quat<T> Quat<T>::rotate(T p, const Vec3 &vec)const{
    return rotate(p, vec[0], vec[1], vec[2]);
}


///< Creates a quaternion representing rotation towards given direction.
template<typename T> inline Quat<T> Quat<T>::direction(const Vec3 &dir){
    static const T epsilon = sqrtepsilon();
    double p;
    tt ret;
    Vec3 dr = dir.norm();

    /* half-angle formula of trigonometry replaces expensive tri-functions to square root */
    ret[3] = ::sqrt((dr[2] + 1) / 2) /*cos(acos(dr[2]) / 2.)*/;

    if(1 - epsilon < ret[3]){
        ret.a[0] = ret.a[1] = ret.a[2] = 0;
    }
    else if(ret[3] < epsilon){
        ret.a[0] = ret.a[2] = 0;
        ret.a[1] = 1;
    }
    else{
        Vec3d v = vec3_001.vp(dr);
        p = sqrt(1 - ret[3] * ret[3]) / (v.len());
        ret[0] = v[0] * p;
        ret[1] = v[1] * p;
        ret[2] = v[2] * p;
    }
    return ret;
}

template<typename T> inline Quat<T> Quat<T>::slerp(const typename Quat<T>::tt &q, const typename Quat<T>::tt &r, const double t){
    tt ret;
    double qr = q.a[0] * r.a[0] + q.a[1] * r.a[1] + q.a[2] * r.a[2] + q.a[3] * r.a[3];
    double ss = 1.0 - qr * qr;
  
    if (ss <= sqrtepsilon()) {
        return q;
    }
    else if(q == r){
        return q;
    }
    else {
        double sp = ::sqrt(ss);
        double ph = ::acos(qr);
        double pt = ph * t;
        double t1 = ::sin(pt) / sp;
        double t0 = ::sin(ph - pt) / sp;

        // Long path case
        if(qr < 0)
            t1 *= -1;

        return tt(
            q.a[0] * t0 + r.a[0] * t1,
            q.a[1] * t0 + r.a[1] * t1,
            q.a[2] * t0 + r.a[2] * t1,
            q.a[3] * t0 + r.a[3] * t1);
    }
}


template<typename T> template<typename T2> inline Quat<T2> Quat<T>::cast()const{
    return Quat<T2>(
        static_cast<T2>(a[0]),
        static_cast<T2>(a[1]),
        static_cast<T2>(a[2]),
        static_cast<T2>(a[3]));
}

/// Instantiation of sqrtepsilon in case of float.
template<> float Quat<float>::sqrtepsilon(){return ::sqrt(FLT_EPSILON);}

}

using namespace cpplib;

#endif