Boost.GeometryでEigenを使う

Boost.Geometry?

皆さん,いきなりですが"Boost.Geometry"をご存知ですか?
一口で言うと「幾何計算をやってくれるC++のライブラリ」なんですが,特筆すべきはその華麗なる設計でC++メタプログラミング大好きマンにとってはバイブルとでも言うべき存在です.そっち系の専門書とかでも良い具体例として引用されています.

具体的なクラス設計とかの話はBoostのドキュメントに書いてあるので興味がある人は読んでみましょう. www.boost.org 英語が苦手という人は日本語訳されている方がいらっしゃいましたのでそちらをどうぞ mmiyano.blogspot.com

Boost.Geometryの柔軟性

さて,そのBoost.Geometryはその"華麗なる設計"によって次の要素を任意に設定することができます.

  • 次元
  • 座標系
  • データ型

例えば,「多倍長浮動小数点数の 4次元極座標系」なんかにも対応することが可能です.(もはやなんでもあり)

Eigenへの対応

さて,今回はみんな大好きEigenの2次元ベクトルをこのBoost.Geometryで使えるようにしていきたいと思います. まず,今回扱う2次元ベクトル,Eigen::Vector2fの基礎情報をチェックしていきますと

項目 Eigen::Vector2fの場合
次元 2
座標系 直交座標系
データ型 float

と言うような感じになります.この情報を元に対応させていきます.

各要素の対応

対応させるときはコードをboost::geometry::traits名前空間に書かないといけないのですが,ここでは省略します.

次元

Eigen::Vecotr2fが2次元であることをBoost.Geometryに教えてあげます.

template<>
struct dimension<Eigen::Vector2f> : boost::mpl::int_<2> {};

座標系

直交座標系であることを教えてあげます.
当然ながら,有名な座標系は予め定義されているのでそれを使います. 頑張れば自己流のオレオレ座標系を作って指定することもできるっぽい!?

template<>
struct coordinate_system<Eigen::Vector2f> {
    typedef cs::cartesian type;
};

データ型

Eigen::Vecotr2ffloatで成り立っていることをBoost.Geometryに教えます.

template<>
struct coordinate_type<Eigen::Vector2f> {
    typedef float type;
};

プリミティブ指定

Eigen::Vector2fがポイント型,すなわち点を表すプリミティブであることを設定します. 点の他にも線やポリゴンなども利用可能です*1

template<>
struct tag<Eigen::Vector2f> {
    typedef point_tag type;
};

要素アクセス

Boost.Geometryの統一的なアクセス法に従って値を取得するgetterと値を設定するsetterを書きます. これによって,Boost.Geometryが自在にEigen::Vector2fを操ることができるようになります.

template<>
struct access<Eigen::Vector2f, 0> {
    static float get(Eigen::Vector2f const &p) {
        return p.x();
    }

    static void set(Eigen::Vector2f &p, float const &value) {
        p.x() = value;
    }
};

template<>
struct access<Eigen::Vector2f, 1> {
    static float get(Eigen::Vector2f const &p) {
        return p.y();
    }

    static void set(Eigen::Vector2f &p, float const &value) {
        p.y() = value;
    }
};

全体像

断片的に解説したのでここでコードの全体像を載せておきます.

#pragma once

#include <boost/geometry.hpp>
#include <Eigen/Core>

namespace boost::geometry::traits {
    template<>
    struct tag<Eigen::Vector2f> {
        typedef point_tag type;
    };
    template<>
    struct coordinate_type<Eigen::Vector2f> {
        typedef float type;
    };

    template<>
    struct coordinate_system<Eigen::Vector2f> {
        typedef cs::cartesian type;
    };

    template<>
    struct dimension<Eigen::Vector2f> : boost::mpl::int_<2> {
    };

    template<>
    struct access<Eigen::zenntaiVector2f, 0> {
        static float get(Eigen::Vector2f const &p) {
            return p.x();
        }

        static void set(Eigen::Vector2f &p, float const &value) {
            p.x() = value;
        }
    };

    template<>
    struct access<Eigen::Vector2f, 1> {
        static float get(Eigen::Vector2f const &p) {
            return p.y();
        }

        static void set(Eigen::Vector2f &p, float const &value) {
            p.y() = value;
        }
    };
}