quaternion: update to use coord framework

This commit is contained in:
Danny Robson 2016-08-10 17:42:52 +10:00
parent bb6678726f
commit 974998cc48
7 changed files with 268 additions and 228 deletions

View File

@ -435,6 +435,7 @@ TEST_BIN = \
test/point \
test/polynomial \
test/pool \
test/quaternion \
test/rand/buckets \
test/random \
test/range \

View File

@ -20,11 +20,23 @@
namespace util { namespace coord {
///////////////////////////////////////////////////////////////////////
// tags for accessor names
//
// colours
struct rgba { };
struct hsv { };
// physical positions
struct xyzw { };
// texture coords
struct stpq { };
// physical dimensions
struct whd { };
// quaternions
struct wxyz { };
struct abcd { };
} }
#endif

View File

@ -30,6 +30,7 @@ namespace util {
template <size_t,typename> struct extent;
template <size_t,typename> struct vector;
template <size_t,typename> struct colour;
template <size_t,typename> struct quaternion;
///////////////////////////////////////////////////////////////////////
// operation traits
@ -52,10 +53,11 @@ namespace util {
template <template <size_t,typename> class> struct is_coord : std::false_type { };
template <> struct is_coord<point> : std::true_type { };
template <> struct is_coord<extent> : std::true_type { };
template <> struct is_coord<vector> : std::true_type { };
template <> struct is_coord<colour> : std::true_type { };
template <> struct is_coord<point> : std::true_type { };
template <> struct is_coord<extent> : std::true_type { };
template <> struct is_coord<vector> : std::true_type { };
template <> struct is_coord<colour> : std::true_type { };
template <> struct is_coord<quaternion> : std::true_type { };
template <template <size_t,typename> class K>
constexpr bool

View File

@ -168,6 +168,17 @@ namespace util { namespace coord {
struct { T w,h,d; };
};
};
///////////////////////////////////////////////////////////////////////////
template <typename T>
struct store<4,T,wxyz,abcd> {
union {
T data[4];
struct { T w,x,y,z; };
struct { T a,b,c,d; };
};
};
} }
#endif

View File

@ -15,75 +15,52 @@
*/
#include "quaternion.hpp"
#include "./quaternion.hpp"
#include "debug.hpp"
#include "maths.hpp"
#include "./debug.hpp"
#include "./maths.hpp"
#include <iostream>
//-----------------------------------------------------------------------------
using util::quaternion;
using util::matrix4;
//-----------------------------------------------------------------------------
template<> const quaternion< float> quaternion< float>::IDENTITY = { 1, 0, 0, 0 };
template<> const quaternion<double> quaternion<double>::IDENTITY = { 1, 0, 0, 0 };
template<> const quaternion<4, float> quaternion<4, float>::IDENTITY = { 1, 0, 0, 0 };
template<> const quaternion<4, double> quaternion<4, double>::IDENTITY = { 1, 0, 0, 0 };
///////////////////////////////////////////////////////////////////////////////
template <typename T>
quaternion<T>::quaternion (T _a, T _b, T _c, T _d):
a (_a),
b (_b),
c (_c),
d (_d)
{ ; }
template <size_t S, typename T>
bool
quaternion<S,T>::is_normalised (void) const
{
return almost_equal (T{1}, magnitude2 ());
}
//-----------------------------------------------------------------------------
template <typename T>
quaternion<T>::quaternion (T _a):
a (_a),
b (T{}),
c (T{}),
d (T{})
{ ; }
//-----------------------------------------------------------------------------
template <typename T>
quaternion<T>::quaternion ():
quaternion (T{}, T{}, T{}, T{})
{ ; }
//-----------------------------------------------------------------------------
template <typename T>
quaternion<T>::quaternion (vector3<T> v):
quaternion (0, v.x, v.y, v.z)
{ ; }
///////////////////////////////////////////////////////////////////////////////
template <typename T>
quaternion<T>
quaternion<T>::rotation (const T radians, const vector<3,T> axis) {
template <size_t S, typename T>
quaternion<S,T>
quaternion<S,T>::rotation (const T radians, const vector<3,T> axis)
{
CHECK (axis.is_normalised ());
auto w = std::cos (radians / 2);
auto xyz = std::sin (radians / 2) * axis;
return {
std::cos (radians / 2),
std::sin (radians / 2) * axis.x,
std::sin (radians / 2) * axis.y,
std::sin (radians / 2) * axis.z
w, xyz.x, xyz.y, xyz.z
};
}
//-----------------------------------------------------------------------------
template <typename T>
quaternion<T>
quaternion<T>::rotation (vector<3,T> src, vector<3,T> dst) {
template <size_t S, typename T>
quaternion<S,T>
quaternion<S,T>::rotation (const vector<3,T> src, const vector<3,T> dst)
{
auto v = util::cross (src, dst);
return {
@ -96,196 +73,148 @@ quaternion<T>::rotation (vector<3,T> src, vector<3,T> dst) {
///////////////////////////////////////////////////////////////////////////////
template <typename T>
template <size_t S, typename T>
T
quaternion<T>::norm2 (void) const
quaternion<S,T>::magnitude2 (void) const
{
return a * a + b * b + c * c + d * d;
return this->a * this->a +
this->b * this->b +
this->c * this->c +
this->d * this->d;
}
//-----------------------------------------------------------------------------
template <typename T>
template <size_t S, typename T>
T
quaternion<T>::norm (void) const
quaternion<S,T>::magnitude (void) const
{
return std::sqrt (norm2 ());
return std::sqrt (magnitude2 ());
}
//-----------------------------------------------------------------------------
template <typename T>
quaternion<T>
quaternion<T>::normalised (void) const
template <size_t S, typename T>
quaternion<S,T>
quaternion<S,T>::normalised (void) const
{
return *this / norm ();
return *this / magnitude ();
}
///////////////////////////////////////////////////////////////////////////////
template <typename T>
quaternion<T>
quaternion<T>::operator- (void) const
{
return { -a, -b, -c, -d };
}
//-----------------------------------------------------------------------------
template <typename T>
quaternion<T>
quaternion<T>::conjugate (void) const
{
return { a, -b, -c, -d };
}
///////////////////////////////////////////////////////////////////////////////
template <typename T>
quaternion<T>
quaternion<T>::operator+ (const quaternion<T> q) const
template <size_t S, typename T>
quaternion<S,T>
util::operator* (const quaternion<S,T> a, const quaternion<S,T> b)
{
return {
a + q.a,
b + q.b,
c + q.c,
d + q.d
a.w * b.w - a.x * b.x - a.y * b.y - a.z * b.z,
a.w * b.x + a.x * b.w + a.y * b.z - a.z * b.y,
a.w * b.y - a.x * b.z + a.y * b.w + a.z * b.x,
a.w * b.z + a.x * b.y - a.y * b.x + a.z * b.w,
};
}
template quaternion<4,float> util::operator* (quaternion<4,float>, quaternion<4,float>);
//-----------------------------------------------------------------------------
template <size_t S, typename T>
quaternion<S,T>
util::operator/ (const quaternion<S,T> a, const quaternion<S,T> b)
{
CHECK (a.is_normalised ());
CHECK (b.is_normalised ());
return quaternion<S,T> {
a.w * b.w + a.x * b.x + a.y * b.y + a.z * b.z,
- a.w * b.x + a.x * b.w + a.y * b.z - a.z * b.y,
- a.w * b.y - a.x * b.z + a.y * b.w + a.z * b.x,
- a.w * b.z + a.x * b.y - a.y * b.x + a.z * b.w,
};
}
template quaternion<4,float> util::operator/ (quaternion<4,float>, quaternion<4,float>);
///////////////////////////////////////////////////////////////////////////////
template <size_t S, typename T>
quaternion<S,T>
util::operator* (const quaternion<S,T> q, const T t)
{
return {
q.w * t,
q.x * t,
q.y * t,
q.z * t
};
}
//-----------------------------------------------------------------------------
template <typename T>
quaternion<T>
quaternion<T>::operator- (const quaternion<T> q) const
template <size_t S, typename T>
quaternion<S,T>
util::operator/ (const quaternion<S,T> q, const T t)
{
return {
a - q.a,
b - q.b,
c - q.c,
d - q.d
q.w / t,
q.x / t,
q.y / t,
q.z / t
};
}
//-----------------------------------------------------------------------------
template <typename T>
quaternion<T>
quaternion<T>::operator* (const quaternion<T> q) const {
return {
a * q.a - b * q.b - c * q.c - d * q.d,
a * q.b + b * q.a + c * q.d - d * q.c,
a * q.c - b * q.d + c * q.a + d * q.b,
a * q.d + b * q.c - c * q.b + d * q.a,
};
}
//-----------------------------------------------------------------------------
template <typename T>
quaternion<T>
quaternion<T>::operator/ (const quaternion<T> q) const
{
auto n = q.norm2 ();
return {
( a * q.a + b * q.b + c * q.c + d * q.d) / n,
(- a * q.b + b * q.a + c * q.d - d * q.c) / n,
(- a * q.c - b * q.d + c * q.a + d * q.b) / n,
(- a * q.d + b * q.c - c * q.b + d * q.a) / n
};
}
///////////////////////////////////////////////////////////////////////////////
template <typename T>
quaternion<T>
quaternion<T>::operator+ (const T t) const
template <size_t S, typename T>
util::matrix4<T>
quaternion<S, T>::as_matrix (void) const
{
return { a + t, b, c, d };
}
CHECK (is_normalised ());
//-----------------------------------------------------------------------------
template <typename T>
quaternion<T>
quaternion<T>::operator- (const T t) const
{
return { a - t, b, c, d };
}
//-----------------------------------------------------------------------------
template <typename T>
quaternion<T>
quaternion<T>::operator* (const T t) const
{
return { a * t, b, c, d };
}
//-----------------------------------------------------------------------------
template <typename T>
quaternion<T>
quaternion<T>::operator/ (const T t) const
{
return { a / t, b, c, d };
}
///////////////////////////////////////////////////////////////////////////////
template <typename T>
bool
quaternion<T>::operator== (const quaternion<T> rhs) const
{
return almost_equal (a, rhs.a) &&
almost_equal (b, rhs.b) &&
almost_equal (c, rhs.c) &&
almost_equal (d, rhs.d);
}
//-----------------------------------------------------------------------------
template <typename T>
bool
quaternion<T>::operator!= (const quaternion<T> rhs) const
{
return !(*this == rhs);
}
///////////////////////////////////////////////////////////////////////////////
template <typename T>
matrix4<T>
quaternion<T>::rotation_matrix (void) const
{
CHECK_EQ (T{1}, norm ());
const T wx = w * x, wy = w * y, wz = w * z;
const T xx = x * x, xy = x * y, xz = x * z;
const T yy = y * y, yz = y * z, zz = z * z;
const T wx = this->w * this->x, wy = this->w * this->y, wz = this->w * this->z;
const T xx = this->x * this->x, xy = this->x * this->y, xz = this->x * this->z;
const T yy = this->y * this->y, yz = this->y * this->z, zz = this->z * this->z;
return { {
{ 1 - 2 * (yy - zz), 2 * (xy - wz), 2 * (xz + wy), 0 },
{ 2 * (xy + wz), 1 - 2 * (xx - zz), 2 * (yz - wx), 0 },
{ 2 * (xz - wy), 2 * (yz + wx), 1 - 2 * (xx - yy), 0 },
{ 2 * (xz - wy), 2 * (yz + wx), 1 - 2 * (xx + yy), 0 },
{ 0, 0, 0, 1 }
} };
}
///////////////////////////////////////////////////////////////////////////////
template <typename T>
template <size_t S, typename T>
std::ostream&
util::operator<< (std::ostream &os, quaternion<T> q)
util::operator<< (std::ostream &os, const quaternion<S,T> q)
{
os << q.w << ' ' << q.x << "i " << q.y << "j " << q.z << 'k';
return os;
return os << "[" << q.w << ", " << q.x << ", " << q.y << ", " << q.z << "]";
}
///////////////////////////////////////////////////////////////////////////////
template struct util::quaternion<float>;
template struct util::quaternion<double>;
template struct util::quaternion<4,float>;
template struct util::quaternion<4,double>;
template std::ostream& util::operator<< (std::ostream&, quaternion<float>);
template std::ostream& util::operator<< (std::ostream&, quaternion<double>);
template std::ostream& util::operator<< (std::ostream&, quaternion<4,float>);
template std::ostream& util::operator<< (std::ostream&, quaternion<4,double>);
///////////////////////////////////////////////////////////////////////////////
namespace util { namespace debug {
template <size_t S, typename T>
struct validator<quaternion<S,T>> {
static bool is_valid (const quaternion<S,T> &q)
{
return q.is_normalised ();
}
};
} }
//-----------------------------------------------------------------------------
template bool util::debug::is_valid(const quaternion<4,float>&);
template bool util::debug::is_valid(const quaternion<4,double>&);

View File

@ -17,6 +17,8 @@
#ifndef __UTIL_QUATERNION_HPP
#define __UTIL_QUATERNION_HPP
#include "coord.hpp"
#include "vector.hpp"
#include "matrix.hpp"
@ -24,53 +26,46 @@
namespace util {
template <typename T>
struct quaternion {
///////////////////////////////////////////////////////////////////////
union {
struct { T w, x, y, z; };
struct { T a, b, c, d; };
T data[4];
};
// quaternions must be 4 elements, but we include a size parameter so it
// fits with the generic coord infrastructure more easily.
//
// specifically:
// large regions of base code require a template template parameter with
// size and type arguments, which is annoying to work around for this one
// case.
//
// we protect against invalid instantiations through static_assert
template <size_t S, typename T>
struct quaternion : public coord::base<4,T,quaternion,coord::wxyz,coord::abcd> {
static_assert (S == 4, "quaternions must be 4 elements");
static const quaternion IDENTITY;
using coord::base<S,T,::util::quaternion,::util::coord::wxyz,::util::coord::abcd>::base;
static quaternion rotation (T radians, vector<3,T> axis);
static quaternion rotation (vector<3,T> src, vector<3,T> dst);
quaternion (T a, T b, T c, T d);
quaternion (T a);
quaternion ();
quaternion (vector<3,T>);
T magnitude (void) const;
T magnitude2 (void) const;
T norm (void) const;
T norm2 (void) const;
bool is_normalised (void) const;
quaternion normalised (void) const;
quaternion operator- (void) const;
quaternion conjugate (void) const;
matrix4<T> as_matrix (void) const;
quaternion operator+ (const quaternion) const;
quaternion operator- (const quaternion) const;
quaternion operator* (const quaternion) const;
quaternion operator/ (const quaternion) const;
quaternion operator+ (const T) const;
quaternion operator- (const T) const;
quaternion operator* (const T) const;
quaternion operator/ (const T) const;
bool operator== (const quaternion) const;
bool operator!= (const quaternion) const;
matrix4<T> rotation_matrix (void) const;
static const quaternion IDENTITY;
};
typedef quaternion<float> quaternionf;
template <size_t S, typename T> quaternion<S,T> operator* (const quaternion<S,T>, const quaternion<S,T>);
template <size_t S, typename T> quaternion<S,T> operator/ (const quaternion<S,T>, const quaternion<S,T>);
template <typename T>
template <size_t S, typename T> quaternion<S,T> operator* (const quaternion<S,T>, const T);
template <size_t S, typename T> quaternion<S,T> operator/ (const quaternion<S,T>, const T);
typedef quaternion<4,float> quaternionf;
template <size_t S, typename T>
std::ostream&
operator<< (std::ostream&, quaternion<T>);
operator<< (std::ostream&, quaternion<S,T>);
}

90
test/quaternion.cpp Normal file
View File

@ -0,0 +1,90 @@
#include <quaternion.hpp>
#include "tap.hpp"
#include "types.hpp"
using util::quaternion;
using util::quaternionf;
///////////////////////////////////////////////////////////////////////////////
int
main (void)
{
util::TAP::logger tap;
tap.expect_eq (
quaternionf::IDENTITY.magnitude (), 1.f,
"identity magnitude is unit"
);
tap.expect_eq (
quaternionf::IDENTITY * quaternionf::IDENTITY,
quaternionf::IDENTITY,
"identity multiplication with identity"
);
{
auto val = quaternionf (2, 3, 4, 7).normalised ();
tap.expect_eq (
val * quaternionf::IDENTITY,
val,
"identity multiplication with quaternion constant"
);
}
{
util::vector4f a_v { 2, -11, 5, -17};
util::vector4f b_v { 3, 13, -7, -19};
auto a = a_v.normalised ().as<quaternion> ();
auto b = b_v.normalised ().as<quaternion> ();
auto c = quaternionf {
-0.27358657116960006f,
-0.43498209092420004f,
-0.8443769181970001f,
-0.15155515799559996f,
};
tap.expect_eq (a * b, c, "multiplication");
tap.expect_neq (b * a, c, "multiplication is not commutative");
}
tap.expect_eq (
quaternionf::IDENTITY.as_matrix (),
util::matrix4f::IDENTITY,
"identity quaternion to matrix"
);
{
static const struct {
float mag;
util::vector3f axis;
} ROTATIONS[] = {
{ 0.f, { 1.f, 0.f, 0.f } },
{ 1.f, { 0.f, 1.f, 0.f } },
{ 0.f, { 0.f, 0.f, 1.f } },
};
for (size_t i = 0; i < elems (ROTATIONS); ++i) {
const auto &r = ROTATIONS[i];
tap.expect_eq (quaternionf::rotation (r.mag, r.axis).as_matrix (),
util::matrix4f::rotation (r.mag, r.axis),
"single basis rotation %zu", i);
}
auto q_total = quaternionf::IDENTITY;
auto m_total = util::matrix4f::IDENTITY;
for (auto r: ROTATIONS) {
q_total = q_total.rotation (r.mag, r.axis) * q_total;
m_total = m_total.rotation (r.mag, r.axis) * m_total;
}
tap.expect_eq (q_total.as_matrix (), m_total, "chained single axis rotations");
}
return tap.status ();
}