From 3ad264893ea0808acf9e11d05a63b78e5bd7ea5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 16 Feb 2013 20:13:36 +0100 Subject: [PATCH] Math: creating rotation and translation DualQuaternion. --- src/Math/DualQuaternion.h | 68 ++++++++++++++++++++++++++++ src/Math/Matrix4.h | 10 ++-- src/Math/Quaternion.h | 6 ++- src/Math/Test/CMakeLists.txt | 8 +++- src/Math/Test/DualQuaternionTest.cpp | 30 ++++++++++++ 5 files changed, 115 insertions(+), 7 deletions(-) diff --git a/src/Math/DualQuaternion.h b/src/Math/DualQuaternion.h index 51aed4e36..0b399b4f3 100644 --- a/src/Math/DualQuaternion.h +++ b/src/Math/DualQuaternion.h @@ -31,6 +31,36 @@ namespace Magnum { namespace Math { */ template class DualQuaternion: public Dual> { public: + /** + * @brief Rotation dual quaternion + * @param angle Rotation angle (counterclockwise, in radians) + * @param normalizedAxis Normalized rotation axis + * + * Expects that the rotation axis is normalized. @f[ + * \hat q = [\boldsymbol a \cdot sin \frac \theta 2, cos \frac \theta 2] + \epsilon [\boldsymbol 0, 0] + * @f] + * @see rotationAngle(), rotationAxis(), Quaternion::rotation(), + * Matrix4::rotation(), Vector3::xAxis(), Vector3::yAxis(), + * Vector3::zAxis(), deg(), rad() + */ + inline static DualQuaternion rotation(T angle, const Vector3& normalizedAxis) { + return {Quaternion::rotation(angle, normalizedAxis), {{}, T(0)}}; + } + + /** + * @brief Translation dual quaternion + * @param vector Translation vector + * + * @f[ + * \hat q = [\boldsymbol 0, 1] + \epsilon [\frac{\boldsymbol v}{2}, 0] + * @f] + * @see translation() const, Matrix3::translation(const Vector2&), + * Vector3::xAxis(), Vector3::yAxis(), Vector3::zAxis() + */ + inline static DualQuaternion translation(const Vector3& vector) { + return {{}, {vector/T(2), T(0)}}; + } + /** * @brief Default constructor * @@ -52,6 +82,44 @@ template class DualQuaternion: public Dual> { */ inline constexpr /*implicit*/ DualQuaternion(const Quaternion& real, const Quaternion& dual): Dual>(real, dual) {} + /** + * @brief Rotation angle of unit dual quaternion + * + * Expects that the real part of the quaternion is normalized. @f[ + * \theta = 2 \cdot acos q_{S 0} + * @f] + * @see rotationAxis(), rotation(), Quaternion::rotationAngle() + */ + inline T rotationAngle() const { + return this->real().rotationAngle(); + } + + /** + * @brief Rotation axis of unit dual quaternion + * + * Expects that the quaternion is normalized. Returns either unit-length + * vector for valid rotation quaternion or NaN vector for + * default-constructed quaternion. @f[ + * \boldsymbol a = \frac{\boldsymbol q_{V 0}}{\sqrt{1 - q_{S 0}^2}} + * @f] + * @see rotationAngle(), rotation(), Quaternion::rotationAxis() + */ + inline Vector3 rotationAxis() const { + return this->real().rotationAxis(); + } + + /** + * @brief Translation part of unit dual quaternion + * + * @f[ + * \boldsymbol a = 2 (q_\epsilon q_0^*)_V + * @f] + * @see translation(const Vector3&) + */ + inline Vector3 translation() const { + return (this->dual()*this->real().conjugated()).vector()*T(2); + } + /** * @brief Quaternion-conjugated dual quaternion * diff --git a/src/Math/Matrix4.h b/src/Math/Matrix4.h index 810493644..4b5c85ce1 100644 --- a/src/Math/Matrix4.h +++ b/src/Math/Matrix4.h @@ -39,8 +39,9 @@ template class Matrix4: public Matrix<4, T> { * @brief 3D translation * @param vector Translation vector * - * @see translation(), Matrix3::translation(const Vector2&), - * Vector3::xAxis(), Vector3::yAxis(), Vector3::zAxis() + * @see translation(), DualQuaternion::translation(), + * Matrix3::translation(const Vector2&), Vector3::xAxis(), + * Vector3::yAxis(), Vector3::zAxis() */ inline constexpr static Matrix4 translation(const Vector3& vector) { return {{ T(1), T(0), T(0), T(0)}, @@ -70,8 +71,9 @@ template class Matrix4: public Matrix<4, T> { * * Expects that the rotation axis is normalized. If possible, use * faster alternatives like rotationX(), rotationY() and rotationZ(). - * @see rotation() const, Quaternion::rotation(), Matrix3::rotation(T), - * Vector3::xAxis(), Vector3::yAxis(), Vector3::zAxis(), deg(), rad() + * @see rotation() const, DualQuaternion::rotation(), + * Quaternion::rotation(), Matrix3::rotation(T), Vector3::xAxis(), + * Vector3::yAxis(), Vector3::zAxis(), deg(), rad() */ static Matrix4 rotation(T angle, const Vector3& normalizedAxis) { CORRADE_ASSERT(MathTypeTraits::equals(normalizedAxis.dot(), T(1)), diff --git a/src/Math/Quaternion.h b/src/Math/Quaternion.h index be49ec335..a25049370 100644 --- a/src/Math/Quaternion.h +++ b/src/Math/Quaternion.h @@ -103,14 +103,16 @@ template class Quaternion { } /** - * @brief Create quaternion from rotation + * @brief Rotation quaternion * @param angle Rotation angle (counterclockwise, in radians) * @param normalizedAxis Normalized rotation axis * * Expects that the rotation axis is normalized. @f[ * q = [\boldsymbol a \cdot sin \frac \theta 2, cos \frac \theta 2] * @f] - * @see Matrix4::rotation() + * @see rotationAngle(), rotationAxis(), DualQuaternion::rotation(), + * Matrix4::rotation(), Vector3::xAxis(), Vector3::yAxis(), + * Vector3::zAxis(), deg(), rad() */ inline static Quaternion rotation(T angle, const Vector3& normalizedAxis) { CORRADE_ASSERT(MathTypeTraits::equals(normalizedAxis.dot(), T(1)), diff --git a/src/Math/Test/CMakeLists.txt b/src/Math/Test/CMakeLists.txt index 84cb4cbfb..1f70c8dcb 100644 --- a/src/Math/Test/CMakeLists.txt +++ b/src/Math/Test/CMakeLists.txt @@ -21,4 +21,10 @@ corrade_add_test(MathDualTest DualTest.cpp) corrade_add_test(MathQuaternionTest QuaternionTest.cpp LIBRARIES MagnumMathTestLib) corrade_add_test(MathDualQuaternionTest DualQuaternionTest.cpp LIBRARIES MagnumMathTestLib) -set_target_properties(MathVectorTest MathMatrix3Test MathMatrix4Test MathQuaternionTest PROPERTIES COMPILE_FLAGS -DCORRADE_GRACEFUL_ASSERT) +set_target_properties( + MathVectorTest + MathMatrix3Test + MathMatrix4Test + MathDualQuaternionTest + MathQuaternionTest + PROPERTIES COMPILE_FLAGS -DCORRADE_GRACEFUL_ASSERT) diff --git a/src/Math/Test/DualQuaternionTest.cpp b/src/Math/Test/DualQuaternionTest.cpp index 18d51109d..dfc41fcf9 100644 --- a/src/Math/Test/DualQuaternionTest.cpp +++ b/src/Math/Test/DualQuaternionTest.cpp @@ -16,6 +16,7 @@ #include #include +#include "Math/Constants.h" #include "Math/DualQuaternion.h" namespace Magnum { namespace Math { namespace Test { @@ -34,6 +35,9 @@ class DualQuaternionTest: public Corrade::TestSuite::Tester { void conjugated(); void inverted(); + void rotation(); + void translation(); + void debug(); }; @@ -53,6 +57,9 @@ DualQuaternionTest::DualQuaternionTest() { &DualQuaternionTest::conjugated, &DualQuaternionTest::inverted, + &DualQuaternionTest::rotation, + &DualQuaternionTest::translation, + &DualQuaternionTest::debug); } @@ -102,6 +109,29 @@ void DualQuaternionTest::inverted() { CORRADE_COMPARE(a.inverted(), b); } +void DualQuaternionTest::rotation() { + std::ostringstream o; + Error::setOutput(&o); + + float angle = deg(120.0f); + Vector3 axis(1.0f/Constants::sqrt3()); + + CORRADE_COMPARE(DualQuaternion::rotation(angle, axis*2.0f), DualQuaternion()); + CORRADE_COMPARE(o.str(), "Math::Quaternion::rotation(): axis must be normalized\n"); + + DualQuaternion q = DualQuaternion::rotation(angle, axis); + CORRADE_COMPARE(q, DualQuaternion({Vector3(0.5f, 0.5f, 0.5f), 0.5f}, {{}, 0.0f})); + CORRADE_COMPARE(q.rotationAngle(), angle); + CORRADE_COMPARE(q.rotationAxis(), axis); +} + +void DualQuaternionTest::translation() { + Vector3 vec(1.0f, -3.5f, 0.5f); + DualQuaternion q = DualQuaternion::translation(vec); + CORRADE_COMPARE(q, DualQuaternion({}, {{0.5f, -1.75f, 0.25f}, 0.0f})); + CORRADE_COMPARE(q.translation(), vec); +} + void DualQuaternionTest::debug() { std::ostringstream o;