get_left_turns.hpp 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Copyright (c) 2012-2014 Barend Gehrels, Amsterdam, the Netherlands.
  3. // This file was modified by Oracle on 2017, 2018.
  4. // Modifications copyright (c) 2017-2018, Oracle and/or its affiliates.
  5. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
  6. // Use, modification and distribution is subject to the Boost Software License,
  7. // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  8. // http://www.boost.org/LICENSE_1_0.txt)
  9. #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_GET_LEFT_TURNS_HPP
  10. #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_GET_LEFT_TURNS_HPP
  11. #include <set>
  12. #include <vector>
  13. #include <boost/geometry/core/assert.hpp>
  14. #include <boost/geometry/algorithms/detail/overlay/segment_identifier.hpp>
  15. #include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
  16. #include <boost/geometry/arithmetic/arithmetic.hpp>
  17. #include <boost/geometry/iterators/closing_iterator.hpp>
  18. #include <boost/geometry/iterators/ever_circling_iterator.hpp>
  19. #include <boost/geometry/strategies/side.hpp>
  20. namespace boost { namespace geometry
  21. {
  22. #ifndef DOXYGEN_NO_DETAIL
  23. namespace detail
  24. {
  25. // TODO: move this to /util/
  26. template <typename T>
  27. inline std::pair<T, T> ordered_pair(T const& first, T const& second)
  28. {
  29. return first < second ? std::make_pair(first, second) : std::make_pair(second, first);
  30. }
  31. namespace left_turns
  32. {
  33. template <typename Vector>
  34. inline int get_quadrant(Vector const& vector)
  35. {
  36. // Return quadrant as layouted in the code below:
  37. // 3 | 0
  38. // -----
  39. // 2 | 1
  40. return geometry::get<1>(vector) >= 0
  41. ? (geometry::get<0>(vector) < 0 ? 3 : 0)
  42. : (geometry::get<0>(vector) < 0 ? 2 : 1)
  43. ;
  44. }
  45. template <typename Vector>
  46. inline int squared_length(Vector const& vector)
  47. {
  48. return geometry::get<0>(vector) * geometry::get<0>(vector)
  49. + geometry::get<1>(vector) * geometry::get<1>(vector)
  50. ;
  51. }
  52. template <typename Point, typename SideStrategy>
  53. struct angle_less
  54. {
  55. typedef Point vector_type;
  56. angle_less(Point const& origin, SideStrategy const& strategy)
  57. : m_origin(origin)
  58. , m_strategy(strategy)
  59. {}
  60. template <typename Angle>
  61. inline bool operator()(Angle const& p, Angle const& q) const
  62. {
  63. // Vector origin -> p and origin -> q
  64. vector_type pv = p.point;
  65. vector_type qv = q.point;
  66. geometry::subtract_point(pv, m_origin);
  67. geometry::subtract_point(qv, m_origin);
  68. int const quadrant_p = get_quadrant(pv);
  69. int const quadrant_q = get_quadrant(qv);
  70. if (quadrant_p != quadrant_q)
  71. {
  72. return quadrant_p < quadrant_q;
  73. }
  74. // Same quadrant, check if p is located left of q
  75. int const side = m_strategy.apply(m_origin, q.point, p.point);
  76. if (side != 0)
  77. {
  78. return side == 1;
  79. }
  80. // Collinear, check if one is incoming, incoming angles come first
  81. if (p.incoming != q.incoming)
  82. {
  83. return int(p.incoming) < int(q.incoming);
  84. }
  85. // Same quadrant/side/direction, return longest first
  86. // TODO: maybe not necessary, decide this
  87. int const length_p = squared_length(pv);
  88. int const length_q = squared_length(qv);
  89. if (length_p != length_q)
  90. {
  91. return squared_length(pv) > squared_length(qv);
  92. }
  93. // They are still the same. Just compare on seg_id
  94. return p.seg_id < q.seg_id;
  95. }
  96. private:
  97. Point m_origin;
  98. SideStrategy m_strategy;
  99. };
  100. template <typename Point, typename SideStrategy>
  101. struct angle_equal_to
  102. {
  103. typedef Point vector_type;
  104. inline angle_equal_to(Point const& origin, SideStrategy const& strategy)
  105. : m_origin(origin)
  106. , m_strategy(strategy)
  107. {}
  108. template <typename Angle>
  109. inline bool operator()(Angle const& p, Angle const& q) const
  110. {
  111. // Vector origin -> p and origin -> q
  112. vector_type pv = p.point;
  113. vector_type qv = q.point;
  114. geometry::subtract_point(pv, m_origin);
  115. geometry::subtract_point(qv, m_origin);
  116. if (get_quadrant(pv) != get_quadrant(qv))
  117. {
  118. return false;
  119. }
  120. // Same quadrant, check if p/q are collinear
  121. int const side = m_strategy.apply(m_origin, q.point, p.point);
  122. return side == 0;
  123. }
  124. private:
  125. Point m_origin;
  126. SideStrategy m_strategy;
  127. };
  128. template <typename AngleCollection, typename Turns>
  129. inline void get_left_turns(AngleCollection const& sorted_angles,
  130. Turns& turns)
  131. {
  132. std::set<std::size_t> good_incoming;
  133. std::set<std::size_t> good_outgoing;
  134. for (typename boost::range_iterator<AngleCollection const>::type it =
  135. sorted_angles.begin(); it != sorted_angles.end(); ++it)
  136. {
  137. if (!it->blocked)
  138. {
  139. if (it->incoming)
  140. {
  141. good_incoming.insert(it->turn_index);
  142. }
  143. else
  144. {
  145. good_outgoing.insert(it->turn_index);
  146. }
  147. }
  148. }
  149. if (good_incoming.empty() || good_outgoing.empty())
  150. {
  151. return;
  152. }
  153. for (typename boost::range_iterator<AngleCollection const>::type it =
  154. sorted_angles.begin(); it != sorted_angles.end(); ++it)
  155. {
  156. if (good_incoming.count(it->turn_index) == 0
  157. || good_outgoing.count(it->turn_index) == 0)
  158. {
  159. turns[it->turn_index].remove_on_multi = true;
  160. }
  161. }
  162. }
  163. //! Returns the number of clusters
  164. template <typename Point, typename AngleCollection, typename SideStrategy>
  165. inline std::size_t assign_cluster_indices(AngleCollection& sorted, Point const& origin,
  166. SideStrategy const& strategy)
  167. {
  168. // Assign same cluster_index for all turns in same direction
  169. BOOST_GEOMETRY_ASSERT(boost::size(sorted) >= 4u);
  170. angle_equal_to<Point, SideStrategy> comparator(origin, strategy);
  171. typename boost::range_iterator<AngleCollection>::type it = sorted.begin();
  172. std::size_t cluster_index = 0;
  173. it->cluster_index = cluster_index;
  174. typename boost::range_iterator<AngleCollection>::type previous = it++;
  175. for (; it != sorted.end(); ++it)
  176. {
  177. if (!comparator(*previous, *it))
  178. {
  179. cluster_index++;
  180. previous = it;
  181. }
  182. it->cluster_index = cluster_index;
  183. }
  184. return cluster_index + 1;
  185. }
  186. template <typename AngleCollection>
  187. inline void block_turns(AngleCollection& sorted, std::size_t cluster_size)
  188. {
  189. BOOST_GEOMETRY_ASSERT(boost::size(sorted) >= 4u && cluster_size > 0);
  190. std::vector<std::pair<bool, bool> > directions;
  191. for (std::size_t i = 0; i < cluster_size; i++)
  192. {
  193. directions.push_back(std::make_pair(false, false));
  194. }
  195. for (typename boost::range_iterator<AngleCollection const>::type it = sorted.begin();
  196. it != sorted.end(); ++it)
  197. {
  198. if (it->incoming)
  199. {
  200. directions[it->cluster_index].first = true;
  201. }
  202. else
  203. {
  204. directions[it->cluster_index].second = true;
  205. }
  206. }
  207. for (typename boost::range_iterator<AngleCollection>::type it = sorted.begin();
  208. it != sorted.end(); ++it)
  209. {
  210. std::size_t const cluster_index = it->cluster_index;
  211. std::size_t const previous_index
  212. = cluster_index == 0 ? cluster_size - 1 : cluster_index - 1;
  213. std::size_t const next_index
  214. = cluster_index + 1 >= cluster_size ? 0 : cluster_index + 1;
  215. if (directions[cluster_index].first
  216. && directions[cluster_index].second)
  217. {
  218. it->blocked = true;
  219. }
  220. else if (!directions[cluster_index].first
  221. && directions[cluster_index].second
  222. && directions[previous_index].second)
  223. {
  224. // Only outgoing, previous was also outgoing: block this one
  225. it->blocked = true;
  226. }
  227. else if (directions[cluster_index].first
  228. && !directions[cluster_index].second
  229. && !directions[previous_index].first
  230. && directions[previous_index].second)
  231. {
  232. // Only incoming, previous was only outgoing: block this one
  233. it->blocked = true;
  234. }
  235. else if (directions[cluster_index].first
  236. && !directions[cluster_index].second
  237. && directions[next_index].first
  238. && !directions[next_index].second)
  239. {
  240. // Only incoming, next also incoming, block this one
  241. it->blocked = true;
  242. }
  243. }
  244. }
  245. #if defined(BOOST_GEOMETRY_BUFFER_ENLARGED_CLUSTERS)
  246. template <typename AngleCollection, typename Point>
  247. inline bool has_rounding_issues(AngleCollection const& angles, Point const& origin)
  248. {
  249. for (typename boost::range_iterator<AngleCollection const>::type it =
  250. angles.begin(); it != angles.end(); ++it)
  251. {
  252. // Vector origin -> p and origin -> q
  253. typedef Point vector_type;
  254. vector_type v = it->point;
  255. geometry::subtract_point(v, origin);
  256. return geometry::math::abs(geometry::get<0>(v)) <= 1
  257. || geometry::math::abs(geometry::get<1>(v)) <= 1
  258. ;
  259. }
  260. return false;
  261. }
  262. #endif
  263. } // namespace left_turns
  264. } // namespace detail
  265. #endif // DOXYGEN_NO_DETAIL
  266. }} // namespace boost::geometry
  267. #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_GET_LEFT_TURNS_HPP