From 5615a4c7f6f8e3629885465d6eaa98686483a4ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 16 Feb 2013 19:32:13 +0100 Subject: [PATCH] Math: initial implementation of DualQuaternion class. Currently practically unusable. --- src/CMakeLists.txt | 1 + src/Math/CMakeLists.txt | 1 + src/Math/DualQuaternion.cpp | 27 +++++ src/Math/DualQuaternion.h | 159 +++++++++++++++++++++++++++ src/Math/Math.h | 1 + src/Math/Quaternion.h | 2 +- src/Math/Test/CMakeLists.txt | 1 + src/Math/Test/DualQuaternionTest.cpp | 114 +++++++++++++++++++ 8 files changed, 305 insertions(+), 1 deletion(-) create mode 100644 src/Math/DualQuaternion.cpp create mode 100644 src/Math/DualQuaternion.h create mode 100644 src/Math/Test/DualQuaternionTest.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1f20f218e..b446f4e95 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -98,6 +98,7 @@ add_library(MagnumObjects OBJECT ${Magnum_SRCS}) # Files shared between main library and math unit test library set(MagnumMath_SRCS + Math/DualQuaternion.cpp Math/Functions.cpp Math/Quaternion.cpp Math/RectangularMatrix.cpp diff --git a/src/Math/CMakeLists.txt b/src/Math/CMakeLists.txt index b8155147e..4bcbf5743 100644 --- a/src/Math/CMakeLists.txt +++ b/src/Math/CMakeLists.txt @@ -2,6 +2,7 @@ set(MagnumMath_HEADERS BoolVector.h Constants.h Dual.h + DualQuaternion.h Functions.h Math.h MathTypeTraits.h diff --git a/src/Math/DualQuaternion.cpp b/src/Math/DualQuaternion.cpp new file mode 100644 index 000000000..7a5d2448c --- /dev/null +++ b/src/Math/DualQuaternion.cpp @@ -0,0 +1,27 @@ +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +#include "DualQuaternion.h" + +namespace Magnum { namespace Math { + +#ifndef DOXYGEN_GENERATING_OUTPUT +template Corrade::Utility::Debug operator<<(Corrade::Utility::Debug, const DualQuaternion&); +#ifndef MAGNUM_TARGET_GLES +template Corrade::Utility::Debug operator<<(Corrade::Utility::Debug, const DualQuaternion&); +#endif +#endif + +}} diff --git a/src/Math/DualQuaternion.h b/src/Math/DualQuaternion.h new file mode 100644 index 000000000..51aed4e36 --- /dev/null +++ b/src/Math/DualQuaternion.h @@ -0,0 +1,159 @@ +#ifndef Magnum_Math_DualQuaternion_h +#define Magnum_Math_DualQuaternion_h +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +/** @file + * @brief Class Magnum::Math::DualQuaternion + */ + +#include "Math/Dual.h" +#include "Math/Quaternion.h" + +namespace Magnum { namespace Math { + +/** +@brief %Dual quaternion + +@see Dual, Quaternion +*/ +template class DualQuaternion: public Dual> { + public: + /** + * @brief Default constructor + * + * All components set to zero except real scalar part, which is `1`. + * @todoc Remove workaround when Doxygen is predictable + */ + #ifdef DOXYGEN_GENERATING_OUTPUT + inline constexpr /*implicit*/ DualQuaternion(); + #else + inline constexpr /*implicit*/ DualQuaternion(): Dual>({}, {{}, T(0)}) {} + #endif + + /** + * @brief Construct dual quaternion from real and dual part + * + * @f[ + * \hat q = q_0 + \epsilon q_\epsilon + * @f] + */ + inline constexpr /*implicit*/ DualQuaternion(const Quaternion& real, const Quaternion& dual): Dual>(real, dual) {} + + /** + * @brief Quaternion-conjugated dual quaternion + * + * @f[ + * \hat q^* = q_0^* + q_\epsilon^* + * @f] + * @see dualConjugated(), conjugated(), Quaternion::conjugated() + */ + inline DualQuaternion quaternionConjugated() const { + return {this->real().conjugated(), this->dual().conjugated()}; + } + + /** + * @brief Dual-conjugated dual quaternion + * + * @f[ + * \overline{\hat q} = q_0 - \epsilon q_\epsilon + * @f] + * @see quaternionConjugated(), conjugated(), Dual::conjugated() + */ + inline DualQuaternion dualConjugated() const { + return Dual>::conjugated(); + } + + /** + * @brief Conjugated dual quaternion + * + * Both quaternion and dual conjugation. @f[ + * \overline{\hat q^*} = q_0^* - \epsilon q_\epsilon^* = q_0^* + \epsilon [\boldsymbol q_{\epsilon V}, -q_S] + * @f] + * @see quaternionConjugated(), dualConjugated(), Quaternion::conjugated(), + * Dual::conjugated() + */ + inline DualQuaternion conjugated() const { + return {this->real().conjugated(), {this->dual().vector(), -this->dual().scalar()}}; + } + + /** + * @brief %Dual quaternion norm + * + * @f[ + * ||\hat q|| = \sqrt{\hat q^* \hat q} = ||q_0|| + \epsilon \frac{q_0 \cdot q_\epsilon}{||q_0||} + * @f] + */ + inline Dual norm() const { + T norm = this->real().length(); + return {norm, Quaternion::dot(this->real(), this->dual())/norm}; + } + + /** + * @brief Inverted dual quaternion + * + * See invertedNormalized() which is faster for normalized + * dual quaternions. @f[ + * \hat q^{-1} = \frac{\hat q^*}{||\hat q||^2} + * @f] + */ + inline DualQuaternion inverted() const { + return quaternionConjugated()/Math::pow<2>(norm()); + } + + /** + * @brief Inverted normalized dual quaternion + * + * Equivalent to quaternionConjugated(). Expects that the quaternion is + * normalized. @f[ + * \hat q^{-1} = \frac{\hat q^*}{||\hat q||^2} = \hat q^* + * @f] + */ + inline DualQuaternion invertedNormalized() const { + CORRADE_ASSERT(MathTypeTraits::equals(norm(), T(1)), + "Math::DualQuaternion::invertedNormalized(): dual quaternion must be normalized", {}); + return quaternionConjugated(); + } + + MAGNUM_DUAL_SUBCLASS_IMPLEMENTATION(DualQuaternion, Quaternion) + + private: + /* Used by Dual operators and dualConjugated() */ + DualQuaternion(const Dual>& other): Dual>(other) {} +}; + +/** @debugoperator{Magnum::Math::DualQuaternion} */ +template Corrade::Utility::Debug operator<<(Corrade::Utility::Debug debug, const DualQuaternion& value) { + debug << "DualQuaternion({{"; + debug.setFlag(Corrade::Utility::Debug::SpaceAfterEachValue, false); + debug << value.real().vector().x() << ", " << value.real().vector().y() << ", " << value.real().vector().z() + << "}, " << value.real().scalar() << "}, {{" + << value.dual().vector().x() << ", " << value.dual().vector().y() << ", " << value.dual().vector().z() + << "}, " << value.dual().scalar() << "})"; + debug.setFlag(Corrade::Utility::Debug::SpaceAfterEachValue, true); + return debug; +} + +/* Explicit instantiation for commonly used types */ +#ifndef DOXYGEN_GENERATING_OUTPUT +extern template Corrade::Utility::Debug MAGNUM_EXPORT operator<<(Corrade::Utility::Debug, const DualQuaternion&); +#ifndef MAGNUM_TARGET_GLES +extern template Corrade::Utility::Debug MAGNUM_EXPORT operator<<(Corrade::Utility::Debug, const DualQuaternion&); +#endif +#endif + +}} + +#endif diff --git a/src/Math/Math.h b/src/Math/Math.h index 38e2a047f..6259f89c2 100644 --- a/src/Math/Math.h +++ b/src/Math/Math.h @@ -28,6 +28,7 @@ namespace Magnum { namespace Math { /* Class Constants used only statically */ template class Dual; +template class DualQuaternion; template class Matrix; template class Matrix3; diff --git a/src/Math/Quaternion.h b/src/Math/Quaternion.h index c1b12c700..47c4f3991 100644 --- a/src/Math/Quaternion.h +++ b/src/Math/Quaternion.h @@ -33,7 +33,7 @@ namespace Magnum { namespace Math { /** @brief %Quaternion -@see Magnum::Quaternion +@see DualQuaternion, Magnum::Quaternion */ template class Quaternion { public: diff --git a/src/Math/Test/CMakeLists.txt b/src/Math/Test/CMakeLists.txt index e7a739288..84cb4cbfb 100644 --- a/src/Math/Test/CMakeLists.txt +++ b/src/Math/Test/CMakeLists.txt @@ -19,5 +19,6 @@ corrade_add_test(MathSwizzleTest SwizzleTest.cpp LIBRARIES MagnumMathTestLib) 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) diff --git a/src/Math/Test/DualQuaternionTest.cpp b/src/Math/Test/DualQuaternionTest.cpp new file mode 100644 index 000000000..18d51109d --- /dev/null +++ b/src/Math/Test/DualQuaternionTest.cpp @@ -0,0 +1,114 @@ +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +#include +#include + +#include "Math/DualQuaternion.h" + +namespace Magnum { namespace Math { namespace Test { + +class DualQuaternionTest: public Corrade::TestSuite::Tester { + public: + explicit DualQuaternionTest(); + + void construct(); + void constructDefault(); + + void norm(); + + void quaternionConjugated(); + void dualConjugated(); + void conjugated(); + void inverted(); + + void debug(); +}; + +typedef Math::Dual Dual; +typedef Math::DualQuaternion DualQuaternion; +typedef Math::Quaternion Quaternion; +typedef Math::Vector3 Vector3; + +DualQuaternionTest::DualQuaternionTest() { + addTests(&DualQuaternionTest::construct, + &DualQuaternionTest::constructDefault, + + &DualQuaternionTest::norm, + + &DualQuaternionTest::quaternionConjugated, + &DualQuaternionTest::dualConjugated, + &DualQuaternionTest::conjugated, + &DualQuaternionTest::inverted, + + &DualQuaternionTest::debug); +} + +void DualQuaternionTest::construct() { + DualQuaternion q({{1.0f, 2.0f, 3.0f}, -4.0f}, {{0.5f, -3.1f, 3.3f}, 2.0f}); + CORRADE_COMPARE(q.real(), Quaternion({1.0f, 2.0f, 3.0f}, -4.0f)); + CORRADE_COMPARE(q.dual(), Quaternion({0.5f, -3.1f, 3.3f}, 2.0f)); +} + +void DualQuaternionTest::constructDefault() { + CORRADE_COMPARE(DualQuaternion(), DualQuaternion({{0.0f, 0.0f, 0.0f}, 1.0f}, {{0.0f, 0.0f, 0.0f}, 0.0f})); +} + +void DualQuaternionTest::norm() { + CORRADE_COMPARE(DualQuaternion().norm(), 1.0f); + + DualQuaternion a({{ 1.0f, 2.0f, 3.0f}, -4.0f}, {{ 0.5f, -3.0f, 3.0f}, 2.0f}); + CORRADE_COMPARE(a.norm(), Dual(5.477226f, -0.821584f)); +} + +void DualQuaternionTest::quaternionConjugated() { + DualQuaternion a({{ 1.0f, 2.0f, 3.0f}, -4.0f}, {{ 0.5f, -3.1f, 3.3f}, 2.0f}); + DualQuaternion b({{-1.0f, -2.0f, -3.0f}, -4.0f}, {{-0.5f, 3.1f, -3.3f}, 2.0f}); + + CORRADE_COMPARE(a.quaternionConjugated(), b); +} + +void DualQuaternionTest::dualConjugated() { + DualQuaternion a({{1.0f, 2.0f, 3.0f}, -4.0f}, {{ 0.5f, -3.1f, 3.3f}, 2.0f}); + DualQuaternion b({{1.0f, 2.0f, 3.0f}, -4.0f}, {{-0.5f, 3.1f, -3.3f}, -2.0f}); + + CORRADE_COMPARE(a.dualConjugated(), b); +} + +void DualQuaternionTest::conjugated() { + DualQuaternion a({{ 1.0f, 2.0f, 3.0f}, -4.0f}, {{ 0.5f, -3.1f, 3.3f}, 2.0f}); + DualQuaternion b({{-1.0f, -2.0f, -3.0f}, -4.0f}, {{ 0.5f, -3.1f, 3.3f}, -2.0f}); + + CORRADE_COMPARE(a.conjugated(), b); +} + +void DualQuaternionTest::inverted() { + DualQuaternion a({{ 1.0f, 2.0f, 3.0f}, -4.0f}, {{ 2.5f, -3.1f, 3.3f}, 2.0f}); + DualQuaternion b({{-0.033333f, -0.066667f, -0.1f}, -0.133333f}, {{-0.087333f, 0.095333f, -0.122f}, 0.050667f}); + + CORRADE_COMPARE(a*a.inverted(), DualQuaternion()); + CORRADE_COMPARE(a.inverted(), b); +} + +void DualQuaternionTest::debug() { + std::ostringstream o; + + Debug(&o) << DualQuaternion({{1.0f, 2.0f, 3.0f}, -4.0f}, {{0.5f, -3.1f, 3.3f}, 2.0f}); + CORRADE_COMPARE(o.str(), "DualQuaternion({{1, 2, 3}, -4}, {{0.5, -3.1, 3.3}, 2})\n"); +} + +}}} + +CORRADE_TEST_MAIN(Magnum::Math::Test::DualQuaternionTest)