Geometry: Reset last row when building Affine/Isometry Transform from matrix libeigen/eigen!3412 Co-authored-by: Amirishetty Sai Vignesh <amirishettysaivignesh1@gmail.com>
diff --git a/CHANGELOG.md b/CHANGELOG.md index ea3c0aa..932fe6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md
@@ -21,6 +21,7 @@ - `BDCSVD` with QR-only `Options`, such as `DisableQRDecomposition`, dropped runtime unitary requests for inputs below the switch size, so `matrixU()` asserted, or returned an empty `U` and `V` under `NDEBUG` [!3212] - `JacobiSVD` with `FullPivHouseholderQRPreconditioner` accepted `ComputeThinV` and returned a non-orthogonal `V` for wide inputs; it now fails to compile, or asserts when requested at runtime [!3212] - `HouseholderQR` (and the decompositions and `SplineFitting` built on it) and `JacobiSVD` did not compile with `AutoDiffScalar` [#3192, !3265] +- Constructing or assigning an `Affine` or `Isometry` `Transform` from a (Dim+1)x(Dim+1) matrix copied its last row verbatim; the last row is now set to `[0 ... 0 1]`, as for Dim x Dim and Dim x (Dim+1) inputs [#2715, !3412] Breaking changes: - The default `epsfcn` for central mode differences was changed to `cbrt(NumTraits<Scalar>::epsilon())` (used to be `sqrt(NumTraits<Scalar>::epsilon())`). The new default should reduce numerical error in most cases.
diff --git a/Eigen/src/Geometry/Transform.h b/Eigen/src/Geometry/Transform.h index 1896f20..4d1133a 100644 --- a/Eigen/src/Geometry/Transform.h +++ b/Eigen/src/Geometry/Transform.h
@@ -296,7 +296,8 @@ using take_affine_part = internal::transform_take_affine_part<Transform>; - /** Constructs and initializes a transformation from a Dim^2 or a (Dim+1)^2 matrix. */ + /** Constructs and initializes a transformation from a Dim^2 or a (Dim+1)^2 matrix. + * If Mode==Affine or Mode==Isometry, then the last row is set to [0 ... 0 1] */ template <typename OtherDerived> EIGEN_DEVICE_FUNC inline explicit Transform(const EigenBase<OtherDerived>& other) { EIGEN_STATIC_ASSERT( @@ -307,7 +308,8 @@ internal::transform_construct_from_matrix<OtherDerived, Mode, Options, Dim, HDim>::run(this, other.derived()); } - /** Set \c *this from a Dim^2 or (Dim+1)^2 matrix. */ + /** Set \c *this from a Dim^2 or (Dim+1)^2 matrix. + * If Mode==Affine or Mode==Isometry, then the last row is set to [0 ... 0 1] */ template <typename OtherDerived> EIGEN_DEVICE_FUNC inline Transform& operator=(const EigenBase<OtherDerived>& other) { EIGEN_STATIC_ASSERT( @@ -1362,6 +1364,9 @@ static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void run( Transform<typename Other::Scalar, Dim, Mode, Options>* transform, const Other& other) { transform->matrix() = other; + // Affine and Isometry transforms require the last row to be [0 ... 0 1]; Projective keeps it as given. + transform_make_affine<(int(Mode) == Affine || int(Mode) == Isometry) ? Affine : AffineCompact>::run( + transform->matrix()); } };
diff --git a/test/geo_transformations.cpp b/test/geo_transformations.cpp index d762eaa..b00bae6 100644 --- a/test/geo_transformations.cpp +++ b/test/geo_transformations.cpp
@@ -951,12 +951,34 @@ VERIFY((s0 - s1).norm() < c * eps); } +// Part of #2715: constructing or assigning from a (Dim+1)^2 matrix must respect Mode. +template <typename Scalar, int Mode, int Options> +void transform_from_full_matrix() { + using Transform3 = Transform<Scalar, 3, Mode, Options>; + using Matrix4 = Matrix<Scalar, 4, 4>; + Matrix4 m = Matrix4::Random(); + Transform3 t1(m); + Transform3 t2; + t2 = m; + Matrix4 expected = m; + if (Mode != int(Projective)) expected.row(3) << 0, 0, 0, 1; + // AffineCompact stores only the top three rows. + const Index r = t1.matrix().rows(); + VERIFY_IS_EQUAL(t1.matrix(), expected.topRows(r)); + VERIFY_IS_EQUAL(t2.matrix(), expected.topRows(r)); +} + EIGEN_DECLARE_TEST(geo_transformations) { for (int i = 0; i < g_repeat; i++) { CALL_SUBTEST_1((transformations<double, Affine, AutoAlign>())); CALL_SUBTEST_1((non_projective_only<double, Affine, AutoAlign>())); CALL_SUBTEST_1((transformations_computed_scaling_continuity<double, Affine, AutoAlign>())); CALL_SUBTEST_1(custom_scalar_test<>()); + CALL_SUBTEST_1((transform_from_full_matrix<double, Affine, AutoAlign>())); + CALL_SUBTEST_1((transform_from_full_matrix<double, Isometry, AutoAlign>())); + CALL_SUBTEST_1((transform_from_full_matrix<double, AffineCompact, AutoAlign>())); + CALL_SUBTEST_1((transform_from_full_matrix<double, Projective, AutoAlign>())); + CALL_SUBTEST_1((transform_from_full_matrix<float, Affine, RowMajor>())); CALL_SUBTEST_2((transformations<float, AffineCompact, AutoAlign>())); CALL_SUBTEST_2((non_projective_only<float, AffineCompact, AutoAlign>()));