Program Listing for File rxso2.hpp

Return to documentation for file (sophus/rxso2.hpp)

#pragma once

#include "so2.hpp"

namespace Sophus {
template <class Scalar_, int Options = 0>
class RxSO2;
using RxSO2d = RxSO2<double>;
using RxSO2f = RxSO2<float>;
}  // namespace Sophus

namespace Eigen {
namespace internal {

template <class Scalar_, int Options_>
struct traits<Sophus::RxSO2<Scalar_, Options_>> {
  static constexpr int Options = Options_;
  using Scalar = Scalar_;
  using ComplexType = Sophus::Vector2<Scalar, Options>;
};

template <class Scalar_, int Options_>
struct traits<Map<Sophus::RxSO2<Scalar_>, Options_>>
    : traits<Sophus::RxSO2<Scalar_, Options_>> {
  static constexpr int Options = Options_;
  using Scalar = Scalar_;
  using ComplexType = Map<Sophus::Vector2<Scalar>, Options>;
};

template <class Scalar_, int Options_>
struct traits<Map<Sophus::RxSO2<Scalar_> const, Options_>>
    : traits<Sophus::RxSO2<Scalar_, Options_> const> {
  static constexpr int Options = Options_;
  using Scalar = Scalar_;
  using ComplexType = Map<Sophus::Vector2<Scalar> const, Options>;
};
}  // namespace internal
}  // namespace Eigen

namespace Sophus {

template <class Derived>
class RxSO2Base {
 public:
  static constexpr int Options = Eigen::internal::traits<Derived>::Options;
  using Scalar = typename Eigen::internal::traits<Derived>::Scalar;
  using ComplexType = typename Eigen::internal::traits<Derived>::ComplexType;
  using ComplexTemporaryType = Sophus::Vector2<Scalar, Options>;

  static int constexpr DoF = 2;
  static int constexpr num_parameters = 2;
  static int constexpr N = 2;
  static int constexpr Dim = 2;
  using Transformation = Matrix<Scalar, N, N>;
  using Point = Vector2<Scalar>;
  using HomogeneousPoint = Vector3<Scalar>;
  using Line = ParametrizedLine2<Scalar>;
  using Hyperplane = Hyperplane2<Scalar>;
  using Tangent = Vector<Scalar, DoF>;
  using Adjoint = Matrix<Scalar, DoF, DoF>;

  template <typename OtherDerived>
  using ReturnScalar = typename Eigen::ScalarBinaryOpTraits<
      Scalar, typename OtherDerived::Scalar>::ReturnType;

  template <typename OtherDerived>
  using RxSO2Product = RxSO2<ReturnScalar<OtherDerived>>;

  template <typename PointDerived>
  using PointProduct = Vector2<ReturnScalar<PointDerived>>;

  template <typename HPointDerived>
  using HomogeneousPointProduct = Vector3<ReturnScalar<HPointDerived>>;

  SOPHUS_FUNC Adjoint Adj() const { return Adjoint::Identity(); }

  SOPHUS_FUNC Scalar angle() const { return SO2<Scalar>(complex()).log(); }

  template <class NewScalarType>
  SOPHUS_FUNC RxSO2<NewScalarType> cast() const {
    typename RxSO2<NewScalarType>::ComplexType c =
        complex().template cast<NewScalarType>();
    return RxSO2<NewScalarType>(c);
  }

  SOPHUS_FUNC Scalar* data() { return complex_nonconst().data(); }

  SOPHUS_FUNC Scalar const* data() const { return complex().data(); }

  SOPHUS_FUNC RxSO2<Scalar> inverse() const {
    Scalar squared_scale = complex().squaredNorm();
    Vector2<Scalar> xy = complex() / squared_scale;
    return RxSO2<Scalar>(xy.x(), -xy.y());
  }

  SOPHUS_FUNC Tangent log() const {
    using std::log;
    Tangent theta_sigma;
    theta_sigma[1] = log(scale());
    theta_sigma[0] = SO2<Scalar>(complex()).log();
    return theta_sigma;
  }

  SOPHUS_FUNC Transformation matrix() const {
    Transformation sR;
    // clang-format off
    sR << complex()[0], -complex()[1],
          complex()[1],  complex()[0];
    // clang-format on
    return sR;
  }

  template <class OtherDerived>
  SOPHUS_FUNC RxSO2Base<Derived>& operator=(
      RxSO2Base<OtherDerived> const& other) {
    complex_nonconst() = other.complex();
    return *this;
  }

  template <typename OtherDerived>
  SOPHUS_FUNC RxSO2Product<OtherDerived> operator*(
      RxSO2Base<OtherDerived> const& other) const {
    using ResultT = ReturnScalar<OtherDerived>;

    Scalar lhs_real = complex().x();
    Scalar lhs_imag = complex().y();
    typename OtherDerived::Scalar const& rhs_real = other.complex().x();
    typename OtherDerived::Scalar const& rhs_imag = other.complex().y();
    typename RxSO2Product<OtherDerived>::ComplexType result_complex(
        lhs_real * rhs_real - lhs_imag * rhs_imag,
        lhs_real * rhs_imag + lhs_imag * rhs_real);

    const ResultT squared_scale = result_complex.squaredNorm();

    if (squared_scale <
        Constants<ResultT>::epsilon() * Constants<ResultT>::epsilon()) {
      result_complex.normalize();
      result_complex *= Constants<ResultT>::epsilonPlus();
    }
    if (squared_scale > Scalar(1.) / (Constants<ResultT>::epsilon() *
                                      Constants<ResultT>::epsilon())) {
      result_complex.normalize();
      result_complex /= Constants<ResultT>::epsilonPlus();
    }
    return RxSO2Product<OtherDerived>(result_complex);
  }

  template <typename PointDerived,
            typename = typename std::enable_if<
                IsFixedSizeVector<PointDerived, 2>::value>::type>
  SOPHUS_FUNC PointProduct<PointDerived> operator*(
      Eigen::MatrixBase<PointDerived> const& p) const {
    return matrix() * p;
  }

  template <typename HPointDerived,
            typename = typename std::enable_if<
                IsFixedSizeVector<HPointDerived, 3>::value>::type>
  SOPHUS_FUNC HomogeneousPointProduct<HPointDerived> operator*(
      Eigen::MatrixBase<HPointDerived> const& p) const {
    const auto rsp = *this * p.template head<2>();
    return HomogeneousPointProduct<HPointDerived>(rsp(0), rsp(1), p(2));
  }

  SOPHUS_FUNC Line operator*(Line const& l) const {
    return Line((*this) * l.origin(), (*this) * l.direction() / scale());
  }

  SOPHUS_FUNC Hyperplane operator*(Hyperplane const& p) const {
    const auto this_scale = scale();
    return Hyperplane((*this) * p.normal() / this_scale,
                      this_scale * p.offset());
  }

  template <typename OtherDerived,
            typename = typename std::enable_if<
                std::is_same<Scalar, ReturnScalar<OtherDerived>>::value>::type>
  SOPHUS_FUNC RxSO2Base<Derived>& operator*=(
      RxSO2Base<OtherDerived> const& other) {
    *static_cast<Derived*>(this) = *this * other;
    return *this;
  }

  SOPHUS_FUNC Matrix<Scalar, num_parameters, DoF> Dx_this_mul_exp_x_at_0()
      const {
    Matrix<Scalar, num_parameters, DoF> J;
    J << -complex().y(), complex().x(), complex().x(), complex().y();
    return J;
  }

  SOPHUS_FUNC Matrix<Scalar, DoF, num_parameters> Dx_log_this_inv_by_x_at_this()
      const {
    Matrix<Scalar, DoF, num_parameters> J;
    const Scalar norm_sq_inv = Scalar(1.) / complex().squaredNorm();
    J << -complex().y(), complex().x(), complex().x(), complex().y();
    return J * norm_sq_inv;
  }

  SOPHUS_FUNC Sophus::Vector<Scalar, num_parameters> params() const {
    return complex();
  }

  SOPHUS_FUNC void setComplex(Vector2<Scalar> const& z) {
    SOPHUS_ENSURE(z.squaredNorm() > Constants<Scalar>::epsilon() *
                                        Constants<Scalar>::epsilon(),
                  "Scale factor must be greater-equal epsilon.");
    SOPHUS_ENSURE(z.squaredNorm() < Scalar(1.) / (Constants<Scalar>::epsilon() *
                                                  Constants<Scalar>::epsilon()),
                  "Inverse scale factor must be greater-equal epsilon.");
    static_cast<Derived*>(this)->complex_nonconst() = z;
  }

  SOPHUS_FUNC ComplexType const& complex() const {
    return static_cast<Derived const*>(this)->complex();
  }

  SOPHUS_FUNC Transformation rotationMatrix() const {
    ComplexTemporaryType norm_quad = complex();
    norm_quad.normalize();
    return SO2<Scalar>(norm_quad).matrix();
  }

  SOPHUS_FUNC Scalar scale() const {
    using std::hypot;
    return hypot(complex().x(), complex().y());
  }

  SOPHUS_FUNC void setAngle(Scalar const& theta) { setSO2(SO2<Scalar>(theta)); }

  SOPHUS_FUNC void setRotationMatrix(Transformation const& R) {
    setSO2(SO2<Scalar>(R));
  }

  SOPHUS_FUNC void setScale(Scalar const& scale) {
    using std::sqrt;
    complex_nonconst().normalize();
    complex_nonconst() *= scale;
  }

  SOPHUS_FUNC void setScaledRotationMatrix(Transformation const& sR) {
    SOPHUS_ENSURE(isScaledOrthogonalAndPositive(sR),
                  "sR must be scaled orthogonal:\n {}", SOPHUS_FMT_ARG(sR));
    complex_nonconst() = sR.col(0);
  }

  SOPHUS_FUNC void setSO2(SO2<Scalar> const& so2) {
    using std::sqrt;
    Scalar saved_scale = scale();
    complex_nonconst() = so2.unit_complex();
    complex_nonconst() *= saved_scale;
  }

  SOPHUS_FUNC SO2<Scalar> so2() const { return SO2<Scalar>(complex()); }

 private:
  SOPHUS_FUNC ComplexType& complex_nonconst() {
    return static_cast<Derived*>(this)->complex_nonconst();
  }
};

template <class Scalar_, int Options>
class RxSO2 : public RxSO2Base<RxSO2<Scalar_, Options>> {
 public:
  using Base = RxSO2Base<RxSO2<Scalar_, Options>>;
  using Scalar = Scalar_;
  using Transformation = typename Base::Transformation;
  using Point = typename Base::Point;
  using HomogeneousPoint = typename Base::HomogeneousPoint;
  using Tangent = typename Base::Tangent;
  using Adjoint = typename Base::Adjoint;
  using ComplexMember = Eigen::Matrix<Scalar, 2, 1, Options>;

  friend class RxSO2Base<RxSO2<Scalar_, Options>>;

  using Base::operator=;

  SOPHUS_FUNC RxSO2& operator=(RxSO2 const& other) = default;

  static int constexpr DoF = Base::DoF;
  static int constexpr num_parameters = Base::num_parameters;

  EIGEN_MAKE_ALIGNED_OPERATOR_NEW

  SOPHUS_FUNC RxSO2() : complex_(Scalar(1), Scalar(0)) {}

  SOPHUS_FUNC RxSO2(RxSO2 const& other) = default;

  template <class OtherDerived>
  SOPHUS_FUNC RxSO2(RxSO2Base<OtherDerived> const& other)
      : complex_(other.complex()) {}

  SOPHUS_FUNC explicit RxSO2(Transformation const& sR) {
    this->setScaledRotationMatrix(sR);
  }

  SOPHUS_FUNC RxSO2(Scalar const& scale, Transformation const& R)
      : RxSO2((scale * SO2<Scalar>(R).unit_complex()).eval()) {}

  SOPHUS_FUNC RxSO2(Scalar const& scale, SO2<Scalar> const& so2)
      : RxSO2((scale * so2.unit_complex()).eval()) {}

  SOPHUS_FUNC explicit RxSO2(Vector2<Scalar> const& z) : complex_(z) {
    SOPHUS_ENSURE(complex_.squaredNorm() >= Constants<Scalar>::epsilon() *
                                                Constants<Scalar>::epsilon(),
                  "Scale factor must be greater-equal epsilon: {} vs {}",
                  SOPHUS_FMT_ARG(complex_.squaredNorm()),
                  SOPHUS_FMT_ARG(Constants<Scalar>::epsilon() *
                                 Constants<Scalar>::epsilon()));
    SOPHUS_ENSURE(
        complex_.squaredNorm() <= Scalar(1.) / (Constants<Scalar>::epsilon() *
                                                Constants<Scalar>::epsilon()),
        "Inverse scale factor must be greater-equal epsilon: {} vs {}",
        SOPHUS_FMT_ARG(Scalar(1.) / complex_.squaredNorm()),
        SOPHUS_FMT_ARG(Constants<Scalar>::epsilon() *
                       Constants<Scalar>::epsilon()));
  }

  SOPHUS_FUNC explicit RxSO2(Scalar const& real, Scalar const& imag)
      : RxSO2(Vector2<Scalar>(real, imag)) {}

  SOPHUS_FUNC ComplexMember const& complex() const { return complex_; }

  SOPHUS_FUNC static Sophus::Matrix<Scalar, num_parameters, DoF> Dx_exp_x(
      Tangent const& a) {
    using std::cos;
    using std::exp;
    using std::sin;
    Scalar const theta = a[0];
    Scalar const sigma = a[1];

    Sophus::Matrix<Scalar, num_parameters, DoF> J;
    J << -sin(theta), cos(theta), cos(theta), sin(theta);
    return J * exp(sigma);
  }

  SOPHUS_FUNC static Sophus::Matrix<Scalar, num_parameters, DoF>
  Dx_exp_x_at_0() {
    Sophus::Matrix<Scalar, num_parameters, DoF> J;
    static Scalar const i(1.);
    static Scalar const o(0.);
    J << o, i, i, o;
    return J;
  }

  SOPHUS_FUNC static Sophus::Matrix<Scalar, 2, DoF> Dx_exp_x_times_point_at_0(
      Point const& point) {
    Sophus::Matrix<Scalar, 2, DoF> j;
    j << Sophus::SO2<Scalar>::Dx_exp_x_times_point_at_0(point), point;
    return j;
  }

  SOPHUS_FUNC static Transformation Dxi_exp_x_matrix_at_0(int i) {
    return generator(i);
  }

  SOPHUS_FUNC static RxSO2<Scalar> exp(Tangent const& a) {
    using std::exp;
    using std::max;
    using std::min;

    Scalar const theta = a[0];
    Scalar const sigma = a[1];
    Scalar s = exp(sigma);
    // Ensuring proper scale
    s = max(s, Constants<Scalar>::epsilonPlus());
    s = min(s, Scalar(1.) / Constants<Scalar>::epsilonPlus());
    Vector2<Scalar> z = SO2<Scalar>::exp(theta).unit_complex();
    z *= s;
    return RxSO2<Scalar>(z);
  }

  SOPHUS_FUNC static Transformation generator(int i) {
    SOPHUS_ENSURE(i >= 0 && i <= 1, "i should be 0 or 1.");
    Tangent e;
    e.setZero();
    e[i] = Scalar(1);
    return hat(e);
  }

  SOPHUS_FUNC static Transformation hat(Tangent const& a) {
    Transformation A;
    // clang-format off
    A << a(1), -a(0),
         a(0),  a(1);
    // clang-format on
    return A;
  }

  SOPHUS_FUNC static Tangent lieBracket(Tangent const&, Tangent const&) {
    Vector2<Scalar> res;
    res.setZero();
    return res;
  }

  template <class UniformRandomBitGenerator>
  static RxSO2 sampleUniform(UniformRandomBitGenerator& generator) {
    std::uniform_real_distribution<Scalar> uniform(Scalar(-1), Scalar(1));
    using std::exp2;
    return RxSO2(exp2(uniform(generator)),
                 SO2<Scalar>::sampleUniform(generator));
  }

  SOPHUS_FUNC static Tangent vee(Transformation const& Omega) {
    using std::abs;
    return Tangent(Omega(1, 0), Omega(0, 0));
  }

 protected:
  SOPHUS_FUNC ComplexMember& complex_nonconst() { return complex_; }

  ComplexMember complex_;
};

}  // namespace Sophus

namespace Eigen {

template <class Scalar_, int Options>
class Map<Sophus::RxSO2<Scalar_>, Options>
    : public Sophus::RxSO2Base<Map<Sophus::RxSO2<Scalar_>, Options>> {
  using Base = Sophus::RxSO2Base<Map<Sophus::RxSO2<Scalar_>, Options>>;

 public:
  using Scalar = Scalar_;
  using Transformation = typename Base::Transformation;
  using Point = typename Base::Point;
  using HomogeneousPoint = typename Base::HomogeneousPoint;
  using Tangent = typename Base::Tangent;
  using Adjoint = typename Base::Adjoint;

  friend class Sophus::RxSO2Base<Map<Sophus::RxSO2<Scalar_>, Options>>;

  using Base::operator=;
  using Base::operator*=;
  using Base::operator*;

  SOPHUS_FUNC explicit Map(Scalar* coeffs) : complex_(coeffs) {}

  SOPHUS_FUNC
  Map<Sophus::Vector2<Scalar>, Options> const& complex() const {
    return complex_;
  }

 protected:
  SOPHUS_FUNC Map<Sophus::Vector2<Scalar>, Options>& complex_nonconst() {
    return complex_;
  }

  Map<Sophus::Vector2<Scalar>, Options> complex_;
};

template <class Scalar_, int Options>
class Map<Sophus::RxSO2<Scalar_> const, Options>
    : public Sophus::RxSO2Base<Map<Sophus::RxSO2<Scalar_> const, Options>> {
 public:
  using Base = Sophus::RxSO2Base<Map<Sophus::RxSO2<Scalar_> const, Options>>;
  using Scalar = Scalar_;
  using Transformation = typename Base::Transformation;
  using Point = typename Base::Point;
  using HomogeneousPoint = typename Base::HomogeneousPoint;
  using Tangent = typename Base::Tangent;
  using Adjoint = typename Base::Adjoint;

  using Base::operator*=;
  using Base::operator*;

  SOPHUS_FUNC
  explicit Map(Scalar const* coeffs) : complex_(coeffs) {}

  SOPHUS_FUNC
  Map<Sophus::Vector2<Scalar> const, Options> const& complex() const {
    return complex_;
  }

 protected:
  Map<Sophus::Vector2<Scalar> const, Options> const complex_;
};
}  // namespace Eigen