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