add computeRotationScaling and computeScalingRotation in SVD
add convenience functions in Transform
reimplement Transform::rotation() to use that
add unit-test
diff --git a/Eigen/src/Geometry/Transform.h b/Eigen/src/Geometry/Transform.h
index a932b3f..80e16a4 100644
--- a/Eigen/src/Geometry/Transform.h
+++ b/Eigen/src/Geometry/Transform.h
@@ -2,6 +2,7 @@
// for linear algebra. Eigen itself is part of the KDE project.
//
// Copyright (C) 2008 Gael Guennebaud <g.gael@free.fr>
+// Copyright (C) 2009 Benoit Jacob <jacob.benoit.1@gmail.com>
//
// Eigen is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
@@ -247,7 +248,11 @@
template<typename Derived>
inline Transform operator*(const RotationBase<Derived,Dim>& r) const;
- LinearMatrixType rotation(TransformTraits traits = Affine) const;
+ LinearMatrixType rotation() const;
+ template<typename RotationMatrixType, typename ScalingMatrixType>
+ void computeRotationScaling(RotationMatrixType *rotation, ScalingMatrixType *scaling) const;
+ template<typename ScalingMatrixType, typename RotationMatrixType>
+ void computeScalingRotation(ScalingMatrixType *scaling, RotationMatrixType *rotation) const;
template<typename PositionDerived, typename OrientationType, typename ScaleDerived>
Transform& fromPositionOrientationScale(const MatrixBase<PositionDerived> &position,
@@ -589,48 +594,61 @@
return res;
}
-/***************************
-*** Specialial functions ***
-***************************/
+/************************
+*** Special functions ***
+************************/
/** \returns the rotation part of the transformation
* \nonstableyet
*
- * \param traits allows to optimize the extraction process when the transformion
- * is known to be not a general aafine transformation. The possible values are:
- * - Affine which use a QR decomposition (default),
- * - Isometry which simply returns the linear part !
+ * \svd_module
*
- * \warning this function consider the scaling is positive
- *
- * \warning to use this method in the general case (traits==GenericAffine), you need
- * to include the QR module.
- *
- * \sa inverse(), class QR
+ * \sa computeRotationScaling(), computeScalingRotation(), class SVD
*/
template<typename Scalar, int Dim>
typename Transform<Scalar,Dim>::LinearMatrixType
-Transform<Scalar,Dim>::rotation(TransformTraits traits) const
+Transform<Scalar,Dim>::rotation() const
{
- ei_assert(traits!=Projective && "you cannot extract a rotation from a non affine transformation");
- if (traits == Affine)
- {
- // FIXME maybe QR should be fixed to return a R matrix with a positive diagonal ??
- QR<LinearMatrixType> qr(linear());
- LinearMatrixType matQ = qr.matrixQ();
- LinearMatrixType matR = qr.matrixR();
- for (int i=0 ; i<Dim; ++i)
- if (matR.coeff(i,i)<0)
- matQ.col(i) = -matQ.col(i);
- return matQ;
- }
- else if (traits == Isometry) // though that's stupid let's handle it !
- return linear(); // FIXME needs to divide by determinant
- else
- {
- ei_assert("invalid traits value in Transform::rotation()");
- return LinearMatrixType();
- }
+ LinearMatrixType result;
+ computeRotationScaling(&result, (LinearMatrixType*)0);
+ return result;
+}
+
+
+/** decomposes the linear part of the transformation as a product rotation x scaling, the scaling being
+ * not necessarily positive.
+ *
+ * If either pointer is zero, the corresponding computation is skipped.
+ *
+ * \nonstableyet
+ *
+ * \svd_module
+ *
+ * \sa computeScalingRotation(), rotation(), class SVD
+ */
+template<typename Scalar, int Dim>
+template<typename RotationMatrixType, typename ScalingMatrixType>
+void Transform<Scalar,Dim>::computeRotationScaling(RotationMatrixType *rotation, ScalingMatrixType *scaling) const
+{
+ linear().svd().computeRotationScaling(rotation, scaling);
+}
+
+/** decomposes the linear part of the transformation as a product rotation x scaling, the scaling being
+ * not necessarily positive.
+ *
+ * If either pointer is zero, the corresponding computation is skipped.
+ *
+ * \nonstableyet
+ *
+ * \svd_module
+ *
+ * \sa computeRotationScaling(), rotation(), class SVD
+ */
+template<typename Scalar, int Dim>
+template<typename ScalingMatrixType, typename RotationMatrixType>
+void Transform<Scalar,Dim>::computeScalingRotation(ScalingMatrixType *scaling, RotationMatrixType *rotation) const
+{
+ linear().svd().computeScalingRotation(scaling, rotation);
}
/** Convenient method to set \c *this from a position, orientation and scale
diff --git a/Eigen/src/SVD/SVD.h b/Eigen/src/SVD/SVD.h
index 9d26916..0a52acf 100644
--- a/Eigen/src/SVD/SVD.h
+++ b/Eigen/src/SVD/SVD.h
@@ -79,8 +79,14 @@
void compute(const MatrixType& matrix);
SVD& sort();
- void computeUnitaryPositive(MatrixUType *unitary, MatrixType *positive) const;
- void computePositiveUnitary(MatrixType *positive, MatrixVType *unitary) const;
+ template<typename UnitaryType, typename PositiveType>
+ void computeUnitaryPositive(UnitaryType *unitary, PositiveType *positive) const;
+ template<typename PositiveType, typename UnitaryType>
+ void computePositiveUnitary(PositiveType *positive, UnitaryType *unitary) const;
+ template<typename RotationType, typename ScalingType>
+ void computeRotationScaling(RotationType *unitary, ScalingType *positive) const;
+ template<typename ScalingType, typename RotationType>
+ void computeScalingRotation(ScalingType *positive, RotationType *unitary) const;
protected:
/** \internal */
@@ -542,10 +548,13 @@
* If either pointer is zero, the corresponding computation is skipped.
*
* Only for square matrices.
+ *
+ * \sa computePositiveUnitary(), computeRotationScaling()
*/
template<typename MatrixType>
-void SVD<MatrixType>::computeUnitaryPositive(typename SVD<MatrixType>::MatrixUType *unitary,
- MatrixType *positive) const
+template<typename UnitaryType, typename PositiveType>
+void SVD<MatrixType>::computeUnitaryPositive(UnitaryType *unitary,
+ PositiveType *positive) const
{
ei_assert(m_matU.cols() == m_matV.cols() && "Polar decomposition is only for square matrices");
if(unitary) *unitary = m_matU * m_matV.adjoint();
@@ -557,16 +566,72 @@
* If either pointer is zero, the corresponding computation is skipped.
*
* Only for square matrices.
+ *
+ * \sa computeUnitaryPositive(), computeRotationScaling()
*/
template<typename MatrixType>
-void SVD<MatrixType>::computePositiveUnitary(MatrixType *positive,
- typename SVD<MatrixType>::MatrixVType *unitary) const
+template<typename UnitaryType, typename PositiveType>
+void SVD<MatrixType>::computePositiveUnitary(UnitaryType *positive,
+ PositiveType *unitary) const
{
ei_assert(m_matU.rows() == m_matV.rows() && "Polar decomposition is only for square matrices");
if(unitary) *unitary = m_matU * m_matV.adjoint();
if(positive) *positive = m_matU * m_sigma.asDiagonal() * m_matU.adjoint();
}
+/** decomposes the matrix as a product rotation x scaling, the scaling being
+ * not necessarily positive.
+ *
+ * If either pointer is zero, the corresponding computation is skipped.
+ *
+ * This method requires the Geometry module.
+ *
+ * \sa computeScalingRotation(), computeUnitaryPositive()
+ */
+template<typename MatrixType>
+template<typename RotationType, typename ScalingType>
+void SVD<MatrixType>::computeRotationScaling(RotationType *rotation, ScalingType *scaling) const
+{
+ ei_assert(m_matU.rows() == m_matV.rows() && "Polar decomposition is only for square matrices");
+ Scalar x = (m_matU * m_matV.adjoint()).determinant(); // so x has absolute value 1
+ Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> sv(m_sigma);
+ sv.coeffRef(0) *= x;
+ if(scaling) scaling->lazyAssign(m_matV * sv.asDiagonal() * m_matV.adjoint());
+ if(rotation)
+ {
+ MatrixType m(m_matU);
+ m.col(0) /= x;
+ rotation->lazyAssign(m * m_matV.adjoint());
+ }
+}
+
+/** decomposes the matrix as a product scaling x rotation, the scaling being
+ * not necessarily positive.
+ *
+ * If either pointer is zero, the corresponding computation is skipped.
+ *
+ * This method requires the Geometry module.
+ *
+ * \sa computeRotationScaling(), computeUnitaryPositive()
+ */
+template<typename MatrixType>
+template<typename ScalingType, typename RotationType>
+void SVD<MatrixType>::computeScalingRotation(ScalingType *scaling, RotationType *rotation) const
+{
+ ei_assert(m_matU.rows() == m_matV.rows() && "Polar decomposition is only for square matrices");
+ Scalar x = (m_matU * m_matV.adjoint()).determinant(); // so x has absolute value 1
+ Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> sv(m_sigma);
+ sv.coeffRef(0) *= x;
+ if(scaling) scaling->lazyAssign(m_matU * sv.asDiagonal() * m_matU.adjoint());
+ if(rotation)
+ {
+ MatrixType m(m_matU);
+ m.col(0) /= x;
+ rotation->lazyAssign(m * m_matV.adjoint());
+ }
+}
+
+
/** \svd_module
* \returns the SVD decomposition of \c *this
*/
diff --git a/test/geometry.cpp b/test/geometry.cpp
index df8ed42..bea3f46 100644
--- a/test/geometry.cpp
+++ b/test/geometry.cpp
@@ -25,7 +25,7 @@
#include "main.h"
#include <Eigen/Geometry>
#include <Eigen/LU>
-#include <Eigen/QR>
+#include <Eigen/SVD>
template<typename Scalar> void geometry(void)
{
@@ -339,10 +339,22 @@
t0.translate(v0).rotate(q1);
VERIFY_IS_APPROX(t0.inverse(Isometry), t0.matrix().inverse());
- // test extract rotation
+ // test extract rotation and scaling
t0.setIdentity();
t0.translate(v0).rotate(q1).scale(v1);
- VERIFY_IS_APPROX(t0.rotation(Affine) * v1, Matrix3(q1) * v1);
+ VERIFY_IS_APPROX(t0.rotation() * v1, Matrix3(q1) * v1);
+
+ Matrix3 mat_rotation, mat_scaling;
+ t0.setIdentity();
+ t0.translate(v0).rotate(q1).scale(v1);
+ t0.computeRotationScaling(&mat_rotation, &mat_scaling);
+ VERIFY_IS_APPROX(t0.linear(), mat_rotation * mat_scaling);
+ VERIFY_IS_APPROX(mat_rotation*mat_rotation.adjoint(), Matrix3::Identity());
+ VERIFY_IS_APPROX(mat_rotation.determinant(), Scalar(1));
+ t0.computeScalingRotation(&mat_scaling, &mat_rotation);
+ VERIFY_IS_APPROX(t0.linear(), mat_scaling * mat_rotation);
+ VERIFY_IS_APPROX(mat_rotation*mat_rotation.adjoint(), Matrix3::Identity());
+ VERIFY_IS_APPROX(mat_rotation.determinant(), Scalar(1));
// test casting
Transform<float,3> t1f = t1.template cast<float>();