From 0cf65741ae09d6df2395b92c2965f9b03758f0d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 20 Oct 2018 13:19:59 +0200 Subject: [PATCH] Math: helpfully print the offending values in all assertions. --- doc/changelog.dox | 2 + src/Magnum/Math/Complex.h | 10 +- src/Magnum/Math/CubicHermite.h | 4 +- src/Magnum/Math/Distance.h | 2 +- src/Magnum/Math/DualComplex.h | 2 +- src/Magnum/Math/DualQuaternion.h | 10 +- src/Magnum/Math/Intersection.h | 3 +- src/Magnum/Math/Matrix.h | 2 +- src/Magnum/Math/Matrix3.h | 10 +- src/Magnum/Math/Matrix4.h | 12 +- src/Magnum/Math/Quaternion.h | 25 ++-- src/Magnum/Math/Test/ComplexTest.cpp | 61 +++++---- src/Magnum/Math/Test/CubicHermiteTest.cpp | 32 ++--- src/Magnum/Math/Test/DistanceTest.cpp | 17 ++- src/Magnum/Math/Test/DualComplexTest.cpp | 32 +++-- src/Magnum/Math/Test/DualQuaternionTest.cpp | 68 +++++++--- src/Magnum/Math/Test/IntersectionTest.cpp | 14 +- src/Magnum/Math/Test/Matrix3Test.cpp | 54 +++++--- src/Magnum/Math/Test/Matrix4Test.cpp | 73 +++++++--- src/Magnum/Math/Test/MatrixTest.cpp | 20 ++- src/Magnum/Math/Test/QuaternionTest.cpp | 140 +++++++++++++------- src/Magnum/Math/Test/VectorTest.cpp | 39 ++++-- src/Magnum/Math/Vector.h | 5 +- 23 files changed, 419 insertions(+), 218 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index 5389cca0f..bb780412a 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -255,6 +255,8 @@ See also: @subsubsection changelog-latest-changes-math Math library +- All assertions in the @ref Math namespace now helpfully print the offending + value that caused the assert for easier debugging - @ref Math::Matrix3::rotation() const and @ref Math::Matrix4::rotation() const now allow non-uniform scaling, but expect the roation/scaling part to be orthogonal after normalization diff --git a/src/Magnum/Math/Complex.h b/src/Magnum/Math/Complex.h index 5377f94bf..ab90c3235 100644 --- a/src/Magnum/Math/Complex.h +++ b/src/Magnum/Math/Complex.h @@ -71,7 +71,7 @@ Expects that both complex numbers are normalized. @f[ */ template inline Rad angle(const Complex& normalizedA, const Complex& normalizedB) { CORRADE_ASSERT(normalizedA.isNormalized() && normalizedB.isNormalized(), - "Math::angle(): complex numbers must be normalized", {}); + "Math::angle(): complex numbers" << normalizedA << "and" << normalizedB << "are not normalized", {}); return Rad(std::acos(dot(normalizedA, normalizedB))); } @@ -115,7 +115,7 @@ template class Complex { */ static Complex fromMatrix(const Matrix2x2& matrix) { CORRADE_ASSERT(matrix.isOrthogonal(), - "Math::Complex::fromMatrix(): the matrix is not orthogonal", {}); + "Math::Complex::fromMatrix(): the matrix is not orthogonal:" << Corrade::Utility::Debug::newline << matrix, {}); return Implementation::complexFromMatrix(matrix); } @@ -488,7 +488,7 @@ template class Complex { */ Complex invertedNormalized() const { CORRADE_ASSERT(isNormalized(), - "Math::Complex::invertedNormalized(): complex number must be normalized", {}); + "Math::Complex::invertedNormalized():" << *this << "is not normalized", {}); return conjugated(); } @@ -574,7 +574,7 @@ Expects that both complex numbers are normalized. @f[ */ template inline Complex lerp(const Complex& normalizedA, const Complex& normalizedB, T t) { CORRADE_ASSERT(normalizedA.isNormalized() && normalizedB.isNormalized(), - "Math::lerp(): complex numbers must be normalized", {}); + "Math::lerp(): complex numbers" << normalizedA << "and" << normalizedB << "are not normalized", {}); return ((T(1) - t)*normalizedA + t*normalizedB).normalized(); } @@ -596,7 +596,7 @@ the same, returns the first argument. @f[ */ template inline Complex slerp(const Complex& normalizedA, const Complex& normalizedB, T t) { CORRADE_ASSERT(normalizedA.isNormalized() && normalizedB.isNormalized(), - "Math::slerp(): complex numbers must be normalized", {}); + "Math::slerp(): complex numbers" << normalizedA << "and" << normalizedB << "are not normalized", {}); const T cosAngle = dot(normalizedA, normalizedB); /* Avoid division by zero */ diff --git a/src/Magnum/Math/CubicHermite.h b/src/Magnum/Math/CubicHermite.h index c1c93a1a7..ab7cdebb8 100644 --- a/src/Magnum/Math/CubicHermite.h +++ b/src/Magnum/Math/CubicHermite.h @@ -493,7 +493,7 @@ Expects that @ref CubicHermite::point() is a normalized complex number in both */ template Complex splerp(const CubicHermiteComplex& a, const CubicHermiteComplex& b, T t) { CORRADE_ASSERT(a.point().isNormalized() && b.point().isNormalized(), - "Math::splerp(): complex spline points must be normalized", {}); + "Math::splerp(): complex spline points" << a.point() << "and" << b.point() << "are not normalized", {}); return ((T(2)*t*t*t - T(3)*t*t + T(1))*a.point() + (t*t*t - T(2)*t*t + t)*a.outTangent() + (T(-2)*t*t*t + T(3)*t*t)*b.point() + @@ -523,7 +523,7 @@ and @p b. */ template Quaternion splerp(const CubicHermiteQuaternion& a, const CubicHermiteQuaternion& b, T t) { CORRADE_ASSERT(a.point().isNormalized() && b.point().isNormalized(), - "Math::splerp(): quaternion spline points must be normalized", {}); + "Math::splerp(): quaternion spline points" << a.point() << "and" << b.point() << "are not normalized", {}); return ((T(2)*t*t*t - T(3)*t*t + T(1))*a.point() + (t*t*t - T(2)*t*t + t)*a.outTangent() + (T(-2)*t*t*t + T(3)*t*t)*b.point() + diff --git a/src/Magnum/Math/Distance.h b/src/Magnum/Math/Distance.h index 53967dc73..600c0f28c 100644 --- a/src/Magnum/Math/Distance.h +++ b/src/Magnum/Math/Distance.h @@ -210,7 +210,7 @@ top. */ template inline T pointPlaneNormalized(const Vector3& point, const Vector4& plane) { CORRADE_ASSERT(plane.xyz().isNormalized(), - "Math::Geometry::Distance::pointPlaneNormalized(): plane normal is not an unit vector", {}); + "Math::Distance::pointPlaneNormalized(): plane normal" << plane.xyz() << "is not normalized", {}); return pointPlaneScaled(point, plane); } diff --git a/src/Magnum/Math/DualComplex.h b/src/Magnum/Math/DualComplex.h index 768751bbd..4e9e6ca34 100644 --- a/src/Magnum/Math/DualComplex.h +++ b/src/Magnum/Math/DualComplex.h @@ -98,7 +98,7 @@ template class DualComplex: public Dual> { */ static DualComplex fromMatrix(const Matrix3& matrix) { CORRADE_ASSERT(matrix.isRigidTransformation(), - "Math::DualComplex::fromMatrix(): the matrix doesn't represent rigid transformation", {}); + "Math::DualComplex::fromMatrix(): the matrix doesn't represent rigid transformation:" << Corrade::Utility::Debug::newline << matrix, {}); return {Implementation::complexFromMatrix(matrix.rotationScaling()), Complex(matrix.translation())}; } diff --git a/src/Magnum/Math/DualQuaternion.h b/src/Magnum/Math/DualQuaternion.h index 3fc96df1d..42114404c 100644 --- a/src/Magnum/Math/DualQuaternion.h +++ b/src/Magnum/Math/DualQuaternion.h @@ -79,7 +79,7 @@ Note that this function does not check for shortest path interpolation, see */ template inline DualQuaternion sclerp(const DualQuaternion& normalizedA, const DualQuaternion& normalizedB, const T t) { CORRADE_ASSERT(normalizedA.isNormalized() && normalizedB.isNormalized(), - "Math::sclerp(): dual quaternions must be normalized", {}); + "Math::sclerp(): dual quaternions" << normalizedA << "and" << normalizedB << "are not normalized", {}); const T cosHalfAngle = dot(normalizedA.real(), normalizedB.real()); /* Avoid division by zero: interpolate just the translation part */ @@ -146,7 +146,7 @@ otherwise, the interpolation is performed as: @f[ */ template inline DualQuaternion sclerpShortestPath(const DualQuaternion& normalizedA, const DualQuaternion& normalizedB, const T t) { CORRADE_ASSERT(normalizedA.isNormalized() && normalizedB.isNormalized(), - "Math::sclerp(): dual quaternions must be normalized", {}); + "Math::sclerp(): dual quaternions" << normalizedA << "and" << normalizedB << "are not normalized", {}); const T cosHalfAngle = dot(normalizedA.real(), normalizedB.real()); /* Avoid division by zero: interpolate just the translation part */ @@ -238,7 +238,7 @@ template class DualQuaternion: public Dual> { */ static DualQuaternion fromMatrix(const Matrix4& matrix) { CORRADE_ASSERT(matrix.isRigidTransformation(), - "Math::DualQuaternion::fromMatrix(): the matrix doesn't represent rigid transformation", {}); + "Math::DualQuaternion::fromMatrix(): the matrix doesn't represent a rigid transformation:" << Corrade::Utility::Debug::newline << matrix, {}); Quaternion q = Implementation::quaternionFromMatrix(matrix.rotationScaling()); return {q, Quaternion(matrix.translation()/2)*q}; @@ -481,7 +481,7 @@ template class DualQuaternion: public Dual> { */ DualQuaternion invertedNormalized() const { CORRADE_ASSERT(isNormalized(), - "Math::DualQuaternion::invertedNormalized(): dual quaternion must be normalized", {}); + "Math::DualQuaternion::invertedNormalized():" << *this << "is not normalized", {}); return quaternionConjugated(); } @@ -515,7 +515,7 @@ template class DualQuaternion: public Dual> { */ Vector3 transformPointNormalized(const Vector3& vector) const { CORRADE_ASSERT(isNormalized(), - "Math::DualQuaternion::transformPointNormalized(): dual quaternion must be normalized", {}); + "Math::DualQuaternion::transformPointNormalized():" << *this << "is not normalized", {}); return ((*this)*DualQuaternion(vector)*conjugated()).dual().vector(); } diff --git a/src/Magnum/Math/Intersection.h b/src/Magnum/Math/Intersection.h index ce5eb502b..38e00b7c6 100644 --- a/src/Magnum/Math/Intersection.h +++ b/src/Magnum/Math/Intersection.h @@ -517,7 +517,8 @@ template bool sphereConeView(const Vector3& sphereCenter, const T sp } template bool sphereConeView(const Vector3& sphereCenter, const T sphereRadius, const Matrix4& coneView, const T sinAngle, const T tanAngle) { - CORRADE_ASSERT(coneView.isRigidTransformation(), "Math::Geometry::Intersection::sphereConeView(): coneView does not represent a rigid transformation", false); + CORRADE_ASSERT(coneView.isRigidTransformation(), + "Math::Intersection::sphereConeView(): coneView does not represent a rigid transformation:" << Corrade::Utility::Debug::newline << coneView, false); /* Transform the sphere so that we can test against Z axis aligned origin cone instead */ diff --git a/src/Magnum/Math/Matrix.h b/src/Magnum/Math/Matrix.h index 2efcd2970..56773bc3f 100644 --- a/src/Magnum/Math/Matrix.h +++ b/src/Magnum/Math/Matrix.h @@ -202,7 +202,7 @@ template class Matrix: public RectangularMatrix invertedOrthogonal() const { CORRADE_ASSERT(isOrthogonal(), - "Math::Matrix::invertedOrthogonal(): the matrix is not orthogonal", {}); + "Math::Matrix::invertedOrthogonal(): the matrix is not orthogonal:" << Corrade::Utility::Debug::Debug::newline << *this, {}); return RectangularMatrix::transposed(); } diff --git a/src/Magnum/Math/Matrix3.h b/src/Magnum/Math/Matrix3.h index a6d1c545b..8c1e12a93 100644 --- a/src/Magnum/Math/Matrix3.h +++ b/src/Magnum/Math/Matrix3.h @@ -117,7 +117,7 @@ template class Matrix3: public Matrix3x3 { */ static Matrix3 reflection(const Vector2& normal) { CORRADE_ASSERT(normal.isNormalized(), - "Math::Matrix3::reflection(): normal must be normalized", {}); + "Math::Matrix3::reflection(): normal" << normal << "is not normalized", {}); return from(Matrix2x2() - T(2)*normal*RectangularMatrix<1, 2, T>(normal).transposed(), {}); } @@ -665,7 +665,7 @@ template Matrix2x2 Matrix3::rotation() const { Matrix2x2 rotation{(*this)[0].xy().normalized(), (*this)[1].xy().normalized()}; CORRADE_ASSERT(rotation.isOrthogonal(), - "Math::Matrix3::rotation(): the normalized rotation part is not orthogonal", {}); + "Math::Matrix3::rotation(): the normalized rotation part is not orthogonal:" << Corrade::Utility::Debug::newline << rotation, {}); return rotation; } @@ -673,20 +673,20 @@ template Matrix2x2 Matrix3::rotationNormalized() const { Matrix2x2 rotation{(*this)[0].xy(), (*this)[1].xy()}; CORRADE_ASSERT(rotation.isOrthogonal(), - "Math::Matrix3::rotationNormalized(): the rotation part is not orthogonal", {}); + "Math::Matrix3::rotationNormalized(): the rotation part is not orthogonal:" << Corrade::Utility::Debug::newline << rotation, {}); return rotation; } template T Matrix3::uniformScalingSquared() const { const T scalingSquared = (*this)[0].xy().dot(); CORRADE_ASSERT(TypeTraits::equals((*this)[1].xy().dot(), scalingSquared), - "Math::Matrix3::uniformScaling(): the matrix doesn't have uniform scaling", {}); + "Math::Matrix3::uniformScaling(): the matrix doesn't have uniform scaling:" << Corrade::Utility::Debug::newline << rotationScaling(), {}); return scalingSquared; } template inline Matrix3 Matrix3::invertedRigid() const { CORRADE_ASSERT(isRigidTransformation(), - "Math::Matrix3::invertedRigid(): the matrix doesn't represent rigid transformation", {}); + "Math::Matrix3::invertedRigid(): the matrix doesn't represent a rigid transformation:" << Corrade::Utility::Debug::newline << *this, {}); Matrix2x2 inverseRotation = rotationScaling().transposed(); return from(inverseRotation, inverseRotation*-translation()); diff --git a/src/Magnum/Math/Matrix4.h b/src/Magnum/Math/Matrix4.h index 76d29d368..e4abe28ea 100644 --- a/src/Magnum/Math/Matrix4.h +++ b/src/Magnum/Math/Matrix4.h @@ -890,7 +890,7 @@ MAGNUM_MATRIXn_OPERATOR_IMPLEMENTATION(4, Matrix4) template Matrix4 Matrix4::rotation(const Rad angle, const Vector3& normalizedAxis) { CORRADE_ASSERT(normalizedAxis.isNormalized(), - "Math::Matrix4::rotation(): axis must be normalized", {}); + "Math::Matrix4::rotation(): axis" << normalizedAxis << "is not normalized", {}); const T sine = std::sin(T(angle)); const T cosine = std::cos(T(angle)); @@ -952,7 +952,7 @@ template Matrix4 Matrix4::rotationZ(const Rad angle) { template Matrix4 Matrix4::reflection(const Vector3& normal) { CORRADE_ASSERT(normal.isNormalized(), - "Math::Matrix4::reflection(): normal must be normalized", {}); + "Math::Matrix4::reflection(): normal" << normal << "is not normalized", {}); return from(Matrix3x3() - T(2)*normal*RectangularMatrix<1, 3, T>(normal).transposed(), {}); } @@ -995,7 +995,7 @@ template Matrix3x3 Matrix4::rotation() const { (*this)[1].xyz().normalized(), (*this)[2].xyz().normalized()}; CORRADE_ASSERT(rotation.isOrthogonal(), - "Math::Matrix4::rotation(): the normalized rotation part is not orthogonal", {}); + "Math::Matrix4::rotation(): the normalized rotation part is not orthogonal:" << Corrade::Utility::Debug::newline << rotation, {}); return rotation; } @@ -1004,7 +1004,7 @@ template Matrix3x3 Matrix4::rotationNormalized() const { (*this)[1].xyz(), (*this)[2].xyz()}; CORRADE_ASSERT(rotation.isOrthogonal(), - "Math::Matrix4::rotationNormalized(): the rotation part is not orthogonal", {}); + "Math::Matrix4::rotationNormalized(): the rotation part is not orthogonal:" << Corrade::Utility::Debug::newline << rotation, {}); return rotation; } @@ -1012,13 +1012,13 @@ template T Matrix4::uniformScalingSquared() const { const T scalingSquared = (*this)[0].xyz().dot(); CORRADE_ASSERT(TypeTraits::equals((*this)[1].xyz().dot(), scalingSquared) && TypeTraits::equals((*this)[2].xyz().dot(), scalingSquared), - "Math::Matrix4::uniformScaling(): the matrix doesn't have uniform scaling", {}); + "Math::Matrix4::uniformScaling(): the matrix doesn't have uniform scaling:" << Corrade::Utility::Debug::newline << rotationScaling(), {}); return scalingSquared; } template Matrix4 Matrix4::invertedRigid() const { CORRADE_ASSERT(isRigidTransformation(), - "Math::Matrix4::invertedRigid(): the matrix doesn't represent rigid transformation", {}); + "Math::Matrix4::invertedRigid(): the matrix doesn't represent a rigid transformation:" << Corrade::Utility::Debug::newline << *this, {}); Matrix3x3 inverseRotation = rotationScaling().transposed(); return from(inverseRotation, inverseRotation*-translation()); diff --git a/src/Magnum/Math/Quaternion.h b/src/Magnum/Math/Quaternion.h index 07da65c85..91e27b569 100644 --- a/src/Magnum/Math/Quaternion.h +++ b/src/Magnum/Math/Quaternion.h @@ -74,7 +74,7 @@ Expects that both quaternions are normalized. @f[ */ template inline Rad angle(const Quaternion& normalizedA, const Quaternion& normalizedB) { CORRADE_ASSERT(normalizedA.isNormalized() && normalizedB.isNormalized(), - "Math::angle(): quaternions must be normalized", {}); + "Math::angle(): quaternions" << normalizedA << "and" << normalizedB << "are not normalized", {}); return Rad{Implementation::angle(normalizedA, normalizedB)}; } @@ -101,7 +101,7 @@ alternative. */ template inline Quaternion lerp(const Quaternion& normalizedA, const Quaternion& normalizedB, T t) { CORRADE_ASSERT(normalizedA.isNormalized() && normalizedB.isNormalized(), - "Math::lerp(): quaternions must be normalized", {}); + "Math::lerp(): quaternions" << normalizedA << "and" << normalizedB << "are not normalized", {}); return ((T(1) - t)*normalizedA + t*normalizedB).normalized(); } @@ -164,7 +164,7 @@ alternative. */ template inline Quaternion slerp(const Quaternion& normalizedA, const Quaternion& normalizedB, T t) { CORRADE_ASSERT(normalizedA.isNormalized() && normalizedB.isNormalized(), - "Math::slerp(): quaternions must be normalized", {}); + "Math::slerp(): quaternions" << normalizedA << "and" << normalizedB << "are not normalized", {}); const T cosHalfAngle = dot(normalizedA, normalizedB); /* Avoid division by zero */ @@ -210,7 +210,7 @@ otherwise, the interpolation is performed as: @f[ */ template inline Quaternion slerpShortestPath(const Quaternion& normalizedA, const Quaternion& normalizedB, T t) { CORRADE_ASSERT(normalizedA.isNormalized() && normalizedB.isNormalized(), - "Math::slerpShortestPath(): quaternions must be normalized", {}); + "Math::slerpShortestPath(): quaternions" << normalizedA << "and" << normalizedB << "are not normalized", {}); const T cosHalfAngle = dot(normalizedA, normalizedB); /* Avoid division by zero */ @@ -685,22 +685,25 @@ template Quaternion quaternionFromMatrix(const Matrix3x3& m) { template inline Quaternion Quaternion::rotation(const Rad angle, const Vector3& normalizedAxis) { CORRADE_ASSERT(normalizedAxis.isNormalized(), - "Math::Quaternion::rotation(): axis must be normalized", {}); + "Math::Quaternion::rotation(): axis" << normalizedAxis << "is not normalized", {}); return {normalizedAxis*std::sin(T(angle)/2), std::cos(T(angle)/2)}; } template inline Quaternion Quaternion::fromMatrix(const Matrix3x3& matrix) { - CORRADE_ASSERT(matrix.isOrthogonal(), "Math::Quaternion::fromMatrix(): the matrix is not orthogonal", {}); + CORRADE_ASSERT(matrix.isOrthogonal(), + "Math::Quaternion::fromMatrix(): the matrix is not orthogonal:" << Corrade::Utility::Debug::newline << matrix, {}); return Implementation::quaternionFromMatrix(matrix); } template inline Rad Quaternion::angle() const { - CORRADE_ASSERT(isNormalized(), "Math::Quaternion::angle(): quaternion must be normalized", {}); + CORRADE_ASSERT(isNormalized(), + "Math::Quaternion::angle():" << *this << "is not normalized", {}); return Rad(T(2)*std::acos(_scalar)); } template inline Vector3 Quaternion::axis() const { - CORRADE_ASSERT(isNormalized(), "Math::Quaternion::axis(): quaternion must be normalized", {}); + CORRADE_ASSERT(isNormalized(), + "Math::Quaternion::axis():" << *this << "is not normalized", {}); return _vector/std::sqrt(1-pow2(_scalar)); } @@ -724,12 +727,14 @@ template inline Quaternion Quaternion::operator*(const Quaternion } template inline Quaternion Quaternion::invertedNormalized() const { - CORRADE_ASSERT(isNormalized(), "Math::Quaternion::invertedNormalized(): quaternion must be normalized", {}); + CORRADE_ASSERT(isNormalized(), + "Math::Quaternion::invertedNormalized():" << *this << "is not normalized", {}); return conjugated(); } template inline Vector3 Quaternion::transformVectorNormalized(const Vector3& vector) const { - CORRADE_ASSERT(isNormalized(), "Math::Quaternion::transformVectorNormalized(): quaternion must be normalized", {}); + CORRADE_ASSERT(isNormalized(), + "Math::Quaternion::transformVectorNormalized():" << *this << "is not normalized", {}); const Vector3 t = T(2)*Math::cross(_vector, vector); return vector + _scalar*t + Math::cross(_vector, t); } diff --git a/src/Magnum/Math/Test/ComplexTest.cpp b/src/Magnum/Math/Test/ComplexTest.cpp index a097aa80e..1aebdd486 100644 --- a/src/Magnum/Math/Test/ComplexTest.cpp +++ b/src/Magnum/Math/Test/ComplexTest.cpp @@ -85,10 +85,13 @@ struct ComplexTest: Corrade::TestSuite::Tester { void conjugated(); void inverted(); void invertedNormalized(); + void invertedNormalizedNotNormalized(); void angle(); + void angleNotNormalized(); void rotation(); void matrix(); + void matrixNotOrthogonal(); void lerp(); void lerpNotNormalized(); void slerp(); @@ -134,10 +137,13 @@ ComplexTest::ComplexTest() { addTests({&ComplexTest::conjugated, &ComplexTest::inverted, &ComplexTest::invertedNormalized, + &ComplexTest::invertedNormalizedNotNormalized, &ComplexTest::angle, + &ComplexTest::angleNotNormalized, &ComplexTest::rotation, &ComplexTest::matrix, + &ComplexTest::matrixNotOrthogonal, &ComplexTest::lerp, &ComplexTest::lerpNotNormalized, &ComplexTest::slerp, @@ -392,31 +398,24 @@ void ComplexTest::inverted() { } void ComplexTest::invertedNormalized() { - std::ostringstream o; - Error redirectError{&o}; - Complex a(-0.6f, 0.8f); Complex b(-0.6f, -0.8f); - (a*2).invertedNormalized(); - CORRADE_COMPARE(o.str(), "Math::Complex::invertedNormalized(): complex number must be normalized\n"); - Complex inverted = a.invertedNormalized(); CORRADE_COMPARE(a*inverted, Complex()); CORRADE_COMPARE(inverted*a, Complex()); CORRADE_COMPARE(inverted, b); } -void ComplexTest::angle() { - std::ostringstream o; - Error redirectError{&o}; - Math::angle(Complex(1.5f, -2.0f).normalized(), {-4.0f, 3.5f}); - CORRADE_COMPARE(o.str(), "Math::angle(): complex numbers must be normalized\n"); +void ComplexTest::invertedNormalizedNotNormalized() { + std::ostringstream out; + Error redirectError{&out}; - o.str({}); - Math::angle({1.5f, -2.0f}, Complex(-4.0f, 3.5f).normalized()); - CORRADE_COMPARE(o.str(), "Math::angle(): complex numbers must be normalized\n"); + (Complex(-0.6f, 0.8f)*2).invertedNormalized(); + CORRADE_COMPARE(out.str(), "Math::Complex::invertedNormalized(): Complex(-1.2, 1.6) is not normalized\n"); +} +void ComplexTest::angle() { /* Verify also that the angle is the same as angle between 2D vectors */ Rad angle = Math::angle(Complex( 1.5f, -2.0f).normalized(), Complex(-4.0f, 3.5f).normalized()); @@ -425,6 +424,16 @@ void ComplexTest::angle() { CORRADE_COMPARE(angle, Rad(2.933128f)); } +void ComplexTest::angleNotNormalized() { + std::ostringstream out; + Error redirectError{&out}; + Math::angle(Complex(1.5f, -2.0f).normalized(), {-4.0f, 3.5f}); + Math::angle({1.5f, -2.0f}, Complex(-4.0f, 3.5f).normalized()); + CORRADE_COMPARE(out.str(), + "Math::angle(): complex numbers Complex(0.6, -0.8) and Complex(-4, 3.5) are not normalized\n" + "Math::angle(): complex numbers Complex(1.5, -2) and Complex(-0.752577, 0.658505) are not normalized\n"); +} + void ComplexTest::rotation() { Complex a = Complex::rotation(Deg(120.0f)); CORRADE_COMPARE(a.length(), 1.0f); @@ -445,14 +454,18 @@ void ComplexTest::matrix() { Matrix2x2 m = Matrix3::rotation(Deg(37.0f)).rotationScaling(); CORRADE_COMPARE(a.toMatrix(), m); + CORRADE_COMPARE(Complex::fromMatrix(m), a); +} - std::ostringstream o; - Error redirectError{&o}; - Complex::fromMatrix(m*2); - CORRADE_COMPARE(o.str(), "Math::Complex::fromMatrix(): the matrix is not orthogonal\n"); +void ComplexTest::matrixNotOrthogonal() { + std::ostringstream out; + Error redirectError{&out}; - Complex b = Complex::fromMatrix(m); - CORRADE_COMPARE(b, a); + Complex::fromMatrix(Matrix3::rotation(Deg(37.0f)).rotationScaling()*2); + CORRADE_COMPARE(out.str(), + "Math::Complex::fromMatrix(): the matrix is not orthogonal:\n" + "Matrix(1.59727, -1.20363,\n" + " 1.20363, 1.59727)\n"); } void ComplexTest::lerp() { @@ -476,8 +489,8 @@ void ComplexTest::lerpNotNormalized() { Math::lerp(a*3.0f, a, 0.35f); Math::lerp(a, a*-3.0f, 0.35f); CORRADE_COMPARE(out.str(), - "Math::lerp(): complex numbers must be normalized\n" - "Math::lerp(): complex numbers must be normalized\n"); + "Math::lerp(): complex numbers Complex(3, 0) and Complex(1, 0) are not normalized\n" + "Math::lerp(): complex numbers Complex(1, 0) and Complex(-3, -0) are not normalized\n"); } void ComplexTest::slerp() { @@ -502,8 +515,8 @@ void ComplexTest::slerpNotNormalized() { Math::slerp(a*3.0f, a, 0.35f); Math::slerp(a, a*-3.0f, 0.35f); CORRADE_COMPARE(out.str(), - "Math::slerp(): complex numbers must be normalized\n" - "Math::slerp(): complex numbers must be normalized\n"); + "Math::slerp(): complex numbers Complex(3, 0) and Complex(1, 0) are not normalized\n" + "Math::slerp(): complex numbers Complex(1, 0) and Complex(-3, -0) are not normalized\n"); } void ComplexTest::transformVector() { diff --git a/src/Magnum/Math/Test/CubicHermiteTest.cpp b/src/Magnum/Math/Test/CubicHermiteTest.cpp index 619b22639..7a955a80f 100644 --- a/src/Magnum/Math/Test/CubicHermiteTest.cpp +++ b/src/Magnum/Math/Test/CubicHermiteTest.cpp @@ -817,8 +817,8 @@ void CubicHermiteTest::lerpComplexNotNormalized() { Math::lerp({}, a, 0.3f); Math::lerp(a, {}, 0.3f); CORRADE_COMPARE(out.str(), - "Math::lerp(): complex numbers must be normalized\n" - "Math::lerp(): complex numbers must be normalized\n"); + "Math::lerp(): complex numbers Complex(1, 0) and Complex(2, 0) are not normalized\n" + "Math::lerp(): complex numbers Complex(2, 0) and Complex(1, 0) are not normalized\n"); } void CubicHermiteTest::lerpQuaternion() { @@ -858,8 +858,8 @@ void CubicHermiteTest::lerpQuaternionNotNormalized() { Math::lerp({}, a, 0.3f); Math::lerp(a, {}, 0.3f); CORRADE_COMPARE(out.str(), - "Math::lerp(): quaternions must be normalized\n" - "Math::lerp(): quaternions must be normalized\n"); + "Math::lerp(): quaternions Quaternion({0, 0, 0}, 1) and Quaternion({0, 0, 0}, 2) are not normalized\n" + "Math::lerp(): quaternions Quaternion({0, 0, 0}, 2) and Quaternion({0, 0, 0}, 1) are not normalized\n"); } void CubicHermiteTest::lerpQuaternionShortestPath() { @@ -899,8 +899,8 @@ void CubicHermiteTest::lerpQuaternionShortestPathNotNormalized() { Math::lerpShortestPath(a, {}, 0.3f); /* lerpShortestPath() is calling lerp(), so the message is from there */ CORRADE_COMPARE(out.str(), - "Math::lerp(): quaternions must be normalized\n" - "Math::lerp(): quaternions must be normalized\n"); + "Math::lerp(): quaternions Quaternion({0, 0, 0}, 1) and Quaternion({0, 0, 0}, 2) are not normalized\n" + "Math::lerp(): quaternions Quaternion({0, 0, 0}, 2) and Quaternion({0, 0, 0}, 1) are not normalized\n"); } void CubicHermiteTest::slerpComplex() { @@ -934,8 +934,8 @@ void CubicHermiteTest::slerpComplexNotNormalized() { Math::slerp({}, a, 0.3f); Math::slerp(a, {}, 0.3f); CORRADE_COMPARE(out.str(), - "Math::slerp(): complex numbers must be normalized\n" - "Math::slerp(): complex numbers must be normalized\n"); + "Math::slerp(): complex numbers Complex(1, 0) and Complex(2, 0) are not normalized\n" + "Math::slerp(): complex numbers Complex(2, 0) and Complex(1, 0) are not normalized\n"); } void CubicHermiteTest::slerpQuaternion() { @@ -975,8 +975,8 @@ void CubicHermiteTest::slerpQuaternionNotNormalized() { Math::slerp({}, a, 0.3f); Math::slerp(a, {}, 0.3f); CORRADE_COMPARE(out.str(), - "Math::slerp(): quaternions must be normalized\n" - "Math::slerp(): quaternions must be normalized\n"); + "Math::slerp(): quaternions Quaternion({0, 0, 0}, 1) and Quaternion({0, 0, 0}, 2) are not normalized\n" + "Math::slerp(): quaternions Quaternion({0, 0, 0}, 2) and Quaternion({0, 0, 0}, 1) are not normalized\n"); } void CubicHermiteTest::slerpQuaternionShortestPath() { @@ -1016,8 +1016,8 @@ void CubicHermiteTest::slerpQuaternionShortestPathNotNormalized() { Math::slerpShortestPath({}, a, 0.3f); Math::slerpShortestPath(a, {}, 0.3f); CORRADE_COMPARE(out.str(), - "Math::slerpShortestPath(): quaternions must be normalized\n" - "Math::slerpShortestPath(): quaternions must be normalized\n"); + "Math::slerpShortestPath(): quaternions Quaternion({0, 0, 0}, 1) and Quaternion({0, 0, 0}, 2) are not normalized\n" + "Math::slerpShortestPath(): quaternions Quaternion({0, 0, 0}, 2) and Quaternion({0, 0, 0}, 1) are not normalized\n"); } void CubicHermiteTest::splerpScalar() { @@ -1088,8 +1088,8 @@ void CubicHermiteTest::splerpComplexNotNormalized() { Math::splerp({}, a, 0.3f); Math::splerp(a, {}, 0.3f); CORRADE_COMPARE(out.str(), - "Math::splerp(): complex spline points must be normalized\n" - "Math::splerp(): complex spline points must be normalized\n"); + "Math::splerp(): complex spline points Complex(1, 0) and Complex(2, 0) are not normalized\n" + "Math::splerp(): complex spline points Complex(2, 0) and Complex(1, 0) are not normalized\n"); } void CubicHermiteTest::splerpQuaternion() { @@ -1125,8 +1125,8 @@ void CubicHermiteTest::splerpQuaternionNotNormalized() { Math::splerp({}, a, 0.3f); Math::splerp(a, {}, 0.3f); CORRADE_COMPARE(out.str(), - "Math::splerp(): quaternion spline points must be normalized\n" - "Math::splerp(): quaternion spline points must be normalized\n"); + "Math::splerp(): quaternion spline points Quaternion({0, 0, 0}, 1) and Quaternion({0, 0, 0}, 2) are not normalized\n" + "Math::splerp(): quaternion spline points Quaternion({0, 0, 0}, 2) and Quaternion({0, 0, 0}, 1) are not normalized\n"); } void CubicHermiteTest::debugScalar() { diff --git a/src/Magnum/Math/Test/DistanceTest.cpp b/src/Magnum/Math/Test/DistanceTest.cpp index 273a34f0d..f08c4d54c 100644 --- a/src/Magnum/Math/Test/DistanceTest.cpp +++ b/src/Magnum/Math/Test/DistanceTest.cpp @@ -43,6 +43,7 @@ struct DistanceTest: Corrade::TestSuite::Tester { void pointPlane(); void pointPlaneScaled(); void pointPlaneNormalized(); + void pointPlaneNormalizedNotNormalized(); }; typedef Math::Vector2 Vector2; @@ -58,7 +59,8 @@ DistanceTest::DistanceTest() { &DistanceTest::pointPlane, &DistanceTest::pointPlaneScaled, - &DistanceTest::pointPlaneNormalized}); + &DistanceTest::pointPlaneNormalized, + &DistanceTest::pointPlaneNormalizedNotNormalized}); } void DistanceTest::linePoint2D() { @@ -183,15 +185,18 @@ void DistanceTest::pointPlaneScaled() { void DistanceTest::pointPlaneNormalized() { Vector3 point{1.0f, 2.0f, 3.0f}; - Vector4 invalidPlane{2.0f, 2.0f, 2.0f, 0.0f}; const Vector4 plane{0.0f, 1.0f, 0.0f, 1.0f}; CORRADE_COMPARE(Distance::pointPlaneNormalized(point, plane), 3.0f); +} - std::ostringstream o; - Error redirectError{&o}; - Distance::pointPlaneNormalized(point, invalidPlane); - CORRADE_COMPARE(o.str(), "Math::Geometry::Distance::pointPlaneNormalized(): plane normal is not an unit vector\n"); +void DistanceTest::pointPlaneNormalizedNotNormalized() { + std::ostringstream out; + Error redirectError{&out}; + + Vector4 invalidPlane{2.0f, 2.0f, 2.0f, 0.0f}; + Distance::pointPlaneNormalized({}, invalidPlane); + CORRADE_COMPARE(out.str(), "Math::Distance::pointPlaneNormalized(): plane normal Vector(2, 2, 2) is not normalized\n"); } }}} diff --git a/src/Magnum/Math/Test/DualComplexTest.cpp b/src/Magnum/Math/Test/DualComplexTest.cpp index 49a44feac..6fd4e78b1 100644 --- a/src/Magnum/Math/Test/DualComplexTest.cpp +++ b/src/Magnum/Math/Test/DualComplexTest.cpp @@ -82,17 +82,21 @@ struct DualComplexTest: Corrade::TestSuite::Tester { void conjugated(); void inverted(); void invertedNormalized(); + void invertedNormalizedNotNormalized(); void rotation(); void translation(); void combinedTransformParts(); void matrix(); + void matrixNotOrthogonal(); void transformPoint(); void debug(); void configuration(); }; +using namespace Math::Literals; + typedef Math::Deg Deg; typedef Math::Rad Rad; typedef Math::Complex Complex; @@ -134,11 +138,13 @@ DualComplexTest::DualComplexTest() { &DualComplexTest::conjugated, &DualComplexTest::inverted, &DualComplexTest::invertedNormalized, + &DualComplexTest::invertedNormalizedNotNormalized, &DualComplexTest::rotation, &DualComplexTest::translation, &DualComplexTest::combinedTransformParts, &DualComplexTest::matrix, + &DualComplexTest::matrixNotOrthogonal, &DualComplexTest::transformPoint, &DualComplexTest::debug, @@ -358,17 +364,20 @@ void DualComplexTest::invertedNormalized() { DualComplex a({-0.316228f, 0.9486831f}, { 3.0f, -2.5f}); DualComplex b({-0.316228f, -0.9486831f}, {3.320391f, 2.05548f}); - std::ostringstream o; - Error redirectError{&o}; - DualComplex({-1.0f, -2.5f}, {}).invertedNormalized(); - 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::invertedNormalizedNotNormalized() { + std::ostringstream out; + Error redirectError{&out}; + + DualComplex({-1.0f, -2.5f}, {}).invertedNormalized(); + CORRADE_COMPARE(out.str(), "Math::Complex::invertedNormalized(): Complex(-1, -2.5) is not normalized\n"); +} + void DualComplexTest::rotation() { DualComplex a = DualComplex::rotation(Deg(120.0f)); CORRADE_COMPARE(a.length(), 1.0f); @@ -405,14 +414,19 @@ void DualComplexTest::matrix() { Matrix3 m = Matrix3::rotation(Deg(23.0f))*Matrix3::translation({2.0f, 3.0f}); CORRADE_COMPARE(a.toMatrix(), m); + CORRADE_COMPARE(DualComplex::fromMatrix(m), a); +} +void DualComplexTest::matrixNotOrthogonal() { std::ostringstream o; Error redirectError{&o}; - DualComplex::fromMatrix(m*2); - CORRADE_COMPARE(o.str(), "Math::DualComplex::fromMatrix(): the matrix doesn't represent rigid transformation\n"); - DualComplex b = DualComplex::fromMatrix(m); - CORRADE_COMPARE(b, a); + DualComplex::fromMatrix(Matrix3::rotation(23.0_degf)*Matrix3::translation({2.0f, 3.0f})*2); + CORRADE_COMPARE(o.str(), + "Math::DualComplex::fromMatrix(): the matrix doesn't represent rigid transformation:\n" + "Matrix(1.84101, -0.781462, 1.33763,\n" + " 0.781462, 1.84101, 7.08595,\n" + " 0, 0, 2)\n"); } void DualComplexTest::transformPoint() { diff --git a/src/Magnum/Math/Test/DualQuaternionTest.cpp b/src/Magnum/Math/Test/DualQuaternionTest.cpp index 041b83b54..9aceedcba 100644 --- a/src/Magnum/Math/Test/DualQuaternionTest.cpp +++ b/src/Magnum/Math/Test/DualQuaternionTest.cpp @@ -83,13 +83,17 @@ struct DualQuaternionTest: Corrade::TestSuite::Tester { void conjugated(); void inverted(); void invertedNormalized(); + void invertedNormalizedNotNormalized(); void rotation(); + void rotationNotNormalized(); void translation(); void combinedTransformParts(); void matrix(); + void matrixNotOrthogonal(); void transformPoint(); void transformPointNormalized(); + void transformPointNormalizedNotNormalized(); void sclerp(); void sclerpShortestPath(); @@ -140,13 +144,17 @@ DualQuaternionTest::DualQuaternionTest() { &DualQuaternionTest::conjugated, &DualQuaternionTest::inverted, &DualQuaternionTest::invertedNormalized, + &DualQuaternionTest::invertedNormalizedNotNormalized, &DualQuaternionTest::rotation, + &DualQuaternionTest::rotationNotNormalized, &DualQuaternionTest::translation, &DualQuaternionTest::combinedTransformParts, &DualQuaternionTest::matrix, + &DualQuaternionTest::matrixNotOrthogonal, &DualQuaternionTest::transformPoint, &DualQuaternionTest::transformPointNormalized, + &DualQuaternionTest::transformPointNormalizedNotNormalized, &DualQuaternionTest::sclerp, &DualQuaternionTest::sclerpShortestPath, @@ -391,11 +399,6 @@ void DualQuaternionTest::invertedNormalized() { DualQuaternion a({{ 1.0f, 2.0f, 3.0f}, -4.0f}, {{ 2.5f, -3.1f, 3.3f}, 2.0f}); DualQuaternion b({{-1.0f, -2.0f, -3.0f}, -4.0f}, {{-2.5f, 3.1f, -3.3f}, 2.0f}); - std::ostringstream o; - Error redirectError{&o}; - CORRADE_COMPARE(a.invertedNormalized(), DualQuaternion()); - CORRADE_COMPARE(o.str(), "Math::DualQuaternion::invertedNormalized(): dual quaternion must be normalized\n"); - DualQuaternion normalized = a.normalized(); DualQuaternion inverted = normalized.invertedNormalized(); CORRADE_COMPARE(normalized*inverted, DualQuaternion()); @@ -403,14 +406,16 @@ void DualQuaternionTest::invertedNormalized() { CORRADE_COMPARE(inverted, b/Math::sqrt(Dual(30.0f, -3.6f))); } -void DualQuaternionTest::rotation() { - std::ostringstream o; - Error redirectError{&o}; +void DualQuaternionTest::invertedNormalizedNotNormalized() { + std::ostringstream out; + Error redirectError{&out}; - Vector3 axis(1.0f/Constants::sqrt3()); + DualQuaternion({{ 1.0f, 2.0f, 3.0f}, -4.0f}, {{ 2.5f, -3.1f, 3.3f}, 2.0f}).invertedNormalized(); + CORRADE_COMPARE(out.str(), "Math::DualQuaternion::invertedNormalized(): DualQuaternion({{1, 2, 3}, -4}, {{2.5, -3.1, 3.3}, 2}) is not normalized\n"); +} - CORRADE_COMPARE(DualQuaternion::rotation(Deg(120.0f), axis*2.0f), DualQuaternion()); - CORRADE_COMPARE(o.str(), "Math::Quaternion::rotation(): axis must be normalized\n"); +void DualQuaternionTest::rotation() { + Vector3 axis(1.0f/Constants::sqrt3()); DualQuaternion q = DualQuaternion::rotation(Deg(120.0f), axis); CORRADE_COMPARE(q.length(), 1.0f); @@ -424,6 +429,14 @@ void DualQuaternionTest::rotation() { CORRADE_COMPARE(c, Quaternion({-1.0f, 2.0f, 3.0f}, 4.0f)); } +void DualQuaternionTest::rotationNotNormalized() { + std::ostringstream out; + Error redirectError{&out}; + + DualQuaternion::rotation(120.0_degf, Vector3(2.0f)); + CORRADE_COMPARE(out.str(), "Math::Quaternion::rotation(): axis Vector(2, 2, 2) is not normalized\n"); +} + void DualQuaternionTest::translation() { Vector3 vec(1.0f, -3.5f, 0.5f); DualQuaternion q = DualQuaternion::translation(vec); @@ -454,13 +467,20 @@ void DualQuaternionTest::matrix() { CORRADE_COMPARE(q.toMatrix(), m); CORRADE_COMPARE((-q).toMatrix(), m); - std::ostringstream o; - Error redirectError{&o}; - DualQuaternion::fromMatrix(m*2); - CORRADE_COMPARE(o.str(), "Math::DualQuaternion::fromMatrix(): the matrix doesn't represent rigid transformation\n"); + CORRADE_COMPARE(DualQuaternion::fromMatrix(m), q); +} - DualQuaternion p = DualQuaternion::fromMatrix(m); - CORRADE_COMPARE(p, q); +void DualQuaternionTest::matrixNotOrthogonal() { + std::ostringstream out; + Error redirectError{&out}; + + DualQuaternion::fromMatrix(Matrix4::rotationX(23.0_degf)*Matrix4::translation({-1.0f, 2.0f, 3.0f})*2); + CORRADE_COMPARE(out.str(), + "Math::DualQuaternion::fromMatrix(): the matrix doesn't represent a rigid transformation:\n" + "Matrix(2, 0, 0, -2,\n" + " 0, 1.84101, -0.781462, 1.33763,\n" + " 0, 0.781462, 1.84101, 7.08595,\n" + " 0, 0, 0, 2)\n"); } void DualQuaternionTest::transformPoint() { @@ -486,11 +506,6 @@ void DualQuaternionTest::transformPointNormalized() { Matrix4 n = Matrix4::rotationX(Deg(23.0f))*Matrix4::translation({-1.0f, 2.0f, 3.0f}); Vector3 v(0.0f, -3.6f, 0.7f); - std::ostringstream o; - Error redirectError{&o}; - (a*Dual(2)).transformPointNormalized(v); - CORRADE_COMPARE(o.str(), "Math::DualQuaternion::transformPointNormalized(): dual quaternion must be normalized\n"); - Vector3 transformedA = a.transformPointNormalized(v); CORRADE_COMPARE(transformedA, m.transformPoint(v)); CORRADE_COMPARE(transformedA, Vector3(-1.0f, -1.58733f, 2.237721f)); @@ -500,6 +515,15 @@ void DualQuaternionTest::transformPointNormalized() { CORRADE_COMPARE(transformedB, Vector3(-1.0f, -2.918512f, 2.780698f)); } +void DualQuaternionTest::transformPointNormalizedNotNormalized() { + std::ostringstream out; + Error redirectError{&out}; + + DualQuaternion a = DualQuaternion::translation({-1.0f, 2.0f, 3.0f})*DualQuaternion::rotation(Deg(23.0f), Vector3::xAxis()); + (a*Dual(2)).transformPointNormalized({}); + CORRADE_COMPARE(out.str(), "Math::DualQuaternion::transformPointNormalized(): DualQuaternion({{0.398736, 0, 0}, 1.95985}, {{-0.979925, 2.55795, 2.54104}, 0.199368}) is not normalized\n"); +} + void DualQuaternionTest::sclerp() { auto from = DualQuaternion::translation({20.0f, 0.0f, 0.0f})* DualQuaternion::rotation(65.0_degf, Vector3::yAxis()); diff --git a/src/Magnum/Math/Test/IntersectionTest.cpp b/src/Magnum/Math/Test/IntersectionTest.cpp index 23318335b..386ceae4a 100644 --- a/src/Magnum/Math/Test/IntersectionTest.cpp +++ b/src/Magnum/Math/Test/IntersectionTest.cpp @@ -48,6 +48,7 @@ struct IntersectionTest: Corrade::TestSuite::Tester { void pointDoubleCone(); void sphereCone(); void sphereConeView(); + void sphereConeViewNotRigid(); void rangeCone(); void aabbCone(); }; @@ -77,6 +78,7 @@ IntersectionTest::IntersectionTest() { &IntersectionTest::pointDoubleCone, &IntersectionTest::sphereCone, &IntersectionTest::sphereConeView, + &IntersectionTest::sphereConeViewNotRigid, &IntersectionTest::rangeCone, &IntersectionTest::aabbCone}); } @@ -377,11 +379,19 @@ void IntersectionTest::sphereConeView() { CORRADE_VERIFY(!Intersection::sphereConeView(center - surface + sNormal*0.25f, 0.5f, coneView, angle)); CORRADE_VERIFY(!Intersection::sphereConeView(center - 4.0f*surface, 0.5f, coneView, angle)); +} + +void IntersectionTest::sphereConeViewNotRigid() { std::ostringstream out; Error redirectError{&out}; - CORRADE_VERIFY(!Intersection::sphereConeView(center, 1.0f, Matrix4{ZeroInit}, angle)); - CORRADE_COMPARE(out.str(), "Math::Geometry::Intersection::sphereConeView(): coneView does not represent a rigid transformation\n"); + CORRADE_VERIFY(!Intersection::sphereConeView({}, 1.0f, Matrix4{ZeroInit}, {})); + CORRADE_COMPARE(out.str(), + "Math::Intersection::sphereConeView(): coneView does not represent a rigid transformation:\n" + "Matrix(0, 0, 0, 0,\n" + " 0, 0, 0, 0,\n" + " 0, 0, 0, 0,\n" + " 0, 0, 0, 0)\n"); } void IntersectionTest::rangeCone() { diff --git a/src/Magnum/Math/Test/Matrix3Test.cpp b/src/Magnum/Math/Test/Matrix3Test.cpp index c9937ef57..edbf6d293 100644 --- a/src/Magnum/Math/Test/Matrix3Test.cpp +++ b/src/Magnum/Math/Test/Matrix3Test.cpp @@ -75,6 +75,7 @@ struct Matrix3Test: Corrade::TestSuite::Tester { void scaling(); void rotation(); void reflection(); + void reflectionNotNormalized(); void reflectionIsScaling(); void shearingX(); void shearingY(); @@ -92,6 +93,7 @@ struct Matrix3Test: Corrade::TestSuite::Tester { void uniformScalingPartNotUniform(); void vectorParts(); void invertedRigid(); + void invertedRigidNotRigid(); void transform(); void debug(); @@ -122,6 +124,7 @@ Matrix3Test::Matrix3Test() { &Matrix3Test::scaling, &Matrix3Test::rotation, &Matrix3Test::reflection, + &Matrix3Test::reflectionNotNormalized, &Matrix3Test::reflectionIsScaling, &Matrix3Test::shearingX, &Matrix3Test::shearingY, @@ -139,6 +142,7 @@ Matrix3Test::Matrix3Test() { &Matrix3Test::uniformScalingPartNotUniform, &Matrix3Test::vectorParts, &Matrix3Test::invertedRigid, + &Matrix3Test::invertedRigidNotRigid, &Matrix3Test::transform, &Matrix3Test::debug, @@ -328,14 +332,7 @@ void Matrix3Test::rotation() { } void Matrix3Test::reflection() { - std::ostringstream o; - Error redirectError{&o}; - Vector2 normal(-1.0f, 2.0f); - - CORRADE_COMPARE(Matrix3::reflection(normal), Matrix3()); - CORRADE_COMPARE(o.str(), "Math::Matrix3::reflection(): normal must be normalized\n"); - Matrix3 actual = Matrix3::reflection(normal.normalized()); Matrix3 expected({0.6f, 0.8f, 0.0f}, {0.8f, -0.6f, 0.0f}, @@ -346,6 +343,14 @@ void Matrix3Test::reflection() { CORRADE_COMPARE(actual, expected); } +void Matrix3Test::reflectionNotNormalized() { + std::ostringstream out; + Error redirectError{&out}; + + Matrix3::reflection({-1.0f, 2.0f}); + CORRADE_COMPARE(out.str(), "Math::Matrix3::reflection(): normal Vector(-1, 2) is not normalized\n"); +} + void Matrix3Test::reflectionIsScaling() { CORRADE_COMPARE(Matrix3::reflection(Vector2::yAxis()), Matrix3::scaling(Vector2::yScale(-1.0f))); } @@ -458,8 +463,12 @@ void Matrix3Test::rotationPartNotOrthogonal() { Matrix3::scaling(Vector2::yScale(0.0f)).rotation(); CORRADE_COMPARE(out.str(), - "Math::Matrix3::rotation(): the normalized rotation part is not orthogonal\n" - "Math::Matrix3::rotation(): the normalized rotation part is not orthogonal\n"); + "Math::Matrix3::rotation(): the normalized rotation part is not orthogonal:\n" + "Matrix(1, 0.83205,\n" + " 0, 0.5547)\n" + "Math::Matrix3::rotation(): the normalized rotation part is not orthogonal:\n" + "Matrix(1, -nan,\n" + " 0, -nan)\n"); } void Matrix3Test::rotationNormalizedPart() { @@ -480,7 +489,10 @@ void Matrix3Test::rotationNormalizedPartNotOrthogonal() { {7.0f, -1.0f, 8.0f}); a.rotationNormalized(); - CORRADE_COMPARE(out.str(), "Math::Matrix3::rotationNormalized(): the rotation part is not orthogonal\n"); + CORRADE_COMPARE(out.str(), + "Math::Matrix3::rotationNormalized(): the rotation part is not orthogonal:\n" + "Matrix(1, 1,\n" + " 0, 0.1)\n"); } void Matrix3Test::scalingPart() { @@ -502,7 +514,10 @@ void Matrix3Test::uniformScalingPartNotUniform() { std::ostringstream out; Error redirectError{&out}; Matrix3::scaling(Vector2::yScale(3.0f)).uniformScaling(); - CORRADE_COMPARE(out.str(), "Math::Matrix3::uniformScaling(): the matrix doesn't have uniform scaling\n"); + CORRADE_COMPARE(out.str(), + "Math::Matrix3::uniformScaling(): the matrix doesn't have uniform scaling:\n" + "Matrix(1, 0,\n" + " 0, 3)\n"); } void Matrix3Test::vectorParts() { @@ -535,15 +550,22 @@ void Matrix3Test::invertedRigid() { Matrix3::reflection(Vector2(0.5f, -2.0f).normalized())* Matrix3::rotation(Deg(74.0f)); - std::ostringstream o; - Error redirectError{&o}; - (2*actual).invertedRigid(); - CORRADE_COMPARE(o.str(), "Math::Matrix3::invertedRigid(): the matrix doesn't represent rigid transformation\n"); - CORRADE_COMPARE(actual.invertedRigid(), expected); CORRADE_COMPARE(actual.invertedRigid(), actual.inverted()); } +void Matrix3Test::invertedRigidNotRigid() { + std::ostringstream out; + Error redirectError{&out}; + (Matrix3::rotation(60.0_degf)*2.0f).invertedRigid(); + + CORRADE_COMPARE(out.str(), + "Math::Matrix3::invertedRigid(): the matrix doesn't represent a rigid transformation:\n" + "Matrix(1, -1.73205, 0,\n" + " 1.73205, 1, 0,\n" + " 0, 0, 2)\n"); +} + void Matrix3Test::transform() { Matrix3 a = Matrix3::translation({1.0f, -5.0f})*Matrix3::rotation(Deg(90.0f)); Vector2 v(1.0f, -2.0f); diff --git a/src/Magnum/Math/Test/Matrix4Test.cpp b/src/Magnum/Math/Test/Matrix4Test.cpp index 785544913..5cbf0279f 100644 --- a/src/Magnum/Math/Test/Matrix4Test.cpp +++ b/src/Magnum/Math/Test/Matrix4Test.cpp @@ -77,10 +77,12 @@ struct Matrix4Test: Corrade::TestSuite::Tester { void translation(); void scaling(); void rotation(); + void rotationNotNormalized(); void rotationX(); void rotationY(); void rotationZ(); void reflection(); + void reflectionNotNormalized(); void reflectionIsScaling(); void shearingXY(); void shearingXZ(); @@ -104,6 +106,7 @@ struct Matrix4Test: Corrade::TestSuite::Tester { void uniformScalingPartNotUniform(); void vectorParts(); void invertedRigid(); + void invertedRigidNotRigid(); void transform(); void transformProjection(); @@ -138,10 +141,12 @@ Matrix4Test::Matrix4Test() { &Matrix4Test::translation, &Matrix4Test::scaling, &Matrix4Test::rotation, + &Matrix4Test::rotationNotNormalized, &Matrix4Test::rotationX, &Matrix4Test::rotationY, &Matrix4Test::rotationZ, &Matrix4Test::reflection, + &Matrix4Test::reflectionNotNormalized, &Matrix4Test::reflectionIsScaling, &Matrix4Test::shearingXY, &Matrix4Test::shearingXZ, @@ -165,6 +170,7 @@ Matrix4Test::Matrix4Test() { &Matrix4Test::uniformScalingPartNotUniform, &Matrix4Test::vectorParts, &Matrix4Test::invertedRigid, + &Matrix4Test::invertedRigidNotRigid, &Matrix4Test::transform, &Matrix4Test::transformProjection, @@ -368,12 +374,6 @@ void Matrix4Test::scaling() { } void Matrix4Test::rotation() { - std::ostringstream o; - Error redirectError{&o}; - - CORRADE_COMPARE(Matrix4::rotation(Deg(-74.0f), {-1.0f, 2.0f, 2.0f}), Matrix4()); - CORRADE_COMPARE(o.str(), "Math::Matrix4::rotation(): axis must be normalized\n"); - Matrix4 matrix({ 0.35612202f, -0.80181062f, 0.47987163f, 0.0f}, { 0.47987163f, 0.59757626f, 0.6423596f, 0.0f}, {-0.80181062f, 0.00151846f, 0.59757626f, 0.0f}, @@ -381,6 +381,14 @@ void Matrix4Test::rotation() { CORRADE_COMPARE(Matrix4::rotation(Deg(-74.0f), Vector3(-1.0f, 2.0f, 2.0f).normalized()), matrix); } +void Matrix4Test::rotationNotNormalized() { + std::ostringstream out; + Error redirectError{&out}; + + Matrix4::rotation(Deg(-74.0f), {-1.0f, 2.0f, 2.0f}); + CORRADE_COMPARE(out.str(), "Math::Matrix4::rotation(): axis Vector(-1, 2, 2) is not normalized\n"); +} + void Matrix4Test::rotationX() { Matrix4 matrix({1.0f, 0.0f, 0.0f, 0.0f}, {0.0f, 0.90096887f, 0.43388374f, 0.0f}, @@ -409,14 +417,7 @@ void Matrix4Test::rotationZ() { } void Matrix4Test::reflection() { - std::ostringstream o; - Error redirectError{&o}; - Vector3 normal(-1.0f, 2.0f, 2.0f); - - CORRADE_COMPARE(Matrix4::reflection(normal), Matrix4()); - CORRADE_COMPARE(o.str(), "Math::Matrix4::reflection(): normal must be normalized\n"); - Matrix4 actual = Matrix4::reflection(normal.normalized()); Matrix4 expected({0.777778f, 0.444444f, 0.444444f, 0.0f}, {0.444444f, 0.111111f, -0.888889f, 0.0f}, @@ -428,6 +429,14 @@ void Matrix4Test::reflection() { CORRADE_COMPARE(actual, expected); } +void Matrix4Test::reflectionNotNormalized() { + std::ostringstream out; + Error redirectError{&out}; + + Matrix4::reflection({-1.0f, 2.0f, 2.0f}); + CORRADE_COMPARE(out.str(), "Math::Matrix4::reflection(): normal Vector(-1, 2, 2) is not normalized\n"); +} + void Matrix4Test::reflectionIsScaling() { CORRADE_COMPARE(Matrix4::reflection(Vector3::yAxis()), Matrix4::scaling(Vector3::yScale(-1.0f))); } @@ -609,8 +618,14 @@ void Matrix4Test::rotationPartNotOrthogonal() { Matrix4::scaling(Vector3::yScale(0.0f)).rotation(); CORRADE_COMPARE(out.str(), - "Math::Matrix4::rotation(): the normalized rotation part is not orthogonal\n" - "Math::Matrix4::rotation(): the normalized rotation part is not orthogonal\n"); + "Math::Matrix4::rotation(): the normalized rotation part is not orthogonal:\n" + "Matrix(1, 0, 0.83205,\n" + " 0, 1, 0,\n" + " 0, 0, 0.5547)\n" + "Math::Matrix4::rotation(): the normalized rotation part is not orthogonal:\n" + "Matrix(1, -nan, 0,\n" + " 0, -nan, 0,\n" + " 0, -nan, 1)\n"); } void Matrix4Test::rotationNormalizedPart() { @@ -633,7 +648,11 @@ void Matrix4Test::rotationNormalizedPartNotOrthogonal() { {0.0f, -1.0f, 0.1f, 0.0f}, {9.0f, 4.0f, 5.0f, 9.0f}); a.rotationNormalized(); - CORRADE_COMPARE(out.str(), "Math::Matrix4::rotationNormalized(): the rotation part is not orthogonal\n"); + CORRADE_COMPARE(out.str(), + "Math::Matrix4::rotationNormalized(): the rotation part is not orthogonal:\n" + "Matrix(0, 1, 0,\n" + " 0, 0, -1,\n" + " 1, 0, 0.1)\n"); } void Matrix4Test::scalingPart() { @@ -654,7 +673,10 @@ void Matrix4Test::uniformScalingPart() { void Matrix4Test::uniformScalingPartNotUniform() { std::ostringstream out; Error redirectError{&out}; Matrix4::scaling(Vector3::yScale(3.0f)).uniformScaling(); - CORRADE_COMPARE(out.str(), "Math::Matrix4::uniformScaling(): the matrix doesn't have uniform scaling\n"); + CORRADE_COMPARE(out.str(), "Math::Matrix4::uniformScaling(): the matrix doesn't have uniform scaling:\n" + "Matrix(1, 0, 0,\n" + " 0, 3, 0,\n" + " 0, 0, 1)\n"); } void Matrix4Test::vectorParts() { @@ -693,15 +715,24 @@ void Matrix4Test::invertedRigid() { Matrix4::reflection(Vector3(0.5f, -2.0f, 2.0f).normalized())* Matrix4::rotation(Deg(74.0f), Vector3(-1.0f, 0.5f, 2.0f).normalized()); - std::ostringstream o; - Error redirectError{&o}; - (2*actual).invertedRigid(); - CORRADE_COMPARE(o.str(), "Math::Matrix4::invertedRigid(): the matrix doesn't represent rigid transformation\n"); CORRADE_COMPARE(actual.invertedRigid(), expected); CORRADE_COMPARE(actual.invertedRigid(), actual.inverted()); } +void Matrix4Test::invertedRigidNotRigid() { + std::ostringstream out; + Error redirectError{&out}; + + (Matrix4::rotationX(-60.0_degf)*2.0f).invertedRigid(); + CORRADE_COMPARE(out.str(), + "Math::Matrix4::invertedRigid(): the matrix doesn't represent a rigid transformation:\n" + "Matrix(2, 0, 0, 0,\n" + " 0, 1, 1.73205, 0,\n" + " 0, -1.73205, 1, 0,\n" + " 0, 0, 0, 2)\n"); +} + void Matrix4Test::transform() { Matrix4 a = Matrix4::translation({1.0f, -5.0f, 3.5f})*Matrix4::rotation(Deg(90.0f), Vector3::zAxis()); Vector3 v(1.0f, -2.0f, 5.5f); diff --git a/src/Magnum/Math/Test/MatrixTest.cpp b/src/Magnum/Math/Test/MatrixTest.cpp index 4db8e4028..f1ddb5c2e 100644 --- a/src/Magnum/Math/Test/MatrixTest.cpp +++ b/src/Magnum/Math/Test/MatrixTest.cpp @@ -77,6 +77,7 @@ struct MatrixTest: Corrade::TestSuite::Tester { void determinant(); void inverted(); void invertedOrthogonal(); + void invertedOrthogonalNotOrthogonal(); void subclassTypes(); void subclass(); @@ -114,6 +115,7 @@ MatrixTest::MatrixTest() { &MatrixTest::determinant, &MatrixTest::inverted, &MatrixTest::invertedOrthogonal, + &MatrixTest::invertedOrthogonalNotOrthogonal, &MatrixTest::subclassTypes, &MatrixTest::subclass, @@ -360,6 +362,15 @@ void MatrixTest::inverted() { } void MatrixTest::invertedOrthogonal() { + Matrix3x3 a(Vector3(Constants::sqrt3()/2.0f, 0.5f, 0.0f), + Vector3(-0.5f, Constants::sqrt3()/2.0f, 0.0f), + Vector3(0.0f, 0.0f, 1.0f)); + + CORRADE_COMPARE(a.invertedOrthogonal()*a, Matrix3x3()); + CORRADE_COMPARE(a.invertedOrthogonal(), a.inverted()); +} + +void MatrixTest::invertedOrthogonalNotOrthogonal() { std::ostringstream o; Error redirectError{&o}; @@ -367,10 +378,11 @@ void MatrixTest::invertedOrthogonal() { Vector3(-0.5f, Constants::sqrt3()/2.0f, 0.0f), Vector3(0.0f, 0.0f, 1.0f)); (a*2).invertedOrthogonal(); - CORRADE_COMPARE(o.str(), "Math::Matrix::invertedOrthogonal(): the matrix is not orthogonal\n"); - - CORRADE_COMPARE(a.invertedOrthogonal()*a, Matrix3x3()); - CORRADE_COMPARE(a.invertedOrthogonal(), a.inverted()); + CORRADE_COMPARE(o.str(), + "Math::Matrix::invertedOrthogonal(): the matrix is not orthogonal:\n" + "Matrix(1.73205, -1, 0,\n" + " 1, 1.73205, 0,\n" + " 0, 0, 2)\n"); } template class BasicVec2: public Math::Vector<2, T> { diff --git a/src/Magnum/Math/Test/QuaternionTest.cpp b/src/Magnum/Math/Test/QuaternionTest.cpp index 879f14521..946e0d6c5 100644 --- a/src/Magnum/Math/Test/QuaternionTest.cpp +++ b/src/Magnum/Math/Test/QuaternionTest.cpp @@ -70,6 +70,8 @@ struct QuaternionTest: Corrade::TestSuite::Tester { void compare(); void isNormalized(); template void isNormalizedEpsilon(); + void axisAngle(); + void axisAngleNotNormalized(); void addSubtract(); void negated(); @@ -85,10 +87,14 @@ struct QuaternionTest: Corrade::TestSuite::Tester { void conjugated(); void inverted(); void invertedNormalized(); + void invertedNormalizedNotNormalized(); void rotation(); + void rotationNotNormalized(); void angle(); + void angleNotNormalized(); void matrix(); + void matrixNotOrthogonal(); void lerp(); void lerp2D(); @@ -103,6 +109,7 @@ struct QuaternionTest: Corrade::TestSuite::Tester { void transformVector(); void transformVectorNormalized(); + void transformVectorNormalizedNotNormalized(); void debug(); void configuration(); @@ -134,6 +141,8 @@ QuaternionTest::QuaternionTest() { &QuaternionTest::isNormalized, &QuaternionTest::isNormalizedEpsilon, &QuaternionTest::isNormalizedEpsilon, + &QuaternionTest::axisAngle, + &QuaternionTest::axisAngleNotNormalized, &QuaternionTest::addSubtract, &QuaternionTest::negated, @@ -152,10 +161,14 @@ QuaternionTest::QuaternionTest() { addTests({&QuaternionTest::conjugated, &QuaternionTest::inverted, &QuaternionTest::invertedNormalized, + &QuaternionTest::invertedNormalizedNotNormalized, &QuaternionTest::rotation, + &QuaternionTest::rotationNotNormalized, &QuaternionTest::angle, + &QuaternionTest::angleNotNormalized, &QuaternionTest::matrix, + &QuaternionTest::matrixNotOrthogonal, &QuaternionTest::lerp, &QuaternionTest::lerp2D, @@ -170,6 +183,7 @@ QuaternionTest::QuaternionTest() { &QuaternionTest::transformVector, &QuaternionTest::transformVectorNormalized, + &QuaternionTest::transformVectorNormalizedNotNormalized, &QuaternionTest::debug, &QuaternionTest::configuration}); @@ -312,6 +326,24 @@ template void QuaternionTest::isNormalizedEpsilon() { CORRADE_VERIFY(!(Math::Quaternion{{T(0.0106550719778129), T(0.311128101752138), T(-0.0468823167023769)}, T(0.949151106053128) + TypeTraits::epsilon()*T(2.0)}.isNormalized())); } +void QuaternionTest::axisAngle() { + Quaternion a = Quaternion::rotation(23.0_degf, {0.6f, -0.8f, 0.0f}); + CORRADE_COMPARE(a.angle(), 23.0_degf); + CORRADE_COMPARE(a.axis(), (Vector3{0.6f, -0.8f, 0.0f})); +} + +void QuaternionTest::axisAngleNotNormalized() { + std::ostringstream out; + Error redirectError{&out}; + + Quaternion a = Quaternion::rotation(23.0_degf, {0.6f, -0.8f, 0.0f})*2; + a.angle(); + a.axis(); + CORRADE_COMPARE(out.str(), + "Math::Quaternion::angle(): Quaternion({0.239242, -0.318989, 0}, 1.95985) is not normalized\n" + "Math::Quaternion::axis(): Quaternion({0.239242, -0.318989, 0}, 1.95985) is not normalized\n"); +} + void QuaternionTest::addSubtract() { Quaternion a({ 1.0f, 3.0f, -2.0f}, -4.0f); Quaternion b({-0.5f, 1.4f, 3.0f}, 12.0f); @@ -390,29 +422,24 @@ void QuaternionTest::inverted() { } void QuaternionTest::invertedNormalized() { - Quaternion a = Quaternion({1.0f, 3.0f, -2.0f}, -4.0f); + Quaternion a = Quaternion{{1.0f, 3.0f, -2.0f}, -4.0f}.normalized(); - std::ostringstream o; - Error redirectError{&o}; - a.invertedNormalized(); - CORRADE_COMPARE(o.str(), "Math::Quaternion::invertedNormalized(): quaternion must be normalized\n"); - - Quaternion aNormalized = a.normalized(); - Quaternion inverted = aNormalized.invertedNormalized(); - CORRADE_COMPARE(aNormalized*inverted, Quaternion()); - CORRADE_COMPARE(inverted*aNormalized, Quaternion()); + Quaternion inverted = a.invertedNormalized(); + CORRADE_COMPARE(a*inverted, Quaternion()); + CORRADE_COMPARE(inverted*a, Quaternion()); CORRADE_COMPARE(inverted, Quaternion({-1.0f, -3.0f, 2.0f}, -4.0f)/std::sqrt(30.0f)); } -void QuaternionTest::rotation() { - std::ostringstream o; - Error redirectError{&o}; - - Vector3 axis(1.0f/Constants::sqrt3()); +void QuaternionTest::invertedNormalizedNotNormalized() { + std::ostringstream out; + Error redirectError{&out}; - CORRADE_COMPARE(Quaternion::rotation(Deg(-74.0f), {-1.0f, 2.0f, 2.0f}), Quaternion()); - CORRADE_COMPARE(o.str(), "Math::Quaternion::rotation(): axis must be normalized\n"); + Quaternion{{1.0f, 3.0f, -2.0f}, -4.0f}.invertedNormalized(); + CORRADE_COMPARE(out.str(), "Math::Quaternion::invertedNormalized(): Quaternion({1, 3, -2}, -4) is not normalized\n"); +} +void QuaternionTest::rotation() { + Vector3 axis(1.0f/Constants::sqrt3()); Quaternion q = Quaternion::rotation(Deg(120.0f), axis); CORRADE_COMPARE(q.length(), 1.0f); CORRADE_COMPARE(q, Quaternion(Vector3(0.5f, 0.5f, 0.5f), 0.5f)); @@ -431,24 +458,35 @@ void QuaternionTest::rotation() { CORRADE_VERIFY(Quaternion().axis() != Quaternion().axis()); } -void QuaternionTest::angle() { - std::ostringstream o; - Error redirectError{&o}; - Math::angle(Quaternion({1.0f, 2.0f, -3.0f}, -4.0f).normalized(), {{4.0f, -3.0f, 2.0f}, -1.0f}); - CORRADE_COMPARE(o.str(), "Math::angle(): quaternions must be normalized\n"); +void QuaternionTest::rotationNotNormalized() { + std::ostringstream out; + Error redirectError{&out}; - o.str({}); - Math::angle({{1.0f, 2.0f, -3.0f}, -4.0f}, Quaternion({4.0f, -3.0f, 2.0f}, -1.0f).normalized()); - CORRADE_COMPARE(o.str(), "Math::angle(): quaternions must be normalized\n"); + Quaternion::rotation(-74.0_degf, {-1.0f, 2.0f, 2.0f}); + CORRADE_COMPARE(out.str(), "Math::Quaternion::rotation(): axis Vector(-1, 2, 2) is not normalized\n"); +} +void QuaternionTest::angle() { /* Verify also that the angle is the same as angle between 4D vectors */ Rad angle = Math::angle(Quaternion({1.0f, 2.0f, -3.0f}, -4.0f).normalized(), Quaternion({4.0f, -3.0f, 2.0f}, -1.0f).normalized()); CORRADE_COMPARE(angle, Math::angle(Vector4(1.0f, 2.0f, -3.0f, -4.0f).normalized(), - Vector4(4.0f, -3.0f, 2.0f, -1.0f).normalized())); + Vector4(4.0f, -3.0f, 2.0f, -1.0f).normalized())); CORRADE_COMPARE(angle, Rad(1.704528f)); } +void QuaternionTest::angleNotNormalized() { + std::ostringstream out; + Error redirectError{&out}; + + Math::angle(Quaternion{{1.0f, 2.0f, -3.0f}, -4.0f}.normalized(), {{4.0f, -3.0f, 2.0f}, -1.0f}); + Math::angle({{1.0f, 2.0f, -3.0f}, -4.0f}, Quaternion{{4.0f, -3.0f, 2.0f}, -1.0f}.normalized()); + + CORRADE_COMPARE(out.str(), + "Math::angle(): quaternions Quaternion({0.182574, 0.365148, -0.547723}, -0.730297) and Quaternion({4, -3, 2}, -1) are not normalized\n" + "Math::angle(): quaternions Quaternion({1, 2, -3}, -4) and Quaternion({0.730297, -0.547723, 0.365148}, -0.182574) are not normalized\n"); +} + void QuaternionTest::matrix() { Vector3 axis = Vector3(-3.0f, 1.0f, 5.0f).normalized(); @@ -459,11 +497,6 @@ void QuaternionTest::matrix() { CORRADE_COMPARE(q.toMatrix(), m); CORRADE_COMPARE((-q).toMatrix(), m); - std::ostringstream o; - Error redirectError{&o}; - Quaternion::fromMatrix(m*2); - CORRADE_COMPARE(o.str(), "Math::Quaternion::fromMatrix(): the matrix is not orthogonal\n"); - /* Trace > 0 */ CORRADE_COMPARE_AS(m.trace(), 0.0f, Corrade::TestSuite::Compare::Greater); CORRADE_COMPARE(Quaternion::fromMatrix(m), q); @@ -498,6 +531,20 @@ void QuaternionTest::matrix() { CORRADE_COMPARE(Quaternion::fromMatrix(m4), q4); } +void QuaternionTest::matrixNotOrthogonal() { + std::ostringstream out; + Error redirectError{&out}; + + Vector3 axis = Vector3(-3.0f, 1.0f, 5.0f).normalized(); + Matrix3x3 m = Matrix4::rotation(37.0_degf, axis).rotationScaling(); + Quaternion::fromMatrix(m*2); + CORRADE_COMPARE(out.str(), + "Math::Quaternion::fromMatrix(): the matrix is not orthogonal:\n" + "Matrix(1.70083, -1.05177, 0.0308525,\n" + " 0.982733, 1.60878, 0.667885,\n" + " -0.376049, -0.552819, 1.88493)\n"); +} + void QuaternionTest::lerp() { Quaternion a = Quaternion::rotation(15.0_degf, Vector3(1.0f/Constants::sqrt3())); Quaternion b = Quaternion::rotation(23.0_degf, Vector3::xAxis()); @@ -532,8 +579,8 @@ void QuaternionTest::lerpNotNormalized() { Math::lerp(a*3.0f, a, 0.35f); Math::lerp(a, a*-3.0f, 0.35f); CORRADE_COMPARE(out.str(), - "Math::lerp(): quaternions must be normalized\n" - "Math::lerp(): quaternions must be normalized\n"); + "Math::lerp(): quaternions Quaternion({0, 0, 0}, 3) and Quaternion({0, 0, 0}, 1) are not normalized\n" + "Math::lerp(): quaternions Quaternion({0, 0, 0}, 1) and Quaternion({-0, -0, -0}, -3) are not normalized\n"); } void QuaternionTest::lerpShortestPath() { @@ -563,8 +610,8 @@ void QuaternionTest::lerpShortestPathNotNormalized() { Math::lerpShortestPath(a, a*-3.0f, 0.35f); /* lerpShortestPath() is calling lerp(), so the message is from there */ CORRADE_COMPARE(out.str(), - "Math::lerp(): quaternions must be normalized\n" - "Math::lerp(): quaternions must be normalized\n"); + "Math::lerp(): quaternions Quaternion({0, 0, 0}, 3) and Quaternion({0, 0, 0}, 1) are not normalized\n" + "Math::lerp(): quaternions Quaternion({-0, -0, -0}, -1) and Quaternion({-0, -0, -0}, -3) are not normalized\n"); } void QuaternionTest::slerp() { @@ -607,8 +654,8 @@ void QuaternionTest::slerpNotNormalized() { Math::slerp(a*3.0f, a, 0.35f); Math::slerp(a, a*-3.0f, 0.35f); CORRADE_COMPARE(out.str(), - "Math::slerp(): quaternions must be normalized\n" - "Math::slerp(): quaternions must be normalized\n"); + "Math::slerp(): quaternions Quaternion({0, 0, 0}, 3) and Quaternion({0, 0, 0}, 1) are not normalized\n" + "Math::slerp(): quaternions Quaternion({0, 0, 0}, 1) and Quaternion({-0, -0, -0}, -3) are not normalized\n"); } void QuaternionTest::slerpShortestPath() { @@ -637,8 +684,8 @@ void QuaternionTest::slerpShortestPathNotNormalized() { Math::slerpShortestPath(a*3.0f, a, 0.35f); Math::slerpShortestPath(a, a*-3.0f, 0.35f); CORRADE_COMPARE(out.str(), - "Math::slerpShortestPath(): quaternions must be normalized\n" - "Math::slerpShortestPath(): quaternions must be normalized\n"); + "Math::slerpShortestPath(): quaternions Quaternion({0, 0, 0}, 3) and Quaternion({0, 0, 0}, 1) are not normalized\n" + "Math::slerpShortestPath(): quaternions Quaternion({0, 0, 0}, 1) and Quaternion({-0, -0, -0}, -3) are not normalized\n"); } void QuaternionTest::transformVector() { @@ -655,17 +702,20 @@ void QuaternionTest::transformVectorNormalized() { Quaternion a = Quaternion::rotation(Deg(23.0f), Vector3::xAxis()); Matrix4 m = Matrix4::rotationX(Deg(23.0f)); Vector3 v(5.0f, -3.6f, 0.7f); - - std::ostringstream o; - Error redirectError{&o}; - (a*2).transformVectorNormalized(v); - CORRADE_COMPARE(o.str(), "Math::Quaternion::transformVectorNormalized(): quaternion must be normalized\n"); - Vector3 rotated = a.transformVectorNormalized(v); CORRADE_COMPARE(rotated, m.transformVector(v)); CORRADE_COMPARE(rotated, a.transformVector(v)); } +void QuaternionTest::transformVectorNormalizedNotNormalized() { + std::ostringstream out; + Error redirectError{&out}; + + Quaternion a = Quaternion::rotation(23.0_degf, Vector3::xAxis()); + (a*2).transformVectorNormalized({}); + CORRADE_COMPARE(out.str(), "Math::Quaternion::transformVectorNormalized(): Quaternion({0.398736, 0, 0}, 1.95985) is not normalized\n"); +} + void QuaternionTest::debug() { std::ostringstream o; diff --git a/src/Magnum/Math/Test/VectorTest.cpp b/src/Magnum/Math/Test/VectorTest.cpp index 53a106bcc..78bd118fd 100644 --- a/src/Magnum/Math/Test/VectorTest.cpp +++ b/src/Magnum/Math/Test/VectorTest.cpp @@ -98,9 +98,11 @@ struct VectorTest: Corrade::TestSuite::Tester { void projected(); void projectedOntoNormalized(); + void projectedOntoNormalizedNotNormalized(); void flipped(); void angle(); + void angleNotNormalized(); void subclassTypes(); void subclass(); @@ -159,9 +161,11 @@ VectorTest::VectorTest() { &VectorTest::projected, &VectorTest::projectedOntoNormalized, + &VectorTest::projectedOntoNormalizedNotNormalized, &VectorTest::flipped, &VectorTest::angle, + &VectorTest::angleNotNormalized, &VectorTest::subclassTypes, &VectorTest::subclass, @@ -481,13 +485,8 @@ void VectorTest::projected() { } void VectorTest::projectedOntoNormalized() { - std::ostringstream o; - Error redirectError{&o}; - Vector3 vector(1.0f, 2.0f, 3.0f); Vector3 line(1.0f, -1.0f, 0.5f); - vector.projectedOntoNormalized(line); - CORRADE_COMPARE(o.str(), "Math::Vector::projectedOntoNormalized(): line must be normalized\n"); Vector3 projected = vector.projectedOntoNormalized(line.normalized()); CORRADE_COMPARE(projected, Vector3(0.222222f, -0.222222f, 0.111111f)); @@ -495,6 +494,16 @@ void VectorTest::projectedOntoNormalized() { CORRADE_COMPARE(projected, vector.projected(line)); } +void VectorTest::projectedOntoNormalizedNotNormalized() { + Vector3 vector(1.0f, 2.0f, 3.0f); + Vector3 line(1.0f, -1.0f, 0.5f); + + std::ostringstream out; + Error redirectError{&out}; + vector.projectedOntoNormalized(line); + CORRADE_COMPARE(out.str(), "Math::Vector::projectedOntoNormalized(): line Vector(1, -1, 0.5) is not normalized\n"); +} + void VectorTest::flipped() { constexpr Vector4 vector{1.0f, -3.5f, 2.1f, 0.5f}; constexpr Vector4 flipped = vector.flipped(); @@ -502,20 +511,22 @@ void VectorTest::flipped() { } void VectorTest::angle() { - std::ostringstream o; - Error redirectError{&o}; - Math::angle(Vector3(2.0f, 3.0f, 4.0f).normalized(), {1.0f, -2.0f, 3.0f}); - CORRADE_COMPARE(o.str(), "Math::angle(): vectors must be normalized\n"); - - o.str({}); - Math::angle({2.0f, 3.0f, 4.0f}, Vector3(1.0f, -2.0f, 3.0f).normalized()); - CORRADE_COMPARE(o.str(), "Math::angle(): vectors must be normalized\n"); - CORRADE_COMPARE(Math::angle(Vector3(2.0f, 3.0f, 4.0f).normalized(), Vector3(1.0f, -2.0f, 3.0f).normalized()), Rad(1.162514f)); } +void VectorTest::angleNotNormalized() { + std::ostringstream out; + Error redirectError{&out}; + Math::angle(Vector3(2.0f, 3.0f, 4.0f).normalized(), {1.0f, -2.0f, 3.0f}); + Math::angle({2.0f, 3.0f, 4.0f}, Vector3(1.0f, -2.0f, 3.0f).normalized()); + + CORRADE_COMPARE(out.str(), + "Math::angle(): vectors Vector(0.371391, 0.557086, 0.742781) and Vector(1, -2, 3) are not normalized\n" + "Math::angle(): vectors Vector(2, 3, 4) and Vector(0.267261, -0.534522, 0.801784) are not normalized\n"); +} + template class BasicVec2: public Math::Vector<2, T> { public: template constexpr BasicVec2(U&&... args): Math::Vector<2, T>{args...} {} diff --git a/src/Magnum/Math/Vector.h b/src/Magnum/Math/Vector.h index db43ecfbf..28a14a558 100644 --- a/src/Magnum/Math/Vector.h +++ b/src/Magnum/Math/Vector.h @@ -100,7 +100,7 @@ typename std::enable_if::value, Rad& normalizedA, const Vector& normalizedB) { CORRADE_ASSERT(normalizedA.isNormalized() && normalizedB.isNormalized(), - "Math::angle(): vectors must be normalized", {}); + "Math::angle(): vectors" << normalizedA << "and" << normalizedB << "are not normalized", {}); return Rad(std::acos(dot(normalizedA, normalizedB))); } @@ -1359,7 +1359,8 @@ inline Vector template inline typename std::enable_if::value, Vector>::type #endif Vector::projectedOntoNormalized(const Vector& line) const { - CORRADE_ASSERT(line.isNormalized(), "Math::Vector::projectedOntoNormalized(): line must be normalized", {}); + CORRADE_ASSERT(line.isNormalized(), + "Math::Vector::projectedOntoNormalized(): line" << line << "is not normalized", {}); return line*Math::dot(*this, line); }