diff -dur orig_polygon/detail/minkowski.hpp polygon/detail/minkowski.hpp --- orig_polygon/detail/minkowski.hpp 2012-02-07 14:32:34.669158200 -0500 +++ polygon/detail/minkowski.hpp 2012-02-07 12:37:04.701652000 -0500 @@ -82,17 +82,18 @@ } template inline polygon_set_data& - polygon_set_data::resize(coordinate_type resizing, bool corner_fill_arc, unsigned int num_circle_segments) { + polygon_set_data::resize(coordinate_type resizing, bool corner_fill_arc, unsigned int num_circle_segments, + double minUnroundedAngleDegrees) { using namespace ::boost::polygon::operators; - if(!corner_fill_arc) { + if(resizing == 0) return *this; + if(empty()) return *this; + if(!corner_fill_arc || minUnroundedAngleDegrees<=0.0) { if(resizing < 0) return shrink(-resizing); - if(resizing > 0) + else return bloat(resizing); - return *this; } - if(resizing == 0) return *this; - if(empty()) return *this; + if(num_circle_segments < 3) num_circle_segments = 4; rectangle_data rect; extents(rect); @@ -100,13 +101,36 @@ ::boost::polygon::bloat(rect, 10); (*this) = rect - (*this); //invert } - //make_arc(std::vector >& return_points, - //point_data< double> start, point_data< double> end, - //point_data< double> center, double r, unsigned int num_circle_segments) + + // scale arc up so that minimum radius corresponds to 'resizing', + // for bug ticket #6063. + const double our_pi=3.1415926535897932384626433832795028841971; + double arcRadScaling = 1.0/cos(our_pi/double(num_circle_segments)); + double arcRadius = double(std::abs(resizing))*arcRadScaling; + + if(minUnroundedAngleDegrees < 180.0) + { + // Don't round corners less than given angle (in radians). + // We do this by setting the minimum radius of the 'circle' to correspond to + // the distance to the 'elbow' created by normal offset of a corner with the given angle. + // e.g. an 90-degree corner with an offset of 1 will generate an elbow-corner at distance sqrt(2), + // so if \param minUnroundedAngleDegrees=90, we set the circle radius to offset*sqrt(2)*arcRadScaling. + // This will of course offset non-corners more than the given resizing, but the extra area + // gets chopped off by the intersection with normal_offset_result, below. + double elbowDist = double(std::abs(resizing))/sin(minUnroundedAngleDegrees*our_pi/180.0/2.0); + arcRadius = elbowDist*arcRadScaling; + } + + // Note: By setting round_away_from_center=true, we make a 'circle polygon' here that is actually the result of + // calling round_away(pt, center) on the vertices of a circle polygon. + // Previously (see bug ticket #6063) we were using round_down instead of round_away, with the result that + // the elbowDist and arcRadScaling above were sometimes insufficient to avoid rounding corners + // with angle >= minUnroundedAngleDegrees, due to integer precision. + bool round_away_from_center = true; + std::vector > circle; - point_data center(0.0, 0.0), start(0.0, (double)resizing); - make_arc(circle, start, start, center, std::abs((double)resizing), - num_circle_segments); + point_data center(0.0, 0.0), start(0.0, arcRadius); + make_arc(circle, start, start, center, arcRadius, num_circle_segments, round_away_from_center); polygon_data poly; set_points(poly, circle.begin(), circle.end()); polygon_set_data offset_set; @@ -114,10 +138,19 @@ polygon_set_data result; detail::minkowski_offset::convolve_two_polygon_sets (result, *this, offset_set); + + // Intersect with result of 'normal' (non-minkowski-sum) offset, + // to remove offsets larger than the given 'resizing' when not near corners. + // Also for bug ticket #6063 + polygon_set_data normal_offset_result(*this); + normal_offset_result.bloat(std::abs(resizing)); + result = result & normal_offset_result; + if(resizing < 0) { result = result & rect;//eliminate overhang result = result ^ rect;//invert } + *this = result; return *this; } diff -dur orig_polygon/polygon_45_set_data.hpp polygon/polygon_45_set_data.hpp --- orig_polygon/polygon_45_set_data.hpp 2012-02-07 14:32:34.692116800 -0500 +++ polygon/polygon_45_set_data.hpp 2011-10-27 16:04:57.306966700 -0400 @@ -1455,7 +1455,8 @@ template void get_error_rects_shell(cT& posE, cT& negE, iT beginr, iT endr) { - typedef typename iT::value_type Point; + //typedef typename iT::value_type Point; DBF per patch at https://svn.boost.org/trac/boost/ticket/6051 + typedef typename std::iterator_traits::value_type Point; typedef typename point_traits::coordinate_type Unit; typedef typename coordinate_traits::area_type area_type; Point pt1, pt2, pt3; diff -dur orig_polygon/polygon_set_data.hpp polygon/polygon_set_data.hpp --- orig_polygon/polygon_set_data.hpp 2012-02-07 14:32:34.701100600 -0500 +++ polygon/polygon_set_data.hpp 2012-02-07 14:25:15.765019100 -0500 @@ -27,10 +27,33 @@ return rounded_val; } template - static inline point_data round_down(point_data v) { + static inline point_data round_down(const point_data &v) { return point_data(round_down(v.x()),round_down(v.y())); } + // utility function to round coordinate types away from another value. + // intended really for integer type T (does not make sense for float) + template + static inline T round_away(double val, double center) { + T rounded_val = (T)(val); + if(val <= center) + { + // round down + if(val < (double)rounded_val) + --rounded_val; + } + else + { + // round up + if(val > (double)rounded_val) + ++rounded_val; + } + return rounded_val; + } + template + static inline point_data round_away(const point_data &v, const point_data ¢er) { + return point_data(round_away(v.x(),center.x()), round_away(v.y(),center.y())); + } //foward declare view @@ -359,7 +382,8 @@ } inline polygon_set_data& - resize(coordinate_type resizing, bool corner_fill_arc = false, unsigned int num_circle_segments=0); + resize(coordinate_type resizing, bool corner_fill_arc = false, unsigned int num_circle_segments=0, + double minUnroundedAngleDegrees=180.0); template inline polygon_set_data& @@ -840,7 +864,9 @@ return_points_back.push_back(curr_prev); point_data dmid(middle.x(),middle.y()); return_points.push_back(return_points1); - int num = make_arc(return_points[return_points.size()-1],mid1_offset,mid2_offset,dmid,sizing_distance,num_circle_segments); + // TODO: not sure what value to use here + bool round_away_from_center=false; + int num = make_arc(return_points[return_points.size()-1],mid1_offset,mid2_offset,dmid,sizing_distance,num_circle_segments,round_away_from_center); curr_prev = round_down(mid2_offset); return num; @@ -874,16 +900,20 @@ } - // this routine should take in start and end point s.t. end point is CCW from start - // it sould make a pie slice polygon that is an intersection of that arc - // with an ngon segments approximation of the circle centered at center with radius r - // point start is gauaranteed to be on the segmentation + // This routine should take in start and end point s.t. end point is CCW from start. + // It should make a pie slice polygon that is an intersection of that arc + // with an ngon segments approximation of the circle centered at center with radius r. + // Point start is guaranteed to be on the segmentation, unless a full circle is created. + // If round_away_from_center is true, points will be rounded to the nearest type T values "away" from the center; + // otherwise, they will be rounded down via round_down(). (new for bug ticket #6063) + // TODO: do we need a version that rounds values "toward" the center? // returnPoints will start with the first point after start // returnPoints vector may be empty template inline int make_arc(std::vector >& return_points, point_data< double> start, point_data< double> end, - point_data< double> center, double r, unsigned int num_circle_segments) { + point_data< double> center, double r, unsigned int num_circle_segments, + bool round_away_from_center) { const double our_pi=3.1415926535897932384626433832795028841971; // derive start and end angles @@ -898,6 +928,8 @@ while (pe <= ps) pe += 2.0 * our_pi; double delta_angle = (2.0 * our_pi) / (double)num_circle_segments; + + std::vector< point_data > tmp_points; if ( start==end) // full circle? { ps = delta_angle*0.5; @@ -908,18 +940,25 @@ start = point_data(x,y); end = start; } - return_points.push_back(round_down(center)); - return_points.push_back(round_down(start)); + else + { + tmp_points.push_back(center); + } + tmp_points.push_back(start); unsigned int i=0; double curr_angle = ps+delta_angle; while( curr_angle < pe - 0.01 && i < 2 * num_circle_segments) { i++; double x = center.x() + r * cos( curr_angle); double y = center.y() + r * sin( curr_angle); - return_points.push_back( round_down((point_data(x,y)))); + tmp_points.push_back(point_data(x,y)); curr_angle+=delta_angle; } - return_points.push_back(round_down(end)); + tmp_points.push_back(end); + + for(int i=0; i(tmp_points[i], center) :round_down(tmp_points[i])); + return return_points.size(); } diff -dur orig_polygon/polygon_traits.hpp polygon/polygon_traits.hpp --- orig_polygon/polygon_traits.hpp 2012-02-07 14:32:34.703097000 -0500 +++ polygon/polygon_traits.hpp 2011-10-27 16:04:57.307966800 -0400 @@ -1036,7 +1036,8 @@ template bool point_sequence_is_45(iT itr, iT itr_end) { - typedef typename iT::value_type Point; + // typedef typename iT::value_type Point; DBF per patch at https://svn.boost.org/trac/boost/ticket/6051 + typedef typename std::iterator_traits::value_type Point; typedef typename point_traits::coordinate_type Unit; if(itr == itr_end) return true; Point firstPt = *itr;