// Boost.Geometry (aka GGL, Generic Geometry Library) // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. // This file was modified by Oracle on 2017. // Modifications copyright (c) 2017, Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSAL_RING_CREATOR_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSAL_RING_CREATOR_HPP #include #include #include #include #include #include #include #include #include namespace boost { namespace geometry { #ifndef DOXYGEN_NO_DETAIL namespace detail { namespace overlay { template < bool Reverse1, bool Reverse2, overlay_type OverlayType, typename Geometry1, typename Geometry2, typename Turns, typename TurnInfoMap, typename Clusters, typename IntersectionStrategy, typename RobustPolicy, typename Visitor, typename Backtrack > struct traversal_ring_creator { typedef traversal < Reverse1, Reverse2, OverlayType, Geometry1, Geometry2, Turns, Clusters, RobustPolicy, typename IntersectionStrategy::side_strategy_type, Visitor > traversal_type; typedef typename boost::range_value::type turn_type; typedef typename turn_type::turn_operation_type turn_operation_type; static const operation_type target_operation = operation_from_overlay::value; inline traversal_ring_creator(Geometry1 const& geometry1, Geometry2 const& geometry2, Turns& turns, TurnInfoMap& turn_info_map, Clusters const& clusters, IntersectionStrategy const& intersection_strategy, RobustPolicy const& robust_policy, Visitor& visitor) : m_trav(geometry1, geometry2, turns, clusters, robust_policy, intersection_strategy.get_side_strategy(), visitor) , m_geometry1(geometry1) , m_geometry2(geometry2) , m_turns(turns) , m_turn_info_map(turn_info_map) , m_clusters(clusters) , m_intersection_strategy(intersection_strategy) , m_robust_policy(robust_policy) , m_visitor(visitor) { } template inline traverse_error_type travel_to_next_turn(signed_size_type start_turn_index, int start_op_index, signed_size_type& turn_index, int& op_index, Ring& current_ring, bool is_start) { int const previous_op_index = op_index; signed_size_type const previous_turn_index = turn_index; turn_type& previous_turn = m_turns[turn_index]; turn_operation_type& previous_op = previous_turn.operations[op_index]; segment_identifier previous_seg_id; signed_size_type to_vertex_index = -1; if (! m_trav.select_turn_from_enriched(turn_index, previous_seg_id, to_vertex_index, start_turn_index, start_op_index, previous_turn, previous_op, is_start)) { return is_start ? traverse_error_no_next_ip_at_start : traverse_error_no_next_ip; } if (to_vertex_index >= 0) { if (previous_op.seg_id.source_index == 0) { geometry::copy_segments(m_geometry1, previous_op.seg_id, to_vertex_index, m_intersection_strategy.get_side_strategy(), m_robust_policy, current_ring); } else { geometry::copy_segments(m_geometry2, previous_op.seg_id, to_vertex_index, m_intersection_strategy.get_side_strategy(), m_robust_policy, current_ring); } } if (m_turns[turn_index].discarded) { return is_start ? traverse_error_dead_end_at_start : traverse_error_dead_end; } if (is_start) { // Register the start previous_op.visited.set_started(); m_visitor.visit_traverse(m_turns, previous_turn, previous_op, "Start"); } if (! m_trav.select_turn(start_turn_index, start_op_index, turn_index, op_index, previous_op_index, previous_turn_index, previous_seg_id, is_start)) { return is_start ? traverse_error_no_next_ip_at_start : traverse_error_no_next_ip; } { // Check operation (TODO: this might be redundant or should be catched before) const turn_type& current_turn = m_turns[turn_index]; const turn_operation_type& op = current_turn.operations[op_index]; if (op.visited.finalized() || m_trav.is_visited(current_turn, op, turn_index, op_index)) { return traverse_error_visit_again; } } // Update registration and append point turn_type& current_turn = m_turns[turn_index]; turn_operation_type& op = current_turn.operations[op_index]; detail::overlay::append_no_dups_or_spikes(current_ring, current_turn.point, m_intersection_strategy.get_side_strategy(), m_robust_policy); // Register the visit m_trav.set_visited(current_turn, op); m_visitor.visit_traverse(m_turns, current_turn, op, "Visit"); return traverse_error_none; } template inline traverse_error_type traverse(Ring& ring, signed_size_type start_turn_index, int start_op_index) { turn_type const& start_turn = m_turns[start_turn_index]; turn_operation_type& start_op = m_turns[start_turn_index].operations[start_op_index]; detail::overlay::append_no_dups_or_spikes(ring, start_turn.point, m_intersection_strategy.get_side_strategy(), m_robust_policy); signed_size_type current_turn_index = start_turn_index; int current_op_index = start_op_index; traverse_error_type error = travel_to_next_turn(start_turn_index, start_op_index, current_turn_index, current_op_index, ring, true); if (error != traverse_error_none) { // This is not necessarily a problem, it happens for clustered turns // which are "build in" or otherwise point inwards return error; } if (current_turn_index == start_turn_index) { start_op.visited.set_finished(); m_visitor.visit_traverse(m_turns, m_turns[current_turn_index], start_op, "Early finish"); return traverse_error_none; } if (start_turn.is_clustered()) { turn_type const& turn = m_turns[current_turn_index]; if (turn.cluster_id == start_turn.cluster_id) { turn_operation_type& op = m_turns[start_turn_index].operations[current_op_index]; op.visited.set_finished(); m_visitor.visit_traverse(m_turns, m_turns[current_turn_index], start_op, "Early finish (cluster)"); return traverse_error_none; } } std::size_t const max_iterations = 2 + 2 * m_turns.size(); for (std::size_t i = 0; i <= max_iterations; i++) { // We assume clockwise polygons only, non self-intersecting, closed. // However, the input might be different, and checking validity // is up to the library user. // Therefore we make here some sanity checks. If the input // violates the assumptions, the output polygon will not be correct // but the routine will stop and output the current polygon, and // will continue with the next one. // Below three reasons to stop. error = travel_to_next_turn(start_turn_index, start_op_index, current_turn_index, current_op_index, ring, false); if (error != traverse_error_none) { return error; } if (current_turn_index == start_turn_index && current_op_index == start_op_index) { start_op.visited.set_finished(); m_visitor.visit_traverse(m_turns, start_turn, start_op, "Finish"); return traverse_error_none; } } return traverse_error_endless_loop; } template void traverse_with_operation(turn_type const& start_turn, std::size_t turn_index, int op_index, Rings& rings, std::size_t& finalized_ring_size, typename Backtrack::state_type& state) { typedef typename boost::range_value::type ring_type; turn_operation_type const& start_op = start_turn.operations[op_index]; if (! start_op.visited.none() || ! start_op.enriched.startable || start_op.visited.rejected() || ! (start_op.operation == target_operation || start_op.operation == detail::overlay::operation_continue)) { return; } ring_type ring; traverse_error_type traverse_error = traverse(ring, turn_index, op_index); if (traverse_error == traverse_error_none) { std::size_t const min_num_points = core_detail::closure::minimum_ring_size < geometry::closure::value >::value; if (geometry::num_points(ring) >= min_num_points) { clean_closing_dups_and_spikes(ring, m_intersection_strategy.get_side_strategy(), m_robust_policy); rings.push_back(ring); m_trav.finalize_visit_info(m_turn_info_map); finalized_ring_size++; } } else { Backtrack::apply( finalized_ring_size, rings, ring, m_turns, start_turn, m_turns[turn_index].operations[op_index], traverse_error, m_geometry1, m_geometry2, m_intersection_strategy, m_robust_policy, state, m_visitor); } } template void iterate(Rings& rings, std::size_t& finalized_ring_size, typename Backtrack::state_type& state) { for (std::size_t turn_index = 0; turn_index < m_turns.size(); ++turn_index) { turn_type const& turn = m_turns[turn_index]; if (turn.discarded || turn.blocked()) { // Skip discarded and blocked turns continue; } if (turn.both(operation_continue)) { // Traverse only one turn, the one with the SMALLEST remaining distance // to avoid skipping a turn in between, which can happen in rare cases // (e.g. #130) turn_operation_type const& op0 = turn.operations[0]; turn_operation_type const& op1 = turn.operations[1]; int const op_index = op0.remaining_distance <= op1.remaining_distance ? 0 : 1; traverse_with_operation(turn, turn_index, op_index, rings, finalized_ring_size, state); } else { for (int op_index = 0; op_index < 2; op_index++) { traverse_with_operation(turn, turn_index, op_index, rings, finalized_ring_size, state); } } } } private: traversal_type m_trav; Geometry1 const& m_geometry1; Geometry2 const& m_geometry2; Turns& m_turns; TurnInfoMap& m_turn_info_map; // contains turn-info information per ring Clusters const& m_clusters; IntersectionStrategy const& m_intersection_strategy; RobustPolicy const& m_robust_policy; Visitor& m_visitor; }; }} // namespace detail::overlay #endif // DOXYGEN_NO_DETAIL }} // namespace boost::geometry #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSAL_RING_CREATOR_HPP