From e9e0de70d753b7f99cbc57621ae77a7993c46134 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 17 Dec 2012 21:36:59 +0100 Subject: [PATCH] Implemented reflection matrices. Long-standing TODO, can be used for in-game mirrors etc. I give up with shearing, as I think that it makes sense only in 2D and I can't find any reasonable use case for that yet. --- src/Math/Matrix3.h | 18 ++++++++++++++++-- src/Math/Matrix4.h | 20 ++++++++++++++++---- src/Math/Test/CMakeLists.txt | 2 +- src/Math/Test/Matrix3Test.cpp | 30 +++++++++++++++++++++++------- src/Math/Test/Matrix3Test.h | 1 + src/Math/Test/Matrix4Test.cpp | 32 ++++++++++++++++++++++++-------- src/Math/Test/Matrix4Test.h | 1 + 7 files changed, 82 insertions(+), 22 deletions(-) diff --git a/src/Math/Matrix3.h b/src/Math/Matrix3.h index ddf8431ae..1a925a12b 100644 --- a/src/Math/Matrix3.h +++ b/src/Math/Matrix3.h @@ -83,6 +83,19 @@ template class Matrix3: public Matrix<3, T> { ); } + /** + * @brief 2D reflection matrix + * @param normal Normal of the line through which to reflect + * (normalized) + * + * @see Matrix4::reflection() + */ + static Matrix3 reflection(const Vector2& normal) { + CORRADE_ASSERT(MathTypeTraits::equals(normal.dot(), T(1)), + "Math::Matrix3::reflection(): normal must be normalized", {}); + return from(Matrix<2, T>() - T(2)*normal*normal.transposed(), {}); + } + /** * @brief Create matrix from rotation/scaling part and translation part * @param rotationScaling Rotation/scaling part (upper-left 2x2 @@ -92,7 +105,7 @@ template class Matrix3: public Matrix<3, T> { * * @see rotationScaling() const, translation() const */ - static Matrix3 from(const Matrix<2, T>& rotationScaling, const Vector2& translation = Vector2()) { + static Matrix3 from(const Matrix<2, T>& rotationScaling, const Vector2& translation) { return from( Vector3(rotationScaling[0], T(0)), Vector3(rotationScaling[1], T(0)), @@ -167,7 +180,8 @@ template class Matrix3: public Matrix<3, T> { * @brief 2D translation part of the matrix * * First two elements of third column. - * @see translation(const Vector2&), Matrix4::translation() + * @see from(const Matrix<2, T>&, const Vector2&), + * translation(const Vector2&), Matrix4::translation() */ inline Vector2& translation() { return (*this)[2].xy(); } inline constexpr Vector2 translation() const { return (*this)[2].xy(); } /**< @overload */ diff --git a/src/Math/Matrix4.h b/src/Math/Matrix4.h index 2d669a7fc..f41458b03 100644 --- a/src/Math/Matrix4.h +++ b/src/Math/Matrix4.h @@ -32,8 +32,6 @@ Provides functions for transformations in 3D. See Matrix3 for 2D transformations. See also @ref matrix-vector for brief introduction. @see Magnum::Matrix4, SceneGraph::MatrixTransformation3D @configurationvalueref{Magnum::Math::Matrix4} -@todo Shearing -@todo Reflection */ template class Matrix4: public Matrix<4, T> { public: @@ -173,6 +171,19 @@ template class Matrix4: public Matrix<4, T> { ); } + /** + * @brief 3D reflection matrix + * @param normal Normal of the plane through which to reflect + * (normalized) + * + * @see Matrix3::reflection() + */ + static Matrix4 reflection(const Vector3& normal) { + CORRADE_ASSERT(MathTypeTraits::equals(normal.dot(), T(1)), + "Math::Matrix4::reflection(): normal must be normalized", {}); + return from(Matrix<3, T>() - T(2)*normal*normal.transposed(), {}); + } + /** * @brief Create matrix from rotation/scaling part and translation part * @param rotationScaling Rotation/scaling part (upper-left 3x3 @@ -182,7 +193,7 @@ template class Matrix4: public Matrix<4, T> { * * @see rotationScaling() const, translation() const */ - static Matrix4 from(const Matrix<3, T>& rotationScaling, const Vector3& translation = Vector3()) { + static Matrix4 from(const Matrix<3, T>& rotationScaling, const Vector3& translation) { return from( Vector4(rotationScaling[0], T(0)), Vector4(rotationScaling[1], T(0)), @@ -273,7 +284,8 @@ template class Matrix4: public Matrix<4, T> { * @brief 3D translation part of the matrix * * First three elements of fourth column. - * @see translation(const Vector3&), Matrix3::translation() + * @see from(const Matrix<3, T>&, const Vector3&), + * translation(const Vector3&), Matrix3::translation() */ inline Vector3& translation() { return (*this)[3].xyz(); } inline constexpr Vector3 translation() const { return (*this)[3].xyz(); } /**< @overload */ diff --git a/src/Math/Test/CMakeLists.txt b/src/Math/Test/CMakeLists.txt index 2d0739185..e613855b9 100644 --- a/src/Math/Test/CMakeLists.txt +++ b/src/Math/Test/CMakeLists.txt @@ -18,4 +18,4 @@ corrade_add_test(MathMatrix4Test Matrix4Test.cpp LIBRARIES MagnumMathTestLib) corrade_add_test(MathSwizzleTest SwizzleTest.cpp LIBRARIES MagnumMathTestLib) -set_target_properties(MathVectorTest MathMatrix4Test PROPERTIES COMPILE_FLAGS -DCORRADE_GRACEFUL_ASSERT) +set_target_properties(MathVectorTest MathMatrix3Test MathMatrix4Test PROPERTIES COMPILE_FLAGS -DCORRADE_GRACEFUL_ASSERT) diff --git a/src/Math/Test/Matrix3Test.cpp b/src/Math/Test/Matrix3Test.cpp index a7e51a5e2..38dac55d3 100644 --- a/src/Math/Test/Matrix3Test.cpp +++ b/src/Math/Test/Matrix3Test.cpp @@ -36,6 +36,7 @@ Matrix3Test::Matrix3Test() { &Matrix3Test::translation, &Matrix3Test::scaling, &Matrix3Test::rotation, + &Matrix3Test::reflection, &Matrix3Test::fromParts, &Matrix3Test::rotationScalingPart, &Matrix3Test::rotationPart, @@ -96,6 +97,27 @@ void Matrix3Test::rotation() { CORRADE_COMPARE(Matrix3::rotation(deg(15.0f)), matrix); } +void Matrix3Test::reflection() { + std::ostringstream o; + Error::setOutput(&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, + 0.0f, 0.0f, 1.0f + ); + + CORRADE_COMPARE(actual*actual, Matrix3()); + CORRADE_COMPARE(actual*normal, -normal); + CORRADE_COMPARE(actual, expected); +} + void Matrix3Test::fromParts() { Matrix2 rotationScaling( 3.0f, 5.0f, @@ -109,14 +131,8 @@ void Matrix3Test::fromParts() { 4.0f, 4.0f, 0.0f, 7.0f, -1.0f, 1.0f ); - CORRADE_COMPARE(Matrix3::from(rotationScaling, translation), expected); - Matrix3 expectedNoRotation( - 3.0f, 5.0f, 0.0f, - 4.0f, 4.0f, 0.0f, - 0.0f, 0.0f, 1.0f - ); - CORRADE_COMPARE(Matrix3::from(rotationScaling), expectedNoRotation); + CORRADE_COMPARE(Matrix3::from(rotationScaling, translation), expected); } void Matrix3Test::rotationScalingPart() { diff --git a/src/Math/Test/Matrix3Test.h b/src/Math/Test/Matrix3Test.h index 9a34ff78e..fa658f74a 100644 --- a/src/Math/Test/Matrix3Test.h +++ b/src/Math/Test/Matrix3Test.h @@ -28,6 +28,7 @@ class Matrix3Test: public Corrade::TestSuite::Tester { void translation(); void scaling(); void rotation(); + void reflection(); void fromParts(); void rotationScalingPart(); void rotationPart(); diff --git a/src/Math/Test/Matrix4Test.cpp b/src/Math/Test/Matrix4Test.cpp index 9e79985cd..eac9539c8 100644 --- a/src/Math/Test/Matrix4Test.cpp +++ b/src/Math/Test/Matrix4Test.cpp @@ -39,6 +39,7 @@ Matrix4Test::Matrix4Test() { &Matrix4Test::rotationX, &Matrix4Test::rotationY, &Matrix4Test::rotationZ, + &Matrix4Test::reflection, &Matrix4Test::fromParts, &Matrix4Test::rotationScalingPart, &Matrix4Test::rotationPart, @@ -136,6 +137,28 @@ void Matrix4Test::rotationZ() { CORRADE_COMPARE(Matrix4::rotationZ(rad(Math::Constants::pi()/7)), matrix); } +void Matrix4Test::reflection() { + std::ostringstream o; + Error::setOutput(&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, + 0.444444f, -0.888889f, 0.111111f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + ); + + CORRADE_COMPARE(actual*actual, Matrix4()); + CORRADE_COMPARE(actual*normal, -normal); + CORRADE_COMPARE(actual, expected); +} + void Matrix4Test::fromParts() { Matrix3 rotationScaling( 3.0f, 5.0f, 8.0f, @@ -151,15 +174,8 @@ void Matrix4Test::fromParts() { 7.0f, -1.0f, 8.0f, 0.0f, 9.0f, 4.0f, 5.0f, 1.0f ); - CORRADE_COMPARE(Matrix4::from(rotationScaling, translation), expected); - Matrix4 expectedNoTranslation( - 3.0f, 5.0f, 8.0f, 0.0f, - 4.0f, 4.0f, 7.0f, 0.0f, - 7.0f, -1.0f, 8.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f - ); - CORRADE_COMPARE(Matrix4::from(rotationScaling), expectedNoTranslation); + CORRADE_COMPARE(Matrix4::from(rotationScaling, translation), expected); } void Matrix4Test::rotationScalingPart() { diff --git a/src/Math/Test/Matrix4Test.h b/src/Math/Test/Matrix4Test.h index 238fa1cc2..3ea386b21 100644 --- a/src/Math/Test/Matrix4Test.h +++ b/src/Math/Test/Matrix4Test.h @@ -31,6 +31,7 @@ class Matrix4Test: public Corrade::TestSuite::Tester { void rotationX(); void rotationY(); void rotationZ(); + void reflection(); void fromParts(); void rotationScalingPart(); void rotationPart();