Browse Source

Math: proper multiplication/division of Duals with different types.

Now works both ways. The base class works with virtually any combination
that is supported by the underlying types, so e.g. Dual<Matrix3<T>>
could be multiplied/divided with Vector3<T> (result is Vector3<T>), with
Matrix3<T> (result is Matrix3<T>) or with T (result is Matrix3<T>).

The macros, on the other hand, because they are there only to help with
implementation of *my* subclasses, restrict that to the two only cases I
need (i.e. multiplication with Dual<T> and Dual<T::Type> and nothing
else). Could be extended in the future if it needs to be.
pull/114/merge
Vladimír Vondruš 11 years ago
parent
commit
62203ae891
  1. 27
      src/Magnum/Math/Dual.h
  2. 10
      src/Magnum/Math/DualComplex.h
  3. 4
      src/Magnum/Math/DualQuaternion.h
  4. 16
      src/Magnum/Math/Test/DualTest.cpp

27
src/Magnum/Math/Dual.h

@ -153,7 +153,7 @@ template<class T> class Dual {
* \hat a \hat b = a_0 b_0 + \epsilon (a_0 b_\epsilon + a_\epsilon b_0)
* @f]
*/
template<class U> Dual<T> operator*(const Dual<U>& other) const {
template<class U> auto operator*(const Dual<U>& other) const -> Dual<decltype(real()*other.real())> {
return {_real*other._real, _real*other._dual + _dual*other._real};
}
@ -164,7 +164,7 @@ template<class T> class Dual {
* \frac{\hat a}{\hat b} = \frac{a_0}{b_0} + \epsilon \frac{a_\epsilon b_0 - a_0 b_\epsilon}{b_0^2}
* @f]
*/
template<class U> Dual<T> operator/(const Dual<U>& other) const {
template<class U> auto operator/(const Dual<U>& other) const -> Dual<decltype(real()/other.real())> {
return {_real/other._real, (_dual*other._real - _real*other._dual)/(other._real*other._real)};
}
@ -184,7 +184,7 @@ template<class T> class Dual {
};
#ifndef DOXYGEN_GENERATING_OUTPUT
#define MAGNUM_DUAL_SUBCLASS_IMPLEMENTATION(Type, Underlying) \
#define MAGNUM_DUAL_SUBCLASS_IMPLEMENTATION(Type, Underlying, Multiplicable) \
Type<T> operator-() const { \
return Math::Dual<Underlying<T>>::operator-(); \
} \
@ -201,7 +201,14 @@ template<class T> class Dual {
} \
Type<T> operator-(const Math::Dual<Underlying<T>>& other) const { \
return Math::Dual<Underlying<T>>::operator-(other); \
} \
Type<T> operator*(const Math::Dual<Multiplicable>& other) const { \
return Math::Dual<Underlying<T>>::operator*(other); \
} \
Type<T> operator/(const Math::Dual<Multiplicable>& other) const { \
return Math::Dual<Underlying<T>>::operator/(other); \
}
/* DualComplex needs its own special implementation of multiplication/division */
#define MAGNUM_DUAL_SUBCLASS_MULTIPLICATION_IMPLEMENTATION(Type, Underlying) \
template<class U> Type<T> operator*(const Math::Dual<U>& other) const { \
@ -209,6 +216,20 @@ template<class T> class Dual {
} \
template<class U> Type<T> operator/(const Math::Dual<U>& other) const { \
return Math::Dual<Underlying<T>>::operator/(other); \
} \
Type<T> operator*(const Math::Dual<Underlying<T>>& other) const { \
return Math::Dual<Underlying<T>>::operator*(other); \
} \
Type<T> operator/(const Math::Dual<Underlying<T>>& other) const { \
return Math::Dual<Underlying<T>>::operator/(other); \
}
#define MAGNUM_DUAL_OPERATOR_IMPLEMENTATION(Type, Underlying, Multiplicable) \
template<class T> inline Type<T> operator*(const Math::Dual<Multiplicable>& a, const Type<T>& b) { \
return a*static_cast<const Math::Dual<Underlying<T>>&>(b); \
} \
template<class T> inline Type<T> operator/(const Math::Dual<Multiplicable>& a, const Type<T>& b) { \
return a/static_cast<const Math::Dual<Underlying<T>>&>(b); \
}
#endif

10
src/Magnum/Math/DualComplex.h

@ -339,17 +339,13 @@ template<class T> class DualComplex: public Dual<Complex<T>> {
return Vector2<T>(((*this)*DualComplex<T>(vector)).dual());
}
MAGNUM_DUAL_SUBCLASS_IMPLEMENTATION(DualComplex, Vector2)
MAGNUM_DUAL_SUBCLASS_IMPLEMENTATION(DualComplex, Vector2, T)
/* Not using MAGNUM_DUAL_SUBCLASS_MULTIPLICATION_IMPLEMENTATION(), as
we have special multiplication/division implementation */
private:
/* 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/;
};
MAGNUM_DUAL_OPERATOR_IMPLEMENTATION(DualComplex, Vector2, T)
/** @debugoperator{Magnum::Math::DualQuaternion} */
template<class T> Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug& debug, const DualComplex<T>& value) {
return debug << "DualComplex({" << Corrade::Utility::Debug::nospace

4
src/Magnum/Math/DualQuaternion.h

@ -354,10 +354,12 @@ template<class T> class DualQuaternion: public Dual<Quaternion<T>> {
return ((*this)*DualQuaternion<T>(vector)*conjugated()).dual().vector();
}
MAGNUM_DUAL_SUBCLASS_IMPLEMENTATION(DualQuaternion, Quaternion)
MAGNUM_DUAL_SUBCLASS_IMPLEMENTATION(DualQuaternion, Quaternion, T)
MAGNUM_DUAL_SUBCLASS_MULTIPLICATION_IMPLEMENTATION(DualQuaternion, Quaternion)
};
MAGNUM_DUAL_OPERATOR_IMPLEMENTATION(DualQuaternion, Quaternion, T)
/** @debugoperator{Magnum::Math::DualQuaternion} */
template<class T> Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug& debug, const DualQuaternion<T>& value) {
return debug << "DualQuaternion({{" << Corrade::Utility::Debug::nospace

16
src/Magnum/Math/Test/DualTest.cpp

@ -150,9 +150,9 @@ void DualTest::multiplyDivideDifferentType() {
DualVector2 d{{-2.0f/1.5f, -1.0f}, {-7.25f/2.25f, 3.6f/4.0f}};
CORRADE_COMPARE(a*b, c);
//CORRADE_COMPARE(b*a, c); does not compile yet
CORRADE_COMPARE(b*a, c);
CORRADE_COMPARE(c/b, a);
//CORRADE_COMPARE(b/a, d); does not compile yet
CORRADE_COMPARE(b/a, d);
}
void DualTest::conjugated() {
@ -170,10 +170,12 @@ template<class T> class BasicDualVec2: public Math::Dual<Math::Vector2<T>> {
/* MSVC 2015 can't handle {} here */
template<class ...U> constexpr BasicDualVec2(U&&... args): Math::Dual<Math::Vector2<T>>(args...) {}
MAGNUM_DUAL_SUBCLASS_IMPLEMENTATION(BasicDualVec2, Math::Vector2)
MAGNUM_DUAL_SUBCLASS_IMPLEMENTATION(BasicDualVec2, Math::Vector2, T)
MAGNUM_DUAL_SUBCLASS_MULTIPLICATION_IMPLEMENTATION(BasicDualVec2, Math::Vector2)
};
MAGNUM_DUAL_OPERATOR_IMPLEMENTATION(BasicDualVec2, Math::Vector2, T)
typedef BasicDualVec2<Float> DualVec2;
}
@ -192,9 +194,9 @@ void DualTest::subclassTypes() {
const Dual c;
CORRADE_VERIFY((std::is_same<decltype(a*c), DualVec2>::value));
//CORRADE_VERIFY((std::is_same<decltype(c*a), DualVec2>::value)); does not compile yet
CORRADE_VERIFY((std::is_same<decltype(c*a), DualVec2>::value));
CORRADE_VERIFY((std::is_same<decltype(a/c), DualVec2>::value));
//CORRADE_VERIFY((std::is_same<decltype(c/a), DualVec2>::value)); does not compile yet
CORRADE_VERIFY((std::is_same<decltype(c/a), DualVec2>::value));
}
void DualTest::subclass() {
@ -216,9 +218,9 @@ void DualTest::subclass() {
const DualVec2 f{Vector2{-3.0f, -4.0f}, Vector2{8.75f, -1.6f}};
const DualVec2 g{Vector2{-2.0f/1.5f, -1.0f}, Vector2{-7.25f/2.25f, 3.6f/4.0f}};
CORRADE_COMPARE(a*e, f);
//CORRADE_COMPARE(e*a, f); does not compile yet
CORRADE_COMPARE(e*a, f);
CORRADE_COMPARE(f/e, a);
//CORRADE_COMPARE(e/a, g); does not compile yet
CORRADE_COMPARE(e/a, g);
}
void DualTest::debug() {

Loading…
Cancel
Save