inline void apply(PointIn const& penultimate_point, PointIn const& perp_left_point, PointIn const& ultimate_point, PointIn const& perp_right_point, buffer_side_selector side, DistanceStrategy const& distance, RangeOut& range_out) const { promoted_type const dist_left = distance.apply(penultimate_point, ultimate_point, buffer_side_left); promoted_type const dist_right = distance.apply(penultimate_point, ultimate_point, buffer_side_right); bool reversed = (side == buffer_side_left && dist_right < 0 && -dist_right > dist_left) || (side == buffer_side_right && dist_left < 0 && -dist_left > dist_right) ; if (reversed) { range_out.push_back(perp_right_point); range_out.push_back(perp_left_point); } else { range_out.push_back(perp_left_point); range_out.push_back(perp_right_point); } // Don't add the ultimate_point (endpoint of the linestring). // The buffer might be generated completely at one side. // In other cases it does no harm but is further useless }
static inline void generate_side(Point const& ip1, Point const& ip2, buffer_side_selector side, DistanceStrategy const& distance, output_point_type& p1, output_point_type& p2) { // Generate a block along (left or right of) the segment // Simulate a vector d (dx,dy) coordinate_type dx = get<0>(ip2) - get<0>(ip1); coordinate_type dy = get<1>(ip2) - get<1>(ip1); // For normalization [0,1] (=dot product d.d, sqrt) // TODO promoted_type coordinate_type const length = sqrt(dx * dx + dy * dy); // Because coordinates are not equal, length should not be zero BOOST_ASSERT((! geometry::math::equals(length, 0))); // Generate the normalized perpendicular p, to the left (ccw) coordinate_type const px = -dy / length; coordinate_type const py = dx / length; coordinate_type const d = distance.apply(ip1, ip2, side); set<0>(p1, get<0>(ip1) + px * d); set<1>(p1, get<1>(ip1) + py * d); set<0>(p2, get<0>(ip2) + px * d); set<1>(p2, get<1>(ip2) + py * d); }
inline void apply(Point const& penultimate_point, Point const& perp_left_point, Point const& ultimate_point, Point const& , buffer_side_selector side, DistanceStrategy const& distance, RangeOut& range_out) const { typedef typename coordinate_type<Point>::type coordinate_type; typedef typename geometry::select_most_precise < coordinate_type, double >::type promoted_type; promoted_type const alpha = calculate_angle<promoted_type>(perp_left_point, ultimate_point); promoted_type const dist_left = distance.apply(penultimate_point, ultimate_point, buffer_side_left); promoted_type const dist_right = distance.apply(penultimate_point, ultimate_point, buffer_side_right); if (geometry::math::equals(dist_left, dist_right)) { generate_points(ultimate_point, alpha, dist_left, range_out); } else { promoted_type const two = 2.0; promoted_type dist_half_diff = (dist_left - dist_right) / two; if (side == buffer_side_right) { dist_half_diff = -dist_half_diff; } Point shifted_point; set<0>(shifted_point, get<0>(ultimate_point) + dist_half_diff * cos(alpha)); set<1>(shifted_point, get<1>(ultimate_point) + dist_half_diff * sin(alpha)); generate_points(shifted_point, alpha, (dist_left + dist_right) / two, range_out); } }
static inline void apply( Point const& input_p1, Point const& input_p2, strategy::buffer::buffer_side_selector side, DistanceStrategy const& distance, OutputRange& output_range) { typedef typename coordinate_type<Point>::type coordinate_type; typedef typename geometry::select_most_precise < coordinate_type, double >::type promoted_type; // Generate a block along (left or right of) the segment // Simulate a vector d (dx,dy) coordinate_type dx = get<0>(input_p2) - get<0>(input_p1); coordinate_type dy = get<1>(input_p2) - get<1>(input_p1); // For normalization [0,1] (=dot product d.d, sqrt) promoted_type const length = geometry::math::sqrt(dx * dx + dy * dy); // Because coordinates are not equal, length should not be zero BOOST_ASSERT((! geometry::math::equals(length, 0))); // Generate the normalized perpendicular p, to the left (ccw) promoted_type const px = -dy / length; promoted_type const py = dx / length; promoted_type const d = distance.apply(input_p1, input_p2, side); output_range.resize(2); set<0>(output_range.front(), get<0>(input_p1) + px * d); set<1>(output_range.front(), get<1>(input_p1) + py * d); set<0>(output_range.back(), get<0>(input_p2) + px * d); set<1>(output_range.back(), get<1>(input_p2) + py * d); }
inline void apply(Point const& point, DistanceStrategy const& distance_strategy, OutputRange& output_range) const { typedef typename boost::range_value<OutputRange>::type output_point_type; typedef typename geometry::select_most_precise < typename geometry::select_most_precise < typename geometry::coordinate_type<Point>::type, typename geometry::coordinate_type<output_point_type>::type >::type, double >::type promoted_type; promoted_type const buffer_distance = distance_strategy.apply(point, point, strategy::buffer::buffer_side_left); promoted_type const two = 2.0; promoted_type const two_pi = two * geometry::math::pi<promoted_type>(); promoted_type const diff = two_pi / promoted_type(m_count); promoted_type a = 0; for (std::size_t i = 0; i < m_count; i++, a -= diff) { output_point_type p; set<0>(p, get<0>(point) + buffer_distance * cos(a)); set<1>(p, get<1>(point) + buffer_distance * sin(a)); output_range.push_back(p); } // Close it: output_range.push_back(output_range.front()); }
static inline void iterate(Inserter& inserter, Iterator begin, Iterator end, buffer_side_selector side, DistanceStrategy const& distance, JoinStrategy const& join #ifdef BOOST_GEOMETRY_DEBUG_WITH_MAPPER , Mapper& mapper #endif ) { output_point_type previous_p1, previous_p2; output_point_type first_p1, first_p2; #ifdef BOOST_GEOMETRY_DEBUG_WITH_MAPPER int index = 0; #endif bool first = true; Iterator it = begin; for (Iterator prev = it++; it != end; ++it) { if (! detail::equals::equals_point_point(*prev, *it)) { bool skip = false; // Simulate a vector d (dx,dy) coordinate_type dx = get<0>(*it) - get<0>(*prev); coordinate_type dy = get<1>(*it) - get<1>(*prev); // For normalization [0,1] (=dot product d.d, sqrt) coordinate_type length = sqrt(dx * dx + dy * dy); // Because coordinates are not equal, length should not be zero BOOST_ASSERT((! geometry::math::equals(length, 0))); // Generate the normalized perpendicular p, to the left (ccw) coordinate_type px = -dy / length; coordinate_type py = dx / length; output_point_type p1, p2; coordinate_type d = distance.apply(*prev, *it, side); set<0>(p2, get<0>(*it) + px * d); set<1>(p2, get<1>(*it) + py * d); set<0>(p1, get<0>(*prev) + px * d); set<1>(p1, get<1>(*prev) + py * d); { #ifdef BOOST_GEOMETRY_DEBUG_WITH_MAPPER ring_type block; block.push_back(*prev); block.push_back(*it); block.push_back(p2); block.push_back(p1); block.push_back(*prev); mapper.map(block, "opacity:0.4;fill:rgb(0,255,0);stroke:rgb(0,0,0);stroke-width:1"); #endif } if (! first) { output_point_type p; segment_type s1(p1, p2); segment_type s2(previous_p1, previous_p2); if (line_line_intersection<output_point_type, segment_type>::apply(s1, s2, p)) { join.apply(p, *prev, previous_p2, p1, distance.apply(*prev, *it, side), inserter.get_ring()); { #ifdef BOOST_GEOMETRY_DEBUG_WITH_MAPPER mapper.map(p, "fill:rgb(0,0,0);", 3); std::ostringstream out; out << index++; mapper.text(p, out.str(), "fill:rgb(0,0,0);font-family='Arial';", 5, 5); #endif } } else { skip = false; } } else { first = false; first_p1 = p1; first_p2 = p2; inserter.insert(p1); } if (! skip) { previous_p1 = p1; previous_p2 = p2; prev = it; } } } // Last one inserter.insert(previous_p2); }
static inline void iterate(Collection& collection, Iterator begin, Iterator end, buffer_side_selector side, DistanceStrategy const& distance, JoinStrategy const& join_strategy, bool close = false) { output_point_type previous_p1, previous_p2; output_point_type first_p1, first_p2; bool first = true; Iterator it = begin; // We want to memorize the last vector too. typedef BOOST_TYPEOF(*it) point_type; point_type last_ip1, last_ip2; for (Iterator prev = it++; it != end; ++it) { if (! detail::equals::equals_point_point(*prev, *it)) { output_point_type p1, p2; last_ip1 = *prev; last_ip2 = *it; generate_side(*prev, *it, side, distance, p1, p2); std::vector<output_point_type> range_out; if (! first) { output_point_type p; segment_type s1(p1, p2); segment_type s2(previous_p1, previous_p2); if (line_line_intersection<output_point_type, segment_type>::apply(s1, s2, p)) { join_strategy.apply(p, *prev, previous_p2, p1, distance.apply(*prev, *it, side), range_out); } } else { first = false; first_p1 = p1; first_p2 = p2; } if (! range_out.empty()) { collection.add_piece(buffered_join, *prev, range_out); range_out.clear(); } collection.add_piece(buffered_segment, *prev, *it, p1, p2); previous_p1 = p1; previous_p2 = p2; prev = it; } } // Might be replaced by specialization if(boost::is_same<Tag, ring_tag>::value) { // Generate closing corner output_point_type p; segment_type s1(previous_p1, previous_p2); segment_type s2(first_p1, first_p2); line_line_intersection<output_point_type, segment_type>::apply(s1, s2, p); std::vector<output_point_type> range_out; join_strategy.apply(p, *begin, previous_p2, first_p1, distance.apply(*(end - 1), *begin, side), range_out); if (! range_out.empty()) { collection.add_piece(buffered_join, *begin, range_out); } // Buffer is closed automatically by last closing corner (NOT FOR OPEN POLYGONS - TODO) } else if (boost::is_same<Tag, linestring_tag>::value) { // Assume flat-end-strategy for now // TODO fix this (approach) for one-side buffer (1.5 - -1.0) output_point_type rp1, rp2; generate_side(last_ip2, last_ip1, side == buffer_side_left ? buffer_side_right : buffer_side_left, distance, rp2, rp1); // For flat end: std::vector<output_point_type> range_out; range_out.push_back(previous_p2); if (close) { range_out.push_back(rp2); } collection.add_piece(buffered_flat_end, range_out); } }
static inline result_code apply( Point const& input_p1, Point const& input_p2, buffer_side_selector side, DistanceStrategy const& distance, OutputRange& output_range) { typedef typename coordinate_type<Point>::type coordinate_type; typedef typename geometry::select_most_precise < coordinate_type, double >::type promoted_type; // Generate a block along (left or right of) the segment // Simulate a vector d (dx,dy) coordinate_type const dx = get<0>(input_p2) - get<0>(input_p1); coordinate_type const dy = get<1>(input_p2) - get<1>(input_p1); // For normalization [0,1] (=dot product d.d, sqrt) promoted_type const length = geometry::math::sqrt(dx * dx + dy * dy); if (! boost::math::isfinite(length)) { // In case of coordinates differences of e.g. 1e300, length // will overflow and we should not generate output #ifdef BOOST_GEOMETRY_DEBUG_BUFFER_WARN std::cout << "Error in length calculation for points " << geometry::wkt(input_p1) << " " << geometry::wkt(input_p2) << " length: " << length << std::endl; #endif return result_error_numerical; } if (geometry::math::equals(length, 0)) { // Coordinates are simplified and therefore most often not equal. // But if simplify is skipped, or for lines with two // equal points, length is 0 and we cannot generate output. return result_no_output; } promoted_type const d = distance.apply(input_p1, input_p2, side); // Generate the normalized perpendicular p, to the left (ccw) promoted_type const px = -dy / length; promoted_type const py = dx / length; if (geometry::math::equals(px, 0) && geometry::math::equals(py, 0)) { // This basically should not occur - because of the checks above. // There are no unit tests triggering this condition #ifdef BOOST_GEOMETRY_DEBUG_BUFFER_WARN std::cout << "Error in perpendicular calculation for points " << geometry::wkt(input_p1) << " " << geometry::wkt(input_p2) << " length: " << length << " distance: " << d << std::endl; #endif return result_no_output; } output_range.resize(2); set<0>(output_range.front(), get<0>(input_p1) + px * d); set<1>(output_range.front(), get<1>(input_p1) + py * d); set<0>(output_range.back(), get<0>(input_p2) + px * d); set<1>(output_range.back(), get<1>(input_p2) + py * d); return result_normal; }