diff --git a/include/boost/geometry/algorithms/detail/occupation_info.hpp b/include/boost/geometry/algorithms/detail/occupation_info.hpp index 633e3e5b4..b2b035e4b 100644 --- a/include/boost/geometry/algorithms/detail/occupation_info.hpp +++ b/include/boost/geometry/algorithms/detail/occupation_info.hpp @@ -186,7 +186,7 @@ public : typedef std::map > map_type; map_type map; - OccupationInfo& find_or_insert(Point point) + inline OccupationInfo& find_or_insert(Point const& point) { typename map_type::iterator it = map.find(point); if (it == boost::end(map)) @@ -199,6 +199,11 @@ public : return it->second; } + inline bool contains(Point const& point) const + { + typename map_type::const_iterator it = map.find(point); + return it != boost::end(map); + } }; diff --git a/include/boost/geometry/extensions/algorithms/buffer/buffer_inserter.hpp b/include/boost/geometry/extensions/algorithms/buffer/buffer_inserter.hpp index cb08c7a15..a9eedafdb 100644 --- a/include/boost/geometry/extensions/algorithms/buffer/buffer_inserter.hpp +++ b/include/boost/geometry/extensions/algorithms/buffer/buffer_inserter.hpp @@ -238,9 +238,12 @@ struct buffer_inserter DistanceStrategy const& distance, JoinStrategy const& join_strategy) { - base::iterate(collection, boost::begin(ring), boost::end(ring), - buffer_side_left, - distance, join_strategy); + if (boost::size(ring) > 3) + { + base::iterate(collection, boost::begin(ring), boost::end(ring), + buffer_side_left, + distance, join_strategy); + } } }; @@ -268,14 +271,17 @@ struct buffer_inserter DistanceStrategy const& distance, JoinStrategy const& join_strategy) { - collection.start_new_ring(); - base::iterate(collection, boost::begin(linestring), boost::end(linestring), - buffer_side_left, - distance, join_strategy); + if (boost::size(linestring) > 1) + { + collection.start_new_ring(); + base::iterate(collection, boost::begin(linestring), boost::end(linestring), + buffer_side_left, + distance, join_strategy); - base::iterate(collection, boost::rbegin(linestring), boost::rend(linestring), - buffer_side_right, - distance, join_strategy, true); + base::iterate(collection, boost::rbegin(linestring), boost::rend(linestring), + buffer_side_right, + distance, join_strategy, true); + } } }; @@ -360,6 +366,7 @@ inline void buffer_inserter(GeometryInput const& geometry_input, OutputIterator #ifdef BOOST_GEOMETRY_DEBUG_WITH_MAPPER //collection.map_offsetted(mapper); + //collection.map_offsetted_points(mapper); collection.map_turns(mapper); //collection.map_opposite_locations(mapper); #endif diff --git a/include/boost/geometry/extensions/algorithms/buffer/buffered_piece_collection.hpp b/include/boost/geometry/extensions/algorithms/buffer/buffered_piece_collection.hpp index e67e71584..90f84f715 100644 --- a/include/boost/geometry/extensions/algorithms/buffer/buffered_piece_collection.hpp +++ b/include/boost/geometry/extensions/algorithms/buffer/buffered_piece_collection.hpp @@ -155,15 +155,19 @@ struct buffered_piece_collection inline bool is_neighbor(piece const& piece1, piece const& piece2) const { + if (piece1.first_seg_id.multi_index != piece2.first_seg_id.multi_index) + { + return false; + } + if (std::abs(piece1.index - piece2.index) == 1) { return true; } - // TODO: of the same multi_index - int const b = boost::size(m_pieces) - 1; // back - return (piece1.index == 0 && piece2.index == b) - || (piece1.index == b && piece2.index == 0) + int const last = boost::size(m_pieces) - 1; + return (piece1.index == 0 && piece2.index == last) + || (piece1.index == last && piece2.index == 0) ; } @@ -173,21 +177,29 @@ struct buffered_piece_collection } template - inline typename boost::range_value::type next_point(Range const& range, Iterator it) const + inline void move_to_next_point(Range const& range, Iterator& next) const { - Iterator next = it; ++next; + if (next == boost::end(range)) + { + next = boost::begin(range) + 1; + } + } - // Get next point. If end get second point (because ring is assumed to be closed) - return next != boost::end(range) ? *next : *(boost::begin(range) + 1); + template + inline Iterator next_point(Range const& range, Iterator it) const + { + Iterator result = it; + move_to_next_point(range, result); + while(geometry::equals(*it, *result)) + { + move_to_next_point(range, result); + } + return result; } inline void calculate_turns(piece const& piece1, piece const& piece2) { - buffer_turn_info the_model; - the_model.operations[0].piece_index = piece1.index; - the_model.operations[0].seg_id = piece1.first_seg_id; - typedef typename boost::range_iterator const>::type iterator; segment_identifier seg_id1 = piece1.first_seg_id; @@ -206,6 +218,10 @@ struct buffered_piece_collection iterator it2_first = boost::begin(ring2) + seg_id2.segment_index; iterator it2_last = boost::begin(ring2) + piece2.last_segment_index; + buffer_turn_info the_model; + the_model.operations[0].piece_index = piece1.index; + the_model.operations[0].seg_id = piece1.first_seg_id; + iterator it1 = it1_first; for (iterator prev1 = it1++; it1 != it1_last; @@ -214,6 +230,8 @@ struct buffered_piece_collection the_model.operations[1].piece_index = piece2.index; the_model.operations[1].seg_id = piece2.first_seg_id; + iterator next1 = next_point(ring1, it1); + iterator it2 = it2_first; for (iterator prev2 = it2++; it2 != it2_last; @@ -223,8 +241,15 @@ struct buffered_piece_collection the_model.operations[0].other_id = the_model.operations[1].seg_id; the_model.operations[1].other_id = the_model.operations[0].seg_id; - turn_policy::apply(*prev1, *it1, next_point(ring1, it1), - *prev2, *it2, next_point(ring2, it2), + iterator next2 = next_point(ring2, it2); + + if (piece1.index == 21 && piece2.index == 32) + { + int kkk = 0; + } + + turn_policy::apply(*prev1, *it1, *next1, + *prev2, *it2, *next2, the_model, std::back_inserter(m_turns)); } } @@ -239,6 +264,7 @@ struct buffered_piece_collection { m_in_opposite_segments.insert(it->operations[0].seg_id); m_in_opposite_segments.insert(it->operations[1].seg_id); +//std::cout << " " << it->operations[0].seg_id.segment_index; } } } @@ -344,8 +370,11 @@ struct buffered_piece_collection } if (side_helper == 0) { - if (geometry::equals(turn.point, pc.helper_segments.back()) - || geometry::equals(turn.point, pc.helper_segments.front())) + //BOOST_AUTO(d1, geometry::comparable_distance(turn.point, pc.helper_segments.back())); + //BOOST_AUTO(d2, geometry::comparable_distance(turn.point, pc.helper_segments.front())); + //if (d1 < 0.1 || d2 < 0.1) + if (geometry::equals(turn.point, pc.helper_segments.back()) + || geometry::equals(turn.point, pc.helper_segments.front())) { turn.count_on_corner++; } @@ -384,6 +413,7 @@ struct buffered_piece_collection m_turns[*sit].count_on_occupied++; } } +//std::cout << geometry::wkt(it->first) << " " << int(info.occupied()) << std::endl; } } @@ -403,6 +433,24 @@ struct buffered_piece_collection add_angles(index, turn.point, turn.operations[1]); } } + + index = 0; + for (typename boost::range_iterator::type it = + boost::begin(m_turns); it != boost::end(m_turns); ++it, ++index) + { + buffer_turn_info& turn = *it; + if (m_in_opposite_segments.count(turn.operations[0].seg_id) == 0 + && m_in_opposite_segments.count(turn.operations[1].seg_id) == 0) + { + // See if it is in the map + if (m_occupation_map.contains(turn.point)) + { +//std::cout << "Adding point " << geometry::wkt(turn.point) << std::endl; + add_angles(index, turn.point, turn.operations[0]); + add_angles(index, turn.point, turn.operations[1]); + } + } + } } inline void classify_turns() @@ -621,7 +669,7 @@ struct buffered_piece_collection offsetted_rings[it->operations[0].seg_id.multi_index].has_discarded_intersections = true; offsetted_rings[it->operations[1].seg_id.multi_index].has_discarded_intersections = true; } - else + else if (! it->both(detail::overlay::operation_union)) { offsetted_rings[it->operations[0].seg_id.multi_index].has_accepted_intersections = true; offsetted_rings[it->operations[1].seg_id.multi_index].has_accepted_intersections = true; @@ -673,6 +721,8 @@ struct buffered_piece_collection ring_identifier id(0, index, -1); selected[id] = properties(*it, true); } + +//std::cout << geometry::wkt(*it) << std::endl; } // Select all created rings @@ -682,11 +732,11 @@ struct buffered_piece_collection it != boost::end(traversed_rings); ++it, ++index) { - ring_identifier id(2, index, -1); - selected[id] = properties(*it, true); + ring_identifier id(2, index, -1); + selected[id] = properties(*it, true); } - detail::overlay::assign_parents(offsetted_rings, traversed_rings, selected); + detail::overlay::assign_parents(offsetted_rings, traversed_rings, selected, false); return detail::overlay::add_rings(selected, offsetted_rings, traversed_rings, out); } diff --git a/include/boost/geometry/extensions/algorithms/buffer/line_line_intersection.hpp b/include/boost/geometry/extensions/algorithms/buffer/line_line_intersection.hpp index d0db8c3b3..94c4661d3 100644 --- a/include/boost/geometry/extensions/algorithms/buffer/line_line_intersection.hpp +++ b/include/boost/geometry/extensions/algorithms/buffer/line_line_intersection.hpp @@ -52,7 +52,7 @@ struct line_line_intersection { set<0>(p, x2); set<1>(p, y2); - return true; + return false; } coordinate_type d1 = det(x1, y1, x2, y2); diff --git a/include/boost/geometry/extensions/algorithms/buffer/multi_buffer_inserter.hpp b/include/boost/geometry/extensions/algorithms/buffer/multi_buffer_inserter.hpp new file mode 100644 index 000000000..0edbc7534 --- /dev/null +++ b/include/boost/geometry/extensions/algorithms/buffer/multi_buffer_inserter.hpp @@ -0,0 +1,91 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2012 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_MULTI_BUFFER_INSERTER_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_MULTI_BUFFER_INSERTER_HPP + +#include +#include + +#include + +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace buffer +{ + +template <> +struct check_original +{ + template + static inline int apply(Point const& point, Geometry const& geometry) + { + return geometry::covered_by(point, geometry) ? 1 : -1; + } +}; + +}} // namespace detail::buffer +#endif // DOXYGEN_NO_DETAIL + + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +template +< + typename Multi, + typename PolygonOutput +> +struct buffer_inserter +{ + template + < + typename Collection, typename DistanceStrategy, typename JoinStrategy + > + static inline void apply(Multi const& multi, + Collection& collection, + DistanceStrategy const& distance, + JoinStrategy const& join_strategy) + { + typedef typename ring_type::type output_ring_type; + typedef buffer_inserter + < + typename single_tag_of + < + typename tag::type + >::type, + typename boost::range_value::type, + output_ring_type + > policy; + + for (typename boost::range_iterator::type + it = boost::begin(multi); + it != boost::end(multi); + ++it) + { + policy::apply(*it, collection, distance, join_strategy); + } + } +}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_MULTI_BUFFER_INSERTER_HPP diff --git a/include/boost/geometry/extensions/strategies/buffer.hpp b/include/boost/geometry/extensions/strategies/buffer.hpp index df59c709f..aed27ff04 100644 --- a/include/boost/geometry/extensions/strategies/buffer.hpp +++ b/include/boost/geometry/extensions/strategies/buffer.hpp @@ -21,6 +21,7 @@ #include #include #include +#include #include @@ -157,9 +158,20 @@ struct join_round typedef typename strategy::side::services::default_strategy::type>::type side; typedef typename coordinate_type::type coordinate_type; + + typedef typename geometry::select_most_precise + < + typename geometry::select_most_precise + < + typename geometry::coordinate_type::type, + typename geometry::coordinate_type::type + >::type, + double + >::type promoted_type; + int m_max_level; - +#ifdef BOOST_GEOMETRY_BUFFER_USE_MIDPOINTS template inline void mid_points(PointIn const& vertex, PointIn const& p1, PointIn const& p2, @@ -196,9 +208,55 @@ struct join_round { mid_points(vertex, mid_point, p2, buffer_distance, range_out, level + 1); } - } +#endif + template + inline void generate_points(PointIn const& vertex, + PointIn const& perp1, PointIn const& perp2, + promoted_type const& buffer_distance, + RangeOut& range_out) const + { + promoted_type dx1 = get<0>(perp1) - get<0>(vertex); + promoted_type dy1 = get<1>(perp1) - get<1>(vertex); + promoted_type dx2 = get<0>(perp2) - get<0>(vertex); + promoted_type dy2 = get<1>(perp2) - get<1>(vertex); + + dx1 /= buffer_distance; + dy1 /= buffer_distance; + dx2 /= buffer_distance; + dy2 /= buffer_distance; + + promoted_type angle_diff = std::acos(dx1 * dx2 + dy1 * dy2); + + // Default might be 100 steps for a full circle (2 pi) + promoted_type const steps_per_circle = 100.0; + int n = int(steps_per_circle * angle_diff + / (2.0 * geometry::math::pi())); + + if (n > 1000) + { + std::cout << dx1 << ", " << dy1 << " .. " << dx2 << ", " << dy2 << std::endl; + std::cout << angle_diff << " -> " << n << std::endl; + n = 1000; + } + else if (n <= 1) + { + return; + } + + promoted_type const angle1 = std::atan2(dy1, dx1); + promoted_type diff = angle_diff / promoted_type(n); + promoted_type a = angle1 - diff; + + for (int i = 0; i < n - 1; i++, a -= diff) + { + PointIn p; + set<0>(p, get<0>(vertex) + buffer_distance * cos(a)); + set<1>(p, get<1>(vertex) + buffer_distance * sin(a)); + range_out.push_back(p); + } + } template inline void apply(PointIn const& ip, PointIn const& vertex, @@ -206,6 +264,12 @@ struct join_round coordinate_type const& buffer_distance, RangeOut& range_out) const { + if (geometry::equals(perp1, perp2)) + { + //std::cout << "Corner for equal points " << geometry::wkt(ip) << " " << geometry::wkt(perp1) << std::endl; + return; + } + coordinate_type zero = 0; int signum = buffer_distance > zero ? 1 : buffer_distance < zero ? -1 @@ -231,6 +295,8 @@ struct join_round set<1>(bp, get<1>(vertex) + viy * prop); range_out.push_back(perp1); + +#ifdef BOOST_GEOMETRY_BUFFER_USE_MIDPOINTS if (m_max_level <= 1) { if (m_max_level == 1) @@ -244,6 +310,10 @@ struct join_round range_out.push_back(bp); mid_points(vertex, bp, perp2, bd, range_out); } +#else + generate_points(vertex, perp1, perp2, bd, range_out); +#endif + range_out.push_back(perp2); } } diff --git a/test/robustness/overlay/buffer/Jamfile.v2 b/test/robustness/overlay/buffer/Jamfile.v2 new file mode 100644 index 000000000..f724fcf8c --- /dev/null +++ b/test/robustness/overlay/buffer/Jamfile.v2 @@ -0,0 +1,19 @@ +# Boost.Geometry (aka GGL, Generic Geometry Library) +# Robustness Test - overlay - buffer +# +# Copyright (c) 2012 Barend Gehrels, Amsterdam, the Netherlands. + +# Use, modification and distribution is subject to the Boost Software License, +# Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + + +project recursive_polygons_buffer + : requirements + . + ../.. + ../../../../../program_options/build//boost_program_options + static + ; + +exe recursive_polygons_buffer : recursive_polygons_buffer.cpp ; diff --git a/test/robustness/overlay/buffer/recursive_polygons_buffer.cpp b/test/robustness/overlay/buffer/recursive_polygons_buffer.cpp new file mode 100644 index 000000000..e54260f3b --- /dev/null +++ b/test/robustness/overlay/buffer/recursive_polygons_buffer.cpp @@ -0,0 +1,352 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) +// Robustness Test + +// Copyright (c) 2012 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#if defined(_MSC_VER) +# pragma warning( disable : 4244 ) +# pragma warning( disable : 4267 ) +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#include // TODO: more specific +#include + +#include + + +#include +#include + + +struct buffer_settings : public common_settings +{ + int join_code; + double distance; +}; + +namespace bg = boost::geometry; + +template +void create_svg(std::string const& filename + , Geometry1 const& mp + , Geometry2 const& buffer + ) +{ + typedef typename boost::geometry::point_type::type point_type; + + + std::ofstream svg(filename.c_str()); + boost::geometry::svg_mapper mapper(svg, 800, 800); + + boost::geometry::model::box box; + bg::envelope(mp, box); + bg::buffer(box, box, 1.0); + mapper.add(box); + + if (bg::num_points(buffer) > 0) + { + bg::envelope(buffer, box); + bg::buffer(box, box, 1.0); + mapper.add(box); + } + + mapper.map(mp, "fill-opacity:0.5;fill:rgb(153,204,0);stroke:rgb(153,204,0);stroke-width:3"); + mapper.map(buffer, "stroke-opacity:0.9;stroke:rgb(0,0,0);fill:none;stroke-width:1"); + + //mapper.map(intersection,"opacity:0.6;stroke:rgb(0,128,0);stroke-width:5"); +} + + + + +template +bool verify(std::string const& caseid, MultiPolygon const& mp, MultiPolygon const& buffer, Settings const& settings) +{ + bool result = true; + + // Area of buffer must be larger than of original polygon + BOOST_AUTO(area_mp, bg::area(mp)); + BOOST_AUTO(area_buf, bg::area(buffer)); + + if (area_buf < area_mp) + { + result = false; + } + + if (result) + { + typedef boost::range_value::type polygon_type; + BOOST_FOREACH(polygon_type const& polygon, mp) + { + typename bg::point_type::type point; + bg::point_on_border(point, polygon); + if (! bg::within(point, buffer)) + { + result = false; + } + } + } + + bool svg = settings.svg; + bool wkt = settings.wkt; + if (! result) + { + std::cout << "ERROR " << caseid << std::endl; + //std::cout << bg::wkt(mp) << std::endl; + //std::cout << bg::wkt(buffer) << std::endl; + svg = true; + wkt = true; + } + + if (svg || wkt) + { + //std::cout << caseid << std::endl; + } + + if (svg) + { + std::ostringstream filename; + filename << caseid << "_" + << typeid(typename bg::coordinate_type::type).name() + << ".svg"; + create_svg(filename.str(), mp, buffer); + } + + if (wkt) + { + std::ostringstream filename; + filename << caseid << "_" + << typeid(typename bg::coordinate_type::type).name() + << ".wkt"; + std::ofstream stream(filename.str().c_str()); + stream << bg::wkt(mp) << std::endl; + stream << bg::wkt(buffer) << std::endl; + } + + return result; +} + +template +bool test_buffer(MultiPolygon& result, int& index, + Generator& generator, + int level, Settings const& settings) +{ + MultiPolygon p, q; + + // Generate two boxes + if (level == 0) + { + p.resize(1); + q.resize(1); + make_square_polygon(p.front(), generator, settings); + make_square_polygon(q.front(), generator, settings); + bg::correct(p); + bg::correct(q); + } + else + { + bg::correct(p); + bg::correct(q); + if (! test_buffer(p, index, generator, level - 1, settings) + || ! test_buffer(q, index, generator, level - 1, settings)) + { + return false; + } + } + + typedef typename boost::range_value::type polygon; + + MultiPolygon mp; + bg::detail::union_::union_insert + < + polygon + >(p, q, std::back_inserter(mp)); + + bg::unique(mp); + bg::unique(mp); + bg::correct(mp); + result = mp; + + + typedef typename bg::coordinate_type::type coordinate_type; + typedef typename bg::point_type::type point_type; + typedef bg::strategy::buffer::distance_assymetric distance_strategy_type; + distance_strategy_type distance_strategy(settings.distance, settings.distance); + + typedef bg::strategy::buffer::join_round join_strategy_type; + join_strategy_type join_strategy; + + typedef typename boost::range_value::type polygon_type; + MultiPolygon buffered; + + std::ostringstream out; + out << "recursive_polygons_buffer_" << index++ << "_" << level; + + try + { + switch(settings.join_code) + { + case 1 : + bg::buffer_inserter(mp, std::back_inserter(buffered), + distance_strategy, + bg::strategy::buffer::join_round()); + break; + case 2 : + bg::buffer_inserter(mp, std::back_inserter(buffered), + distance_strategy, + bg::strategy::buffer::join_miter()); + break; + default : + return false; + } + } + catch(std::exception const& e) + { + MultiPolygon empty; + std::cout << out.str() << std::endl; + std::cout << "Exception " << e.what() << std::endl; + verify(out.str(), mp, empty, settings); + return false; + } + + + return verify(out.str(), mp, buffered, settings); +} + + +template +void test_all(int seed, int count, int level, Settings const& settings) +{ + boost::timer t; + + typedef boost::minstd_rand base_generator_type; + + base_generator_type generator(seed); + + boost::uniform_int<> random_coordinate(0, settings.field_size - 1); + boost::variate_generator > + coordinate_generator(generator, random_coordinate); + + typedef bg::model::polygon + < + bg::model::d2::point_xy, Clockwise, Closed + > polygon; + typedef bg::model::multi_polygon mp; + + + int index = 0; + for(int i = 0; i < count; i++) + { + mp p; + test_buffer(p, index, coordinate_generator, level, settings); + } + std::cout + << "geometries: " << index + << " type: " << typeid(T).name() + << " time: " << t.elapsed() << std::endl; +} + +int main(int argc, char** argv) +{ + try + { + namespace po = boost::program_options; + po::options_description description("=== recursive_polygons_linear_areal ===\nAllowed options"); + + int count = 1; + int seed = static_cast(std::time(0)); + int level = 3; + bool ccw = false; + bool open = false; + buffer_settings settings; + std::string form = "box"; + std::string join = "round"; + + description.add_options() + ("help", "Help message") + ("seed", po::value(&seed), "Initialization seed for random generator") + ("count", po::value(&count)->default_value(1), "Number of tests") + ("level", po::value(&level)->default_value(3), "Level to reach (higher->slower)") + ("distance", po::value(&settings.distance)->default_value(1.0), "Distance (1.0)") + ("form", po::value(&form)->default_value("box"), "Form of the polygons (box, triangle)") + ("join", po::value(&join)->default_value("round"), "Form of the joins (round, miter)") + ("ccw", po::value(&ccw)->default_value(false), "Counter clockwise polygons") + ("open", po::value(&open)->default_value(false), "Open polygons") + ("size", po::value(&settings.field_size)->default_value(10), "Size of the field") + ("wkt", po::value(&settings.wkt)->default_value(false), "Create a WKT of the inputs, for all tests") + ("svg", po::value(&settings.svg)->default_value(false), "Create a SVG for all tests") + ; + + po::variables_map varmap; + po::store(po::parse_command_line(argc, argv, description), varmap); + po::notify(varmap); + + if (varmap.count("help") + || (form != "box" && form != "triangle") + || (join != "round" && join != "miter") + ) + { + std::cout << description << std::endl; + return 1; + } + + settings.triangular = form != "box"; + settings.join_code = join == "round" ? 1 : 2; + + if (ccw && open) + { + test_all(seed, count, level, settings); + } + else if (ccw) + { + test_all(seed, count, level, settings); + } + else if (open) + { + test_all(seed, count, level, settings); + } + else + { + test_all(seed, count, level, settings); + } + +#if defined(HAVE_TTMATH) + // test_all(seed, count, max, svg, level); +#endif + } + catch(std::exception const& e) + { + std::cout << "Exception " << e.what() << std::endl; + } + catch(...) + { + std::cout << "Other exception" << std::endl; + } + + return 0; +} diff --git a/test/robustness/overlay/buffer/recursive_polygons_buffer.sln b/test/robustness/overlay/buffer/recursive_polygons_buffer.sln new file mode 100644 index 000000000..6e8c2574f --- /dev/null +++ b/test/robustness/overlay/buffer/recursive_polygons_buffer.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "recursive_polygons_buffer", "recursive_polygons_buffer.vcxproj", "{02C9CFA4-C625-55CA-1C8E-2B96EBB09FE8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {02C9CFA4-C625-55CA-1C8E-2B96EBB09FE8}.Debug|Win32.ActiveCfg = Debug|Win32 + {02C9CFA4-C625-55CA-1C8E-2B96EBB09FE8}.Debug|Win32.Build.0 = Debug|Win32 + {02C9CFA4-C625-55CA-1C8E-2B96EBB09FE8}.Release|Win32.ActiveCfg = Release|Win32 + {02C9CFA4-C625-55CA-1C8E-2B96EBB09FE8}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/test/robustness/overlay/buffer/recursive_polygons_buffer.vcproj b/test/robustness/overlay/buffer/recursive_polygons_buffer.vcproj new file mode 100644 index 000000000..3cc4a384f --- /dev/null +++ b/test/robustness/overlay/buffer/recursive_polygons_buffer.vcproj @@ -0,0 +1,222 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test_extensions/algorithms/buffer/Jamfile.v2 b/test_extensions/algorithms/buffer/Jamfile.v2 index c1f072817..c5e6b9a3c 100644 --- a/test_extensions/algorithms/buffer/Jamfile.v2 +++ b/test_extensions/algorithms/buffer/Jamfile.v2 @@ -16,5 +16,6 @@ test-suite boost-geometry-extensions-algorithms-buffer : [ run polygon_buffer.cpp ] [ run linestring_buffer.cpp ] + [ run multi_polygon_buffer.cpp ] ; diff --git a/test_extensions/algorithms/buffer/buffer.sln b/test_extensions/algorithms/buffer/buffer.sln index d43a684b7..57ff89e45 100644 --- a/test_extensions/algorithms/buffer/buffer.sln +++ b/test_extensions/algorithms/buffer/buffer.sln @@ -4,6 +4,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "polygon_buffer", "polygon_b EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "linestring_buffer", "linestring_buffer.vcproj", "{02387445-E879-49F4-8264-C7CF9C6B8B9D}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "multi_polygon_buffer", "multi_polygon_buffer.vcproj", "{1E74F110-996E-44DD-A2EC-5D3B55425903}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -18,6 +20,10 @@ Global {02387445-E879-49F4-8264-C7CF9C6B8B9D}.Debug|Win32.Build.0 = Debug|Win32 {02387445-E879-49F4-8264-C7CF9C6B8B9D}.Release|Win32.ActiveCfg = Release|Win32 {02387445-E879-49F4-8264-C7CF9C6B8B9D}.Release|Win32.Build.0 = Release|Win32 + {1E74F110-996E-44DD-A2EC-5D3B55425903}.Debug|Win32.ActiveCfg = Debug|Win32 + {1E74F110-996E-44DD-A2EC-5D3B55425903}.Debug|Win32.Build.0 = Debug|Win32 + {1E74F110-996E-44DD-A2EC-5D3B55425903}.Release|Win32.ActiveCfg = Release|Win32 + {1E74F110-996E-44DD-A2EC-5D3B55425903}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/test_extensions/algorithms/buffer/linestring_buffer.cpp b/test_extensions/algorithms/buffer/linestring_buffer.cpp index 17429bde1..d8a3cdcab 100644 --- a/test_extensions/algorithms/buffer/linestring_buffer.cpp +++ b/test_extensions/algorithms/buffer/linestring_buffer.cpp @@ -93,9 +93,9 @@ void test_all() //test_one("curve", curve, 'r', 99, 5.0, 3.0); //test_one("curve", curve, 'm', 99, 5.0, 3.0); - //test_one("chained2", chained2, 'r', 99, 2.5, 1.5); - //test_one("chained3", chained3, 'r', 99, 2.5, 1.5); - //test_one("chained4", chained4, 'r', 99, 2.5, 1.5); + test_one("chained2", chained2, 'r', 11.3137, 2.5, 1.5); + test_one("chained3", chained3, 'r', 16.9706, 2.5, 1.5); + test_one("chained4", chained4, 'r', 22.6274, 2.5, 1.5); //test_one("reallife1", reallife1, 'r', 99, 16.5, 6.5); } diff --git a/test_extensions/algorithms/buffer/multi_polygon_buffer.cpp b/test_extensions/algorithms/buffer/multi_polygon_buffer.cpp new file mode 100644 index 000000000..7c7964c87 --- /dev/null +++ b/test_extensions/algorithms/buffer/multi_polygon_buffer.cpp @@ -0,0 +1,123 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) +// Unit Test + +// Copyright (c) 2010-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + + +#include + +#define BOOST_GEOMETRY_TEST_BUFFER_POLYGON +#include + +#include // TODO: more specific +#include +#include + + + +static std::string const simplex + = "MULTIPOLYGON(((0 1,2 5,5 3,0 1)),((1 1,5 2,5 0,1 1)))"; + +static std::string const zonethru + = "MULTIPOLYGON(((0 0,0 6,5 6,5 4,3 4,3 0,0 0)),((5 0,5 2,7 2,7 6,10 6,10 0,5 0)))"; + +static std::string const wrapped + = "MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0),(2 2,8 2,8 8,2 8,2 2)),((4 4,4 6,6 6,6 4,4 4)))"; + +// From robustness tests + +// Case with duplicate points (due to chained boxes) (round) +static std::string const rt_a + = "MULTIPOLYGON(((2 7,2 8,3 8,3 7,2 7)),((5 4,5 5,6 5,6 4,5 4)),((5 8,6 8,6 7,6 6,5 6,5 7,4 7,4 8,5 8)),((3 5,4 5,4 4,3 4,2 4,2 5,3 5)))"; + +// Case with u-u (miter) +static std::string const rt_b + = "MULTIPOLYGON(((8 4,8 5,9 5,9 4,8 4)),((6 2,6 3,7 3,7 2,6 2)),((8 0,8 1,9 1,9 0,8 0)),((9 7,9 8,10 8,10 7,9 7)))"; + +// Case with geometry::equals( turn.point(7.0000000000000000, 4.3368086899420177e-019), helper_segment(7.0000000000000000, 0.00000000000000000))) (round) +static std::string const rt_c + = "MULTIPOLYGON(((6 1,6 2,7 2,7 1,6 1)),((8 0,8 1,9 1,9 0,8 0)))"; + +// Case with round corner on same perpendicular points (round) +static std::string const rt_d + = "MULTIPOLYGON(((2 2,2 3,3 2,2 2)),((2 5,2 6,3 5,2 5)),((2 4,2 5,3 4,2 4)),((3 2,3 3,4 2,3 2)),((4 4,4 5,5 4,4 4)),((5 6,5 5,4 5,4 6,5 7,5 6)),((2 2,3 1,3 0,2 0,2 1,1 1,1 2,2 2)),((1 3,1 2,0 2,1 3)),((1 4,2 4,2 3,1 3,1 4)))"; + +// Case with missing turning point (miter) and many intersections (round, OK) +static std::string const rt_e + = "MULTIPOLYGON(((0 6,0 7,1 6,0 6)),((3 7,3 8,4 8,4 7,3 7)),((4 6,4 7,5 7,4 6)),((3 6,3 7,4 6,3 6)),((1 9,2 10,2 9,1 9)),((1 9,1 8,0 8,0 9,1 9)),((3 5,3 4,2 4,2 5,2 6,3 5)))"; + +// Extact of e (miter) +static std::string const rt_f + = "MULTIPOLYGON(((0 6,0 7,1 6,0 6)),((1 9,1 8,0 8,0 9,1 9)))"; + +static std::string const rt_g + = "MULTIPOLYGON(((3 8,3 9,4 9,3 8)),((7 5,7 6,8 5,7 5)),((1 8,1 9,2 9,1 8)),((1 6,1 7,2 7,1 6)))"; + + + +template +void test_all() +{ + namespace buf = bg::strategy::buffer; + + typedef bg::model::polygon

polygon_type; + typedef bg::model::multi_polygon multi_polygon_type; +// goto wrong; + test_one("multi_simplex_05", simplex, 'r', 23.7030, 0.5); + test_one("multi_simplex_05", simplex, 'm', 24.5965, 0.5); + test_one("multi_simplex_10", simplex, 'r', 34.2532, 1.0); + test_one("multi_simplex_10", simplex, 'm', 38.1379, 1.0); + test_one("multi_simplex_20", simplex, 'r', 59.9159, 2.0); + test_one("multi_simplex_20", simplex, 'm', 77.7060, 2.0); + test_one("multi_simplex_50", simplex, 'r', 174.46, 5.0); + test_one("multi_simplex_50", simplex, 'm', 298.797, 5.0); + + test_one("zonethru_05", zonethru, 'r', 67.4627, 0.5); + test_one("zonethru_05", zonethru, 'm', 68.0000, 0.5); + test_one("zonethru_10", zonethru, 'r', 93.8508, 1.0, -999, 1); + test_one("zonethru_10", zonethru, 'm', 96.0000, 1.0); + test_one("zonethru_15", zonethru, 'r', 114.584, 1.5); + test_one("zonethru_15", zonethru, 'm', 117.000, 1.5); + test_one("wrapped_05", wrapped, 'r', 104.570, 0.5); + test_one("wrapped_05", wrapped, 'm', 105.000, 0.5); + test_one("wrapped_10", wrapped, 'r', 142.281, 1.0); + test_one("wrapped_10", wrapped, 'm', 144.000, 1.0); + test_one("wrapped_15", wrapped, 'r', 167.066, 1.5); + test_one("wrapped_15", wrapped, 'm', 169.000, 1.5); + + test_one("rt_a", rt_a, 'r', 34.5344, 1.0); + test_one("rt_a", rt_a, 'm', 36, 1.0); + test_one("rt_b", rt_b, 'r', 31.4186, 1.0); + test_one("rt_b", rt_b, 'm', 34, 1.0); + test_one("rt_c", rt_c, 'r', 14.7093, 1.0); + test_one("rt_c", rt_c, 'm', 16, 1.0); + test_one("rt_d", rt_d, 'r', 18.8726, 0.3); + test_one("rt_d", rt_d, 'm', 19.8823, 0.3); + test_one("rt_e", rt_e, 'r', 14.1866, 0.3); + test_one("rt_e", rt_e, 'm', 15.1198, 0.3); + test_one("rt_f", rt_f, 'r', 4.28937, 0.3); + test_one("rt_f", rt_f, 'm', 4.60853, 0.3); + +// TO BE FIXED: +// wrong: +// test_one("rt_g", rt_g, 'r', 99, 1.0); +// test_one("rt_g", rt_g, 'm', 99, 1.0); +} + + +#ifdef HAVE_TTMATH +#include +#endif + +int test_main(int, char* []) +{ + test_all >(); + //test_all >(); + + return 0; +} + diff --git a/test_extensions/algorithms/buffer/multi_polygon_buffer.vcproj b/test_extensions/algorithms/buffer/multi_polygon_buffer.vcproj new file mode 100644 index 000000000..5494a8631 --- /dev/null +++ b/test_extensions/algorithms/buffer/multi_polygon_buffer.vcproj @@ -0,0 +1,174 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test_extensions/algorithms/buffer/polygon_buffer.cpp b/test_extensions/algorithms/buffer/polygon_buffer.cpp index e096ad6fd..8df72b83e 100644 --- a/test_extensions/algorithms/buffer/polygon_buffer.cpp +++ b/test_extensions/algorithms/buffer/polygon_buffer.cpp @@ -19,6 +19,9 @@ static std::string const simplex = "POLYGON ((0 0,1 5,6 1,0 0))"; static std::string const concave_simplex = "POLYGON ((0 0,3 5,3 3,5 3,0 0))"; +static std::string const chained_box + = "POLYGON((0 0,0 4,4 4,8 4,12 4,12 0,8 0,4 0,0 0))"; + static std::string const donut_simplex = "POLYGON ((0 0,1 9,8 1,0 0),(1 1,4 1,1 4,1 1))"; static std::string const letter_L @@ -65,12 +68,14 @@ void test_all() typedef bg::model::polygon

polygon_type; - test_one("L", letter_L, 'm', 14.0, 0.5); - test_one("L", letter_L, 'r', 13.7314, 0.5); - test_one("simplex", simplex, 'm', 52.8733, 1.5); test_one("simplex", simplex, 'r', 47.9408, 1.5); - test_one("concave_simplex", concave_simplex, 'm', 16.3861, 0.5); + test_one("simplex", simplex, 'm', 52.8733, 1.5); test_one("concave_simplex", concave_simplex, 'r', 14.5616, 0.5); + test_one("concave_simplex", concave_simplex, 'm', 16.3861, 0.5); + test_one("chained_box", chained_box, 'r', 83.1403, 1.0); + test_one("chained_box", chained_box, 'm', 84, 1.0); + test_one("L", letter_L, 'r', 13.7314, 0.5); + test_one("L", letter_L, 'm', 14.0, 0.5); test_one("indentation4", indentation, 'm', 25.7741, 0.4); test_one("indentation4", indentation, 'r', 25.5695, 0.4); diff --git a/test_extensions/algorithms/buffer/test_buffer.hpp b/test_extensions/algorithms/buffer/test_buffer.hpp index 305912467..bd958ce95 100644 --- a/test_extensions/algorithms/buffer/test_buffer.hpp +++ b/test_extensions/algorithms/buffer/test_buffer.hpp @@ -10,7 +10,7 @@ #ifndef BOOST_GEOMETRY_TEST_BUFFER_HPP #define BOOST_GEOMETRY_TEST_BUFFER_HPP -//#define BOOST_GEOMETRY_DEBUG_WITH_MAPPER +#define BOOST_GEOMETRY_DEBUG_WITH_MAPPER #define TEST_WITH_SVG #include @@ -79,7 +79,7 @@ template void test_buffer(std::string const& caseid, Geometry const& geometry, char join, bool check, double expected_area, - double distance_left, double distance_right) + double distance_left, double distance_right, int expected_self_tangencies) { namespace bg = boost::geometry; @@ -89,9 +89,17 @@ void test_buffer(std::string const& caseid, Geometry const& geometry, typedef typename bg::ring_type::type ring_type; + typedef typename bg::tag::type tag; + std::string type = boost::is_same::value ? "poly" + : boost::is_same::value ? "line" + : boost::is_same::value ? "multipoly" + : boost::is_same::value ? "multiline" + : "" + ; + std::ostringstream complete; complete - << (bg::geometry_id::value == 2 ? "line" : "poly") << "_" + << type << "_" << caseid << "_" << string_from_type::name() << "_" << join; @@ -113,8 +121,12 @@ void test_buffer(std::string const& caseid, Geometry const& geometry, { d += std::abs(distance_right); } + else + { + distance_right = distance_left; + } - bg::buffer(box, box, d * 1.1); + bg::buffer(box, box, d * (join == 'm' ? 2.0 : 1.1)); mapper.add(box); } @@ -167,7 +179,8 @@ void test_buffer(std::string const& caseid, Geometry const& geometry, // Be sure resulting polygon does not contain // self-intersections // But indentation5 should contain 1 self-ip TODO give this check as an argument - if (! boost::contains(complete.str(), "indentation5_d_r") + if (expected_self_tangencies == 0 + && ! boost::contains(complete.str(), "indentation5_d_r") && ! boost::contains(complete.str(), "flower25_d_r")) { BOOST_FOREACH(GeometryOut const& polygon, buffered) @@ -205,7 +218,7 @@ template > void test_one(std::string const& caseid, std::string const& wkt, char join, double expected_area, - double distance_left, double distance_right = -999) + double distance_left, double distance_right = -999, int expected_self_tangencies = 0) { namespace bg = boost::geometry; Geometry g; @@ -227,48 +240,10 @@ void test_one(std::string const& caseid, std::string const& wkt, #endif test_buffer - (caseid, g, join, false, expected_area, distance_left, distance_right); + (caseid, g, join, false, expected_area, + distance_left, distance_right, expected_self_tangencies); } -template -< - typename Geometry, - template class JoinStrategy, - typename GeometryOut -> -void test_one(bool check, std::string const& caseid, std::string const& wkt, - char join, double expected_area, - double distance_left, double distance_right = -999) -{ - namespace bg = boost::geometry; - Geometry g; - bg::read_wkt(wkt, g); - - typedef typename bg::point_type::type point_type; - - //std::cout << caseid << std::endl; - if (join == 'm') - { - //return; - } - - - -#ifdef BOOST_GEOMETRY_CHECK_WITH_POSTGIS - std::cout - << (counter > 0 ? "union " : "") - << "select " << counter++ - << ", '" << caseid << "' as caseid" - << ", ST_Area(ST_Buffer(ST_GeomFromText('" << wkt << "'), " - << distance_left - << ", 'endcap=flat join=" << (join == 'm' ? "miter" : "round") << "'))" - << ", " << expected_area - << std::endl; -#endif - - test_buffer - (caseid, g, join, check, expected_area, distance_left, distance_right); -} #endif