* fix Quaternion::setFromTwoVectors (thanks to "benv" from the forum)

* extend PartialRedux::cross() to any matrix sizes with automatic
  vectorization when possible
* unit tests: add "geo_" prefix to all unit tests related to the
  geometry module and start splitting the big "geometry.cpp" tests to
  multiple smaller ones (also include new tests)
This commit is contained in:
Gael Guennebaud
2009-02-17 09:53:05 +00:00
parent 67b4fab4e3
commit e6f1104b57
16 changed files with 445 additions and 157 deletions

View File

@@ -129,10 +129,13 @@ ei_add_test(inverse)
ei_add_test(qr)
ei_add_test(eigensolver " " "${GSL_LIBRARIES}")
ei_add_test(svd)
ei_add_test(geometry)
ei_add_test(hyperplane)
ei_add_test(parametrizedline)
ei_add_test(alignedbox)
ei_add_test(geo_orthomethods)
ei_add_test(geo_quaternion)
ei_add_test(geo_transformations)
ei_add_test(geo_eulerangles)
ei_add_test(geo_hyperplane)
ei_add_test(geo_parametrizedline)
ei_add_test(geo_alignedbox)
ei_add_test(regression)
ei_add_test(stdvector)
ei_add_test(resize)

View File

@@ -1,7 +1,7 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra. Eigen itself is part of the KDE project.
//
// Copyright (C) 2008 Gael Guennebaud <g.gael@free.fr>
// Copyright (C) 2008-2009 Gael Guennebaud <g.gael@free.fr>
//
// Eigen is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
@@ -65,7 +65,7 @@ template<typename BoxType> void alignedbox(const BoxType& _box)
VERIFY_IS_APPROX(hp1d.template cast<Scalar>(),b0);
}
void test_alignedbox()
void test_geo_alignedbox()
{
for(int i = 0; i < g_repeat; i++) {
CALL_SUBTEST( alignedbox(AlignedBox<float,2>()) );

70
test/geo_eulerangles.cpp Normal file
View File

@@ -0,0 +1,70 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra. Eigen itself is part of the KDE project.
//
// Copyright (C) 2008-2009 Gael Guennebaud <g.gael@free.fr>
//
// Eigen is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
//
// Alternatively, you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of
// the License, or (at your option) any later version.
//
// Eigen is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License or the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License and a copy of the GNU General Public License along with
// Eigen. If not, see <http://www.gnu.org/licenses/>.
#include "main.h"
#include <Eigen/Geometry>
#include <Eigen/LU>
#include <Eigen/SVD>
template<typename Scalar> void eulerangles(void)
{
typedef Matrix<Scalar,3,3> Matrix3;
typedef Matrix<Scalar,3,1> Vector3;
typedef Quaternion<Scalar> Quaternionx;
typedef AngleAxis<Scalar> AngleAxisx;
Scalar a = ei_random<Scalar>(-Scalar(M_PI), Scalar(M_PI));
Quaternionx q1;
q1 = AngleAxisx(a, Vector3::Random().normalized());
Matrix3 m;
m = q1;
#define VERIFY_EULER(I,J,K, X,Y,Z) { \
Vector3 ea = m.eulerAngles(I,J,K); \
Matrix3 m1 = Matrix3(AngleAxisx(ea[0], Vector3::Unit##X()) * AngleAxisx(ea[1], Vector3::Unit##Y()) * AngleAxisx(ea[2], Vector3::Unit##Z())); \
VERIFY_IS_APPROX(m, Matrix3(AngleAxisx(ea[0], Vector3::Unit##X()) * AngleAxisx(ea[1], Vector3::Unit##Y()) * AngleAxisx(ea[2], Vector3::Unit##Z()))); \
}
VERIFY_EULER(0,1,2, X,Y,Z);
VERIFY_EULER(0,1,0, X,Y,X);
VERIFY_EULER(0,2,1, X,Z,Y);
VERIFY_EULER(0,2,0, X,Z,X);
VERIFY_EULER(1,2,0, Y,Z,X);
VERIFY_EULER(1,2,1, Y,Z,Y);
VERIFY_EULER(1,0,2, Y,X,Z);
VERIFY_EULER(1,0,1, Y,X,Y);
VERIFY_EULER(2,0,1, Z,X,Y);
VERIFY_EULER(2,0,2, Z,X,Z);
VERIFY_EULER(2,1,0, Z,Y,X);
VERIFY_EULER(2,1,2, Z,Y,Z);
}
void test_geo_eulerangles()
{
for(int i = 0; i < g_repeat; i++) {
CALL_SUBTEST( eulerangles<float>() );
CALL_SUBTEST( eulerangles<double>() );
}
}

View File

@@ -127,7 +127,7 @@ template<typename Scalar> void lines()
}
}
void test_hyperplane()
void test_geo_hyperplane()
{
for(int i = 0; i < g_repeat; i++) {
CALL_SUBTEST( hyperplane(Hyperplane<float,2>()) );

109
test/geo_orthomethods.cpp Normal file
View File

@@ -0,0 +1,109 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra. Eigen itself is part of the KDE project.
//
// Copyright (C) 2008-2009 Gael Guennebaud <g.gael@free.fr>
//
// Eigen is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
//
// Alternatively, you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of
// the License, or (at your option) any later version.
//
// Eigen is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License or the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License and a copy of the GNU General Public License along with
// Eigen. If not, see <http://www.gnu.org/licenses/>.
#include "main.h"
#include <Eigen/Geometry>
#include <Eigen/LU>
#include <Eigen/SVD>
/* this test covers the following files:
Geometry/OrthoMethods.h
*/
template<typename Scalar> void orthomethods_3()
{
typedef Matrix<Scalar,3,3> Matrix3;
typedef Matrix<Scalar,3,1> Vector3;
Vector3 v0 = Vector3::Random(),
v1 = Vector3::Random(),
v2 = Vector3::Random();
// cross product
VERIFY_IS_MUCH_SMALLER_THAN(v1.cross(v2).dot(v1), Scalar(1));
Matrix3 mat3;
mat3 << v0.normalized(),
(v0.cross(v1)).normalized(),
(v0.cross(v1).cross(v0)).normalized();
VERIFY(mat3.isUnitary());
// colwise/rowwise cross product
mat3.setRandom();
Vector3 vec3 = Vector3::Random();
Matrix3 mcross;
int i = ei_random<int>(0,2);
mcross = mat3.colwise().cross(vec3);
VERIFY_IS_APPROX(mcross.col(i), mat3.col(i).cross(vec3));
mcross = mat3.rowwise().cross(vec3);
VERIFY_IS_APPROX(mcross.row(i), mat3.row(i).cross(vec3));
}
template<typename Scalar, int Size> void orthomethods(int size=Size)
{
typedef Matrix<Scalar,Size,1> VectorType;
typedef Matrix<Scalar,3,Size> Matrix3N;
typedef Matrix<Scalar,Size,3> MatrixN3;
typedef Matrix<Scalar,3,1> Vector3;
VectorType v0 = VectorType::Random(size),
v1 = VectorType::Random(size),
v2 = VectorType::Random(size);
// unitOrthogonal
VERIFY_IS_MUCH_SMALLER_THAN(v0.unitOrthogonal().dot(v0), Scalar(1));
VERIFY_IS_APPROX(v0.unitOrthogonal().norm(), Scalar(1));
// colwise/rowwise cross product
Vector3 vec3 = Vector3::Random();
int i = ei_random<int>(0,size-1);
Matrix3N mat3N(3,size), mcross3N(3,size);
mat3N.setRandom();
mcross3N = mat3N.colwise().cross(vec3);
VERIFY_IS_APPROX(mcross3N.col(i), mat3N.col(i).cross(vec3));
MatrixN3 matN3(size,3), mcrossN3(size,3);
matN3.setRandom();
mcrossN3 = matN3.rowwise().cross(vec3);
VERIFY_IS_APPROX(mcrossN3.row(i), matN3.row(i).cross(vec3));
}
void test_geo_orthomethods()
{
for(int i = 0; i < g_repeat; i++) {
CALL_SUBTEST( orthomethods_3<float>() );
CALL_SUBTEST( orthomethods_3<double>() );
CALL_SUBTEST( (orthomethods<float,2>()) );
CALL_SUBTEST( (orthomethods<double,2>()) );
CALL_SUBTEST( (orthomethods<float,3>()) );
CALL_SUBTEST( (orthomethods<double,3>()) );
CALL_SUBTEST( (orthomethods<float,7>()) );
CALL_SUBTEST( (orthomethods<double,8>()) );
CALL_SUBTEST( (orthomethods<float,Dynamic>(36)) );
CALL_SUBTEST( (orthomethods<double,Dynamic>(35)) );
}
}

View File

@@ -66,7 +66,7 @@ template<typename LineType> void parametrizedline(const LineType& _line)
VERIFY_IS_APPROX(hp1d.template cast<Scalar>(),l0);
}
void test_parametrizedline()
void test_geo_parametrizedline()
{
for(int i = 0; i < g_repeat; i++) {
CALL_SUBTEST( parametrizedline(ParametrizedLine<float,2>()) );

116
test/geo_quaternion.cpp Normal file
View File

@@ -0,0 +1,116 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra. Eigen itself is part of the KDE project.
//
// Copyright (C) 2008-2009 Gael Guennebaud <g.gael@free.fr>
//
// Eigen is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
//
// Alternatively, you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of
// the License, or (at your option) any later version.
//
// Eigen is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License or the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License and a copy of the GNU General Public License along with
// Eigen. If not, see <http://www.gnu.org/licenses/>.
#include "main.h"
#include <Eigen/Geometry>
#include <Eigen/LU>
#include <Eigen/SVD>
template<typename Scalar> void quaternion(void)
{
/* this test covers the following files:
Quaternion.h
*/
typedef Matrix<Scalar,3,3> Matrix3;
typedef Matrix<Scalar,3,1> Vector3;
typedef Quaternion<Scalar> Quaternionx;
typedef AngleAxis<Scalar> AngleAxisx;
Scalar largeEps = test_precision<Scalar>();
if (ei_is_same_type<Scalar,float>::ret)
largeEps = 1e-3f;
Scalar eps = ei_random<Scalar>() * 1e-2;
Vector3 v0 = Vector3::Random(),
v1 = Vector3::Random(),
v2 = Vector3::Random(),
v3 = Vector3::Random();
Scalar a = ei_random<Scalar>(-Scalar(M_PI), Scalar(M_PI));
// Quaternion: Identity(), setIdentity();
Quaternionx q1, q2;
q2.setIdentity();
VERIFY_IS_APPROX(Quaternionx(Quaternionx::Identity()).coeffs(), q2.coeffs());
q1.coeffs().setRandom();
VERIFY_IS_APPROX(q1.coeffs(), (q1*q2).coeffs());
q1 = AngleAxisx(a, v0.normalized());
q2 = AngleAxisx(a, v1.normalized());
// angular distance
Scalar refangle = ei_abs(AngleAxisx(q1.inverse()*q2).angle());
if (refangle>Scalar(M_PI))
refangle = Scalar(2)*Scalar(M_PI) - refangle;
if((q1.coeffs()-q2.coeffs()).norm() > 10*largeEps)
{
VERIFY(ei_isApprox(q1.angularDistance(q2), refangle, largeEps));
}
// rotation matrix conversion
VERIFY_IS_APPROX(q1 * v2, q1.toRotationMatrix() * v2);
VERIFY_IS_APPROX(q1 * q2 * v2,
q1.toRotationMatrix() * q2.toRotationMatrix() * v2);
VERIFY( (q2*q1).isApprox(q1*q2, largeEps)
|| !(q2 * q1 * v2).isApprox(q1.toRotationMatrix() * q2.toRotationMatrix() * v2));
q2 = q1.toRotationMatrix();
VERIFY_IS_APPROX(q1*v1,q2*v1);
// angle-axis conversion
AngleAxisx aa = q1;
VERIFY_IS_APPROX(q1 * v1, Quaternionx(aa) * v1);
VERIFY_IS_NOT_APPROX(q1 * v1, Quaternionx(AngleAxisx(aa.angle()*2,aa.axis())) * v1);
// from two vector creation
VERIFY_IS_APPROX( v2.normalized(),(q2.setFromTwoVectors(v1, v2)*v1).normalized());
VERIFY_IS_APPROX( v1.normalized(),(q2.setFromTwoVectors(v1, v1)*v1).normalized());
VERIFY_IS_APPROX(-v1.normalized(),(q2.setFromTwoVectors(v1,-v1)*v1).normalized());
v3 = v1.cwise()+eps;
VERIFY_IS_APPROX( v3.normalized(),(q2.setFromTwoVectors(v1, v3)*v1).normalized());
VERIFY_IS_APPROX(-v3.normalized(),(q2.setFromTwoVectors(v1,-v3)*v1).normalized());
// inverse and conjugate
VERIFY_IS_APPROX(q1 * (q1.inverse() * v1), v1);
VERIFY_IS_APPROX(q1 * (q1.conjugate() * v1), v1);
// test casting
Quaternion<float> q1f = q1.template cast<float>();
VERIFY_IS_APPROX(q1f.template cast<Scalar>(),q1);
Quaternion<double> q1d = q1.template cast<double>();
VERIFY_IS_APPROX(q1d.template cast<Scalar>(),q1);
}
void test_geo_quaternion()
{
for(int i = 0; i < g_repeat; i++) {
// CALL_SUBTEST( quaternion<float>() );
CALL_SUBTEST( quaternion<double>() );
}
}

View File

@@ -1,7 +1,7 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra. Eigen itself is part of the KDE project.
//
// Copyright (C) 2008 Gael Guennebaud <g.gael@free.fr>
// Copyright (C) 2008-2009 Gael Guennebaud <g.gael@free.fr>
//
// Eigen is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
@@ -27,7 +27,7 @@
#include <Eigen/LU>
#include <Eigen/SVD>
template<typename Scalar> void geometry(void)
template<typename Scalar> void transformations(void)
{
/* this test covers the following files:
Cross.h Quaternion.h, Transform.cpp
@@ -56,32 +56,10 @@ template<typename Scalar> void geometry(void)
v1 = Vector3::Random(),
v2 = Vector3::Random();
Vector2 u0 = Vector2::Random();
Matrix3 matrot1;
Matrix3 matrot1, m;
Scalar a = ei_random<Scalar>(-Scalar(M_PI), Scalar(M_PI));
// cross product
VERIFY_IS_MUCH_SMALLER_THAN(v1.cross(v2).dot(v1), Scalar(1));
Matrix3 m;
m << v0.normalized(),
(v0.cross(v1)).normalized(),
(v0.cross(v1).cross(v0)).normalized();
VERIFY(m.isUnitary());
// Quaternion: Identity(), setIdentity();
Quaternionx q1, q2;
q2.setIdentity();
VERIFY_IS_APPROX(Quaternionx(Quaternionx::Identity()).coeffs(), q2.coeffs());
q1.coeffs().setRandom();
VERIFY_IS_APPROX(q1.coeffs(), (q1*q2).coeffs());
// unitOrthogonal
VERIFY_IS_MUCH_SMALLER_THAN(u0.unitOrthogonal().dot(u0), Scalar(1));
VERIFY_IS_MUCH_SMALLER_THAN(v0.unitOrthogonal().dot(v0), Scalar(1));
VERIFY_IS_APPROX(u0.unitOrthogonal().norm(), Scalar(1));
VERIFY_IS_APPROX(v0.unitOrthogonal().norm(), Scalar(1));
VERIFY_IS_APPROX(v0, AngleAxisx(a, v0.normalized()) * v0);
VERIFY_IS_APPROX(-v0, AngleAxisx(Scalar(M_PI), v0.unitOrthogonal()) * v0);
VERIFY_IS_APPROX(ei_cos(a)*v0.squaredNorm(), v0.dot(AngleAxisx(a, v0.unitOrthogonal()) * v0));
@@ -89,30 +67,11 @@ template<typename Scalar> void geometry(void)
VERIFY_IS_APPROX(Matrix3::Identity(), m * AngleAxisx(a, v0.normalized()));
VERIFY_IS_APPROX(Matrix3::Identity(), AngleAxisx(a, v0.normalized()) * m);
Quaternionx q1, q2;
q1 = AngleAxisx(a, v0.normalized());
q2 = AngleAxisx(a, v1.normalized());
// angular distance
Scalar refangle = ei_abs(AngleAxisx(q1.inverse()*q2).angle());
if (refangle>Scalar(M_PI))
refangle = Scalar(2)*Scalar(M_PI) - refangle;
if((q1.coeffs()-q2.coeffs()).norm() > 10*largeEps)
{
VERIFY(ei_isApprox(q1.angularDistance(q2), refangle, largeEps));
}
// rotation matrix conversion
VERIFY_IS_APPROX(q1 * v2, q1.toRotationMatrix() * v2);
VERIFY_IS_APPROX(q1 * q2 * v2,
q1.toRotationMatrix() * q2.toRotationMatrix() * v2);
VERIFY( (q2*q1).isApprox(q1*q2, largeEps) || !(q2 * q1 * v2).isApprox(
q1.toRotationMatrix() * q2.toRotationMatrix() * v2));
q2 = q1.toRotationMatrix();
VERIFY_IS_APPROX(q1*v1,q2*v1);
matrot1 = AngleAxisx(Scalar(0.1), Vector3::UnitX())
* AngleAxisx(Scalar(0.2), Vector3::UnitY())
* AngleAxisx(Scalar(0.3), Vector3::UnitZ());
@@ -126,14 +85,6 @@ template<typename Scalar> void geometry(void)
VERIFY_IS_APPROX(q1 * v1, Quaternionx(aa) * v1);
VERIFY_IS_NOT_APPROX(q1 * v1, Quaternionx(AngleAxisx(aa.angle()*2,aa.axis())) * v1);
// from two vector creation
VERIFY_IS_APPROX(v2.normalized(),(q2.setFromTwoVectors(v1,v2)*v1).normalized());
VERIFY_IS_APPROX(v2.normalized(),(q2.setFromTwoVectors(v1,v2)*v1).normalized());
// inverse and conjugate
VERIFY_IS_APPROX(q1 * (q1.inverse() * v1), v1);
VERIFY_IS_APPROX(q1 * (q1.conjugate() * v1), v1);
// AngleAxis
VERIFY_IS_APPROX(AngleAxisx(a,v1.normalized()).toRotationMatrix(),
Quaternionx(AngleAxisx(a,v1.normalized())).toRotationMatrix());
@@ -382,11 +333,6 @@ template<typename Scalar> void geometry(void)
DiagonalMatrix<double,3> sc1d; sc1d = (sc1.template cast<double>());
VERIFY_IS_APPROX(sc1d.template cast<Scalar>(),sc1);
Quaternion<float> q1f = q1.template cast<float>();
VERIFY_IS_APPROX(q1f.template cast<Scalar>(),q1);
Quaternion<double> q1d = q1.template cast<double>();
VERIFY_IS_APPROX(q1d.template cast<Scalar>(),q1);
AngleAxis<float> aa1f = aa1.template cast<float>();
VERIFY_IS_APPROX(aa1f.template cast<Scalar>(),aa1);
AngleAxis<double> aa1d = aa1.template cast<double>();
@@ -397,47 +343,12 @@ template<typename Scalar> void geometry(void)
VERIFY_IS_APPROX(r2d1f.template cast<Scalar>(),r2d1);
Rotation2D<double> r2d1d = r2d1.template cast<double>();
VERIFY_IS_APPROX(r2d1d.template cast<Scalar>(),r2d1);
m = q1;
// m.col(1) = Vector3(0,ei_random<Scalar>(),ei_random<Scalar>()).normalized();
// m.col(0) = Vector3(-1,0,0).normalized();
// m.col(2) = m.col(0).cross(m.col(1));
#define VERIFY_EULER(I,J,K, X,Y,Z) { \
Vector3 ea = m.eulerAngles(I,J,K); \
Matrix3 m1 = Matrix3(AngleAxisx(ea[0], Vector3::Unit##X()) * AngleAxisx(ea[1], Vector3::Unit##Y()) * AngleAxisx(ea[2], Vector3::Unit##Z())); \
VERIFY_IS_APPROX(m, Matrix3(AngleAxisx(ea[0], Vector3::Unit##X()) * AngleAxisx(ea[1], Vector3::Unit##Y()) * AngleAxisx(ea[2], Vector3::Unit##Z()))); \
}
VERIFY_EULER(0,1,2, X,Y,Z);
VERIFY_EULER(0,1,0, X,Y,X);
VERIFY_EULER(0,2,1, X,Z,Y);
VERIFY_EULER(0,2,0, X,Z,X);
VERIFY_EULER(1,2,0, Y,Z,X);
VERIFY_EULER(1,2,1, Y,Z,Y);
VERIFY_EULER(1,0,2, Y,X,Z);
VERIFY_EULER(1,0,1, Y,X,Y);
VERIFY_EULER(2,0,1, Z,X,Y);
VERIFY_EULER(2,0,2, Z,X,Z);
VERIFY_EULER(2,1,0, Z,Y,X);
VERIFY_EULER(2,1,2, Z,Y,Z);
// colwise/rowwise cross product
mat3.setRandom();
Vector3 vec3 = Vector3::Random();
Matrix3 mcross;
int i = ei_random<int>(0,2);
mcross = mat3.colwise().cross(vec3);
VERIFY_IS_APPROX(mcross.col(i), mat3.col(i).cross(vec3));
mcross = mat3.rowwise().cross(vec3);
VERIFY_IS_APPROX(mcross.row(i), mat3.row(i).cross(vec3));
}
void test_geometry()
void test_geo_transformations()
{
for(int i = 0; i < g_repeat; i++) {
CALL_SUBTEST( geometry<float>() );
CALL_SUBTEST( geometry<double>() );
CALL_SUBTEST( transformations<float>() );
CALL_SUBTEST( transformations<double>() );
}
}

View File

@@ -34,7 +34,6 @@ template<typename SparseMatrixType> void sparse_product(const SparseMatrixType&
double density = std::max(8./(rows*cols), 0.01);
typedef Matrix<Scalar,Dynamic,Dynamic> DenseMatrix;
typedef Matrix<Scalar,Dynamic,1> DenseVector;
Scalar eps = 1e-6;
// test matrix-matrix product
{