interface.hpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands.
  3. // Copyright (c) 2008-2015 Bruno Lalande, Paris, France.
  4. // Copyright (c) 2009-2015 Mateusz Loskot, London, UK.
  5. // Copyright (c) 2023 Adam Wulkiewicz, Lodz, Poland.
  6. // This file was modified by Oracle on 2014-2021.
  7. // Modifications copyright (c) 2014-2021 Oracle and/or its affiliates.
  8. // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle
  9. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
  10. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
  11. // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
  12. // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.
  13. // Use, modification and distribution is subject to the Boost Software License,
  14. // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  15. // http://www.boost.org/LICENSE_1_0.txt)
  16. #ifndef BOOST_GEOMETRY_ALGORITHMS_CONVEX_HULL_INTERFACE_HPP
  17. #define BOOST_GEOMETRY_ALGORITHMS_CONVEX_HULL_INTERFACE_HPP
  18. #include <array>
  19. #include <boost/range/size.hpp>
  20. #include <boost/geometry/algorithms/detail/assign_box_corners.hpp>
  21. #include <boost/geometry/algorithms/detail/convex_hull/graham_andrew.hpp>
  22. #include <boost/geometry/algorithms/detail/equals/point_point.hpp>
  23. #include <boost/geometry/algorithms/detail/for_each_range.hpp>
  24. #include <boost/geometry/algorithms/detail/select_geometry_type.hpp>
  25. #include <boost/geometry/algorithms/detail/visit.hpp>
  26. #include <boost/geometry/algorithms/is_empty.hpp>
  27. #include <boost/geometry/core/closure.hpp>
  28. #include <boost/geometry/core/cs.hpp>
  29. #include <boost/geometry/core/exterior_ring.hpp>
  30. #include <boost/geometry/core/geometry_types.hpp>
  31. #include <boost/geometry/core/point_order.hpp>
  32. #include <boost/geometry/core/ring_type.hpp>
  33. #include <boost/geometry/core/tag.hpp>
  34. #include <boost/geometry/core/tags.hpp>
  35. #include <boost/geometry/core/visit.hpp>
  36. #include <boost/geometry/geometries/adapted/boost_variant.hpp> // For backward compatibility
  37. #include <boost/geometry/geometries/concepts/check.hpp>
  38. #include <boost/geometry/geometries/ring.hpp>
  39. #include <boost/geometry/strategies/convex_hull/cartesian.hpp>
  40. #include <boost/geometry/strategies/convex_hull/geographic.hpp>
  41. #include <boost/geometry/strategies/convex_hull/spherical.hpp>
  42. #include <boost/geometry/strategies/default_strategy.hpp>
  43. #include <boost/geometry/util/constexpr.hpp>
  44. #include <boost/geometry/util/range.hpp>
  45. #include <boost/geometry/util/sequence.hpp>
  46. #include <boost/geometry/util/type_traits.hpp>
  47. namespace boost { namespace geometry
  48. {
  49. // TODO: This file is named interface.hpp but the code below is not the interface.
  50. // It's the implementation of the algorithm.
  51. #ifndef DOXYGEN_NO_DETAIL
  52. namespace detail { namespace convex_hull
  53. {
  54. // Abstraction representing ranges/rings of a geometry
  55. template <typename Geometry>
  56. struct input_geometry_proxy
  57. {
  58. input_geometry_proxy(Geometry const& geometry)
  59. : m_geometry(geometry)
  60. {}
  61. template <typename UnaryFunction>
  62. inline void for_each_range(UnaryFunction fun) const
  63. {
  64. geometry::detail::for_each_range(m_geometry, fun);
  65. }
  66. Geometry const& m_geometry;
  67. };
  68. // Abstraction representing ranges/rings of subgeometries of geometry collection
  69. // with boxes converted to rings
  70. template <typename Geometry, typename BoxRings>
  71. struct input_geometry_collection_proxy
  72. {
  73. input_geometry_collection_proxy(Geometry const& geometry, BoxRings const& box_rings)
  74. : m_geometry(geometry)
  75. , m_box_rings(box_rings)
  76. {}
  77. template <typename UnaryFunction>
  78. inline void for_each_range(UnaryFunction fun) const
  79. {
  80. detail::visit_breadth_first([&](auto const& g)
  81. {
  82. input_geometry_collection_proxy::call_for_non_boxes(g, fun);
  83. return true;
  84. }, m_geometry);
  85. for (auto const& r : m_box_rings)
  86. {
  87. geometry::detail::for_each_range(r, fun);
  88. }
  89. }
  90. private:
  91. template <typename G, typename F, std::enable_if_t<! util::is_box<G>::value, int> = 0>
  92. static inline void call_for_non_boxes(G const& g, F & f)
  93. {
  94. geometry::detail::for_each_range(g, f);
  95. }
  96. template <typename G, typename F, std::enable_if_t<util::is_box<G>::value, int> = 0>
  97. static inline void call_for_non_boxes(G const&, F &)
  98. {}
  99. Geometry const& m_geometry;
  100. BoxRings const& m_box_rings;
  101. };
  102. // TODO: Or just implement point_type<> for GeometryCollection
  103. // and enforce the same point_type used in the whole sequence in check().
  104. template <typename Geometry, typename Tag = typename tag<Geometry>::type>
  105. struct default_strategy
  106. {
  107. using type = typename strategies::convex_hull::services::default_strategy
  108. <
  109. Geometry
  110. >::type;
  111. };
  112. template <typename Geometry>
  113. struct default_strategy<Geometry, geometry_collection_tag>
  114. : default_strategy<typename detail::first_geometry_type<Geometry>::type>
  115. {};
  116. // Utilities for output GC and DG
  117. template <typename G1, typename G2>
  118. struct output_polygonal_less
  119. {
  120. template <typename G>
  121. using priority = std::integral_constant
  122. <
  123. int,
  124. (util::is_ring<G>::value ? 0 :
  125. util::is_polygon<G>::value ? 1 :
  126. util::is_multi_polygon<G>::value ? 2 : 3)
  127. >;
  128. static const bool value = priority<G1>::value < priority<G2>::value;
  129. };
  130. template <typename G1, typename G2>
  131. struct output_linear_less
  132. {
  133. template <typename G>
  134. using priority = std::integral_constant
  135. <
  136. int,
  137. (util::is_segment<G>::value ? 0 :
  138. util::is_linestring<G>::value ? 1 :
  139. util::is_multi_linestring<G>::value ? 2 : 3)
  140. >;
  141. static const bool value = priority<G1>::value < priority<G2>::value;
  142. };
  143. template <typename G1, typename G2>
  144. struct output_pointlike_less
  145. {
  146. template <typename G>
  147. using priority = std::integral_constant
  148. <
  149. int,
  150. (util::is_point<G>::value ? 0 :
  151. util::is_multi_point<G>::value ? 1 : 2)
  152. >;
  153. static const bool value = priority<G1>::value < priority<G2>::value;
  154. };
  155. }} // namespace detail::convex_hull
  156. #endif // DOXYGEN_NO_DETAIL
  157. #ifndef DOXYGEN_NO_DISPATCH
  158. namespace dispatch
  159. {
  160. template
  161. <
  162. typename Geometry,
  163. typename Tag = typename tag<Geometry>::type
  164. >
  165. struct convex_hull
  166. {
  167. template <typename OutputGeometry, typename Strategy>
  168. static inline void apply(Geometry const& geometry,
  169. OutputGeometry& out,
  170. Strategy const& strategy)
  171. {
  172. detail::convex_hull::input_geometry_proxy<Geometry> in_proxy(geometry);
  173. detail::convex_hull::graham_andrew
  174. <
  175. typename point_type<Geometry>::type
  176. >::apply(in_proxy, out, strategy);
  177. }
  178. };
  179. // A hull for boxes is trivial. Any strategy is (currently) skipped.
  180. // TODO: This is not correct in spherical and geographic CS.
  181. template <typename Box>
  182. struct convex_hull<Box, box_tag>
  183. {
  184. template <typename OutputGeometry, typename Strategy>
  185. static inline void apply(Box const& box,
  186. OutputGeometry& out,
  187. Strategy const& )
  188. {
  189. static bool const Close
  190. = geometry::closure<OutputGeometry>::value == closed;
  191. static bool const Reverse
  192. = geometry::point_order<OutputGeometry>::value == counterclockwise;
  193. std::array<typename point_type<OutputGeometry>::type, 4> arr;
  194. // TODO: This assigns only 2d cooridnates!
  195. // And it is also used in box_view<>!
  196. geometry::detail::assign_box_corners_oriented<Reverse>(box, arr);
  197. std::move(arr.begin(), arr.end(), range::back_inserter(out));
  198. if BOOST_GEOMETRY_CONSTEXPR (Close)
  199. {
  200. range::push_back(out, range::front(out));
  201. }
  202. }
  203. };
  204. template <typename GeometryCollection>
  205. struct convex_hull<GeometryCollection, geometry_collection_tag>
  206. {
  207. template <typename OutputGeometry, typename Strategy>
  208. static inline void apply(GeometryCollection const& geometry,
  209. OutputGeometry& out,
  210. Strategy const& strategy)
  211. {
  212. // Assuming that single point_type is used by the GeometryCollection
  213. using subgeometry_type = typename detail::first_geometry_type<GeometryCollection>::type;
  214. using point_type = typename geometry::point_type<subgeometry_type>::type;
  215. using ring_type = model::ring<point_type, true, false>;
  216. // Calculate box rings once
  217. std::vector<ring_type> box_rings;
  218. detail::visit_breadth_first([&](auto const& g)
  219. {
  220. convex_hull::add_ring_for_box(box_rings, g, strategy);
  221. return true;
  222. }, geometry);
  223. detail::convex_hull::input_geometry_collection_proxy
  224. <
  225. GeometryCollection, std::vector<ring_type>
  226. > in_proxy(geometry, box_rings);
  227. detail::convex_hull::graham_andrew
  228. <
  229. point_type
  230. >::apply(in_proxy, out, strategy);
  231. }
  232. private:
  233. template
  234. <
  235. typename Ring, typename SubGeometry, typename Strategy,
  236. std::enable_if_t<util::is_box<SubGeometry>::value, int> = 0
  237. >
  238. static inline void add_ring_for_box(std::vector<Ring> & rings, SubGeometry const& box,
  239. Strategy const& strategy)
  240. {
  241. Ring ring;
  242. convex_hull<SubGeometry>::apply(box, ring, strategy);
  243. rings.push_back(std::move(ring));
  244. }
  245. template
  246. <
  247. typename Ring, typename SubGeometry, typename Strategy,
  248. std::enable_if_t<! util::is_box<SubGeometry>::value, int> = 0
  249. >
  250. static inline void add_ring_for_box(std::vector<Ring> & , SubGeometry const& ,
  251. Strategy const& )
  252. {}
  253. };
  254. template <typename OutputGeometry, typename Tag = typename tag<OutputGeometry>::type>
  255. struct convex_hull_out
  256. {
  257. BOOST_GEOMETRY_STATIC_ASSERT_FALSE("This OutputGeometry is not supported.", OutputGeometry, Tag);
  258. };
  259. template <typename OutputGeometry>
  260. struct convex_hull_out<OutputGeometry, ring_tag>
  261. {
  262. template <typename Geometry, typename Strategies>
  263. static inline void apply(Geometry const& geometry,
  264. OutputGeometry& out,
  265. Strategies const& strategies)
  266. {
  267. dispatch::convex_hull<Geometry>::apply(geometry, out, strategies);
  268. }
  269. };
  270. template <typename OutputGeometry>
  271. struct convex_hull_out<OutputGeometry, polygon_tag>
  272. {
  273. template <typename Geometry, typename Strategies>
  274. static inline void apply(Geometry const& geometry,
  275. OutputGeometry& out,
  276. Strategies const& strategies)
  277. {
  278. auto&& ring = exterior_ring(out);
  279. dispatch::convex_hull<Geometry>::apply(geometry, ring, strategies);
  280. }
  281. };
  282. template <typename OutputGeometry>
  283. struct convex_hull_out<OutputGeometry, multi_polygon_tag>
  284. {
  285. template <typename Geometry, typename Strategies>
  286. static inline void apply(Geometry const& geometry,
  287. OutputGeometry& out,
  288. Strategies const& strategies)
  289. {
  290. typename boost::range_value<OutputGeometry>::type polygon;
  291. auto&& ring = exterior_ring(polygon);
  292. dispatch::convex_hull<Geometry>::apply(geometry, ring, strategies);
  293. // Empty input is checked so the output shouldn't be empty
  294. range::push_back(out, std::move(polygon));
  295. }
  296. };
  297. template <typename OutputGeometry>
  298. struct convex_hull_out<OutputGeometry, geometry_collection_tag>
  299. {
  300. using polygonal_t = typename util::sequence_min_element
  301. <
  302. typename traits::geometry_types<OutputGeometry>::type,
  303. detail::convex_hull::output_polygonal_less
  304. >::type;
  305. using linear_t = typename util::sequence_min_element
  306. <
  307. typename traits::geometry_types<OutputGeometry>::type,
  308. detail::convex_hull::output_linear_less
  309. >::type;
  310. using pointlike_t = typename util::sequence_min_element
  311. <
  312. typename traits::geometry_types<OutputGeometry>::type,
  313. detail::convex_hull::output_pointlike_less
  314. >::type;
  315. // select_element may define different kind of geometry than the one that is desired
  316. BOOST_GEOMETRY_STATIC_ASSERT(util::is_polygonal<polygonal_t>::value,
  317. "It must be possible to store polygonal geometry in OutputGeometry.", polygonal_t);
  318. BOOST_GEOMETRY_STATIC_ASSERT(util::is_linear<linear_t>::value,
  319. "It must be possible to store linear geometry in OutputGeometry.", linear_t);
  320. BOOST_GEOMETRY_STATIC_ASSERT(util::is_pointlike<pointlike_t>::value,
  321. "It must be possible to store pointlike geometry in OutputGeometry.", pointlike_t);
  322. template <typename Geometry, typename Strategies>
  323. static inline void apply(Geometry const& geometry,
  324. OutputGeometry& out,
  325. Strategies const& strategies)
  326. {
  327. polygonal_t polygonal;
  328. convex_hull_out<polygonal_t>::apply(geometry, polygonal, strategies);
  329. // Empty input is checked so the output shouldn't be empty
  330. auto&& out_ring = ring(polygonal);
  331. if (boost::size(out_ring) == detail::minimum_ring_size<polygonal_t>::value)
  332. {
  333. using detail::equals::equals_point_point;
  334. if (equals_point_point(range::front(out_ring), range::at(out_ring, 1), strategies))
  335. {
  336. pointlike_t pointlike;
  337. move_to_pointlike(out_ring, pointlike);
  338. move_to_out(pointlike, out);
  339. return;
  340. }
  341. if (equals_point_point(range::front(out_ring), range::at(out_ring, 2), strategies))
  342. {
  343. linear_t linear;
  344. move_to_linear(out_ring, linear);
  345. move_to_out(linear, out);
  346. return;
  347. }
  348. }
  349. move_to_out(polygonal, out);
  350. }
  351. private:
  352. template <typename Polygonal, util::enable_if_ring_t<Polygonal, int> = 0>
  353. static decltype(auto) ring(Polygonal const& polygonal)
  354. {
  355. return polygonal;
  356. }
  357. template <typename Polygonal, util::enable_if_polygon_t<Polygonal, int> = 0>
  358. static decltype(auto) ring(Polygonal const& polygonal)
  359. {
  360. return exterior_ring(polygonal);
  361. }
  362. template <typename Polygonal, util::enable_if_multi_polygon_t<Polygonal, int> = 0>
  363. static decltype(auto) ring(Polygonal const& polygonal)
  364. {
  365. return exterior_ring(range::front(polygonal));
  366. }
  367. template <typename Range, typename Linear, util::enable_if_segment_t<Linear, int> = 0>
  368. static void move_to_linear(Range & out_range, Linear & seg)
  369. {
  370. detail::assign_point_to_index<0>(range::front(out_range), seg);
  371. detail::assign_point_to_index<1>(range::at(out_range, 1), seg);
  372. }
  373. template <typename Range, typename Linear, util::enable_if_linestring_t<Linear, int> = 0>
  374. static void move_to_linear(Range & out_range, Linear & ls)
  375. {
  376. std::move(boost::begin(out_range), boost::begin(out_range) + 2, range::back_inserter(ls));
  377. }
  378. template <typename Range, typename Linear, util::enable_if_multi_linestring_t<Linear, int> = 0>
  379. static void move_to_linear(Range & out_range, Linear & mls)
  380. {
  381. typename boost::range_value<Linear>::type ls;
  382. std::move(boost::begin(out_range), boost::begin(out_range) + 2, range::back_inserter(ls));
  383. range::push_back(mls, std::move(ls));
  384. }
  385. template <typename Range, typename PointLike, util::enable_if_point_t<PointLike, int> = 0>
  386. static void move_to_pointlike(Range & out_range, PointLike & pt)
  387. {
  388. pt = range::front(out_range);
  389. }
  390. template <typename Range, typename PointLike, util::enable_if_multi_point_t<PointLike, int> = 0>
  391. static void move_to_pointlike(Range & out_range, PointLike & mpt)
  392. {
  393. range::push_back(mpt, std::move(range::front(out_range)));
  394. }
  395. template
  396. <
  397. typename Geometry, typename OutputGeometry_,
  398. util::enable_if_geometry_collection_t<OutputGeometry_, int> = 0
  399. >
  400. static void move_to_out(Geometry & g, OutputGeometry_ & out)
  401. {
  402. range::emplace_back(out, std::move(g));
  403. }
  404. template
  405. <
  406. typename Geometry, typename OutputGeometry_,
  407. util::enable_if_dynamic_geometry_t<OutputGeometry_, int> = 0
  408. >
  409. static void move_to_out(Geometry & g, OutputGeometry_ & out)
  410. {
  411. out = std::move(g);
  412. }
  413. };
  414. template <typename OutputGeometry>
  415. struct convex_hull_out<OutputGeometry, dynamic_geometry_tag>
  416. : convex_hull_out<OutputGeometry, geometry_collection_tag>
  417. {};
  418. // For backward compatibility
  419. template <typename OutputGeometry>
  420. struct convex_hull_out<OutputGeometry, linestring_tag>
  421. : convex_hull_out<OutputGeometry, ring_tag>
  422. {};
  423. } // namespace dispatch
  424. #endif // DOXYGEN_NO_DISPATCH
  425. namespace resolve_strategy {
  426. template <typename Strategies>
  427. struct convex_hull
  428. {
  429. template <typename Geometry, typename OutputGeometry>
  430. static inline void apply(Geometry const& geometry,
  431. OutputGeometry& out,
  432. Strategies const& strategies)
  433. {
  434. dispatch::convex_hull_out<OutputGeometry>::apply(geometry, out, strategies);
  435. }
  436. };
  437. template <>
  438. struct convex_hull<default_strategy>
  439. {
  440. template <typename Geometry, typename OutputGeometry>
  441. static inline void apply(Geometry const& geometry,
  442. OutputGeometry& out,
  443. default_strategy const&)
  444. {
  445. using strategy_type = typename detail::convex_hull::default_strategy
  446. <
  447. Geometry
  448. >::type;
  449. dispatch::convex_hull_out<OutputGeometry>::apply(geometry, out, strategy_type());
  450. }
  451. };
  452. } // namespace resolve_strategy
  453. namespace resolve_dynamic {
  454. template <typename Geometry, typename Tag = typename tag<Geometry>::type>
  455. struct convex_hull
  456. {
  457. template <typename OutputGeometry, typename Strategy>
  458. static inline void apply(Geometry const& geometry,
  459. OutputGeometry& out,
  460. Strategy const& strategy)
  461. {
  462. concepts::check_concepts_and_equal_dimensions<
  463. const Geometry,
  464. OutputGeometry
  465. >();
  466. resolve_strategy::convex_hull<Strategy>::apply(geometry, out, strategy);
  467. }
  468. };
  469. template <typename Geometry>
  470. struct convex_hull<Geometry, dynamic_geometry_tag>
  471. {
  472. template <typename OutputGeometry, typename Strategy>
  473. static inline void apply(Geometry const& geometry,
  474. OutputGeometry& out,
  475. Strategy const& strategy)
  476. {
  477. traits::visit<Geometry>::apply([&](auto const& g)
  478. {
  479. convex_hull<util::remove_cref_t<decltype(g)>>::apply(g, out, strategy);
  480. }, geometry);
  481. }
  482. };
  483. } // namespace resolve_dynamic
  484. /*!
  485. \brief \brief_calc{convex hull} \brief_strategy
  486. \ingroup convex_hull
  487. \details \details_calc{convex_hull,convex hull} \brief_strategy.
  488. \tparam Geometry the input geometry type
  489. \tparam OutputGeometry the output geometry type
  490. \tparam Strategy the strategy type
  491. \param geometry \param_geometry, input geometry
  492. \param out \param_geometry \param_set{convex hull}
  493. \param strategy \param_strategy{area}
  494. \qbk{distinguish,with strategy}
  495. \qbk{[include reference/algorithms/convex_hull.qbk]}
  496. */
  497. template<typename Geometry, typename OutputGeometry, typename Strategy>
  498. inline void convex_hull(Geometry const& geometry, OutputGeometry& out, Strategy const& strategy)
  499. {
  500. if (geometry::is_empty(geometry))
  501. {
  502. // Leave output empty
  503. return;
  504. }
  505. resolve_dynamic::convex_hull<Geometry>::apply(geometry, out, strategy);
  506. }
  507. /*!
  508. \brief \brief_calc{convex hull}
  509. \ingroup convex_hull
  510. \details \details_calc{convex_hull,convex hull}.
  511. \tparam Geometry the input geometry type
  512. \tparam OutputGeometry the output geometry type
  513. \param geometry \param_geometry, input geometry
  514. \param hull \param_geometry \param_set{convex hull}
  515. \qbk{[include reference/algorithms/convex_hull.qbk]}
  516. */
  517. template<typename Geometry, typename OutputGeometry>
  518. inline void convex_hull(Geometry const& geometry, OutputGeometry& hull)
  519. {
  520. geometry::convex_hull(geometry, hull, default_strategy());
  521. }
  522. }} // namespace boost::geometry
  523. #endif // BOOST_GEOMETRY_ALGORITHMS_CONVEX_HULL_INTERFACE_HPP