/** * @file Quaternion.hpp * * All rotations and axis systems follow the right-hand rule. * * In order to rotate a vector v by a righthand rotation defined by the quaternion q * one can use the following operation: * v_rotated = q^(-1) * [0;v] * q * where q^(-1) represents the inverse of the quaternion q. * The product z of two quaternions z = q1 * q2 represents an intrinsic rotation * in the order of first q1 followed by q2. * The first element of the quaternion * represents the real part, thus, a quaternion representing a zero-rotation * is defined as (1,0,0,0). * * @author James Goppert */ #pragma once #include "math.hpp" #include "helper_functions.hpp" namespace matrix { template class Dcm; template class Euler; template class AxisAngle; /** * Quaternion class * * The rotation between two coordinate frames is * described by this class. */ template class Quaternion : public Vector { public: virtual ~Quaternion() {}; typedef Matrix Matrix41; typedef Matrix Matrix31; /** * Constructor from array * * @param data_ array */ Quaternion(const Type *data_) : Vector(data_) { } /** * Standard constructor */ Quaternion() : Vector() { Quaternion &q = *this; q(0) = 1; q(1) = 0; q(2) = 0; q(3) = 0; } /** * Constructor from Matrix41 * * @param other Matrix41 to copy */ Quaternion(const Matrix41 &other) : Vector(other) { } /** * Constructor from dcm * * Instance is initialized from a dcm representing coordinate transformation * from frame 2 to frame 1. * * @param dcm dcm to set quaternion to */ Quaternion(const Dcm &dcm) : Vector() { Quaternion &q = *this; q(0) = 1; q(1) = 0; q(2) = 0; q(3) = 0; Type q0_tmp = Type(0.5) * Type(sqrt(Type(1) + dcm(0, 0) + dcm(1, 1) + dcm(2, 2))); if (fabsf(q0_tmp) > 0) { q(0) = q0_tmp; q(1) = Type((dcm(2, 1) - dcm(1, 2)) / (Type(4) * q(0))); q(2) = Type((dcm(0, 2) - dcm(2, 0)) / (Type(4) * q(0))); q(3) = Type((dcm(1, 0) - dcm(0, 1)) / (Type(4) * q(0))); } } /** * Constructor from euler angles * * This sets the instance to a quaternion representing coordinate transformation from * frame 2 to frame 1 where the rotation from frame 1 to frame 2 is described * by a 3-2-1 intrinsic Tait-Bryan rotation sequence. * * @param euler euler angle instance */ Quaternion(const Euler &euler) : Vector() { Quaternion &q = *this; Type cosPhi_2 = Type(cos(euler.phi() / (Type)2.0)); Type cosTheta_2 = Type(cos(euler.theta() / (Type)2.0)); Type cosPsi_2 = Type(cos(euler.psi() / (Type)2.0)); Type sinPhi_2 = Type(sin(euler.phi() / (Type)2.0)); Type sinTheta_2 = Type(sin(euler.theta() / (Type)2.0)); Type sinPsi_2 = Type(sin(euler.psi() / (Type)2.0)); q(0) = cosPhi_2 * cosTheta_2 * cosPsi_2 + sinPhi_2 * sinTheta_2 * sinPsi_2; q(1) = sinPhi_2 * cosTheta_2 * cosPsi_2 - cosPhi_2 * sinTheta_2 * sinPsi_2; q(2) = cosPhi_2 * sinTheta_2 * cosPsi_2 + sinPhi_2 * cosTheta_2 * sinPsi_2; q(3) = cosPhi_2 * cosTheta_2 * sinPsi_2 - sinPhi_2 * sinTheta_2 * cosPsi_2; } /** * Quaternion from AxisAngle * * @param aa axis-angle vector */ Quaternion(const AxisAngle &aa) : Vector() { Quaternion &q = *this; Type angle = aa.norm(); Vector axis = aa.unit(); if (angle < (Type)1e-10) { q(0) = (Type)1.0; q(1) = q(2) = q(3) = 0; } else { Type magnitude = sinf(angle / 2.0f); q(0) = cosf(angle / 2.0f); q(1) = axis(0) * magnitude; q(2) = axis(1) * magnitude; q(3) = axis(2) * magnitude; } } /** * Constructor from quaternion values * * Instance is initialized from quaternion values representing coordinate * transformation from frame 2 to frame 1. * A zero-rotation quaternion is represented by (1,0,0,0). * * @param a set quaternion value 0 * @param b set quaternion value 1 * @param c set quaternion value 2 * @param d set quaternion value 3 */ Quaternion(Type a, Type b, Type c, Type d) : Vector() { Quaternion &q = *this; q(0) = a; q(1) = b; q(2) = c; q(3) = d; } /** * Quaternion multiplication operator * * @param q quaternion to multiply with * @return product */ Quaternion operator*(const Quaternion &q) const { const Quaternion &p = *this; Quaternion r; r(0) = p(0) * q(0) - p(1) * q(1) - p(2) * q(2) - p(3) * q(3); r(1) = p(0) * q(1) + p(1) * q(0) - p(2) * q(3) + p(3) * q(2); r(2) = p(0) * q(2) + p(1) * q(3) + p(2) * q(0) - p(3) * q(1); r(3) = p(0) * q(3) - p(1) * q(2) + p(2) * q(1) + p(3) * q(0); return r; } /** * Self-multiplication operator * * @param other quaternion to multiply with */ void operator*=(const Quaternion &other) { Quaternion &self = *this; self = self * other; } /** * Scalar multiplication operator * * @param scalar scalar to multiply with * @return product */ Quaternion operator*(Type scalar) const { const Quaternion &q = *this; return scalar * q; } /** * Scalar self-multiplication operator * * @param scalar scalar to multiply with */ void operator*=(Type scalar) { Quaternion &q = *this; q = q * scalar; } /** * Computes the derivative * * @param w direction */ Matrix41 derivative(const Matrix31 &w) const { const Quaternion &q = *this; Quaternion v(0, w(0, 0), w(1, 0), w(2, 0)); return v * q * Type(0.5); } /** * Invert quaternion */ void invert() { Quaternion &q = *this; q(1) *= -1; q(2) *= -1; q(3) *= -1; } /** * Invert quaternion * * @return inverted quaternion */ Quaternion inversed() { Quaternion &q = *this; Quaternion ret; ret(0) = q(0); ret(1) = -q(1); ret(2) = -q(2); ret(3) = -q(3); return ret; } /** * Rotate quaternion from rotation vector * TODO replace with AxisAngle call * * @param vec rotation vector */ void rotate(const Vector &vec) { Quaternion res; res.from_axis_angle(vec); (*this) = (*this) * res; } Vector3f conjugate(const Vector3f &vec) { Quaternion q = *this; Quaternion v(0, vec(0), vec(1), vec(2)); Quaternion res = q*v*q.inversed(); return Vector3f(res(1), res(2), res(3)); } Vector3f conjugate_inversed(const Vector3f &vec) { Quaternion q = *this; Quaternion v(0, vec(0), vec(1), vec(2)); Quaternion res = q.inversed()*v*q; return Vector3f(res(1), res(2), res(3)); } /** * Rotation quaternion from vector * * The axis of rotation is given by vector direction and * the angle is given by the norm. * * @param vec rotation vector * @return quaternion representing the rotation */ void from_axis_angle(Vector vec) { Quaternion &q = *this; Type theta = vec.norm(); if (theta < (Type)1e-10) { q(0) = (Type)1.0; q(1) = q(2) = q(3) = 0; return; } vec /= theta; from_axis_angle(vec, theta); } /** * Rotation quaternion from axis and angle * XXX DEPRECATED, use AxisAngle class * * @param axis axis of rotation * @param theta scalar describing angle of rotation * @return quaternion representing the rotation */ void from_axis_angle(const Vector &axis, Type theta) { Quaternion &q = *this; if (theta < (Type)1e-10) { q(0) = (Type)1.0; q(1) = q(2) = q(3) = 0; } Type magnitude = sinf(theta / 2.0f); q(0) = cosf(theta / 2.0f); q(1) = axis(0) * magnitude; q(2) = axis(1) * magnitude; q(3) = axis(2) * magnitude; } /** * Rotation vector from quaternion * XXX DEPRECATED, use AxisAngle class * * The axis of rotation is given by vector direction and * the angle is given by the norm. * * @return vector, direction representing rotation axis and norm representing angle */ Vector to_axis_angle() { Quaternion &q = *this; Type axis_magnitude = Type(sqrt(q(1) * q(1) + q(2) * q(2) + q(3) * q(3))); Vector vec; vec(0) = q(1); vec(1) = q(2); vec(2) = q(3); if (axis_magnitude >= (Type)1e-10) { vec = vec / axis_magnitude; vec = vec * wrap_pi((Type)2.0 * atan2f(axis_magnitude, q(0))); } return vec; } /** * Imaginary components of quaternion */ Vector3 imag() { Quaternion &q = *this; return Vector3(q(1), q(2), q(3)); } /** * XXX DEPRECATED, can use assignment or ctor */ Quaternion from_dcm(Matrix dcm) { return Quaternion(Dcmf(dcm)); } /** * XXX DEPRECATED, can use assignment or ctor */ Dcm to_dcm() { return Dcm(*this); } }; typedef Quaternion Quatf; typedef Quaternion Quaternionf; } // namespace matrix /* vim: set et fenc=utf-8 ff=unix sts=0 sw=4 ts=4 : */