#include #include #include "tests.hpp" // Explicit instantiate all class templates so that all member methods // get compiled and for code coverage analysis. namespace Eigen { template class Map>; template class Map const>; } // namespace Eigen namespace Sophus { template class RxSO2; template class RxSO2; #if SOPHUS_CERES template class RxSO2>; #endif template class Tests { public: using SO2Type = SO2; using RxSO2Type = RxSO2; using RotationMatrixType = typename SO2::Transformation; using Point = typename RxSO2::Point; using Tangent = typename RxSO2::Tangent; Scalar const kPi = Constants::pi(); Tests() { rxso2_vec_.push_back(RxSO2Type::exp(Tangent(0.2, 1.))); rxso2_vec_.push_back(RxSO2Type::exp(Tangent(0.2, 1.1))); rxso2_vec_.push_back(RxSO2Type::exp(Tangent(0., 1.1))); rxso2_vec_.push_back(RxSO2Type::exp(Tangent(0.00001, 0.))); rxso2_vec_.push_back(RxSO2Type::exp(Tangent(0.00001, 0.00001))); rxso2_vec_.push_back(RxSO2Type::exp(Tangent(kPi, 0.9))); rxso2_vec_.push_back(RxSO2Type::exp(Tangent(0.2, 0)) * RxSO2Type::exp(Tangent(kPi, 0.0)) * RxSO2Type::exp(Tangent(-0.2, 0))); rxso2_vec_.push_back(RxSO2Type::exp(Tangent(0.3, 0)) * RxSO2Type::exp(Tangent(kPi, 0.001)) * RxSO2Type::exp(Tangent(-0.3, 0))); Tangent tmp; tmp << Scalar(0), Scalar(0); tangent_vec_.push_back(tmp); tmp << Scalar(1), Scalar(0); tangent_vec_.push_back(tmp); tmp << Scalar(1), Scalar(0.1); tangent_vec_.push_back(tmp); tmp << Scalar(0), Scalar(0.1); tangent_vec_.push_back(tmp); tmp << Scalar(0), Scalar(-0.1); tangent_vec_.push_back(tmp); tmp << Scalar(-1), Scalar(-0.1); tangent_vec_.push_back(tmp); tmp << Scalar(20), Scalar(2); tangent_vec_.push_back(tmp); point_vec_.push_back(Point(Scalar(1), Scalar(4))); point_vec_.push_back(Point(Scalar(1), Scalar(-3))); } template enable_if_t::value, bool> testFit() { bool passed = true; for (int i = 0; i < 10; ++i) { Matrix2 M = Matrix2::Random(); for (Scalar scale : {Scalar(0.01), Scalar(0.99), Scalar(1), Scalar(10)}) { Matrix2 R = makeRotationMatrix(M); Matrix2 sR = scale * R; SOPHUS_TEST(passed, isScaledOrthogonalAndPositive(sR), "isScaledOrthogonalAndPositive(sR): % *\n%", scale, R); Matrix2 sR_cols_swapped; sR_cols_swapped << sR.col(1), sR.col(0); SOPHUS_TEST(passed, !isScaledOrthogonalAndPositive(sR_cols_swapped), "isScaledOrthogonalAndPositive(-sR): % *\n%", scale, R); } } return passed; } template enable_if_t::value, bool> testFit() { return true; } void runAll() { bool passed = testLieProperties(); passed &= testSaturation(); passed &= testRawDataAcces(); passed &= testConstructors(); passed &= testFit(); processTestResult(passed); } private: bool testLieProperties() { LieGroupTests tests(rxso2_vec_, tangent_vec_, point_vec_); return tests.doAllTestsPass(); } bool testSaturation() { bool passed = true; RxSO2Type small1(Scalar(1.1) * Constants::epsilon(), SO2Type()); RxSO2Type small2(Scalar(1.1) * Constants::epsilon(), SO2Type::exp(Constants::pi())); RxSO2Type saturated_product = small1 * small2; SOPHUS_TEST_APPROX(passed, saturated_product.scale(), Constants::epsilon(), Constants::epsilon()); SOPHUS_TEST_APPROX(passed, saturated_product.so2().matrix(), (small1.so2() * small2.so2()).matrix(), Constants::epsilon()); return passed; } bool testRawDataAcces() { bool passed = true; Eigen::Matrix raw = {0, 1}; Eigen::Map map_of_const_rxso2(raw.data()); SOPHUS_TEST_APPROX(passed, map_of_const_rxso2.complex().eval(), raw, Constants::epsilon()); SOPHUS_TEST_EQUAL(passed, map_of_const_rxso2.complex().data(), raw.data()); Eigen::Map const_shallow_copy = map_of_const_rxso2; SOPHUS_TEST_EQUAL(passed, const_shallow_copy.complex().eval(), map_of_const_rxso2.complex().eval()); Eigen::Matrix raw2 = {1, 0}; Eigen::Map map_of_rxso2(raw2.data()); SOPHUS_TEST_APPROX(passed, map_of_rxso2.complex().eval(), raw2, Constants::epsilon()); SOPHUS_TEST_EQUAL(passed, map_of_rxso2.complex().data(), raw2.data()); Eigen::Map shallow_copy = map_of_rxso2; SOPHUS_TEST_EQUAL(passed, shallow_copy.complex().eval(), map_of_rxso2.complex().eval()); RxSO2Type const const_so2(raw2); for (int i = 0; i < 2; ++i) { SOPHUS_TEST_EQUAL(passed, const_so2.data()[i], raw2.data()[i]); } RxSO2Type so2(raw2); for (int i = 0; i < 2; ++i) { so2.data()[i] = raw[i]; } for (int i = 0; i < 2; ++i) { SOPHUS_TEST_EQUAL(passed, so2.data()[i], raw.data()[i]); } // regression: test that rotationMatrix API doesn't change underlying value // for non-const-map and compiles at all for const-map Eigen::Matrix raw3 = {Scalar(2), Scalar(0)}; Eigen::Map map_of_rxso2_3(raw3.data()); Eigen::Map const_map_of_rxso2_3(raw3.data()); RxSO2Type rxso2_copy3 = map_of_rxso2_3; const RotationMatrixType r_ref = map_of_rxso2_3.so2().matrix(); const RotationMatrixType r = map_of_rxso2_3.rotationMatrix(); SOPHUS_TEST_APPROX(passed, r_ref, r, Constants::epsilon()); SOPHUS_TEST_APPROX(passed, map_of_rxso2_3.complex().eval(), rxso2_copy3.complex().eval(), Constants::epsilon()); const RotationMatrixType r_const = const_map_of_rxso2_3.rotationMatrix(); SOPHUS_TEST_APPROX(passed, r_ref, r_const, Constants::epsilon()); SOPHUS_TEST_APPROX(passed, const_map_of_rxso2_3.complex().eval(), rxso2_copy3.complex().eval(), Constants::epsilon()); Eigen::Matrix data1, data2; data1 << Scalar(.1), Scalar(.2); data2 << Scalar(.5), Scalar(.4); Eigen::Map map1(data1.data()), map2(data2.data()); // map -> map assignment map2 = map1; SOPHUS_TEST_EQUAL(passed, map1.matrix(), map2.matrix()); // map -> type assignment RxSO2Type copy; copy = map1; SOPHUS_TEST_EQUAL(passed, map1.matrix(), copy.matrix()); // type -> map assignment copy = RxSO2Type::exp(Tangent(Scalar(0.2), Scalar(0.5))); map1 = copy; SOPHUS_TEST_EQUAL(passed, map1.matrix(), copy.matrix()); return passed; } bool testConstructors() { bool passed = true; RxSO2Type rxso2; Scalar scale(1.2); rxso2.setScale(scale); SOPHUS_TEST_APPROX(passed, scale, rxso2.scale(), Constants::epsilon(), "setScale"); Scalar angle(0.2); rxso2.setAngle(angle); SOPHUS_TEST_APPROX(passed, angle, rxso2.angle(), Constants::epsilon(), "setAngle"); SOPHUS_TEST_APPROX(passed, scale, rxso2.scale(), Constants::epsilon(), "setAngle leaves scale as is"); auto so2 = rxso2_vec_[0].so2(); rxso2.setSO2(so2); SOPHUS_TEST_APPROX(passed, scale, rxso2.scale(), Constants::epsilon(), "setSO2"); SOPHUS_TEST_APPROX(passed, RxSO2Type(scale, so2).matrix(), rxso2.matrix(), Constants::epsilon(), "RxSO2(scale, SO2)"); SOPHUS_TEST_APPROX(passed, RxSO2Type(scale, so2.matrix()).matrix(), rxso2.matrix(), Constants::epsilon(), "RxSO2(scale, SO2)"); Matrix2 R = SO2::exp(Scalar(0.2)).matrix(); Matrix2 sR = R * Scalar(1.3); SOPHUS_TEST_APPROX(passed, RxSO2Type(sR).matrix(), sR, Constants::epsilon(), "RxSO2(sR)"); rxso2.setScaledRotationMatrix(sR); SOPHUS_TEST_APPROX(passed, sR, rxso2.matrix(), Constants::epsilon(), "setScaleRotationMatrix"); rxso2.setScale(scale); rxso2.setRotationMatrix(R); SOPHUS_TEST_APPROX(passed, R, rxso2.rotationMatrix(), Constants::epsilon(), "setRotationMatrix"); SOPHUS_TEST_APPROX(passed, scale, rxso2.scale(), Constants::epsilon(), "setScale"); return passed; } std::vector> rxso2_vec_; std::vector> tangent_vec_; std::vector> point_vec_; }; int test_rxso2() { using std::cerr; using std::endl; cerr << "Test RxSO2" << endl << endl; cerr << "Double tests: " << endl; Tests().runAll(); cerr << "Float tests: " << endl; Tests().runAll(); #if SOPHUS_CERES cerr << "ceres::Jet tests: " << endl; Tests>().runAll(); #endif return 0; } } // namespace Sophus int main() { return Sophus::test_rxso2(); }