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