Initial commit

This commit is contained in:
Bassem Girgis
2018-12-20 17:34:07 -06:00
parent 7a2d899662
commit 81b4b9e273
34743 changed files with 5940233 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,241 @@
// Boost.Geometry (aka GGL, Generic Geometry Library)
// Copyright (c) 2012-2014 Barend Gehrels, Amsterdam, the Netherlands.
// This file was modified by Oracle on 2017.
// Modifications copyright (c) 2017, 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
#if ! defined(BOOST_GEOMETRY_NO_ROBUSTNESS)
# define BOOST_GEOMETRY_BUFFER_USE_SIDE_OF_INTERSECTION
#endif
#include <cstddef>
#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/detail/overlay/backtrack_check_si.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
{
enum intersection_location_type
{
location_ok, inside_buffer, location_discard
};
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 )
{}
};
// 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 for buffer including type of location, is_opposite, and helper variables
template <typename Point, typename RobustPoint, typename SegmentRatio>
struct buffer_turn_info
: public detail::overlay::turn_info
<
Point,
SegmentRatio,
buffer_turn_operation<Point, SegmentRatio>
>
{
typedef Point point_type;
typedef RobustPoint robust_point_type;
std::size_t turn_index; // TODO: this might go if partition can operate on non-const input
RobustPoint robust_point;
#if defined(BOOST_GEOMETRY_BUFFER_ENLARGED_CLUSTERS)
// Will (most probably) be removed later
RobustPoint mapped_robust_point; // alas... we still need to adapt our points, offsetting them 1 integer to be co-located with neighbours
#endif
inline RobustPoint const& get_robust_point() const
{
#if defined(BOOST_GEOMETRY_BUFFER_ENLARGED_CLUSTERS)
return mapped_robust_point;
#endif
return robust_point;
}
intersection_location_type location;
#if defined(BOOST_GEOMETRY_BUFFER_USE_SIDE_OF_INTERSECTION)
robust_point_type rob_pi, rob_pj, rob_qi, rob_qj;
#endif
std::size_t count_within;
bool within_original;
std::size_t count_on_original_boundary;
signed_size_type count_in_original; // increased by +1 for in ext.ring, -1 for int.ring
std::size_t count_on_offsetted;
std::size_t count_on_helper;
#if ! defined(BOOST_GEOMETRY_BUFFER_USE_SIDE_OF_INTERSECTION)
std::size_t count_within_near_offsetted;
#endif
bool remove_on_multi;
// Obsolete:
std::size_t count_on_occupied;
std::size_t count_on_multi;
inline buffer_turn_info()
: turn_index(0)
, location(location_ok)
, count_within(0)
, within_original(false)
, count_on_original_boundary(0)
, count_in_original(0)
, count_on_offsetted(0)
, count_on_helper(0)
#if ! defined(BOOST_GEOMETRY_BUFFER_USE_SIDE_OF_INTERSECTION)
, count_within_near_offsetted(0)
#endif
, remove_on_multi(false)
, count_on_occupied(0)
, count_on_multi(0)
{}
};
struct buffer_operation_less
{
template <typename Turn>
inline bool operator()(Turn const& left, Turn const& right) const
{
segment_identifier const& sl = left.seg_id;
segment_identifier const& sr = right.seg_id;
// Sort them descending
return sl == sr
? left.fraction < right.fraction
: sl < sr;
}
};
}} // namespace detail::buffer
#endif // DOXYGEN_NO_DETAIL
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_BUFFER_POLICIES_HPP

View File

@@ -0,0 +1,282 @@
// Boost.Geometry (aka GGL, Generic Geometry Library)
// Copyright (c) 2012-2015 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_RING
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_BUFFERED_RING
#include <cstddef>
#include <boost/range.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 dispatch
{
template <typename Geometry, bool IsConst>
struct check<Geometry, detail::buffer::buffered_ring_collection_tag, IsConst>
{
};
}
#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

View File

@@ -0,0 +1,304 @@
// Boost.Geometry (aka GGL, Generic Geometry Library)
// Copyright (c) 2012-2014 Barend Gehrels, Amsterdam, the Netherlands.
// This file was modified by Oracle on 2017.
// Modifications copyright (c) 2017 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/range.hpp>
#include <boost/geometry/algorithms/equals.hpp>
#include <boost/geometry/algorithms/expand.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
{
#if defined(BOOST_GEOMETRY_BUFFER_USE_SIDE_OF_INTERSECTION)
struct buffer_assign_turn
{
static bool const include_no_turn = false;
static bool const include_degenerate = false;
static bool const include_opposite = false;
template
<
typename Info,
typename Point1,
typename Point2,
typename IntersectionInfo
>
static inline void apply(Info& info,
Point1 const& /*p1*/,
Point2 const& /*p2*/,
IntersectionInfo const& iinfo)
{
info.rob_pi = iinfo.rpi();
info.rob_pj = iinfo.rpj();
info.rob_qi = iinfo.rqi();
info.rob_qj = iinfo.rqj();
}
};
#endif
template
<
typename Pieces,
typename Rings,
typename Turns,
typename IntersectionStrategy,
typename RobustPolicy
>
class piece_turn_visitor
{
Pieces const& m_pieces;
Rings const& m_rings;
Turns& m_turns;
IntersectionStrategy const& m_intersection_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 <typename Range, typename Iterator>
inline void move_to_next_point(Range const& range, Iterator& next) const
{
++next;
if (next == boost::end(range))
{
next = boost::begin(range) + 1;
}
}
template <typename Range, typename Iterator>
inline Iterator next_point(Range const& range, Iterator it) const
{
Iterator result = it;
move_to_next_point(range, result);
// TODO: we could use either piece-boundaries, or comparison with
// robust points, to check if the point equals the last one
while(geometry::equals(*it, *result))
{
move_to_next_point(range, result);
}
return result;
}
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& other_bounding_box)
{
for(; it_begin != it_beyond
&& it_begin + 1 != it_beyond
&& detail::section::preceding<Dimension>(dir, *(it_begin + 1),
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& 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),
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;
typedef typename boost::range_iterator<ring_type const>::type iterator;
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];
iterator it1_first = boost::begin(ring1) + sec1_first_index;
iterator it1_beyond = boost::begin(ring1) + sec1_last_index + 1;
ring_type const& ring2 = m_rings[piece2.first_seg_id.multi_index];
iterator it2_first = boost::begin(ring2) + sec2_first_index;
iterator 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], section2.bounding_box);
move_end_iterator<0>(it1_first, it1_beyond,
section1.directions[0], section2.bounding_box);
move_begin_iterator<1>(it1_first, it1_beyond, index1,
section1.directions[1], section2.bounding_box);
move_end_iterator<1>(it1_first, it1_beyond,
section1.directions[1], section2.bounding_box);
signed_size_type index2 = sec2_first_index;
move_begin_iterator<0>(it2_first, it2_beyond, index2,
section2.directions[0], section1.bounding_box);
move_end_iterator<0>(it2_first, it2_beyond,
section2.directions[0], section1.bounding_box);
move_begin_iterator<1>(it2_first, it2_beyond, index2,
section2.directions[1], section1.bounding_box);
move_end_iterator<1>(it2_first, it2_beyond,
section2.directions[1], 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
iterator it1 = it1_first;
for (iterator 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
iterator next1 = next_point(ring1, it1);
iterator it2 = it2_first;
for (iterator prev2 = it2++;
it2 != it2_beyond;
prev2 = it2++, the_model.operations[1].seg_id.segment_index++)
{
iterator next2 = next_point(ring2, it2);
// TODO: internally get_turn_info calculates robust points.
// But they are already calculated.
// We should be able to use them.
// this means passing them to this visitor,
// and iterating in sync with them...
typedef detail::overlay::get_turn_info
<
#if defined(BOOST_GEOMETRY_BUFFER_USE_SIDE_OF_INTERSECTION)
buffer_assign_turn
#else
detail::overlay::assign_null_policy
#endif
> turn_policy;
turn_policy::apply(*prev1, *it1, *next1,
*prev2, *it2, *next2,
false, false, false, false,
the_model,
m_intersection_strategy,
m_robust_policy,
std::back_inserter(m_turns));
}
}
}
public:
piece_turn_visitor(Pieces const& pieces,
Rings const& ring_collection,
Turns& turns,
IntersectionStrategy const& intersection_strategy,
RobustPolicy const& robust_policy)
: m_pieces(pieces)
, m_rings(ring_collection)
, m_turns(turns)
, m_intersection_strategy(intersection_strategy)
, m_robust_policy(robust_policy)
{}
template <typename Section>
inline void apply(Section const& section1, Section const& section2,
bool first = true)
{
boost::ignore_unused_variable_warning(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) )
{
return;
}
calculate_turns(piece1, piece2, section1, section2);
}
};
}} // namespace detail::buffer
#endif // DOXYGEN_NO_DETAIL
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_GET_PIECE_TURNS_HPP

View File

@@ -0,0 +1,88 @@
// 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_LINE_LINE_INTERSECTION_HPP
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_LINE_LINE_INTERSECTION_HPP
#include <boost/geometry/arithmetic/determinant.hpp>
#include <boost/geometry/util/math.hpp>
#include <boost/geometry/strategies/buffer.hpp>
#include <boost/geometry/algorithms/detail/buffer/parallel_continue.hpp>
namespace boost { namespace geometry
{
#ifndef DOXYGEN_NO_DETAIL
namespace detail { namespace buffer
{
// TODO: once change this to proper strategy
// It is different from current segment intersection because these are not segments but lines
// If we have the Line concept, we can create a strategy
// Assumes a convex corner
struct line_line_intersection
{
template <typename Point>
static inline strategy::buffer::join_selector apply(Point const& pi, Point const& pj,
Point const& qi, Point const& qj, Point& ip)
{
// See http://mathworld.wolfram.com/Line-LineIntersection.html
typedef typename coordinate_type<Point>::type coordinate_type;
coordinate_type const denominator
= determinant<coordinate_type>(get<0>(pi) - get<0>(pj),
get<1>(pi) - get<1>(pj),
get<0>(qi) - get<0>(qj),
get<1>(qi) - get<1>(qj));
// Even if the corner was checked before (so it is convex now), that
// was done on the original geometry. This function runs on the buffered
// geometries, where sides are generated and might be slightly off. In
// Floating Point, that slightly might just exceed the limit and we have
// to check it again.
// For round joins, it will not be used at all.
// For miter joints, there is a miter limit
// If segments are parallel/collinear we must be distinguish two cases:
// they continue each other, or they form a spike
if (math::equals(denominator, coordinate_type()))
{
return parallel_continue(get<0>(qj) - get<0>(qi),
get<1>(qj) - get<1>(qi),
get<0>(pj) - get<0>(pi),
get<1>(pj) - get<1>(pi))
? strategy::buffer::join_continue
: strategy::buffer::join_spike
;
}
coordinate_type d1 = determinant<coordinate_type>(get<0>(pi), get<1>(pi), get<0>(pj), get<1>(pj));
coordinate_type d2 = determinant<coordinate_type>(get<0>(qi), get<1>(qi), get<0>(qj), get<1>(qj));
double const multiplier = 1.0 / denominator;
set<0>(ip, determinant<coordinate_type>(d1, get<0>(pi) - get<0>(pj), d2, get<0>(qi) - get<0>(qj)) * multiplier);
set<1>(ip, determinant<coordinate_type>(d1, get<1>(pi) - get<1>(pj), d2, get<1>(qi) - get<1>(qj)) * multiplier);
return strategy::buffer::join_convex;
}
};
}} // namespace detail::buffer
#endif // DOXYGEN_NO_DETAIL
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_LINE_LINE_INTERSECTION_HPP

View File

@@ -0,0 +1,33 @@
// 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_PARALLEL_CONTINUE_HPP
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_PARALLEL_CONTINUE_HPP
namespace boost { namespace geometry
{
#ifndef DOXYGEN_NO_DETAIL
namespace detail { namespace buffer
{
template <typename T>
inline bool parallel_continue(T dx1, T dy1, T dx2, T dy2)
{
T const dot = dx1 * dx2 + dy1 * dy2;
return dot > 0;
}
}} // namespace detail::buffer
#endif // DOXYGEN_NO_DETAIL
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_PARALLEL_CONTINUE_HPP

View File

@@ -0,0 +1,275 @@
// Boost.Geometry (aka GGL, Generic Geometry Library)
// Copyright (c) 2014 Barend Gehrels, Amsterdam, the Netherlands.
// This file was modified by Oracle on 2016.
// Modifications copyright (c) 2016 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_ORIGINAL_VISITOR
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_TURN_IN_ORIGINAL_VISITOR
#include <boost/core/ignore_unused.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
{
struct original_get_box
{
template <typename Box, typename Original>
static inline void apply(Box& total, Original const& original)
{
geometry::expand(total, original.m_box);
}
};
struct original_ovelaps_box
{
template <typename Box, typename Original>
static inline bool apply(Box const& box, Original const& original)
{
return ! detail::disjoint::disjoint_box_box(box, original.m_box);
}
};
struct include_turn_policy
{
template <typename Turn>
static inline bool apply(Turn const& turn)
{
return turn.location == location_ok;
}
};
struct turn_in_original_ovelaps_box
{
template <typename Box, typename Turn>
static inline bool apply(Box const& box, Turn const& turn)
{
if (turn.location != location_ok || turn.within_original)
{
// Skip all points already processed
return false;
}
return ! geometry::detail::disjoint::disjoint_point_box(
turn.robust_point, box);
}
};
//! 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>
inline int point_in_original(Point const& point, Original const& original)
{
// The winding strategy is scanning in x direction
// therefore it's critical to pass direction calculated
// for x dimension below.
typedef strategy::within::winding<Point> strategy_type;
typename strategy_type::state_type state;
strategy_type strategy;
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);
}
typedef typename Original::sections_type sections_type;
typedef typename boost::range_iterator<sections_type const>::type iterator_type;
typedef typename boost::range_value<sections_type const>::type section_type;
typedef typename geometry::coordinate_type<Point>::type coordinate_type;
coordinate_type const point_x = geometry::get<0>(point);
// Walk through all monotonic sections of this original
for (iterator_type it = boost::begin(original.m_sections);
it != boost::end(original.m_sections);
++it)
{
section_type const& section = *it;
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>
class turn_in_original_visitor
{
public:
turn_in_original_visitor(Turns& turns)
: m_mutable_turns(turns)
{}
template <typename Turn, typename Original>
inline void apply(Turn const& turn, Original const& original, bool first = true)
{
boost::ignore_unused_variable_warning(first);
if (turn.location != location_ok || turn.within_original)
{
// Skip all points already processed
return;
}
if (geometry::disjoint(turn.robust_point, original.m_box))
{
// Skip all disjoint
return;
}
int const code = point_in_original(turn.robust_point, original);
if (code == -1)
{
return;
}
Turn& mutable_turn = m_mutable_turns[turn.turn_index];
if (code == 0)
{
// On border of original: always discard
mutable_turn.location = location_discard;
}
// 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;
}
}
private :
Turns& m_mutable_turns;
};
}} // namespace detail::buffer
#endif // DOXYGEN_NO_DETAIL
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_TURN_IN_ORIGINAL_VISITOR

View File

@@ -0,0 +1,803 @@
// Boost.Geometry (aka GGL, Generic Geometry Library)
// Copyright (c) 2012-2014 Barend Gehrels, Amsterdam, the Netherlands.
// This file was modified by Oracle on 2016.
// Modifications copyright (c) 2016 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
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_TURN_IN_PIECE_VISITOR
#include <boost/core/ignore_unused.hpp>
#include <boost/range.hpp>
#include <boost/geometry/core/assert.hpp>
#include <boost/geometry/arithmetic/dot_product.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/disjoint/point_box.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/policies/compare.hpp>
#include <boost/geometry/strategies/buffer.hpp>
#include <boost/geometry/algorithms/detail/buffer/buffer_policies.hpp>
#if defined(BOOST_GEOMETRY_BUFFER_USE_SIDE_OF_INTERSECTION)
#include <boost/geometry/strategies/cartesian/side_of_intersection.hpp>
#endif
namespace boost { namespace geometry
{
#ifndef DOXYGEN_NO_DETAIL
namespace detail { namespace buffer
{
struct piece_get_box
{
template <typename Box, typename Piece>
static inline void apply(Box& total, Piece const& piece)
{
geometry::expand(total, piece.robust_envelope);
}
};
struct piece_ovelaps_box
{
template <typename Box, typename Piece>
static inline bool apply(Box const& box, Piece const& piece)
{
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;
}
return ! geometry::detail::disjoint::disjoint_box_box(box, piece.robust_envelope);
}
};
struct turn_get_box
{
template <typename Box, typename Turn>
static inline void apply(Box& total, Turn const& turn)
{
geometry::expand(total, turn.robust_point);
}
};
struct turn_ovelaps_box
{
template <typename Box, typename Turn>
static inline bool apply(Box const& box, Turn const& turn)
{
return ! geometry::detail::disjoint::disjoint_point_box(turn.robust_point, box);
}
};
enum analyse_result
{
analyse_unknown,
analyse_continue,
analyse_disjoint,
analyse_within,
analyse_on_original_boundary,
analyse_on_offsetted
#if ! defined(BOOST_GEOMETRY_BUFFER_USE_SIDE_OF_INTERSECTION)
, analyse_near_offsetted
#endif
};
template <typename Point>
inline bool in_box(Point const& previous,
Point const& current, Point const& point)
{
// Get its box (TODO: this can be prepared-on-demand later)
typedef geometry::model::box<Point> box_type;
box_type box;
geometry::assign_inverse(box);
geometry::expand(box, previous);
geometry::expand(box, current);
return geometry::covered_by(point, box);
}
template <typename Point, typename Turn>
inline analyse_result check_segment(Point const& previous,
Point const& current, Turn const& turn,
bool from_monotonic)
{
#if defined(BOOST_GEOMETRY_BUFFER_USE_SIDE_OF_INTERSECTION)
typedef geometry::model::referring_segment<Point const> segment_type;
segment_type const p(turn.rob_pi, turn.rob_pj);
segment_type const q(turn.rob_qi, turn.rob_qj);
segment_type const r(previous, current);
int const side = strategy::side::side_of_intersection::apply(p, q, r,
turn.robust_point);
if (side == 0)
{
return analyse_on_offsetted;
}
if (side == -1 && from_monotonic)
{
return analyse_within;
}
if (side == 1 && from_monotonic)
{
return analyse_disjoint;
}
return analyse_continue;
#else
typedef typename strategy::side::services::default_strategy
<
typename cs_tag<Point>::type
>::type side_strategy;
typedef typename geometry::coordinate_type<Point>::type coordinate_type;
coordinate_type const twice_area
= side_strategy::template side_value
<
coordinate_type,
coordinate_type
>(previous, current, turn.robust_point);
if (twice_area == 0)
{
// Collinear, only on segment if it is covered by its bbox
if (in_box(previous, current, turn.robust_point))
{
return analyse_on_offsetted;
}
}
else if (twice_area < 0)
{
// It is in the triangle right-of the segment where the
// segment is the hypothenusa. Check if it is close
// (within rounding-area)
if (twice_area * twice_area < geometry::comparable_distance(previous, current)
&& in_box(previous, current, turn.robust_point))
{
return analyse_near_offsetted;
}
else if (from_monotonic)
{
return analyse_within;
}
}
else if (twice_area > 0 && from_monotonic)
{
// Left of segment
return analyse_disjoint;
}
// Not monotonic, on left or right side: continue analysing
return analyse_continue;
#endif
}
class analyse_turn_wrt_point_piece
{
public :
template <typename Turn, typename Piece>
static inline analyse_result apply(Turn const& turn, Piece const& piece)
{
typedef typename Piece::section_type section_type;
typedef typename Turn::robust_point_type point_type;
typedef typename geometry::coordinate_type<point_type>::type coordinate_type;
#if defined(BOOST_GEOMETRY_BUFFER_USE_SIDE_OF_INTERSECTION)
typedef geometry::model::referring_segment<point_type const> segment_type;
segment_type const p(turn.rob_pi, turn.rob_pj);
segment_type const q(turn.rob_qi, turn.rob_qj);
#else
typedef strategy::within::winding<point_type> strategy_type;
typename strategy_type::state_type state;
strategy_type strategy;
boost::ignore_unused(strategy);
#endif
BOOST_GEOMETRY_ASSERT(! piece.sections.empty());
coordinate_type const point_x = geometry::get<0>(turn.robust_point);
for (std::size_t s = 0; s < piece.sections.size(); s++)
{
section_type const& section = piece.sections[s];
// If point within horizontal range of monotonic section:
if (! section.duplicate
&& section.begin_index < section.end_index
&& point_x >= geometry::get<min_corner, 0>(section.bounding_box) - 1
&& point_x <= geometry::get<max_corner, 0>(section.bounding_box) + 1)
{
for (signed_size_type i = section.begin_index + 1; i <= section.end_index; i++)
{
point_type const& previous = piece.robust_ring[i - 1];
point_type const& current = piece.robust_ring[i];
#if defined(BOOST_GEOMETRY_BUFFER_USE_SIDE_OF_INTERSECTION)
// First check if it is in range - if it is not, the
// expensive side_of_intersection does not need to be
// applied
coordinate_type x1 = geometry::get<0>(previous);
coordinate_type x2 = geometry::get<0>(current);
if (x1 > x2)
{
std::swap(x1, x2);
}
if (point_x >= x1 - 1 && point_x <= x2 + 1)
{
segment_type const r(previous, current);
int const side = strategy::side::side_of_intersection::apply(p, q, r,
turn.robust_point);
// Sections are monotonic in x-dimension
if (side == 1)
{
// Left on segment
return analyse_disjoint;
}
else if (side == 0)
{
// Collinear - TODO: check if really on segment
return analyse_on_offsetted;
}
}
#else
analyse_result code = check_segment(previous, current, turn, false);
if (code != analyse_continue)
{
return code;
}
// Get the state (to determine it is within), we don't have
// to cover the on-segment case (covered above)
strategy.apply(turn.robust_point, previous, current, state);
#endif
}
}
}
#if defined(BOOST_GEOMETRY_BUFFER_USE_SIDE_OF_INTERSECTION)
// It is nowhere outside, and not on segment, so it is within
return analyse_within;
#else
int const code = strategy.result(state);
if (code == 1)
{
return analyse_within;
}
else if (code == -1)
{
return analyse_disjoint;
}
// Should normally not occur - on-segment is covered
return analyse_unknown;
#endif
}
};
class analyse_turn_wrt_piece
{
template <typename Point, typename Turn>
static inline analyse_result check_helper_segment(Point const& s1,
Point const& s2, Turn const& turn,
bool is_original,
Point const& offsetted)
{
boost::ignore_unused(offsetted);
#if defined(BOOST_GEOMETRY_BUFFER_USE_SIDE_OF_INTERSECTION)
typedef geometry::model::referring_segment<Point const> segment_type;
segment_type const p(turn.rob_pi, turn.rob_pj);
segment_type const q(turn.rob_qi, turn.rob_qj);
segment_type const r(s1, s2);
int const side = strategy::side::side_of_intersection::apply(p, q, r,
turn.robust_point);
if (side == 1)
{
// left of segment
return analyse_disjoint;
}
else if (side == 0)
{
// If is collinear, either on segment or before/after
typedef geometry::model::box<Point> box_type;
box_type box;
geometry::assign_inverse(box);
geometry::expand(box, s1);
geometry::expand(box, s2);
if (geometry::covered_by(turn.robust_point, box))
{
// Points on helper-segments are considered as within
// Points on original boundary are processed differently
return is_original
? analyse_on_original_boundary
: analyse_within;
}
// It is collinear but not on the segment. Because these
// segments are convex, it is outside
// Unless the offsetted ring is collinear or concave w.r.t.
// helper-segment but that scenario is not yet supported
return analyse_disjoint;
}
// right of segment
return analyse_continue;
#else
typedef typename strategy::side::services::default_strategy
<
typename cs_tag<Point>::type
>::type side_strategy;
switch(side_strategy::apply(s1, s2, turn.robust_point))
{
case 1 :
return analyse_disjoint; // left of segment
case 0 :
{
// If is collinear, either on segment or before/after
typedef geometry::model::box<Point> box_type;
box_type box;
geometry::assign_inverse(box);
geometry::expand(box, s1);
geometry::expand(box, s2);
if (geometry::covered_by(turn.robust_point, box))
{
// It is on the segment
if (! is_original
&& geometry::comparable_distance(turn.robust_point, offsetted) <= 1)
{
// It is close to the offsetted-boundary, take
// any rounding-issues into account
return analyse_near_offsetted;
}
// Points on helper-segments are considered as within
// Points on original boundary are processed differently
return is_original
? analyse_on_original_boundary
: analyse_within;
}
// It is collinear but not on the segment. Because these
// segments are convex, it is outside
// Unless the offsetted ring is collinear or concave w.r.t.
// helper-segment but that scenario is not yet supported
return analyse_disjoint;
}
break;
}
// right of segment
return analyse_continue;
#endif
}
template <typename Turn, typename Piece>
static inline analyse_result check_helper_segments(Turn const& turn, Piece const& piece)
{
typedef typename Turn::robust_point_type point_type;
geometry::equal_to<point_type> comparator;
point_type points[4];
signed_size_type helper_count = static_cast<signed_size_type>(piece.robust_ring.size())
- piece.offsetted_count;
if (helper_count == 4)
{
for (int i = 0; i < 4; i++)
{
points[i] = piece.robust_ring[piece.offsetted_count + i];
}
}
else if (helper_count == 3)
{
// Triangular piece, assign points but assign second twice
for (int i = 0; i < 4; i++)
{
int index = i < 2 ? i : i - 1;
points[i] = piece.robust_ring[piece.offsetted_count + index];
}
}
else
{
// Some pieces (e.g. around points) do not have helper segments.
// Others should have 3 (join) or 4 (side)
return analyse_continue;
}
// First check point-equality
point_type const& point = turn.robust_point;
if (comparator(point, points[0]) || comparator(point, points[3]))
{
return analyse_on_offsetted;
}
if (comparator(point, points[1]) || comparator(point, points[2]))
{
return analyse_on_original_boundary;
}
// Right side of the piece
analyse_result result
= check_helper_segment(points[0], points[1], turn,
false, points[0]);
if (result != analyse_continue)
{
return result;
}
// Left side of the piece
result = check_helper_segment(points[2], points[3], turn,
false, points[3]);
if (result != analyse_continue)
{
return result;
}
if (! comparator(points[1], points[2]))
{
// Side of the piece at side of original geometry
result = check_helper_segment(points[1], points[2], turn,
true, point);
if (result != analyse_continue)
{
return result;
}
}
// We are within the \/ or |_| shaped piece, where the top is the
// offsetted ring.
if (! geometry::covered_by(point, piece.robust_offsetted_envelope))
{
// Not in offsetted-area. This makes a cheap check possible
typedef typename strategy::side::services::default_strategy
<
typename cs_tag<point_type>::type
>::type side_strategy;
switch(side_strategy::apply(points[3], points[0], point))
{
case 1 : return analyse_disjoint;
case -1 : return analyse_within;
case 0 : return analyse_disjoint;
}
}
return analyse_continue;
}
template <typename Turn, typename Piece, typename Compare>
static inline analyse_result check_monotonic(Turn const& turn, Piece const& piece, Compare const& compare)
{
typedef typename Piece::piece_robust_ring_type ring_type;
typedef typename ring_type::const_iterator it_type;
it_type end = piece.robust_ring.begin() + piece.offsetted_count;
it_type it = std::lower_bound(piece.robust_ring.begin(),
end,
turn.robust_point,
compare);
if (it != end
&& it != piece.robust_ring.begin())
{
// iterator points to point larger than point
// w.r.t. specified direction, and prev points to a point smaller
// We now know if it is inside/outside
it_type prev = it - 1;
return check_segment(*prev, *it, turn, true);
}
return analyse_continue;
}
public :
template <typename Turn, typename Piece>
static inline analyse_result apply(Turn const& turn, Piece const& piece)
{
typedef typename Turn::robust_point_type point_type;
analyse_result code = check_helper_segments(turn, piece);
if (code != analyse_continue)
{
return code;
}
geometry::equal_to<point_type> comparator;
if (piece.offsetted_count > 8)
{
// If the offset contains some points and is monotonic, we try
// to avoid walking all points linearly.
// We try it only once.
if (piece.is_monotonic_increasing[0])
{
code = check_monotonic(turn, piece, geometry::less<point_type, 0>());
if (code != analyse_continue) return code;
}
else if (piece.is_monotonic_increasing[1])
{
code = check_monotonic(turn, piece, geometry::less<point_type, 1>());
if (code != analyse_continue) return code;
}
else if (piece.is_monotonic_decreasing[0])
{
code = check_monotonic(turn, piece, geometry::greater<point_type, 0>());
if (code != analyse_continue) return code;
}
else if (piece.is_monotonic_decreasing[1])
{
code = check_monotonic(turn, piece, geometry::greater<point_type, 1>());
if (code != analyse_continue) return code;
}
}
// It is small or not monotonic, walk linearly through offset
// TODO: this will be combined with winding strategy
for (signed_size_type i = 1; i < piece.offsetted_count; i++)
{
point_type const& previous = piece.robust_ring[i - 1];
point_type const& current = piece.robust_ring[i];
// The robust ring can contain duplicates
// (on which any side or side-value would return 0)
if (! comparator(previous, current))
{
code = check_segment(previous, current, turn, false);
if (code != analyse_continue)
{
return code;
}
}
}
return analyse_unknown;
}
};
template <typename Turns, typename Pieces>
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
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;
}
#if defined(BOOST_GEOMETRY_BUFFER_USE_SIDE_OF_INTERSECTION)
// NOTE: this function returns a side value in {-1, 0, 1}
template <typename Turn, typename Piece>
static inline int turn_in_convex_piece(Turn const& turn,
Piece const& piece)
{
typedef typename Turn::robust_point_type point_type;
typedef typename Piece::piece_robust_ring_type ring_type;
typedef geometry::model::referring_segment<point_type const> segment;
segment const p(turn.rob_pi, turn.rob_pj);
segment const q(turn.rob_qi, turn.rob_qj);
typedef typename boost::range_iterator<ring_type const>::type iterator_type;
iterator_type it = boost::begin(piece.robust_ring);
iterator_type end = boost::end(piece.robust_ring);
// A robust ring is always closed, and always clockwise
for (iterator_type previous = it++; it != end; ++previous, ++it)
{
geometry::equal_to<point_type> comparator;
if (comparator(*previous, *it))
{
// Points are the same
continue;
}
segment r(*previous, *it);
int const side = strategy::side::side_of_intersection::apply(p, q, r,
turn.robust_point);
if (side == 1)
{
// IP is left of segment, so it is outside
return -1; // outside
}
else if (side == 0)
{
// IP is collinear with segment. TODO: we should analyze this further
// For now we use the fallback point
if (in_box(*previous, *it, turn.robust_point))
{
return 0;
}
else
{
return -1; // outside
}
}
}
return 1; // inside
}
#endif
public:
inline turn_in_piece_visitor(Turns& turns, Pieces const& pieces)
: m_turns(turns)
, m_pieces(pieces)
{}
template <typename Turn, typename Piece>
inline void apply(Turn const& turn, Piece const& piece, bool first = true)
{
boost::ignore_unused_variable_warning(first);
if (turn.count_within > 0)
{
// Already inside - no need to check again
return;
}
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;
}
if (! geometry::covered_by(turn.robust_point, piece.robust_envelope))
{
// Easy check: if the turn is not in the envelope, we can safely return
return;
}
if (skip(turn.operations[0], piece) || skip(turn.operations[1], piece))
{
return;
}
// TODO: mutable_piece to make some on-demand preparations in analyse
Turn& mutable_turn = m_turns[turn.turn_index];
if (piece.type == geometry::strategy::buffer::buffered_point)
{
// Optimization for buffer around points: if distance from center
// is not between min/max radius, the result is clear
typedef typename default_comparable_distance_result
<
typename Turn::robust_point_type
>::type distance_type;
distance_type const cd
= geometry::comparable_distance(piece.robust_center,
turn.robust_point);
if (cd < piece.robust_min_comparable_radius)
{
mutable_turn.count_within++;
return;
}
if (cd > piece.robust_max_comparable_radius)
{
return;
}
}
analyse_result analyse_code =
piece.type == geometry::strategy::buffer::buffered_point
? analyse_turn_wrt_point_piece::apply(turn, piece)
: analyse_turn_wrt_piece::apply(turn, piece);
switch(analyse_code)
{
case analyse_disjoint :
return;
case analyse_on_offsetted :
mutable_turn.count_on_offsetted++; // value is not used anymore
return;
case analyse_on_original_boundary :
mutable_turn.count_on_original_boundary++;
return;
case analyse_within :
mutable_turn.count_within++;
return;
#if ! defined(BOOST_GEOMETRY_BUFFER_USE_SIDE_OF_INTERSECTION)
case analyse_near_offsetted :
mutable_turn.count_within_near_offsetted++;
return;
#endif
default :
break;
}
#if defined(BOOST_GEOMETRY_BUFFER_USE_SIDE_OF_INTERSECTION)
// We don't know (yet)
int geometry_code = 0;
if (piece.is_convex)
{
geometry_code = turn_in_convex_piece(turn, piece);
}
else
{
// TODO: this point_in_geometry is a performance-bottleneck here and
// will be replaced completely by extending analyse_piece functionality
geometry_code = detail::within::point_in_geometry(turn.robust_point, piece.robust_ring);
}
#else
int geometry_code = detail::within::point_in_geometry(turn.robust_point, piece.robust_ring);
#endif
if (geometry_code == 1)
{
mutable_turn.count_within++;
}
}
};
}} // namespace detail::buffer
#endif // DOXYGEN_NO_DETAIL
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_TURN_IN_PIECE_VISITOR