From 90f5a006c4e02717c50e0850878d137925531c35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 25 Feb 2013 21:51:26 +0100 Subject: [PATCH] Math: reworked DualComplex to actually work. The tests now pass and it works similarly to transformation matrix multiplication/inversion in 2D, but it hasn't any connection to dual numbers anymore. --- src/Math/DualComplex.h | 83 ++++++++++++++++++++++++------- src/Math/Test/DualComplexTest.cpp | 44 +++++++++------- 2 files changed, 92 insertions(+), 35 deletions(-) diff --git a/src/Math/DualComplex.h b/src/Math/DualComplex.h index d03186a80..4cc1deea2 100644 --- a/src/Math/DualComplex.h +++ b/src/Math/DualComplex.h @@ -30,6 +30,8 @@ namespace Magnum { namespace Math { Represents 2D rotation and translation. @see Dual, Complex, Matrix3 +@todo Can this be done similarly as in dual quaternions? It sort of works, but + the math beneath is weird. */ template class DualComplex: public Dual> { public: @@ -107,7 +109,19 @@ template class DualComplex: public Dual> { * @see translation(const Vector2&) */ inline Vector2 translation() const { - return Vector2(this->dual()*this->real().conjugated()); + return Vector2(this->dual()); + } + + /** + * @brief Multipy with dual complex number + * + * @f[ + * \hat a \hat b = a_0 b_0 + \epsilon (a_0 b_\epsilon + a_\epsilon) + * @f] + * @todo can this be done similarly to dual quaternions? + */ + inline DualComplex operator*(const DualComplex& other) const { + return {this->real()*other.real(), this->real()*other.dual() + this->dual()}; } /** @@ -152,11 +166,12 @@ template class DualComplex: public Dual> { * * Should be used instead of length() for comparing complex number * length with other values, because it doesn't compute the square root. @f[ - * |\hat c|^2 = \sqrt{\hat c^* \hat c}^2 = c_0 \cdot c_0 + \epsilon 2 (c_0 \cdot c_\epsilon) + * |\hat c|^2 = c_0 \cdot c_0 = |c_0|^2 * @f] + * @todo Can this be done similarly to dual quaternins? */ - inline Dual lengthSquared() const { - return {this->real().dot(), T(2)*Complex::dot(this->real(), this->dual())}; + inline T lengthSquared() const { + return this->real().dot(); } /** @@ -164,16 +179,24 @@ template class DualComplex: public Dual> { * * See lengthSquared() which is faster for comparing length with other * values. @f[ - * |\hat c| = \sqrt{\hat{c^*} \hat c} = |c_0| + \epsilon \frac{c_0 \cdot c_\epsilon}{|c_0|} + * |\hat c| = \sqrt{c_0 \cdot c_0} = |c_0| * @f] + * @todo can this be done similarly to dual quaternions? */ - inline Dual length() const { - return Math::sqrt(lengthSquared()); + inline T length() const { + return this->real().length(); } - /** @brief Normalized dual complex number (of unit length) */ + /** + * @brief Normalized dual complex number (of unit length) + * + * @f[ + * c' = \frac{c_0}{|c_0|} + * @f] + * @todo can this be done similarly to dual quaternions? + */ inline DualComplex normalized() const { - return (*this)/length(); + return {this->real()/length(), this->dual()}; } /** @@ -181,33 +204,57 @@ template class DualComplex: public Dual> { * * See invertedNormalized() which is faster for normalized dual complex * numbers. @f[ - * \hat c^{-1} = \frac{\hat c^*}{|\hat c|^2} + * \hat c^{-1} = c_0^{-1} - \epsilon c_\epsilon * @f] + * @todo can this be done similarly to dual quaternions? */ inline DualComplex inverted() const { - return complexConjugated()/lengthSquared(); + return DualComplex(this->real().inverted(), {{}, {}})*DualComplex({}, -this->dual()); } /** * @brief Inverted normalized dual complex number * - * Equivalent to complexConjugated(). Expects that the complex number - * is normalized. @f[ - * \hat c^{-1} = \frac{\hat c^*}{|\hat c|^2} = \hat c^* + * Expects that the complex number is normalized. @f[ + * \hat c^{-1} = c_0^{-1} - \epsilon c_\epsilon = c_0^* - \epsilon c_\epsilon * @f] * @see inverted() + * @todo can this be done similarly to dual quaternions? */ inline DualComplex invertedNormalized() const { - CORRADE_ASSERT(lengthSquared() == Dual(1), - "Math::DualComplex::invertedNormalized(): dual complex number must be normalized", {}); - return complexConjugated(); + return DualComplex(this->real().invertedNormalized(), {{}, {}})*DualComplex({}, -this->dual()); } - MAGNUM_DUAL_SUBCLASS_IMPLEMENTATION(DualComplex, Complex) + /* Verbatim copy of DUAL_SUBCLASS_IMPLEMENTATION(), as we need to hide + Dual's operator*() and operator/() */ + #ifndef DOXYGEN_GENERATING_OUTPUT + inline DualComplex operator-() const { + return Dual>::operator-(); + } + inline DualComplex& operator+=(const Dual>& other) { + Dual>::operator+=(other); + return *this; + } + inline DualComplex operator+(const Dual>& other) const { + return Dual>::operator+(other); + } + inline DualComplex& operator-=(const Dual>& other) { + Dual>::operator-=(other); + return *this; + } + inline DualComplex operator-(const Dual>& other) const { + return Dual>::operator-(other); + } + #endif private: /* Used by Dual operators and dualConjugated() */ inline constexpr DualComplex(const Dual>& other): Dual>(other) {} + + /* Just to be sure nobody uses this, as it wouldn't probably work with + our operator*() */ + using Dual>::operator*; + using Dual>::operator/; }; /** @debugoperator{Magnum::Math::DualQuaternion} */ diff --git a/src/Math/Test/DualComplexTest.cpp b/src/Math/Test/DualComplexTest.cpp index c6f7dabbc..2a37f25c1 100644 --- a/src/Math/Test/DualComplexTest.cpp +++ b/src/Math/Test/DualComplexTest.cpp @@ -17,6 +17,7 @@ #include #include "Math/DualComplex.h" +#include "Math/DualQuaternion.h" namespace Magnum { namespace Math { namespace Test { @@ -29,6 +30,8 @@ class DualComplexTest: public Corrade::TestSuite::Tester { void constExpressions(); + void multiply(); + void lengthSquared(); void length(); void normalized(); @@ -59,6 +62,8 @@ DualComplexTest::DualComplexTest() { &DualComplexTest::constExpressions, + &DualComplexTest::multiply, + &DualComplexTest::lengthSquared, &DualComplexTest::length, &DualComplexTest::normalized, @@ -101,19 +106,25 @@ void DualComplexTest::constExpressions() { CORRADE_COMPARE(d, DualComplex({-1.0f, 2.5f}, {3.0f, -7.5f})); } +void DualComplexTest::multiply() { + DualComplex a({-1.5f, 2.0f}, { 3.0f, -6.5f}); + DualComplex b({ 2.0f, -7.5f}, {-0.5f, 1.0f});; + CORRADE_COMPARE(a*b, DualComplex({12.0f, 15.25f}, {1.75f, -9.0f})); +} + void DualComplexTest::lengthSquared() { DualComplex a({-1.0f, 3.0f}, {0.5f, -2.0f}); - CORRADE_COMPARE(a.lengthSquared(), Dual(10.0f, -13.0f)); + CORRADE_COMPARE(a.lengthSquared(), 10.0f); } void DualComplexTest::length() { DualComplex a({-1.0f, 3.0f}, {0.5f, -2.0f}); - CORRADE_COMPARE(a.length(), Dual(3.162278f, -2.05548f)); + CORRADE_COMPARE(a.length(), 3.162278f); } void DualComplexTest::normalized() { DualComplex a({-1.0f, 3.0f}, {0.5f, -2.0f}); - DualComplex b({-0.316228f, 0.948683f}, {-0.0474342f, -0.0158114f}); + DualComplex b({-0.316228f, 0.948683f}, {0.5f, -2.0f}); CORRADE_COMPARE(a.normalized().length(), 1.0f); CORRADE_COMPARE(a.normalized(), b); } @@ -137,27 +148,26 @@ void DualComplexTest::conjugated() { } void DualComplexTest::inverted() { - DualComplex a({-1.0f, 2.5f}, { 3.0f, -7.5f}); - DualComplex b({-1.0f, -2.5f}, { 3.0f, 7.5f}); - + DualComplex a({-1.0f, 1.5f}, {3.0f, -7.5f}); + DualComplex b({-0.307692f, -0.461538f}, {4.384616f, -0.923077f}); CORRADE_COMPARE(a*a.inverted(), DualComplex()); - CORRADE_COMPARE(a.inverted(), b/Dual(7.25f, -43.5f)); + CORRADE_COMPARE(a.inverted(), b); } void DualComplexTest::invertedNormalized() { - DualComplex a({-1.0f, 2.5f}, { 3.0f, -7.5f}); - DualComplex b({-1.0f, -2.5f}, { 3.0f, 7.5f}); + DualComplex a({-0.316228f, 0.9486831f}, { 3.0f, -2.5f}); + DualComplex b({-0.316228f, -0.9486831f}, {3.320391f, 2.05548f}); std::ostringstream o; Error::setOutput(&o); - CORRADE_COMPARE(a.invertedNormalized(), DualComplex()); - CORRADE_COMPARE(o.str(), "Math::DualComplex::invertedNormalized(): dual complex number must be normalized\n"); - - DualComplex normalized = a.normalized(); - DualComplex inverted = normalized.invertedNormalized(); - CORRADE_COMPARE(normalized*inverted, DualComplex()); - CORRADE_COMPARE(inverted*normalized, DualComplex()); - CORRADE_COMPARE(inverted, b/Math::sqrt(Dual(7.25f, -43.5f))); + DualComplex notInverted = DualComplex({-1.0f, -2.5f}, {}).invertedNormalized(); + CORRADE_VERIFY(notInverted != notInverted); + CORRADE_COMPARE(o.str(), "Math::Complex::invertedNormalized(): complex number must be normalized\n"); + + DualComplex inverted = a.invertedNormalized(); + CORRADE_COMPARE(a*inverted, DualComplex()); + CORRADE_COMPARE(inverted*a, DualComplex()); + CORRADE_COMPARE(inverted, b); } void DualComplexTest::rotation() {