Ticket #6063: boost_polygon_bug6063_fix3.patch

File boost_polygon_bug6063_fix3.patch, 10.9 KB (added by dbfaken@…, 11 years ago)

Third version of fix for boost::polygon bug ticket #6063. Also includes a patch for ticket 6051 (commented and easily removed if desired).

  • detail/minkowski.hpp

    diff -dur orig_polygon/detail/minkowski.hpp polygon/detail/minkowski.hpp
    old new  
    8282}
    8383  template<typename T>
    8484  inline polygon_set_data<T>&
    85   polygon_set_data<T>::resize(coordinate_type resizing, bool corner_fill_arc, unsigned int num_circle_segments) {
     85  polygon_set_data<T>::resize(coordinate_type resizing, bool corner_fill_arc, unsigned int num_circle_segments,
     86                              double minUnroundedAngleDegrees) {
    8687    using namespace ::boost::polygon::operators;
    87     if(!corner_fill_arc) {
     88    if(resizing == 0) return *this;
     89    if(empty()) return *this;
     90    if(!corner_fill_arc || minUnroundedAngleDegrees<=0.0) {
    8891      if(resizing < 0)
    8992        return shrink(-resizing);
    90       if(resizing > 0)
     93      else
    9194        return bloat(resizing);
    92       return *this;
    9395    }
    94     if(resizing == 0) return *this;
    95     if(empty()) return *this;
     96
    9697    if(num_circle_segments < 3) num_circle_segments = 4;
    9798    rectangle_data<coordinate_type> rect;
    9899    extents(rect);
     
    100101      ::boost::polygon::bloat(rect, 10);
    101102      (*this) = rect - (*this); //invert
    102103    }
    103     //make_arc(std::vector<point_data< T> >& return_points, 
    104     //point_data< double> start, point_data< double>  end,
    105     //point_data< double> center,  double r, unsigned int num_circle_segments)     
     104
     105    // scale arc up so that minimum radius corresponds to 'resizing',
     106    // for bug ticket #6063.
     107    const double our_pi=3.1415926535897932384626433832795028841971;
     108    double arcRadScaling = 1.0/cos(our_pi/double(num_circle_segments));
     109    double arcRadius = double(std::abs(resizing))*arcRadScaling;
     110
     111    if(minUnroundedAngleDegrees < 180.0)
     112    {
     113        // Don't round corners less than given angle (in radians).
     114        // We do this by setting the minimum radius of the 'circle' to correspond to
     115        // the distance to the 'elbow' created by normal offset of a corner with the given angle.
     116        // e.g. an 90-degree corner with an offset of 1 will generate an elbow-corner at distance sqrt(2),
     117        // so if \param minUnroundedAngleDegrees=90, we set the circle radius to offset*sqrt(2)*arcRadScaling.
     118        // This will of course offset non-corners more than the given resizing, but the extra area
     119        // gets chopped off by the intersection with normal_offset_result, below.
     120        double elbowDist = double(std::abs(resizing))/sin(minUnroundedAngleDegrees*our_pi/180.0/2.0);
     121        arcRadius = elbowDist*arcRadScaling;
     122    }
     123
     124    // Note: By setting round_away_from_center=true, we make a 'circle polygon' here that is actually the result of
     125    // calling round_away<coordinate_type>(pt, center) on the vertices of a circle polygon.
     126    // Previously (see bug ticket #6063) we were using round_down instead of round_away, with the result that
     127    // the elbowDist and arcRadScaling above were sometimes insufficient to avoid rounding corners
     128    // with angle >= minUnroundedAngleDegrees, due to integer precision.
     129    bool round_away_from_center = true;
     130
    106131    std::vector<point_data<coordinate_type> > circle;
    107     point_data<double> center(0.0, 0.0), start(0.0, (double)resizing);
    108     make_arc(circle, start, start, center, std::abs((double)resizing),
    109              num_circle_segments);
     132    point_data<double> center(0.0, 0.0), start(0.0, arcRadius);
     133    make_arc(circle, start, start, center, arcRadius, num_circle_segments, round_away_from_center);
    110134    polygon_data<coordinate_type> poly;
    111135    set_points(poly, circle.begin(), circle.end());
    112136    polygon_set_data<coordinate_type> offset_set;
     
    114138    polygon_set_data<coordinate_type> result;
    115139    detail::minkowski_offset<coordinate_type>::convolve_two_polygon_sets
    116140      (result, *this, offset_set);
     141
     142    // Intersect with result of 'normal' (non-minkowski-sum) offset,
     143    // to remove offsets larger than the given 'resizing' when not near corners.
     144    // Also for bug ticket #6063
     145    polygon_set_data<coordinate_type> normal_offset_result(*this);
     146    normal_offset_result.bloat(std::abs(resizing));
     147    result = result & normal_offset_result;
     148
    117149    if(resizing < 0) {
    118150      result = result & rect;//eliminate overhang
    119151      result = result ^ rect;//invert
    120152    }
     153
    121154    *this = result;
    122155    return *this;
    123156  }
  • polygon_45_set_data.hpp

    diff -dur orig_polygon/polygon_45_set_data.hpp polygon/polygon_45_set_data.hpp
    old new  
    14551455
    14561456  template <typename cT, typename iT>
    14571457  void get_error_rects_shell(cT& posE, cT& negE, iT beginr, iT endr) {
    1458     typedef typename iT::value_type Point;
     1458    //typedef typename iT::value_type Point;  DBF per patch at https://svn.boost.org/trac/boost/ticket/6051
     1459    typedef typename std::iterator_traits<iT>::value_type Point;
    14591460    typedef typename point_traits<Point>::coordinate_type Unit;
    14601461    typedef typename coordinate_traits<Unit>::area_type area_type;
    14611462    Point pt1, pt2, pt3;
  • polygon_set_data.hpp

    diff -dur orig_polygon/polygon_set_data.hpp polygon/polygon_set_data.hpp
    old new  
    2727     return rounded_val;
    2828  }
    2929  template <typename T>
    30   static inline point_data<T> round_down(point_data<double> v) {
     30  static inline point_data<T> round_down(const point_data<double> &v) {
    3131     return point_data<T>(round_down<T>(v.x()),round_down<T>(v.y()));
    3232  }
    3333
     34  // utility function to round coordinate types away from another value.
     35  // intended really for integer type T (does not make sense for float)
     36  template <typename T>
     37  static inline T round_away(double val, double center) {
     38     T rounded_val = (T)(val);
     39     if(val <= center)
     40     {
     41         // round down
     42         if(val < (double)rounded_val)
     43             --rounded_val;
     44     }
     45     else
     46     {
     47         // round up
     48         if(val > (double)rounded_val)
     49             ++rounded_val;
     50     }
     51     return rounded_val;
     52  }
     53  template <typename T>
     54  static inline point_data<T> round_away(const point_data<double> &v, const point_data<double> &center) {
     55     return point_data<T>(round_away<T>(v.x(),center.x()), round_away<T>(v.y(),center.y()));
     56  }
    3457
    3558
    3659  //foward declare view
     
    359382    }
    360383
    361384    inline polygon_set_data&
    362     resize(coordinate_type resizing, bool corner_fill_arc = false, unsigned int num_circle_segments=0);
     385    resize(coordinate_type resizing, bool corner_fill_arc = false, unsigned int num_circle_segments=0,
     386           double minUnroundedAngleDegrees=180.0);
    363387
    364388    template <typename transform_type>
    365389    inline polygon_set_data&
     
    840864         return_points_back.push_back(curr_prev);
    841865         point_data<double> dmid(middle.x(),middle.y());
    842866         return_points.push_back(return_points1);
    843          int num = make_arc(return_points[return_points.size()-1],mid1_offset,mid2_offset,dmid,sizing_distance,num_circle_segments);
     867         // TODO: not sure what value to use here
     868         bool round_away_from_center=false;
     869         int num = make_arc(return_points[return_points.size()-1],mid1_offset,mid2_offset,dmid,sizing_distance,num_circle_segments,round_away_from_center);
    844870         curr_prev = round_down<T>(mid2_offset);
    845871         return num;
    846872         
     
    874900
    875901  }
    876902
    877   // this routine should take in start and end point s.t. end point is CCW from start
    878   // it sould make a pie slice polygon  that is an intersection of that arc
    879   // with an ngon segments approximation of the circle centered at center with radius r
    880   // point start is gauaranteed to be on the segmentation
     903  // This routine should take in start and end point s.t. end point is CCW from start.
     904  // It should make a pie slice polygon  that is an intersection of that arc
     905  // with an ngon segments approximation of the circle centered at center with radius r.
     906  // Point start is guaranteed to be on the segmentation, unless a full circle is created.
     907  // If round_away_from_center is true, points will be rounded to the nearest type T values "away" from the center;
     908  // otherwise, they will be rounded down via round_down().  (new for bug ticket #6063)
     909  // TODO: do we need a version that rounds values "toward" the center?
    881910  // returnPoints will start with the first point after start
    882911  // returnPoints vector  may be empty
    883912  template <typename  T>
    884913  inline int  make_arc(std::vector<point_data< T> >& return_points, 
    885914                       point_data< double> start, point_data< double>  end,
    886                        point_data< double> center,  double r, unsigned int num_circle_segments) {
     915                       point_data< double> center,  double r, unsigned int num_circle_segments,
     916                       bool round_away_from_center) {
    887917      const double our_pi=3.1415926535897932384626433832795028841971;
    888918
    889919      // derive start and end angles
     
    898928      while (pe <= ps) 
    899929         pe += 2.0 * our_pi;
    900930      double delta_angle = (2.0 * our_pi) / (double)num_circle_segments;
     931
     932      std::vector< point_data<double> > tmp_points;
    901933      if ( start==end) // full circle?
    902934      {
    903935          ps = delta_angle*0.5;
     
    908940          start = point_data<double>(x,y);
    909941          end = start;
    910942      }
    911       return_points.push_back(round_down<T>(center));
    912       return_points.push_back(round_down<T>(start));
     943      else
     944      {
     945          tmp_points.push_back(center);
     946      }
     947      tmp_points.push_back(start);
    913948      unsigned int i=0;
    914949      double curr_angle = ps+delta_angle;
    915950      while( curr_angle < pe - 0.01 && i < 2 * num_circle_segments) {
    916951         i++;
    917952         double x = center.x() + r * cos( curr_angle);
    918953         double y = center.y() + r * sin( curr_angle);
    919          return_points.push_back( round_down<T>((point_data<double>(x,y))));
     954         tmp_points.push_back(point_data<double>(x,y));
    920955         curr_angle+=delta_angle;
    921956      }
    922       return_points.push_back(round_down<T>(end));
     957      tmp_points.push_back(end);
     958
     959      for(int i=0; i<tmp_points.size(); i++)
     960          return_points.push_back(round_away_from_center ?round_away<T>(tmp_points[i], center) :round_down<T>(tmp_points[i]));
     961
    923962      return return_points.size();
    924963  }
    925964
  • polygon_traits.hpp

    diff -dur orig_polygon/polygon_traits.hpp polygon/polygon_traits.hpp
    old new  
    10361036
    10371037  template <typename iT>
    10381038  bool point_sequence_is_45(iT itr, iT itr_end) {
    1039     typedef typename iT::value_type Point;
     1039    // typedef typename iT::value_type Point;  DBF per patch at https://svn.boost.org/trac/boost/ticket/6051
     1040    typedef typename std::iterator_traits<iT>::value_type Point;
    10401041    typedef typename point_traits<Point>::coordinate_type Unit;
    10411042    if(itr == itr_end) return true;
    10421043    Point firstPt = *itr;