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