blob: d91efa71c082033368c8447a0e00acc8988ec49d [file] [log] [blame]
// 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/turn_info.hpp>
#include <boost/geometry/algorithms/detail/overlay/copy_segments.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 Turns>
inline void clear_visit_info(Turns& turns)
{
typedef typename boost::range_value<Turns>::type tp_type;
for (typename boost::range_iterator<Turns>::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 <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;
}
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<Rings>::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<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);
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<Rings>::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<Reverse1, Reverse2>(
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<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
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