diff --git a/include/boost/geometry/algorithms/detail/convex_hull/graham_andrew.hpp b/include/boost/geometry/algorithms/detail/convex_hull/graham_andrew.hpp index 7f3d61567..9e59927f5 100644 --- a/include/boost/geometry/algorithms/detail/convex_hull/graham_andrew.hpp +++ b/include/boost/geometry/algorithms/detail/convex_hull/graham_andrew.hpp @@ -46,13 +46,13 @@ namespace detail { namespace convex_hull // TODO: All of the copies could be avoided if this function stored pointers to points. // But would it be possible considering that a range can return proxy reference? -template -inline void get_extremes(Ranges const& ranges, +template +inline void get_extremes(InputProxy const& in_proxy, Point& left, Point& right, Less const& less) { bool first = true; - ranges.for_each_range([&](auto const& range) + in_proxy.for_each_range([&](auto const& range) { if (boost::empty(range)) { @@ -108,13 +108,13 @@ inline void get_extremes(Ranges const& ranges, } -template -inline void assign_ranges(Ranges const& ranges, +template +inline void assign_ranges(InputProxy const& in_proxy, Point const& most_left, Point const& most_right, Container& lower_points, Container& upper_points, SideStrategy const& side) { - ranges.for_each_range([&](auto const& range) + in_proxy.for_each_range([&](auto const& range) { // Put points in one of the two output sequences for (auto it = boost::begin(range); it != boost::end(range); ++it) @@ -161,12 +161,12 @@ class graham_andrew }; public: - template - static void apply(InputRanges const& ranges, OutputRing & out_ring, Strategy& strategy) + template + static void apply(InputProxy const& in_proxy, OutputRing & out_ring, Strategy& strategy) { partitions state; - apply(ranges, state, strategy); + apply(in_proxy, state, strategy); result(state, range::back_inserter(out_ring), @@ -175,8 +175,8 @@ public: } private: - template - static void apply(InputRanges const& ranges, partitions& state, Strategy& strategy) + template + static void apply(InputProxy const& in_proxy, partitions& state, Strategy& strategy) { // First pass. // Get min/max (in most cases left / right) points @@ -195,7 +195,7 @@ private: // TODO: User-defined CS-specific less-compare geometry::less less; - detail::convex_hull::get_extremes(ranges, most_left, most_right, less); + detail::convex_hull::get_extremes(in_proxy, most_left, most_right, less); container_type lower_points, upper_points; @@ -204,7 +204,7 @@ private: // Bounding left/right points // Second pass, now that extremes are found, assign all points // in either lower, either upper - detail::convex_hull::assign_ranges(ranges, most_left, most_right, + detail::convex_hull::assign_ranges(in_proxy, most_left, most_right, lower_points, upper_points, side_strategy); diff --git a/include/boost/geometry/algorithms/detail/convex_hull/interface.hpp b/include/boost/geometry/algorithms/detail/convex_hull/interface.hpp index c7d169242..c7823b1b8 100644 --- a/include/boost/geometry/algorithms/detail/convex_hull/interface.hpp +++ b/include/boost/geometry/algorithms/detail/convex_hull/interface.hpp @@ -68,9 +68,9 @@ namespace detail { namespace convex_hull // Abstraction representing ranges/rings of a geometry template -struct geometry_ranges +struct input_geometry_proxy { - geometry_ranges(Geometry const& geometry) + input_geometry_proxy(Geometry const& geometry) : m_geometry(geometry) {} @@ -86,9 +86,9 @@ struct geometry_ranges // Abstraction representing ranges/rings of subgeometries of geometry collection // with boxes converted to rings template -struct geometry_collection_ranges +struct input_geometry_collection_proxy { - geometry_collection_ranges(Geometry const& geometry, BoxRings const& box_rings) + input_geometry_collection_proxy(Geometry const& geometry, BoxRings const& box_rings) : m_geometry(geometry) , m_box_rings(box_rings) {} @@ -140,66 +140,7 @@ struct default_strategy {}; -// Wrapper for output geometry. It contains reference to OutputGeometry and may contain -// a temporary geometry which is moved to the output at the end of the algorithm. -template ::type> -struct output_geometry -{ - BOOST_GEOMETRY_STATIC_ASSERT_FALSE("This OutputGeometry is not supported.", OutputGeometry, Tag); -}; - -// For backward compatibility -template -struct output_geometry -{ - explicit output_geometry(OutputGeometry & out) : m_out(out) {} - OutputGeometry & range() { return m_out; } - template - void move_to_out(Strategy const& ) {} -private: - OutputGeometry & m_out; -}; - -template -struct output_geometry -{ - explicit output_geometry(OutputGeometry & out) : m_out(out) {} - OutputGeometry & range() { return m_out; } - template - void move_to_out(Strategy const& ) {} -private: - OutputGeometry & m_out; -}; - -template -struct output_geometry -{ - explicit output_geometry(OutputGeometry & out) : m_out(out) {} - decltype(auto) range() { return exterior_ring(m_out); } - template - void move_to_out(Strategy const& ) {} -private: - OutputGeometry & m_out; -}; - -template -struct output_geometry -{ - explicit output_geometry(OutputGeometry & out) : m_out(out) {} - decltype(auto) range() { return exterior_ring(m_polygon); } - template - void move_to_out(Strategy const& ) - { - if (! boost::empty(exterior_ring(m_polygon))) - { - range::push_back(m_out, std::move(m_polygon)); - } - } -private: - OutputGeometry & m_out; - typename boost::range_value::type m_polygon; -}; - +// Utilities for output GC and DG template struct output_polygonal_less { @@ -244,41 +185,186 @@ struct output_pointlike_less static const bool value = priority::value < priority::value; }; -struct move_emplace_back_policy + +}} // namespace detail::convex_hull +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch { - template - static inline void apply(Geometry & g, OutputGeometry & out) + + +template +< + typename Geometry, + typename Tag = typename tag::type +> +struct convex_hull +{ + template + static inline void apply(Geometry const& geometry, + OutputGeometry& out, + Strategy const& strategy) { - range::emplace_back(out, std::move(g)); + detail::convex_hull::input_geometry_proxy in_proxy(geometry); + detail::convex_hull::graham_andrew + < + typename point_type::type + >::apply(in_proxy, out, strategy); } }; -struct move_assign_policy + +// A hull for boxes is trivial. Any strategy is (currently) skipped. +// TODO: This is not correct in spherical and geographic CS. +template +struct convex_hull { - template - static inline void apply(Geometry & g, OutputGeometry & out) + template + static inline void apply(Box const& box, + OutputGeometry& out, + Strategy const& strategy) { - out = std::move(g); + static bool const Close + = geometry::closure::value == closed; + static bool const Reverse + = geometry::point_order::value == counterclockwise; + + std::array::type, 4> arr; + // TODO: This assigns only 2d cooridnates! + // And it is also used in box_view<>! + geometry::detail::assign_box_corners_oriented(box, arr); + + std::move(arr.begin(), arr.end(), range::back_inserter(out)); + if (BOOST_GEOMETRY_CONDITION(Close)) + { + range::push_back(out, range::front(out)); + } } }; -template -struct output_geometry_dg_or_gc + +template +struct convex_hull +{ + template + static inline void apply(GeometryCollection const& geometry, + OutputGeometry& out, + Strategy const& strategy) + { + // Assuming that single point_type is used by the GeometryCollection + using subgeometry_type = typename detail::first_geometry_type::type; + using point_type = typename geometry::point_type::type; + using ring_type = model::ring; + + // Calculate box rings once + std::vector box_rings; + detail::visit_breadth_first([&](auto const& g) + { + add_ring_for_box(box_rings, g, strategy); + return true; + }, geometry); + + detail::convex_hull::input_geometry_collection_proxy + < + GeometryCollection, std::vector + > in_proxy(geometry, box_rings); + + detail::convex_hull::graham_andrew + < + point_type + >::apply(in_proxy, out, strategy); + } + +private: + template + < + typename Ring, typename SubGeometry, typename Strategy, + std::enable_if_t::value, int> = 0 + > + static inline void add_ring_for_box(std::vector & rings, SubGeometry const& box, + Strategy const& strategy) + { + Ring ring; + convex_hull::apply(box, ring, strategy); + rings.push_back(std::move(ring)); + } + template + < + typename Ring, typename SubGeometry, typename Strategy, + std::enable_if_t::value, int> = 0 + > + static inline void add_ring_for_box(std::vector & , SubGeometry const& , + Strategy const& ) + {} +}; + + +template ::type> +struct convex_hull_out +{ + BOOST_GEOMETRY_STATIC_ASSERT_FALSE("This OutputGeometry is not supported.", OutputGeometry, Tag); +}; + +template +struct convex_hull_out +{ + template + static inline void apply(Geometry const& geometry, + OutputGeometry& out, + Strategies const& strategies) + { + dispatch::convex_hull::apply(geometry, out, strategies); + } +}; + +template +struct convex_hull_out +{ + template + static inline void apply(Geometry const& geometry, + OutputGeometry& out, + Strategies const& strategies) + { + auto&& ring = exterior_ring(out); + dispatch::convex_hull::apply(geometry, ring, strategies); + } +}; + +template +struct convex_hull_out +{ + template + static inline void apply(Geometry const& geometry, + OutputGeometry& out, + Strategies const& strategies) + { + typename boost::range_value::type polygon; + auto&& ring = exterior_ring(polygon); + dispatch::convex_hull::apply(geometry, ring, strategies); + // Empty input is checked so the output shouldn't be empty + range::push_back(out, std::move(polygon)); + } +}; + +template +struct convex_hull_out { using polygonal_t = typename util::sequence_min_element < typename traits::geometry_types::type, - output_polygonal_less + detail::convex_hull::output_polygonal_less >::type; using linear_t = typename util::sequence_min_element < typename traits::geometry_types::type, - output_linear_less + detail::convex_hull::output_linear_less >::type; using pointlike_t = typename util::sequence_min_element < typename traits::geometry_types::type, - output_pointlike_less + detail::convex_hull::output_pointlike_less >::type; // select_element may define different kind of geometry than the one that is desired @@ -289,48 +375,55 @@ struct output_geometry_dg_or_gc BOOST_GEOMETRY_STATIC_ASSERT(util::is_pointlike::value, "It must be possible to store pointlike geometry in OutputGeometry.", pointlike_t); - explicit output_geometry_dg_or_gc(OutputGeometry & out) - : m_out(out), m_wrapper(m_polygonal) - {} - - decltype(auto) range() { return m_wrapper.range(); } - - template - void move_to_out(Strategy const& strategy) + template + static inline void apply(Geometry const& geometry, + OutputGeometry& out, + Strategies const& strategies) { - auto&& out_range = m_wrapper.range(); - if (! boost::empty(out_range)) + polygonal_t polygonal; + convex_hull_out::apply(geometry, polygonal, strategies); + // Empty input is checked so the output shouldn't be empty + auto&& out_ring = ring(polygonal); + + if (boost::size(out_ring) == detail::minimum_ring_size::value) { - auto size = boost::size(out_range); - if (size > minimum_ring_size::value) + using detail::equals::equals_point_point; + if (equals_point_point(range::front(out_ring), range::at(out_ring, 1), strategies)) { - m_wrapper.move_to_out(strategy); - MovePolicy::apply(m_polygonal, m_out); + pointlike_t pointlike; + move_to_pointlike(out_ring, pointlike); + move_to_out(pointlike, out); + return; } - else // size == 3 || size == 4 + if (equals_point_point(range::front(out_ring), range::at(out_ring, 2), strategies)) { - if (detail::equals::equals_point_point(range::front(out_range), range::at(out_range, 1), strategy)) - { - pointlike_t pointlike; - move_to_pointlike(out_range, pointlike); - MovePolicy::apply(pointlike, m_out); - } - else if (detail::equals::equals_point_point(range::front(out_range), range::at(out_range, 2), strategy)) - { - linear_t linear; - move_to_linear(out_range, linear); - MovePolicy::apply(linear, m_out); - } - else - { - m_wrapper.move_to_out(strategy); - MovePolicy::apply(m_polygonal, m_out); - } + linear_t linear; + move_to_linear(out_ring, linear); + move_to_out(linear, out); + return; } } + + move_to_out(polygonal, out); } private: + template = 0> + static decltype(auto) ring(Polygonal const& polygonal) + { + return polygonal; + } + template = 0> + static decltype(auto) ring(Polygonal const& polygonal) + { + return exterior_ring(polygonal); + } + template = 0> + static decltype(auto) ring(Polygonal const& polygonal) + { + return exterior_ring(range::front(polygonal)); + } + template = 0> static void move_to_linear(Range & out_range, Linear & seg) { @@ -361,167 +454,38 @@ private: range::push_back(mpt, std::move(range::front(out_range))); } - OutputGeometry & m_out; - polygonal_t m_polygonal; - output_geometry m_wrapper; -}; - -template -struct output_geometry - : output_geometry_dg_or_gc -{ - explicit output_geometry(OutputGeometry & out) - : output_geometry_dg_or_gc(out) - {} -}; - -template -struct output_geometry - : output_geometry_dg_or_gc -{ - explicit output_geometry(OutputGeometry & out) - : output_geometry_dg_or_gc(out) - {} -}; - - -}} // namespace detail::convex_hull -#endif // DOXYGEN_NO_DETAIL - - -#ifndef DOXYGEN_NO_DISPATCH -namespace dispatch -{ - - -template -< - typename Geometry, - typename Tag = typename tag::type -> -struct convex_hull -{ - template - static inline void apply(Geometry const& geometry, - OutputGeometry& out, - Strategy const& strategy) - { - detail::convex_hull::geometry_ranges ranges(geometry); - - detail::convex_hull::output_geometry out_wrapper(out); - // NOTE: A variable is created here because this can be a proxy range - // and back_insert_iterator<> can store a pointer to it. - auto&& out_range = out_wrapper.range(); - - detail::convex_hull::graham_andrew - < - typename point_type::type - >::apply(ranges, out_range, strategy); - - out_wrapper.move_to_out(strategy); - } -}; - - -// A hull for boxes is trivial. Any strategy is (currently) skipped. -// TODO: This is not correct in spherical and geographic CS. -template -struct convex_hull -{ - template - static inline void apply(Box const& box, - OutputGeometry& out, - Strategy const& strategy) - { - detail::convex_hull::output_geometry out_wrapper(out); - // NOTE: A variable is created here because this can be a proxy range - // and back_insert_iterator<> can store a pointer to it. - auto&& out_range = out_wrapper.range(); - - using out_range_t = std::remove_reference_t; - static bool const Close - = geometry::closure::value == closed; - static bool const Reverse - = geometry::point_order::value == counterclockwise; - - std::array::type, 4> arr; - // TODO: This assigns only 2d cooridnates! - // And it is also used in box_view<>! - geometry::detail::assign_box_corners_oriented(box, arr); - - std::move(arr.begin(), arr.end(), range::back_inserter(out_range)); - if (BOOST_GEOMETRY_CONDITION(Close)) - { - range::push_back(out_range, range::front(out_range)); - } - - out_wrapper.move_to_out(strategy); - } -}; - - -template -struct convex_hull -{ - template - static inline void apply(GeometryCollection const& geometry, - OutputGeometry& out, - Strategy const& strategy) - { - // Assuming that single point_type is used by the GeometryCollection - using subgeometry_type = typename detail::first_geometry_type::type; - using point_type = typename geometry::point_type::type; - using ring_type = model::ring; - - // Calculate box rings once - std::vector box_rings; - detail::visit_breadth_first([&](auto const& g) - { - add_ring_for_box(box_rings, g, strategy); - return true; - }, geometry); - - detail::convex_hull::geometry_collection_ranges - < - GeometryCollection, std::vector - > ranges(geometry, box_rings); - - detail::convex_hull::output_geometry out_wrapper(out); - // NOTE: A variable is created here because this can be a proxy range - // and back_insert_iterator<> can store a pointer to it. - auto&& out_range = out_wrapper.range(); - - detail::convex_hull::graham_andrew - < - point_type - >::apply(ranges, out_range, strategy); - - out_wrapper.move_to_out(strategy); - } - -private: template < - typename Ring, typename SubGeometry, typename Strategy, - std::enable_if_t::value, int> = 0 + typename Geometry, typename OutputGeometry_, + util::enable_if_geometry_collection_t = 0 > - static inline void add_ring_for_box(std::vector & rings, SubGeometry const& box, - Strategy const& strategy) + static void move_to_out(Geometry & g, OutputGeometry_ & out) { - Ring ring; - convex_hull::apply(box, ring, strategy); - rings.push_back(std::move(ring)); + range::emplace_back(out, std::move(g)); } template < - typename Ring, typename SubGeometry, typename Strategy, - std::enable_if_t::value, int> = 0 + typename Geometry, typename OutputGeometry_, + util::enable_if_dynamic_geometry_t = 0 > - static inline void add_ring_for_box(std::vector & , SubGeometry const& , - Strategy const& ) - {} + static void move_to_out(Geometry & g, OutputGeometry_ & out) + { + out = std::move(g); + } }; +template +struct convex_hull_out + : convex_hull_out +{}; + + +// For backward compatibility +template +struct convex_hull_out + : convex_hull_out +{}; + } // namespace dispatch #endif // DOXYGEN_NO_DISPATCH @@ -537,7 +501,7 @@ struct convex_hull OutputGeometry& out, Strategies const& strategies) { - dispatch::convex_hull::apply(geometry, out, strategies); + dispatch::convex_hull_out::apply(geometry, out, strategies); } }; @@ -554,7 +518,7 @@ struct convex_hull Geometry >::type; - dispatch::convex_hull::apply(geometry, out, strategy_type()); + dispatch::convex_hull_out::apply(geometry, out, strategy_type()); } }; diff --git a/include/boost/geometry/util/type_traits.hpp b/include/boost/geometry/util/type_traits.hpp index 7ad4c71bc..c58b5bad0 100644 --- a/include/boost/geometry/util/type_traits.hpp +++ b/include/boost/geometry/util/type_traits.hpp @@ -225,6 +225,30 @@ struct enable_if_box template using enable_if_box_t = typename enable_if_box::type; +template +struct enable_if_ring + : std::enable_if::value, T> +{}; + +template +using enable_if_ring_t = typename enable_if_ring::type; + +template +struct enable_if_polygon + : std::enable_if::value, T> +{}; + +template +using enable_if_polygon_t = typename enable_if_polygon::type; + +template +struct enable_if_multi_polygon + : std::enable_if::value, T> +{}; + +template +using enable_if_multi_polygon_t = typename enable_if_multi_polygon::type; + template struct enable_if_polygonal @@ -253,6 +277,23 @@ template using enable_if_polysegmental_t = typename enable_if_polysegmental::type; +template +struct enable_if_dynamic_geometry + : std::enable_if::value, T> +{}; + +template +using enable_if_dynamic_geometry_t = typename enable_if_dynamic_geometry::type; + +template +struct enable_if_geometry_collection + : std::enable_if::value, T> +{}; + +template +using enable_if_geometry_collection_t = typename enable_if_geometry_collection::type; + + } // namespace util