You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and dots ('.'), can be up to 35 characters long. Letters must be lowercase.
394 lines
14 KiB
394 lines
14 KiB
// 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 <cstddef> |
|
|
|
#include <boost/range.hpp> |
|
|
|
#include <boost/geometry/algorithms/detail/overlay/append_no_duplicates.hpp> |
|
#include <boost/geometry/algorithms/detail/overlay/backtrack_check_si.hpp> |
|
#include <boost/geometry/algorithms/detail/overlay/copy_segments.hpp> |
|
#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp> |
|
#include <boost/geometry/core/access.hpp> |
|
#include <boost/geometry/core/coordinate_dimension.hpp> |
|
#include <boost/geometry/geometries/concepts/check.hpp> |
|
|
|
|
|
#if defined(BOOST_GEOMETRY_DEBUG_INTERSECTION) || defined(BOOST_GEOMETRY_OVERLAY_REPORT_WKT) |
|
# include <string> |
|
# include <boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp> |
|
# include <boost/geometry/domains/gis/io/wkt/wkt.hpp> |
|
#endif |
|
|
|
|
|
|
|
namespace boost { namespace geometry |
|
{ |
|
|
|
|
|
#ifndef DOXYGEN_NO_DETAIL |
|
namespace detail { namespace overlay |
|
{ |
|
|
|
template <typename Turn, typename Operation> |
|
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 <typename Info, typename Turn> |
|
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<Turns>::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<Reverse1>(g1, info.seg_id, |
|
info.enriched.travels_to_vertex_index, |
|
current_output); |
|
} |
|
else |
|
{ |
|
geometry::copy_segments<Reverse2>(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; |
|
} |
|
|
|
detail::overlay::append_no_duplicates(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; |
|
} |
|
|
|
|
|
|
|
/*! |
|
\brief Traverses through intersection points / geometries |
|
\ingroup overlay |
|
*/ |
|
template |
|
< |
|
bool Reverse1, bool Reverse2, |
|
typename Geometry1, |
|
typename Geometry2, |
|
typename Backtrack = backtrack_check_self_intersections<Geometry1, Geometry2> |
|
> |
|
class traverse |
|
{ |
|
public : |
|
template <typename Turns, typename Rings> |
|
static inline void apply(Geometry1 const& geometry1, |
|
Geometry2 const& geometry2, |
|
detail::overlay::operation_type operation, |
|
Turns& turns, Rings& rings) |
|
{ |
|
typedef typename boost::range_iterator<Turns>::type turn_iterator; |
|
typedef typename boost::range_value<Turns>::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); |
|
|
|
typename Backtrack::state_type state; |
|
do |
|
{ |
|
state.reset(); |
|
|
|
// Iterate through all unvisited points |
|
for (turn_iterator it = boost::begin(turns); |
|
state.good() && it != boost::end(turns); |
|
++it) |
|
{ |
|
// Skip discarded ones |
|
if (! (it->is_discarded() || it->blocked())) |
|
{ |
|
for (turn_operation_iterator_type iit = boost::begin(it->operations); |
|
state.good() && 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<Rings>::type current_output; |
|
detail::overlay::append_no_duplicates(current_output, |
|
it->point, true); |
|
|
|
turn_iterator current = it; |
|
turn_operation_iterator_type current_iit = iit; |
|
segment_identifier current_seg_id; |
|
|
|
if (! detail::overlay::assign_next_ip<Reverse1, Reverse2>( |
|
geometry1, geometry2, |
|
turns, |
|
current, current_output, |
|
*iit, current_seg_id)) |
|
{ |
|
Backtrack::apply( |
|
size_at_start, |
|
rings, current_output, turns, *current_iit, |
|
"No next IP", |
|
geometry1, geometry2, state); |
|
} |
|
|
|
if (! detail::overlay::select_next_ip( |
|
operation, |
|
*current, |
|
current_seg_id, |
|
current_iit)) |
|
{ |
|
Backtrack::apply( |
|
size_at_start, |
|
rings, current_output, turns, *iit, |
|
"Dead end at start", |
|
geometry1, geometry2, state); |
|
} |
|
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 && state.good()) |
|
{ |
|
if (current_iit->visited.visited()) |
|
{ |
|
// It visits a visited node again, without passing the start node. |
|
// This makes it suspicious for endless loops |
|
Backtrack::apply( |
|
size_at_start, |
|
rings, current_output, turns, *iit, |
|
"Visit again", |
|
geometry1, geometry2, state); |
|
} |
|
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<Reverse1, Reverse2>( |
|
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 |
|
Backtrack::apply( |
|
size_at_start, |
|
rings, current_output, turns, *iit, |
|
"Dead end", |
|
geometry1, geometry2, state); |
|
} |
|
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. |
|
Backtrack::apply( |
|
size_at_start, |
|
rings, current_output, turns, *iit, |
|
"Endless loop", |
|
geometry1, geometry2, state); |
|
} |
|
} |
|
} |
|
|
|
if (state.good()) |
|
{ |
|
iit->visited.set_finished(); |
|
detail::overlay::debug_traverse(*current, *iit, "->Finished"); |
|
rings.push_back(current_output); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} while (! state.good()); |
|
} |
|
}; |
|
|
|
}} // namespace detail::overlay |
|
#endif // DOXYGEN_NO_DETAIL |
|
|
|
|
|
}} // namespace boost::geometry |
|
|
|
|
|
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSE_HPP
|
|
|