diff --git a/src/Math/Complex.h b/src/Math/Complex.h index 923d39c72..93122b74b 100644 --- a/src/Math/Complex.h +++ b/src/Math/Complex.h @@ -23,6 +23,7 @@ #include #include +#include "Math/Angle.h" #include "Math/MathTypeTraits.h" #include "magnumVisibility.h" @@ -51,6 +52,20 @@ template class Complex { return a*b.conjugated(); } + /** + * @brief Angle between normalized complex numbers + * + * Expects that both complex numbers are normalized. @f[ + * \theta = acos \left( \frac{Re(c_0 \cdot c_1))}{|c_0| |c_1|} \right) = acos (a_0 a_1 + b_0 b_1) + * @f] + * @see Quaternion::angle(), Vector::angle() + */ + inline static Rad angle(const Complex& normalizedA, const Complex& normalizedB) { + CORRADE_ASSERT(MathTypeTraits::equals(normalizedA.dot(), T(1)) && MathTypeTraits::equals(normalizedB.dot(), T(1)), + "Math::Complex::angle(): complex numbers must be normalized", Rad(std::numeric_limits::quiet_NaN())); + return Rad(std::acos(normalizedA._real*normalizedB._real + normalizedA._imaginary*normalizedB._imaginary)); + } + /** * @brief Default constructor * diff --git a/src/Math/Quaternion.h b/src/Math/Quaternion.h index 0c284a8ac..5edfee668 100644 --- a/src/Math/Quaternion.h +++ b/src/Math/Quaternion.h @@ -59,6 +59,7 @@ template class Quaternion { * Expects that both quaternions are normalized. @f[ * \theta = acos \left( \frac{p \cdot q}{|p| |q|} \right) = acos(p \cdot q) * @f] + * @see Complex::angle(), Vector::angle() */ inline static Rad angle(const Quaternion& normalizedA, const Quaternion& normalizedB) { CORRADE_ASSERT(MathTypeTraits::equals(normalizedA.dot(), T(1)) && MathTypeTraits::equals(normalizedB.dot(), T(1)), diff --git a/src/Math/Test/ComplexTest.cpp b/src/Math/Test/ComplexTest.cpp index 8b0f1ec7a..f448f2003 100644 --- a/src/Math/Test/ComplexTest.cpp +++ b/src/Math/Test/ComplexTest.cpp @@ -17,6 +17,7 @@ #include #include "Math/Complex.h" +#include "Math/Vector2.h" namespace Magnum { namespace Math { namespace Test { @@ -44,6 +45,8 @@ class ComplexTest: public Corrade::TestSuite::Tester { void inverted(); void invertedNormalized(); + void angle(); + void debug(); }; @@ -68,10 +71,14 @@ ComplexTest::ComplexTest() { &ComplexTest::inverted, &ComplexTest::invertedNormalized, + &ComplexTest::angle, + &ComplexTest::debug); } +typedef Math::Rad Rad; typedef Math::Complex Complex; +typedef Math::Vector2 Vector2; void ComplexTest::construct() { Complex c(0.5f, -3.7f); @@ -201,6 +208,26 @@ void ComplexTest::invertedNormalized() { CORRADE_COMPARE(inverted, b); } +void ComplexTest::angle() { + std::ostringstream o; + Error::setOutput(&o); + auto angle = Complex::angle(Complex(1.5f, -2.0f).normalized(), {-4.0f, 3.5f}); + CORRADE_VERIFY(angle != angle); + CORRADE_COMPARE(o.str(), "Math::Complex::angle(): complex numbers must be normalized\n"); + + o.str({}); + angle = Complex::angle({1.5f, -2.0f}, Complex(-4.0f, 3.5f).normalized()); + CORRADE_VERIFY(angle != angle); + CORRADE_COMPARE(o.str(), "Math::Complex::angle(): complex numbers must be normalized\n"); + + /* Verify also that the angle is the same as angle between 2D vectors */ + angle = Complex::angle(Complex( 1.5f, -2.0f).normalized(), + Complex(-4.0f, 3.5f).normalized()); + CORRADE_COMPARE(angle, Vector2::angle(Vector2( 1.5f, -2.0f).normalized(), + Vector2(-4.0f, 3.5f).normalized())); + CORRADE_COMPARE(angle, Rad(2.933128f)); +} + void ComplexTest::debug() { std::ostringstream o; diff --git a/src/Math/Test/QuaternionTest.cpp b/src/Math/Test/QuaternionTest.cpp index 6b2e357ae..3b6ad4955 100644 --- a/src/Math/Test/QuaternionTest.cpp +++ b/src/Math/Test/QuaternionTest.cpp @@ -253,7 +253,7 @@ void QuaternionTest::rotation() { void QuaternionTest::angle() { std::ostringstream o; - Corrade::Utility::Error::setOutput(&o); + Error::setOutput(&o); auto angle = Quaternion::angle(Quaternion({1.0f, 2.0f, -3.0f}, -4.0f).normalized(), {{4.0f, -3.0f, 2.0f}, -1.0f}); CORRADE_VERIFY(angle != angle); CORRADE_COMPARE(o.str(), "Math::Quaternion::angle(): quaternions must be normalized\n"); @@ -263,9 +263,12 @@ void QuaternionTest::angle() { CORRADE_VERIFY(angle != angle); CORRADE_COMPARE(o.str(), "Math::Quaternion::angle(): quaternions must be normalized\n"); - CORRADE_COMPARE(Quaternion::angle(Quaternion({1.0f, 2.0f, -3.0f}, -4.0f).normalized(), - Quaternion({4.0f, -3.0f, 2.0f}, -1.0f).normalized()), - Rad(1.704528f)); + /* Verify also that the angle is the same as angle between 4D vectors */ + angle = Quaternion::angle(Quaternion({1.0f, 2.0f, -3.0f}, -4.0f).normalized(), + Quaternion({4.0f, -3.0f, 2.0f}, -1.0f).normalized()); + CORRADE_COMPARE(angle, Vector4::angle(Vector4(1.0f, 2.0f, -3.0f, -4.0f).normalized(), + Vector4(4.0f, -3.0f, 2.0f, -1.0f).normalized())); + CORRADE_COMPARE(angle, Rad(1.704528f)); } void QuaternionTest::matrix() { diff --git a/src/Math/Vector.h b/src/Math/Vector.h index e07862b63..8630cca78 100644 --- a/src/Math/Vector.h +++ b/src/Math/Vector.h @@ -84,6 +84,7 @@ template class Vector { * Expects that both vectors are normalized. @f[ * \theta = acos \left( \frac{\boldsymbol a \cdot \boldsymbol b}{|\boldsymbol a| |\boldsymbol b|} \right) = acos (\boldsymbol a \cdot \boldsymbol b) * @f] + * @see Quaternion::angle(), Complex::angle() */ inline static Rad angle(const Vector& normalizedA, const Vector& normalizedB) { CORRADE_ASSERT(MathTypeTraits::equals(normalizedA.dot(), T(1)) && MathTypeTraits::equals(normalizedB.dot(), T(1)),