From f80cacc4cbf8a5e1bd044d6f58cd4401348f23c7 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Tue, 3 May 2022 14:16:45 +0200 Subject: [PATCH] [relate] Refactor relate utilities and implementation. Implement boundary_checker and follow helpers for geometry collection. They are commented-out for now because they are not used anywhere. The code is there in case they were needed in the future. --- .../algorithms/detail/overlay/follow.hpp | 7 +- .../detail/overlay/linear_linear.hpp | 9 +- .../algorithms/detail/relate/areal_areal.hpp | 35 +- .../detail/relate/boundary_checker.hpp | 235 +++++++--- .../detail/relate/follow_helpers.hpp | 260 +++++++---- .../detail/relate/implementation.hpp | 23 +- .../algorithms/detail/relate/linear_areal.hpp | 417 ++++++++---------- .../detail/relate/linear_linear.hpp | 130 +++--- .../algorithms/detail/relate/turns.hpp | 32 +- .../set_operations/check_turn_less.hpp | 3 +- .../test_get_turns_ll_invariance.hpp | 5 +- 11 files changed, 643 insertions(+), 513 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/follow.hpp b/include/boost/geometry/algorithms/detail/overlay/follow.hpp index afcd2bd82..d0e45c710 100644 --- a/include/boost/geometry/algorithms/detail/overlay/follow.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/follow.hpp @@ -3,8 +3,8 @@ // Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. // Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland. -// This file was modified by Oracle on 2014-2020. -// Modifications copyright (c) 2014-2020 Oracle and/or its affiliates. +// This file was modified by Oracle on 2014-2022. +// Modifications copyright (c) 2014-2022 Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -438,8 +438,7 @@ public : // for different ring id: c, i, u, x typedef relate::turns::less < - 0, relate::turns::less_op_linear_areal_single<0>, - typename Strategy::cs_tag + 0, relate::turns::less_op_linear_areal_single<0>, Strategy > turn_less; std::sort(boost::begin(turns), boost::end(turns), turn_less()); diff --git a/include/boost/geometry/algorithms/detail/overlay/linear_linear.hpp b/include/boost/geometry/algorithms/detail/overlay/linear_linear.hpp index d68762eca..7807e9a8c 100644 --- a/include/boost/geometry/algorithms/detail/overlay/linear_linear.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/linear_linear.hpp @@ -1,6 +1,6 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2014-2020, Oracle and/or its affiliates. +// Copyright (c) 2014-2022, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -160,14 +160,10 @@ protected: detail::get_turns::no_interrupt_policy interrupt_policy; - using point_type = typename geometry::point_type::type; - using mutable_point_type = typename helper_geometry::type; - geometry::detail::relate::turns::get_turns < LinearGeometry1, LinearGeometry2, - mutable_point_type, detail::get_turns::get_turn_info_type < LinearGeometry1, @@ -235,13 +231,10 @@ public: OutputIterator oit, Strategy const& strategy) { - using point_type = typename geometry::point_type::type; - using mutable_point_type = typename helper_geometry::type; typedef typename detail::relate::turns::get_turns < Linear1, Linear2, - mutable_point_type, detail::get_turns::get_turn_info_type < Linear1, diff --git a/include/boost/geometry/algorithms/detail/relate/areal_areal.hpp b/include/boost/geometry/algorithms/detail/relate/areal_areal.hpp index b14c45347..8ddc4e476 100644 --- a/include/boost/geometry/algorithms/detail/relate/areal_areal.hpp +++ b/include/boost/geometry/algorithms/detail/relate/areal_areal.hpp @@ -208,9 +208,6 @@ struct areal_areal static const bool interruption_enabled = true; - typedef typename geometry::point_type::type point1_type; - typedef typename geometry::point_type::type point2_type; - template static inline void apply(Geometry1 const& geometry1, Geometry2 const& geometry2, Result & result, @@ -225,23 +222,18 @@ struct areal_areal return; // get and analyse turns - using point_type = typename geometry::point_type::type; - using mutable_point_type = typename helper_geometry::type; - - typedef typename turns::get_turns + using turn_type = typename turns::get_turns < - Geometry1, Geometry2, mutable_point_type - >::template turn_info_type::type turn_type; + Geometry1, Geometry2 + >::template turn_info_type::type; std::vector turns; interrupt_policy_areal_areal interrupt_policy(geometry1, geometry2, result); - turns::get_turns::apply(turns, geometry1, geometry2, interrupt_policy, strategy); + turns::get_turns::apply(turns, geometry1, geometry2, interrupt_policy, strategy); if ( BOOST_GEOMETRY_CONDITION(result.interrupt) ) return; - typedef typename Strategy::cs_tag cs_tag; - no_turns_aa_pred pred1(geometry2, result, strategy); for_each_disjoint_geometry_if<0, Geometry1>::apply(turns.begin(), turns.end(), geometry1, pred1); @@ -264,8 +256,8 @@ struct areal_areal || may_update(result) ) { // sort turns - typedef turns::less<0, turns::less_op_areal_areal<0>, cs_tag> less; - std::sort(turns.begin(), turns.end(), less()); + using less_t = turns::less<0, turns::less_op_areal_areal<0>, Strategy>; + std::sort(turns.begin(), turns.end(), less_t()); /*if ( may_update(result) || may_update(result) @@ -304,8 +296,8 @@ struct areal_areal || may_update(result) ) { // sort turns - typedef turns::less<1, turns::less_op_areal_areal<1>, cs_tag> less; - std::sort(turns.begin(), turns.end(), less()); + using less_t = turns::less<1, turns::less_op_areal_areal<1>, Strategy>; + std::sort(turns.begin(), turns.end(), less_t()); /*if ( may_update(result) || may_update(result) @@ -357,9 +349,7 @@ struct areal_areal template inline bool apply(Range const& turns) { - typedef typename boost::range_iterator::type iterator; - - for (iterator it = boost::begin(turns) ; it != boost::end(turns) ; ++it) + for (auto it = boost::begin(turns) ; it != boost::end(turns) ; ++it) { per_turn<0>(*it); per_turn<1>(*it); @@ -657,10 +647,9 @@ struct areal_areal return; } - typename detail::sub_range_return_type::type - range_ref = detail::sub_range(geometry, seg_id); + auto const& sub_range = detail::sub_range(geometry, seg_id); - if ( boost::empty(range_ref) ) + if ( boost::empty(sub_range) ) { // TODO: throw an exception? return; // ignore @@ -673,7 +662,7 @@ struct areal_areal // TODO: optimize! e.g. use spatial index // O(N) - running it in a loop gives O(NM) using detail::within::point_in_geometry; - int const pig = point_in_geometry(range::front(range_ref), + int const pig = point_in_geometry(range::front(sub_range), other_geometry, m_point_in_areal_strategy); diff --git a/include/boost/geometry/algorithms/detail/relate/boundary_checker.hpp b/include/boost/geometry/algorithms/detail/relate/boundary_checker.hpp index 591d82714..50eb0b7dd 100644 --- a/include/boost/geometry/algorithms/detail/relate/boundary_checker.hpp +++ b/include/boost/geometry/algorithms/detail/relate/boundary_checker.hpp @@ -1,6 +1,6 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2014-2020 Oracle and/or its affiliates. +// Copyright (c) 2014-2022 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -15,6 +15,7 @@ #include #include +//#include #include #include @@ -28,9 +29,9 @@ namespace boost { namespace geometry { #ifndef DOXYGEN_NO_DETAIL -namespace detail { namespace relate { +namespace detail { namespace relate +{ -enum boundary_query { boundary_front, boundary_back, boundary_any }; template < @@ -43,31 +44,26 @@ class boundary_checker {}; template class boundary_checker { - using mutable_point_type - = typename helper_geometry::type>::type; - public: boundary_checker(Geometry const& g, Strategy const& s) - : m_has_boundary( boost::size(g) >= 2 - && ! detail::equals::equals_point_point(range::front(g), - range::back(g), - s) ) + : m_has_boundary( + boost::size(g) >= 2 + && ! detail::equals::equals_point_point(range::front(g), range::back(g), s)) #ifdef BOOST_GEOMETRY_DEBUG_RELATE_BOUNDARY_CHECKER , m_geometry(g) #endif , m_strategy(s) {} - template + template bool is_endpoint_boundary(Point const& pt) const { boost::ignore_unused(pt); #ifdef BOOST_GEOMETRY_DEBUG_RELATE_BOUNDARY_CHECKER // may give false positives for INT - BOOST_GEOMETRY_ASSERT( (BoundaryQuery == boundary_front || BoundaryQuery == boundary_any) - && detail::equals::equals_point_point(pt, range::front(m_geometry), m_strategy) - || (BoundaryQuery == boundary_back || BoundaryQuery == boundary_any) - && detail::equals::equals_point_point(pt, range::back(m_geometry), m_strategy) ); + BOOST_GEOMETRY_ASSERT( + detail::equals::equals_point_point(pt, range::front(m_geometry), m_strategy) + || detail::equals::equals_point_point(pt, range::back(m_geometry), m_strategy)); #endif return m_has_boundary; } @@ -85,6 +81,67 @@ private: Strategy const& m_strategy; }; + +template +inline void copy_pts_boundary_points(Point const& front_pt, Point const& back_pt, + Strategy const& strategy, Out & boundary_points) +{ + using mutable_point_type = typename Out::value_type; + // linear ring or point - no boundary + if (! equals::equals_point_point(front_pt, back_pt, strategy)) + { + // do not add points containing NaN coordinates + // because they cannot be reasonably compared, e.g. with MSVC + // an assertion failure is reported in std::equal_range() + if (! geometry::has_nan_coordinate(front_pt)) + { + mutable_point_type pt; + geometry::convert(front_pt, pt); + boundary_points.push_back(front_pt); + } + if (! geometry::has_nan_coordinate(back_pt)) + { + mutable_point_type pt; + geometry::convert(back_pt, pt); + boundary_points.push_back(back_pt); + } + } +} + +template +inline void copy_seg_boundary_points(Segment const& seg, Strategy const& strategy, + Out & boundary_points) +{ + typename Out::value_type front_pt, back_pt; + assign_point_from_index<0>(seg, front_pt); + assign_point_from_index<1>(seg, back_pt); + copy_pts_boundary_points(front_pt, back_pt, strategy, boundary_points); +} + +template +inline void copy_ls_boundary_points(Linestring const& ls, Strategy const& strategy, + Out & boundary_points) +{ + // empty or point - no boundary + if (boost::size(ls) >= 2) + { + auto const& front_pt = range::front(ls); + auto const& back_pt = range::back(ls); + copy_pts_boundary_points(front_pt, back_pt, strategy, boundary_points); + } +} + +template +inline void copy_mls_boundary_points(MultiLinestring const& mls, Strategy const& strategy, + Out & boundary_points) +{ + for (auto it = boost::begin(mls); it != boost::end(mls); ++it) + { + copy_ls_boundary_points(*it, strategy, boundary_points); + } +} + + template class boundary_checker { @@ -98,72 +155,36 @@ public: // First call O(NlogN) // Each next call O(logN) - template + template bool is_endpoint_boundary(Point const& pt) const { using less_type = geometry::less; auto const multi_count = boost::size(m_geometry); - if ( multi_count < 1 ) + if (multi_count < 1) { return false; } - if ( ! m_is_filled ) + if (! m_is_filled) { //boundary_points.clear(); m_boundary_points.reserve(multi_count * 2); - for (auto it = boost::begin(m_geometry); it != boost::end(m_geometry); ++it) - { - auto const& ls = *it; + copy_mls_boundary_points(m_geometry, m_strategy, m_boundary_points); - // empty or only one point - no boundary - if (boost::size(ls) < 2) - { - continue; - } - - auto const& front_pt = range::front(ls); - auto const& back_pt = range::back(ls); - - // linear ring or point - no boundary - if (! equals::equals_point_point(front_pt, back_pt, m_strategy)) - { - // do not add points containing NaN coordinates - // because they cannot be reasonably compared, e.g. with MSVC - // an assertion failure is reported in std::equal_range() - if (! geometry::has_nan_coordinate(front_pt)) - { - mutable_point_type pt; - geometry::convert(front_pt, pt); - m_boundary_points.push_back(pt); - } - if (! geometry::has_nan_coordinate(back_pt)) - { - mutable_point_type pt; - geometry::convert(back_pt, pt); - m_boundary_points.push_back(pt); - } - } - } - - std::sort(m_boundary_points.begin(), - m_boundary_points.end(), - less_type()); + std::sort(m_boundary_points.begin(), m_boundary_points.end(), less_type()); m_is_filled = true; } - auto const equal_points_count - = boost::size( - std::equal_range(m_boundary_points.begin(), - m_boundary_points.end(), - pt, - less_type()) - ); + auto const equal_range = std::equal_range(m_boundary_points.begin(), + m_boundary_points.end(), + pt, + less_type()); + std::size_t const equal_points_count = boost::size(equal_range); return equal_points_count % 2 != 0;// && equal_points_count > 0; // the number is odd and > 0 } @@ -181,6 +202,98 @@ private: Strategy const& m_strategy; }; +// NOTE: In case it was needed in the future. Commented-out for now. +/* +template +class boundary_checker +{ + using point_type = typename point_type::type; + using mutable_point_type = typename helper_geometry::type; + +public: + boundary_checker(Geometry const& g, Strategy const& s) + : m_is_filled(false), m_geometry(g), m_strategy(s) + {} + + // First call O(NlogN) + // Each next call O(logN) + template + bool is_endpoint_boundary(Point const& pt) const + { + using less_t = geometry::less; + + auto multi_count = boost::size(m_geometry); + + if (multi_count < 1) + { + return false; + } + + if (! m_is_filled) + { + //boundary_points.clear(); + m_boundary_points.reserve(multi_count * 2); + + detail::visit_breadth_first([&](auto const& g) + { + add_boundary_points(g); + return true; + }, m_geometry); + + std::sort(m_boundary_points.begin(), + m_boundary_points.end(), + less_t()); + + m_is_filled = true; + } + + auto const equal_range = std::equal_range(m_boundary_points.begin(), + m_boundary_points.end(), + pt, + less_t()); + + std::size_t const equal_points_count = boost::size(equal_range); + return equal_points_count % 2 != 0;// && equal_points_count > 0; // the number is odd and > 0 + } + + Strategy const& strategy() const + { + return m_strategy; + } + +private: + template ::value, int> = 0> + void add_boundary_points(Geom const& geom) const + { + copy_mls_boundary_points(geom, m_strategy, m_boundary_points); + } + + template ::value, int> = 0> + void add_boundary_points(Geom const& geom) const + { + copy_ls_boundary_points(geom, m_strategy, m_boundary_points); + } + + template ::value, int> = 0> + void add_boundary_points(Geom const& geom) const + { + copy_seg_boundary_points(geom, m_strategy, m_boundary_points); + } + + template ::value, int> = 0> + void add_boundary_points(Geom const& ) const + {} + + mutable bool m_is_filled; + // TODO: store references/pointers instead of converted points? + mutable std::vector m_boundary_points; + + Geometry const& m_geometry; + Strategy const& m_strategy; +}; +*/ + + }} // namespace detail::relate #endif // DOXYGEN_NO_DETAIL diff --git a/include/boost/geometry/algorithms/detail/relate/follow_helpers.hpp b/include/boost/geometry/algorithms/detail/relate/follow_helpers.hpp index a0162d18d..47525cd84 100644 --- a/include/boost/geometry/algorithms/detail/relate/follow_helpers.hpp +++ b/include/boost/geometry/algorithms/detail/relate/follow_helpers.hpp @@ -2,8 +2,8 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2013, 2014, 2018. -// Modifications copyright (c) 2013-2018 Oracle and/or its affiliates. +// This file was modified by Oracle on 2013-2022. +// Modifications copyright (c) 2013-2022 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -52,14 +52,12 @@ template struct for_each_disjoint_geometry_if { template - static inline bool apply(TurnIt first, TurnIt last, - Geometry const& geometry, - Pred & pred) + static void apply(TurnIt first, TurnIt last, Geometry const& geometry, Pred & pred) { - if ( first != last ) - return false; - pred(geometry); - return true; + if (first == last) + { + pred(geometry); + } } }; @@ -67,49 +65,43 @@ template struct for_each_disjoint_geometry_if { template - static inline bool apply(TurnIt first, TurnIt last, - Geometry const& geometry, - Pred & pred) + static void apply(TurnIt first, TurnIt last, Geometry const& geometry, Pred & pred) { - if ( first != last ) - return for_turns(first, last, geometry, pred); + if (first == last) + { + for_empty(geometry, pred); + } else - return for_empty(geometry, pred); + { + for_turns(first, last, geometry, pred); + } } template - static inline bool for_empty(Geometry const& geometry, - Pred & pred) + static void for_empty(Geometry const& geometry, Pred & pred) { - typedef typename boost::range_iterator::type iterator; - // O(N) // check predicate for each contained geometry without generated turn - for ( iterator it = boost::begin(geometry) ; - it != boost::end(geometry) ; ++it ) + for (auto it = boost::begin(geometry); it != boost::end(geometry) ; ++it) { - bool cont = pred(*it); - if ( !cont ) + if (! pred(*it)) + { break; + } } - - return !boost::empty(geometry); } template - static inline bool for_turns(TurnIt first, TurnIt last, - Geometry const& geometry, - Pred & pred) + static void for_turns(TurnIt first, TurnIt last, Geometry const& geometry, Pred & pred) { BOOST_GEOMETRY_ASSERT(first != last); const std::size_t count = boost::size(geometry); - boost::ignore_unused(count); // O(I) // gather info about turns generated for contained geometries std::vector detected_intersections(count, false); - for ( TurnIt it = first ; it != last ; ++it ) + for (TurnIt it = first; it != last; ++it) { signed_size_type multi_index = it->operations[OpId].seg_id.multi_index; BOOST_GEOMETRY_ASSERT(multi_index >= 0); @@ -118,28 +110,167 @@ struct for_each_disjoint_geometry_if detected_intersections[index] = true; } - bool found = false; - // O(N) // check predicate for each contained geometry without generated turn - for ( std::vector::iterator it = detected_intersections.begin() ; - it != detected_intersections.end() ; ++it ) + for (std::size_t index = 0; index < detected_intersections.size(); ++index) { // if there were no intersections for this multi_index - if ( *it == false ) + if (detected_intersections[index] == false) { - found = true; - std::size_t const index = std::size_t(std::distance(detected_intersections.begin(), it)); - bool cont = pred(range::at(geometry, index)); - if ( !cont ) + if (! pred(range::at(geometry, index))) + { break; + } } } - - return found; } }; + +// In case it was needed in the future. Commented-out for now. +/* +// GC is expected to support random access, i.e. random_access_view has to be passed. +// Predicate is called for each segmental single geometry. +// Point-like geometries and boxes are ignored. +template +struct for_each_disjoint_geometry_if +{ + struct detected_data + { + void set_multi_index(std::size_t multi_index) + { + if (multi_index >= multi.size()) + { + multi.resize(multi_index + 1, false); + } + multi[multi_index] = true; + } + + std::vector multi; + bool is_detected = false; + }; + + template + static void apply(TurnIt first, TurnIt last, RandomGC const& random_gc, Pred & pred) + { + return first != last + ? for_turns(first, last, random_gc, pred) + : for_empty(random_gc, pred); + } + + template + static void for_empty(RandomGC const& random_gc, Pred & pred) + { + auto const end = boost::end(random_gc); + for (auto it = boost::begin(random_gc); it != end; ++it) + { + bool ok = true; + traits::iter_visit::apply([&](auto const& g) + { + ok = call_pred(pred, g); + }, it); + if (! ok) + { + break; + } + } + } + + template + static void for_turns(TurnIt first, TurnIt last, RandomGC const& random_gc, Pred & pred) + { + BOOST_GEOMETRY_ASSERT(first != last); + + const std::size_t count = boost::size(random_gc); + + std::vector detected_intersections(count); + for (TurnIt it = first; it != last; ++it) + { + auto const& op = it->operations[OpId]; + auto const& seg_id = op.seg_id; + BOOST_GEOMETRY_ASSERT(op.gc_index >= 0); + detected_intersections[op.gc_index].is_detected = true; + if (seg_id.multi_index >= 0) + { + detected_intersections[op.gc_index].set_multi_index(std::size_t(seg_id.multi_index)); + } + } + + auto const begin = boost::begin(random_gc); + auto const end = boost::end(random_gc); + for (auto it = begin; it != end; ++it) + { + bool ok = true; + traits::iter_visit::apply([&](auto const& g) + { + std::size_t gc_index = it - begin; + // either single or the whole multi was not detected + if (detected_intersections[gc_index].is_detected == false) + { + ok = call_pred(pred, g); + } + else + { + ok = check_multi_elements(pred, g, detected_intersections[gc_index].multi); + } + }, it); + if (! ok) + { + break; + } + } + } + + template ::value && util::is_multi::value, int> = 0> + static bool call_pred(Pred & pred, Geometry const& geometry) + { + for (auto it = boost::begin(geometry); it != boost::end(geometry); ++it) + { + if (! pred(*it)) + { + return false; + } + } + return true; + } + + template ::value && util::is_single::value, int> = 0> + static bool call_pred(Pred & pred, Geometry const& geometry) + { + return pred(geometry); + } + + template ::value, int> = 0> + static bool call_pred(Pred & , Geometry const&) + { + return true; + } + + template ::value && util::is_multi::value, int> = 0> + static bool check_multi_elements(Pred & pred, Geometry const& geometry, std::vector const& detected_multi) + { + std::size_t size = boost::size(geometry); + for (std::size_t multi_index = 0; multi_index < size; ++multi_index) + { + if (multi_index >= detected_multi.size() || detected_multi[multi_index] == false) + { + if (! pred(range::at(geometry, multi_index))) + { + return false; + } + } + } + return true; + } + + template ::value || ! util::is_multi::value, int> = 0> + static bool check_multi_elements(Pred & , Geometry const& , std::vector const& ) + { + return true; + } +}; +*/ + // WARNING! This class stores pointers! // Passing a reference to local variable will result in undefined behavior! template @@ -365,44 +496,17 @@ inline bool turn_on_the_same_ip(Turn const& prev_turn, Turn const& curr_turn, return detail::equals::equals_point_point(prev_turn.point, curr_turn.point, strategy); } -template -inline bool is_endpoint_on_boundary(Point const& pt, - BoundaryChecker & boundary_checker) +template +static inline bool is_ip_on_boundary(IntersectionPoint const& ip, + OperationInfo const& operation_info, + BoundaryChecker const& boundary_checker) { - return boundary_checker.template is_endpoint_boundary(pt); -} - -template -inline bool is_ip_on_boundary(IntersectionPoint const& ip, - OperationInfo const& operation_info, - BoundaryChecker & boundary_checker, - segment_identifier const& seg_id) -{ - boost::ignore_unused(seg_id); - - bool res = false; - - // IP on the last point of the linestring - if ( BOOST_GEOMETRY_CONDITION(BoundaryQuery == boundary_back || BoundaryQuery == boundary_any) - && operation_info.position == overlay::position_back ) - { - // check if this point is a boundary - res = boundary_checker.template is_endpoint_boundary(ip); - } - // IP on the last point of the linestring - else if ( BOOST_GEOMETRY_CONDITION(BoundaryQuery == boundary_front || BoundaryQuery == boundary_any) - && operation_info.position == overlay::position_front ) - { - // check if this point is a boundary - res = boundary_checker.template is_endpoint_boundary(ip); - } - - return res; + // IP on the first or the last point of the linestring + return (operation_info.position == overlay::position_back + || operation_info.position == overlay::position_front) + // check if this point is a boundary + ? boundary_checker.is_endpoint_boundary(ip) + : false; } diff --git a/include/boost/geometry/algorithms/detail/relate/implementation.hpp b/include/boost/geometry/algorithms/detail/relate/implementation.hpp index 190a5e3d6..61e95a1f9 100644 --- a/include/boost/geometry/algorithms/detail/relate/implementation.hpp +++ b/include/boost/geometry/algorithms/detail/relate/implementation.hpp @@ -2,9 +2,8 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2013-2021. -// Modifications copyright (c) 2013-2021 Oracle and/or its affiliates. - +// This file was modified by Oracle on 2013-2022. +// Modifications copyright (c) 2013-2022 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Use, modification and distribution is subject to the Boost Software License, @@ -15,16 +14,15 @@ #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_IMPLEMENTATION_HPP -#include - -#include - -#include -#include -#include -#include -#include #include +#include +#include +#include +#include +#include +#include + +#include #include #include @@ -116,6 +114,7 @@ struct relate : detail::relate::areal_areal {}; + } // namespace dispatch #endif // DOXYGEN_NO_DISPATCH diff --git a/include/boost/geometry/algorithms/detail/relate/linear_areal.hpp b/include/boost/geometry/algorithms/detail/relate/linear_areal.hpp index 293b79de2..4330bf4de 100644 --- a/include/boost/geometry/algorithms/detail/relate/linear_areal.hpp +++ b/include/boost/geometry/algorithms/detail/relate/linear_areal.hpp @@ -2,8 +2,8 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2013-2021. -// Modifications copyright (c) 2013-2021 Oracle and/or its affiliates. +// This file was modified by Oracle on 2013-2022. +// Modifications copyright (c) 2013-2022 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -53,7 +53,7 @@ template < typename Geometry2, typename Result, - typename PointInArealStrategy, + typename Strategy, typename BoundaryChecker, bool TransposeResult > @@ -62,11 +62,11 @@ class no_turns_la_linestring_pred public: no_turns_la_linestring_pred(Geometry2 const& geometry2, Result & res, - PointInArealStrategy const& point_in_areal_strategy, + Strategy const& strategy, BoundaryChecker const& boundary_checker) : m_geometry2(geometry2) , m_result(res) - , m_point_in_areal_strategy(point_in_areal_strategy) + , m_strategy(strategy) , m_boundary_checker(boundary_checker) , m_interrupt_flags(0) { @@ -112,7 +112,7 @@ public: int const pig = detail::within::point_in_geometry(range::front(linestring), m_geometry2, - m_point_in_areal_strategy); + m_strategy); //BOOST_GEOMETRY_ASSERT_MSG(pig != 0, "There should be no IPs"); if ( pig > 0 ) @@ -127,11 +127,9 @@ public: } // check if there is a boundary - if ( ( m_interrupt_flags & 0xC ) != 0xC // if wasn't already set - && ( m_boundary_checker.template - is_endpoint_boundary(range::front(linestring)) - || m_boundary_checker.template - is_endpoint_boundary(range::back(linestring)) ) ) + if ((m_interrupt_flags & 0xC) != 0xC // if wasn't already set + && (m_boundary_checker.is_endpoint_boundary(range::front(linestring)) + || m_boundary_checker.is_endpoint_boundary(range::back(linestring)))) { if ( pig > 0 ) { @@ -152,7 +150,7 @@ public: private: Geometry2 const& m_geometry2; Result & m_result; - PointInArealStrategy const& m_point_in_areal_strategy; + Strategy const& m_strategy; BoundaryChecker const& m_boundary_checker; unsigned m_interrupt_flags; }; @@ -200,13 +198,134 @@ private: bool const interrupt; }; + +template +inline It find_next_non_duplicated(It first, It current, It last, Strategy const& strategy) +{ + BOOST_GEOMETRY_ASSERT(current != last); + + It it = current; + for (++it ; it != last ; ++it) + { + if (! equals::equals_point_point(*current, *it, strategy)) + { + return it; + } + } + + // if not found start from the beginning + for (it = first ; it != current ; ++it) + { + if (! equals::equals_point_point(*current, *it, strategy)) + { + return it; + } + } + + return current; +} + +// calculate inside or outside based on side_calc +// this is simplified version of a check from equal<> +template +< + typename Pi, typename Pj, typename Pk, + typename Qi, typename Qj, typename Qk, + typename Strategy +> +inline bool calculate_from_inside_sides(Pi const& pi, Pj const& pj, Pk const& pk, + Qi const& qi, Qj const& qj, Qk const& qk, + Strategy const& strategy) +{ + auto const side_strategy = strategy.side(); + + int const side_pk_p = side_strategy.apply(pi, pj, pk); + int const side_qk_p = side_strategy.apply(pi, pj, qk); + // If they turn to same side (not opposite sides) + if (! overlay::base_turn_handler::opposite(side_pk_p, side_qk_p)) + { + int const side_pk_q2 = side_strategy.apply(qj, qk, pk); + return side_pk_q2 == -1; + } + else + { + return side_pk_p == -1; + } +} + +// check if the passed turn's segment of Linear geometry arrived +// from the inside or the outside of the Areal geometry +template +< + std::size_t OpId, + typename Geometry1, typename Geometry2, + typename Turn, typename Strategy +> +inline bool calculate_from_inside(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Turn const& turn, + Strategy const& strategy) +{ + static const std::size_t op_id = OpId; + static const std::size_t other_op_id = (OpId + 1) % 2; + + if (turn.operations[op_id].position == overlay::position_front) + { + return false; + } + + auto const& range1 = sub_range(geometry1, turn.operations[op_id].seg_id); + + using range2_view = detail::closed_clockwise_view::type const>; + using range2_iterator = typename boost::range_iterator::type; + range2_view const range2(sub_range(geometry2, turn.operations[other_op_id].seg_id)); + + BOOST_GEOMETRY_ASSERT(boost::size(range1)); + std::size_t const s2 = boost::size(range2); + BOOST_GEOMETRY_ASSERT(s2 > 2); + std::size_t const seg_count2 = s2 - 1; + + std::size_t const p_seg_ij = static_cast(turn.operations[op_id].seg_id.segment_index); + std::size_t const q_seg_ij = static_cast(turn.operations[other_op_id].seg_id.segment_index); + + BOOST_GEOMETRY_ASSERT(p_seg_ij + 1 < boost::size(range1)); + BOOST_GEOMETRY_ASSERT(q_seg_ij + 1 < s2); + + auto const& pi = range::at(range1, p_seg_ij); + auto const& qi = range::at(range2, q_seg_ij); + auto const& qj = range::at(range2, q_seg_ij + 1); + + bool const is_ip_qj = equals::equals_point_point(turn.point, qj, strategy); + // TODO: test this! + // BOOST_GEOMETRY_ASSERT(!equals::equals_point_point(turn.point, pi)); + // BOOST_GEOMETRY_ASSERT(!equals::equals_point_point(turn.point, qi)); + + if (is_ip_qj) + { + std::size_t const q_seg_jk = (q_seg_ij + 1) % seg_count2; + // TODO: the following function should return immediately, however the worst case complexity is O(N) + // It would be good to replace it with some O(1) mechanism + range2_iterator qk_it = find_next_non_duplicated(boost::begin(range2), + range::pos(range2, q_seg_jk), + boost::end(range2), + strategy); + + // Calculate sides in a different point order for P and Q + // Will this sequence of points be always correct? + return calculate_from_inside_sides(qi, turn.point, pi, qi, qj, *qk_it, strategy); + } + else + { + // Calculate sides with different points for P and Q + return calculate_from_inside_sides(qi, turn.point, pi, qi, turn.point, qj, strategy); + } +} + + // The implementation of an algorithm calculating relate() for L/A template struct linear_areal { - using point_type = typename geometry::point_type::type; - using mutable_point_type = typename helper_geometry::type; - // check Linear / Areal BOOST_STATIC_ASSERT(topological_dimension::value == 1 && topological_dimension::value == 2); @@ -215,7 +334,7 @@ struct linear_areal template struct multi_turn_info - : turns::get_turns::template turn_info_type::type + : turns::get_turns::template turn_info_type::type { multi_turn_info() : priority(0) {} int priority; // single-geometry sorting priority @@ -227,7 +346,7 @@ struct linear_areal < util::is_multi::value, multi_turn_info, - typename turns::get_turns::template turn_info_type::type + typename turns::get_turns::template turn_info_type::type > {}; @@ -236,7 +355,7 @@ struct linear_areal Result & result, Strategy const& strategy) { -// TODO: If Areal geometry may have infinite size, change the following line: + // TODO: If Areal geometry may have infinite size, change the following line: // The result should be FFFFFFFFF relate::set::value, TransposeResult>(result);// FFFFFFFFd, d in [1,9] or T @@ -245,12 +364,12 @@ struct linear_areal return; // get and analyse turns - typedef typename turn_info_type::type turn_type; + using turn_type = typename turn_info_type::type; std::vector turns; interrupt_policy_linear_areal interrupt_policy(geometry2, result); - turns::get_turns::apply(turns, geometry1, geometry2, interrupt_policy, strategy); + turns::get_turns::apply(turns, geometry1, geometry2, interrupt_policy, strategy); if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) ) return; @@ -293,7 +412,7 @@ struct linear_areal return; { - sort_dispatch(turns.begin(), turns.end(), util::is_multi()); + sort_dispatch(turns.begin(), turns.end(), strategy, util::is_multi()); turns_analyser analyser; analyse_each_turn(result, analyser, @@ -325,12 +444,9 @@ struct linear_areal // sort by multi_index and rind_index std::sort(turns.begin(), turns.end(), less_ring()); - typedef typename std::vector::iterator turn_iterator; - - turn_iterator it = turns.begin(); segment_identifier * prev_seg_id_ptr = NULL; // for each ring - for ( ; it != turns.end() ; ) + for (auto it = turns.begin() ; it != turns.end() ; ) { // it's the next single geometry if ( prev_seg_id_ptr == NULL @@ -377,7 +493,7 @@ struct linear_areal // find the next ring first iterator and check if the analysis should be performed has_boundary_intersection has_boundary_inters; - turn_iterator next = find_next_ring(it, turns.end(), has_boundary_inters); + auto next = find_next_ring(it, turns.end(), has_boundary_inters); // if there is no 1d overlap with the boundary if ( !has_boundary_inters.result ) @@ -390,12 +506,12 @@ struct linear_areal else { // u, c - typedef turns::less<1, turns::less_op_areal_linear<1>, cs_tag> less; - std::sort(it, next, less()); + using less_t = turns::less<1, turns::less_op_areal_linear<1>, Strategy>; + std::sort(it, next, less_t()); // analyse areal_boundary_analyser analyser; - for ( turn_iterator rit = it ; rit != next ; ++rit ) + for (auto rit = it ; rit != next ; ++rit) { // if the analyser requests, break the search if ( !analyser.apply(it, rit, next, strategy) ) @@ -518,12 +634,13 @@ struct linear_areal } }; - template - static void sort_dispatch(TurnIt first, TurnIt last, std::true_type const& /*is_multi*/) + template + static void sort_dispatch(TurnIt first, TurnIt last, Strategy const& , + std::true_type const& /*is_multi*/) { // sort turns by Linear seg_id, then by fraction, then by other multi_index - typedef turns::less<0, turns::less_other_multi_index<0>, CSTag> less; - std::sort(first, last, less()); + using less_t = turns::less<0, turns::less_other_multi_index<0>, Strategy>; + std::sort(first, last, less_t()); // For the same IP and multi_index - the same other's single geometry // set priorities as the least operation found for the whole single geometry @@ -536,20 +653,21 @@ struct linear_areal // When priorities for single geometries are set now sort turns for the same IP // if multi_index is the same sort them according to the single-less // else use priority of the whole single-geometry set earlier - typedef turns::less<0, turns::less_op_linear_areal_single<0>, CSTag> single_less; + using single_less_t = turns::less<0, turns::less_op_linear_areal_single<0>, Strategy>; for_each_equal_range(first, last, - sort_turns_group(), + sort_turns_group(), same_ip()); } - template - static void sort_dispatch(TurnIt first, TurnIt last, std::false_type const& /*is_multi*/) + template + static void sort_dispatch(TurnIt first, TurnIt last, Strategy const& , + std::false_type const& /*is_multi*/) { // sort turns by Linear seg_id, then by fraction, then // for same ring id: x, u, i, c // for different ring id: c, i, u, x - typedef turns::less<0, turns::less_op_linear_areal_single<0>, CSTag> less; - std::sort(first, last, less()); + using less_t = turns::less<0, turns::less_op_linear_areal_single<0>, Strategy>; + std::sort(first, last, less_t()); } @@ -571,9 +689,7 @@ struct linear_areal template inline bool apply(Range const& turns) { - typedef typename boost::range_iterator::type iterator; - - for (iterator it = boost::begin(turns) ; it != boost::end(turns) ; ++it) + for (auto it = boost::begin(turns) ; it != boost::end(turns) ; ++it) { if ( it->operations[0].operation == overlay::operation_intersection ) { @@ -621,37 +737,6 @@ struct linear_areal static const std::size_t op_id = 0; static const std::size_t other_op_id = 1; - template - struct la_side_calculator - { - typedef decltype(std::declval().side()) side_strategy_type; - - inline la_side_calculator(Pi const& pi, Pj const& pj, Pk const& pk, - Qi const& qi, Qj const& qj, Qk const& qk, - Strategy const& strategy) - : m_pi(pi), m_pj(pj), m_pk(pk) - , m_qi(qi), m_qj(qj), m_qk(qk) - , m_side_strategy(strategy.side()) - {} - - inline int pk_wrt_p1() const { return m_side_strategy.apply(m_pi, m_pj, m_pk); } - inline int qk_wrt_p1() const { return m_side_strategy.apply(m_pi, m_pj, m_qk); } - inline int pk_wrt_q2() const { return m_side_strategy.apply(m_qj, m_qk, m_pk); } - - private : - Pi const& m_pi; - Pj const& m_pj; - Pk const& m_pk; - Qi const& m_qi; - Qj const& m_qj; - Qk const& m_qk; - - side_strategy_type m_side_strategy; - }; - - public: turns_analyser() : m_previous_turn_ptr(NULL) @@ -714,9 +799,8 @@ struct linear_areal // NOTE: similar code is in the post-last-ip-apply() segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id; - bool const prev_back_b = is_endpoint_on_boundary( - range::back(sub_range(geometry, prev_seg_id)), - boundary_checker); + bool const prev_back_b = boundary_checker.is_endpoint_boundary( + range::back(sub_range(geometry, prev_seg_id))); // if there is a boundary on the last point if ( prev_back_b ) @@ -809,9 +893,8 @@ struct linear_areal { segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id; - bool const prev_back_b = is_endpoint_on_boundary( - range::back(sub_range(geometry, prev_seg_id)), - boundary_checker); + bool const prev_back_b = boundary_checker.is_endpoint_boundary( + range::back(sub_range(geometry, prev_seg_id))); // if there is a boundary on the last point if ( prev_back_b ) @@ -890,11 +973,8 @@ struct linear_areal update(res); } - bool const this_b - = is_ip_on_boundary(it->point, - it->operations[op_id], - boundary_checker, - seg_id); + bool const this_b = is_ip_on_boundary(it->point, it->operations[op_id], + boundary_checker); // going inside on boundary point if ( this_b ) { @@ -911,11 +991,11 @@ struct linear_areal && it->operations[op_id].position != overlay::position_front ) { // TODO: calculate_from_inside() is only needed if the current Linestring is not closed - bool const from_inside = first_point - && calculate_from_inside(geometry, - other_geometry, - *it, - strategy); + bool const from_inside = + first_point && calculate_from_inside(geometry, + other_geometry, + *it, + strategy); if ( from_inside ) update(res); @@ -925,9 +1005,8 @@ struct linear_areal // if it's the first IP then the first point is outside if ( first_point ) { - bool const front_b = is_endpoint_on_boundary( - range::front(sub_range(geometry, seg_id)), - boundary_checker); + bool const front_b = boundary_checker.is_endpoint_boundary( + range::front(sub_range(geometry, seg_id))); // if there is a boundary on the first point if ( front_b ) @@ -974,7 +1053,7 @@ struct linear_areal { // check if this is indeed the boundary point // NOTE: is_ip_on_boundary<>() should be called here but the result will be the same - if ( is_endpoint_on_boundary(it->point, boundary_checker) ) + if (boundary_checker.is_endpoint_boundary(it->point)) { update(res); } @@ -989,10 +1068,8 @@ struct linear_areal // we're outside or inside and this is the first turn else { - bool const this_b = is_ip_on_boundary(it->point, - it->operations[op_id], - boundary_checker, - seg_id); + bool const this_b = is_ip_on_boundary(it->point, it->operations[op_id], + boundary_checker); // if current IP is on boundary of the geometry if ( this_b ) { @@ -1012,11 +1089,11 @@ struct linear_areal // For LS/MultiPolygon multiple x/u turns may be generated // the first checked Polygon may be the one which LS is outside for. bool const first_point = first_in_range || m_first_from_unknown; - bool const first_from_inside = first_point - && calculate_from_inside(geometry, - other_geometry, - *it, - strategy); + bool const first_from_inside = + first_point && calculate_from_inside(geometry, + other_geometry, + *it, + strategy); if ( first_from_inside ) { update(res); @@ -1044,9 +1121,8 @@ struct linear_areal // first IP on the last segment point - this means that the first point is outside or inside if ( first_point && ( !this_b || op_blocked ) ) { - bool const front_b = is_endpoint_on_boundary( - range::front(sub_range(geometry, seg_id)), - boundary_checker); + bool const front_b = boundary_checker.is_endpoint_boundary( + range::front(sub_range(geometry, seg_id))); // if there is a boundary on the first point if ( front_b ) @@ -1135,9 +1211,8 @@ struct linear_areal segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id; - bool const prev_back_b = is_endpoint_on_boundary( - range::back(sub_range(geometry, prev_seg_id)), - boundary_checker); + bool const prev_back_b = boundary_checker.is_endpoint_boundary( + range::back(sub_range(geometry, prev_seg_id))); // if there is a boundary on the last point if ( prev_back_b ) @@ -1158,9 +1233,8 @@ struct linear_areal segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id; - bool const prev_back_b = is_endpoint_on_boundary( - range::back(sub_range(geometry, prev_seg_id)), - boundary_checker); + bool const prev_back_b = boundary_checker.is_endpoint_boundary( + range::back(sub_range(geometry, prev_seg_id))); // if there is a boundary on the last point if ( prev_back_b ) @@ -1184,9 +1258,8 @@ struct linear_areal segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id; - bool const prev_back_b = is_endpoint_on_boundary( - range::back(sub_range(geometry, prev_seg_id)), - boundary_checker); + bool const prev_back_b = boundary_checker.is_endpoint_boundary( + range::back(sub_range(geometry, prev_seg_id))); // if there is a boundary on the last point if ( prev_back_b ) @@ -1202,122 +1275,6 @@ struct linear_areal m_first_from_unknown_boundary_detected = false; } - // check if the passed turn's segment of Linear geometry arrived - // from the inside or the outside of the Areal geometry - template - static inline bool calculate_from_inside(Geometry1 const& geometry1, - Geometry2 const& geometry2, - Turn const& turn, - Strategy const& strategy) - { - if ( turn.operations[op_id].position == overlay::position_front ) - { - return false; - } - - typename sub_range_return_type::type - range1 = sub_range(geometry1, turn.operations[op_id].seg_id); - - using range2_view = detail::closed_clockwise_view::type const>; - using range2_iterator = typename boost::range_iterator::type; - range2_view const range2(sub_range(geometry2, turn.operations[other_op_id].seg_id)); - - BOOST_GEOMETRY_ASSERT(boost::size(range1)); - std::size_t const s2 = boost::size(range2); - BOOST_GEOMETRY_ASSERT(s2 > 2); - std::size_t const seg_count2 = s2 - 1; - - std::size_t const p_seg_ij = static_cast(turn.operations[op_id].seg_id.segment_index); - std::size_t const q_seg_ij = static_cast(turn.operations[other_op_id].seg_id.segment_index); - - BOOST_GEOMETRY_ASSERT(p_seg_ij + 1 < boost::size(range1)); - BOOST_GEOMETRY_ASSERT(q_seg_ij + 1 < s2); - - auto const& pi = range::at(range1, p_seg_ij); - auto const& qi = range::at(range2, q_seg_ij); - auto const& qj = range::at(range2, q_seg_ij + 1); - - bool const is_ip_qj = equals::equals_point_point(turn.point, qj, strategy); -// TODO: test this! -// BOOST_GEOMETRY_ASSERT(!equals::equals_point_point(turn.point, pi)); -// BOOST_GEOMETRY_ASSERT(!equals::equals_point_point(turn.point, qi)); - - if (is_ip_qj) - { - std::size_t const q_seg_jk = (q_seg_ij + 1) % seg_count2; -// TODO: the following function should return immediately, however the worst case complexity is O(N) -// It would be good to replace it with some O(1) mechanism - range2_iterator qk_it = find_next_non_duplicated(boost::begin(range2), - range::pos(range2, q_seg_jk), - boost::end(range2), - strategy); - - // Calculate sides in a different point order for P and Q - // Will this sequence of points be always correct? - return calculate_from_inside_sides(qi, turn.point, pi, qi, qj, *qk_it, strategy); - } - else - { - // Calculate sides with different points for P and Q - return calculate_from_inside_sides(qi, turn.point, pi, qi, turn.point, qj, strategy); - } - } - - template - static inline It find_next_non_duplicated(It first, It current, It last, - Strategy const& strategy) - { - BOOST_GEOMETRY_ASSERT( current != last ); - - It it = current; - - for ( ++it ; it != last ; ++it ) - { - if ( !equals::equals_point_point(*current, *it, strategy) ) - return it; - } - - // if not found start from the beginning - for ( it = first ; it != current ; ++it ) - { - if ( !equals::equals_point_point(*current, *it, strategy) ) - return it; - } - - return current; - } - - // calculate inside or outside based on side_calc - // this is simplified version of a check from equal<> - template - static inline - bool calculate_from_inside_sides(Pi const& pi, Pj const& pj, Pk const& pk, - Qi const& qi, Qj const& qj, Qk const& qk, - Strategy const& strategy) - { - la_side_calculator - < - Strategy, - Pi, Pj, Pk, - Qi, Qj, Qk - > side_calc(pi, pj, pk, qi, qj, qk, strategy); - - int const side_pk_p = side_calc.pk_wrt_p1(); - int const side_qk_p = side_calc.qk_wrt_p1(); - // If they turn to same side (not opposite sides) - if (! overlay::base_turn_handler::opposite(side_pk_p, side_qk_p)) - { - int const side_pk_q2 = side_calc.pk_wrt_q2(); - return side_pk_q2 == -1; - } - else - { - return side_pk_p == -1; - } - } - private: exit_watcher m_exit_watcher; segment_watcher m_seg_watcher; diff --git a/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp b/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp index 28efcbd2b..4de6c2811 100644 --- a/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp +++ b/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp @@ -2,8 +2,8 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2013, 2014, 2015, 2017, 2018, 2019. -// Modifications copyright (c) 2013-2019 Oracle and/or its affiliates. +// This file was modified by Oracle on 2013-2022. +// Modifications copyright (c) 2013-2022 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -93,11 +93,9 @@ public: m_flags |= 1; // check if there is a boundary - if ( m_flags < 2 - && ( m_boundary_checker.template - is_endpoint_boundary(range::front(linestring)) - || m_boundary_checker.template - is_endpoint_boundary(range::back(linestring)) ) ) + if (m_flags < 2 + && (m_boundary_checker.is_endpoint_boundary(range::front(linestring)) + || m_boundary_checker.is_endpoint_boundary(range::back(linestring)))) { update(m_result); m_flags |= 2; @@ -124,20 +122,16 @@ struct linear_linear Result & result, Strategy const& strategy) { - typedef typename Strategy::cs_tag cs_tag; - // The result should be FFFFFFFFF relate::set::value>(result);// FFFFFFFFd, d in [1,9] or T if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) ) return; // get and analyse turns - using point_type = typename geometry::point_type::type; - using mutable_point_type = typename helper_geometry::type; - typedef typename turns::get_turns + using turn_type = typename turns::get_turns < - Geometry1, Geometry2, mutable_point_type - >::template turn_info_type::type turn_type; + Geometry1, Geometry2 + >::template turn_info_type::type; std::vector turns; interrupt_policy_linear_linear interrupt_policy(result); @@ -146,7 +140,6 @@ struct linear_linear < Geometry1, Geometry2, - mutable_point_type, detail::get_turns::get_turn_info_type > >::apply(turns, geometry1, geometry2, interrupt_policy, strategy); @@ -180,8 +173,8 @@ struct linear_linear || may_update(result) || may_update(result) ) { - typedef turns::less<0, turns::less_op_linear_linear<0>, cs_tag> less; - std::sort(turns.begin(), turns.end(), less()); + using less_t = turns::less<0, turns::less_op_linear_linear<0>, Strategy>; + std::sort(turns.begin(), turns.end(), less_t()); turns_analyser analyser; analyse_each_turn(result, analyser, @@ -200,8 +193,8 @@ struct linear_linear || may_update(result) || may_update(result) ) { - typedef turns::less<1, turns::less_op_linear_linear<1>, cs_tag> less; - std::sort(turns.begin(), turns.end(), less()); + using less_t = turns::less<1, turns::less_op_linear_linear<1>, Strategy>; + std::sort(turns.begin(), turns.end(), less_t()); turns_analyser analyser; analyse_each_turn(result, analyser, @@ -226,9 +219,7 @@ struct linear_linear template inline bool apply(Range const& turns) { - typedef typename boost::range_iterator::type iterator; - - for (iterator it = boost::begin(turns) ; it != boost::end(turns) ; ++it) + for (auto it = boost::begin(turns) ; it != boost::end(turns) ; ++it) { if ( it->operations[0].operation == overlay::operation_intersection || it->operations[1].operation == overlay::operation_intersection ) @@ -376,20 +367,16 @@ struct linear_linear update(res); bool const this_b = it->operations[op_id].position == overlay::position_front // ignore spikes! - && is_ip_on_boundary(it->point, - it->operations[op_id], - boundary_checker, - seg_id); + && is_ip_on_boundary(it->point, it->operations[op_id], + boundary_checker); // going inside on boundary point // may be front only if ( this_b ) { // may be front and back - bool const other_b = is_ip_on_boundary(it->point, - it->operations[other_op_id], - other_boundary_checker, - other_id); + bool const other_b = is_ip_on_boundary(it->point, it->operations[other_op_id], + other_boundary_checker); // it's also the boundary of the other geometry if ( other_b ) @@ -415,9 +402,8 @@ struct linear_linear // if it's the first IP then the first point is outside if ( first_in_range ) { - bool const front_b = is_endpoint_on_boundary( - range::front(sub_range(geometry, seg_id)), - boundary_checker); + bool const front_b = boundary_checker.is_endpoint_boundary( + range::front(sub_range(geometry, seg_id))); // if there is a boundary on the first point if ( front_b ) @@ -463,13 +449,12 @@ struct linear_linear { // check if this is indeed the boundary point // NOTE: is_ip_on_boundary<>() should be called here but the result will be the same - if ( is_endpoint_on_boundary(it->point, boundary_checker) ) + if (boundary_checker.is_endpoint_boundary(it->point)) { // may be front and back - bool const other_b = is_ip_on_boundary(it->point, - it->operations[other_op_id], - other_boundary_checker, - other_id); + bool const other_b = is_ip_on_boundary(it->point, + it->operations[other_op_id], + other_boundary_checker); // it's also the boundary of the other geometry if ( other_b ) { @@ -503,9 +488,8 @@ struct linear_linear // it's the first point in range if ( first_in_range ) { - bool const front_b = is_endpoint_on_boundary( - range::front(sub_range(geometry, seg_id)), - boundary_checker); + bool const front_b = boundary_checker.is_endpoint_boundary( + range::front(sub_range(geometry, seg_id))); // if there is a boundary on the first point if ( front_b ) @@ -517,16 +501,14 @@ struct linear_linear // method other than crosses, check more conditions else { - bool const this_b = is_ip_on_boundary(it->point, - it->operations[op_id], - boundary_checker, - seg_id); - - bool const other_b = is_ip_on_boundary(it->point, - it->operations[other_op_id], - other_boundary_checker, - other_id); + bool const this_b = is_ip_on_boundary(it->point, + it->operations[op_id], + boundary_checker); + bool const other_b = is_ip_on_boundary(it->point, + it->operations[other_op_id], + other_boundary_checker); + // if current IP is on boundary of the geometry if ( this_b ) { @@ -562,9 +544,8 @@ struct linear_linear && ! m_collinear_spike_exit /*&& !is_collinear*/ ) { - bool const front_b = is_endpoint_on_boundary( - range::front(sub_range(geometry, seg_id)), - boundary_checker); + bool const front_b = boundary_checker.is_endpoint_boundary( + range::front(sub_range(geometry, seg_id))); // if there is a boundary on the first point if ( front_b ) @@ -621,9 +602,8 @@ struct linear_linear segment_identifier const& prev_seg_id = turn_ptr->operations[op_id].seg_id; //BOOST_GEOMETRY_ASSERT(!boost::empty(sub_range(geometry, prev_seg_id))); - bool const prev_back_b = is_endpoint_on_boundary( - range::back(sub_range(geometry, prev_seg_id)), - boundary_checker); + bool const prev_back_b = boundary_checker.is_endpoint_boundary( + range::back(sub_range(geometry, prev_seg_id))); // if there is a boundary on the last point if ( prev_back_b ) @@ -661,19 +641,17 @@ struct linear_linear OtherBoundaryChecker const& other_boundary_checker, bool first_in_range) { - typename detail::single_geometry_return_type::type - ls1_ref = detail::single_geometry(geometry, turn.operations[op_id].seg_id); - typename detail::single_geometry_return_type::type - ls2_ref = detail::single_geometry(other_geometry, turn.operations[other_op_id].seg_id); + auto const& ls1 = detail::single_geometry(geometry, turn.operations[op_id].seg_id); + auto const& ls2 = detail::single_geometry(other_geometry, turn.operations[other_op_id].seg_id); // only one of those should be true: if ( turn.operations[op_id].position == overlay::position_front ) { // valid, point-sized - if ( boost::size(ls2_ref) == 2 ) + if ( boost::size(ls2) == 2 ) { - bool const front_b = is_endpoint_on_boundary(turn.point, boundary_checker); + bool const front_b = boundary_checker.is_endpoint_boundary(turn.point); if ( front_b ) { @@ -693,11 +671,11 @@ struct linear_linear else if ( turn.operations[op_id].position == overlay::position_back ) { // valid, point-sized - if ( boost::size(ls2_ref) == 2 ) + if ( boost::size(ls2) == 2 ) { update(res); - bool const back_b = is_endpoint_on_boundary(turn.point, boundary_checker); + bool const back_b = boundary_checker.is_endpoint_boundary(turn.point); if ( back_b ) { @@ -710,9 +688,9 @@ struct linear_linear if ( first_in_range ) { - //BOOST_GEOMETRY_ASSERT(!boost::empty(ls1_ref)); - bool const front_b = is_endpoint_on_boundary( - range::front(ls1_ref), boundary_checker); + //BOOST_GEOMETRY_ASSERT(!boost::empty(ls1)); + bool const front_b = boundary_checker.is_endpoint_boundary( + range::front(ls1)); if ( front_b ) { update(res); @@ -727,13 +705,13 @@ struct linear_linear // here we don't know which one is degenerated - bool const is_point1 = boost::size(ls1_ref) == 2 - && equals::equals_point_point(range::front(ls1_ref), - range::back(ls1_ref), + bool const is_point1 = boost::size(ls1) == 2 + && equals::equals_point_point(range::front(ls1), + range::back(ls1), boundary_checker.strategy()); - bool const is_point2 = boost::size(ls2_ref) == 2 - && equals::equals_point_point(range::front(ls2_ref), - range::back(ls2_ref), + bool const is_point2 = boost::size(ls2) == 2 + && equals::equals_point_point(range::front(ls2), + range::back(ls2), other_boundary_checker.strategy()); // if the second one is degenerated @@ -743,9 +721,9 @@ struct linear_linear if ( first_in_range ) { - //BOOST_GEOMETRY_ASSERT(!boost::empty(ls1_ref)); - bool const front_b = is_endpoint_on_boundary( - range::front(ls1_ref), boundary_checker); + //BOOST_GEOMETRY_ASSERT(!boost::empty(ls1)); + bool const front_b = boundary_checker.is_endpoint_boundary( + range::front(ls1)); if ( front_b ) { update(res); diff --git a/include/boost/geometry/algorithms/detail/relate/turns.hpp b/include/boost/geometry/algorithms/detail/relate/turns.hpp index ed070e935..f88f64ab1 100644 --- a/include/boost/geometry/algorithms/detail/relate/turns.hpp +++ b/include/boost/geometry/algorithms/detail/relate/turns.hpp @@ -2,8 +2,8 @@ // Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2013-2020. -// Modifications copyright (c) 2013-2020 Oracle and/or its affiliates. +// This file was modified by Oracle on 2013-2022. +// Modifications copyright (c) 2013-2022 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle @@ -20,6 +20,8 @@ #include #include +#include + #include #include @@ -46,7 +48,6 @@ template < typename Geometry1, typename Geometry2, - typename Point, typename GetTurnPolicy = detail::get_turns::get_turn_info_type < Geometry1, Geometry2, assign_policy<> @@ -54,6 +55,11 @@ template > struct get_turns { + using turn_point_type = typename helper_geometry + < + typename geometry::point_type::type + >::type; + template struct robust_policy_type : geometry::rescale_overlay_policy_type @@ -71,14 +77,14 @@ struct get_turns > struct turn_info_type { - using ratio_type = typename segment_ratio_type::type; + using ratio_type = typename segment_ratio_type::type; using type = overlay::turn_info < - Point, + turn_point_type, ratio_type, typename detail::get_turns::turn_operation_type < - Geometry1, Geometry2, Point, ratio_type + Geometry1, Geometry2, turn_point_type, ratio_type >::type >; }; @@ -166,7 +172,7 @@ struct less_op_xxx_linear template struct less_op_linear_linear - : less_op_xxx_linear< OpId, op_to_int<0,2,3,1,4,0> > + : less_op_xxx_linear< OpId, op_to_int<0,2,3,1,4,0> > // xuic {}; template @@ -269,7 +275,7 @@ struct less_other_multi_index // sort turns by G1 - source_index == 0 by: // seg_id -> distance and coordinates -> operation -template +template struct less { BOOST_STATIC_ASSERT(OpId < 2); @@ -277,14 +283,8 @@ struct less template static inline bool use_fraction(Turn const& left, Turn const& right) { - typedef typename geometry::strategy::within::services::default_strategy - < - typename Turn::point_type, typename Turn::point_type, - point_tag, point_tag, - pointlike_tag, pointlike_tag, - typename tag_cast::type, - typename tag_cast::type - >::type eq_pp_strategy_type; + using eq_pp_strategy_type = decltype(std::declval().relate( + detail::dummy_point(), detail::dummy_point())); static LessOp less_op; diff --git a/test/algorithms/set_operations/check_turn_less.hpp b/test/algorithms/set_operations/check_turn_less.hpp index 174eced4a..7534f6b27 100644 --- a/test/algorithms/set_operations/check_turn_less.hpp +++ b/test/algorithms/set_operations/check_turn_less.hpp @@ -1,6 +1,6 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2015-2020, Oracle and/or its affiliates. +// Copyright (c) 2015-2022, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -87,7 +87,6 @@ struct check_turn_less < Geometry1, Geometry2, - typename bg::point_type::type, bg::detail::get_turns::get_turn_info_type < Geometry1, Geometry2, assign_policy<> diff --git a/test/algorithms/set_operations/test_get_turns_ll_invariance.hpp b/test/algorithms/set_operations/test_get_turns_ll_invariance.hpp index dfaefd20f..0496af280 100644 --- a/test/algorithms/set_operations/test_get_turns_ll_invariance.hpp +++ b/test/algorithms/set_operations/test_get_turns_ll_invariance.hpp @@ -1,6 +1,6 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2014-2021, Oracle and/or its affiliates. +// Copyright (c) 2014-2022, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -84,7 +84,6 @@ private: < LinearGeometry1, LinearGeometry2, - typename bg::point_type::type, bg_detail::get_turns::get_turn_info_type < LinearGeometry1, @@ -110,7 +109,7 @@ public: typedef typename bg_detail::relate::turns::get_turns < - Linear1, Linear2, typename bg::point_type::type + Linear1, Linear2 >::template turn_info_type::type turn_info; typedef std::vector turns_container;