2014年3月7日金曜日

boost::polygon self intersection problem

ここ2つ前からの投稿の問題は解決しました。この前に geos というライブラリでもテストコードを実行させてみたところ
TopologyException: side location conflict at -752.63088031450525 -148.60182192355231
という例外が発生しました。boost::geometry はロバストに出来ていて、自分が例外キャッチするのを忘れていただけで、ちゃんと
Boost.Geometry Overlay invalid input exception
という例外が送出されていました。  で、どういう問題が発生してたか boost::geometryのコードを読んでみたところ、図形が Simple でない=自己交差を起こしている場合に例外が送出されていました。 なので、自己交差を解消してやれば、問題をクリアできそうな感触を得ました。 残念ながら、自己交差を解消する関数は、見当たらなかったので自作。スタイルが古いですが、古いコンパイラを使っているので auto とか使えなかったんです。 list::insert してるあたりがダサいですが、古い環境なんで、このままです(>_<) 結局、自己交差を解消してやれば、boost::polygon のコードも通りました。
#include <boost/cstdint.hpp>
#include <boost/polygon/polygon.hpp>
#include <vector>
#include <deque>
#include <cmath>
#include <iomanip>

namespace bpl = boost::polygon;
typedef double point_value_type;
typedef bpl::segment_data<point_value_type> Segment;
typedef bpl::polygon_with_holes_data<point_value_type> Polygon;
typedef bpl::polygon_data<point_value_type> Ring;
typedef bpl::polygon_traits<Polygon>::point_type Point;
typedef std::vector<Point> PointSet;
typedef std::vector<Polygon> PolygonSet;
typedef std::list<Segment> SegmentSet;

#define ACCY 1e-6

Polygon make_polygon( const point_value_type* pts, size_t count ) {
  Polygon polygon;
  polygon.self_.coords_.resize( count );
  for( size_t i = 0; i < count; ++i ) {
    polygon.self_.coords_[i].x( *pts++ );
    polygon.self_.coords_[i].y( *pts++ );
  }
  return polygon;
}

void construct( PolygonSet& polygons, Polygon polygon ) {
  using namespace bpl::operators;
  polygons |= polygon; 
}

void construct( SegmentSet& segments, const point_value_type* pts, size_t count ) {
  point_value_type ax = pts[count*2-2];
  point_value_type ay = pts[count*2-1];
  for( size_t i = 0; i < count; ++i ) {
    point_value_type bx = *pts++;
    point_value_type by = *pts++;
    segments.push_back( Segment( Point(ax, ay), Point(bx, by) ) );
    ax = bx;  ay = by;
  }
}

Polygon make_polygon(const SegmentSet& segments ) {
  Polygon polygon;
  size_t count = segments.size();
  polygon.self_.coords_.resize( count );
  size_t i = 0;
  SegmentSet::const_iterator s = segments.begin();
  SegmentSet::const_iterator e = segments.end();
  while( s != e ) {
    polygon.self_.coords_[i++] = bpl::low(*s++);
  }
  return polygon;
}


std::ostream& operator << (std::ostream& os, const Point& p) {
  os << std::setprecision(10);
  os << "{ " << bpl::x(p) << ", " << bpl::y(p) << " },\n";
  return os;
}

std::ostream& operator << (std::ostream& os, const SegmentSet& segments) {
  SegmentSet::const_iterator s = segments.begin();
  SegmentSet::const_iterator e = segments.end();
  while( s != e ) {
    os << bpl::low( *s++ );
  }
  return os;
}

//! 4点交点(線分による完全交差版)を取得する
/*!
 @attention 交点が存在しない場合、x,y は未定義である
 @retval true 交点が存在
 @retval false 交点は存在しない
*/
bool k_4t_close( 
 double& x, //!< [out] 交点X座標値 
 double& y, //!< [out] 交点Y座標値
 double xk, //!< [in] 線分KLの始点X座標値
 double yk, //!< [in] 線分KLの始点Y座標値
 double xl, //!< [in] 線分KLの終点X座標値
 double yl, //!< [in] 線分KLの終点Y座標値
 double xm, //!< [in] 線分MNの始点X座標値
 double ym, //!< [in] 線分MNの始点Y座標値
 double xn, //!< [in] 線分MNの終点X座標値
 double yn   //!< [in] 線分MNの終点Y座標値
) {
 long double xlk = xl - xk;
 long double ylk = yl - yk;
 long double xnm = xn - xm;
 long double ynm = yn - ym;
 long double xmk = xm - xk;
 long double ymk = ym - yk;
 long double det = xnm * ylk - ynm * xlk;
 if( std::fabs( det ) < ACCY ) return false;
 long double s = (xnm * ymk - ynm * xmk ) / det;
 long double t = (xlk * ymk - ylk * xmk ) / det;
 if( s < 0.0 || 1.0 < s || t < 0.0 || 1.0 < t ) return false;
 x = xk + xlk * s;
 y = yk + ylk * s;
 return true;
}

void resolve_segment( SegmentSet& segments ) {
  SegmentSet::iterator is = segments.begin();
  SegmentSet::iterator ie = segments.end();
  while( is != ie ) {
    SegmentSet::iterator cs = segments.begin();
    SegmentSet::iterator ce = segments.end();
    while( cs != ce ) {
      if( cs != is ) {
        if( bpl::intersects( *cs, *is, false ) ) {
          //std::cout << "X" << std::endl;
          double xx, yy;
          Point a = bpl::low( *cs );
          Point b = bpl::high( *cs );
          Point c = bpl::low( *is );
          Point d = bpl::high( *is );
          k_4t_close( xx, yy,
            bpl::x( a ), bpl::y( a ), bpl::x( b ), bpl::y( b ),
            bpl::x( c ), bpl::y( c ), bpl::x( d ), bpl::y( d )
          );
          Point n( xx, yy );
          //std::cout << a << n << b << std::endl;
          *cs = Segment( n, b );
          cs = segments.insert( cs, Segment( a, n ) );
          *is = Segment( n, d );
          is = segments.insert( is, Segment( c, n ) );
        } else {
          //std::cout << "o" << std::endl;
        }
      }
      ++cs;
    }
    ++is;
  }
}


int main() {
  point_value_type pts[] = {
   -162.277344, -684.941406,
   -154.300781, -684.707031,
   -147.291016, -768.253906,
   -148.601562, -752.630859,
   -156.675781, -753.292969,
   -155.425781, -768.546875,
  };
  /*
  point_value_type pts2[] = {
   -155.425781, -768.546875 ,
   -162.277344, -684.941406 ,
   -154.300781, -684.707031 , 
   -148.6018219, -752.6308803 ,
   -147.291016, -768.253906 ,
   -148.601562, -752.630859 ,
   -148.6018219, -752.6308803 ,
   -156.675781, -753.292969 ,
  };
  */
  
  SegmentSet segments;
  construct( segments, pts, 6 );

  std::cout << segments;
  
  resolve_segment( segments );
  
  std::cout << "===== RESOLVE ====\n";
  std::cout << segments;
  
  PolygonSet polygons;
  //construct( polygons, make_polygon( pts, 6 ) );
  construct( polygons, make_polygon( segments ) );
  
  return 0;
}


追記:あれ? insert のあたり、よく考えたら、まずいですかね・・・。

0 件のコメント: