6

I'm wrapping two Eigen3 vectors in a templated LineSegment<T,dim> class. You might use it like this:

typedef LineSegment<double,2> LineSegment2d;
typedef LineSegment<double,3> LineSegment3d;
typedef LineSegment<int,3> LineSegment3i;

It contains a templated method to change the dimensions of the components. Here is the trimmed definition:

template<typename T,int dim>
struct LineSegment
{
public:
  template<int newDim>
  LineSegment<T,newDim> to() const
  {
    Eigen::Matrix<T,newDim,1> newp1;
    Eigen::Matrix<T,newDim,1> newp2;

    // TODO initialise newp1 and newp2 from d_p1 and d_p2

    return LineSegment<T,newDim>(newp1, newp2);
  }

  // ... other members ...

protected:
  Eigen::Matrix<T,dim,1> d_p1;
  Eigen::Matrix<T,dim,1> d_p2;
}

So my question is, how can I compose the return value, as shown above? This should support both increasing and decreasing the dimension.

I tried using the Eigen3 resize(int) method, but couldn't get it to work without seeing warnings about mixing matrix sizes.

Ultimately, this should work:

LineSegment2d ls2d;
LineSegment3d ls3d = ls2d.to<3>(); // increase dim
ls2d = ls3d.to<2>();               // decrease dim

I'm relatively new to C++ templates, and would appreciate a bit of explanation if this isn't just an API question and is related to templates.

Drew Noakes
  • 300,895
  • 165
  • 679
  • 742
  • What are the desired semantics of the dimension change? Projection onto first coordinates and expansion with zero values in the new coordinates? – us2012 Mar 18 '13 at 17:21
  • @us2012, the template is to be used rather generally as part of a robotics system that will primarily be converting between 2D line segments from the camera image to 3D line segments from the map of its surroundings. This conversion is bidirectional. I could use subclasses rather than typedefs and add specific `to3` and `to2` methods, but wondered if there's a way of doing this. The only example I've seen of resizing arrays is for dynamically sized vectors such as `VectorXd`. – Drew Noakes Mar 18 '13 at 17:30
  • In Eigen2, I believe you could use `.start<3>()`, but I may be mistaken. – Drew Noakes Mar 18 '13 at 17:34

2 Answers2

7

Firstly, Eigen's resize method reallocates memory if the new number of elements is not the same as the old, both when growing and when shrinking, so you would lose data in this case

The following method uses .head<int>(), which is Eigen3's version of .start<int>(), plus some template programming so you don't have to check whether you're shrinking or growing:

#include <Eigen/Core>

template <bool COND, int A, int B>
struct IF
{
  enum { val = A };
};

template <int A, int B>
struct IF<false, A, B>
{
  enum { val = B };
};

template <int A, int B>
struct MIN : IF<A < B, A, B>
{
};

template <typename T,int dim,int newDim>
Eigen::Matrix<T,newDim,1> to(Eigen::Matrix<T,dim,1> p)
{
  Eigen::Matrix<int,newDim,1> newp =
    Eigen::Matrix<T,newDim,1>::Zero();

  newp.template head< MIN<dim,newDim>::val >() =
    p.template head< MIN<dim,newDim>::val >();

  return newp;
}

Using this, the following program:

#include <iostream>

int main()
{
  Eigen::Vector2i p_2i(1,2);
  Eigen::Vector3i p_3i(3,4,5);

  std::cout << to<int, 2, 3>(p_2i) << std::endl << std::endl;
  std::cout << to<int, 3, 2>(p_3i) << std::endl << std::endl;

}

Gives as output:

1
2
0

3
4
sgvd
  • 3,819
  • 18
  • 31
0

For completeness, here's the solution in situ, using @sgvd's technique which did the job perfectly:

template<typename T,int dim>
struct LineSegment
{
public:
  template<int newDim>
  LineSegment<T,newDim> to() const
  {
    Eigen::Matrix<T,newDim,1> newp1;
    Eigen::Matrix<T,newDim,1> newp2;

    newp1.template head< MIN<dim,newDim>::val >() = d_p1.template head< MIN<dim,newDim>::val >();
    newp2.template head< MIN<dim,newDim>::val >() = d_p2.template head< MIN<dim,newDim>::val >();

    return LineSegment<T,newDim>(newp1, newp2);
  }

  // ... other members ...

protected:
  Eigen::Matrix<T,dim,1> d_p1;
  Eigen::Matrix<T,dim,1> d_p2;

private:
  template <bool COND, int A, int B>
  struct IF
  {
    enum { val = A };
  };

  template <int A, int B>
  struct IF<false, A, B>
  {
    enum { val = B };
  };

  template <int A, int B>
  struct MIN : IF<A < B, A, B>
  {};
}

And a unit test that passes:

TEST (LineSegmentTests, to)
{
  EXPECT_EQ ( LineSegment3i(Vector3i(1,2,0), Vector3i(3,4,0)),
              LineSegment2i(Vector2i(1,2),   Vector2i(3,4)  ).to<3>() );

  EXPECT_EQ ( LineSegment2i(Vector2i(1,2),   Vector2i(4,5)),
              LineSegment3i(Vector3i(1,2,3), Vector3i(4,5,6)).to<2>() );

  EXPECT_EQ ( LineSegment3i(Vector3i(1,2,3), Vector3i(4,5,6)),
              LineSegment3i(Vector3i(1,2,3), Vector3i(4,5,6)).to<3>() );
}
Community
  • 1
  • 1
Drew Noakes
  • 300,895
  • 165
  • 679
  • 742