Chris@16
|
1 // Boost.Geometry (aka GGL, Generic Geometry Library)
|
Chris@16
|
2
|
Chris@16
|
3 // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
|
Chris@16
|
4
|
Chris@16
|
5 // Use, modification and distribution is subject to the Boost Software License,
|
Chris@16
|
6 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
Chris@16
|
7 // http://www.boost.org/LICENSE_1_0.txt)
|
Chris@16
|
8
|
Chris@16
|
9 #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSE_HPP
|
Chris@16
|
10 #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSE_HPP
|
Chris@16
|
11
|
Chris@16
|
12 #include <cstddef>
|
Chris@16
|
13
|
Chris@16
|
14 #include <boost/range.hpp>
|
Chris@16
|
15
|
Chris@16
|
16 #include <boost/geometry/algorithms/detail/overlay/backtrack_check_si.hpp>
|
Chris@16
|
17 #include <boost/geometry/algorithms/detail/overlay/copy_segments.hpp>
|
Chris@16
|
18 #include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
|
Chris@16
|
19 #include <boost/geometry/algorithms/num_points.hpp>
|
Chris@16
|
20 #include <boost/geometry/core/access.hpp>
|
Chris@16
|
21 #include <boost/geometry/core/closure.hpp>
|
Chris@16
|
22 #include <boost/geometry/core/coordinate_dimension.hpp>
|
Chris@16
|
23 #include <boost/geometry/geometries/concepts/check.hpp>
|
Chris@16
|
24
|
Chris@16
|
25 #if defined(BOOST_GEOMETRY_DEBUG_INTERSECTION) \
|
Chris@16
|
26 || defined(BOOST_GEOMETRY_OVERLAY_REPORT_WKT) \
|
Chris@16
|
27 || defined(BOOST_GEOMETRY_DEBUG_TRAVERSE)
|
Chris@16
|
28 # include <string>
|
Chris@16
|
29 # include <boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp>
|
Chris@16
|
30 # include <boost/geometry/io/wkt/wkt.hpp>
|
Chris@16
|
31 #endif
|
Chris@16
|
32
|
Chris@16
|
33 namespace boost { namespace geometry
|
Chris@16
|
34 {
|
Chris@16
|
35
|
Chris@16
|
36 #ifndef DOXYGEN_NO_DETAIL
|
Chris@16
|
37 namespace detail { namespace overlay
|
Chris@16
|
38 {
|
Chris@16
|
39
|
Chris@16
|
40 template <typename Turn, typename Operation>
|
Chris@16
|
41 #ifdef BOOST_GEOMETRY_DEBUG_TRAVERSE
|
Chris@101
|
42 inline void debug_traverse(Turn const& turn, Operation op,
|
Chris@16
|
43 std::string const& header)
|
Chris@16
|
44 {
|
Chris@16
|
45 std::cout << header
|
Chris@16
|
46 << " at " << op.seg_id
|
Chris@16
|
47 << " meth: " << method_char(turn.method)
|
Chris@16
|
48 << " op: " << operation_char(op.operation)
|
Chris@16
|
49 << " vis: " << visited_char(op.visited)
|
Chris@16
|
50 << " of: " << operation_char(turn.operations[0].operation)
|
Chris@16
|
51 << operation_char(turn.operations[1].operation)
|
Chris@16
|
52 << " " << geometry::wkt(turn.point)
|
Chris@16
|
53 << std::endl;
|
Chris@16
|
54
|
Chris@16
|
55 if (boost::contains(header, "Finished"))
|
Chris@16
|
56 {
|
Chris@16
|
57 std::cout << std::endl;
|
Chris@16
|
58 }
|
Chris@16
|
59 }
|
Chris@16
|
60 #else
|
Chris@16
|
61 inline void debug_traverse(Turn const& , Operation, const char*)
|
Chris@16
|
62 {
|
Chris@16
|
63 }
|
Chris@16
|
64 #endif
|
Chris@16
|
65
|
Chris@16
|
66
|
Chris@16
|
67 template <typename Info, typename Turn>
|
Chris@16
|
68 inline void set_visited_for_continue(Info& info, Turn const& turn)
|
Chris@16
|
69 {
|
Chris@16
|
70 // On "continue", set "visited" for ALL directions
|
Chris@16
|
71 if (turn.operation == detail::overlay::operation_continue)
|
Chris@16
|
72 {
|
Chris@16
|
73 for (typename boost::range_iterator
|
Chris@16
|
74 <
|
Chris@16
|
75 typename Info::container_type
|
Chris@16
|
76 >::type it = boost::begin(info.operations);
|
Chris@16
|
77 it != boost::end(info.operations);
|
Chris@16
|
78 ++it)
|
Chris@16
|
79 {
|
Chris@16
|
80 if (it->visited.none())
|
Chris@16
|
81 {
|
Chris@16
|
82 it->visited.set_visited();
|
Chris@16
|
83 }
|
Chris@16
|
84 }
|
Chris@16
|
85 }
|
Chris@16
|
86 }
|
Chris@16
|
87
|
Chris@16
|
88
|
Chris@16
|
89 template
|
Chris@16
|
90 <
|
Chris@16
|
91 bool Reverse1, bool Reverse2,
|
Chris@16
|
92 typename GeometryOut,
|
Chris@16
|
93 typename G1,
|
Chris@16
|
94 typename G2,
|
Chris@16
|
95 typename Turns,
|
Chris@101
|
96 typename IntersectionInfo,
|
Chris@101
|
97 typename RobustPolicy
|
Chris@16
|
98 >
|
Chris@16
|
99 inline bool assign_next_ip(G1 const& g1, G2 const& g2,
|
Chris@16
|
100 Turns& turns,
|
Chris@16
|
101 typename boost::range_iterator<Turns>::type& ip,
|
Chris@16
|
102 GeometryOut& current_output,
|
Chris@16
|
103 IntersectionInfo& info,
|
Chris@101
|
104 segment_identifier& seg_id,
|
Chris@101
|
105 RobustPolicy const& robust_policy)
|
Chris@16
|
106 {
|
Chris@16
|
107 info.visited.set_visited();
|
Chris@16
|
108 set_visited_for_continue(*ip, info);
|
Chris@16
|
109
|
Chris@16
|
110 // If there is no next IP on this segment
|
Chris@16
|
111 if (info.enriched.next_ip_index < 0)
|
Chris@16
|
112 {
|
Chris@101
|
113 if (info.enriched.travels_to_vertex_index < 0
|
Chris@16
|
114 || info.enriched.travels_to_ip_index < 0)
|
Chris@16
|
115 {
|
Chris@16
|
116 return false;
|
Chris@16
|
117 }
|
Chris@16
|
118
|
Chris@16
|
119 BOOST_ASSERT(info.enriched.travels_to_vertex_index >= 0);
|
Chris@16
|
120 BOOST_ASSERT(info.enriched.travels_to_ip_index >= 0);
|
Chris@16
|
121
|
Chris@16
|
122 if (info.seg_id.source_index == 0)
|
Chris@16
|
123 {
|
Chris@16
|
124 geometry::copy_segments<Reverse1>(g1, info.seg_id,
|
Chris@16
|
125 info.enriched.travels_to_vertex_index,
|
Chris@101
|
126 robust_policy,
|
Chris@16
|
127 current_output);
|
Chris@16
|
128 }
|
Chris@16
|
129 else
|
Chris@16
|
130 {
|
Chris@16
|
131 geometry::copy_segments<Reverse2>(g2, info.seg_id,
|
Chris@16
|
132 info.enriched.travels_to_vertex_index,
|
Chris@101
|
133 robust_policy,
|
Chris@16
|
134 current_output);
|
Chris@16
|
135 }
|
Chris@16
|
136 seg_id = info.seg_id;
|
Chris@16
|
137 ip = boost::begin(turns) + info.enriched.travels_to_ip_index;
|
Chris@16
|
138 }
|
Chris@16
|
139 else
|
Chris@16
|
140 {
|
Chris@16
|
141 ip = boost::begin(turns) + info.enriched.next_ip_index;
|
Chris@16
|
142 seg_id = info.seg_id;
|
Chris@16
|
143 }
|
Chris@16
|
144
|
Chris@101
|
145 detail::overlay::append_no_dups_or_spikes(current_output, ip->point,
|
Chris@101
|
146 robust_policy);
|
Chris@16
|
147
|
Chris@16
|
148 return true;
|
Chris@16
|
149 }
|
Chris@16
|
150
|
Chris@16
|
151
|
Chris@101
|
152 inline bool select_source(operation_type operation,
|
Chris@101
|
153 signed_index_type source1,
|
Chris@101
|
154 signed_index_type source2)
|
Chris@16
|
155 {
|
Chris@16
|
156 return (operation == operation_intersection && source1 != source2)
|
Chris@16
|
157 || (operation == operation_union && source1 == source2)
|
Chris@16
|
158 ;
|
Chris@16
|
159 }
|
Chris@16
|
160
|
Chris@16
|
161
|
Chris@16
|
162 template
|
Chris@16
|
163 <
|
Chris@16
|
164 typename Turn,
|
Chris@16
|
165 typename Iterator
|
Chris@16
|
166 >
|
Chris@16
|
167 inline bool select_next_ip(operation_type operation,
|
Chris@16
|
168 Turn& turn,
|
Chris@16
|
169 segment_identifier const& seg_id,
|
Chris@16
|
170 Iterator& selected)
|
Chris@16
|
171 {
|
Chris@16
|
172 if (turn.discarded)
|
Chris@16
|
173 {
|
Chris@16
|
174 return false;
|
Chris@16
|
175 }
|
Chris@16
|
176 bool has_tp = false;
|
Chris@16
|
177 selected = boost::end(turn.operations);
|
Chris@16
|
178 for (Iterator it = boost::begin(turn.operations);
|
Chris@16
|
179 it != boost::end(turn.operations);
|
Chris@16
|
180 ++it)
|
Chris@16
|
181 {
|
Chris@16
|
182 if (it->visited.started())
|
Chris@16
|
183 {
|
Chris@16
|
184 selected = it;
|
Chris@16
|
185 //std::cout << " RETURN";
|
Chris@16
|
186 return true;
|
Chris@16
|
187 }
|
Chris@16
|
188
|
Chris@16
|
189 // In some cases there are two alternatives.
|
Chris@16
|
190 // For "ii", take the other one (alternate)
|
Chris@16
|
191 // UNLESS the other one is already visited
|
Chris@16
|
192 // For "uu", take the same one (see above);
|
Chris@16
|
193 // For "cc", take either one, but if there is a starting one,
|
Chris@16
|
194 // take that one.
|
Chris@16
|
195 if ( (it->operation == operation_continue
|
Chris@16
|
196 && (! has_tp || it->visited.started()
|
Chris@16
|
197 )
|
Chris@16
|
198 )
|
Chris@16
|
199 || (it->operation == operation
|
Chris@16
|
200 && ! it->visited.finished()
|
Chris@16
|
201 && (! has_tp
|
Chris@16
|
202 || select_source(operation,
|
Chris@16
|
203 it->seg_id.source_index, seg_id.source_index)
|
Chris@16
|
204 )
|
Chris@16
|
205 )
|
Chris@16
|
206 )
|
Chris@16
|
207 {
|
Chris@16
|
208 selected = it;
|
Chris@16
|
209 debug_traverse(turn, *it, " Candidate");
|
Chris@16
|
210 has_tp = true;
|
Chris@16
|
211 }
|
Chris@16
|
212 }
|
Chris@16
|
213
|
Chris@16
|
214 if (has_tp)
|
Chris@16
|
215 {
|
Chris@16
|
216 debug_traverse(turn, *selected, " Accepted");
|
Chris@16
|
217 }
|
Chris@16
|
218
|
Chris@16
|
219
|
Chris@16
|
220 return has_tp;
|
Chris@16
|
221 }
|
Chris@16
|
222
|
Chris@16
|
223
|
Chris@16
|
224
|
Chris@16
|
225 /*!
|
Chris@16
|
226 \brief Traverses through intersection points / geometries
|
Chris@16
|
227 \ingroup overlay
|
Chris@16
|
228 */
|
Chris@16
|
229 template
|
Chris@16
|
230 <
|
Chris@16
|
231 bool Reverse1, bool Reverse2,
|
Chris@16
|
232 typename Geometry1,
|
Chris@16
|
233 typename Geometry2,
|
Chris@16
|
234 typename Backtrack = backtrack_check_self_intersections<Geometry1, Geometry2>
|
Chris@16
|
235 >
|
Chris@16
|
236 class traverse
|
Chris@16
|
237 {
|
Chris@16
|
238 public :
|
Chris@101
|
239 template <typename RobustPolicy, typename Turns, typename Rings>
|
Chris@16
|
240 static inline void apply(Geometry1 const& geometry1,
|
Chris@16
|
241 Geometry2 const& geometry2,
|
Chris@16
|
242 detail::overlay::operation_type operation,
|
Chris@101
|
243 RobustPolicy const& robust_policy,
|
Chris@16
|
244 Turns& turns, Rings& rings)
|
Chris@16
|
245 {
|
Chris@16
|
246 typedef typename boost::range_value<Rings>::type ring_type;
|
Chris@16
|
247 typedef typename boost::range_iterator<Turns>::type turn_iterator;
|
Chris@16
|
248 typedef typename boost::range_value<Turns>::type turn_type;
|
Chris@16
|
249 typedef typename boost::range_iterator
|
Chris@16
|
250 <
|
Chris@16
|
251 typename turn_type::container_type
|
Chris@16
|
252 >::type turn_operation_iterator_type;
|
Chris@16
|
253
|
Chris@16
|
254 std::size_t const min_num_points
|
Chris@16
|
255 = core_detail::closure::minimum_ring_size
|
Chris@16
|
256 <
|
Chris@16
|
257 geometry::closure<ring_type>::value
|
Chris@16
|
258 >::value;
|
Chris@16
|
259
|
Chris@16
|
260 std::size_t size_at_start = boost::size(rings);
|
Chris@16
|
261
|
Chris@16
|
262 typename Backtrack::state_type state;
|
Chris@16
|
263 do
|
Chris@16
|
264 {
|
Chris@16
|
265 state.reset();
|
Chris@16
|
266
|
Chris@16
|
267 // Iterate through all unvisited points
|
Chris@16
|
268 for (turn_iterator it = boost::begin(turns);
|
Chris@16
|
269 state.good() && it != boost::end(turns);
|
Chris@16
|
270 ++it)
|
Chris@16
|
271 {
|
Chris@16
|
272 // Skip discarded ones
|
Chris@101
|
273 if (! (it->discarded || ! it->selectable_start || it->blocked()))
|
Chris@16
|
274 {
|
Chris@16
|
275 for (turn_operation_iterator_type iit = boost::begin(it->operations);
|
Chris@16
|
276 state.good() && iit != boost::end(it->operations);
|
Chris@16
|
277 ++iit)
|
Chris@16
|
278 {
|
Chris@16
|
279 if (iit->visited.none()
|
Chris@16
|
280 && ! iit->visited.rejected()
|
Chris@16
|
281 && (iit->operation == operation
|
Chris@16
|
282 || iit->operation == detail::overlay::operation_continue)
|
Chris@16
|
283 )
|
Chris@16
|
284 {
|
Chris@16
|
285 set_visited_for_continue(*it, *iit);
|
Chris@16
|
286
|
Chris@16
|
287 ring_type current_output;
|
Chris@101
|
288 detail::overlay::append_no_dups_or_spikes(current_output,
|
Chris@101
|
289 it->point, robust_policy);
|
Chris@16
|
290
|
Chris@16
|
291 turn_iterator current = it;
|
Chris@16
|
292 turn_operation_iterator_type current_iit = iit;
|
Chris@16
|
293 segment_identifier current_seg_id;
|
Chris@16
|
294
|
Chris@16
|
295 if (! detail::overlay::assign_next_ip<Reverse1, Reverse2>(
|
Chris@16
|
296 geometry1, geometry2,
|
Chris@16
|
297 turns,
|
Chris@16
|
298 current, current_output,
|
Chris@101
|
299 *iit, current_seg_id,
|
Chris@101
|
300 robust_policy))
|
Chris@16
|
301 {
|
Chris@16
|
302 Backtrack::apply(
|
Chris@101
|
303 size_at_start,
|
Chris@16
|
304 rings, current_output, turns, *current_iit,
|
Chris@16
|
305 "No next IP",
|
Chris@101
|
306 geometry1, geometry2, robust_policy, state);
|
Chris@16
|
307 }
|
Chris@16
|
308
|
Chris@16
|
309 if (! detail::overlay::select_next_ip(
|
Chris@16
|
310 operation,
|
Chris@16
|
311 *current,
|
Chris@16
|
312 current_seg_id,
|
Chris@16
|
313 current_iit))
|
Chris@16
|
314 {
|
Chris@16
|
315 Backtrack::apply(
|
Chris@101
|
316 size_at_start,
|
Chris@16
|
317 rings, current_output, turns, *iit,
|
Chris@16
|
318 "Dead end at start",
|
Chris@101
|
319 geometry1, geometry2, robust_policy, state);
|
Chris@16
|
320 }
|
Chris@16
|
321 else
|
Chris@16
|
322 {
|
Chris@16
|
323
|
Chris@16
|
324 iit->visited.set_started();
|
Chris@16
|
325 detail::overlay::debug_traverse(*it, *iit, "-> Started");
|
Chris@16
|
326 detail::overlay::debug_traverse(*current, *current_iit, "Selected ");
|
Chris@16
|
327
|
Chris@16
|
328
|
Chris@101
|
329 typename boost::range_size<Turns>::type i = 0;
|
Chris@16
|
330
|
Chris@16
|
331 while (current_iit != iit && state.good())
|
Chris@16
|
332 {
|
Chris@16
|
333 if (current_iit->visited.visited())
|
Chris@16
|
334 {
|
Chris@16
|
335 // It visits a visited node again, without passing the start node.
|
Chris@16
|
336 // This makes it suspicious for endless loops
|
Chris@16
|
337 Backtrack::apply(
|
Chris@101
|
338 size_at_start,
|
Chris@16
|
339 rings, current_output, turns, *iit,
|
Chris@16
|
340 "Visit again",
|
Chris@101
|
341 geometry1, geometry2, robust_policy, state);
|
Chris@16
|
342 }
|
Chris@16
|
343 else
|
Chris@16
|
344 {
|
Chris@16
|
345
|
Chris@16
|
346
|
Chris@16
|
347 // We assume clockwise polygons only, non self-intersecting, closed.
|
Chris@16
|
348 // However, the input might be different, and checking validity
|
Chris@16
|
349 // is up to the library user.
|
Chris@16
|
350
|
Chris@16
|
351 // Therefore we make here some sanity checks. If the input
|
Chris@16
|
352 // violates the assumptions, the output polygon will not be correct
|
Chris@16
|
353 // but the routine will stop and output the current polygon, and
|
Chris@16
|
354 // will continue with the next one.
|
Chris@16
|
355
|
Chris@16
|
356 // Below three reasons to stop.
|
Chris@16
|
357 detail::overlay::assign_next_ip<Reverse1, Reverse2>(
|
Chris@16
|
358 geometry1, geometry2,
|
Chris@16
|
359 turns, current, current_output,
|
Chris@101
|
360 *current_iit, current_seg_id,
|
Chris@101
|
361 robust_policy);
|
Chris@16
|
362
|
Chris@16
|
363 if (! detail::overlay::select_next_ip(
|
Chris@16
|
364 operation,
|
Chris@16
|
365 *current,
|
Chris@16
|
366 current_seg_id,
|
Chris@16
|
367 current_iit))
|
Chris@16
|
368 {
|
Chris@16
|
369 // Should not occur in valid (non-self-intersecting) polygons
|
Chris@16
|
370 // Should not occur in self-intersecting polygons without spikes
|
Chris@16
|
371 // Might occur in polygons with spikes
|
Chris@16
|
372 Backtrack::apply(
|
Chris@101
|
373 size_at_start,
|
Chris@16
|
374 rings, current_output, turns, *iit,
|
Chris@16
|
375 "Dead end",
|
Chris@101
|
376 geometry1, geometry2, robust_policy, state);
|
Chris@16
|
377 }
|
Chris@16
|
378 else
|
Chris@16
|
379 {
|
Chris@16
|
380 detail::overlay::debug_traverse(*current, *current_iit, "Selected ");
|
Chris@16
|
381 }
|
Chris@16
|
382
|
Chris@16
|
383 if (i++ > 2 + 2 * turns.size())
|
Chris@16
|
384 {
|
Chris@16
|
385 // Sanity check: there may be never more loops
|
Chris@16
|
386 // than turn points.
|
Chris@16
|
387 // Turn points marked as "ii" can be visited twice.
|
Chris@16
|
388 Backtrack::apply(
|
Chris@101
|
389 size_at_start,
|
Chris@16
|
390 rings, current_output, turns, *iit,
|
Chris@16
|
391 "Endless loop",
|
Chris@101
|
392 geometry1, geometry2, robust_policy, state);
|
Chris@16
|
393 }
|
Chris@16
|
394 }
|
Chris@16
|
395 }
|
Chris@16
|
396
|
Chris@16
|
397 if (state.good())
|
Chris@16
|
398 {
|
Chris@16
|
399 iit->visited.set_finished();
|
Chris@16
|
400 detail::overlay::debug_traverse(*current, *iit, "->Finished");
|
Chris@16
|
401 if (geometry::num_points(current_output) >= min_num_points)
|
Chris@16
|
402 {
|
Chris@101
|
403 clean_closing_dups_and_spikes(current_output, robust_policy);
|
Chris@16
|
404 rings.push_back(current_output);
|
Chris@16
|
405 }
|
Chris@16
|
406 }
|
Chris@16
|
407 }
|
Chris@16
|
408 }
|
Chris@16
|
409 }
|
Chris@16
|
410 }
|
Chris@16
|
411 }
|
Chris@16
|
412 } while (! state.good());
|
Chris@16
|
413 }
|
Chris@16
|
414 };
|
Chris@16
|
415
|
Chris@16
|
416 }} // namespace detail::overlay
|
Chris@16
|
417 #endif // DOXYGEN_NO_DETAIL
|
Chris@16
|
418
|
Chris@16
|
419 }} // namespace boost::geometry
|
Chris@16
|
420
|
Chris@16
|
421 #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSE_HPP
|