From 89c2bf913cd97ba10654fccd9889086323ecd6ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 5 Jan 2013 21:47:41 +0100 Subject: [PATCH] Math: initial implementation of Quaternion class. Still practically unusable. --- src/CMakeLists.txt | 1 + src/Math/CMakeLists.txt | 1 + src/Math/Quaternion.cpp | 27 +++++ src/Math/Quaternion.h | 168 +++++++++++++++++++++++++++ src/Math/Test/CMakeLists.txt | 2 + src/Math/Test/MathQuaternionTest.cpp | 105 +++++++++++++++++ 6 files changed, 304 insertions(+) create mode 100644 src/Math/Quaternion.cpp create mode 100644 src/Math/Quaternion.h create mode 100644 src/Math/Test/MathQuaternionTest.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8ae6ea8fa..48cbbc1b7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -103,6 +103,7 @@ add_library(MagnumObjects OBJECT ${Magnum_SRCS}) # Files shared between main library and math unit test library set(MagnumMath_SRCS Math/Math.cpp + Math/Quaternion.cpp Math/RectangularMatrix.cpp Math/Vector.cpp) add_library(MagnumMathObjects OBJECT ${MagnumMath_SRCS}) diff --git a/src/Math/CMakeLists.txt b/src/Math/CMakeLists.txt index ef962ab43..4e4505fee 100644 --- a/src/Math/CMakeLists.txt +++ b/src/Math/CMakeLists.txt @@ -9,6 +9,7 @@ set(MagnumMath_HEADERS Matrix4.h Point2D.h Point3D.h + Quaternion.h RectangularMatrix.h Swizzle.h Vector.h diff --git a/src/Math/Quaternion.cpp b/src/Math/Quaternion.cpp new file mode 100644 index 000000000..80d01c33b --- /dev/null +++ b/src/Math/Quaternion.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 "Quaternion.h" + +namespace Magnum { namespace Math { + +#ifndef DOXYGEN_GENERATING_OUTPUT +template Corrade::Utility::Debug operator<<(Corrade::Utility::Debug, const Magnum::Math::Quaternion&); +#ifndef MAGNUM_TARGET_GLES +template Corrade::Utility::Debug operator<<(Corrade::Utility::Debug, const Magnum::Math::Quaternion&); +#endif +#endif + +}} diff --git a/src/Math/Quaternion.h b/src/Math/Quaternion.h new file mode 100644 index 000000000..dac5f1a39 --- /dev/null +++ b/src/Math/Quaternion.h @@ -0,0 +1,168 @@ +#ifndef Magnum_Math_Quaternion_h +#define Magnum_Math_Quaternion_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::Quaternion + */ + +#include +#include + +#include "Vector3.h" +#include "MathTypeTraits.h" + +namespace Magnum { namespace Math { + +/** @brief %Quaternion */ +template class Quaternion { + public: + /** @brief Default constructor */ + inline constexpr Quaternion(): _scalar(T(1)) {} + + /** @brief Create quaternion from vector and scalar */ + inline constexpr Quaternion(const Vector3& vector, T scalar): _vector(vector), _scalar(scalar) {} + + /** @brief Equality comparison */ + inline bool operator==(const Quaternion& other) const { + return _vector == other._vector && MathTypeTraits::equals(_scalar, other._scalar); + } + + /** @brief Non-equality comparison */ + inline bool operator!=(const Quaternion& other) const { + return !operator==(other); + } + + /** @brief %Vector part */ + inline constexpr Vector3 vector() const { return _vector; } + + /** @brief %Scalar part */ + inline constexpr T scalar() const { return _scalar; } + + /** + * @brief Multiply with scalar and assign + * + * The computation is done in-place. + */ + inline Quaternion& operator*=(T number) { + _vector *= number; + _scalar *= number; + return *this; + } + + /** + * @brief Divide with scalar and assign + * + * The computation is done in-place. + */ + inline Quaternion& operator/=(T number) { + _vector /= number; + _scalar /= number; + return *this; + } + + /** + * @brief Multiply with scalar + * + * @see operator*=(T) + */ + inline constexpr Quaternion operator*(T scalar) const { + return Quaternion(*this)*=scalar; + } + + /** + * @brief Divide with scalar + * + * @see operator/=(T) + */ + inline constexpr Quaternion operator/(T scalar) const { + return Quaternion(*this)/=scalar; + } + + /** + * @brief Multiply with quaternion + * + * The computation is *not* done in-place. + */ + inline constexpr Quaternion operator*(const Quaternion& other) const { + return {_scalar*other._vector + other._scalar*_vector + Vector3::cross(_vector, other._vector), + _scalar*other._scalar - Vector3::dot(_vector, other._vector)}; + } + + /** + * @brief Multiply with quaternion and assign + * + * @see operator*(const Quaternion&) const + */ + inline Quaternion& operator*=(const Quaternion& other) { + return (*this = *this * other); + } + + /** @brief %Quaternion length squared */ + inline constexpr T lengthSquared() const { + return _vector.dot() + _scalar*_scalar; + } + + /** @brief %Quaternion length */ + inline constexpr T length() const { + return std::sqrt(lengthSquared()); + } + + /** @brief Normalized quaternion */ + inline constexpr Quaternion normalized() const { + return (*this)/length(); + } + + /** @brief Conjugated quaternion */ + inline constexpr Quaternion conjugated() const { + return {-_vector, _scalar}; + } + + /** + * @brief Inverted quaternion + * + * If the quaternion is already normalized, this function is + * equivalent to conjugated(). + */ + inline constexpr Quaternion inverted() const { + return conjugated()/lengthSquared(); + } + + private: + Vector3 _vector; + T _scalar; +}; + +/** @debugoperator{Magnum::Math::Geometry::Rectangle} */ +template Corrade::Utility::Debug operator<<(Corrade::Utility::Debug debug, const Quaternion& value) { + debug << "Quaternion({"; + debug.setFlag(Corrade::Utility::Debug::SpaceAfterEachValue, false); + debug << value.vector().x() << ", " << value.vector().y() << ", " << value.vector().z() << "}, " << value.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 Quaternion&); +#ifndef MAGNUM_TARGET_GLES +extern template Corrade::Utility::Debug MAGNUM_EXPORT operator<<(Corrade::Utility::Debug, const Quaternion&); +#endif +#endif + +}} + +#endif diff --git a/src/Math/Test/CMakeLists.txt b/src/Math/Test/CMakeLists.txt index e613855b9..c193b8335 100644 --- a/src/Math/Test/CMakeLists.txt +++ b/src/Math/Test/CMakeLists.txt @@ -18,4 +18,6 @@ corrade_add_test(MathMatrix4Test Matrix4Test.cpp LIBRARIES MagnumMathTestLib) corrade_add_test(MathSwizzleTest SwizzleTest.cpp LIBRARIES MagnumMathTestLib) +corrade_add_test(MathQuaternionTest MathQuaternionTest.cpp LIBRARIES MagnumMathTestLib) + set_target_properties(MathVectorTest MathMatrix3Test MathMatrix4Test PROPERTIES COMPILE_FLAGS -DCORRADE_GRACEFUL_ASSERT) diff --git a/src/Math/Test/MathQuaternionTest.cpp b/src/Math/Test/MathQuaternionTest.cpp new file mode 100644 index 000000000..362ee3825 --- /dev/null +++ b/src/Math/Test/MathQuaternionTest.cpp @@ -0,0 +1,105 @@ +/* + 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/Quaternion.h" + +namespace Magnum { namespace Math { namespace Test { + +class QuaternionTest: public Corrade::TestSuite::Tester { + public: + explicit QuaternionTest(); + + void construct(); + void multiplyDivideScalar(); + void multiply(); + void length(); + void normalized(); + void conjugated(); + void inverted(); + + void debug(); +}; + +typedef Math::Quaternion Quaternion; +typedef Math::Vector3 Vector3; + +QuaternionTest::QuaternionTest() { + addTests(&QuaternionTest::construct, + &QuaternionTest::multiplyDivideScalar, + &QuaternionTest::multiply, + &QuaternionTest::length, + &QuaternionTest::normalized, + &QuaternionTest::conjugated, + &QuaternionTest::inverted, + &QuaternionTest::debug); +} + +void QuaternionTest::construct() { + Quaternion q({1.0f, 2.0f, 3.0f}, -4.0f); + CORRADE_COMPARE(q.vector(), Vector3(1.0f, 2.0f, 3.0f)); + CORRADE_COMPARE(q.scalar(), -4.0f); + + CORRADE_COMPARE(Quaternion(), Quaternion({0.0f, 0.0f, 0.0f}, {1.0f})); +} + +void QuaternionTest::multiplyDivideScalar() { + Quaternion a({1.0f, 3.0f, -2.0f}, -4.0f); + Quaternion b({-1.5f, -4.5f, 3.0f}, 6.0f); + + CORRADE_COMPARE(a*-1.5f, b); + CORRADE_COMPARE(b/-1.5f, a); +} + +void QuaternionTest::multiply() { + CORRADE_COMPARE(Quaternion({-6.0f, -9.0f, 15.0f}, 0.5f)*Quaternion({2.0f, 3.0f, -5.0f}, 2.0f), + Quaternion({-11.0f, -16.5f, 27.5f}, 115.0f)); +} + +void QuaternionTest::length() { + CORRADE_COMPARE(Quaternion({1.0f, 3.0f, -2.0f}, -4.0f).length(), std::sqrt(30.0f)); +} + +void QuaternionTest::normalized() { + CORRADE_COMPARE(Quaternion({1.0f, 3.0f, -2.0f}, -4.0f).normalized(), + Quaternion({1.0f, 3.0f, -2.0f}, -4.0f)/std::sqrt(30.0f)); +} + +void QuaternionTest::conjugated() { + CORRADE_COMPARE(Quaternion({1.0f, 3.0f, -2.0f}, -4.0f).conjugated(), + Quaternion({-1.0f, -3.0f, 2.0f}, -4.0f)); +} + +void QuaternionTest::inverted() { + Quaternion a = Quaternion({1.0f, 3.0f, -2.0f}, -4.0f); + Quaternion inverted = a.inverted(); + + CORRADE_COMPARE(a*inverted, Quaternion()); + CORRADE_COMPARE(inverted*a, Quaternion()); + CORRADE_COMPARE(inverted, Quaternion({-1.0f, -3.0f, 2.0f}, -4.0f)/30.0f); +} + +void QuaternionTest::debug() { + std::ostringstream o; + + Corrade::Utility::Debug(&o) << Quaternion({1.0f, 2.0f, 3.0f}, -4.0f); + CORRADE_COMPARE(o.str(), "Quaternion({1, 2, 3}, -4)\n"); +} + +}}} + +CORRADE_TEST_MAIN(Magnum::Math::Test::QuaternionTest)