Search code examples
c++eigen3

Mapping a custom struct with a double array to Eigen::Matrix


i have this c struct from a third party library:

typedef struct Point3d
{
#ifdef __cplusplus  
  enum{ SIZE = 3 };
  typedef double value_type;                                          ///< type export 
  double&       operator[](size_t index)       { return vec[index];}  ///< random-access
  const double& operator[](size_t index) const { return vec[index];}  ///< random-access
#endif

  double vec[3];        ///< point data, e.g. 0=x,1=y,2=z
  void* data; ///< pointer to additional user-defined data
} Point3d;

this struct is used as an array like this:

Point3d* mypoints = new Point3d[200];

i now want to map this array - specifically the 3 doubles saved in Point3d::vec - to an Eigen::Matrix<double, 3, Eigen::Dynamic>. I do this by following this answer:

https://stackoverflow.com/a/58980755/10825362

which resulted in this function:

Eigen::Matrix<double, 3, Eigen::Dynamic> exportToEigen(Point3d *points, int size)
{
    Eigen::MatrixXd result(size, 3);
    constexpr int stride = sizeof(ALG3D_Point3d)/sizeof(double);

    Eigen::VectorXd X = 
        Eigen::Map<Eigen::VectorXd, Eigen::Unaligned, Eigen::InnerStride<stride>>(
            &points[0].vec[0], size
        );

    Eigen::VectorXd Y = 
        Eigen::Map<Eigen::VectorXd, Eigen::Unaligned, Eigen::InnerStride<stride>>(
            &points[0].vec[1], size
        );

    Eigen::VectorXd Z = 
        Eigen::Map<Eigen::VectorXd, Eigen::Unaligned, Eigen::InnerStride<stride>>(
            &points[0].vec[2], size
        );

    result << X, Y, Z;
    return result.transpose();
}

now i was wondering if this can be done without the need for the temporary X, Y, Z Eigen::VectorXds?


Solution

  • now want to map this array - specifically the 3 doubles saved in Point3d::vec - to an Eigen::Matrix<double, 3, Eigen::Dynamic>

    There is a typedef for Eigen::Matrix<double, 3, Eigen::Dynamic> and other common types. You can simply write Eigen::Matrix3Xd, just fyi.

    Anyway, try this:

    using Point3dStride = Eigen::OuterStride<sizeof(Point3d) / sizeof(double)>;
    using Point3dMap = Eigen::Map<
          Eigen::Matrix3Xd, Eigen::Unaligned, Point3dStride>;
    using ConstPoint3dMap = Eigen::Map<
          const Eigen::Matrix3Xd, Eigen::Unaligned, Point3dStride>;
    
    Point3dMap exportToEigen(Point3d *points, Eigen::Index size) noexcept
    { return {points->vec, 3 /*rows*/, size}; }
    
    ConstPoint3dMap exportToEigen(const Point3d *points, Eigen::Index size) noexcept
    { return {points->vec, 3 /*rows*/, size}; }
    

    Note: This creates a plain map that references the old data, not a copy. Your code did a copy. If you want that, either assign the return value to a Matrix3Xd or change the code to this:

    Eigen::Matrix3Xd copyToEigen(const Point3d *points, Eigen::Index size) noexcept
    { return ConstPoint3dMap{points->vec, 3 /*rows*/, size}; }