読者です 読者をやめる 読者になる 読者になる

ゆとりーなの日記

日記的な事を書いて行くと思はれる

自作ライブラリのBoost.Geometry対応

 取り敢えず折角1.47.0で入ったので暫定対応してみました。取り敢えず描画オブジェクトが継承するdrawable_facedeの最後にBoost.Geometry用のtagを入れるテンプレート引数を追加しました。

template <typename Drawable, // 描画オブジェクトの型
          std::size_t Size, // 頂点構造体の要素数
          std::uint32_t Count, // 描画カウント
          typename FVFTag, // FVFタグ
          typename PrimitiveTag, // プリミティブタグ
          typename GeometryTag> // Boost.Geometry用のタグ
struct drawable_facade;

// 点描画オブジェクト
class pixel : public drawable_facade<pixel, 
                                     1,
                                     1, 
                                     shape_2d_fvf_tag,
                                     point_list_primitive_tag,
                                     boost::geometry::point_tag> {
  // ...
};

// 線描画オブジェクト
class line : public drawable_facade<line,
                                    2,
                                    2,
                                    shape_2d_fvf_tag,
                                    line_list_primitive_tag,
                                    boost::geometry::linestring_tag> {
  // ...
};

// 等々...

 で、Geometryとして描画オブジェクトを扱うにはgeometryedという安直な名前のアダプタを噛ませてやることで適用できますみたいな感じです。

int main() {
  ngy313::triangle ring({1.f, 1.f}, {2.f, 1.f}, {2.f, 2.f});
  std::cout << "Area: "  << boost::geometry::area(ring | ngy313::moved({320.f, 240.f}) | ngy313::geometryed) << std::endl; 
  return 0;
}

出力

Area: 0.5

現状の実装はこんな感じになってます。

namespace ngy313 { namespace geometryed_adaptor { namespace detail {
template <typename Vertex>
struct vertex_type {
  typedef typename std::decay<Vertex>::type::value_type type;
};

template <typename Vertex>
struct iterator_type {
  typedef typename std::decay<Vertex>::type::const_iterator type;
};

// 頂点配列から余計な情報を省いて初期化
template <typename PrimitveTag, typename = void>
struct init_traits {
  template <typename Drawable>
  static std::vector<rhw_position_t> init(const Drawable &drawable) {
    typedef vertex_type<decltype(drawable.vertex())>::type vertex_type; 
    typedef boost::transform_iterator<
              const rhw_position_t &(*)(const vertex_type &),
              iterator_type<decltype(drawable.vertex())>::type> itertor_type;
    const auto vertex = drawable.vertex();
    return std::vector<rhw_position_t>(itertor_type(
                                           vertex.begin(), 
                                           &vertex_position),
                                       itertor_type(
                                           vertex.end(),
                                           &vertex_position));
  }
};

// 頂点配列から余計な情報を省いて初期化
// triangle_list_primitive_tagの場合は頂点が閉じていないので先頭をpush_backして強制的に閉じる特殊化
template <>
struct init_traits<triangle_list_primitive_tag> {
  template <typename Drawable>
  static std::vector<rhw_position_t> init(const Drawable &drawable) {
    typedef vertex_type<decltype(drawable.vertex())>::type vertex_type;
    typedef boost::transform_iterator<
              const rhw_position_t &(*)(const vertex_type &),
              iterator_type<decltype(drawable.vertex())>::type> itertor_type;
    const auto vertex = drawable.vertex();
    std::vector<rhw_position_t> v(itertor_type(
                                      vertex.begin(), 
                                      &vertex_position),
                                  itertor_type(
                                      vertex.end(),
                                      &vertex_position));
    v.push_back(v[0]);
    return v;
  }
};

// boost::geometry::ring_tagのコンセプトを満たすようなクラス
// 単純に頂点のレンジを継承し、保存した頂点配列を突っ込む
struct ring_adaptor 
    : boost::iterator_range<std::vector<rhw_position_t>::const_iterator> {
  template <typename Drawable>
  explicit ring_adaptor(const Drawable &drawable)
      : geometry_(init_traits<typename boost::mpl::at<
                                  typename Drawable::list_type, 
                                  primitive_key>::type>::init(drawable)) {
    ring_adaptor::iterator_range::operator=(geometry_);
  }

 private:
  std::vector<rhw_position_t> geometry_;
};

// boost::geometry::linestring_tagのコンセプトを満たすようなクラス
// 単純に頂点のレンジを継承し、保存した頂点配列を突っ込む
struct linestring_adaptor 
    : boost::iterator_range<std::vector<rhw_position_t>::const_iterator> {
  template <typename Drawable>
  explicit linestring_adaptor(const Drawable &drawable)
      : geometry_(init_traits<line_list_primitive_tag>::init(drawable)) {
    linestring_adaptor::iterator_range::operator=(geometry_);
  }

 private:
  std::vector<rhw_position_t> geometry_;
};

// boost::geometry::point_tagのコンセプトを満たすようなクラス
struct point_adaptor {
  template <typename Drawable>
  explicit point_adaptor(const Drawable &drawable)
      : x(shape_position_x(drawable, 0)), x(shape_position_y(drawable, 0)) {}

  float x;
  float y;
};

// boost::geometry::box_tagのコンセプトを満たすようなクラsう
struct box_adaptor {
  template <typename Drawable>
  explicit box_adaptor(const Drawable &drawable) 
      : drawable_(drawable) {}

  rhw_position_t ll;
  rhw_position_t ur;
};

// タグディスパッチ
template <typename Tag>
struct adaptor_traits {};

template <>
struct adaptor_traits<boost::geometry::ring_tag> {
  typedef ring_adaptor type;
};

template <>
struct adaptor_traits<boost::geometry::linestring_tag> {
  typedef linestring_adaptor type;
};

template <>
struct adaptor_traits<boost::geometry::point_tag> {
  typedef point_adaptor type;
};

template <>
struct adaptor_traits<boost::geometry::box_tag> {
  typedef box_adaptor type;
};
}}}

// boost::geometry::ring_tagを持つ描画オブジェクトの頂点の順番は
// カリングとかの関係上順番は半時計周りにしておかないと描画されないので特殊化しておく
namespace boost { namespace geometry { namespace traits {
template <>
struct point_order<ngy313::geometryed_adaptor::detail::ring_adaptor> {
  static const order_selector value = counterclockwise;
};
}}}

// Geometryにそれぞれアダプト
BOOST_GEOMETRY_REGISTER_BOX(ngy313::geometryed_adaptor::detail::box_adaptor,
                            ngy313::rhw_position_t,
                            ll,
                            ur);
BOOST_GEOMETRY_REGISTER_LINESTRING(
    ngy313::geometryed_adaptor::detail::linestring_adaptor);
BOOST_GEOMETRY_REGISTER_POINT_2D_CONST(
    ngy313::geometryed_adaptor::detail::point_adaptor,
    float,
    cs::cartesian,
    x,
    y);
BOOST_GEOMETRY_REGISTER_RING(ngy313::geometryed_adaptor::detail::ring_adaptor);

namespace ngy313 { namespace geometryed_adaptor {
// 函数版
template <typename Drawable>
typename detail::adaptor_traits<
    typename boost::mpl::at<
        typename Drawable::list_type, 
        geometry_key>::type>::type geometry(const Drawable &drawable) {
  return typename detail::adaptor_traits<
             typename boost::mpl::at<
                 typename Drawable::list_type, 
                 geometry_key>::type>::type(drawable);
}

// |に突っ込める形のバージョン
const struct geometryed_t : pipe_operator::base<geometryed_t> {
  template <typename Drawable>
  auto operator ()(const Drawable &drawable) const -> decltype(geometry(drawable)) {
    return geometry(drawable);
  }
} geometryed = geometryed_t();
}}

namespace ngy313 {
using geometryed_adaptor::geometry;
using geometryed_adaptor::geometryed;
}

 あんまり綺麗じゃないですし、密かに把握してる問題等あったりするので(コンセプトをぶっ壊すアダプタとかがあったりする、とある一頂点だけ移動するとか)、そこらへんは後々直していくという方向で。