From 4bb8336b2012f5f5325a03d1aab04d29c44864a4 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Mon, 31 Oct 2022 09:28:54 +0200 Subject: [PATCH] Add a Quaternion class --- source/geometry/affinetransform.h | 13 ++ source/geometry/quaternion.h | 194 ++++++++++++++++++++++++++++++ 2 files changed, 207 insertions(+) create mode 100644 source/geometry/quaternion.h diff --git a/source/geometry/affinetransform.h b/source/geometry/affinetransform.h index 1d48a5d..f9660c6 100644 --- a/source/geometry/affinetransform.h +++ b/source/geometry/affinetransform.h @@ -4,6 +4,7 @@ #include #include "angle.h" #include "boundingbox.h" +#include "quaternion.h" #include "ray.h" namespace Msp { @@ -41,6 +42,7 @@ protected: public: static AffineTransform rotation(Angle, const LinAl::Vector &); + static AffineTransform rotation(const Quaternion &); }; @@ -144,6 +146,17 @@ AffineTransform AffineTransformOps::rotation(Angle angle, const L return r; } +template +AffineTransform AffineTransformOps::rotation(const Quaternion &quat) +{ + AffineTransform r; + LinAl::Matrix m = quat.to_matrix(); + for(unsigned i=0; i<3; ++i) + for(unsigned j=0; j<3; ++j) + r.matrix(i, j) = m(i, j); + return r; +} + template inline AffineTransform &AffineTransform::operator*=(const AffineTransform &other) { diff --git a/source/geometry/quaternion.h b/source/geometry/quaternion.h new file mode 100644 index 0000000..7c03612 --- /dev/null +++ b/source/geometry/quaternion.h @@ -0,0 +1,194 @@ +#ifndef MSP_MATH_QUATERNION_H_ +#define MSP_MATH_QUATERNION_H_ + +#include +#include +#include +#include "angle.h" + +namespace Msp { +namespace Geometry { + +template +struct Quaternion +{ + T a, b, c, d; + + Quaternion(): a(0), b(0), c(0), d(0) { } + Quaternion(T a_, T b_, T c_, T d_): a(a_), b(b_), c(c_), d(d_) { } + + static Quaternion one() { return Quaternion(1, 0, 0, 0); } + static Quaternion rotation(Angle, const LinAl::Vector &); + + T scalar() const { return a; } + LinAl::Vector vector() const { return LinAl::Vector(b, c, d); } + + Quaternion &operator+=(const Quaternion &); + Quaternion &operator-=(const Quaternion &); + Quaternion &operator*=(T); + Quaternion &operator*=(const Quaternion &); + Quaternion &operator/=(T); + Quaternion &operator/=(const Quaternion &); + + T norm() const; + Quaternion &normalize(); + + Quaternion conjugate() const { return Quaternion(a, -b, -c, -d); } + Quaternion inverse() const; + + LinAl::Vector rotate(const LinAl::Vector &) const; + LinAl::Matrix to_matrix() const; +}; + +template +Quaternion Quaternion::rotation(Angle angle, const LinAl::Vector &axis) +{ + float c = cos(angle/T(2)); + float s = sin(angle/T(2)); + return Quaternion(c, s*axis.x, s*axis.y, s*axis.z); +} + +template +Quaternion &Quaternion::operator+=(const Quaternion &q) +{ + a += q.a; + b += q.b; + c += q.c; + d += q.d; + return *this; +} + +template +Quaternion operator+(const Quaternion &q1, const Quaternion &q2) +{ + Quaternion r(q1); + return r += q2; +} + +template +Quaternion &Quaternion::operator-=(const Quaternion &q) +{ + a -= q.a; + b -= q.b; + c -= q.c; + d -= q.d; + return *this; +} + +template +Quaternion operator-(const Quaternion &q1, const Quaternion &q2) +{ + Quaternion r(q1); + return r -= q2; +} + +template +Quaternion &Quaternion::operator*=(T s) +{ + a *= s; + b *= s; + c *= s; + d *= s; + return *this; +} + +template +Quaternion operator*(const Quaternion &q, T s) +{ + Quaternion r(q); + return r *= s; +} + +template +Quaternion operator*(T s, const Quaternion &q) +{ + return q*s; +} + +template +Quaternion operator*(const Quaternion &q1, const Quaternion &q2) +{ + return Quaternion(q1.a*q2.a-q1.b*q2.b-q1.c*q2.c-q1.d*q2.d, + q1.a*q2.b+q1.b*q2.a+q1.c*q2.d-q1.d*q2.c, + q1.a*q2.c+q1.c*q2.a-q1.b*q2.d+q1.d*q2.b, + q1.a*q2.d+q1.d*q2.a+q1.b*q2.c-q1.c*q2.b); +} + +template +Quaternion &Quaternion::operator*=(const Quaternion &q) +{ + return *this = *this*q; +} + +template +Quaternion &Quaternion::operator/=(T s) +{ + a /= s; + b /= s; + c /= s; + d /= s; + return *this; +} + +template +Quaternion operator/(const Quaternion &q, T s) +{ + Quaternion r(q); + return r /= s; +} + +template +Quaternion operator/(T s, const Quaternion &q) +{ + return s*q.inverse(); +} + +template +Quaternion &Quaternion::operator/=(const Quaternion &q) +{ + return *this *= q.inverse(); +} + +template +Quaternion operator/(const Quaternion &q1, const Quaternion &q2) +{ + Quaternion r(q1); + return r /= q2; +} + +template +T Quaternion::norm() const +{ + using std::sqrt; + return sqrt(a*a+b*b+c*c+d*d); +} + +template +Quaternion Quaternion::inverse() const +{ + return conjugate()/(a*a+b*b+c*c+d*d); +} + +template +LinAl::Vector Quaternion::rotate(const LinAl::Vector &v) const +{ + return (*this*Quaternion(0, v.x, v.y, v.z)*inverse()).vector(); +} + +template +LinAl::Matrix Quaternion::to_matrix() const +{ + T s = 2*(a*a+b*b+c*c+d*d); + LinAl::Vector columns[3] = + { + { 1-s*(c*c+d*d), s*(b*c+a*d), s*(b*d-a*c) }, + { s*(b*c-a*d), 1-s*(b*b+d*d), s*(c*d+a*b) }, + { s*(b*d+a*c), s*(c*d-a*b), 1-s*(b*b+c*c) } + }; + return LinAl::Matrix::from_columns(columns); +} + +} // namespace Geometry +} // namespace Msp + +#endif -- 2.45.2