From 32175d483b5234bfef0a14b0ec87eeb1cd26c402 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 25 Feb 2015 22:47:48 +0100 Subject: [PATCH 01/24] [buffer][test] increase tolerance of tests added today, to support slightly varying results on different compilers --- test/algorithms/buffer/multi_point_buffer.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/algorithms/buffer/multi_point_buffer.cpp b/test/algorithms/buffer/multi_point_buffer.cpp index dafa3c0c3..e57a6af66 100644 --- a/test/algorithms/buffer/multi_point_buffer.cpp +++ b/test/algorithms/buffer/multi_point_buffer.cpp @@ -88,15 +88,15 @@ void test_all() using bg::strategy::buffer::point_circle; test_with_custom_strategies("mysql_report_2015_02_25_1_800", mysql_report_2015_02_25_1, join_miter, end_flat, - distance_strategy(6051788), side_strategy, point_circle(800), 115057490003226.125); + distance_strategy(6051788), side_strategy, point_circle(800), 115057490003226.125, 1.0); test_with_custom_strategies("mysql_report_2015_02_25_1_8000", mysql_report_2015_02_25_1, join_miter, end_flat, - distance_strategy(6051788), side_strategy, point_circle(8000), 115058661065242.812); + distance_strategy(6051788), side_strategy, point_circle(8000), 115058661065242.812, 1.0); test_with_custom_strategies("mysql_report_2015_02_25_1", mysql_report_2015_02_25_1, join_miter, end_flat, - distance_strategy(6051788), side_strategy, point_circle(83585), 115058672785611.219); + distance_strategy(6051788), side_strategy, point_circle(83585), 115058672785611.219, 1.0); #if defined(BOOST_GEOMETRY_BUFFER_INCLUDE_FAILING_TESTS) // Try to specify even more points per circle @@ -104,12 +104,12 @@ void test_all() // But the follow-up still takes too long (there are 63409 turns), this might be improved too test_with_custom_strategies("mysql_report_2015_02_25_1", mysql_report_2015_02_25_1, join_miter, end_flat, - distance_strategy(6051788), side_strategy, point_circle(800000), 115058672785611.219); + distance_strategy(6051788), side_strategy, point_circle(800000), 115058672799999.999, 1.0); // area to be determined #endif test_with_custom_strategies("mysql_report_2015_02_25_2", mysql_report_2015_02_25_2, join_miter, end_flat, - distance_strategy(5666962), side_strategy, point_circle(46641), 100891031341757.344); + distance_strategy(5666962), side_strategy, point_circle(46641), 100891031341757.344, 1.0); } } From 32d35b5c05c0ba5c0a6d5f0e07430309514bf570 Mon Sep 17 00:00:00 2001 From: Menelaos Karavelas Date: Thu, 26 Feb 2015 00:46:48 +0200 Subject: [PATCH 02/24] [doc][geometry] update copyright year for the generated documentation --- doc/geometry.qbk | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/geometry.qbk b/doc/geometry.qbk index 4cd56e81a..442604534 100644 --- a/doc/geometry.qbk +++ b/doc/geometry.qbk @@ -1,9 +1,9 @@ [/============================================================================ Boost.Geometry (aka GGL, Generic Geometry Library) - Copyright (c) 2009-2012 Barend Gehrels, Amsterdam, the Netherlands. - Copyright (c) 2009-2012 Mateusz Loskot, London, UK. - Copyright (c) 2009-2012 Bruno Lalande, Paris, France. + Copyright (c) 2009-2015 Barend Gehrels, Amsterdam, the Netherlands. + Copyright (c) 2009-2015 Mateusz Loskot, London, UK. + Copyright (c) 2009-2015 Bruno Lalande, Paris, France. Use, modification and distribution is subject to the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -13,7 +13,7 @@ [library Geometry [quickbook 1.5] [authors [Gehrels, Barend], [Lalande, Bruno], [Loskot, Mateusz], [Wulkiewicz, Adam], [Karavelas, Menelaos]] - [copyright 2009-2014 Barend Gehrels, Bruno Lalande, Mateusz Loskot, Adam Wulkiewicz, Oracle and/or its affiliates] + [copyright 2009-2015 Barend Gehrels, Bruno Lalande, Mateusz Loskot, Adam Wulkiewicz, Oracle and/or its affiliates] [purpose Documentation of Boost.Geometry library] [license Distributed under the Boost Software License, Version 1.0. From d31cabb0679885c07045d94ab8c4d6d554ede20b Mon Sep 17 00:00:00 2001 From: Menelaos Karavelas Date: Thu, 26 Feb 2015 17:30:24 +0200 Subject: [PATCH 03/24] [strategies][cartesian][buffer][point_circle] fix internal point count for small input values (less than 3): when the input point count is less than 3, set the internal point count to 3; this is important for generating valid polygons; --- .../strategies/cartesian/buffer_point_circle.hpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/include/boost/geometry/strategies/cartesian/buffer_point_circle.hpp b/include/boost/geometry/strategies/cartesian/buffer_point_circle.hpp index f64a82d8f..86ebc43c9 100644 --- a/include/boost/geometry/strategies/cartesian/buffer_point_circle.hpp +++ b/include/boost/geometry/strategies/cartesian/buffer_point_circle.hpp @@ -1,5 +1,12 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2012-2014 Barend Gehrels, Amsterdam, the Netherlands. + +// Copyright (c) 2012-2015 Barend Gehrels, Amsterdam, the Netherlands. + +// This file was modified by Oracle on 2015. +// Modifications copyright (c) 2015, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + // 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) @@ -45,9 +52,10 @@ class point_circle { public : //! \brief Constructs the strategy - //! \param count number of points for the created circle + //! \param count number of points for the created circle (if count + //! is smaller than 3, count is internally set to 3) explicit point_circle(std::size_t count = 90) - : m_count(count) + : m_count((count < 3u) ? 3u : count) {} #ifndef DOXYGEN_SHOULD_SKIP_THIS From 6afc5a464a5b5340462f834ee61d3f6730c31b84 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Fri, 27 Feb 2015 22:11:50 +0100 Subject: [PATCH 04/24] [buffer] performance: use monotonic properties of monotonic sections to limit quadratic iterations within two overlapping sections This can improve performance with about 25% --- .../detail/buffer/get_piece_turns.hpp | 58 ++++++++++++++-- .../detail/sections/section_functions.hpp | 66 +++++++++++++++++++ 2 files changed, 118 insertions(+), 6 deletions(-) create mode 100644 include/boost/geometry/algorithms/detail/sections/section_functions.hpp diff --git a/include/boost/geometry/algorithms/detail/buffer/get_piece_turns.hpp b/include/boost/geometry/algorithms/detail/buffer/get_piece_turns.hpp index 31ffdec53..93ba95938 100644 --- a/include/boost/geometry/algorithms/detail/buffer/get_piece_turns.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/get_piece_turns.hpp @@ -16,6 +16,7 @@ #include #include #include +#include namespace boost { namespace geometry @@ -88,6 +89,38 @@ class piece_turn_visitor return result; } + template + inline void move_begin_iterator(Iterator& it_begin, Iterator it_beyond, + int& index, int dir, Box const& other_bounding_box) + { + for(; it_begin != it_beyond + && it_begin + 1 != it_beyond + && detail::section::preceding<0>(dir, *(it_begin + 1), + other_bounding_box, m_robust_policy); + ++it_begin, index++) + {} + } + + template + inline void move_end_iterator(Iterator it_begin, Iterator& it_beyond, + int dir, Box const& other_bounding_box) + { + while (it_beyond != it_begin + && it_beyond - 1 != it_begin + && it_beyond - 2 != it_begin) + { + if (detail::section::exceeding<0>(dir, *(it_beyond - 2), + other_bounding_box, m_robust_policy)) + { + --it_beyond; + } + else + { + return; + } + } + } + template inline void calculate_turns(Piece const& piece1, Piece const& piece2, Section const& section1, Section const& section2) @@ -113,17 +146,30 @@ class piece_turn_visitor // get geometry and iterators over these sections ring_type const& ring1 = m_rings[piece1.first_seg_id.multi_index]; - iterator const it1_first = boost::begin(ring1) + sec1_first_index; - iterator const it1_beyond = boost::begin(ring1) + sec1_last_index + 1; + iterator it1_first = boost::begin(ring1) + sec1_first_index; + iterator it1_beyond = boost::begin(ring1) + sec1_last_index + 1; ring_type const& ring2 = m_rings[piece2.first_seg_id.multi_index]; - iterator const it2_first = boost::begin(ring2) + sec2_first_index; - iterator const it2_beyond = boost::begin(ring2) + sec2_last_index + 1; + iterator it2_first = boost::begin(ring2) + sec2_first_index; + iterator it2_beyond = boost::begin(ring2) + sec2_last_index + 1; + + // Set begin/end of monotonic ranges + int index1 = sec1_first_index; + move_begin_iterator(it1_first, it1_beyond, index1, + section1.directions[0], section2.bounding_box); + move_end_iterator(it1_first, it1_beyond, + section1.directions[0], section2.bounding_box); + + int index2 = sec2_first_index; + move_begin_iterator(it2_first, it2_beyond, index2, + section2.directions[0], section1.bounding_box); + move_end_iterator(it2_first, it2_beyond, + section2.directions[0], section1.bounding_box); turn_type the_model; the_model.operations[0].piece_index = piece1.index; the_model.operations[0].seg_id = piece1.first_seg_id; - the_model.operations[0].seg_id.segment_index = sec1_first_index; // override + the_model.operations[0].seg_id.segment_index = index1; // override iterator it1 = it1_first; for (iterator prev1 = it1++; @@ -132,7 +178,7 @@ class piece_turn_visitor { the_model.operations[1].piece_index = piece2.index; the_model.operations[1].seg_id = piece2.first_seg_id; - the_model.operations[1].seg_id.segment_index = sec2_first_index; // override + the_model.operations[1].seg_id.segment_index = index2; // override iterator next1 = next_point(ring1, it1); diff --git a/include/boost/geometry/algorithms/detail/sections/section_functions.hpp b/include/boost/geometry/algorithms/detail/sections/section_functions.hpp new file mode 100644 index 000000000..ba1cf931b --- /dev/null +++ b/include/boost/geometry/algorithms/detail/sections/section_functions.hpp @@ -0,0 +1,66 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2015 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_SECTIONS_FUNCTIONS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_SECTIONS_FUNCTIONS_HPP + + +#include +#include +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace section +{ + +template +< + std::size_t Dimension, + typename Point, + typename RobustBox, + typename RobustPolicy +> +static inline bool preceding(int dir, Point const& point, + RobustBox const& robust_box, + RobustPolicy const& robust_policy) +{ + typename geometry::robust_point_type::type robust_point; + geometry::recalculate(robust_point, point, robust_policy); + return (dir == 1 && get(robust_point) < get(robust_box)) + || (dir == -1 && get(robust_point) > get(robust_box)); +} + +template +< + std::size_t Dimension, + typename Point, + typename RobustBox, + typename RobustPolicy +> +static inline bool exceeding(int dir, Point const& point, + RobustBox const& robust_box, + RobustPolicy const& robust_policy) +{ + typename geometry::robust_point_type::type robust_point; + geometry::recalculate(robust_point, point, robust_policy); + return (dir == 1 && get(robust_point) > get(robust_box)) + || (dir == -1 && get(robust_point) < get(robust_box)); +} + + +}} // namespace detail::section +#endif + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_SECTIONS_FUNCTIONS_HPP From a6d10d610cb364c179a623ea4047f50ea9b8b903 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Fri, 27 Feb 2015 22:15:08 +0100 Subject: [PATCH 05/24] [turns] use preceding/exceeding from headerfile --- .../algorithms/detail/overlay/get_turns.hpp | 26 +++---------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/get_turns.hpp b/include/boost/geometry/algorithms/detail/overlay/get_turns.hpp index 88104d25a..a5d8f3f02 100644 --- a/include/boost/geometry/algorithms/detail/overlay/get_turns.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/get_turns.hpp @@ -63,6 +63,7 @@ #include #include +#include #ifdef BOOST_GEOMETRY_DEBUG_INTERSECTION # include @@ -228,7 +229,7 @@ public : // section 2: [--------------] // section 1: |----|---|---|---|---| for (prev1 = it1++, next1++; - it1 != end1 && ! exceeding<0>(dir1, *prev1, sec2.bounding_box, robust_policy); + it1 != end1 && ! detail::section::exceeding<0>(dir1, *prev1, sec2.bounding_box, robust_policy); ++prev1, ++it1, ++index1, ++next1, ++ndi1) { ever_circling_iterator nd_next1( @@ -246,7 +247,7 @@ public : next2++; for (prev2 = it2++, next2++; - it2 != end2 && ! exceeding<0>(dir2, *prev2, sec1.bounding_box, robust_policy); + it2 != end2 && ! detail::section::exceeding<0>(dir2, *prev2, sec1.bounding_box, robust_policy); ++prev2, ++it2, ++index2, ++next2, ++ndi2) { bool skip = same_source; @@ -317,25 +318,6 @@ private : typedef typename model::referring_segment segment1_type; typedef typename model::referring_segment segment2_type; - - template - static inline bool preceding(int dir, Point const& point, Box const& box, RobustPolicy const& robust_policy) - { - typename robust_point_type::type robust_point; - geometry::recalculate(robust_point, point, robust_policy); - return (dir == 1 && get(robust_point) < get(box)) - || (dir == -1 && get(robust_point) > get(box)); - } - - template - static inline bool exceeding(int dir, Point const& point, Box const& box, RobustPolicy const& robust_policy) - { - typename robust_point_type::type robust_point; - geometry::recalculate(robust_point, point, robust_policy); - return (dir == 1 && get(robust_point) > get(box)) - || (dir == -1 && get(robust_point) < get(box)); - } - template static inline void advance_to_non_duplicate_next(Iterator& next, RangeIterator const& it, Section const& section, RobustPolicy const& robust_policy) @@ -386,7 +368,7 @@ private : // Mimic section-iterator: // Skip to point such that section interects other box prev = it++; - for(; it != end && preceding<0>(dir, *it, other_bounding_box, robust_policy); + for(; it != end && detail::section::preceding<0>(dir, *it, other_bounding_box, robust_policy); prev = it++, index++, ndi++) {} // Go back one step because we want to start completely preceding From c244debf530471651cf49ab777ec70acdd088819 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Sat, 28 Feb 2015 12:58:51 +0100 Subject: [PATCH 06/24] [buffer] calculate turns: use monotonic properties in two dimensions (does not give improvements for circles, however it might do for ellipses longer in y dimension) --- .../detail/buffer/get_piece_turns.hpp | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/buffer/get_piece_turns.hpp b/include/boost/geometry/algorithms/detail/buffer/get_piece_turns.hpp index 93ba95938..6a3daa282 100644 --- a/include/boost/geometry/algorithms/detail/buffer/get_piece_turns.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/get_piece_turns.hpp @@ -89,19 +89,19 @@ class piece_turn_visitor return result; } - template + template inline void move_begin_iterator(Iterator& it_begin, Iterator it_beyond, int& index, int dir, Box const& other_bounding_box) { for(; it_begin != it_beyond && it_begin + 1 != it_beyond - && detail::section::preceding<0>(dir, *(it_begin + 1), + && detail::section::preceding(dir, *(it_begin + 1), other_bounding_box, m_robust_policy); ++it_begin, index++) {} } - template + template inline void move_end_iterator(Iterator it_begin, Iterator& it_beyond, int dir, Box const& other_bounding_box) { @@ -109,7 +109,7 @@ class piece_turn_visitor && it_beyond - 1 != it_begin && it_beyond - 2 != it_begin) { - if (detail::section::exceeding<0>(dir, *(it_beyond - 2), + if (detail::section::exceeding(dir, *(it_beyond - 2), other_bounding_box, m_robust_policy)) { --it_beyond; @@ -153,18 +153,26 @@ class piece_turn_visitor iterator it2_first = boost::begin(ring2) + sec2_first_index; iterator it2_beyond = boost::begin(ring2) + sec2_last_index + 1; - // Set begin/end of monotonic ranges + // Set begin/end of monotonic ranges, in both x/y directions int index1 = sec1_first_index; - move_begin_iterator(it1_first, it1_beyond, index1, + move_begin_iterator<0>(it1_first, it1_beyond, index1, section1.directions[0], section2.bounding_box); - move_end_iterator(it1_first, it1_beyond, + move_end_iterator<0>(it1_first, it1_beyond, section1.directions[0], section2.bounding_box); + move_begin_iterator<1>(it1_first, it1_beyond, index1, + section1.directions[1], section2.bounding_box); + move_end_iterator<1>(it1_first, it1_beyond, + section1.directions[1], section2.bounding_box); int index2 = sec2_first_index; - move_begin_iterator(it2_first, it2_beyond, index2, + move_begin_iterator<0>(it2_first, it2_beyond, index2, section2.directions[0], section1.bounding_box); - move_end_iterator(it2_first, it2_beyond, + move_end_iterator<0>(it2_first, it2_beyond, section2.directions[0], section1.bounding_box); + move_begin_iterator<1>(it2_first, it2_beyond, index2, + section2.directions[1], section1.bounding_box); + move_end_iterator<1>(it2_first, it2_beyond, + section2.directions[1], section1.bounding_box); turn_type the_model; the_model.operations[0].piece_index = piece1.index; From ad24b1001fc5ea5fa5a55d346f38f23f2bdb478a Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Sat, 28 Feb 2015 13:06:21 +0100 Subject: [PATCH 07/24] [test] Add define for debug/release mode (now recognized by gcc/clang/msvc) --- test/geometry_test_common.hpp | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/test/geometry_test_common.hpp b/test/geometry_test_common.hpp index 1399e8d6c..415d4b670 100644 --- a/test/geometry_test_common.hpp +++ b/test/geometry_test_common.hpp @@ -1,8 +1,8 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. -// Copyright (c) 2008-2012 Bruno Lalande, Paris, France. -// Copyright (c) 2009-2012 Mateusz Loskot, London, UK. +// Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2015 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2015 Mateusz Loskot, London, UK. // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. @@ -15,6 +15,26 @@ #ifndef GEOMETRY_TEST_GEOMETRY_TEST_COMMON_HPP #define GEOMETRY_TEST_GEOMETRY_TEST_COMMON_HPP +// Determine debug/release mode +// (it would be convenient if Boost.Config or Boost.Test would define this) +#if defined (__clang__) || defined(__gcc__) +#if defined(__OPTIMIZE__) + #define BOOST_GEOMETRY_COMPILER_MODE_RELEASE +#endif +#if defined(__NO_INLINE__) +#define BOOST_GEOMETRY_COMPILER_MODE_DEBUG +#endif +#endif + +#if defined(_MSC_VER) +#if defined(_DEBUG) +#define BOOST_GEOMETRY_COMPILER_MODE_DEBUG +#else +#define BOOST_GEOMETRY_COMPILER_MODE_RELEASE +#endif +#endif + + #if defined(_MSC_VER) // We deliberately mix float/double's so turn off warnings #pragma warning( disable : 4244 ) From 4652e56d4048ad15c79417b54872329ce91bce2b Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Sat, 28 Feb 2015 13:06:49 +0100 Subject: [PATCH 08/24] [buffer][test] test tests with many points only in release mode --- test/algorithms/buffer/multi_point_buffer.cpp | 94 +++++++++++++------ 1 file changed, 66 insertions(+), 28 deletions(-) diff --git a/test/algorithms/buffer/multi_point_buffer.cpp b/test/algorithms/buffer/multi_point_buffer.cpp index e57a6af66..ff1b185f3 100644 --- a/test/algorithms/buffer/multi_point_buffer.cpp +++ b/test/algorithms/buffer/multi_point_buffer.cpp @@ -1,7 +1,7 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) // Unit Test -// Copyright (c) 2012-2014 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2012-2015 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 @@ -73,44 +73,76 @@ void test_all() distance_strategy(0.54), side_strategy, point_strategy, 99); #endif - } + test_with_custom_strategies("mysql_report_2015_02_25_1_800", + mysql_report_2015_02_25_1, join_miter, end_flat, + distance_strategy(6051788), side_strategy, + bg::strategy::buffer::point_circle(800), 115057490003226.125, 1.0); +} + +template +void test_many_points_per_circle() +{ // Tests for large distances / many points in circles. // Before Boost 1.58, this would (seem to) hang. It is solved by using monotonic sections in get_turns for buffer // This is more time consuming, only calculate this for counter clockwise - if (! BOOST_GEOMETRY_CONDITION(Clockwise)) - { - // Reported by MySQL 2015-02-25 - // SELECT ST_ASTEXT(ST_BUFFER(ST_GEOMFROMTEXT(''), 6051788, ST_BUFFER_STRATEGY('point_circle', 83585))); - // SELECT ST_ASTEXT(ST_BUFFER(ST_GEOMFROMTEXT(''), 5666962, ST_BUFFER_STRATEGY('point_circle', 46641))) ; + // Reported by MySQL 2015-02-25 + // SELECT ST_ASTEXT(ST_BUFFER(ST_GEOMFROMTEXT(''), 6051788, ST_BUFFER_STRATEGY('point_circle', 83585))); + // SELECT ST_ASTEXT(ST_BUFFER(ST_GEOMFROMTEXT(''), 5666962, ST_BUFFER_STRATEGY('point_circle', 46641))) ; - using bg::strategy::buffer::point_circle; - test_with_custom_strategies("mysql_report_2015_02_25_1_800", - mysql_report_2015_02_25_1, join_miter, end_flat, - distance_strategy(6051788), side_strategy, point_circle(800), 115057490003226.125, 1.0); + typedef bg::model::polygon polygon; + typedef bg::model::multi_point

multi_point_type; - test_with_custom_strategies("mysql_report_2015_02_25_1_8000", - mysql_report_2015_02_25_1, join_miter, end_flat, - distance_strategy(6051788), side_strategy, point_circle(8000), 115058661065242.812, 1.0); + bg::strategy::buffer::join_miter join_miter; + bg::strategy::buffer::end_flat end_flat; + typedef bg::strategy::buffer::distance_symmetric + < + typename bg::coordinate_type

::type + > distance_strategy; + bg::strategy::buffer::side_straight side_strategy; - test_with_custom_strategies("mysql_report_2015_02_25_1", - mysql_report_2015_02_25_1, join_miter, end_flat, - distance_strategy(6051788), side_strategy, point_circle(83585), 115058672785611.219, 1.0); + using bg::strategy::buffer::point_circle; -#if defined(BOOST_GEOMETRY_BUFFER_INCLUDE_FAILING_TESTS) - // Try to specify even more points per circle - // Turns are calculated in 23 seconds - // But the follow-up still takes too long (there are 63409 turns), this might be improved too - test_with_custom_strategies("mysql_report_2015_02_25_1", - mysql_report_2015_02_25_1, join_miter, end_flat, - distance_strategy(6051788), side_strategy, point_circle(800000), 115058672799999.999, 1.0); // area to be determined + double const tolerance = 1.0; + + // Strategies with many points, which are (very) slow in debug mode + test_with_custom_strategies( + "mysql_report_2015_02_25_1_8000", + mysql_report_2015_02_25_1, join_miter, end_flat, + distance_strategy(6051788), side_strategy, point_circle(8000), + 115058661065242.812, tolerance); + + test_with_custom_strategies( + "mysql_report_2015_02_25_1", + mysql_report_2015_02_25_1, join_miter, end_flat, + distance_strategy(6051788), side_strategy, point_circle(83585), + 115058672785611.219, tolerance); + +#if defined(BOOST_GEOMETRY_BUFFER_INCLUDE_SLOW_TESTS) + // Takes about 60 seconds in release mode + test_with_custom_strategies( + "mysql_report_2015_02_25_1_250k", + mysql_report_2015_02_25_1, join_miter, end_flat, + distance_strategy(6051788), side_strategy, point_circle(250000), + 115058672880671.531, tolerance); #endif - test_with_custom_strategies("mysql_report_2015_02_25_2", - mysql_report_2015_02_25_2, join_miter, end_flat, - distance_strategy(5666962), side_strategy, point_circle(46641), 100891031341757.344, 1.0); - } +#if defined(BOOST_GEOMETRY_BUFFER_INCLUDE_FAILING_TESTS) + // Takes too long, TODO improve turn_in_piece_visitor + test_with_custom_strategies( + "mysql_report_2015_02_25_1", + mysql_report_2015_02_25_1, join_miter, end_flat, + distance_strategy(6051788), side_strategy, point_circle(800000), + 115058672799999.999, tolerance); // area to be determined +#endif + + test_with_custom_strategies( + "mysql_report_2015_02_25_2", + mysql_report_2015_02_25_2, join_miter, end_flat, + distance_strategy(5666962), side_strategy, point_circle(46641), + 100891031341757.344, tolerance); + } int test_main(int, char* []) @@ -118,5 +150,11 @@ int test_main(int, char* []) test_all >(); test_all >(); +#if defined(BOOST_GEOMETRY_COMPILER_MODE_RELEASE) && ! defined(BOOST_GEOMETRY_COMPILER_MODE_DEBUG) + test_many_points_per_circle >(); +#else + std::cout << "Skipping some tests in debug or unknown mode" << std::endl; +#endif + return 0; } From 4d4c98f213394c016169ee685f1bee760b7db971 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Sat, 28 Feb 2015 13:07:08 +0100 Subject: [PATCH 09/24] [test] add comment --- test/geometry_test_common.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/geometry_test_common.hpp b/test/geometry_test_common.hpp index 415d4b670..ca9959ad7 100644 --- a/test/geometry_test_common.hpp +++ b/test/geometry_test_common.hpp @@ -17,6 +17,7 @@ // Determine debug/release mode // (it would be convenient if Boost.Config or Boost.Test would define this) +// Note that they might be combined (e.g. for optimize+no inline) #if defined (__clang__) || defined(__gcc__) #if defined(__OPTIMIZE__) #define BOOST_GEOMETRY_COMPILER_MODE_RELEASE From f45df767d51a73206ba83bfd79e40aef2a325e04 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Sun, 1 Mar 2015 11:25:13 +0100 Subject: [PATCH 10/24] [buffer] remove obsolete file --- .../detail/buffer/turn_in_input.hpp | 98 ------------------- 1 file changed, 98 deletions(-) delete mode 100644 include/boost/geometry/algorithms/detail/buffer/turn_in_input.hpp diff --git a/include/boost/geometry/algorithms/detail/buffer/turn_in_input.hpp b/include/boost/geometry/algorithms/detail/buffer/turn_in_input.hpp deleted file mode 100644 index 2b1c33d29..000000000 --- a/include/boost/geometry/algorithms/detail/buffer/turn_in_input.hpp +++ /dev/null @@ -1,98 +0,0 @@ -// Boost.Geometry (aka GGL, Generic Geometry Library) - -// Copyright (c) 2012-2014 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_TURN_IN_INPUT_HPP -#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_TURN_IN_INPUT_HPP - -#include -#include - - -namespace boost { namespace geometry -{ - -#ifndef DOXYGEN_NO_DETAIL -namespace detail { namespace buffer -{ - -// Checks if an turn/intersection point is inside (or covered by) the input geometry - -template -struct turn_in_input -{ -}; - -template <> -struct turn_in_input -{ - template - static inline int apply(Point const& point, Geometry const& geometry) - { - return geometry::covered_by(point, geometry) ? 1 : -1; - } -}; - -template <> -struct turn_in_input -{ - template - static inline int apply(Point const& , Geometry const& ) - { - return 0; - } -}; - -template <> -struct turn_in_input -{ - template - static inline int apply(Point const& , Geometry const& ) - { - return 0; - } -}; - -template <> -struct turn_in_input -{ - template - static inline int apply(Point const& point, Geometry const& geometry) - { - return geometry::covered_by(point, geometry) ? 1 : -1; - } -}; - -template <> -struct turn_in_input -{ - template - static inline int apply(Point const& , Geometry const& ) - { - return 0; - } -}; - -template <> -struct turn_in_input -{ - template - static inline int apply(Point const& , Geometry const& ) - { - return 0; - } -}; - - -}} // namespace detail::buffer -#endif // DOXYGEN_NO_DETAIL - - - -}} // namespace boost::geometry - -#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_TURN_IN_INPUT_HPP From cf167f9a3695228f2421e7b15a45cb1eb10e3825 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Sun, 1 Mar 2015 13:57:39 +0100 Subject: [PATCH 11/24] [sectionalize] support sections in vector (so sections_type is redundant) --- .../algorithms/detail/sections/sectionalize.hpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/sections/sectionalize.hpp b/include/boost/geometry/algorithms/detail/sections/sectionalize.hpp index 3d1d8e1d1..a744ea0a3 100644 --- a/include/boost/geometry/algorithms/detail/sections/sectionalize.hpp +++ b/include/boost/geometry/algorithms/detail/sections/sectionalize.hpp @@ -74,6 +74,7 @@ template struct section { typedef Box box_type; + static std::size_t const dimension_count = DimensionCount; int directions[DimensionCount]; ring_identifier ring_id; @@ -286,13 +287,14 @@ struct sectionalize_part std::size_t max_count) { boost::ignore_unused_variable_warning(robust_policy); + + typedef typename boost::range_value::type section_type; BOOST_STATIC_ASSERT ( - (static_cast(Sections::value) + (static_cast(section_type::dimension_count) == static_cast(boost::mpl::size::value)) ); - typedef typename boost::range_value::type section_type; typedef typename geometry::robust_point_type < Point, @@ -765,17 +767,13 @@ inline void sectionalize(Geometry const& geometry, { concept::check(); - BOOST_STATIC_ASSERT - ( - (static_cast(Sections::value) - == static_cast(boost::mpl::size::value)) - ); + typedef typename boost::range_value::type section_type; // Compiletime check for point type of section boxes // and point type related to robust policy typedef typename geometry::coordinate_type < - typename Sections::box_type + typename section_type::box_type >::type ctype1; typedef typename geometry::coordinate_type < From f74180b0842e42e7972cb796dce6873df39b177c Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Sun, 1 Mar 2015 14:08:16 +0100 Subject: [PATCH 12/24] [buffer] Enhance turn-in-piece for pieces around points. They now make use of monotonic sections, and get special treatment because there are no helper segments, basically the whole procedure is different This can make buffers around points 3 times faster (if the buffered ring contains many points) --- .../buffer/buffered_piece_collection.hpp | 34 +++- .../detail/buffer/turn_in_piece_visitor.hpp | 164 ++++++++++++------ test/algorithms/buffer/multi_point_buffer.cpp | 4 +- 3 files changed, 146 insertions(+), 56 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp b/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp index ea045a267..a501e3f19 100644 --- a/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp @@ -168,6 +168,9 @@ struct buffered_piece_collection struct piece { + typedef robust_ring_type piece_robust_ring_type; + typedef geometry::section section_type; + strategy::buffer::piece_type type; int index; @@ -191,6 +194,10 @@ struct buffered_piece_collection bool is_monotonic_increasing[2]; // 0=x, 1=y bool is_monotonic_decreasing[2]; // 0=x, 1=y + // Monotonic sections of pieces around points + std::vector sections; + + // Robust representations // 3: complete ring robust_ring_type robust_ring; @@ -199,8 +206,6 @@ struct buffered_piece_collection robust_box_type robust_offsetted_envelope; std::vector robust_turns; // Used only in insert_rescaled_piece_turns - we might use a map instead - - typedef robust_ring_type piece_robust_ring_type; }; struct robust_original @@ -647,6 +652,29 @@ struct buffered_piece_collection } } + inline void prepare_buffered_point_piece(piece& pc) + { + // create monotonic sections in y-dimension + typedef boost::mpl::vector_c dimensions; + geometry::sectionalize(pc.robust_ring, + detail::no_rescale_policy(), pc.sections); + + // TODO (next phase) determine min/max radius + } + + inline void prepare_buffered_point_pieces() + { + for (typename piece_vector_type::iterator it = boost::begin(m_pieces); + it != boost::end(m_pieces); + ++it) + { + if (it->type == geometry::strategy::buffer::buffered_point) + { + prepare_buffered_point_piece(*it); + } + } + } + inline void get_turns() { for(typename boost::range_iterator::type it @@ -681,6 +709,8 @@ struct buffered_piece_collection determine_properties(); + prepare_buffered_point_pieces(); + { // Check if it is inside any of the pieces turn_in_piece_visitor diff --git a/include/boost/geometry/algorithms/detail/buffer/turn_in_piece_visitor.hpp b/include/boost/geometry/algorithms/detail/buffer/turn_in_piece_visitor.hpp index 7eaca10e4..a7731549d 100644 --- a/include/boost/geometry/algorithms/detail/buffer/turn_in_piece_visitor.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/turn_in_piece_visitor.hpp @@ -90,74 +90,134 @@ enum analyse_result analyse_near_offsetted }; -class analyse_turn_wrt_piece +template +inline bool in_box(Point const& previous, + Point const& current, Point const& point) { - template - static inline bool in_box(Point const& previous, - Point const& current, Point const& point) - { - // Get its box (TODO: this can be prepared-on-demand later) - typedef geometry::model::box box_type; - box_type box; - geometry::assign_inverse(box); - geometry::expand(box, previous); - geometry::expand(box, current); + // Get its box (TODO: this can be prepared-on-demand later) + typedef geometry::model::box box_type; + box_type box; + geometry::assign_inverse(box); + geometry::expand(box, previous); + geometry::expand(box, current); - return geometry::covered_by(point, box); + return geometry::covered_by(point, box); +} + +template +inline analyse_result check_segment(Point const& previous, + Point const& current, Turn const& turn, + bool from_monotonic) +{ + typedef typename strategy::side::services::default_strategy + < + typename cs_tag::type + >::type side_strategy; + typedef typename geometry::coordinate_type::type coordinate_type; + + coordinate_type const twice_area + = side_strategy::template side_value + < + coordinate_type, + coordinate_type + >(previous, current, turn.robust_point); + + if (twice_area == 0) + { + // Collinear, only on segment if it is covered by its bbox + if (in_box(previous, current, turn.robust_point)) + { + return analyse_on_offsetted; + } + } + else if (twice_area < 0) + { + // It is in the triangle right-of the segment where the + // segment is the hypothenusa. Check if it is close + // (within rounding-area) + if (twice_area * twice_area < geometry::comparable_distance(previous, current) + && in_box(previous, current, turn.robust_point)) + { + return analyse_near_offsetted; + } + else if (from_monotonic) + { + return analyse_within; + } + } + else if (twice_area > 0 && from_monotonic) + { + // Left of segment + return analyse_disjoint; } - template - static inline analyse_result check_segment(Point const& previous, - Point const& current, Turn const& turn, - bool from_monotonic) + // Not monotonic, on left or right side: continue analysing + return analyse_continue; +} + + +class analyse_turn_wrt_point_piece +{ +public : + template + static inline analyse_result apply(Turn const& turn, Piece const& piece) { - typedef typename strategy::side::services::default_strategy - < - typename cs_tag::type - >::type side_strategy; - typedef typename geometry::coordinate_type::type coordinate_type; + typedef typename Piece::section_type section_type; + typedef typename Turn::robust_point_type point_type; + typedef typename geometry::coordinate_type::type coordinate_type; - coordinate_type const twice_area - = side_strategy::template side_value - < - coordinate_type, - coordinate_type - >(previous, current, turn.robust_point); + coordinate_type const point_y = geometry::get<1>(turn.robust_point); - if (twice_area == 0) + typedef strategy::within::winding strategy_type; + + typename strategy_type::state_type state; + strategy_type strategy; + + for (std::size_t s = 0; s < piece.sections.size(); s++) { - // Collinear, only on segment if it is covered by its bbox - if (in_box(previous, current, turn.robust_point)) + section_type const& section = piece.sections[s]; + // If point within vertical range of monotonic section: + if (! section.duplicate + && section.begin_index < section.end_index + && point_y >= geometry::get(section.bounding_box) - 1 + && point_y <= geometry::get(section.bounding_box) + 1) { - return analyse_on_offsetted; + for (int i = section.begin_index + 1; i <= section.end_index; i++) + { + point_type const& previous = piece.robust_ring[i - 1]; + point_type const& current = piece.robust_ring[i]; + + analyse_result code = check_segment(previous, current, turn, false); + if (code != analyse_continue) + { + return code; + } + + // Get the state (to determine it is within), we don't have + // to cover the on-segment case (covered above) + strategy.apply(turn.robust_point, previous, current, state); + } } } - else if (twice_area < 0) + + int const code = strategy.result(state); + if (code == 1) { - // It is in the triangle right-of the segment where the - // segment is the hypothenusa. Check if it is close - // (within rounding-area) - if (twice_area * twice_area < geometry::comparable_distance(previous, current) - && in_box(previous, current, turn.robust_point)) - { - return analyse_near_offsetted; - } - else if (from_monotonic) - { - return analyse_within; - } + return analyse_within; } - else if (twice_area > 0 && from_monotonic) + else if (code == -1) { - // Left of segment return analyse_disjoint; } - // Not monotonic, on left or right side: continue analysing - return analyse_continue; + // Should normally not occur - on-segment is covered + return analyse_unknown; } +}; +class analyse_turn_wrt_piece +{ template static inline analyse_result check_helper_segment(Point const& s1, Point const& s2, Turn const& turn, @@ -389,7 +449,7 @@ public : } } - return analyse_unknown; + return analyse_unknown; } }; @@ -466,8 +526,10 @@ public: } // TODO: mutable_piece to make some on-demand preparations in analyse - analyse_result analyse_code - = analyse_turn_wrt_piece::apply(turn, piece); + analyse_result analyse_code = + piece.type == geometry::strategy::buffer::buffered_point + ? analyse_turn_wrt_point_piece::apply(turn, piece) + : analyse_turn_wrt_piece::apply(turn, piece); Turn& mutable_turn = m_turns[turn.turn_index]; switch(analyse_code) diff --git a/test/algorithms/buffer/multi_point_buffer.cpp b/test/algorithms/buffer/multi_point_buffer.cpp index ff1b185f3..5c4cbd6a7 100644 --- a/test/algorithms/buffer/multi_point_buffer.cpp +++ b/test/algorithms/buffer/multi_point_buffer.cpp @@ -119,14 +119,12 @@ void test_many_points_per_circle() distance_strategy(6051788), side_strategy, point_circle(83585), 115058672785611.219, tolerance); -#if defined(BOOST_GEOMETRY_BUFFER_INCLUDE_SLOW_TESTS) - // Takes about 60 seconds in release mode + // Takes about 20 seconds in release mode test_with_custom_strategies( "mysql_report_2015_02_25_1_250k", mysql_report_2015_02_25_1, join_miter, end_flat, distance_strategy(6051788), side_strategy, point_circle(250000), 115058672880671.531, tolerance); -#endif #if defined(BOOST_GEOMETRY_BUFFER_INCLUDE_FAILING_TESTS) // Takes too long, TODO improve turn_in_piece_visitor From cae3bab2fe5d08315ea8512dfcbb9f4981730a75 Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Fri, 27 Feb 2015 17:03:03 +0100 Subject: [PATCH 13/24] [util][range] Fix back() *(--boost::end(rng)); is not valid for range with pointer as iterators. since the range is supposed to be BidirectionalRange, use rbegin instead. --- include/boost/geometry/util/range.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/boost/geometry/util/range.hpp b/include/boost/geometry/util/range.hpp index 177fca92c..89d981619 100644 --- a/include/boost/geometry/util/range.hpp +++ b/include/boost/geometry/util/range.hpp @@ -138,7 +138,7 @@ back(BidirectionalRange const& rng) { BOOST_RANGE_CONCEPT_ASSERT(( boost::BidirectionalRangeConcept )); BOOST_ASSERT(!boost::empty(rng)); - return *(--boost::end(rng)); + return *(boost::rbegin(rng)); } /*! @@ -151,7 +151,7 @@ back(BidirectionalRange & rng) { BOOST_RANGE_CONCEPT_ASSERT(( boost::BidirectionalRangeConcept )); BOOST_ASSERT(!boost::empty(rng)); - return *(--boost::end(rng)); + return *(boost::rbegin(rng)); } From d9f95e6952555aefe8916e08b3192716e2353d51 Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Mon, 2 Mar 2015 09:37:44 +0100 Subject: [PATCH 14/24] [algorithms][within] Use geometry::range Make use of geometry::range algorithms to get front and back values of Linestring (rather than Boost.Range begin() and front()). --- .../algorithms/detail/within/point_in_geometry.hpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/within/point_in_geometry.hpp b/include/boost/geometry/algorithms/detail/within/point_in_geometry.hpp index 454c0d5d0..e7486f0e4 100644 --- a/include/boost/geometry/algorithms/detail/within/point_in_geometry.hpp +++ b/include/boost/geometry/algorithms/detail/within/point_in_geometry.hpp @@ -36,6 +36,7 @@ #include #include +#include #include namespace boost { namespace geometry { @@ -197,11 +198,11 @@ struct point_in_geometry return -1; // exterior // if the linestring doesn't have a boundary - if ( detail::equals::equals_point_point(*boost::begin(linestring), *(--boost::end(linestring))) ) + if (detail::equals::equals_point_point(range::front(linestring), range::back(linestring))) return 1; // interior // else if the point is equal to the one of the terminal points - else if ( detail::equals::equals_point_point(point, *boost::begin(linestring)) - || detail::equals::equals_point_point(point, *(--boost::end(linestring))) ) + else if (detail::equals::equals_point_point(point, range::front(linestring)) + || detail::equals::equals_point_point(point, range::back(linestring))) return 0; // boundary else return 1; // interior @@ -210,7 +211,7 @@ struct point_in_geometry // throw an exception here? /*else if ( count == 1 ) { - if ( detail::equals::equals_point_point(point, *boost::begin(linestring)) ) + if ( detail::equals::equals_point_point(point, range::front(linestring)) ) return 1; }*/ @@ -336,8 +337,8 @@ struct point_in_geometry if ( boost::size(*it) < 2 ) continue; - point_type const& front = *boost::begin(*it); - point_type const& back = *(--boost::end(*it)); + point_type const& front = range::front(*it); + point_type const& back = range::back(*it); // is closed_ring - no boundary if ( detail::equals::equals_point_point(front, back) ) From 38fc1224c705f16a9e8ada38f9dfa104cb39dcad Mon Sep 17 00:00:00 2001 From: Menelaos Karavelas Date: Mon, 2 Mar 2015 21:11:40 +0200 Subject: [PATCH 15/24] [strategies][cartesian][buffer][end_round] fix internal point count for small input values (less than 4): when the input point count is less than 4, set the internal point count to 4; for values less than 4 the generated ends do not look round; --- .../boost/geometry/strategies/cartesian/buffer_end_round.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/boost/geometry/strategies/cartesian/buffer_end_round.hpp b/include/boost/geometry/strategies/cartesian/buffer_end_round.hpp index 74780d616..1a4c35560 100644 --- a/include/boost/geometry/strategies/cartesian/buffer_end_round.hpp +++ b/include/boost/geometry/strategies/cartesian/buffer_end_round.hpp @@ -95,8 +95,9 @@ public : //! \brief Constructs the strategy //! \param points_per_circle points which would be used for a full circle + //! (if points_per_circle is smaller than 4, it is internally set to 4) explicit inline end_round(std::size_t points_per_circle = 90) - : m_points_per_circle(points_per_circle) + : m_points_per_circle((points_per_circle < 4u) ? 4u : points_per_circle) {} #ifndef DOXYGEN_SHOULD_SKIP_THIS From a6a448f4299541e11223b7fc4670307273595ce1 Mon Sep 17 00:00:00 2001 From: Menelaos Karavelas Date: Tue, 3 Mar 2015 09:50:46 +0200 Subject: [PATCH 16/24] [strategies][cartesian][buffer][join_round] make sure that at least one interior point is generated --- .../strategies/cartesian/buffer_join_round.hpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/include/boost/geometry/strategies/cartesian/buffer_join_round.hpp b/include/boost/geometry/strategies/cartesian/buffer_join_round.hpp index f88b2904b..e7f7fb6a9 100644 --- a/include/boost/geometry/strategies/cartesian/buffer_join_round.hpp +++ b/include/boost/geometry/strategies/cartesian/buffer_join_round.hpp @@ -1,6 +1,11 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2012-2014 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2012-2015 Barend Gehrels, Amsterdam, the Netherlands. + +// This file was modified by Oracle on 2015. +// Modifications copyright (c) 2015, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -9,6 +14,8 @@ #ifndef BOOST_GEOMETRY_STRATEGIES_CARTESIAN_BUFFER_JOIN_ROUND_HPP #define BOOST_GEOMETRY_STRATEGIES_CARTESIAN_BUFFER_JOIN_ROUND_HPP +#include + #include #include #include @@ -83,15 +90,18 @@ private : { angle2 -= two_pi; } + PromotedType const dangle = angle1 - angle2; // Divide the angle into an integer amount of steps to make it // visually correct also for a low number of points / circle + // n is set to be at least 2 so that at least one interior + // point is generated int const n = static_cast ( - m_points_per_circle * (angle1 - angle2) / two_pi + (std::max)(m_points_per_circle * dangle / two_pi, two) ); - PromotedType const diff = (angle1 - angle2) / static_cast(n); + PromotedType const diff = dangle / static_cast(n); PromotedType a = angle1 - diff; for (int i = 0; i < n - 1; i++, a -= diff) { From ac1a78f1cdec20892e9273685ce986819727c40e Mon Sep 17 00:00:00 2001 From: Menelaos Karavelas Date: Tue, 3 Mar 2015 16:04:32 +0200 Subject: [PATCH 17/24] [strategies][cartesian][buffer][join_round] move the max outside the static cast --- .../geometry/strategies/cartesian/buffer_join_round.hpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/include/boost/geometry/strategies/cartesian/buffer_join_round.hpp b/include/boost/geometry/strategies/cartesian/buffer_join_round.hpp index e7f7fb6a9..80c081795 100644 --- a/include/boost/geometry/strategies/cartesian/buffer_join_round.hpp +++ b/include/boost/geometry/strategies/cartesian/buffer_join_round.hpp @@ -96,10 +96,8 @@ private : // visually correct also for a low number of points / circle // n is set to be at least 2 so that at least one interior // point is generated - int const n = static_cast - ( - (std::max)(m_points_per_circle * dangle / two_pi, two) - ); + int const n = (std::max) + (static_cast(m_points_per_circle * dangle / two_pi), 2); PromotedType const diff = dangle / static_cast(n); PromotedType a = angle1 - diff; From 90bb9a8a4f2cdf9d830edfac93083e7ef2306006 Mon Sep 17 00:00:00 2001 From: Menelaos Karavelas Date: Wed, 4 Mar 2015 12:09:56 +0200 Subject: [PATCH 18/24] [strategies][cartesian][buffer][join_round] revert change in strategy --- .../strategies/cartesian/buffer_join_round.hpp | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/include/boost/geometry/strategies/cartesian/buffer_join_round.hpp b/include/boost/geometry/strategies/cartesian/buffer_join_round.hpp index 80c081795..838e101c9 100644 --- a/include/boost/geometry/strategies/cartesian/buffer_join_round.hpp +++ b/include/boost/geometry/strategies/cartesian/buffer_join_round.hpp @@ -2,11 +2,6 @@ // Copyright (c) 2012-2015 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2015. -// Modifications copyright (c) 2015, Oracle and/or its affiliates. - -// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle - // 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) @@ -94,10 +89,7 @@ private : // Divide the angle into an integer amount of steps to make it // visually correct also for a low number of points / circle - // n is set to be at least 2 so that at least one interior - // point is generated - int const n = (std::max) - (static_cast(m_points_per_circle * dangle / two_pi), 2); + int const n = static_cast(m_points_per_circle * dangle / two_pi); PromotedType const diff = dangle / static_cast(n); PromotedType a = angle1 - diff; From ed2f52a16d4e783a2db3a01022f0e0c255acc777 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 4 Mar 2015 11:40:44 +0100 Subject: [PATCH 19/24] [buffer] Fix end caps with odd number of points, they were not closed Including unit test. Issue was found by Oracle/MySQL testing --- .../strategies/cartesian/buffer_end_round.hpp | 9 ++++- test/algorithms/buffer/linestring_buffer.cpp | 38 +++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/include/boost/geometry/strategies/cartesian/buffer_end_round.hpp b/include/boost/geometry/strategies/cartesian/buffer_end_round.hpp index 1a4c35560..a233f1c4b 100644 --- a/include/boost/geometry/strategies/cartesian/buffer_end_round.hpp +++ b/include/boost/geometry/strategies/cartesian/buffer_end_round.hpp @@ -107,7 +107,7 @@ public : inline void apply(Point const& penultimate_point, Point const& perp_left_point, Point const& ultimate_point, - Point const& , + Point const& perp_right_point, buffer_side_selector side, DistanceStrategy const& distance, RangeOut& range_out) const @@ -143,6 +143,13 @@ public : 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); } + + if (m_points_per_circle % 2 == 1) + { + // For a half circle, if the number of points is not even, + // we should insert the end point too, to generate a full cap + range_out.push_back(perp_right_point); + } } template diff --git a/test/algorithms/buffer/linestring_buffer.cpp b/test/algorithms/buffer/linestring_buffer.cpp index 01b066415..63f6d8567 100644 --- a/test/algorithms/buffer/linestring_buffer.cpp +++ b/test/algorithms/buffer/linestring_buffer.cpp @@ -59,6 +59,10 @@ static std::string const crossing = "LINESTRING(0 0,10 10,10 0,0 10)"; // Simplified cases from multi_linestring tesets: static std::string const mikado1 = "LINESTRING(11.406143344709896325639419956133 0.75426621160409546007485914742574,12 1,11.403846153846153299582510953769 0.75)"; +static std::string const mysql_report_2015_03_02a = "LINESTRING(0 0,0 5,5 5,5 0,0 0)"; // closed +static std::string const mysql_report_2015_03_02b = "LINESTRING(0 1,0 5,5 5,5 0,1 0)"; // not closed, 1 difference +static std::string const mysql_report_2015_03_02c = "LINESTRING(0 2,0 5,5 5,5 0,2 0)"; // not closed, 2 difference + template void test_all() @@ -166,6 +170,40 @@ void test_all() test_one("degenerate4", degenerate4, join_round, end_round, 36.7410, 3.0); test_one("degenerate4", degenerate4, join_round, end_flat, 8.4853, 3.0); + { + // These tests do test behaviour in end_round strategy: + // -> it should generate closed pieces, also for an odd number of points. + // It also tests behaviour in join_round strategy: + // -> it should generate e.g. 4 points for a full circle, + // so a quarter circle does not get points in between + using bg::strategy::buffer::join_round; + using bg::strategy::buffer::end_round; + + double const d10 = 1.0; + + test_one("mysql_report_2015_03_02a_3", mysql_report_2015_03_02a, join_round(3), end_round(3), 38.000, d10); + test_one("mysql_report_2015_03_02a_4", mysql_report_2015_03_02a, join_round(4), end_round(4), 38.000, d10); + test_one("mysql_report_2015_03_02a_5", mysql_report_2015_03_02a, join_round(5), end_round(5), 38.169, d10); + test_one("mysql_report_2015_03_02a_6", mysql_report_2015_03_02a, join_round(6), end_round(6), 38.196, d10); + test_one("mysql_report_2015_03_02a_7", mysql_report_2015_03_02a, join_round(7), end_round(7), 38.230, d10); + test_one("mysql_report_2015_03_02b_3", mysql_report_2015_03_02b, join_round(3), end_round(3), 36.500, d10); + test_one("mysql_report_2015_03_02b_4", mysql_report_2015_03_02b, join_round(4), end_round(4), 36.500, d10); + test_one("mysql_report_2015_03_02b_5", mysql_report_2015_03_02b, join_round(5), end_round(5), 36.724, d10); + test_one("mysql_report_2015_03_02b_6", mysql_report_2015_03_02b, join_round(6), end_round(6), 36.781, d10); + test_one("mysql_report_2015_03_02b_7", mysql_report_2015_03_02b, join_round(7), end_round(7), 36.884, d10); + test_one("mysql_report_2015_03_02c_3", mysql_report_2015_03_02c, join_round(2), end_round(3), 32.500, d10); + test_one("mysql_report_2015_03_02c_4", mysql_report_2015_03_02c, join_round(4), end_round(4), 32.500, d10); + test_one("mysql_report_2015_03_02c_5", mysql_report_2015_03_02c, join_round(5), end_round(5), 32.990, d10); + test_one("mysql_report_2015_03_02c_6", mysql_report_2015_03_02c, join_round(6), end_round(6), 33.098, d10); + test_one("mysql_report_2015_03_02c_7", mysql_report_2015_03_02c, join_round(7), end_round(7), 33.279, d10); + + // Testing the asymmetric end caps with odd number of points + double const d15 = 1.5; + test_one("mysql_report_2015_03_02c_asym1", mysql_report_2015_03_02c, join_round(7), end_round(7), 39.093, d10, d15); + test_one("mysql_report_2015_03_02c_asym2", mysql_report_2015_03_02c, join_round(7), end_round(7), 44.718, d15, d10); + } + + { double tolerance = 1.0e-10; From e80ec0babf13e2e27cf16e17cddd7af2f2571a35 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 4 Mar 2015 12:26:49 +0100 Subject: [PATCH 20/24] [buffer][doc] Make miter limit more clear in documentation --- .../boost/geometry/strategies/cartesian/buffer_join_miter.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/boost/geometry/strategies/cartesian/buffer_join_miter.hpp b/include/boost/geometry/strategies/cartesian/buffer_join_miter.hpp index 8fcf3b996..99ec80527 100644 --- a/include/boost/geometry/strategies/cartesian/buffer_join_miter.hpp +++ b/include/boost/geometry/strategies/cartesian/buffer_join_miter.hpp @@ -35,6 +35,8 @@ namespace strategy { namespace buffer their length. The miter is not changed to a bevel form (as done in some other software), it is just adapted to the specified miter_limit but keeps its miter form. + If the buffer distance is 5.0, and the miter limit is 2.0, generated points + will be located at a distance of at most 10.0 (2*5) units. This strategy is only applicable for Cartesian coordinate systems. \qbk{ From 2e9eedd4ea28bb4ab5e5a139aa387487ff91c806 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 4 Mar 2015 12:31:59 +0100 Subject: [PATCH 21/24] [buffer][test] Add cases for various miter limits --- test/algorithms/buffer/polygon_buffer.cpp | 57 +++++++++++++++++++++-- 1 file changed, 52 insertions(+), 5 deletions(-) diff --git a/test/algorithms/buffer/polygon_buffer.cpp b/test/algorithms/buffer/polygon_buffer.cpp index a683e88f6..cc108a489 100644 --- a/test/algorithms/buffer/polygon_buffer.cpp +++ b/test/algorithms/buffer/polygon_buffer.cpp @@ -532,18 +532,65 @@ void test_all() { - bg::strategy::buffer::join_round join_round12(12); + using bg::strategy::buffer::join_round; + using bg::strategy::buffer::join_miter; buffer_custom_side_strategy side_strategy; bg::strategy::buffer::point_circle point_strategy; - bg::strategy::buffer::distance_symmetric + typedef bg::strategy::buffer::distance_symmetric < typename bg::coordinate_type

::type - > distance_strategy(1.0); + > distance; - test_with_custom_strategies("sharp_triangle", + test_with_custom_strategies("sharp_triangle_j12", sharp_triangle, - join_round12, end_flat, distance_strategy, side_strategy, point_strategy, + join_round(12), end_flat, distance(1.0), side_strategy, point_strategy, 31.0721); + // Test very various number of points (min is 3) + test_with_custom_strategies("sharp_triangle_j2", + sharp_triangle, + join_round(2), end_flat, distance(1.0), side_strategy, point_strategy, + 29.2865); + test_with_custom_strategies("sharp_triangle_j5", + sharp_triangle, + join_round(5), end_flat, distance(1.0), side_strategy, point_strategy, + 30.2291); + +#if defined(BOOST_GEOMETRY_BUFFER_INCLUDE_FAILING_TESTS) + test_with_custom_strategies("sharp_triangle_j36", + sharp_triangle, + join_round(36), end_flat, distance(1.0), side_strategy, point_strategy, + 29.9999); // TBD + test_with_custom_strategies("sharp_triangle_j360", + sharp_triangle, + join_round(360), end_flat, distance(1.0), side_strategy, point_strategy, + 29.9999); // TBD + test_with_custom_strategies("sharp_triangle_j3600", + sharp_triangle, + join_round(3600), end_flat, distance(1.0), side_strategy, point_strategy, + 29.9999); // TBD +#endif + + // Test with various miter limits + test_with_custom_strategies("sharp_triangle_m2", + sharp_triangle, + join_miter(2), end_flat, distance(4.0), side_strategy, point_strategy, + 166.5374); + test_with_custom_strategies("sharp_triangle_m3", + sharp_triangle, + join_miter(3), end_flat, distance(4.0), side_strategy, point_strategy, + 184.0015); + test_with_custom_strategies("sharp_triangle_m4", + sharp_triangle, + join_miter(4), end_flat, distance(4.0), side_strategy, point_strategy, + 201.4656); + test_with_custom_strategies("sharp_triangle_m5", + sharp_triangle, + join_miter(5), end_flat, distance(4.0), side_strategy, point_strategy, + 218.9296); + test_with_custom_strategies("sharp_triangle_m25", + sharp_triangle, + join_miter(25), end_flat, distance(4.0), side_strategy, point_strategy, + 286.4892); } } From fbd880a0c78f459f4d27be6765de3a8534802eb6 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 4 Mar 2015 12:33:03 +0100 Subject: [PATCH 22/24] [test][buffer] Only test symmetric case in release mode --- test/algorithms/buffer/test_buffer.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/algorithms/buffer/test_buffer.hpp b/test/algorithms/buffer/test_buffer.hpp index b268eb40d..dc0177d15 100644 --- a/test/algorithms/buffer/test_buffer.hpp +++ b/test/algorithms/buffer/test_buffer.hpp @@ -688,7 +688,10 @@ void test_one(std::string const& caseid, std::string const& wkt, check_self_intersections, expected_area, tolerance, NULL); +#if !defined(BOOST_GEOMETRY_COMPILER_MODE_DEBUG) && defined(BOOST_GEOMETRY_COMPILER_MODE_RELEASE) + // Also test symmetric distance strategy if right-distance is not specified + // (only in release mode) if (bg::math::equals(distance_right, -999)) { bg::strategy::buffer::distance_symmetric @@ -704,6 +707,7 @@ void test_one(std::string const& caseid, std::string const& wkt, tolerance, NULL); } +#endif } // Version (currently for the Aimes test) counting self-ip's instead of checking From f411490532197828cb7a5adab57e626e73a0a0b8 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 4 Mar 2015 12:33:21 +0100 Subject: [PATCH 23/24] [doc] add buffer fix to release notes --- doc/release_notes.qbk | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/release_notes.qbk b/doc/release_notes.qbk index 724d125eb..6b732cefd 100644 --- a/doc/release_notes.qbk +++ b/doc/release_notes.qbk @@ -61,6 +61,7 @@ * Bug in centroid(), non-deterministic result if calculated for e.g. a Polygon with zero-area. * Bug in buffers for joins with a limited number of points * Bug in buffers for joins with large buffer distances +* Bug in buffers for round ends with an odd number of points * Bug in buffers for flat ends with large buffer distances * Bug in buffers for interior rings with large negative buffer distances * Bug in closing iterator not working properly when the input range is empty From 6cac10bec6f058b4604966b376b2f5e58b6176a9 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 4 Mar 2015 13:15:42 +0100 Subject: [PATCH 24/24] [test][buffer] fix last tests which were done with custom side. Which explains the "failing" test which were actually succeeding. Naming is now more clear --- test/algorithms/buffer/polygon_buffer.cpp | 34 +++++++++++------------ 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/test/algorithms/buffer/polygon_buffer.cpp b/test/algorithms/buffer/polygon_buffer.cpp index cc108a489..d0eeedb09 100644 --- a/test/algorithms/buffer/polygon_buffer.cpp +++ b/test/algorithms/buffer/polygon_buffer.cpp @@ -534,7 +534,7 @@ void test_all() { using bg::strategy::buffer::join_round; using bg::strategy::buffer::join_miter; - buffer_custom_side_strategy side_strategy; + bg::strategy::buffer::side_straight side_strategy; bg::strategy::buffer::point_circle point_strategy; typedef bg::strategy::buffer::distance_symmetric < @@ -544,53 +544,53 @@ void test_all() test_with_custom_strategies("sharp_triangle_j12", sharp_triangle, join_round(12), end_flat, distance(1.0), side_strategy, point_strategy, - 31.0721); + 29.0980); // Test very various number of points (min is 3) test_with_custom_strategies("sharp_triangle_j2", sharp_triangle, join_round(2), end_flat, distance(1.0), side_strategy, point_strategy, - 29.2865); + 27.2399); test_with_custom_strategies("sharp_triangle_j5", sharp_triangle, join_round(5), end_flat, distance(1.0), side_strategy, point_strategy, - 30.2291); + 28.1091); -#if defined(BOOST_GEOMETRY_BUFFER_INCLUDE_FAILING_TESTS) test_with_custom_strategies("sharp_triangle_j36", sharp_triangle, join_round(36), end_flat, distance(1.0), side_strategy, point_strategy, - 29.9999); // TBD + 29.2482); test_with_custom_strategies("sharp_triangle_j360", sharp_triangle, join_round(360), end_flat, distance(1.0), side_strategy, point_strategy, - 29.9999); // TBD - test_with_custom_strategies("sharp_triangle_j3600", - sharp_triangle, - join_round(3600), end_flat, distance(1.0), side_strategy, point_strategy, - 29.9999); // TBD -#endif + 29.2659); // Test with various miter limits test_with_custom_strategies("sharp_triangle_m2", sharp_triangle, join_miter(2), end_flat, distance(4.0), side_strategy, point_strategy, - 166.5374); + 148.500); test_with_custom_strategies("sharp_triangle_m3", sharp_triangle, join_miter(3), end_flat, distance(4.0), side_strategy, point_strategy, - 184.0015); + 164.376); test_with_custom_strategies("sharp_triangle_m4", sharp_triangle, join_miter(4), end_flat, distance(4.0), side_strategy, point_strategy, - 201.4656); + 180.2529); test_with_custom_strategies("sharp_triangle_m5", sharp_triangle, join_miter(5), end_flat, distance(4.0), side_strategy, point_strategy, - 218.9296); + 196.1293); test_with_custom_strategies("sharp_triangle_m25", sharp_triangle, join_miter(25), end_flat, distance(4.0), side_strategy, point_strategy, - 286.4892); + 244.7471); + + buffer_custom_side_strategy custom_side_strategy; + test_with_custom_strategies("sharp_triangle_custom_side", + sharp_triangle, + join_round(49), end_flat, distance(1.0), custom_side_strategy, point_strategy, + 31.1087); } }