// Boost.Geometry (aka GGL, Generic Geometry Library) // Copyright (c) 2007-2011 Barend Gehrels, Amsterdam, the Netherlands. // 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_TRAVERSE_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSE_HPP #include #include #include #include #include #include #include #if defined(BOOST_GEOMETRY_DEBUG_INTERSECTION) || defined(BOOST_GEOMETRY_OVERLAY_REPORT_WKT) # include # include # include #endif namespace boost { namespace geometry { #ifndef DOXYGEN_NO_DETAIL namespace detail { namespace overlay { template inline void debug_traverse(Turn const& turn, Operation op, std::string const& header) { #ifdef BOOST_GEOMETRY_DEBUG_TRAVERSE std::cout << header << " at " << op.seg_id << " op: " << operation_char(op.operation) << " vis: " << visited_char(op.visited) << " of: " << operation_char(turn.operations[0].operation) << operation_char(turn.operations[1].operation) << std::endl; if (boost::contains(header, "Finished")) { std::cout << std::endl; } #endif } template inline void clear_visit_info(Turns& turns) { typedef typename boost::range_value::type tp_type; for (typename boost::range_iterator::type it = boost::begin(turns); it != boost::end(turns); ++it) { for (typename boost::range_iterator < typename tp_type::container_type >::type op_it = boost::begin(it->operations); op_it != boost::end(it->operations); ++op_it) { op_it->visited.clear(); } it->discarded = false; } } template inline void set_visited_for_continue(Info& info, Turn const& turn) { // On "continue", set "visited" for ALL directions if (turn.operation == detail::overlay::operation_continue) { for (typename boost::range_iterator < typename Info::container_type >::type it = boost::begin(info.operations); it != boost::end(info.operations); ++it) { if (it->visited.none()) { it->visited.set_visited(); } } } } template < bool Reverse1, bool Reverse2, typename GeometryOut, typename G1, typename G2, typename Turns, typename IntersectionInfo > inline bool assign_next_ip(G1 const& g1, G2 const& g2, Turns& turns, typename boost::range_iterator::type& ip, GeometryOut& current_output, IntersectionInfo& info, segment_identifier& seg_id) { info.visited.set_visited(); set_visited_for_continue(*ip, info); // If there is no next IP on this segment if (info.enriched.next_ip_index < 0) { if (info.enriched.travels_to_vertex_index < 0 || info.enriched.travels_to_ip_index < 0) return false; BOOST_ASSERT(info.enriched.travels_to_vertex_index >= 0); BOOST_ASSERT(info.enriched.travels_to_ip_index >= 0); if (info.seg_id.source_index == 0) { geometry::copy_segments(g1, info.seg_id, info.enriched.travels_to_vertex_index, current_output); } else { geometry::copy_segments(g2, info.seg_id, info.enriched.travels_to_vertex_index, current_output); } seg_id = info.seg_id; ip = boost::begin(turns) + info.enriched.travels_to_ip_index; } else { ip = boost::begin(turns) + info.enriched.next_ip_index; seg_id = info.seg_id; } geometry::append(current_output, ip->point); return true; } inline bool select_source(operation_type operation, int source1, int source2) { return (operation == operation_intersection && source1 != source2) || (operation == operation_union && source1 == source2) ; } template < typename Turn, typename Iterator > inline bool select_next_ip(operation_type operation, Turn& turn, segment_identifier const& seg_id, Iterator& selected) { if (turn.discarded) { return false; } bool has_tp = false; selected = boost::end(turn.operations); for (Iterator it = boost::begin(turn.operations); it != boost::end(turn.operations); ++it) { if (it->visited.started()) { selected = it; //std::cout << " RETURN"; return true; } // In some cases there are two alternatives. // For "ii", take the other one (alternate) // UNLESS the other one is already visited // For "uu", take the same one (see above); // For "cc", take either one, but if there is a starting one, // take that one. if ( (it->operation == operation_continue && (! has_tp || it->visited.started() ) ) || (it->operation == operation && ! it->visited.finished() && (! has_tp || select_source(operation, it->seg_id.source_index, seg_id.source_index) ) ) ) { selected = it; debug_traverse(turn, *it, " Candidate"); has_tp = true; } } if (has_tp) { debug_traverse(turn, *selected, " Accepted"); } return has_tp; } template < typename Rings, typename Turns, typename Operation, typename Geometry1, typename Geometry2 > inline void backtrack(std::size_t size_at_start, bool& fail, Rings& rings, typename boost::range_value::type& ring, Turns& turns, Operation& operation, #ifdef BOOST_GEOMETRY_OVERLAY_REPORT_WKT std::string const& reason, Geometry1 const& geometry1, Geometry2 const& geometry2 #else std::string const& reason, Geometry1 const& , Geometry2 const& #endif ) { #ifdef BOOST_GEOMETRY_DEBUG_ENRICH std::cout << " REJECT " << reason << std::endl; #endif fail = true; // Make bad output clean rings.resize(size_at_start); ring.clear(); // Reject this as a starting point operation.visited.set_rejected(); // And clear all visit info clear_visit_info(turns); /*** int c = 0; for (int i = 0; i < turns.size(); i++) { for (int j = 0; j < 2; j++) { if (turns[i].operations[j].visited.rejected()) { c++; } } } std::cout << "BACKTRACK (" << reason << " )" << " " << c << " of " << turns.size() << " rejected" << std::endl; ***/ #ifdef BOOST_GEOMETRY_OVERLAY_REPORT_WKT std::cout << " BT (" << reason << " )"; std::cout << geometry::wkt(geometry1) << std::endl << geometry::wkt(geometry2) << std::endl; #endif } }} // namespace detail::overlay #endif // DOXYGEN_NO_DETAIL /*! \brief Traverses through intersection points / geometries \ingroup overlay */ template < bool Reverse1, bool Reverse2, typename Geometry1, typename Geometry2, typename Turns, typename Rings > inline void traverse(Geometry1 const& geometry1, Geometry2 const& geometry2, detail::overlay::operation_type operation, Turns& turns, Rings& rings) { typedef typename boost::range_iterator::type turn_iterator; typedef typename boost::range_value::type turn_type; typedef typename boost::range_iterator < typename turn_type::container_type >::type turn_operation_iterator_type; std::size_t size_at_start = boost::size(rings); bool fail = false; do { fail = false; // Iterate through all unvisited points for (turn_iterator it = boost::begin(turns); ! fail && it != boost::end(turns); ++it) { // Skip discarded ones if (! (it->is_discarded() || it->blocked())) { for (turn_operation_iterator_type iit = boost::begin(it->operations); ! fail && iit != boost::end(it->operations); ++iit) { if (iit->visited.none() && ! iit->visited.rejected() && (iit->operation == operation || iit->operation == detail::overlay::operation_continue) ) { set_visited_for_continue(*it, *iit); typename boost::range_value::type current_output; geometry::append(current_output, it->point); turn_iterator current = it; turn_operation_iterator_type current_iit = iit; segment_identifier current_seg_id; if (! detail::overlay::assign_next_ip( geometry1, geometry2, turns, current, current_output, *iit, current_seg_id)) { detail::overlay::backtrack( size_at_start, fail, rings, current_output, turns, *current_iit, "No next IP", geometry1, geometry2); } if (! detail::overlay::select_next_ip( operation, *current, current_seg_id, current_iit)) { detail::overlay::backtrack( size_at_start, fail, rings, current_output, turns, *iit, "Dead end at start", geometry1, geometry2); } else { iit->visited.set_started(); detail::overlay::debug_traverse(*it, *iit, "-> Started"); detail::overlay::debug_traverse(*current, *current_iit, "Selected "); unsigned int i = 0; while (current_iit != iit && ! fail) { if (current_iit->visited.visited()) { // It visits a visited node again, without passing the start node. // This makes it suspicious for endless loops detail::overlay::backtrack( size_at_start, fail, rings, current_output, turns, *iit, "Visit again", geometry1, geometry2); } else { // 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. detail::overlay::assign_next_ip( geometry1, geometry2, turns, current, current_output, *current_iit, current_seg_id); if (! detail::overlay::select_next_ip( operation, *current, current_seg_id, current_iit)) { // Should not occur in valid (non-self-intersecting) polygons // Should not occur in self-intersecting polygons without spikes // Might occur in polygons with spikes detail::overlay::backtrack( size_at_start, fail, rings, current_output, turns, *iit, "Dead end", geometry1, geometry2); } detail::overlay::debug_traverse(*current, *current_iit, "Selected "); if (i++ > 2 + 2 * turns.size()) { // Sanity check: there may be never more loops // than turn points. // Turn points marked as "ii" can be visited twice. detail::overlay::backtrack( size_at_start, fail, rings, current_output, turns, *iit, "Endless loop", geometry1, geometry2); } } } if (! fail) { iit->visited.set_finished(); detail::overlay::debug_traverse(*current, *iit, "->Finished"); rings.push_back(current_output); } } } } } } } while (fail); } }} // namespace boost::geometry #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSE_HPP