diff --git a/src/Math/Complex.h b/src/Math/Complex.h index 65c455b2e..9b1d36ea3 100644 --- a/src/Math/Complex.h +++ b/src/Math/Complex.h @@ -75,10 +75,10 @@ template class Complex { * 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() + * @see isNormalized(), Quaternion::angle(), Vector::angle() */ inline static Rad angle(const Complex& normalizedA, const Complex& normalizedB) { - CORRADE_ASSERT(TypeTraits::equals(normalizedA.dot(), T(1)) && TypeTraits::equals(normalizedB.dot(), T(1)), + CORRADE_ASSERT(normalizedA.isNormalized() && normalizedB.isNormalized(), "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)); } @@ -147,6 +147,18 @@ template class Complex { return !operator==(other); } + /** + * @brief Whether the complex number is normalized + * + * Complex number is normalized if it has unit length: @f[ + * |c|^2 = |c| = 1 + * @f] + * @see dot(), normalized() + */ + inline bool isNormalized() const { + return TypeTraits::equals(dot(), T(1)); + } + /** @brief Real part */ inline constexpr T real() const { return _real; } @@ -311,7 +323,7 @@ template class Complex { * with other values, because it doesn't compute the square root. @f[ * c \cdot c = a^2 + b^2 * @f] - * @see dot(const Complex&, const Complex&) + * @see dot(const Complex&, const Complex&), isNormalized() */ inline T dot() const { return dot(*this, *this); @@ -324,12 +336,17 @@ template class Complex { * values. @f[ * |c| = \sqrt{c \cdot c} * @f] + * @see isNormalized() */ inline T length() const { return std::hypot(_real, _imaginary); } - /** @brief Normalized complex number (of unit length) */ + /** + * @brief Normalized complex number (of unit length) + * + * @see isNormalized() + */ inline Complex normalized() const { return (*this)/length(); } @@ -364,10 +381,10 @@ template class Complex { * normalized. @f[ * c^{-1} = \frac{c^*}{c \cdot c} = c^* * @f] - * @see inverted() + * @see isNormalized(), inverted() */ inline Complex invertedNormalized() const { - CORRADE_ASSERT(TypeTraits::equals(dot(), T(1)), + CORRADE_ASSERT(isNormalized(), "Math::Complex::invertedNormalized(): complex number must be normalized", Complex(std::numeric_limits::quiet_NaN(), {})); return conjugated(); diff --git a/src/Math/DualComplex.h b/src/Math/DualComplex.h index f1cce6981..6854c4f6a 100644 --- a/src/Math/DualComplex.h +++ b/src/Math/DualComplex.h @@ -126,6 +126,18 @@ template class DualComplex: public Dual> { inline constexpr explicit DualComplex(const Vector2& vector): Dual>({}, Complex(vector)) {} #endif + /** + * @brief Whether the dual complex number is normalized + * + * Dual complex number is normalized if its real part has unit length: @f[ + * |c_0|^2 = |c_0| = 1 + * @f] + * @see Complex::dot(), normalized() + */ + inline bool isNormalized() const { + return TypeTraits::equals(this->real().dot(), T(1)); + } + /** * @brief Rotation angle of dual complex number * @@ -240,6 +252,7 @@ template class DualComplex: public Dual> { * @f[ * c' = \frac{c_0}{|c_0|} * @f] + * @see isNormalized() * @todo can this be done similarly to dual quaternions? */ inline DualComplex normalized() const { @@ -265,7 +278,7 @@ template class DualComplex: public Dual> { * 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() + * @see isNormalized(), inverted() * @todo can this be done similarly to dual quaternions? */ inline DualComplex invertedNormalized() const { diff --git a/src/Math/Test/ComplexTest.cpp b/src/Math/Test/ComplexTest.cpp index 75ad05df8..931a66ead 100644 --- a/src/Math/Test/ComplexTest.cpp +++ b/src/Math/Test/ComplexTest.cpp @@ -37,7 +37,9 @@ class ComplexTest: public Corrade::TestSuite::Tester { void construct(); void constructDefault(); void constructFromVector(); + void compare(); + void isNormalized(); void constExpressions(); @@ -67,7 +69,9 @@ ComplexTest::ComplexTest() { addTests({&ComplexTest::construct, &ComplexTest::constructDefault, &ComplexTest::constructFromVector, + &ComplexTest::compare, + &ComplexTest::isNormalized, &ComplexTest::constExpressions, @@ -126,6 +130,11 @@ void ComplexTest::compare() { CORRADE_VERIFY(Complex(1.0f+TypeTraits::epsilon()*2, 3.7f) != Complex(1.0f, 3.7f)); } +void ComplexTest::isNormalized() { + CORRADE_VERIFY(!Complex(2.5f, -3.7f).isNormalized()); + CORRADE_VERIFY(Complex::rotation(Deg(23.0f)).isNormalized()); +} + void ComplexTest::constExpressions() { /* Default constructor */ constexpr Complex a; diff --git a/src/Math/Test/DualComplexTest.cpp b/src/Math/Test/DualComplexTest.cpp index b67f69259..9d8db2094 100644 --- a/src/Math/Test/DualComplexTest.cpp +++ b/src/Math/Test/DualComplexTest.cpp @@ -38,6 +38,8 @@ class DualComplexTest: public Corrade::TestSuite::Tester { void constructDefault(); void constructFromVector(); + void isNormalized(); + void constExpressions(); void multiply(); @@ -74,6 +76,8 @@ DualComplexTest::DualComplexTest() { &DualComplexTest::constructDefault, &DualComplexTest::constructFromVector, + &DualComplexTest::isNormalized, + &DualComplexTest::constExpressions, &DualComplexTest::multiply, @@ -112,6 +116,11 @@ void DualComplexTest::constructFromVector() { CORRADE_COMPARE(DualComplex(Vector2(1.5f, -3.0f)), DualComplex({1.0f, 0.0f}, {1.5f, -3.0f})); } +void DualComplexTest::isNormalized() { + CORRADE_VERIFY(!DualComplex({2.0f, 1.0f}, {}).isNormalized()); + CORRADE_VERIFY((DualComplex::rotation(Deg(23.0f))*DualComplex::translation({6.0f, 3.0f})).isNormalized()); +} + void DualComplexTest::constExpressions() { /* Default constructor */ constexpr DualComplex a;