Added thirdparty: boost library

This commit is contained in:
Viacheslav Demydiuk
2024-01-06 19:55:56 +02:00
parent bf49f439e1
commit bccd1e7051
15683 changed files with 3239840 additions and 0 deletions
@@ -0,0 +1,62 @@
// Boost.Geometry (aka GGL, Generic Geometry Library)
// Copyright (c) 2018-2019 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_BUFFER_BUFFER_BOX_HPP
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_BUFFER_BOX_HPP
#include <cstddef>
#include <boost/geometry/core/coordinate_dimension.hpp>
#include <boost/geometry/core/coordinate_type.hpp>
#include <boost/geometry/core/access.hpp>
namespace boost { namespace geometry
{
#ifndef DOXYGEN_NO_DETAIL
namespace detail { namespace buffer
{
template <typename BoxIn, typename BoxOut, typename T, std::size_t C, std::size_t D, std::size_t N>
struct box_loop
{
typedef typename coordinate_type<BoxOut>::type coordinate_type;
static inline void apply(BoxIn const& box_in, T const& distance, BoxOut& box_out)
{
coordinate_type d = distance;
set<C, D>(box_out, get<C, D>(box_in) + d);
box_loop<BoxIn, BoxOut, T, C, D + 1, N>::apply(box_in, distance, box_out);
}
};
template <typename BoxIn, typename BoxOut, typename T, std::size_t C, std::size_t N>
struct box_loop<BoxIn, BoxOut, T, C, N, N>
{
static inline void apply(BoxIn const&, T const&, BoxOut&) {}
};
// Extends a box with the same amount in all directions
template<typename BoxIn, typename BoxOut, typename T>
inline void buffer_box(BoxIn const& box_in, T const& distance, BoxOut& box_out)
{
assert_dimension_equal<BoxIn, BoxOut>();
static const std::size_t N = dimension<BoxIn>::value;
box_loop<BoxIn, BoxOut, T, min_corner, 0, N>::apply(box_in, -distance, box_out);
box_loop<BoxIn, BoxOut, T, max_corner, 0, N>::apply(box_in, distance, box_out);
}
}} // namespace detail::buffer
#endif // DOXYGEN_NO_DETAIL
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_BUFFER_BOX_HPP
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,300 @@
// Boost.Geometry (aka GGL, Generic Geometry Library)
// Copyright (c) 2012-2014 Barend Gehrels, Amsterdam, the Netherlands.
// This file was modified by Oracle on 2017-2020.
// Modifications copyright (c) 2017-2020, 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_BUFFER_BUFFER_POLICIES_HPP
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_BUFFER_POLICIES_HPP
#include <cstddef>
#include <boost/range/value_type.hpp>
#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/detail/overlay/traversal_info.hpp>
#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
#include <boost/geometry/strategies/buffer.hpp>
namespace boost { namespace geometry
{
#ifndef DOXYGEN_NO_DETAIL
namespace detail { namespace buffer
{
class backtrack_for_buffer
{
public :
typedef detail::overlay::backtrack_state state_type;
template
<
typename Operation,
typename Rings,
typename Turns,
typename Geometry,
typename Strategy,
typename RobustPolicy,
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& ,
RobustPolicy 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 :
void print(char const* /*header*/)
{
}
template <typename Turns>
void print(char const* /*header*/, Turns const& /*turns*/, int /*turn_index*/)
{
}
template <typename Turns>
void print(char const* /*header*/, Turns const& /*turns*/, int /*turn_index*/, int /*op_index*/)
{
}
template <typename Turns>
void visit_turns(int , Turns const& ) {}
template <typename Clusters, typename Turns>
void visit_clusters(Clusters const& , Turns const& ) {}
template <typename Turns, typename Turn, typename Operation>
void visit_traverse(Turns const& /*turns*/, Turn const& /*turn*/, Operation const& /*op*/, const char* /*header*/)
{
}
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& )
{}
};
// Should follow traversal-turn-concept (enrichment, visit structure)
// and adds index in piece vector to find it back
template <typename Point, typename SegmentRatio>
struct buffer_turn_operation
: public detail::overlay::traversal_turn_operation<Point, SegmentRatio>
{
signed_size_type piece_index;
signed_size_type index_in_robust_ring;
inline buffer_turn_operation()
: piece_index(-1)
, index_in_robust_ring(-1)
{}
};
// Version of turn_info for buffer with its turn index and other helper variables
template <typename Point, typename SegmentRatio>
struct buffer_turn_info
: public detail::overlay::turn_info
<
Point,
SegmentRatio,
buffer_turn_operation<Point, SegmentRatio>
>
{
typedef Point point_type;
std::size_t turn_index;
// 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)
{}
};
struct buffer_less
{
template <typename Indexed>
inline bool operator()(Indexed const& left, Indexed const& right) const
{
if (! (left.subject->seg_id == right.subject->seg_id))
{
return left.subject->seg_id < right.subject->seg_id;
}
// Both left and right are located on the SAME segment.
if (! (left.subject->fraction == right.subject->fraction))
{
return left.subject->fraction < right.subject->fraction;
}
return left.turn_index < right.turn_index;
}
};
template <typename Strategy>
struct piece_get_box
{
explicit piece_get_box(Strategy const& strategy)
: m_strategy(strategy)
{}
template <typename Box, typename Piece>
inline void apply(Box& total, Piece const& piece) const
{
assert_coordinate_type_equal(total, piece.m_piece_border.m_envelope);
if (piece.m_piece_border.m_has_envelope)
{
geometry::expand(total, piece.m_piece_border.m_envelope,
m_strategy);
}
}
Strategy const& m_strategy;
};
template <typename Strategy>
struct piece_overlaps_box
{
explicit piece_overlaps_box(Strategy const& strategy)
: m_strategy(strategy)
{}
template <typename Box, typename Piece>
inline bool apply(Box const& box, Piece const& piece) const
{
assert_coordinate_type_equal(box, piece.m_piece_border.m_envelope);
if (piece.type == strategy::buffer::buffered_flat_end
|| piece.type == strategy::buffer::buffered_concave)
{
// Turns cannot be inside a flat end (though they can be on border)
// Neither we need to check if they are inside concave helper pieces
// Skip all pieces not used as soon as possible
return false;
}
if (! piece.m_piece_border.m_has_envelope)
{
return false;
}
return ! geometry::detail::disjoint::disjoint_box_box(box, piece.m_piece_border.m_envelope,
m_strategy);
}
Strategy const& m_strategy;
};
template <typename Strategy>
struct turn_get_box
{
explicit turn_get_box(Strategy const& strategy)
: m_strategy(strategy)
{}
template <typename Box, typename Turn>
inline void apply(Box& total, Turn const& turn) const
{
assert_coordinate_type_equal(total, turn.point);
geometry::expand(total, turn.point, m_strategy);
}
Strategy const& m_strategy;
};
template <typename Strategy>
struct turn_overlaps_box
{
explicit turn_overlaps_box(Strategy const& strategy)
: m_strategy(strategy)
{}
template <typename Box, typename Turn>
inline bool apply(Box const& box, Turn const& turn) const
{
assert_coordinate_type_equal(turn.point, box);
return ! geometry::detail::disjoint::disjoint_point_box(turn.point, box,
m_strategy);
}
Strategy const& m_strategy;
};
struct enriched_map_buffer_include_policy
{
template <typename Operation>
static inline bool include(Operation const& op)
{
return op != detail::overlay::operation_intersection
&& op != detail::overlay::operation_blocked;
}
};
}} // namespace detail::buffer
#endif // DOXYGEN_NO_DETAIL
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_BUFFER_POLICIES_HPP
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,290 @@
// Boost.Geometry (aka GGL, Generic Geometry Library)
// Copyright (c) 2012-2015 Barend Gehrels, Amsterdam, the Netherlands.
// This file was modified by Oracle on 2020.
// Modifications copyright (c) 2020 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_BUFFER_BUFFERED_RING
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_BUFFERED_RING
#include <cstddef>
#include <boost/range/size.hpp>
#include <boost/range/value_type.hpp>
#include <boost/geometry/core/assert.hpp>
#include <boost/geometry/core/coordinate_type.hpp>
#include <boost/geometry/core/closure.hpp>
#include <boost/geometry/core/point_order.hpp>
#include <boost/geometry/core/point_type.hpp>
#include <boost/geometry/strategies/buffer.hpp>
#include <boost/geometry/algorithms/within.hpp>
#include <boost/geometry/algorithms/detail/overlay/copy_segments.hpp>
#include <boost/geometry/algorithms/detail/overlay/copy_segment_point.hpp>
#include <boost/geometry/algorithms/detail/overlay/enrichment_info.hpp>
#include <boost/geometry/algorithms/detail/overlay/get_ring.hpp>
#include <boost/geometry/algorithms/detail/overlay/traversal_info.hpp>
#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
namespace boost { namespace geometry
{
#ifndef DOXYGEN_NO_DETAIL
namespace detail { namespace buffer
{
struct buffered_ring_collection_tag : polygonal_tag, multi_tag
{};
template <typename Ring>
struct buffered_ring : public Ring
{
bool has_concave;
bool has_accepted_intersections;
bool has_discarded_intersections;
bool is_untouched_outside_original;
inline buffered_ring()
: has_concave(false)
, has_accepted_intersections(false)
, has_discarded_intersections(false)
, is_untouched_outside_original(false)
{}
inline bool discarded() const
{
return has_discarded_intersections && ! has_accepted_intersections;
}
inline bool has_intersections() const
{
return has_discarded_intersections || has_accepted_intersections;
}
};
// This is a collection now special for overlay (needs vector of rings)
template <typename Ring>
struct buffered_ring_collection : public std::vector<Ring>
{
};
}} // namespace detail::buffer
// Turn off concept checking (for now)
namespace concepts
{
template <typename Geometry>
struct concept_type<Geometry, geometry::detail::buffer::buffered_ring_collection_tag>
{
struct dummy {};
using type = dummy;
};
}
#endif // DOXYGEN_NO_DETAIL
// Register the types
namespace traits
{
template <typename Ring>
struct tag<geometry::detail::buffer::buffered_ring<Ring> >
{
typedef ring_tag type;
};
template <typename Ring>
struct point_order<geometry::detail::buffer::buffered_ring<Ring> >
{
static const order_selector value = geometry::point_order<Ring>::value;
};
template <typename Ring>
struct closure<geometry::detail::buffer::buffered_ring<Ring> >
{
static const closure_selector value = geometry::closure<Ring>::value;
};
template <typename Ring>
struct point_type<geometry::detail::buffer::buffered_ring_collection<Ring> >
{
typedef typename geometry::point_type<Ring>::type type;
};
template <typename Ring>
struct tag<geometry::detail::buffer::buffered_ring_collection<Ring> >
{
typedef geometry::detail::buffer::buffered_ring_collection_tag type;
};
} // namespace traits
namespace core_dispatch
{
template <typename Ring>
struct ring_type
<
detail::buffer::buffered_ring_collection_tag,
detail::buffer::buffered_ring_collection<Ring>
>
{
typedef Ring type;
};
// There is a specific tag, so this specialization cannot be placed in traits
template <typename Ring>
struct point_order<detail::buffer::buffered_ring_collection_tag,
geometry::detail::buffer::buffered_ring_collection
<
geometry::detail::buffer::buffered_ring<Ring>
> >
{
static const order_selector value
= core_dispatch::point_order<ring_tag, Ring>::value;
};
}
template <>
struct single_tag_of<detail::buffer::buffered_ring_collection_tag>
{
typedef ring_tag type;
};
namespace dispatch
{
template
<
typename MultiRing,
bool Reverse,
typename SegmentIdentifier,
typename PointOut
>
struct copy_segment_point
<
detail::buffer::buffered_ring_collection_tag,
MultiRing,
Reverse,
SegmentIdentifier,
PointOut
>
: detail::copy_segments::copy_segment_point_multi
<
MultiRing,
SegmentIdentifier,
PointOut,
detail::copy_segments::copy_segment_point_range
<
typename boost::range_value<MultiRing>::type,
Reverse,
SegmentIdentifier,
PointOut
>
>
{};
template<bool Reverse>
struct copy_segments
<
detail::buffer::buffered_ring_collection_tag,
Reverse
>
: detail::copy_segments::copy_segments_multi
<
detail::copy_segments::copy_segments_ring<Reverse>
>
{};
template <typename Point, typename MultiGeometry>
struct within
<
Point,
MultiGeometry,
point_tag,
detail::buffer::buffered_ring_collection_tag
>
{
template <typename Strategy>
static inline bool apply(Point const& point,
MultiGeometry const& multi, Strategy const& strategy)
{
return detail::within::point_in_geometry(point, multi, strategy) == 1;
}
};
template <typename Geometry>
struct is_empty<Geometry, detail::buffer::buffered_ring_collection_tag>
: detail::is_empty::multi_is_empty<detail::is_empty::range_is_empty>
{};
template <typename Geometry>
struct envelope<Geometry, detail::buffer::buffered_ring_collection_tag>
: detail::envelope::envelope_multi_range
<
detail::envelope::envelope_range
>
{};
} // namespace dispatch
namespace detail { namespace overlay
{
template<>
struct get_ring<detail::buffer::buffered_ring_collection_tag>
{
template<typename MultiGeometry>
static inline typename ring_type<MultiGeometry>::type const& apply(
ring_identifier const& id,
MultiGeometry const& multi_ring)
{
BOOST_GEOMETRY_ASSERT
(
id.multi_index >= 0
&& id.multi_index < int(boost::size(multi_ring))
);
return get_ring<ring_tag>::apply(id, multi_ring[id.multi_index]);
}
};
}}
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_BUFFERED_RING
@@ -0,0 +1,327 @@
// Boost.Geometry (aka GGL, Generic Geometry Library)
// Copyright (c) 2012-2014 Barend Gehrels, Amsterdam, the Netherlands.
// Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland.
// This file was modified by Oracle on 2017-2020.
// Modifications copyright (c) 2017-2020 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_BUFFER_GET_PIECE_TURNS_HPP
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_GET_PIECE_TURNS_HPP
#include <boost/core/ignore_unused.hpp>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <boost/range/value_type.hpp>
#include <boost/geometry/core/assert.hpp>
#include <boost/geometry/algorithms/equals.hpp>
#include <boost/geometry/algorithms/detail/disjoint/box_box.hpp>
#include <boost/geometry/algorithms/detail/overlay/segment_identifier.hpp>
#include <boost/geometry/algorithms/detail/overlay/get_turn_info.hpp>
#include <boost/geometry/algorithms/detail/sections/section_functions.hpp>
#include <boost/geometry/algorithms/detail/buffer/buffer_policies.hpp>
namespace boost { namespace geometry
{
#ifndef DOXYGEN_NO_DETAIL
namespace detail { namespace buffer
{
// Implements a unique_sub_range for a buffered piece,
// the range can return subsequent points
// known as "i", "j" and "k" (and further), indexed as 0,1,2,3
template <typename Ring>
struct unique_sub_range_from_piece
{
typedef typename boost::range_iterator<Ring const>::type iterator_type;
typedef typename geometry::point_type<Ring const>::type point_type;
unique_sub_range_from_piece(Ring const& ring,
iterator_type iterator_at_i, iterator_type iterator_at_j)
: m_ring(ring)
, m_iterator_at_i(iterator_at_i)
, m_iterator_at_j(iterator_at_j)
, m_point_retrieved(false)
{}
static inline bool is_first_segment() { return false; }
static inline bool is_last_segment() { return false; }
static inline std::size_t size() { return 3u; }
inline point_type const& at(std::size_t index) const
{
BOOST_GEOMETRY_ASSERT(index < size());
switch (index)
{
case 0 : return *m_iterator_at_i;
case 1 : return *m_iterator_at_j;
case 2 : return get_point_k();
default : return *m_iterator_at_i;
}
}
private :
inline point_type const& get_point_k() const
{
if (! m_point_retrieved)
{
m_iterator_at_k = advance_one(m_iterator_at_j);
m_point_retrieved = true;
}
return *m_iterator_at_k;
}
inline void circular_advance_one(iterator_type& next) const
{
++next;
if (next == boost::end(m_ring))
{
next = boost::begin(m_ring) + 1;
}
}
inline iterator_type advance_one(iterator_type it) const
{
iterator_type result = it;
circular_advance_one(result);
// TODO: we could also use piece-boundaries
// to check if the point equals the last one
while (geometry::equals(*it, *result))
{
circular_advance_one(result);
}
return result;
}
Ring const& m_ring;
iterator_type m_iterator_at_i;
iterator_type m_iterator_at_j;
mutable iterator_type m_iterator_at_k;
mutable bool m_point_retrieved;
};
template
<
typename Pieces,
typename Rings,
typename Turns,
typename Strategy,
typename RobustPolicy
>
class piece_turn_visitor
{
Pieces const& m_pieces;
Rings const& m_rings;
Turns& m_turns;
Strategy const& m_strategy;
RobustPolicy const& m_robust_policy;
template <typename Piece>
inline bool is_adjacent(Piece const& piece1, Piece const& piece2) const
{
if (piece1.first_seg_id.multi_index != piece2.first_seg_id.multi_index)
{
return false;
}
return piece1.index == piece2.left_index
|| piece1.index == piece2.right_index;
}
template <typename Piece>
inline bool is_on_same_convex_ring(Piece const& piece1, Piece const& piece2) const
{
if (piece1.first_seg_id.multi_index != piece2.first_seg_id.multi_index)
{
return false;
}
return ! m_rings[piece1.first_seg_id.multi_index].has_concave;
}
template <std::size_t Dimension, typename Iterator, typename Box>
inline void move_begin_iterator(Iterator& it_begin, Iterator it_beyond,
signed_size_type& index, int dir,
Box const& this_bounding_box,
Box const& other_bounding_box)
{
for(; it_begin != it_beyond
&& it_begin + 1 != it_beyond
&& detail::section::preceding<Dimension>(dir, *(it_begin + 1),
this_bounding_box,
other_bounding_box,
m_robust_policy);
++it_begin, index++)
{}
}
template <std::size_t Dimension, typename Iterator, typename Box>
inline void move_end_iterator(Iterator it_begin, Iterator& it_beyond,
int dir, Box const& this_bounding_box,
Box const& other_bounding_box)
{
while (it_beyond != it_begin
&& it_beyond - 1 != it_begin
&& it_beyond - 2 != it_begin)
{
if (detail::section::exceeding<Dimension>(dir, *(it_beyond - 2),
this_bounding_box, other_bounding_box, m_robust_policy))
{
--it_beyond;
}
else
{
return;
}
}
}
template <typename Piece, typename Section>
inline void calculate_turns(Piece const& piece1, Piece const& piece2,
Section const& section1, Section const& section2)
{
typedef typename boost::range_value<Rings const>::type ring_type;
typedef typename boost::range_value<Turns const>::type turn_type;
signed_size_type const piece1_first_index = piece1.first_seg_id.segment_index;
signed_size_type const piece2_first_index = piece2.first_seg_id.segment_index;
if (piece1_first_index < 0 || piece2_first_index < 0)
{
return;
}
// Get indices of part of offsetted_rings for this monotonic section:
signed_size_type const sec1_first_index = piece1_first_index + section1.begin_index;
signed_size_type const sec2_first_index = piece2_first_index + section2.begin_index;
// index of last point in section, beyond-end is one further
signed_size_type const sec1_last_index = piece1_first_index + section1.end_index;
signed_size_type const sec2_last_index = piece2_first_index + section2.end_index;
// get geometry and iterators over these sections
ring_type const& ring1 = m_rings[piece1.first_seg_id.multi_index];
auto it1_first = boost::begin(ring1) + sec1_first_index;
auto it1_beyond = boost::begin(ring1) + sec1_last_index + 1;
ring_type const& ring2 = m_rings[piece2.first_seg_id.multi_index];
auto it2_first = boost::begin(ring2) + sec2_first_index;
auto it2_beyond = boost::begin(ring2) + sec2_last_index + 1;
// Set begin/end of monotonic ranges, in both x/y directions
signed_size_type index1 = sec1_first_index;
move_begin_iterator<0>(it1_first, it1_beyond, index1,
section1.directions[0], section1.bounding_box, section2.bounding_box);
move_end_iterator<0>(it1_first, it1_beyond,
section1.directions[0], section1.bounding_box, section2.bounding_box);
move_begin_iterator<1>(it1_first, it1_beyond, index1,
section1.directions[1], section1.bounding_box, section2.bounding_box);
move_end_iterator<1>(it1_first, it1_beyond,
section1.directions[1], section1.bounding_box, section2.bounding_box);
signed_size_type index2 = sec2_first_index;
move_begin_iterator<0>(it2_first, it2_beyond, index2,
section2.directions[0], section2.bounding_box, section1.bounding_box);
move_end_iterator<0>(it2_first, it2_beyond,
section2.directions[0], section2.bounding_box, section1.bounding_box);
move_begin_iterator<1>(it2_first, it2_beyond, index2,
section2.directions[1], section2.bounding_box, section1.bounding_box);
move_end_iterator<1>(it2_first, it2_beyond,
section2.directions[1], section2.bounding_box, section1.bounding_box);
turn_type the_model;
the_model.operations[0].piece_index = piece1.index;
the_model.operations[0].seg_id = piece1.first_seg_id;
the_model.operations[0].seg_id.segment_index = index1; // override
auto it1 = it1_first;
for (auto prev1 = it1++;
it1 != it1_beyond;
prev1 = it1++, the_model.operations[0].seg_id.segment_index++)
{
the_model.operations[1].piece_index = piece2.index;
the_model.operations[1].seg_id = piece2.first_seg_id;
the_model.operations[1].seg_id.segment_index = index2; // override
unique_sub_range_from_piece<ring_type> unique_sub_range1(ring1, prev1, it1);
auto it2 = it2_first;
for (auto prev2 = it2++;
it2 != it2_beyond;
prev2 = it2++, the_model.operations[1].seg_id.segment_index++)
{
unique_sub_range_from_piece<ring_type> unique_sub_range2(ring2, prev2, it2);
typedef detail::overlay::get_turn_info
<
detail::overlay::assign_policy_only_start_turns
> turn_policy;
turn_policy::apply(unique_sub_range1, unique_sub_range2,
the_model,
m_strategy,
m_robust_policy,
std::back_inserter(m_turns));
}
}
}
public:
piece_turn_visitor(Pieces const& pieces,
Rings const& ring_collection,
Turns& turns,
Strategy const& strategy,
RobustPolicy const& robust_policy)
: m_pieces(pieces)
, m_rings(ring_collection)
, m_turns(turns)
, m_strategy(strategy)
, m_robust_policy(robust_policy)
{}
template <typename Section>
inline bool apply(Section const& section1, Section const& section2,
bool first = true)
{
boost::ignore_unused(first);
typedef typename boost::range_value<Pieces const>::type piece_type;
piece_type const& piece1 = m_pieces[section1.ring_id.source_index];
piece_type const& piece2 = m_pieces[section2.ring_id.source_index];
if ( piece1.index == piece2.index
|| is_adjacent(piece1, piece2)
|| is_on_same_convex_ring(piece1, piece2)
|| detail::disjoint::disjoint_box_box(section1.bounding_box,
section2.bounding_box,
m_strategy) )
{
return true;
}
calculate_turns(piece1, piece2, section1, section2);
return true;
}
};
}} // namespace detail::buffer
#endif // DOXYGEN_NO_DETAIL
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_GET_PIECE_TURNS_HPP
@@ -0,0 +1,214 @@
// Boost.Geometry (aka GGL, Generic Geometry Library)
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
// Copyright (c) 2008-2012 Bruno Lalande, Paris, France.
// Copyright (c) 2009-2012 Mateusz Loskot, London, UK.
// This file was modified by Oracle on 2017-2022.
// Modifications copyright (c) 2017-2022 Oracle and/or its affiliates.
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
// (geolib/GGL), copyright (c) 1995-2010 Geodan, 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_BUFFER_IMPLEMENTATION_HPP
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_IMPLEMENTATION_HPP
#include <boost/range/value_type.hpp>
#include <boost/geometry/algorithms/detail/buffer/buffer_box.hpp>
#include <boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp>
#include <boost/geometry/algorithms/detail/buffer/interface.hpp>
#include <boost/geometry/algorithms/detail/visit.hpp> // for GC
#include <boost/geometry/algorithms/envelope.hpp>
#include <boost/geometry/algorithms/is_empty.hpp>
#include <boost/geometry/algorithms/union.hpp> // for GC
#include <boost/geometry/arithmetic/arithmetic.hpp>
#include <boost/geometry/geometries/box.hpp>
#include <boost/geometry/strategies/buffer/cartesian.hpp>
#include <boost/geometry/strategies/buffer/geographic.hpp>
#include <boost/geometry/strategies/buffer/spherical.hpp>
#include <boost/geometry/util/math.hpp>
#include <boost/geometry/util/range.hpp>
namespace boost { namespace geometry
{
#ifndef DOXYGEN_NO_DISPATCH
namespace dispatch
{
template <typename BoxIn, typename BoxOut>
struct buffer_dc<BoxIn, BoxOut, box_tag, box_tag>
{
template <typename Distance>
static inline void apply(BoxIn const& box_in, BoxOut& box_out,
Distance const& distance, Distance const& )
{
detail::buffer::buffer_box(box_in, distance, box_out);
}
};
template <typename Input, typename Output, typename TagIn>
struct buffer_all<Input, Output, TagIn, multi_polygon_tag>
{
template
<
typename DistanceStrategy,
typename SideStrategy,
typename JoinStrategy,
typename EndStrategy,
typename PointStrategy,
typename Strategies
>
static inline void apply(Input const& geometry_in,
Output& geometry_out,
DistanceStrategy const& distance_strategy,
SideStrategy const& side_strategy,
JoinStrategy const& join_strategy,
EndStrategy const& end_strategy,
PointStrategy const& point_strategy,
Strategies const& strategies)
{
typedef typename boost::range_value<Output>::type polygon_type;
typedef typename point_type<Input>::type point_type;
typedef typename rescale_policy_type
<
point_type,
typename geometry::cs_tag<point_type>::type
>::type rescale_policy_type;
if (geometry::is_empty(geometry_in))
{
// Then output geometry is kept empty as well
return;
}
model::box<point_type> box;
geometry::envelope(geometry_in, box);
geometry::buffer(box, box, distance_strategy.max_distance(join_strategy, end_strategy));
rescale_policy_type rescale_policy
= boost::geometry::get_rescale_policy<rescale_policy_type>(
box, strategies);
detail::buffer::buffer_inserter<polygon_type>(geometry_in,
range::back_inserter(geometry_out),
distance_strategy,
side_strategy,
join_strategy,
end_strategy,
point_strategy,
strategies,
rescale_policy);
}
};
template <typename Input, typename Output>
struct buffer_all<Input, Output, geometry_collection_tag, multi_polygon_tag>
{
template
<
typename DistanceStrategy,
typename SideStrategy,
typename JoinStrategy,
typename EndStrategy,
typename PointStrategy,
typename Strategies
>
static inline void apply(Input const& geometry_in,
Output& geometry_out,
DistanceStrategy const& distance_strategy,
SideStrategy const& side_strategy,
JoinStrategy const& join_strategy,
EndStrategy const& end_strategy,
PointStrategy const& point_strategy,
Strategies const& strategies)
{
// NOTE: The buffer normally calculates everything at once (by pieces) and traverses all
// of them to apply the union operation. Not even by merging elements. But that is
// complex and has led to issues as well. Here intermediate results are calculated
// with buffer and the results are merged afterwards.
// NOTE: This algorithm merges partial results iteratively.
// We could first gather all of the results and after that
// use some more optimal method like merge_elements().
detail::visit_breadth_first([&](auto const& g)
{
Output buffer_result;
buffer_all
<
util::remove_cref_t<decltype(g)>, Output
>::apply(g, buffer_result, distance_strategy, side_strategy,
join_strategy, end_strategy, point_strategy, strategies);
if (! geometry::is_empty(buffer_result))
{
Output union_result;
geometry::union_(geometry_out, buffer_result, union_result, strategies);
geometry_out = std::move(union_result);
}
return true;
}, geometry_in);
}
};
template <typename Input, typename Output>
struct buffer_all<Input, Output, geometry_collection_tag, geometry_collection_tag>
{
template
<
typename DistanceStrategy,
typename SideStrategy,
typename JoinStrategy,
typename EndStrategy,
typename PointStrategy,
typename Strategies
>
static inline void apply(Input const& geometry_in,
Output& geometry_out,
DistanceStrategy const& distance_strategy,
SideStrategy const& side_strategy,
JoinStrategy const& join_strategy,
EndStrategy const& end_strategy,
PointStrategy const& point_strategy,
Strategies const& strategies)
{
// NOTE: We could also allow returning GC containing only polygons.
// We'd have to wrap them in model::multi_polygon and then
// iteratively emplace_back() into the GC.
using mpo_t = typename util::sequence_find_if
<
typename traits::geometry_types<Output>::type,
util::is_multi_polygon
>::type;
mpo_t result;
buffer_all
<
Input, mpo_t
>::apply(geometry_in, result, distance_strategy, side_strategy,
join_strategy, end_strategy, point_strategy, strategies);
range::emplace_back(geometry_out, std::move(result));
}
};
template <typename Input, typename Output, typename TagIn>
struct buffer_all<Input, Output, TagIn, geometry_collection_tag>
: buffer_all<Input, Output, geometry_collection_tag, geometry_collection_tag>
{};
} // namespace dispatch
#endif // DOXYGEN_NO_DISPATCH
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_IMPLEMENTATION_HPP
+279
View File
@@ -0,0 +1,279 @@
// Boost.Geometry (aka GGL, Generic Geometry Library)
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
// Copyright (c) 2008-2012 Bruno Lalande, Paris, France.
// Copyright (c) 2009-2012 Mateusz Loskot, London, UK.
// This file was modified by Oracle on 2017-2022.
// Modifications copyright (c) 2017-2022 Oracle and/or its affiliates.
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
// (geolib/GGL), copyright (c) 1995-2010 Geodan, 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_BUFFER_INTERFACE_HPP
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_INTERFACE_HPP
#include <boost/geometry/algorithms/clear.hpp>
#include <boost/geometry/algorithms/not_implemented.hpp>
#include <boost/geometry/core/visit.hpp>
#include <boost/geometry/core/tag.hpp>
#include <boost/geometry/geometries/adapted/boost_variant.hpp>
#include <boost/geometry/geometries/concepts/check.hpp>
#include <boost/geometry/strategies/buffer/services.hpp>
#include <boost/geometry/util/type_traits_std.hpp>
namespace boost { namespace geometry
{
#ifndef DOXYGEN_NO_DISPATCH
namespace dispatch
{
template
<
typename Input,
typename Output,
typename TagIn = typename tag<Input>::type,
typename TagOut = typename tag<Output>::type
>
struct buffer_dc : not_implemented<TagIn, TagOut>
{};
template
<
typename Input,
typename Output,
typename TagIn = typename tag<Input>::type,
typename TagOut = typename tag<Output>::type
>
struct buffer_all : not_implemented<TagIn, TagOut>
{};
} // namespace dispatch
#endif // DOXYGEN_NO_DISPATCH
namespace resolve_dynamic
{
template
<
typename Input,
typename TagIn = typename geometry::tag<Input>::type
>
struct buffer_dc
{
template <typename Output, typename Distance>
static inline void apply(Input const& geometry_in,
Output& geometry_out,
Distance const& distance,
Distance const& chord_length)
{
dispatch::buffer_dc<Input, Output>::apply(geometry_in, geometry_out, distance, chord_length);
}
};
template <typename Input>
struct buffer_dc<Input, dynamic_geometry_tag>
{
template <typename Output, typename Distance>
static inline void apply(Input const& geometry_in,
Output& geometry_out,
Distance const& distance,
Distance const& chord_length)
{
traits::visit<Input>::apply([&](auto const& g)
{
dispatch::buffer_dc
<
util::remove_cref_t<decltype(g)>, Output
>::apply(g, geometry_out, distance, chord_length);
}, geometry_in);
}
};
template
<
typename Input,
typename TagIn = typename geometry::tag<Input>::type
>
struct buffer_all
{
template
<
typename Output,
typename DistanceStrategy,
typename SideStrategy,
typename JoinStrategy,
typename EndStrategy,
typename PointStrategy
>
static inline void apply(Input const& geometry_in,
Output& geometry_out,
DistanceStrategy const& distance_strategy,
SideStrategy const& side_strategy,
JoinStrategy const& join_strategy,
EndStrategy const& end_strategy,
PointStrategy const& point_strategy)
{
typename strategies::buffer::services::default_strategy
<
Input
>::type strategies;
dispatch::buffer_all
<
Input, Output
>::apply(geometry_in, geometry_out, distance_strategy, side_strategy,
join_strategy, end_strategy, point_strategy, strategies);
}
};
template <typename Input>
struct buffer_all<Input, dynamic_geometry_tag>
{
template
<
typename Output,
typename DistanceStrategy,
typename SideStrategy,
typename JoinStrategy,
typename EndStrategy,
typename PointStrategy
>
static inline void apply(Input const& geometry_in,
Output& geometry_out,
DistanceStrategy const& distance_strategy,
SideStrategy const& side_strategy,
JoinStrategy const& join_strategy,
EndStrategy const& end_strategy,
PointStrategy const& point_strategy)
{
traits::visit<Input>::apply([&](auto const& g)
{
buffer_all
<
util::remove_cref_t<decltype(g)>
>::apply(g, geometry_out, distance_strategy, side_strategy,
join_strategy, end_strategy, point_strategy);
}, geometry_in);
}
};
} // namespace resolve_dynamic
/*!
\brief \brief_calc{buffer}
\ingroup buffer
\details \details_calc{buffer, \det_buffer}.
\tparam Input \tparam_geometry
\tparam Output \tparam_geometry
\tparam Distance \tparam_numeric
\param geometry_in \param_geometry
\param geometry_out \param_geometry
\param distance The distance to be used for the buffer
\param chord_length (optional) The length of the chord's in the generated arcs around points or bends
\qbk{[include reference/algorithms/buffer.qbk]}
*/
template <typename Input, typename Output, typename Distance>
inline void buffer(Input const& geometry_in, Output& geometry_out,
Distance const& distance, Distance const& chord_length = -1)
{
concepts::check<Input const>();
concepts::check<Output>();
resolve_dynamic::buffer_dc<Input>::apply(geometry_in, geometry_out, distance, chord_length);
}
/*!
\brief \brief_calc{buffer}
\ingroup buffer
\details \details_calc{return_buffer, \det_buffer}. \details_return{buffer}.
\tparam Input \tparam_geometry
\tparam Output \tparam_geometry
\tparam Distance \tparam_numeric
\param geometry \param_geometry
\param distance The distance to be used for the buffer
\param chord_length (optional) The length of the chord's in the generated arcs
around points or bends (RESERVED, NOT YET USED)
\return \return_calc{buffer}
*/
template <typename Output, typename Input, typename Distance>
inline Output return_buffer(Input const& geometry, Distance const& distance,
Distance const& chord_length = -1)
{
concepts::check<Input const>();
concepts::check<Output>();
Output geometry_out;
resolve_dynamic::buffer_dc<Input>::apply(geometry, geometry_out, distance, chord_length);
return geometry_out;
}
/*!
\brief \brief_calc{buffer}
\ingroup buffer
\details \details_calc{buffer, \det_buffer}.
\tparam GeometryIn \tparam_geometry
\tparam GeometryOut \tparam_geometry{GeometryOut}
\tparam DistanceStrategy A strategy defining distance (or radius)
\tparam SideStrategy A strategy defining creation along sides
\tparam JoinStrategy A strategy defining creation around convex corners
\tparam EndStrategy A strategy defining creation at linestring ends
\tparam PointStrategy A strategy defining creation around points
\param geometry_in \param_geometry
\param geometry_out output geometry, e.g. multi polygon,
will contain a buffered version of the input geometry
\param distance_strategy The distance strategy to be used
\param side_strategy The side strategy to be used
\param join_strategy The join strategy to be used
\param end_strategy The end strategy to be used
\param point_strategy The point strategy to be used
\qbk{distinguish,with strategies}
\qbk{[include reference/algorithms/buffer_with_strategies.qbk]}
*/
template
<
typename GeometryIn,
typename GeometryOut,
typename DistanceStrategy,
typename SideStrategy,
typename JoinStrategy,
typename EndStrategy,
typename PointStrategy
>
inline void buffer(GeometryIn const& geometry_in,
GeometryOut& geometry_out,
DistanceStrategy const& distance_strategy,
SideStrategy const& side_strategy,
JoinStrategy const& join_strategy,
EndStrategy const& end_strategy,
PointStrategy const& point_strategy)
{
concepts::check<GeometryIn const>();
concepts::check<GeometryOut>();
geometry::clear(geometry_out);
resolve_dynamic::buffer_all
<
GeometryIn, GeometryOut
>::apply(geometry_in, geometry_out, distance_strategy, side_strategy,
join_strategy, end_strategy, point_strategy);
}
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_INTERFACE_HPP
@@ -0,0 +1,120 @@
// Boost.Geometry (aka GGL, Generic Geometry Library)
// Copyright (c) 2012-2020 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_BUFFER_LINE_LINE_INTERSECTION_HPP
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_LINE_LINE_INTERSECTION_HPP
#include <boost/geometry/algorithms/detail/make/make.hpp>
#include <boost/geometry/arithmetic/infinite_line_functions.hpp>
#include <boost/geometry/util/math.hpp>
namespace boost { namespace geometry
{
#ifndef DOXYGEN_NO_DETAIL
namespace detail { namespace buffer
{
struct line_line_intersection
{
template <typename Point>
static Point between_point(Point const& a, Point const& b)
{
Point result;
geometry::set<0>(result, (geometry::get<0>(a) + geometry::get<0>(b)) / 2.0);
geometry::set<1>(result, (geometry::get<1>(a) + geometry::get<1>(b)) / 2.0);
return result;
}
template <typename Point>
static bool
apply(Point const& pi, Point const& pj, Point const& qi, Point const& qj,
Point const& vertex, bool equidistant, Point& ip)
{
// Calculates ip (below) by either intersecting p (pi, pj)
// with q (qi, qj) or by taking a point between pj and qi (b) and
// intersecting r (b, v), where v is the original vertex, with p (or q).
// The reason for dual approach: p might be nearly collinear with q,
// and in that case the intersection points can lose precision
// (or be plainly wrong).
// Therefore it takes the most precise option (this is usually p, r)
//
// /qj |
// / |
// / / |
// / / |
// / / |
// /qi / |
// / |
// ip * + b * v |
// \ |
// \pj \ |
// \ \ |
// \ \ |
// \ \ |
// \pi \ |
//
// If generated sides along the segments can have an adapted distance,
// in a custom strategy, then the calculation of the point in between
// might be incorrect and the optimization is not used.
using ct = typename coordinate_type<Point>::type;
auto const p = detail::make::make_infinite_line<ct>(pi, pj);
auto const q = detail::make::make_infinite_line<ct>(qi, qj);
using line = decltype(p);
using arithmetic::determinant;
using arithmetic::assign_intersection_point;
// The denominator is the determinant of (a,b) values of lines p q
// | pa pa |
// | qb qb |
auto const denominator_pq = determinant<line, &line::a, &line::b>(p, q);
static decltype(denominator_pq) const zero = 0;
if (equidistant)
{
auto const between = between_point(pj, qi);
auto const r = detail::make::make_infinite_line<ct>(vertex, between);
auto const denominator_pr = determinant<line, &line::a, &line::b>(p, r);
if (math::equals(denominator_pq, zero)
&& math::equals(denominator_pr, zero))
{
// Degenerate case (for example when length results in <inf>)
return false;
}
ip = geometry::math::abs(denominator_pq) > geometry::math::abs(denominator_pr)
? assign_intersection_point<Point>(p, q, denominator_pq)
: assign_intersection_point<Point>(p, r, denominator_pr);
}
else
{
if (math::equals(denominator_pq, zero))
{
return false;
}
ip = assign_intersection_point<Point>(p, q, denominator_pq);
}
return true;
}
};
}} // namespace detail::buffer
#endif // DOXYGEN_NO_DETAIL
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_LINE_LINE_INTERSECTION_HPP
@@ -0,0 +1,512 @@
// Boost.Geometry (aka GGL, Generic Geometry Library)
// Copyright (c) 2020-2021 Barend Gehrels, Amsterdam, the Netherlands.
// Copyright (c) 2023 Adam Wulkiewicz, Lodz, Poland.
// This file was modified by Oracle on 2020-2022.
// Modifications copyright (c) 2020-2022, 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_BUFFER_PIECE_BORDER_HPP
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_PIECE_BORDER_HPP
#include <array>
#include <boost/core/addressof.hpp>
#include <boost/range/size.hpp>
#include <boost/geometry/core/assert.hpp>
#include <boost/geometry/core/config.hpp>
#include <boost/geometry/algorithms/assign.hpp>
#include <boost/geometry/algorithms/comparable_distance.hpp>
#include <boost/geometry/algorithms/equals.hpp>
#include <boost/geometry/algorithms/expand.hpp>
#include <boost/geometry/algorithms/detail/buffer/buffer_policies.hpp>
#include <boost/geometry/algorithms/detail/expand_by_epsilon.hpp>
#include <boost/geometry/strategies/cartesian/turn_in_ring_winding.hpp>
#include <boost/geometry/geometries/box.hpp>
#include <boost/geometry/geometries/segment.hpp>
namespace boost { namespace geometry
{
#ifndef DOXYGEN_NO_DETAIL
namespace detail
{
template <typename It, typename T, typename Compare>
inline bool get_range_around(It begin, It end, T const& value, Compare const& compare, It& lower, It& upper)
{
lower = end;
upper = end;
// Get first element not smaller than value
if (begin == end)
{
return false;
}
if (compare(value, *begin))
{
// The value is smaller than the first item, therefore not in range
return false;
}
// *(begin + std::distance(begin, end) - 1))
if (compare(*(end - 1), value))
{
// The last item is larger than the value, therefore not in range
return false;
}
// Assign the iterators.
// lower >= begin and lower < end
// upper > lower and upper <= end
// lower_bound points to first element NOT LESS than value - but because
// we want the first value LESS than value, we decrease it
lower = std::lower_bound(begin, end, value, compare);
// upper_bound points to first element of which value is LESS
upper = std::upper_bound(begin, end, value, compare);
if (lower != begin)
{
--lower;
}
if (upper != end)
{
++upper;
}
return true;
}
}
namespace detail { namespace buffer
{
//! Contains the border of the piece, consisting of 4 parts:
//! 1: the part of the offsetted ring (referenced, not copied)
//! 2: the part of the original (one or two points)
//! 3: the left part (from original to offsetted)
//! 4: the right part (from offsetted to original)
//! Besides that, it contains some properties of the piece(border);
//! - convexity
//! - envelope
//! - monotonicity of the offsetted ring
//! - min/max radius of a point buffer
//! - if it is a "reversed" piece (linear features with partly negative buffers)
template <typename Ring, typename Point>
struct piece_border
{
typedef typename geometry::coordinate_type<Point>::type coordinate_type;
typedef typename default_comparable_distance_result<Point>::type radius_type;
typedef typename geometry::strategy::buffer::turn_in_ring_winding<coordinate_type>::state_type state_type;
bool m_reversed;
// Points from the offsetted ring. They are not copied, this structure
// refers to those points
Ring const* m_ring;
std::size_t m_begin;
std::size_t m_end;
// Points from the original (one or two, depending on piece shape)
// Note, if there are 2 points, they are REVERSED w.r.t. the original
// Therefore here we can walk in its order.
std::array<Point, 2> m_originals;
std::size_t m_original_size;
geometry::model::box<Point> m_envelope;
bool m_has_envelope;
// True if piece is determined as "convex"
bool m_is_convex;
// True if offsetted part is monotonically changing in x-direction
bool m_is_monotonic_increasing;
bool m_is_monotonic_decreasing;
radius_type m_min_comparable_radius;
radius_type m_max_comparable_radius;
piece_border()
: m_reversed(false)
, m_ring(NULL)
, m_begin(0)
, m_end(0)
, m_original_size(0)
, m_has_envelope(false)
, m_is_convex(false)
, m_is_monotonic_increasing(false)
, m_is_monotonic_decreasing(false)
, m_min_comparable_radius(0)
, m_max_comparable_radius(0)
{
}
// Only used for debugging (SVG)
Ring get_full_ring() const
{
Ring result;
if (ring_or_original_empty())
{
return result;
}
std::copy(m_ring->begin() + m_begin,
m_ring->begin() + m_end,
std::back_inserter(result));
std::copy(m_originals.begin(),
m_originals.begin() + m_original_size,
std::back_inserter(result));
// Add the closing point
result.push_back(*(m_ring->begin() + m_begin));
return result;
}
template <typename Strategy>
void get_properties_of_border(bool is_point_buffer, Point const& center,
Strategy const& strategy)
{
m_has_envelope = calculate_envelope(m_envelope, strategy);
if (m_has_envelope)
{
// Take roundings into account, enlarge box
geometry::detail::expand_by_epsilon(m_envelope);
}
if (! ring_or_original_empty() && is_point_buffer)
{
// Determine min/max radius
calculate_radii(center, m_ring->begin() + m_begin, m_ring->begin() + m_end);
}
}
template <typename Strategy>
void get_properties_of_offsetted_ring_part(Strategy const& strategy)
{
if (! ring_or_original_empty())
{
m_is_convex = is_convex(strategy);
check_monotonicity(m_ring->begin() + m_begin, m_ring->begin() + m_end);
}
}
void set_offsetted(Ring const& ring, std::size_t begin, std::size_t end)
{
BOOST_GEOMETRY_ASSERT(begin <= end);
BOOST_GEOMETRY_ASSERT(begin < boost::size(ring));
BOOST_GEOMETRY_ASSERT(end <= boost::size(ring));
m_ring = boost::addressof(ring);
m_begin = begin;
m_end = end;
}
void add_original_point(Point const& point)
{
BOOST_GEOMETRY_ASSERT(m_original_size < 2);
m_originals[m_original_size++] = point;
}
template <typename Box, typename Strategy>
bool calculate_envelope(Box& envelope, Strategy const& strategy) const
{
geometry::assign_inverse(envelope);
if (ring_or_original_empty())
{
return false;
}
expand_envelope(envelope, m_ring->begin() + m_begin, m_ring->begin() + m_end, strategy);
expand_envelope(envelope, m_originals.begin(), m_originals.begin() + m_original_size, strategy);
return true;
}
// Whatever the return value, the state should be checked.
template <typename TurnPoint, typename State>
bool point_on_piece(TurnPoint const& point,
bool one_sided, bool is_linear_end_point,
State& state) const
{
if (ring_or_original_empty())
{
return false;
}
// Walk over the different parts of the ring, in clockwise order
// For performance reasons: start with the helper part (one segment)
// then the original part (one segment, if any), then the other helper
// part (one segment), and only then the offsetted part
// (probably more segments, check monotonicity)
geometry::strategy::buffer::turn_in_ring_winding<coordinate_type> tir;
Point const offsetted_front = *(m_ring->begin() + m_begin);
Point const offsetted_back = *(m_ring->begin() + m_end - 1);
// For onesided buffers, or turns colocated with linear end points,
// the place on the ring is changed to offsetted (because of colocation)
geometry::strategy::buffer::place_on_ring_type const por_original
= adapted_place_on_ring(geometry::strategy::buffer::place_on_ring_original,
one_sided, is_linear_end_point);
geometry::strategy::buffer::place_on_ring_type const por_from_offsetted
= adapted_place_on_ring(geometry::strategy::buffer::place_on_ring_from_offsetted,
one_sided, is_linear_end_point);
geometry::strategy::buffer::place_on_ring_type const por_to_offsetted
= adapted_place_on_ring(geometry::strategy::buffer::place_on_ring_to_offsetted,
one_sided, is_linear_end_point);
bool continue_processing = true;
if (m_original_size == 1)
{
// One point. Walk from last offsetted to point, and from point to first offsetted
continue_processing = step(point, offsetted_back, m_originals[0],
tir, por_from_offsetted, state)
&& step(point, m_originals[0], offsetted_front,
tir, por_to_offsetted, state);
}
else if (m_original_size == 2)
{
// Two original points. Walk from last offsetted point to first original point,
// then along original, then from second oginal to first offsetted point
continue_processing = step(point, offsetted_back, m_originals[0],
tir, por_from_offsetted, state)
&& step(point, m_originals[0], m_originals[1],
tir, por_original, state)
&& step(point, m_originals[1], offsetted_front,
tir, por_to_offsetted, state);
}
if (continue_processing)
{
// Check the offsetted ring (in rounded joins, these might be
// several segments)
walk_offsetted(point, m_ring->begin() + m_begin, m_ring->begin() + m_end,
tir, state);
}
return true;
}
//! Returns true if empty (no ring, or no points, or no original)
bool ring_or_original_empty() const
{
return m_ring == NULL || m_begin >= m_end || m_original_size == 0;
}
private :
static geometry::strategy::buffer::place_on_ring_type
adapted_place_on_ring(geometry::strategy::buffer::place_on_ring_type target,
bool one_sided, bool is_linear_end_point)
{
return one_sided || is_linear_end_point
? geometry::strategy::buffer::place_on_ring_offsetted
: target;
}
template
<
typename TurnPoint, typename Iterator,
typename TiRStrategy,
typename State
>
bool walk_offsetted(TurnPoint const& point, Iterator begin, Iterator end,
TiRStrategy const & strategy,
State& state) const
{
Iterator it = begin;
Iterator beyond = end;
// Move iterators if the offsetted ring is monotonic increasing or decreasing
if (m_is_monotonic_increasing)
{
if (! get_range_around(begin, end, point, geometry::less<Point, 0>(), it, beyond))
{
return true;
}
}
else if (m_is_monotonic_decreasing)
{
if (! get_range_around(begin, end, point, geometry::greater<Point, 0>(), it, beyond))
{
return true;
}
}
for (Iterator previous = it++ ; it != beyond ; ++previous, ++it )
{
if (! step(point, *previous, *it, strategy,
geometry::strategy::buffer::place_on_ring_offsetted, state))
{
return false;
}
}
return true;
}
template <typename TurnPoint, typename TiRStrategy, typename State>
bool step(TurnPoint const& point, Point const& p1, Point const& p2,
TiRStrategy const& strategy,
geometry::strategy::buffer::place_on_ring_type place_on_ring, State& state) const
{
return strategy.apply(point, p1, p2, place_on_ring, m_is_convex, state);
}
template <typename It, typename Box, typename Strategy>
void expand_envelope(Box& envelope, It begin, It end, Strategy const& strategy) const
{
for (It it = begin; it != end; ++it)
{
geometry::expand(envelope, *it, strategy);
}
}
template <typename Strategy>
bool is_convex(Strategy const& strategy) const
{
if (ring_or_original_empty())
{
// Convexity is undetermined, and for this case it does not matter,
// because it is only used for optimization in point_on_piece,
// but that is not called if the piece border is not valid
return false;
}
if (m_end - m_begin <= 2)
{
// The offsetted ring part of this piece has only two points.
// If this is true, and the original ring part has only one point,
// a triangle and it is convex. If the original ring part has two
// points, it is a rectangle and theoretically could be concave,
// but because of the way the buffer is generated, that is never
// the case.
return true;
}
// The offsetted ring part of thie piece has at least three points
// (this is often the case in a piece marked as "join")
// We can assume all points of the offset ring are different, and also
// that all points on the original are different, and that the offsetted
// ring is different from the original(s)
Point const offsetted_front = *(m_ring->begin() + m_begin);
Point const offsetted_second = *(m_ring->begin() + m_begin + 1);
// These two points will be reassigned in every is_convex call
Point previous = offsetted_front;
Point current = offsetted_second;
// Verify the offsetted range (from the second point on), the original,
// and loop through the first two points of the offsetted range
bool const result = is_convex(previous, current, m_ring->begin() + m_begin + 2, m_ring->begin() + m_end, strategy)
&& is_convex(previous, current, m_originals.begin(), m_originals.begin() + m_original_size, strategy)
&& is_convex(previous, current, offsetted_front, strategy)
&& is_convex(previous, current, offsetted_second, strategy);
return result;
}
template <typename It, typename Strategy>
bool is_convex(Point& previous, Point& current, It begin, It end, Strategy const& strategy) const
{
for (It it = begin; it != end; ++it)
{
if (! is_convex(previous, current, *it, strategy))
{
return false;
}
}
return true;
}
template <typename Strategy>
bool is_convex(Point& previous, Point& current, Point const& next, Strategy const& strategy) const
{
int const side = strategy.side().apply(previous, current, next);
if (side == 1)
{
// Next is on the left side of clockwise ring: piece is not convex
return false;
}
if (! equals::equals_point_point(current, next, strategy))
{
previous = current;
current = next;
}
return true;
}
template <int Direction>
inline void step_for_monotonicity(Point const& current, Point const& next)
{
if (geometry::get<Direction>(current) >= geometry::get<Direction>(next))
{
m_is_monotonic_increasing = false;
}
if (geometry::get<Direction>(current) <= geometry::get<Direction>(next))
{
m_is_monotonic_decreasing = false;
}
}
template <typename It>
void check_monotonicity(It begin, It end)
{
m_is_monotonic_increasing = true;
m_is_monotonic_decreasing = true;
if (begin == end || begin + 1 == end)
{
return;
}
It it = begin;
for (It previous = it++; it != end; ++previous, ++it)
{
step_for_monotonicity<0>(*previous, *it);
}
}
template <typename It>
inline void calculate_radii(Point const& center, It begin, It end)
{
typedef geometry::model::referring_segment<Point const> segment_type;
bool first = true;
// An offsetted point-buffer ring around a point is supposed to be closed,
// therefore walking from start to end is fine.
It it = begin;
for (It previous = it++; it != end; ++previous, ++it)
{
Point const& p0 = *previous;
Point const& p1 = *it;
segment_type const s(p0, p1);
radius_type const d = geometry::comparable_distance(center, s);
if (first || d < m_min_comparable_radius)
{
m_min_comparable_radius = d;
}
if (first || d > m_max_comparable_radius)
{
m_max_comparable_radius = d;
}
first = false;
}
}
};
}} // namespace detail::buffer
#endif // DOXYGEN_NO_DETAIL
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_PIECE_BORDER_HPP
@@ -0,0 +1,300 @@
// Boost.Geometry (aka GGL, Generic Geometry Library)
// Copyright (c) 2014 Barend Gehrels, Amsterdam, the Netherlands.
// This file was modified by Oracle on 2016-2023.
// Modifications copyright (c) 2016-2023 Oracle and/or its affiliates.
// Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle
// 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_BUFFER_TURN_IN_ORIGINAL_VISITOR
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_TURN_IN_ORIGINAL_VISITOR
#include <boost/core/ignore_unused.hpp>
#include <boost/range/size.hpp>
#include <boost/geometry/core/coordinate_type.hpp>
#include <boost/geometry/algorithms/detail/buffer/buffer_policies.hpp>
#include <boost/geometry/algorithms/detail/disjoint/interface.hpp>
#include <boost/geometry/algorithms/expand.hpp>
#include <boost/geometry/strategies/agnostic/point_in_poly_winding.hpp>
#include <boost/geometry/strategies/buffer.hpp>
namespace boost { namespace geometry
{
#ifndef DOXYGEN_NO_DETAIL
namespace detail { namespace buffer
{
template <typename Strategy>
struct original_get_box
{
explicit original_get_box(Strategy const& strategy)
: m_strategy(strategy)
{}
template <typename Box, typename Original>
inline void apply(Box& total, Original const& original) const
{
assert_coordinate_type_equal(total, original.m_box);
geometry::expand(total, original.m_box, m_strategy);
}
Strategy const& m_strategy;
};
template <typename Strategy>
struct original_overlaps_box
{
explicit original_overlaps_box(Strategy const& strategy)
: m_strategy(strategy)
{}
template <typename Box, typename Original>
inline bool apply(Box const& box, Original const& original) const
{
assert_coordinate_type_equal(box, original.m_box);
return ! detail::disjoint::disjoint_box_box(box, original.m_box,
m_strategy);
}
Strategy const& m_strategy;
};
struct include_turn_policy
{
template <typename Turn>
static inline bool apply(Turn const& turn)
{
return turn.is_turn_traversable;
}
};
template <typename Strategy>
struct turn_in_original_overlaps_box
{
explicit turn_in_original_overlaps_box(Strategy const& strategy)
: m_strategy(strategy)
{}
template <typename Box, typename Turn>
inline bool apply(Box const& box, Turn const& turn) const
{
if (! turn.is_turn_traversable || turn.within_original)
{
// Skip all points already processed
return false;
}
return ! geometry::detail::disjoint::disjoint_point_box(
turn.point, box, m_strategy);
}
Strategy const& m_strategy;
};
//! Check if specified is in range of specified iterators
//! Return value of strategy (true if we can bail out)
template
<
typename Strategy,
typename State,
typename Point,
typename Iterator
>
inline bool point_in_range(Strategy& strategy, State& state,
Point const& point, Iterator begin, Iterator end)
{
boost::ignore_unused(strategy);
Iterator it = begin;
for (Iterator previous = it++; it != end; ++previous, ++it)
{
if (! strategy.apply(point, *previous, *it, state))
{
// We're probably on the boundary
return false;
}
}
return true;
}
template
<
typename Strategy,
typename State,
typename Point,
typename CoordinateType,
typename Iterator
>
inline bool point_in_section(Strategy& strategy, State& state,
Point const& point, CoordinateType const& point_x,
Iterator begin, Iterator end,
int direction)
{
if (direction == 0)
{
// Not a monotonic section, or no change in X-direction
return point_in_range(strategy, state, point, begin, end);
}
// We're in a monotonic section in x-direction
Iterator it = begin;
for (Iterator previous = it++; it != end; ++previous, ++it)
{
// Depending on sections.direction we can quit for this section
CoordinateType const previous_x = geometry::get<0>(*previous);
if (direction == 1 && point_x < previous_x)
{
// Section goes upwards, x increases, point is is below section
return true;
}
else if (direction == -1 && point_x > previous_x)
{
// Section goes downwards, x decreases, point is above section
return true;
}
if (! strategy.apply(point, *previous, *it, state))
{
// We're probably on the boundary
return false;
}
}
return true;
}
template <typename Point, typename Original, typename PointInGeometryStrategy>
inline int point_in_original(Point const& point, Original const& original,
PointInGeometryStrategy const& strategy)
{
typename PointInGeometryStrategy::state_type state;
if (boost::size(original.m_sections) == 0
|| boost::size(original.m_ring) - boost::size(original.m_sections) < 16)
{
// There are no sections, or it does not profit to walk over sections
// instead of over points. Boundary of 16 is arbitrary but can influence
// performance
point_in_range(strategy, state, point,
original.m_ring.begin(), original.m_ring.end());
return strategy.result(state);
}
auto const point_x = geometry::get<0>(point);
// Walk through all monotonic sections of this original
for (auto const& section : original.m_sections)
{
if (! section.duplicate
&& section.begin_index < section.end_index
&& point_x >= geometry::get<min_corner, 0>(section.bounding_box)
&& point_x <= geometry::get<max_corner, 0>(section.bounding_box))
{
// x-coordinate of point overlaps with section
if (! point_in_section(strategy, state, point, point_x,
boost::begin(original.m_ring) + section.begin_index,
boost::begin(original.m_ring) + section.end_index + 1,
section.directions[0]))
{
// We're probably on the boundary
break;
}
}
}
return strategy.result(state);
}
template <typename Turns, typename Strategy>
class turn_in_original_visitor
{
public:
turn_in_original_visitor(Turns& turns, Strategy const& strategy)
: m_mutable_turns(turns)
, m_strategy(strategy)
{}
template <typename Turn, typename Original>
inline bool apply(Turn const& turn, Original const& original)
{
if (boost::empty(original.m_ring))
{
// Skip empty rings
return true;
}
if (! turn.is_turn_traversable || turn.within_original)
{
// Skip all points already processed
return true;
}
if (geometry::disjoint(turn.point, original.m_box, m_strategy))
{
// Skip all disjoint
return true;
}
int const code = point_in_original(turn.point, original,
m_strategy.relate(turn.point, original.m_ring));
if (code == -1)
{
return true;
}
Turn& mutable_turn = m_mutable_turns[turn.turn_index];
if (code == 0)
{
// On border of original: always discard
mutable_turn.is_turn_traversable = false;
}
// Point is inside an original ring
if (original.m_is_interior)
{
mutable_turn.count_in_original--;
}
else if (original.m_has_interiors)
{
mutable_turn.count_in_original++;
}
else
{
// It is an exterior ring and there are no interior rings.
// Then we are completely ready with this turn
mutable_turn.within_original = true;
mutable_turn.count_in_original = 1;
}
return true;
}
private :
Turns& m_mutable_turns;
Strategy const& m_strategy;
};
}} // namespace detail::buffer
#endif // DOXYGEN_NO_DETAIL
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_TURN_IN_ORIGINAL_VISITOR
@@ -0,0 +1,194 @@
// Boost.Geometry (aka GGL, Generic Geometry Library)
// Copyright (c) 2012-2020 Barend Gehrels, Amsterdam, the Netherlands.
// Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland.
// This file was modified by Oracle on 2016-2022.
// Modifications copyright (c) 2016-2022 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_BUFFER_TURN_IN_PIECE_VISITOR_HPP
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_TURN_IN_PIECE_VISITOR_HPP
#include <boost/geometry/core/assert.hpp>
#include <boost/geometry/core/config.hpp>
#include <boost/geometry/algorithms/comparable_distance.hpp>
#include <boost/geometry/algorithms/covered_by.hpp>
#include <boost/geometry/algorithms/detail/disjoint/point_box.hpp>
#include <boost/geometry/algorithms/detail/disjoint/box_box.hpp>
#include <boost/geometry/algorithms/detail/dummy_geometries.hpp>
#include <boost/geometry/algorithms/detail/buffer/buffer_policies.hpp>
#include <boost/geometry/geometries/box.hpp>
namespace boost { namespace geometry
{
#ifndef DOXYGEN_NO_DETAIL
namespace detail { namespace buffer
{
template
<
typename CsTag,
typename Turns,
typename Pieces,
typename DistanceStrategy,
typename UmbrellaStrategy
>
class turn_in_piece_visitor
{
Turns& m_turns; // because partition is currently operating on const input only
Pieces const& m_pieces; // to check for piece-type
DistanceStrategy const& m_distance_strategy; // to check if point is on original or one_sided
UmbrellaStrategy const& m_umbrella_strategy;
template <typename Operation, typename Piece>
inline bool skip(Operation const& op, Piece const& piece) const
{
if (op.piece_index == piece.index)
{
return true;
}
Piece const& pc = m_pieces[op.piece_index];
if (pc.left_index == piece.index || pc.right_index == piece.index)
{
if (pc.type == strategy::buffer::buffered_flat_end)
{
// If it is a flat end, don't compare against its neighbor:
// it will always be located on one of the helper segments
return true;
}
if (pc.type == strategy::buffer::buffered_concave)
{
// If it is concave, the same applies: the IP will be
// located on one of the helper segments
return true;
}
}
return false;
}
template <typename NumericType>
inline bool is_one_sided(NumericType const& left, NumericType const& right) const
{
static NumericType const zero = 0;
return geometry::math::equals(left, zero)
|| geometry::math::equals(right, zero);
}
template <typename Point>
inline bool has_zero_distance_at(Point const& point) const
{
return is_one_sided(m_distance_strategy.apply(point, point,
strategy::buffer::buffer_side_left),
m_distance_strategy.apply(point, point,
strategy::buffer::buffer_side_right));
}
public:
inline turn_in_piece_visitor(Turns& turns, Pieces const& pieces,
DistanceStrategy const& distance_strategy,
UmbrellaStrategy const& umbrella_strategy)
: m_turns(turns)
, m_pieces(pieces)
, m_distance_strategy(distance_strategy)
, m_umbrella_strategy(umbrella_strategy)
{}
template <typename Turn, typename Piece>
inline bool apply(Turn const& turn, Piece const& piece)
{
if (! turn.is_turn_traversable)
{
// Already handled
return true;
}
if (piece.type == strategy::buffer::buffered_flat_end
|| piece.type == strategy::buffer::buffered_concave)
{
// Turns cannot be located within flat-end or concave pieces
return true;
}
if (skip(turn.operations[0], piece) || skip(turn.operations[1], piece))
{
return true;
}
return apply(turn, piece, piece.m_piece_border);
}
template <typename Turn, typename Piece, typename Border>
inline bool apply(Turn const& turn, Piece const& piece, Border const& border)
{
if (! geometry::covered_by(turn.point, border.m_envelope, m_umbrella_strategy))
{
// Easy check: if turn is not in the (expanded) envelope
return true;
}
if (piece.type == geometry::strategy::buffer::buffered_empty_side)
{
return false;
}
if (piece.type == geometry::strategy::buffer::buffered_point)
{
// Optimization for a buffer around points: if distance from center
// is not between min/max radius, it is either inside or outside,
// and more expensive checks are not necessary.
auto const d = geometry::comparable_distance(piece.m_center, turn.point,
m_umbrella_strategy);
if (d < border.m_min_comparable_radius)
{
Turn& mutable_turn = m_turns[turn.turn_index];
mutable_turn.is_turn_traversable = false;
return true;
}
if (d > border.m_max_comparable_radius)
{
return true;
}
}
// Check if buffer is one-sided (at this point), because then a point
// on the original border is not considered as within.
bool const one_sided = has_zero_distance_at(turn.point);
typename Border::state_type state;
if (! border.point_on_piece(turn.point, one_sided,
turn.is_linear_end_point, state))
{
return true;
}
if (state.is_inside() && ! state.is_on_boundary())
{
Turn& mutable_turn = m_turns[turn.turn_index];
mutable_turn.is_turn_traversable = false;
}
return true;
}
};
}} // namespace detail::buffer
#endif // DOXYGEN_NO_DETAIL
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_TURN_IN_PIECE_VISITOR_HPP