blob: a501e3f1975d5006680c462fa770d27b98d77a19 [file] [log] [blame]
// Boost.Geometry (aka GGL, Generic Geometry Library)
// Copyright (c) 2012-2014 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_BUFFERED_PIECE_COLLECTION_HPP
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_BUFFERED_PIECE_COLLECTION_HPP
#include <algorithm>
#include <cstddef>
#include <set>
#include <boost/core/ignore_unused.hpp>
#include <boost/range.hpp>
#include <boost/geometry/core/coordinate_type.hpp>
#include <boost/geometry/core/point_type.hpp>
#include <boost/geometry/algorithms/covered_by.hpp>
#include <boost/geometry/algorithms/envelope.hpp>
#include <boost/geometry/strategies/buffer.hpp>
#include <boost/geometry/geometries/ring.hpp>
#include <boost/geometry/algorithms/detail/buffer/buffered_ring.hpp>
#include <boost/geometry/algorithms/detail/buffer/buffer_policies.hpp>
#include <boost/geometry/algorithms/detail/buffer/get_piece_turns.hpp>
#include <boost/geometry/algorithms/detail/buffer/turn_in_piece_visitor.hpp>
#include <boost/geometry/algorithms/detail/buffer/turn_in_original_visitor.hpp>
#include <boost/geometry/algorithms/detail/disjoint/point_box.hpp>
#include <boost/geometry/algorithms/detail/overlay/add_rings.hpp>
#include <boost/geometry/algorithms/detail/overlay/assign_parents.hpp>
#include <boost/geometry/algorithms/detail/overlay/enrichment_info.hpp>
#include <boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp>
#include <boost/geometry/algorithms/detail/overlay/ring_properties.hpp>
#include <boost/geometry/algorithms/detail/overlay/traversal_info.hpp>
#include <boost/geometry/algorithms/detail/overlay/traverse.hpp>
#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
#include <boost/geometry/algorithms/detail/occupation_info.hpp>
#include <boost/geometry/algorithms/detail/partition.hpp>
#include <boost/geometry/algorithms/detail/sections/sectionalize.hpp>
#include <boost/geometry/algorithms/detail/sections/section_box_policies.hpp>
#include <boost/geometry/util/range.hpp>
namespace boost { namespace geometry
{
#ifndef DOXYGEN_NO_DETAIL
namespace detail { namespace buffer
{
enum segment_relation_code
{
segment_relation_on_left,
segment_relation_on_right,
segment_relation_within,
segment_relation_disjoint
};
/*
* Terminology
*
* Suppose we make a buffer (using blocked corners) of this rectangle:
*
* +-------+
* | |
* | rect |
* | |
* +-------+
*
* For the sides we get these four buffered side-pieces (marked with s)
* and four buffered corner pieces (marked with c)
*
* c---+---s---+---c
* | | piece | | <- see below for details of the middle top-side-piece
* +---+-------+---+
* | | | |
* s | rect | s <- two side pieces left/right of rect
* | | | |
* +---+-------+---+
* | | piece | | <- one side-piece below, and two corner pieces
* c---+---s---+---c
*
* The outer part of the picture above, using all pieces,
* form together the offsetted ring (marked with o below)
* The 8 pieces are part of the piece collection and use for inside-checks
* The inner parts form (using 1 or 2 points per piece, often co-located)
* form together the robust_polygons (marked with r below)
* The remaining piece-segments are helper-segments (marked with h)
*
* ooooooooooooooooo
* o h h o
* ohhhrrrrrrrrrhhho
* o r r o
* o r r o
* o r r o
* ohhhrrrrrrrrrhhho
* o h h o
* ooooooooooooooooo
*
*/
template <typename Ring, typename RobustPolicy>
struct buffered_piece_collection
{
typedef buffered_piece_collection<Ring, RobustPolicy> this_type;
typedef typename geometry::point_type<Ring>::type point_type;
typedef typename geometry::coordinate_type<Ring>::type coordinate_type;
typedef typename geometry::robust_point_type
<
point_type,
RobustPolicy
>::type robust_point_type;
// Robust ring/polygon type, always clockwise
typedef geometry::model::ring<robust_point_type> robust_ring_type;
typedef geometry::model::box<robust_point_type> robust_box_type;
typedef typename strategy::side::services::default_strategy
<
typename cs_tag<point_type>::type
>::type side_strategy;
typedef typename geometry::rescale_policy_type
<
typename geometry::point_type<Ring>::type
>::type rescale_policy_type;
typedef typename geometry::segment_ratio_type
<
point_type,
RobustPolicy
>::type segment_ratio_type;
typedef buffer_turn_info
<
point_type,
robust_point_type,
segment_ratio_type
> buffer_turn_info_type;
typedef buffer_turn_operation
<
point_type,
segment_ratio_type
> buffer_turn_operation_type;
typedef std::vector<buffer_turn_info_type> turn_vector_type;
struct robust_turn
{
int turn_index;
int operation_index;
robust_point_type point;
segment_identifier seg_id;
segment_ratio_type fraction;
};
struct piece
{
typedef robust_ring_type piece_robust_ring_type;
typedef geometry::section<robust_box_type, 1> section_type;
strategy::buffer::piece_type type;
int index;
int left_index; // points to previous piece of same ring
int right_index; // points to next piece of same ring
// The next two members (1, 2) form together a complete clockwise ring
// for each piece (with one dupped point)
// The complete clockwise ring is also included as a robust ring (3)
// 1: half, part of offsetted_rings
segment_identifier first_seg_id;
int last_segment_index; // no segment-identifier - it is the same as first_seg_id
int offsetted_count; // part in robust_ring which is part of offsetted ring
#if defined(BOOST_GEOMETRY_BUFFER_USE_HELPER_POINTS)
// 2: half, not part of offsetted rings - part of robust ring
std::vector<point_type> helper_points; // 4 points for side, 3 points for join - 0 points for flat-end
#endif
bool is_monotonic_increasing[2]; // 0=x, 1=y
bool is_monotonic_decreasing[2]; // 0=x, 1=y
// Monotonic sections of pieces around points
std::vector<section_type> sections;
// Robust representations
// 3: complete ring
robust_ring_type robust_ring;
robust_box_type robust_envelope;
robust_box_type robust_offsetted_envelope;
std::vector<robust_turn> robust_turns; // Used only in insert_rescaled_piece_turns - we might use a map instead
};
struct robust_original
{
typedef robust_ring_type original_robust_ring_type;
typedef geometry::sections<robust_box_type, 1> sections_type;
inline robust_original()
: m_is_interior(false)
, m_has_interiors(true)
{}
inline robust_original(robust_ring_type const& ring,
bool is_interior, bool has_interiors)
: m_ring(ring)
, m_is_interior(is_interior)
, m_has_interiors(has_interiors)
{
geometry::envelope(m_ring, m_box);
// create monotonic sections in y-dimension
typedef boost::mpl::vector_c<std::size_t, 1> dimensions;
geometry::sectionalize<false, dimensions>(m_ring,
detail::no_rescale_policy(), m_sections);
}
robust_ring_type m_ring;
robust_box_type m_box;
sections_type m_sections;
bool m_is_interior;
bool m_has_interiors;
};
typedef std::vector<piece> piece_vector_type;
piece_vector_type m_pieces;
turn_vector_type m_turns;
int m_first_piece_index;
buffered_ring_collection<buffered_ring<Ring> > offsetted_rings; // indexed by multi_index
std::vector<robust_original> robust_originals; // robust representation of the original(s)
robust_ring_type current_robust_ring;
buffered_ring_collection<Ring> traversed_rings;
segment_identifier current_segment_id;
// Specificly for offsetted rings around points
// but also for large joins with many points
typedef geometry::sections<robust_box_type, 2> sections_type;
sections_type monotonic_sections;
RobustPolicy const& m_robust_policy;
struct redundant_turn
{
inline bool operator()(buffer_turn_info_type const& turn) const
{
return turn.remove_on_multi;
}
};
buffered_piece_collection(RobustPolicy const& robust_policy)
: m_first_piece_index(-1)
, m_robust_policy(robust_policy)
{}
#if defined(BOOST_GEOMETRY_BUFFER_ENLARGED_CLUSTERS)
// Will (most probably) be removed later
template <typename OccupationMap>
inline void adapt_mapped_robust_point(OccupationMap const& map,
buffer_turn_info_type& turn, int distance) const
{
for (int x = -distance; x <= distance; x++)
{
for (int y = -distance; y <= distance; y++)
{
robust_point_type rp = turn.robust_point;
geometry::set<0>(rp, geometry::get<0>(rp) + x);
geometry::set<1>(rp, geometry::get<1>(rp) + y);
if (map.find(rp) != map.end())
{
turn.mapped_robust_point = rp;
return;
}
}
}
}
#endif
inline void get_occupation(
#if defined(BOOST_GEOMETRY_BUFFER_ENLARGED_CLUSTERS)
int distance = 0
#endif
)
{
typedef occupation_info<angle_info<robust_point_type, coordinate_type> >
buffer_occupation_info;
typedef std::map
<
robust_point_type,
buffer_occupation_info,
geometry::less<robust_point_type>
> occupation_map_type;
occupation_map_type occupation_map;
// 1: Add all intersection points to occupation map
typedef typename boost::range_iterator<turn_vector_type>::type
iterator_type;
for (iterator_type it = boost::begin(m_turns);
it != boost::end(m_turns);
++it)
{
if (it->location == location_ok)
{
#if defined(BOOST_GEOMETRY_BUFFER_ENLARGED_CLUSTERS)
if (distance > 0 && ! occupation_map.empty())
{
adapt_mapped_robust_point(occupation_map, *it, distance);
}
#endif
occupation_map[it->get_robust_point()].count++;
}
}
// Remove all points with one or more u/u points from the map
// (Alternatively, we could NOT do this here and change all u/u
// behaviour in overlay. Currently nothing is done: each polygon is
// just followed there. We could also always switch polygons there. For
// buffer behaviour, where 3 pieces might meet of which 2 (or more) form
// a u/u turn, this last option would have been better, probably).
for (iterator_type it = boost::begin(m_turns);
it != boost::end(m_turns);
++it)
{
if (it->both(detail::overlay::operation_union))
{
typename occupation_map_type::iterator mit =
occupation_map.find(it->get_robust_point());
if (mit != occupation_map.end())
{
occupation_map.erase(mit);
}
}
}
// 2: Remove all points from map which has only one
typename occupation_map_type::iterator it = occupation_map.begin();
while (it != occupation_map.end())
{
if (it->second.count <= 1)
{
typename occupation_map_type::iterator to_erase = it;
++it;
occupation_map.erase(to_erase);
}
else
{
++it;
}
}
if (occupation_map.empty())
{
return;
}
// 3: Add vectors (incoming->intersection-point,
// intersection-point -> outgoing)
// for all (co-located) points still present in the map
for (iterator_type it = boost::begin(m_turns);
it != boost::end(m_turns);
++it)
{
typename occupation_map_type::iterator mit =
occupation_map.find(it->get_robust_point());
if (mit != occupation_map.end())
{
buffer_occupation_info& info = mit->second;
for (int i = 0; i < 2; i++)
{
add_incoming_and_outgoing_angles(it->get_robust_point(), *it,
m_pieces,
i, it->operations[i].seg_id,
info);
}
it->count_on_multi++;
}
}
#if defined(BOOST_GEOMETRY_BUFFER_ENLARGED_CLUSTERS)
// X: Check rounding issues
if (distance == 0)
{
for (typename occupation_map_type::const_iterator it = occupation_map.begin();
it != occupation_map.end(); ++it)
{
if (it->second.has_rounding_issues(it->first))
{
if(distance == 0)
{
get_occupation(distance + 1);
return;
}
}
}
}
#endif
// Get left turns from all clusters
for (typename occupation_map_type::iterator it = occupation_map.begin();
it != occupation_map.end(); ++it)
{
it->second.get_left_turns(it->first, m_turns);
}
}
inline void classify_turns(bool linear)
{
for (typename boost::range_iterator<turn_vector_type>::type it =
boost::begin(m_turns); it != boost::end(m_turns); ++it)
{
if (it->count_within > 0)
{
it->location = inside_buffer;
}
if (it->count_on_original_boundary > 0 && ! linear)
{
it->location = inside_buffer;
}
if (it->count_within_near_offsetted > 0)
{
// Within can have in rare cases a rounding issue. We don't discard this
// point, so it can be used to continue started rings in traversal. But
// will never start a new ring from this type of points.
it->selectable_start = false;
}
}
}
template <typename DistanceStrategy>
inline void check_remaining_points(DistanceStrategy const& distance_strategy)
{
// Check if a turn is inside any of the originals
turn_in_original_visitor<turn_vector_type> visitor(m_turns);
geometry::partition
<
robust_box_type,
turn_get_box, turn_in_original_ovelaps_box,
original_get_box, original_ovelaps_box,
include_turn_policy, detail::partition::include_all_policy
>::apply(m_turns, robust_originals, visitor);
bool const deflate = distance_strategy.negative();
for (typename boost::range_iterator<turn_vector_type>::type it =
boost::begin(m_turns); it != boost::end(m_turns); ++it)
{
buffer_turn_info_type& turn = *it;
if (turn.location == location_ok)
{
if (deflate && turn.count_in_original <= 0)
{
// For deflate: it is not in original, discard
turn.location = location_discard;
}
else if (! deflate && turn.count_in_original > 0)
{
// For inflate: it is in original, discard
turn.location = location_discard;
}
}
}
}
inline bool assert_indices_in_robust_rings() const
{
geometry::equal_to<robust_point_type> comparator;
for (typename boost::range_iterator<turn_vector_type const>::type it =
boost::begin(m_turns); it != boost::end(m_turns); ++it)
{
for (int i = 0; i < 2; i++)
{
robust_point_type const &p1
= m_pieces[it->operations[i].piece_index].robust_ring
[it->operations[i].index_in_robust_ring];
robust_point_type const &p2 = it->robust_point;
if (! comparator(p1, p2))
{
return false;
}
}
}
return true;
}
inline void insert_rescaled_piece_turns()
{
// Add rescaled turn points to corresponding pieces
// (after this, each turn occurs twice)
int index = 0;
for (typename boost::range_iterator<turn_vector_type>::type it =
boost::begin(m_turns); it != boost::end(m_turns); ++it, ++index)
{
geometry::recalculate(it->robust_point, it->point, m_robust_policy);
#if defined(BOOST_GEOMETRY_BUFFER_ENLARGED_CLUSTERS)
it->mapped_robust_point = it->robust_point;
#endif
robust_turn turn;
it->turn_index = index;
turn.turn_index = index;
turn.point = it->robust_point;
for (int i = 0; i < 2; i++)
{
turn.operation_index = i;
turn.seg_id = it->operations[i].seg_id;
turn.fraction = it->operations[i].fraction;
piece& pc = m_pieces[it->operations[i].piece_index];
pc.robust_turns.push_back(turn);
// Take into account for the box (intersection points should fall inside,
// but in theory they can be one off because of rounding
geometry::expand(pc.robust_envelope, it->robust_point);
geometry::expand(pc.robust_offsetted_envelope, it->robust_point);
}
}
// Insert all rescaled turn-points into these rings, to form a
// reliable integer-based ring. All turns can be compared (inside) to this
// rings to see if they are inside.
for (typename piece_vector_type::iterator it = boost::begin(m_pieces);
it != boost::end(m_pieces);
++it)
{
piece& pc = *it;
int piece_segment_index = pc.first_seg_id.segment_index;
if (! pc.robust_turns.empty())
{
if (pc.robust_turns.size() > 1u)
{
std::sort(pc.robust_turns.begin(), pc.robust_turns.end(), buffer_operation_less());
}
// Walk through them, in reverse to insert at right index
int index_offset = pc.robust_turns.size() - 1;
for (typename std::vector<robust_turn>::const_reverse_iterator
rit = pc.robust_turns.rbegin();
rit != pc.robust_turns.rend();
++rit, --index_offset)
{
int const index_in_vector = 1 + rit->seg_id.segment_index - piece_segment_index;
BOOST_ASSERT
(
index_in_vector > 0
&& index_in_vector < pc.offsetted_count
);
pc.robust_ring.insert(boost::begin(pc.robust_ring) + index_in_vector, rit->point);
pc.offsetted_count++;
m_turns[rit->turn_index].operations[rit->operation_index].index_in_robust_ring = index_in_vector + index_offset;
}
}
}
BOOST_ASSERT(assert_indices_in_robust_rings());
}
template <std::size_t Dimension>
static inline void determine_monotonicity(piece& pc,
robust_point_type const& current,
robust_point_type const& next)
{
if (geometry::get<Dimension>(current) >= geometry::get<Dimension>(next))
{
pc.is_monotonic_increasing[Dimension] = false;
}
if (geometry::get<Dimension>(current) <= geometry::get<Dimension>(next))
{
pc.is_monotonic_decreasing[Dimension] = false;
}
}
static inline void determine_properties(piece& pc)
{
pc.is_monotonic_increasing[0] = true;
pc.is_monotonic_increasing[1] = true;
pc.is_monotonic_decreasing[0] = true;
pc.is_monotonic_decreasing[1] = true;
if (pc.offsetted_count < 2)
{
return;
}
typename robust_ring_type::const_iterator current = pc.robust_ring.begin();
typename robust_ring_type::const_iterator next = current + 1;
for (int i = 1; i < pc.offsetted_count; i++)
{
determine_monotonicity<0>(pc, *current, *next);
determine_monotonicity<1>(pc, *current, *next);
current = next;
++next;
}
}
void determine_properties()
{
for (typename piece_vector_type::iterator it = boost::begin(m_pieces);
it != boost::end(m_pieces);
++it)
{
determine_properties(*it);
}
}
inline void reverse_negative_robust_rings()
{
for (typename piece_vector_type::iterator it = boost::begin(m_pieces);
it != boost::end(m_pieces);
++it)
{
piece& pc = *it;
if (geometry::area(pc.robust_ring) < 0)
{
// Rings can be ccw:
// - in a concave piece
// - in a line-buffer with a negative buffer-distance
std::reverse(pc.robust_ring.begin(), pc.robust_ring.end());
}
}
}
inline void prepare_buffered_point_piece(piece& pc)
{
// create monotonic sections in y-dimension
typedef boost::mpl::vector_c<std::size_t, 1> dimensions;
geometry::sectionalize<false, dimensions>(pc.robust_ring,
detail::no_rescale_policy(), pc.sections);
// TODO (next phase) determine min/max radius
}
inline void prepare_buffered_point_pieces()
{
for (typename piece_vector_type::iterator it = boost::begin(m_pieces);
it != boost::end(m_pieces);
++it)
{
if (it->type == geometry::strategy::buffer::buffered_point)
{
prepare_buffered_point_piece(*it);
}
}
}
inline void get_turns()
{
for(typename boost::range_iterator<sections_type>::type it
= boost::begin(monotonic_sections);
it != boost::end(monotonic_sections);
++it)
{
enlarge_box(it->bounding_box, 1);
}
{
// Calculate the turns
piece_turn_visitor
<
piece_vector_type,
buffered_ring_collection<buffered_ring<Ring> >,
turn_vector_type,
RobustPolicy
> visitor(m_pieces, offsetted_rings, m_turns, m_robust_policy);
geometry::partition
<
robust_box_type,
detail::section::get_section_box,
detail::section::overlaps_section_box
>::apply(monotonic_sections, visitor);
}
insert_rescaled_piece_turns();
reverse_negative_robust_rings();
determine_properties();
prepare_buffered_point_pieces();
{
// Check if it is inside any of the pieces
turn_in_piece_visitor
<
turn_vector_type, piece_vector_type
> visitor(m_turns, m_pieces);
geometry::partition
<
robust_box_type,
turn_get_box, turn_ovelaps_box,
piece_get_box, piece_ovelaps_box
>::apply(m_turns, m_pieces, visitor);
}
}
inline void start_new_ring()
{
int const n = offsetted_rings.size();
current_segment_id.source_index = 0;
current_segment_id.multi_index = n;
current_segment_id.ring_index = -1;
current_segment_id.segment_index = 0;
offsetted_rings.resize(n + 1);
current_robust_ring.clear();
m_first_piece_index = boost::size(m_pieces);
}
inline void update_closing_point()
{
BOOST_ASSERT(! offsetted_rings.empty());
buffered_ring<Ring>& added = offsetted_rings.back();
if (! boost::empty(added))
{
range::back(added) = range::front(added);
}
}
inline void update_last_point(point_type const& p,
buffered_ring<Ring>& ring)
{
// For the first point of a new piece, and there were already
// points in the offsetted ring, for some piece types the first point
// is a duplicate of the last point of the previous piece.
// TODO: disable that, that point should not be added
// For now, it is made equal because due to numerical instability,
// it can be a tiny bit off, possibly causing a self-intersection
BOOST_ASSERT(boost::size(m_pieces) > 0);
if (! ring.empty()
&& current_segment_id.segment_index
== m_pieces.back().first_seg_id.segment_index)
{
ring.back() = p;
}
}
inline void finish_ring(bool is_interior = false, bool has_interiors = false)
{
if (m_first_piece_index == -1)
{
return;
}
if (m_first_piece_index < static_cast<int>(boost::size(m_pieces)))
{
// If piece was added
// Reassign left-of-first and right-of-last
geometry::range::at(m_pieces, m_first_piece_index).left_index
= boost::size(m_pieces) - 1;
geometry::range::back(m_pieces).right_index = m_first_piece_index;
}
m_first_piece_index = -1;
update_closing_point();
if (! current_robust_ring.empty())
{
BOOST_ASSERT
(
geometry::equals(current_robust_ring.front(),
current_robust_ring.back())
);
robust_originals.push_back(
robust_original(current_robust_ring,
is_interior, has_interiors));
}
}
inline void set_current_ring_concave()
{
BOOST_ASSERT(boost::size(offsetted_rings) > 0);
offsetted_rings.back().has_concave = true;
}
inline int add_point(point_type const& p)
{
BOOST_ASSERT(boost::size(offsetted_rings) > 0);
buffered_ring<Ring>& current_ring = offsetted_rings.back();
update_last_point(p, current_ring);
current_segment_id.segment_index++;
current_ring.push_back(p);
return current_ring.size();
}
//-------------------------------------------------------------------------
inline piece& create_piece(strategy::buffer::piece_type type,
bool decrease_segment_index_by_one)
{
if (type == strategy::buffer::buffered_concave)
{
offsetted_rings.back().has_concave = true;
}
piece pc;
pc.type = type;
pc.index = boost::size(m_pieces);
pc.first_seg_id = current_segment_id;
// Assign left/right (for first/last piece per ring they will be re-assigned later)
pc.left_index = pc.index - 1;
pc.right_index = pc.index + 1;
std::size_t const n = boost::size(offsetted_rings.back());
pc.first_seg_id.segment_index = decrease_segment_index_by_one ? n - 1 : n;
pc.last_segment_index = pc.first_seg_id.segment_index;
m_pieces.push_back(pc);
return m_pieces.back();
}
inline void init_rescale_piece(piece& pc, std::size_t helper_points_size)
{
if (pc.first_seg_id.segment_index < 0)
{
// This indicates an error situation: an earlier piece was empty
// It currently does not happen
// std::cout << "EMPTY " << pc.type << " " << pc.index << " " << pc.first_seg_id.multi_index << std::endl;
pc.offsetted_count = 0;
return;
}
BOOST_ASSERT(pc.first_seg_id.multi_index >= 0);
BOOST_ASSERT(pc.last_segment_index >= 0);
pc.offsetted_count = pc.last_segment_index - pc.first_seg_id.segment_index;
BOOST_ASSERT(pc.offsetted_count >= 0);
pc.robust_ring.reserve(pc.offsetted_count + helper_points_size);
// Add rescaled offsetted segments
{
buffered_ring<Ring> const& ring = offsetted_rings[pc.first_seg_id.multi_index];
typedef typename boost::range_iterator<const buffered_ring<Ring> >::type it_type;
for (it_type it = boost::begin(ring) + pc.first_seg_id.segment_index;
it != boost::begin(ring) + pc.last_segment_index;
++it)
{
robust_point_type point;
geometry::recalculate(point, *it, m_robust_policy);
pc.robust_ring.push_back(point);
}
}
}
inline robust_point_type add_helper_point(piece& pc, const point_type& point)
{
#if defined(BOOST_GEOMETRY_BUFFER_USE_HELPER_POINTS)
pc.helper_points.push_back(point);
#endif
robust_point_type rob_point;
geometry::recalculate(rob_point, point, m_robust_policy);
pc.robust_ring.push_back(rob_point);
return rob_point;
}
// TODO: this is shared with sectionalize, move to somewhere else (assign?)
template <typename Box, typename Value>
inline void enlarge_box(Box& box, Value value)
{
geometry::set<0, 0>(box, geometry::get<0, 0>(box) - value);
geometry::set<0, 1>(box, geometry::get<0, 1>(box) - value);
geometry::set<1, 0>(box, geometry::get<1, 0>(box) + value);
geometry::set<1, 1>(box, geometry::get<1, 1>(box) + value);
}
inline void calculate_robust_envelope(piece& pc)
{
if (pc.offsetted_count == 0)
{
return;
}
geometry::detail::envelope::envelope_range::apply(pc.robust_ring,
pc.robust_envelope);
geometry::assign_inverse(pc.robust_offsetted_envelope);
for (int i = 0; i < pc.offsetted_count; i++)
{
geometry::expand(pc.robust_offsetted_envelope, pc.robust_ring[i]);
}
// Take roundings into account, enlarge boxes with 1 integer
enlarge_box(pc.robust_envelope, 1);
enlarge_box(pc.robust_offsetted_envelope, 1);
}
inline void sectionalize(piece& pc)
{
buffered_ring<Ring> const& ring = offsetted_rings.back();
typedef geometry::detail::sectionalize::sectionalize_part
<
point_type,
boost::mpl::vector_c<std::size_t, 0, 1> // x,y dimension
> sectionalizer;
// Create a ring-identifier. The source-index is the piece index
// The multi_index is as in this collection (the ring), but not used here
// The ring_index is not used
ring_identifier ring_id(pc.index, pc.first_seg_id.multi_index, -1);
sectionalizer::apply(monotonic_sections,
boost::begin(ring) + pc.first_seg_id.segment_index,
boost::begin(ring) + pc.last_segment_index,
m_robust_policy,
ring_id, 10);
}
inline void finish_piece(piece& pc)
{
init_rescale_piece(pc, 0u);
calculate_robust_envelope(pc);
sectionalize(pc);
}
inline void finish_piece(piece& pc,
const point_type& point1,
const point_type& point2,
const point_type& point3)
{
init_rescale_piece(pc, 3u);
if (pc.offsetted_count == 0)
{
return;
}
add_helper_point(pc, point1);
robust_point_type mid_point = add_helper_point(pc, point2);
add_helper_point(pc, point3);
calculate_robust_envelope(pc);
sectionalize(pc);
current_robust_ring.push_back(mid_point);
}
inline void finish_piece(piece& pc,
const point_type& point1,
const point_type& point2,
const point_type& point3,
const point_type& point4)
{
init_rescale_piece(pc, 4u);
add_helper_point(pc, point1);
robust_point_type mid_point2 = add_helper_point(pc, point2);
robust_point_type mid_point1 = add_helper_point(pc, point3);
add_helper_point(pc, point4);
sectionalize(pc);
calculate_robust_envelope(pc);
// Add mid-points in other order to current helper_ring
current_robust_ring.push_back(mid_point1);
current_robust_ring.push_back(mid_point2);
}
inline void add_piece(strategy::buffer::piece_type type, point_type const& p,
point_type const& b1, point_type const& b2)
{
piece& pc = create_piece(type, false);
add_point(b1);
pc.last_segment_index = add_point(b2);
finish_piece(pc, b2, p, b1);
}
template <typename Range>
inline void add_range_to_piece(piece& pc, Range const& range, bool add_front)
{
BOOST_ASSERT(boost::size(range) != 0u);
typename Range::const_iterator it = boost::begin(range);
// If it follows a non-join (so basically the same piece-type) point b1 should be added.
// There should be two intersections later and it should be discarded.
// But for now we need it to calculate intersections
if (add_front)
{
add_point(*it);
}
for (++it; it != boost::end(range); ++it)
{
pc.last_segment_index = add_point(*it);
}
}
template <typename Range>
inline void add_piece(strategy::buffer::piece_type type, Range const& range,
bool decrease_segment_index_by_one)
{
piece& pc = create_piece(type, decrease_segment_index_by_one);
if (boost::size(range) > 0u)
{
add_range_to_piece(pc, range, offsetted_rings.back().empty());
}
finish_piece(pc);
}
template <typename Range>
inline void add_side_piece(point_type const& p1, point_type const& p2,
Range const& range, bool first)
{
BOOST_ASSERT(boost::size(range) >= 2u);
piece& pc = create_piece(strategy::buffer::buffered_segment, ! first);
add_range_to_piece(pc, range, first);
finish_piece(pc, range.back(), p2, p1, range.front());
}
template <typename Range>
inline void add_piece(strategy::buffer::piece_type type,
point_type const& p, Range const& range)
{
piece& pc = create_piece(type, true);
if (boost::size(range) > 0u)
{
add_range_to_piece(pc, range, offsetted_rings.back().empty());
finish_piece(pc, range.back(), p, range.front());
}
else
{
finish_piece(pc);
}
}
template <typename EndcapStrategy, typename Range>
inline void add_endcap(EndcapStrategy const& strategy, Range const& range,
point_type const& end_point)
{
boost::ignore_unused(strategy);
if (range.empty())
{
return;
}
strategy::buffer::piece_type pt = strategy.get_piece_type();
if (pt == strategy::buffer::buffered_flat_end)
{
// It is flat, should just be added, without helper segments
add_piece(pt, range, true);
}
else
{
// Normal case, it has an "inside", helper segments should be added
add_piece(pt, end_point, range);
}
}
//-------------------------------------------------------------------------
inline void enrich()
{
typedef typename strategy::side::services::default_strategy
<
typename cs_tag<Ring>::type
>::type side_strategy_type;
enrich_intersection_points<false, false>(m_turns,
detail::overlay::operation_union,
offsetted_rings, offsetted_rings,
m_robust_policy, side_strategy_type());
}
// Discards all rings which do have not-OK intersection points only.
// Those can never be traversed and should not be part of the output.
inline void discard_rings()
{
for (typename boost::range_iterator<turn_vector_type const>::type it =
boost::begin(m_turns); it != boost::end(m_turns); ++it)
{
if (it->location != location_ok)
{
offsetted_rings[it->operations[0].seg_id.multi_index].has_discarded_intersections = true;
offsetted_rings[it->operations[1].seg_id.multi_index].has_discarded_intersections = true;
}
else if (! it->both(detail::overlay::operation_union))
{
offsetted_rings[it->operations[0].seg_id.multi_index].has_accepted_intersections = true;
offsetted_rings[it->operations[1].seg_id.multi_index].has_accepted_intersections = true;
}
}
}
inline bool point_coveredby_original(point_type const& point)
{
robust_point_type any_point;
geometry::recalculate(any_point, point, m_robust_policy);
int count_in_original = 0;
// Check of the robust point of this outputted ring is in
// any of the robust original rings
// This can go quadratic if the input has many rings, and there
// are many untouched deflated rings around
for (typename std::vector<robust_original>::const_iterator it
= robust_originals.begin();
it != robust_originals.end();
++it)
{
robust_original const& original = *it;
if (detail::disjoint::disjoint_point_box(any_point,
original.m_box))
{
continue;
}
int const geometry_code
= detail::within::point_in_geometry(any_point,
original.m_ring);
if (geometry_code == -1)
{
// Outside, continue
continue;
}
// Apply for possibly nested interior rings
if (original.m_is_interior)
{
count_in_original--;
}
else if (original.m_has_interiors)
{
count_in_original++;
}
else
{
// Exterior ring without interior rings
return true;
}
}
return count_in_original > 0;
}
// For a deflate, all rings around inner rings which are untouched
// (no intersections/turns) and which are OUTSIDE the original should
// be discarded
inline void discard_nonintersecting_deflated_rings()
{
for(typename buffered_ring_collection<buffered_ring<Ring> >::iterator it
= boost::begin(offsetted_rings);
it != boost::end(offsetted_rings);
++it)
{
buffered_ring<Ring>& ring = *it;
if (! ring.has_intersections()
&& boost::size(ring) > 0u
&& geometry::area(ring) < 0)
{
if (! point_coveredby_original(geometry::range::front(ring)))
{
ring.is_untouched_outside_original = true;
}
}
}
}
inline void block_turns()
{
// To fix left-turn issues like #rt_u13
// But currently it causes more other issues than it fixes
// m_turns.erase
// (
// std::remove_if(boost::begin(m_turns), boost::end(m_turns),
// redundant_turn()),
// boost::end(m_turns)
// );
for (typename boost::range_iterator<turn_vector_type>::type it =
boost::begin(m_turns); it != boost::end(m_turns); ++it)
{
if (it->location != location_ok)
{
// Set it to blocked. They should not be discarded, to avoid
// generating rings over these turns
// Performance goes down a tiny bit from 161 s to 173 because there
// are sometimes much more turns.
// We might speed it up a bit by keeping only one blocked
// intersection per segment, but that is complex to program
// because each turn involves two segments
it->operations[0].operation = detail::overlay::operation_blocked;
it->operations[1].operation = detail::overlay::operation_blocked;
}
}
}
inline void traverse()
{
typedef detail::overlay::traverse
<
false, false,
buffered_ring_collection<buffered_ring<Ring> >,
buffered_ring_collection<buffered_ring<Ring > >,
backtrack_for_buffer
> traverser;
traversed_rings.clear();
traverser::apply(offsetted_rings, offsetted_rings,
detail::overlay::operation_union,
m_robust_policy, m_turns, traversed_rings);
}
inline void reverse()
{
for(typename buffered_ring_collection<buffered_ring<Ring> >::iterator it = boost::begin(offsetted_rings);
it != boost::end(offsetted_rings);
++it)
{
if (! it->has_intersections())
{
std::reverse(it->begin(), it->end());
}
}
for (typename boost::range_iterator<buffered_ring_collection<Ring> >::type
it = boost::begin(traversed_rings);
it != boost::end(traversed_rings);
++it)
{
std::reverse(it->begin(), it->end());
}
}
template <typename GeometryOutput, typename OutputIterator>
inline OutputIterator assign(OutputIterator out) const
{
typedef detail::overlay::ring_properties<point_type> properties;
std::map<ring_identifier, properties> selected;
// Select all rings which do not have any self-intersection
// Inner rings, for deflate, which do not have intersections, and
// which are outside originals, are skipped
// (other ones should be traversed)
int index = 0;
for(typename buffered_ring_collection<buffered_ring<Ring> >::const_iterator it = boost::begin(offsetted_rings);
it != boost::end(offsetted_rings);
++it, ++index)
{
if (! it->has_intersections()
&& ! it->is_untouched_outside_original)
{
ring_identifier id(0, index, -1);
selected[id] = properties(*it);
}
}
// Select all created rings
index = 0;
for (typename boost::range_iterator<buffered_ring_collection<Ring> const>::type
it = boost::begin(traversed_rings);
it != boost::end(traversed_rings);
++it, ++index)
{
ring_identifier id(2, index, -1);
selected[id] = properties(*it);
}
detail::overlay::assign_parents(offsetted_rings, traversed_rings, selected, true);
return detail::overlay::add_rings<GeometryOutput>(selected, offsetted_rings, traversed_rings, out);
}
};
}} // namespace detail::buffer
#endif // DOXYGEN_NO_DETAIL
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_BUFFERED_PIECE_COLLECTION_HPP