From e87151790976313f7b99f865ebb372228c0b98e0 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Mon, 25 Jan 2021 00:59:13 +0300 Subject: [PATCH] Add quaternions & quaternion-based rotations --- libs/geom/include/psemek/geom/quaternion.hpp | 140 +++++++++++++++++++ libs/geom/include/psemek/geom/rotation.hpp | 110 +++++++++++++++ 2 files changed, 250 insertions(+) create mode 100644 libs/geom/include/psemek/geom/quaternion.hpp diff --git a/libs/geom/include/psemek/geom/quaternion.hpp b/libs/geom/include/psemek/geom/quaternion.hpp new file mode 100644 index 00000000..1fc0f5c5 --- /dev/null +++ b/libs/geom/include/psemek/geom/quaternion.hpp @@ -0,0 +1,140 @@ +#pragma once + +#include + +namespace psemek::geom +{ + + template + struct quaternion + { + geom::vector coords; + + quaternion() = default; + + static quaternion zero(); + static quaternion identity(); + static quaternion scalar(T value); + static quaternion vector(geom::vector const & v); + static quaternion rotation(T angle, geom::vector const & axis); + + template + quaternion(Args && ... args) + : coords{ static_cast(std::forward(args))... } + { + static_assert(sizeof...(Args) == 4); + } + + T & operator[] (std::size_t i) { return coords[i]; } + T const & operator[] (std::size_t i) const { return coords[i]; } + }; + + template + quaternion(Args && ...) -> quaternion>; + + template + quaternion quaternion::zero() + { + return quaternion{T(0), T(0), T(0), T(0)}; + } + + template + quaternion quaternion::identity() + { + return quaternion{T(1), T(0), T(0), T(0)}; + } + + template + quaternion quaternion::scalar(T value) + { + return {value, T(0), T(0), T(0)}; + } + + template + quaternion quaternion::vector(geom::vector const & v) + { + return {T(0), v[0], v[1], v[2]}; + } + + template + quaternion quaternion::rotation(T angle, geom::vector const & axis) + { + auto const c = std::cos(angle / 2); + auto const s = std::sin(angle / 2); + + return quaternion{c, s * axis[0], s * axis[1], s * axis[2]}; + } + + template + quaternion operator - (quaternion const & q) + { + return {-q.coords}; + } + + template + quaternion operator + (quaternion const & q1, quaternion const & q2) + { + return {q1.coords + q2.coords}; + } + + template + quaternion operator - (quaternion const & q1, quaternion const & q2) + { + return {q1.coords - q2.coords}; + } + + template + quaternion operator * (quaternion const & q, T s) + { + return {q.coords * s}; + } + + template + quaternion operator * (T s, quaternion const & q) + { + return {s * q.coords}; + } + + template + quaternion operator * (quaternion const & q1, quaternion const & q2) + { + quaternion r; + r[0] = q1[0] * q2[0] - q1[1] * q2[1] - q1[2] * q2[2] - q1[3] * q2[3]; + r[1] = q1[0] * q2[1] + q1[1] * q2[0] + q1[2] * q2[3] - q1[3] * q2[2]; + r[2] = q1[0] * q2[2] - q1[1] * q2[3] + q1[2] * q2[0] + q1[3] * q2[1]; + r[3] = q1[0] * q2[3] + q1[1] * q2[2] - q1[2] * q2[1] + q1[3] * q2[0]; + return r; + } + + template + T norm_sqr(quaternion const & q) + { + return length_sqr(q.coords); + } + + template + T norm(quaternion const & q) + { + return length(q.coords); + } + + template + quaternion conjugate(quaternion const & q) + { + return {q[0], -q[1], -q[2], -q[3]}; + } + + template + quaternion inverse(quaternion const & q) + { + return conjugate(q) / norm(q); + } + + template + vector rotate(quaternion const & q, vector const & v) + { + auto res = q * quaternion::vector(v) * inverse(q); + return {res[1], res[2], res[3]}; + } + +} diff --git a/libs/geom/include/psemek/geom/rotation.hpp b/libs/geom/include/psemek/geom/rotation.hpp index 01f24dd1..bb3eb3f3 100644 --- a/libs/geom/include/psemek/geom/rotation.hpp +++ b/libs/geom/include/psemek/geom/rotation.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include @@ -59,6 +60,30 @@ namespace psemek::geom void fill_matrix(Matrix & m) const; }; + template + struct quaternion_rotation + { + quaternion quat; + + quaternion_rotation(); + quaternion_rotation(quaternion const & quat); + quaternion_rotation(T angle, vector const & axis); + + matrix affine_matrix() const; + matrix linear_matrix() const; + vector translation_vector() const; + matrix homogeneous_matrix() const; + + affine_transform transform() const; + + vector operator()(vector const & v) const; + point operator()(point const & p) const; + + private: + template + void fill_matrix(Matrix & m) const; + }; + template axis_rotation(vector, T) -> axis_rotation; @@ -239,4 +264,89 @@ namespace psemek::geom return {r.axis, -r.angle}; } + template + quaternion_rotation::quaternion_rotation() + : quat{quaternion::identity()} + {} + + template + quaternion_rotation::quaternion_rotation(quaternion const & quat) + : quat{quat} + {} + + template + quaternion_rotation::quaternion_rotation(T angle, vector const & axis) + : quat{quaternion::rotation(angle, axis)} + {} + + template + matrix quaternion_rotation::affine_matrix() const + { + auto result = matrix::identity(); + fill_matrix(result); + return result; + } + + template + matrix quaternion_rotation::linear_matrix() const + { + auto result = matrix::identity(); + fill_matrix(result); + return result; + } + + template + vector quaternion_rotation::translation_vector() const + { + return vector::zero(); + } + + template + matrix quaternion_rotation::homogeneous_matrix() const + { + auto result = matrix::identity(); + fill_matrix(result); + return result; + } + + template + affine_transform quaternion_rotation::transform() const + { + return {affine_matrix()}; + } + + template + vector quaternion_rotation::operator()(vector const & v) const + { + return linear_matrix() * v; + } + + template + point quaternion_rotation::operator()(point const & p) const + { + static constexpr auto o = point::zero(); + return o + linear_matrix() * (p - o); + } + + template + template + void quaternion_rotation::fill_matrix(Matrix & m) const + { + m[0][0] = 1 - 2 * (quat[2] * quat[2] + quat[3] * quat[3]); + m[0][1] = 2 * (quat[1] * quat[2] - quat[0] * quat[3]); + m[0][2] = 2 * (quat[1] * quat[3] + quat[0] * quat[2]); + m[1][0] = 2 * (quat[1] * quat[2] + quat[0] * quat[3]); + m[1][1] = 1 - 2 * (quat[1] * quat[1] + quat[3] * quat[3]); + m[1][2] = 2 * (quat[2] * quat[3] - quat[0] * quat[1]); + m[2][0] = 2 * (quat[1] * quat[3] - quat[0] * quat[2]); + m[2][1] = 2 * (quat[2] * quat[3] + quat[0] * quat[1]); + m[2][2] = 1 - 2 * (quat[1] * quat[1] + quat[2] * quat[2]); + } + + template + quaternion_rotation inverse(quaternion_rotation const & r) + { + return quaternion_rotation{quaternion{r.quat[0], -r.quat[1], -r.quat[2], -r.quat[3]}}; + } + }