1 // Boost.Geometry (aka GGL, Generic Geometry Library)
3 // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
5 // This file was modified by Oracle on 2017.
6 // Modifications copyright (c) 2017, Oracle and/or its affiliates.
7 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
9 // Use, modification and distribution is subject to the Boost Software License,
10 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
11 // http://www.boost.org/LICENSE_1_0.txt)
13 #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSAL_RING_CREATOR_HPP
14 #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSAL_RING_CREATOR_HPP
18 #include <boost/range.hpp>
20 #include <boost/geometry/algorithms/detail/overlay/copy_segments.hpp>
21 #include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
22 #include <boost/geometry/algorithms/detail/overlay/traversal.hpp>
23 #include <boost/geometry/algorithms/num_points.hpp>
24 #include <boost/geometry/core/access.hpp>
25 #include <boost/geometry/core/assert.hpp>
26 #include <boost/geometry/core/closure.hpp>
28 namespace boost { namespace geometry
31 #ifndef DOXYGEN_NO_DETAIL
32 namespace detail { namespace overlay
40 overlay_type OverlayType,
46 typename IntersectionStrategy,
47 typename RobustPolicy,
51 struct traversal_ring_creator
55 Reverse1, Reverse2, OverlayType,
56 Geometry1, Geometry2, Turns, Clusters,
57 RobustPolicy, typename IntersectionStrategy::side_strategy_type,
61 typedef typename boost::range_value<Turns>::type turn_type;
62 typedef typename turn_type::turn_operation_type turn_operation_type;
64 static const operation_type target_operation
65 = operation_from_overlay<OverlayType>::value;
67 inline traversal_ring_creator(Geometry1 const& geometry1, Geometry2 const& geometry2,
68 Turns& turns, TurnInfoMap& turn_info_map,
69 Clusters const& clusters,
70 IntersectionStrategy const& intersection_strategy,
71 RobustPolicy const& robust_policy, Visitor& visitor)
72 : m_trav(geometry1, geometry2, turns, clusters,
73 robust_policy, intersection_strategy.get_side_strategy(),
75 , m_geometry1(geometry1)
76 , m_geometry2(geometry2)
78 , m_turn_info_map(turn_info_map)
79 , m_clusters(clusters)
80 , m_intersection_strategy(intersection_strategy)
81 , m_robust_policy(robust_policy)
86 template <typename Ring>
87 inline traverse_error_type travel_to_next_turn(signed_size_type start_turn_index,
89 signed_size_type& turn_index,
94 int const previous_op_index = op_index;
95 signed_size_type const previous_turn_index = turn_index;
96 turn_type& previous_turn = m_turns[turn_index];
97 turn_operation_type& previous_op = previous_turn.operations[op_index];
98 segment_identifier previous_seg_id;
100 signed_size_type to_vertex_index = -1;
101 if (! m_trav.select_turn_from_enriched(turn_index, previous_seg_id,
102 to_vertex_index, start_turn_index, start_op_index,
103 previous_turn, previous_op, is_start))
106 ? traverse_error_no_next_ip_at_start
107 : traverse_error_no_next_ip;
109 if (to_vertex_index >= 0)
111 if (previous_op.seg_id.source_index == 0)
113 geometry::copy_segments<Reverse1>(m_geometry1,
114 previous_op.seg_id, to_vertex_index,
115 m_intersection_strategy.get_side_strategy(),
116 m_robust_policy, current_ring);
120 geometry::copy_segments<Reverse2>(m_geometry2,
121 previous_op.seg_id, to_vertex_index,
122 m_intersection_strategy.get_side_strategy(),
123 m_robust_policy, current_ring);
127 if (m_turns[turn_index].discarded)
130 ? traverse_error_dead_end_at_start
131 : traverse_error_dead_end;
136 // Register the start
137 previous_op.visited.set_started();
138 m_visitor.visit_traverse(m_turns, previous_turn, previous_op, "Start");
141 if (! m_trav.select_turn(start_turn_index, start_op_index,
142 turn_index, op_index,
143 previous_op_index, previous_turn_index, previous_seg_id,
147 ? traverse_error_no_next_ip_at_start
148 : traverse_error_no_next_ip;
152 // Check operation (TODO: this might be redundant or should be catched before)
153 const turn_type& current_turn = m_turns[turn_index];
154 const turn_operation_type& op = current_turn.operations[op_index];
155 if (op.visited.finalized()
156 || m_trav.is_visited(current_turn, op, turn_index, op_index))
158 return traverse_error_visit_again;
162 // Update registration and append point
163 turn_type& current_turn = m_turns[turn_index];
164 turn_operation_type& op = current_turn.operations[op_index];
165 detail::overlay::append_no_dups_or_spikes(current_ring, current_turn.point,
166 m_intersection_strategy.get_side_strategy(),
169 // Register the visit
170 m_trav.set_visited(current_turn, op);
171 m_visitor.visit_traverse(m_turns, current_turn, op, "Visit");
173 return traverse_error_none;
176 template <typename Ring>
177 inline traverse_error_type traverse(Ring& ring,
178 signed_size_type start_turn_index, int start_op_index)
180 turn_type const& start_turn = m_turns[start_turn_index];
181 turn_operation_type& start_op = m_turns[start_turn_index].operations[start_op_index];
183 detail::overlay::append_no_dups_or_spikes(ring, start_turn.point,
184 m_intersection_strategy.get_side_strategy(),
187 signed_size_type current_turn_index = start_turn_index;
188 int current_op_index = start_op_index;
190 traverse_error_type error = travel_to_next_turn(start_turn_index,
192 current_turn_index, current_op_index,
195 if (error != traverse_error_none)
197 // This is not necessarily a problem, it happens for clustered turns
198 // which are "build in" or otherwise point inwards
202 if (current_turn_index == start_turn_index)
204 start_op.visited.set_finished();
205 m_visitor.visit_traverse(m_turns, m_turns[current_turn_index], start_op, "Early finish");
206 return traverse_error_none;
209 if (start_turn.is_clustered())
211 turn_type const& turn = m_turns[current_turn_index];
212 if (turn.cluster_id == start_turn.cluster_id)
214 turn_operation_type& op = m_turns[start_turn_index].operations[current_op_index];
215 op.visited.set_finished();
216 m_visitor.visit_traverse(m_turns, m_turns[current_turn_index], start_op, "Early finish (cluster)");
217 return traverse_error_none;
221 std::size_t const max_iterations = 2 + 2 * m_turns.size();
222 for (std::size_t i = 0; i <= max_iterations; i++)
224 // We assume clockwise polygons only, non self-intersecting, closed.
225 // However, the input might be different, and checking validity
226 // is up to the library user.
228 // Therefore we make here some sanity checks. If the input
229 // violates the assumptions, the output polygon will not be correct
230 // but the routine will stop and output the current polygon, and
231 // will continue with the next one.
233 // Below three reasons to stop.
234 error = travel_to_next_turn(start_turn_index, start_op_index,
235 current_turn_index, current_op_index,
238 if (error != traverse_error_none)
243 if (current_turn_index == start_turn_index
244 && current_op_index == start_op_index)
246 start_op.visited.set_finished();
247 m_visitor.visit_traverse(m_turns, start_turn, start_op, "Finish");
248 return traverse_error_none;
252 return traverse_error_endless_loop;
255 template <typename Rings>
256 void traverse_with_operation(turn_type const& start_turn,
257 std::size_t turn_index, int op_index,
258 Rings& rings, std::size_t& finalized_ring_size,
259 typename Backtrack::state_type& state)
261 typedef typename boost::range_value<Rings>::type ring_type;
263 turn_operation_type const& start_op = start_turn.operations[op_index];
265 if (! start_op.visited.none()
266 || ! start_op.enriched.startable
267 || start_op.visited.rejected()
268 || ! (start_op.operation == target_operation
269 || start_op.operation == detail::overlay::operation_continue))
275 traverse_error_type traverse_error = traverse(ring, turn_index, op_index);
277 if (traverse_error == traverse_error_none)
279 std::size_t const min_num_points
280 = core_detail::closure::minimum_ring_size
282 geometry::closure<ring_type>::value
285 if (geometry::num_points(ring) >= min_num_points)
287 clean_closing_dups_and_spikes(ring,
288 m_intersection_strategy.get_side_strategy(),
290 rings.push_back(ring);
292 m_trav.finalize_visit_info(m_turn_info_map);
293 finalized_ring_size++;
300 rings, ring, m_turns, start_turn,
301 m_turns[turn_index].operations[op_index],
303 m_geometry1, m_geometry2,
304 m_intersection_strategy, m_robust_policy,
309 template <typename Rings>
310 void iterate(Rings& rings, std::size_t& finalized_ring_size,
311 typename Backtrack::state_type& state)
313 for (std::size_t turn_index = 0; turn_index < m_turns.size(); ++turn_index)
315 turn_type const& turn = m_turns[turn_index];
317 if (turn.discarded || turn.blocked())
319 // Skip discarded and blocked turns
323 if (turn.both(operation_continue))
325 // Traverse only one turn, the one with the SMALLEST remaining distance
326 // to avoid skipping a turn in between, which can happen in rare cases
328 turn_operation_type const& op0 = turn.operations[0];
329 turn_operation_type const& op1 = turn.operations[1];
331 = op0.remaining_distance <= op1.remaining_distance ? 0 : 1;
333 traverse_with_operation(turn, turn_index, op_index,
334 rings, finalized_ring_size, state);
338 for (int op_index = 0; op_index < 2; op_index++)
340 traverse_with_operation(turn, turn_index, op_index,
341 rings, finalized_ring_size, state);
348 traversal_type m_trav;
350 Geometry1 const& m_geometry1;
351 Geometry2 const& m_geometry2;
353 TurnInfoMap& m_turn_info_map; // contains turn-info information per ring
354 Clusters const& m_clusters;
355 IntersectionStrategy const& m_intersection_strategy;
356 RobustPolicy const& m_robust_policy;
360 }} // namespace detail::overlay
361 #endif // DOXYGEN_NO_DETAIL
363 }} // namespace boost::geometry
365 #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSAL_RING_CREATOR_HPP