1 // Boost.Geometry (aka GGL, Generic Geometry Library)
3 // Copyright (c) 2012-2014 Barend Gehrels, Amsterdam, the Netherlands.
5 // This file was modified by Oracle on 2017, 2018.
6 // Modifications copyright (c) 2017-2018, Oracle and/or its affiliates.
8 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
10 // Use, modification and distribution is subject to the Boost Software License,
11 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
12 // http://www.boost.org/LICENSE_1_0.txt)
14 #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_GET_LEFT_TURNS_HPP
15 #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_GET_LEFT_TURNS_HPP
20 #include <boost/geometry/core/assert.hpp>
22 #include <boost/geometry/algorithms/detail/overlay/segment_identifier.hpp>
23 #include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
24 #include <boost/geometry/arithmetic/arithmetic.hpp>
25 #include <boost/geometry/iterators/closing_iterator.hpp>
26 #include <boost/geometry/iterators/ever_circling_iterator.hpp>
27 #include <boost/geometry/strategies/side.hpp>
29 namespace boost { namespace geometry
33 #ifndef DOXYGEN_NO_DETAIL
37 // TODO: move this to /util/
39 inline std::pair<T, T> ordered_pair(T const& first, T const& second)
41 return first < second ? std::make_pair(first, second) : std::make_pair(second, first);
49 template <typename Vector>
50 inline int get_quadrant(Vector const& vector)
52 // Return quadrant as layouted in the code below:
56 return geometry::get<1>(vector) >= 0
57 ? (geometry::get<0>(vector) < 0 ? 3 : 0)
58 : (geometry::get<0>(vector) < 0 ? 2 : 1)
62 template <typename Vector>
63 inline int squared_length(Vector const& vector)
65 return geometry::get<0>(vector) * geometry::get<0>(vector)
66 + geometry::get<1>(vector) * geometry::get<1>(vector)
71 template <typename Point, typename SideStrategy>
74 typedef Point vector_type;
76 angle_less(Point const& origin, SideStrategy const& strategy)
78 , m_strategy(strategy)
81 template <typename Angle>
82 inline bool operator()(Angle const& p, Angle const& q) const
84 // Vector origin -> p and origin -> q
85 vector_type pv = p.point;
86 vector_type qv = q.point;
87 geometry::subtract_point(pv, m_origin);
88 geometry::subtract_point(qv, m_origin);
90 int const quadrant_p = get_quadrant(pv);
91 int const quadrant_q = get_quadrant(qv);
92 if (quadrant_p != quadrant_q)
94 return quadrant_p < quadrant_q;
96 // Same quadrant, check if p is located left of q
97 int const side = m_strategy.apply(m_origin, q.point, p.point);
102 // Collinear, check if one is incoming, incoming angles come first
103 if (p.incoming != q.incoming)
105 return int(p.incoming) < int(q.incoming);
107 // Same quadrant/side/direction, return longest first
108 // TODO: maybe not necessary, decide this
109 int const length_p = squared_length(pv);
110 int const length_q = squared_length(qv);
111 if (length_p != length_q)
113 return squared_length(pv) > squared_length(qv);
115 // They are still the same. Just compare on seg_id
116 return p.seg_id < q.seg_id;
121 SideStrategy m_strategy;
124 template <typename Point, typename SideStrategy>
125 struct angle_equal_to
127 typedef Point vector_type;
129 inline angle_equal_to(Point const& origin, SideStrategy const& strategy)
131 , m_strategy(strategy)
134 template <typename Angle>
135 inline bool operator()(Angle const& p, Angle const& q) const
137 // Vector origin -> p and origin -> q
138 vector_type pv = p.point;
139 vector_type qv = q.point;
140 geometry::subtract_point(pv, m_origin);
141 geometry::subtract_point(qv, m_origin);
143 if (get_quadrant(pv) != get_quadrant(qv))
147 // Same quadrant, check if p/q are collinear
148 int const side = m_strategy.apply(m_origin, q.point, p.point);
154 SideStrategy m_strategy;
157 template <typename AngleCollection, typename Turns>
158 inline void get_left_turns(AngleCollection const& sorted_angles,
161 std::set<std::size_t> good_incoming;
162 std::set<std::size_t> good_outgoing;
164 for (typename boost::range_iterator<AngleCollection const>::type it =
165 sorted_angles.begin(); it != sorted_angles.end(); ++it)
171 good_incoming.insert(it->turn_index);
175 good_outgoing.insert(it->turn_index);
180 if (good_incoming.empty() || good_outgoing.empty())
185 for (typename boost::range_iterator<AngleCollection const>::type it =
186 sorted_angles.begin(); it != sorted_angles.end(); ++it)
188 if (good_incoming.count(it->turn_index) == 0
189 || good_outgoing.count(it->turn_index) == 0)
191 turns[it->turn_index].remove_on_multi = true;
197 //! Returns the number of clusters
198 template <typename Point, typename AngleCollection, typename SideStrategy>
199 inline std::size_t assign_cluster_indices(AngleCollection& sorted, Point const& origin,
200 SideStrategy const& strategy)
202 // Assign same cluster_index for all turns in same direction
203 BOOST_GEOMETRY_ASSERT(boost::size(sorted) >= 4u);
205 angle_equal_to<Point, SideStrategy> comparator(origin, strategy);
206 typename boost::range_iterator<AngleCollection>::type it = sorted.begin();
208 std::size_t cluster_index = 0;
209 it->cluster_index = cluster_index;
210 typename boost::range_iterator<AngleCollection>::type previous = it++;
211 for (; it != sorted.end(); ++it)
213 if (!comparator(*previous, *it))
218 it->cluster_index = cluster_index;
220 return cluster_index + 1;
223 template <typename AngleCollection>
224 inline void block_turns(AngleCollection& sorted, std::size_t cluster_size)
226 BOOST_GEOMETRY_ASSERT(boost::size(sorted) >= 4u && cluster_size > 0);
228 std::vector<std::pair<bool, bool> > directions;
229 for (std::size_t i = 0; i < cluster_size; i++)
231 directions.push_back(std::make_pair(false, false));
234 for (typename boost::range_iterator<AngleCollection const>::type it = sorted.begin();
235 it != sorted.end(); ++it)
239 directions[it->cluster_index].first = true;
243 directions[it->cluster_index].second = true;
247 for (typename boost::range_iterator<AngleCollection>::type it = sorted.begin();
248 it != sorted.end(); ++it)
250 std::size_t const cluster_index = it->cluster_index;
251 std::size_t const previous_index
252 = cluster_index == 0 ? cluster_size - 1 : cluster_index - 1;
253 std::size_t const next_index
254 = cluster_index + 1 >= cluster_size ? 0 : cluster_index + 1;
256 if (directions[cluster_index].first
257 && directions[cluster_index].second)
261 else if (!directions[cluster_index].first
262 && directions[cluster_index].second
263 && directions[previous_index].second)
265 // Only outgoing, previous was also outgoing: block this one
268 else if (directions[cluster_index].first
269 && !directions[cluster_index].second
270 && !directions[previous_index].first
271 && directions[previous_index].second)
273 // Only incoming, previous was only outgoing: block this one
276 else if (directions[cluster_index].first
277 && !directions[cluster_index].second
278 && directions[next_index].first
279 && !directions[next_index].second)
281 // Only incoming, next also incoming, block this one
287 #if defined(BOOST_GEOMETRY_BUFFER_ENLARGED_CLUSTERS)
288 template <typename AngleCollection, typename Point>
289 inline bool has_rounding_issues(AngleCollection const& angles, Point const& origin)
291 for (typename boost::range_iterator<AngleCollection const>::type it =
292 angles.begin(); it != angles.end(); ++it)
294 // Vector origin -> p and origin -> q
295 typedef Point vector_type;
296 vector_type v = it->point;
297 geometry::subtract_point(v, origin);
298 return geometry::math::abs(geometry::get<0>(v)) <= 1
299 || geometry::math::abs(geometry::get<1>(v)) <= 1
307 } // namespace left_turns
309 } // namespace detail
310 #endif // DOXYGEN_NO_DETAIL
313 }} // namespace boost::geometry
315 #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_GET_LEFT_TURNS_HPP