diff --git a/src/Math/Matrix3.h b/src/Math/Matrix3.h index ae49a62b9..d977a79c1 100644 --- a/src/Math/Matrix3.h +++ b/src/Math/Matrix3.h @@ -163,20 +163,36 @@ template class Matrix3: public Matrix<3, T> { * @brief 2D rotation and scaling part of the matrix * * Upper-left 2x2 part of the matrix. - * @see from(const Matrix<2, T>&, const Vector2&), rotation() const, - * rotation(T), Matrix4::rotationScaling() const - * @todo extract rotation with assert for no scaling + * @see from(const Matrix<2, T>&, const Vector2&), rotation() const + * rotationNormalized(), rotation(T), + * Matrix4::rotationScaling() const */ constexpr Matrix<2, T> rotationScaling() const { return {(*this)[0].xy(), (*this)[1].xy()}; } + /** + * @brief 2D rotation part of the matrix assuming there is no scaling + * + * Similar to @ref rotationScaling(), but additionally checks that the + * base vectors are normalized. + * @see rotation() const, @ref Matrix4::rotationNormalized() + * @todo assert also orthogonality or this is good enough? + */ + Matrix<2, T> rotationNormalized() const { + CORRADE_ASSERT((*this)[0].xy().isNormalized() && (*this)[1].xy().isNormalized(), + "Math::Matrix3::rotationNormalized(): the rotation part is not normalized", {}); + return {(*this)[0].xy(), + (*this)[1].xy()}; + } + /** * @brief 2D rotation part of the matrix * * Normalized upper-left 2x2 part of the matrix. - * @see rotationScaling() const, rotation(T), Matrix4::rotation() const + * @see rotationNormalized(), rotationScaling() const, rotation(T), + * Matrix4::rotation() const * @todo assert uniform scaling (otherwise this would be garbage) */ Matrix<2, T> rotation() const { diff --git a/src/Math/Matrix4.h b/src/Math/Matrix4.h index 23028357d..dec15dbfc 100644 --- a/src/Math/Matrix4.h +++ b/src/Math/Matrix4.h @@ -227,8 +227,8 @@ template class Matrix4: public Matrix<4, T> { * * Upper-left 3x3 part of the matrix. * @see from(const Matrix<3, T>&, const Vector3&), rotation() const, - * rotation(T, const Vector3&), Matrix3::rotationScaling() const - * @todo extract rotation with assert for no scaling + * rotationNormalized(), rotation(T, const Vector3&), + * Matrix3::rotationScaling() const */ /* Not Matrix3, because it is for affine 2D transformations */ constexpr Matrix<3, T> rotationScaling() const { @@ -237,12 +237,29 @@ template class Matrix4: public Matrix<4, T> { (*this)[2].xyz()}; } + /** + * @brief 3D rotation part of the matrix assuming there is no scaling + * + * Similar to @ref rotationScaling(), but additionally checks that the + * base vectors are normalized. + * @see rotation() const, @ref Matrix3::rotationNormalized() + * @todo assert also orthogonality or this is good enough? + */ + /* Not Matrix3, because it is for affine 2D transformations */ + Matrix<3, T> rotationNormalized() const { + CORRADE_ASSERT((*this)[0].xyz().isNormalized() && (*this)[1].xyz().isNormalized() && (*this)[2].xyz().isNormalized(), + "Math::Matrix4::rotationNormalized(): the rotation part is not normalized", {}); + return {(*this)[0].xyz(), + (*this)[1].xyz(), + (*this)[2].xyz()}; + } + /** * @brief 3D rotation part of the matrix * * Normalized upper-left 3x3 part of the matrix. - * @see rotationScaling() const, rotation(T, const Vector3&), - * Matrix3::rotation() const + * @see rotationNormalized(), rotationScaling() const, + * rotation(T, const Vector3&), Matrix3::rotation() const * @todo assert uniform scaling (otherwise this would be garbage) */ /* Not Matrix3, because it is for affine 2D transformations */ diff --git a/src/Math/Test/Matrix3Test.cpp b/src/Math/Test/Matrix3Test.cpp index 7546434ca..e7884f2b5 100644 --- a/src/Math/Test/Matrix3Test.cpp +++ b/src/Math/Test/Matrix3Test.cpp @@ -76,6 +76,7 @@ class Matrix3Test: public Corrade::TestSuite::Tester { void projection(); void fromParts(); void rotationScalingPart(); + void rotationNormalizedPart(); void rotationPart(); void vectorParts(); void invertedRigid(); @@ -110,6 +111,7 @@ Matrix3Test::Matrix3Test() { &Matrix3Test::projection, &Matrix3Test::fromParts, &Matrix3Test::rotationScalingPart, + &Matrix3Test::rotationNormalizedPart, &Matrix3Test::rotationPart, &Matrix3Test::vectorParts, &Matrix3Test::invertedRigid, @@ -285,6 +287,24 @@ void Matrix3Test::rotationScalingPart() { Vector2(4.0f, 4.0f))); } +void Matrix3Test::rotationNormalizedPart() { + std::ostringstream o; + Error::setOutput(&o); + + Matrix3 a({1.0f, 0.0f, 8.0f}, + {1.0f, 0.1f, 7.0f}, + {7.0f, -1.0f, 8.0f}); + a.rotationNormalized(); + + CORRADE_COMPARE(o.str(), "Math::Matrix3::rotationNormalized(): the rotation part is not normalized\n"); + + Matrix3 b({ 0.965926f, 0.258819f, 1.0f}, + {-0.258819f, 0.965926f, 3.0f}, + { 0.0f, 0.0f, 1.0f}); + CORRADE_COMPARE(b.rotationNormalized(), Matrix2(Vector2( 0.965926f, 0.258819f), + Vector2(-0.258819f, 0.965926f))); +} + void Matrix3Test::rotationPart() { Matrix3 rotation = Matrix3::rotation(Deg(15.0f)); Matrix2 expectedRotationPart(Vector2( 0.965926f, 0.258819f), diff --git a/src/Math/Test/Matrix4Test.cpp b/src/Math/Test/Matrix4Test.cpp index d5bbfa70b..1f05a383f 100644 --- a/src/Math/Test/Matrix4Test.cpp +++ b/src/Math/Test/Matrix4Test.cpp @@ -83,6 +83,7 @@ class Matrix4Test: public Corrade::TestSuite::Tester { void perspectiveProjectionFov(); void fromParts(); void rotationScalingPart(); + void rotationNormalizedPart(); void rotationPart(); void vectorParts(); void invertedRigid(); @@ -122,6 +123,7 @@ Matrix4Test::Matrix4Test() { &Matrix4Test::perspectiveProjectionFov, &Matrix4Test::fromParts, &Matrix4Test::rotationScalingPart, + &Matrix4Test::rotationNormalizedPart, &Matrix4Test::rotationPart, &Matrix4Test::vectorParts, &Matrix4Test::invertedRigid, @@ -368,6 +370,26 @@ void Matrix4Test::rotationScalingPart() { Vector3(7.0f, -1.0f, 8.0f))); } +void Matrix4Test::rotationNormalizedPart() { + std::ostringstream o; + Error::setOutput(&o); + + Matrix4 a({0.0f, 0.0f, 1.0f, 4.0f}, + {1.0f, 0.0f, 0.0f, 3.0f}, + {0.0f, -1.0f, 0.1f, 0.0f}, + {9.0f, 4.0f, 5.0f, 9.0f}); + a.rotationNormalized(); + CORRADE_COMPARE(o.str(), "Math::Matrix4::rotationNormalized(): the rotation part is not normalized\n"); + + Matrix4 b({ 0.35612214f, -0.80181062f, 0.47987163f, 1.0f}, + { 0.47987163f, 0.59757638f, 0.6423595f, 3.0f}, + {-0.80181062f, 0.0015183985f, 0.59757638f, 4.0f}, + { 0.0f, 0.0f, 0.0f, 1.0f}); + CORRADE_COMPARE(b.rotationNormalized(), Matrix3(Vector3( 0.35612214f, -0.80181062f, 0.47987163f), + Vector3( 0.47987163f, 0.59757638f, 0.6423595f), + Vector3(-0.80181062f, 0.0015183985f, 0.59757638f))); +} + void Matrix4Test::rotationPart() { Matrix4 rotation = Matrix4::rotation(Deg(-74.0f), Vector3(-1.0f, 2.0f, 2.0f).normalized()); Matrix3 expectedRotationPart(Vector3( 0.35612214f, -0.80181062f, 0.47987163f),