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