Skip to content

Commit

Permalink
feat: major rewrite of traversal
Browse files Browse the repository at this point in the history
  • Loading branch information
barendgehrels committed Jan 30, 2025
1 parent eeeb75a commit 2e61003
Show file tree
Hide file tree
Showing 85 changed files with 4,002 additions and 6,772 deletions.
3 changes: 2 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ target_link_libraries(boost_geometry
Boost::core
Boost::crc
Boost::function_types
Boost::graph
Boost::iterator
Boost::lexical_cast
Boost::math
Expand Down Expand Up @@ -103,6 +104,7 @@ if(BUILD_TESTING AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/test/CMakeLists.txt")
algorithm
any
crc
graph
lexical_cast
math
multiprecision
Expand Down Expand Up @@ -131,4 +133,3 @@ if(BUILD_TESTING AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/test/CMakeLists.txt")
add_subdirectory(index/test EXCLUDE_FROM_ALL)

endif()

Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,11 @@ struct visit_pieces_default_policy
template <typename Collection>
static inline void apply(Collection const&, int)
{}

template <typename Turns, typename Cluster, typename Connections>
inline void visit_cluster_connections(signed_size_type cluster_id,
Turns const& turns, Cluster const& cluster, Connections const& connections) {}

};

template
Expand Down Expand Up @@ -940,6 +945,7 @@ inline void buffer_inserter(GeometryInput const& geometry_input, OutputIterator
}
collection.handle_colocations();
collection.check_turn_in_pieces();
collection.assign_side_counts(visit_pieces_policy);
collection.make_traversable_consistent_per_cluster();

// Visit the piece collection. This does nothing (by default), but
Expand All @@ -949,7 +955,7 @@ inline void buffer_inserter(GeometryInput const& geometry_input, OutputIterator
visit_pieces_policy.apply(const_collection, 0);

collection.discard_rings();
collection.block_turns();
collection.discard_non_traversable_turns();
collection.enrich();

// phase 1: turns (after enrichment/clustering)
Expand All @@ -960,7 +966,7 @@ inline void buffer_inserter(GeometryInput const& geometry_input, OutputIterator
collection.deflate_check_turns();
}

collection.traverse();
collection.traverse(visit_pieces_policy);

// Reverse all offsetted rings / traversed rings if:
// - they were generated on the negative side (deflate) of polygons
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
#include <boost/geometry/core/coordinate_type.hpp>
#include <boost/geometry/core/point_type.hpp>

#include <boost/geometry/algorithms/detail/overlay/backtrack_check_si.hpp>
#include <boost/geometry/algorithms/disjoint.hpp>
#include <boost/geometry/algorithms/expand.hpp>
#include <boost/geometry/algorithms/detail/overlay/traversal_info.hpp>
#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>

Expand All @@ -36,54 +37,6 @@ namespace boost { namespace geometry
namespace detail { namespace buffer
{

class backtrack_for_buffer
{
public :
using state_type = detail::overlay::backtrack_state;

template
<
typename Operation,
typename Rings,
typename Turns,
typename Geometry,
typename Strategy,
typename Visitor
>
static inline void apply(std::size_t size_at_start,
Rings& rings, typename boost::range_value<Rings>::type& ring,
Turns& turns,
typename boost::range_value<Turns>::type const& /*turn*/,
Operation& operation,
detail::overlay::traverse_error_type /*traverse_error*/,
Geometry const& ,
Geometry const& ,
Strategy const& ,
state_type& state,
Visitor& /*visitor*/
)
{
#if defined(BOOST_GEOMETRY_COUNT_BACKTRACK_WARNINGS)
extern int g_backtrack_warning_count;
g_backtrack_warning_count++;
#endif
//std::cout << "!";
//std::cout << "WARNING " << traverse_error_string(traverse_error) << std::endl;

state.m_good = false;

// 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);
}
};

struct buffer_overlay_visitor
{
public :
Expand All @@ -98,11 +51,6 @@ public :
{
}

template <typename Turns, typename Turn, typename Operation>
void visit_traverse_reject(Turns const& , Turn const& , Operation const& ,
detail::overlay::traverse_error_type )
{}

template <typename Rings>
void visit_generated_rings(Rings const& )
{}
Expand Down Expand Up @@ -141,15 +89,13 @@ struct buffer_turn_info
// Information if turn can be used. It is not traversable if it is within
// another piece, or within the original (depending on deflation),
// or (for deflate) if there are not enough points to traverse it.
bool is_turn_traversable;

bool is_linear_end_point;
bool within_original;
signed_size_type count_in_original; // increased by +1 for in ext.ring, -1 for int.ring

inline buffer_turn_info()
: turn_index(0)
, is_turn_traversable(true)
, is_linear_end_point(false)
, within_original(false)
, count_in_original(0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,12 @@

#include <boost/geometry/geometries/ring.hpp>

#include <boost/geometry/algorithms/detail/overlay/graph/assign_side_counts.hpp>
#include <boost/geometry/algorithms/detail/buffer/buffered_ring.hpp>
#include <boost/geometry/algorithms/detail/buffer/buffer_policies.hpp>
#include <boost/geometry/algorithms/detail/overlay/cluster_info.hpp>
#include <boost/geometry/algorithms/detail/overlay/get_properties_ahead.hpp>
#include <boost/geometry/algorithms/detail/overlay/handle_colocations.hpp>
#include <boost/geometry/algorithms/detail/buffer/get_piece_turns.hpp>
#include <boost/geometry/algorithms/detail/buffer/piece_border.hpp>
#include <boost/geometry/algorithms/detail/buffer/turn_in_piece_visitor.hpp>
Expand Down Expand Up @@ -308,7 +311,7 @@ struct buffered_piece_collection
// be three turns (which cannot be checked here - TODO: add to traverse)
for (auto& turn : m_turns)
{
if (! turn.is_turn_traversable)
if (! turn.is_traversable)
{
continue;
}
Expand Down Expand Up @@ -348,18 +351,18 @@ struct buffered_piece_collection

for (auto& turn : m_turns)
{
if (turn.is_turn_traversable)
if (turn.is_traversable)
{
if (deflate && turn.count_in_original <= 0)
{
// For deflate/negative buffers:
// it is not in the original, so don't use it
turn.is_turn_traversable = false;
turn.is_traversable = false;
}
else if (! deflate && turn.count_in_original > 0)
{
// For inflate: it is in original, so don't use it
turn.is_turn_traversable = false;
turn.is_traversable = false;
}
}
}
Expand Down Expand Up @@ -439,6 +442,21 @@ struct buffered_piece_collection
detail::section::overlaps_section_box<Strategy>(m_strategy));
}

// This fixes the fact that sometimes wrong ix or xi turns are generated.
// See comments in get_turn_info (block_q).
// The ix turns are not relevant for buffer anyway, it is fine to remove them,
// as long as they are removed before calculating turn indices.
// It will also enhance performance a bit (no need to calculate point in original,
// point in piece). Therefore we remove ii and xx as well.
m_turns.erase(std::remove_if(m_turns.begin(), m_turns.end(),
[](auto const& turn)
{
bool const is_ix = turn.combination(overlay::operation_intersection, overlay::operation_blocked);
bool const is_ii = turn.both(overlay::operation_intersection);
return is_ix || is_ii || turn.blocked();
}),
m_turns.end());

update_turn_administration();
}

Expand Down Expand Up @@ -869,29 +887,27 @@ struct buffered_piece_collection

inline void handle_colocations()
{
if (! detail::overlay::handle_colocations
<
false, false, overlay_buffer,
ring_collection_t, ring_collection_t
>(m_turns, m_clusters))
{
return;
}

detail::overlay::gather_cluster_properties
<
false, false, overlay_buffer
>(m_clusters, m_turns, detail::overlay::operation_union,
offsetted_rings, offsetted_rings, m_strategy);
detail::overlay::handle_colocations(m_turns, m_clusters);
}

template <typename Visitor>
inline void assign_side_counts(Visitor& visitor)
{
// Assign count_left, count_right and open_count
detail::overlay::assign_side_counts
<false, false, overlay_buffer>
(offsetted_rings, offsetted_rings,
m_turns, m_clusters,
m_strategy, visitor);

// Mark closed clusters as not traversable
for (auto const& cluster : m_clusters)
{
if (cluster.second.open_count == 0 && cluster.second.spike_count == 0)
if (cluster.second.open_count == 0)
{
// If the cluster is completely closed, mark it as not traversable.
for (auto const& index : cluster.second.turn_indices)
{
m_turns[index].is_turn_traversable = false;
m_turns[index].is_traversable = false;
}
}
}
Expand All @@ -904,7 +920,7 @@ struct buffered_piece_collection
bool is_traversable = false;
for (auto const& index : cluster.second.turn_indices)
{
if (m_turns[index].is_turn_traversable)
if (m_turns[index].is_traversable)
{
// If there is one turn traversable in the cluster,
// then all turns should be traversable.
Expand All @@ -916,17 +932,21 @@ struct buffered_piece_collection
{
for (auto const& index : cluster.second.turn_indices)
{
m_turns[index].is_turn_traversable = true;
m_turns[index].is_traversable = true;
}
}
}
}

inline void enrich()
{
enrich_intersection_points<false, false, overlay_buffer>(m_turns,
m_clusters, offsetted_rings, offsetted_rings,
m_strategy);
detail::overlay::enrich_discard_turns<overlay_buffer>(
m_turns, m_clusters, offsetted_rings, offsetted_rings, m_strategy);
detail::overlay::enrich_turns<false, false, overlay_buffer>(
m_turns, offsetted_rings, offsetted_rings, m_strategy);

detail::overlay::get_properties_ahead<false, false>(m_turns, m_clusters, offsetted_rings,
offsetted_rings, m_strategy);
}

// Discards all rings which do have not-OK intersection points only.
Expand All @@ -935,7 +955,7 @@ struct buffered_piece_collection
{
for (auto const& turn : m_turns)
{
if (turn.is_turn_traversable)
if (turn.is_traversable)
{
offsetted_rings[turn.operations[0].seg_id.multi_index].has_accepted_intersections = true;
offsetted_rings[turn.operations[1].seg_id.multi_index].has_accepted_intersections = true;
Expand Down Expand Up @@ -1013,28 +1033,27 @@ struct buffered_piece_collection
}
}

inline void block_turns()
inline void discard_non_traversable_turns()
{
for (auto& turn : m_turns)
{
if (! turn.is_turn_traversable)
if (! turn.is_traversable)
{
// Discard this turn (don't set it to blocked to avoid colocated
// clusters being discarded afterwards
// Discard the non traversable turn
turn.discarded = true;
}
}
}

inline void traverse()
template <typename PieceVisitor>
inline void traverse(PieceVisitor const& piece_visitor)
{
using traverser = detail::overlay::traverse
<
false, false,
buffered_ring_collection<buffered_ring<Ring> >,
buffered_ring_collection<buffered_ring<Ring > >,
overlay_buffer,
backtrack_for_buffer
overlay_buffer
>;
std::map<ring_identifier, overlay::ring_turn_info> turn_info_per_ring;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ struct include_turn_policy
template <typename Turn>
static inline bool apply(Turn const& turn)
{
return turn.is_turn_traversable;
return turn.is_traversable;
}
};

Expand All @@ -89,7 +89,7 @@ struct turn_in_original_overlaps_box
template <typename Box, typename Turn>
inline bool apply(Box const& box, Turn const& turn) const
{
if (! turn.is_turn_traversable || turn.within_original)
if (! turn.is_traversable || turn.within_original)
{
// Skip all points already processed
return false;
Expand Down Expand Up @@ -237,7 +237,7 @@ class turn_in_original_visitor
return true;
}

if (! turn.is_turn_traversable || turn.within_original)
if (! turn.is_traversable || turn.within_original)
{
// Skip all points already processed
return true;
Expand All @@ -262,7 +262,7 @@ class turn_in_original_visitor
if (code == 0)
{
// On border of original: always discard
mutable_turn.is_turn_traversable = false;
mutable_turn.is_traversable = false;
}

// Point is inside an original ring
Expand Down
Loading

0 comments on commit 2e61003

Please sign in to comment.