Browse Source

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.
pull/278/head
Vladimír Vondruš 13 years ago
parent
commit
90f5a006c4
  1. 83
      src/Math/DualComplex.h
  2. 44
      src/Math/Test/DualComplexTest.cpp

83
src/Math/DualComplex.h

@ -30,6 +30,8 @@ namespace Magnum { namespace Math {
Represents 2D rotation and translation. Represents 2D rotation and translation.
@see Dual, Complex, Matrix3 @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 T> class DualComplex: public Dual<Complex<T>> { template<class T> class DualComplex: public Dual<Complex<T>> {
public: public:
@ -107,7 +109,19 @@ template<class T> class DualComplex: public Dual<Complex<T>> {
* @see translation(const Vector2&) * @see translation(const Vector2&)
*/ */
inline Vector2<T> translation() const { inline Vector2<T> translation() const {
return Vector2<T>(this->dual()*this->real().conjugated()); return Vector2<T>(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<T> operator*(const DualComplex<T>& other) const {
return {this->real()*other.real(), this->real()*other.dual() + this->dual()};
} }
/** /**
@ -152,11 +166,12 @@ template<class T> class DualComplex: public Dual<Complex<T>> {
* *
* Should be used instead of length() for comparing complex number * Should be used instead of length() for comparing complex number
* length with other values, because it doesn't compute the square root. @f[ * 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] * @f]
* @todo Can this be done similarly to dual quaternins?
*/ */
inline Dual<T> lengthSquared() const { inline T lengthSquared() const {
return {this->real().dot(), T(2)*Complex<T>::dot(this->real(), this->dual())}; return this->real().dot();
} }
/** /**
@ -164,16 +179,24 @@ template<class T> class DualComplex: public Dual<Complex<T>> {
* *
* See lengthSquared() which is faster for comparing length with other * See lengthSquared() which is faster for comparing length with other
* values. @f[ * 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] * @f]
* @todo can this be done similarly to dual quaternions?
*/ */
inline Dual<T> length() const { inline T length() const {
return Math::sqrt(lengthSquared()); 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<T> normalized() const { inline DualComplex<T> normalized() const {
return (*this)/length(); return {this->real()/length(), this->dual()};
} }
/** /**
@ -181,33 +204,57 @@ template<class T> class DualComplex: public Dual<Complex<T>> {
* *
* See invertedNormalized() which is faster for normalized dual complex * See invertedNormalized() which is faster for normalized dual complex
* numbers. @f[ * numbers. @f[
* \hat c^{-1} = \frac{\hat c^*}{|\hat c|^2} * \hat c^{-1} = c_0^{-1} - \epsilon c_\epsilon
* @f] * @f]
* @todo can this be done similarly to dual quaternions?
*/ */
inline DualComplex<T> inverted() const { inline DualComplex<T> inverted() const {
return complexConjugated()/lengthSquared(); return DualComplex<T>(this->real().inverted(), {{}, {}})*DualComplex<T>({}, -this->dual());
} }
/** /**
* @brief Inverted normalized dual complex number * @brief Inverted normalized dual complex number
* *
* Equivalent to complexConjugated(). Expects that the complex number * Expects that the complex number is normalized. @f[
* is normalized. @f[ * \hat c^{-1} = c_0^{-1} - \epsilon c_\epsilon = c_0^* - \epsilon c_\epsilon
* \hat c^{-1} = \frac{\hat c^*}{|\hat c|^2} = \hat c^*
* @f] * @f]
* @see inverted() * @see inverted()
* @todo can this be done similarly to dual quaternions?
*/ */
inline DualComplex<T> invertedNormalized() const { inline DualComplex<T> invertedNormalized() const {
CORRADE_ASSERT(lengthSquared() == Dual<T>(1), return DualComplex<T>(this->real().invertedNormalized(), {{}, {}})*DualComplex<T>({}, -this->dual());
"Math::DualComplex::invertedNormalized(): dual complex number must be normalized", {});
return complexConjugated();
} }
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<T> operator-() const {
return Dual<Complex<T>>::operator-();
}
inline DualComplex<T>& operator+=(const Dual<Complex<T>>& other) {
Dual<Complex<T>>::operator+=(other);
return *this;
}
inline DualComplex<T> operator+(const Dual<Complex<T>>& other) const {
return Dual<Complex<T>>::operator+(other);
}
inline DualComplex<T>& operator-=(const Dual<Complex<T>>& other) {
Dual<Complex<T>>::operator-=(other);
return *this;
}
inline DualComplex<T> operator-(const Dual<Complex<T>>& other) const {
return Dual<Complex<T>>::operator-(other);
}
#endif
private: private:
/* Used by Dual operators and dualConjugated() */ /* Used by Dual operators and dualConjugated() */
inline constexpr DualComplex(const Dual<Complex<T>>& other): Dual<Complex<T>>(other) {} inline constexpr DualComplex(const Dual<Complex<T>>& other): Dual<Complex<T>>(other) {}
/* Just to be sure nobody uses this, as it wouldn't probably work with
our operator*() */
using Dual<Complex<T>>::operator*;
using Dual<Complex<T>>::operator/;
}; };
/** @debugoperator{Magnum::Math::DualQuaternion} */ /** @debugoperator{Magnum::Math::DualQuaternion} */

44
src/Math/Test/DualComplexTest.cpp

@ -17,6 +17,7 @@
#include <TestSuite/Tester.h> #include <TestSuite/Tester.h>
#include "Math/DualComplex.h" #include "Math/DualComplex.h"
#include "Math/DualQuaternion.h"
namespace Magnum { namespace Math { namespace Test { namespace Magnum { namespace Math { namespace Test {
@ -29,6 +30,8 @@ class DualComplexTest: public Corrade::TestSuite::Tester {
void constExpressions(); void constExpressions();
void multiply();
void lengthSquared(); void lengthSquared();
void length(); void length();
void normalized(); void normalized();
@ -59,6 +62,8 @@ DualComplexTest::DualComplexTest() {
&DualComplexTest::constExpressions, &DualComplexTest::constExpressions,
&DualComplexTest::multiply,
&DualComplexTest::lengthSquared, &DualComplexTest::lengthSquared,
&DualComplexTest::length, &DualComplexTest::length,
&DualComplexTest::normalized, &DualComplexTest::normalized,
@ -101,19 +106,25 @@ void DualComplexTest::constExpressions() {
CORRADE_COMPARE(d, DualComplex({-1.0f, 2.5f}, {3.0f, -7.5f})); 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() { void DualComplexTest::lengthSquared() {
DualComplex a({-1.0f, 3.0f}, {0.5f, -2.0f}); 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() { void DualComplexTest::length() {
DualComplex a({-1.0f, 3.0f}, {0.5f, -2.0f}); 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() { void DualComplexTest::normalized() {
DualComplex a({-1.0f, 3.0f}, {0.5f, -2.0f}); 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().length(), 1.0f);
CORRADE_COMPARE(a.normalized(), b); CORRADE_COMPARE(a.normalized(), b);
} }
@ -137,27 +148,26 @@ void DualComplexTest::conjugated() {
} }
void DualComplexTest::inverted() { void DualComplexTest::inverted() {
DualComplex a({-1.0f, 2.5f}, { 3.0f, -7.5f}); DualComplex a({-1.0f, 1.5f}, {3.0f, -7.5f});
DualComplex b({-1.0f, -2.5f}, { 3.0f, 7.5f}); DualComplex b({-0.307692f, -0.461538f}, {4.384616f, -0.923077f});
CORRADE_COMPARE(a*a.inverted(), DualComplex()); CORRADE_COMPARE(a*a.inverted(), DualComplex());
CORRADE_COMPARE(a.inverted(), b/Dual(7.25f, -43.5f)); CORRADE_COMPARE(a.inverted(), b);
} }
void DualComplexTest::invertedNormalized() { void DualComplexTest::invertedNormalized() {
DualComplex a({-1.0f, 2.5f}, { 3.0f, -7.5f}); DualComplex a({-0.316228f, 0.9486831f}, { 3.0f, -2.5f});
DualComplex b({-1.0f, -2.5f}, { 3.0f, 7.5f}); DualComplex b({-0.316228f, -0.9486831f}, {3.320391f, 2.05548f});
std::ostringstream o; std::ostringstream o;
Error::setOutput(&o); Error::setOutput(&o);
CORRADE_COMPARE(a.invertedNormalized(), DualComplex()); DualComplex notInverted = DualComplex({-1.0f, -2.5f}, {}).invertedNormalized();
CORRADE_COMPARE(o.str(), "Math::DualComplex::invertedNormalized(): dual complex number must be normalized\n"); CORRADE_VERIFY(notInverted != notInverted);
CORRADE_COMPARE(o.str(), "Math::Complex::invertedNormalized(): complex number must be normalized\n");
DualComplex normalized = a.normalized();
DualComplex inverted = normalized.invertedNormalized(); DualComplex inverted = a.invertedNormalized();
CORRADE_COMPARE(normalized*inverted, DualComplex()); CORRADE_COMPARE(a*inverted, DualComplex());
CORRADE_COMPARE(inverted*normalized, DualComplex()); CORRADE_COMPARE(inverted*a, DualComplex());
CORRADE_COMPARE(inverted, b/Math::sqrt(Dual(7.25f, -43.5f))); CORRADE_COMPARE(inverted, b);
} }
void DualComplexTest::rotation() { void DualComplexTest::rotation() {

Loading…
Cancel
Save