diff --git a/src/Magnum/Math/Algorithms/Svd.h b/src/Magnum/Math/Algorithms/Svd.h index 0654b464e..0f861d2af 100644 --- a/src/Magnum/Math/Algorithms/Svd.h +++ b/src/Magnum/Math/Algorithms/Svd.h @@ -60,20 +60,25 @@ template<> constexpr Double smallestDelta() { return 1.0e-64; } Performs [Thin SVD](https://en.wikipedia.org/wiki/Singular-value_decomposition#Thin_SVD) on given matrix where @p rows >= `cols`: @f[ - M = U \Sigma V^* + \boldsymbol{M} = \boldsymbol{U} \boldsymbol{\Sigma} \boldsymbol{V}^* @f] -Returns first @p cols column vectors of @f$ U @f$, diagonal of @f$ \Sigma @f$ -and non-transposed @f$ V @f$. If the solution doesn't converge, returns -zero matrices. +Returns first @p cols column vectors of @f$ \boldsymbol{U} @f$, diagonal of +@f$ \boldsymbol{\Sigma} @f$ and non-transposed @f$ \boldsymbol{V} @f$. If the +solution doesn't converge, returns zero matrices. -Full @f$ U @f$, @f$ \Sigma @f$ matrices and original @f$ M @f$ matrix can be -reconstructed from the values as following: +Full @f$ \boldsymbol{U} @f$, @f$ \boldsymbol{\Sigma} @f$ matrices and original +@f$ \boldsymbol{M} @f$ matrix can be reconstructed from the values as +following: @snippet MagnumMathAlgorithms.cpp svd -Implementation based on *Golub, G. H.; Reinsch, C. (1970). "Singular value -decomposition and least squares solutions"*. +One possible use is to decompose a transformation matrix into separate rotation +and scaling parts. Note, however, that the decomposition is not unique. See the +[associated test case](https://github.com/mosra/magnum/blob/master/src/Magnum/Math/Algorithms/Test/SvdTest.cpp) +for an example. Implementation based on *Golub, G. H.; Reinsch, C. (1970). +"Singular value decomposition and least squares solutions"*. +@see @ref qr(), @ref Matrix3::rotationShear(), @ref Matrix4::rotationShear() */ /* The matrix is passed by value because it is changed inside */ template std::tuple, Vector, Matrix> svd(RectangularMatrix m) { diff --git a/src/Magnum/Math/Algorithms/Test/SvdTest.cpp b/src/Magnum/Math/Algorithms/Test/SvdTest.cpp index 36cddbd50..0b9890f10 100644 --- a/src/Magnum/Math/Algorithms/Test/SvdTest.cpp +++ b/src/Magnum/Math/Algorithms/Test/SvdTest.cpp @@ -25,6 +25,7 @@ #include +#include "Magnum/Math/Matrix4.h" #include "Magnum/Math/Algorithms/Svd.h" namespace Magnum { namespace Math { namespace Algorithms { namespace Test { @@ -33,6 +34,7 @@ struct SvdTest: Corrade::TestSuite::Tester { explicit SvdTest(); template void test(); + void decomposeRotationShear(); }; template using Matrix5x8 = RectangularMatrix<5, 8, T>; @@ -42,8 +44,9 @@ template using Vector8 = Vector<8, T>; template using Vector5 = Vector<5, T>; SvdTest::SvdTest() { - addTests({&SvdTest::test, - &SvdTest::test}); + addTests({&SvdTest::test, + &SvdTest::test, + &SvdTest::decomposeRotationShear}); } template void SvdTest::test() { @@ -91,6 +94,28 @@ template void SvdTest::test() { } } +void SvdTest::decomposeRotationShear() { + typedef Math::Matrix4 Matrix4; + typedef Math::Matrix3x3 Matrix3x3; + typedef Math::Vector3 Vector3; + + using namespace Math::Literals; + + Matrix4 a = Matrix4::scaling({1.5f, 2.0f, 1.0f})*Matrix4::rotationZ(35.0_degf); + + Matrix3x3 u{NoInit}; + Vector3 w{NoInit}; + Matrix3x3 v{NoInit}; + std::tie(u, w, v) = Algorithms::svd(a.rotationScaling()); + + CORRADE_COMPARE(u*Matrix3x3::fromDiagonal(w)*v.transposed(), a.rotationScaling()); + + /* U contains a flipped sign for Z, use it to remove the sign from the + transposed rotation matrix V */ + CORRADE_COMPARE(w, (Vector3{1.5f, 2.0f, 1.0f})); + CORRADE_COMPARE(Matrix4::from(u*v.transposed(), {}), Matrix4::rotationZ(35.0_degf)); +} + }}}} CORRADE_TEST_MAIN(Magnum::Math::Algorithms::Test::SvdTest)