blob: 4a3cacbeddf20613c0b30a3adea31f21c2083c9a [file] [log] [blame]
// Boost.Geometry (aka GGL, Generic Geometry Library)
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
// This file was modified by Oracle on 2013, 2014.
// Modifications copyright (c) 2013-2014 Oracle and/or its affiliates.
// 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)
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_FOR_ENDPOINT_HPP
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_FOR_ENDPOINT_HPP
#include <boost/geometry/algorithms/detail/overlay/get_turn_info.hpp>
#include <boost/geometry/policies/robustness/no_rescale_policy.hpp>
namespace boost { namespace geometry {
#ifndef DOXYGEN_NO_DETAIL
namespace detail { namespace overlay {
// SEGMENT_INTERSECTION RESULT
// C H0 H1 A0 A1 O IP1 IP2
// D0 and D1 == 0
// |--------> 2 0 0 0 0 F i/i x/x
// |-------->
//
// |--------> 2 0 0 0 0 T i/x x/i
// <--------|
//
// |-----> 1 0 0 0 0 T x/x
// <-----|
//
// |---------> 2 0 0 0 1 T i/x x/i
// <----|
//
// |---------> 2 0 0 0 0 F i/i x/x
// |---->
//
// |---------> 2 0 0 -1 1 F i/i u/x
// |---->
//
// |---------> 2 0 0 -1 0 T i/x u/i
// <----|
// |-------> 2 0 0 1 -1 F and i/i x/u
// |-------> 2 0 0 -1 1 F symetric i/i u/x
// |------->
//
// |-------> 2 0 0 -1 -1 T i/u u/i
// <-------|
//
// |-------> 2 0 0 1 1 T i/x x/i
// <-------|
//
// |--------> 2 0 0 -1 1 F i/i u/x
// |---->
//
// |--------> 2 0 0 -1 1 T i/x u/i
// <----|
// |-----> 1 -1 -1 -1 -1 T u/u
// <-----|
//
// |-----> 1 -1 0 -1 0 F and u/x
// |-----> 1 0 -1 0 -1 F symetric x/u
// |----->
// D0 or D1 != 0
// ^
// |
// + 1 -1 1 -1 1 F and u/x (P is vertical)
// |--------> 1 1 -1 1 -1 F symetric x/u (P is horizontal)
// ^
// |
// +
//
// +
// |
// v
// |--------> 1 1 1 1 1 F x/x (P is vertical)
//
// ^
// |
// +
// |--------> 1 -1 -1 -1 -1 F u/u (P is vertical)
//
// ^
// |
// +
// |--------> 1 0 -1 0 -1 F u/u (P is vertical)
//
// +
// |
// v
// |--------> 1 0 1 0 1 F u/x (P is vertical)
//
class linear_intersections
{
public:
template <typename Point1, typename Point2, typename IntersectionResult>
linear_intersections(Point1 const& pi,
Point2 const& qi,
IntersectionResult const& result,
bool is_p_last, bool is_q_last)
{
int arrival_a = result.template get<1>().arrival[0];
int arrival_b = result.template get<1>().arrival[1];
bool same_dirs = result.template get<1>().dir_a == 0
&& result.template get<1>().dir_b == 0;
if ( same_dirs )
{
if ( result.template get<0>().count == 2 )
{
if ( ! result.template get<1>().opposite )
{
ips[0].p_operation = operation_intersection;
ips[0].q_operation = operation_intersection;
ips[1].p_operation = union_or_blocked_same_dirs(arrival_a, is_p_last);
ips[1].q_operation = union_or_blocked_same_dirs(arrival_b, is_q_last);
ips[0].is_pi
= equals::equals_point_point(
pi, result.template get<0>().intersections[0]);
ips[0].is_qi
= equals::equals_point_point(
qi, result.template get<0>().intersections[0]);
ips[1].is_pj = arrival_a != -1;
ips[1].is_qj = arrival_b != -1;
}
else
{
ips[0].p_operation = operation_intersection;
ips[0].q_operation = union_or_blocked_same_dirs(arrival_b, is_q_last);
ips[1].p_operation = union_or_blocked_same_dirs(arrival_a, is_p_last);
ips[1].q_operation = operation_intersection;
ips[0].is_pi = arrival_b != 1;
ips[0].is_qj = arrival_b != -1;
ips[1].is_pj = arrival_a != -1;
ips[1].is_qi = arrival_a != 1;
}
}
else
{
BOOST_ASSERT(result.template get<0>().count == 1);
ips[0].p_operation = union_or_blocked_same_dirs(arrival_a, is_p_last);
ips[0].q_operation = union_or_blocked_same_dirs(arrival_b, is_q_last);
ips[0].is_pi = arrival_a == -1;
ips[0].is_qi = arrival_b == -1;
ips[0].is_pj = arrival_a == 0;
ips[0].is_qj = arrival_b == 0;
}
}
else
{
ips[0].p_operation = union_or_blocked_different_dirs(arrival_a, is_p_last);
ips[0].q_operation = union_or_blocked_different_dirs(arrival_b, is_q_last);
ips[0].is_pi = arrival_a == -1;
ips[0].is_qi = arrival_b == -1;
ips[0].is_pj = arrival_a == 1;
ips[0].is_qj = arrival_b == 1;
}
}
struct ip_info
{
inline ip_info()
: p_operation(operation_none), q_operation(operation_none)
, is_pi(false), is_pj(false), is_qi(false), is_qj(false)
{}
operation_type p_operation, q_operation;
bool is_pi, is_pj, is_qi, is_qj;
};
template <std::size_t I>
ip_info const& get() const
{
BOOST_STATIC_ASSERT(I < 2);
return ips[I];
}
private:
// only if collinear (same_dirs)
static inline operation_type union_or_blocked_same_dirs(int arrival, bool is_last)
{
if ( arrival == 1 )
return operation_blocked;
else if ( arrival == -1 )
return operation_union;
else
return is_last ? operation_blocked : operation_union;
//return operation_blocked;
}
// only if not collinear (!same_dirs)
static inline operation_type union_or_blocked_different_dirs(int arrival, bool is_last)
{
if ( arrival == 1 )
//return operation_blocked;
return is_last ? operation_blocked : operation_union;
else
return operation_union;
}
ip_info ips[2];
};
template <typename AssignPolicy, bool EnableFirst, bool EnableLast>
struct get_turn_info_for_endpoint
{
BOOST_STATIC_ASSERT(EnableFirst || EnableLast);
template<typename Point1,
typename Point2,
typename TurnInfo,
typename IntersectionInfo,
typename OutputIterator
>
static inline bool apply(Point1 const& pi, Point1 const& pj, Point1 const& pk,
Point2 const& qi, Point2 const& qj, Point2 const& qk,
bool is_p_first, bool is_p_last,
bool is_q_first, bool is_q_last,
TurnInfo const& tp_model,
IntersectionInfo const& inters,
method_type /*method*/,
OutputIterator out)
{
std::size_t ip_count = inters.i_info().count;
// no intersection points
if ( ip_count == 0 )
return false;
if ( !is_p_first && !is_p_last && !is_q_first && !is_q_last )
return false;
linear_intersections intersections(pi, qi, inters.result(), is_p_last, is_q_last);
bool append0_last
= analyse_segment_and_assign_ip(pi, pj, pk, qi, qj, qk,
is_p_first, is_p_last, is_q_first, is_q_last,
intersections.template get<0>(),
tp_model, inters, 0, out);
// NOTE: opposite && ip_count == 1 may be true!
bool opposite = inters.d_info().opposite;
// don't ignore only for collinear opposite
bool result_ignore_ip0 = append0_last && ( ip_count == 1 || !opposite );
if ( intersections.template get<1>().p_operation == operation_none )
return result_ignore_ip0;
bool append1_last
= analyse_segment_and_assign_ip(pi, pj, pk, qi, qj, qk,
is_p_first, is_p_last, is_q_first, is_q_last,
intersections.template get<1>(),
tp_model, inters, 1, out);
// don't ignore only for collinear opposite
bool result_ignore_ip1 = append1_last && !opposite /*&& ip_count == 2*/;
return result_ignore_ip0 || result_ignore_ip1;
}
template <typename Point1,
typename Point2,
typename TurnInfo,
typename IntersectionInfo,
typename OutputIterator>
static inline
bool analyse_segment_and_assign_ip(Point1 const& pi, Point1 const& pj, Point1 const& pk,
Point2 const& qi, Point2 const& qj, Point2 const& qk,
bool is_p_first, bool is_p_last,
bool is_q_first, bool is_q_last,
linear_intersections::ip_info const& ip_info,
TurnInfo const& tp_model,
IntersectionInfo const& inters,
unsigned int ip_index,
OutputIterator out)
{
#ifdef BOOST_GEOMETRY_DEBUG_GET_TURNS_LINEAR_LINEAR
// may this give false positives for INTs?
typename IntersectionResult::point_type const&
inters_pt = result.template get<0>().intersections[ip_index];
BOOST_ASSERT(ip_info.is_pi == equals::equals_point_point(pi, inters_pt));
BOOST_ASSERT(ip_info.is_qi == equals::equals_point_point(qi, inters_pt));
BOOST_ASSERT(ip_info.is_pj == equals::equals_point_point(pj, inters_pt));
BOOST_ASSERT(ip_info.is_qj == equals::equals_point_point(qj, inters_pt));
#endif
// TODO - calculate first/last only if needed
bool is_p_first_ip = is_p_first && ip_info.is_pi;
bool is_p_last_ip = is_p_last && ip_info.is_pj;
bool is_q_first_ip = is_q_first && ip_info.is_qi;
bool is_q_last_ip = is_q_last && ip_info.is_qj;
bool append_first = EnableFirst && (is_p_first_ip || is_q_first_ip);
bool append_last = EnableLast && (is_p_last_ip || is_q_last_ip);
operation_type p_operation = ip_info.p_operation;
operation_type q_operation = ip_info.q_operation;
if ( append_first || append_last )
{
bool handled = handle_internal<0>(pi, pj, pk, qi, qj, qk,
inters.rpi(), inters.rpj(), inters.rpk(),
inters.rqi(), inters.rqj(), inters.rqk(),
is_p_first_ip, is_p_last_ip,
is_q_first_ip, is_q_last_ip,
ip_info.is_qi, ip_info.is_qj,
tp_model, inters, ip_index,
p_operation, q_operation);
if ( !handled )
{
handle_internal<1>(qi, qj, qk, pi, pj, pk,
inters.rqi(), inters.rqj(), inters.rqk(),
inters.rpi(), inters.rpj(), inters.rpk(),
is_q_first_ip, is_q_last_ip,
is_p_first_ip, is_p_last_ip,
ip_info.is_pi, ip_info.is_pj,
tp_model, inters, ip_index,
q_operation, p_operation);
}
if ( p_operation != operation_none )
{
method_type method = endpoint_ip_method(ip_info.is_pi, ip_info.is_pj,
ip_info.is_qi, ip_info.is_qj);
turn_position p_pos = ip_position(is_p_first_ip, is_p_last_ip);
turn_position q_pos = ip_position(is_q_first_ip, is_q_last_ip);
// handle spikes
// P is spike and should be handled
if ( !is_p_last
&& ip_info.is_pj // this check is redundant (also in is_spike_p) but faster
&& inters.i_info().count == 2
&& inters.is_spike_p() )
{
assign(pi, qi, inters.result(), ip_index, method, operation_blocked, q_operation,
p_pos, q_pos, is_p_first_ip, is_q_first_ip, true, false, tp_model, out);
assign(pi, qi, inters.result(), ip_index, method, operation_intersection, q_operation,
p_pos, q_pos, is_p_first_ip, is_q_first_ip, true, false, tp_model, out);
}
// Q is spike and should be handled
else if ( !is_q_last
&& ip_info.is_qj // this check is redundant (also in is_spike_q) but faster
&& inters.i_info().count == 2
&& inters.is_spike_q() )
{
assign(pi, qi, inters.result(), ip_index, method, p_operation, operation_blocked,
p_pos, q_pos, is_p_first_ip, is_q_first_ip, false, true, tp_model, out);
assign(pi, qi, inters.result(), ip_index, method, p_operation, operation_intersection,
p_pos, q_pos, is_p_first_ip, is_q_first_ip, false, true, tp_model, out);
}
// no spikes
else
{
assign(pi, qi, inters.result(), ip_index, method, p_operation, q_operation,
p_pos, q_pos, is_p_first_ip, is_q_first_ip, false, false, tp_model, out);
}
}
}
return append_last;
}
// TODO: IT'S ALSO PROBABLE THAT ALL THIS FUNCTION COULD BE INTEGRATED WITH handle_segment
// however now it's lazily calculated and then it would be always calculated
template<std::size_t G1Index,
typename Point1,
typename Point2,
typename RobustPoint1,
typename RobustPoint2,
typename TurnInfo,
typename IntersectionInfo
>
static inline bool handle_internal(Point1 const& /*i1*/, Point1 const& /*j1*/, Point1 const& /*k1*/,
Point2 const& i2, Point2 const& j2, Point2 const& /*k2*/,
RobustPoint1 const& ri1, RobustPoint1 const& rj1, RobustPoint1 const& /*rk1*/,
RobustPoint2 const& ri2, RobustPoint2 const& rj2, RobustPoint2 const& rk2,
bool first1, bool last1, bool first2, bool last2,
bool ip_i2, bool ip_j2, TurnInfo const& tp_model,
IntersectionInfo const& inters, unsigned int ip_index,
operation_type & op1, operation_type & op2)
{
boost::ignore_unused_variable_warning(i2);
boost::ignore_unused_variable_warning(j2);
boost::ignore_unused_variable_warning(ip_index);
boost::ignore_unused_variable_warning(tp_model);
if ( !first2 && !last2 )
{
if ( first1 )
{
#ifdef BOOST_GEOMETRY_DEBUG_GET_TURNS_LINEAR_LINEAR
// may this give false positives for INTs?
typename IntersectionResult::point_type const&
inters_pt = inters.i_info().intersections[ip_index];
BOOST_ASSERT(ip_i2 == equals::equals_point_point(i2, inters_pt));
BOOST_ASSERT(ip_j2 == equals::equals_point_point(j2, inters_pt));
#endif
if ( ip_i2 )
{
// don't output this IP - for the first point of other geometry segment
op1 = operation_none;
op2 = operation_none;
return true;
}
else if ( ip_j2 )
{
side_calculator<RobustPoint1, RobustPoint2, RobustPoint2>
side_calc(ri2, ri1, rj1, ri2, rj2, rk2);
std::pair<operation_type, operation_type>
operations = operations_of_equal(side_calc);
// TODO: must the above be calculated?
// wouldn't it be enough to check if segments are collinear?
if ( operations_both(operations, operation_continue) )
{
if ( op1 != operation_union
|| op2 != operation_union
|| ! ( G1Index == 0 ? inters.is_spike_q() : inters.is_spike_p() ) )
{
// THIS IS WRT THE ORIGINAL SEGMENTS! NOT THE ONES ABOVE!
bool opposite = inters.d_info().opposite;
op1 = operation_intersection;
op2 = opposite ? operation_union : operation_intersection;
}
}
else
{
BOOST_ASSERT(operations_combination(operations, operation_intersection, operation_union));
//op1 = operation_union;
//op2 = operation_union;
}
return true;
}
// else do nothing - shouldn't be handled this way
}
else if ( last1 )
{
#ifdef BOOST_GEOMETRY_DEBUG_GET_TURNS_LINEAR_LINEAR
// may this give false positives for INTs?
typename IntersectionResult::point_type const&
inters_pt = inters.i_info().intersections[ip_index];
BOOST_ASSERT(ip_i2 == equals::equals_point_point(i2, inters_pt));
BOOST_ASSERT(ip_j2 == equals::equals_point_point(j2, inters_pt));
#endif
if ( ip_i2 )
{
// don't output this IP - for the first point of other geometry segment
op1 = operation_none;
op2 = operation_none;
return true;
}
else if ( ip_j2 )
{
side_calculator<RobustPoint1, RobustPoint2, RobustPoint2>
side_calc(ri2, rj1, ri1, ri2, rj2, rk2);
std::pair<operation_type, operation_type>
operations = operations_of_equal(side_calc);
// TODO: must the above be calculated?
// wouldn't it be enough to check if segments are collinear?
if ( operations_both(operations, operation_continue) )
{
if ( op1 != operation_blocked
|| op2 != operation_union
|| ! ( G1Index == 0 ? inters.is_spike_q() : inters.is_spike_p() ) )
{
// THIS IS WRT THE ORIGINAL SEGMENTS! NOT THE ONES ABOVE!
bool second_going_out = inters.i_info().count > 1;
op1 = operation_blocked;
op2 = second_going_out ? operation_union : operation_intersection;
}
}
else
{
BOOST_ASSERT(operations_combination(operations, operation_intersection, operation_union));
//op1 = operation_blocked;
//op2 = operation_union;
}
return true;
}
// else do nothing - shouldn't be handled this way
}
// else do nothing - shouldn't be handled this way
}
return false;
}
static inline method_type endpoint_ip_method(bool ip_pi, bool ip_pj, bool ip_qi, bool ip_qj)
{
if ( (ip_pi || ip_pj) && (ip_qi || ip_qj) )
return method_touch;
else
return method_touch_interior;
}
static inline turn_position ip_position(bool is_ip_first_i, bool is_ip_last_j)
{
return is_ip_first_i ? position_front :
( is_ip_last_j ? position_back : position_middle );
}
template <typename Point1,
typename Point2,
typename IntersectionResult,
typename TurnInfo,
typename OutputIterator>
static inline void assign(Point1 const& pi, Point2 const& qi,
IntersectionResult const& result,
unsigned int ip_index,
method_type method,
operation_type op0, operation_type op1,
turn_position pos0, turn_position pos1,
bool is_p_first_ip, bool is_q_first_ip,
bool is_p_spike, bool is_q_spike,
TurnInfo const& tp_model,
OutputIterator out)
{
TurnInfo tp = tp_model;
//geometry::convert(ip, tp.point);
//tp.method = method;
base_turn_handler::assign_point(tp, method, result.template get<0>(), ip_index);
tp.operations[0].operation = op0;
tp.operations[1].operation = op1;
tp.operations[0].position = pos0;
tp.operations[1].position = pos1;
if ( result.template get<0>().count > 1 )
{
// NOTE: is_collinear is NOT set for the first endpoint
// for which there is no preceding segment
//BOOST_ASSERT( result.template get<1>().dir_a == 0 && result.template get<1>().dir_b == 0 );
if ( ! is_p_first_ip )
{
tp.operations[0].is_collinear = op0 != operation_intersection
|| is_p_spike;
}
if ( ! is_q_first_ip )
{
tp.operations[1].is_collinear = op1 != operation_intersection
|| is_q_spike;
}
}
else //if ( result.template get<0>().count == 1 )
{
if ( op0 == operation_blocked && op1 == operation_intersection )
{
tp.operations[0].is_collinear = true;
}
else if ( op0 == operation_intersection && op1 == operation_blocked )
{
tp.operations[1].is_collinear = true;
}
}
// TODO: this should get an intersection_info, which is unavailable here
// Because the assign_null policy accepts any structure, we pass the result instead for now
AssignPolicy::apply(tp, pi, qi, result);
*out++ = tp;
}
template <typename SidePolicy>
static inline std::pair<operation_type, operation_type> operations_of_equal(SidePolicy const& side)
{
int const side_pk_q2 = side.pk_wrt_q2();
int const side_pk_p = side.pk_wrt_p1();
int const side_qk_p = side.qk_wrt_p1();
// If pk is collinear with qj-qk, they continue collinearly.
// This can be on either side of p1 (== q1), or collinear
// The second condition checks if they do not continue
// oppositely
if ( side_pk_q2 == 0 && side_pk_p == side_qk_p )
{
return std::make_pair(operation_continue, operation_continue);
}
// If they turn to same side (not opposite sides)
if ( ! base_turn_handler::opposite(side_pk_p, side_qk_p) )
{
// If pk is left of q2 or collinear: p: union, q: intersection
if ( side_pk_q2 != -1 )
{
return std::make_pair(operation_union, operation_intersection);
}
else
{
return std::make_pair(operation_intersection, operation_union);
}
}
else
{
// They turn opposite sides. If p turns left (or collinear),
// p: union, q: intersection
if ( side_pk_p != -1 )
{
return std::make_pair(operation_union, operation_intersection);
}
else
{
return std::make_pair(operation_intersection, operation_union);
}
}
}
static inline bool operations_both(
std::pair<operation_type, operation_type> const& operations,
operation_type const op)
{
return operations.first == op && operations.second == op;
}
static inline bool operations_combination(
std::pair<operation_type, operation_type> const& operations,
operation_type const op1, operation_type const op2)
{
return ( operations.first == op1 && operations.second == op2 )
|| ( operations.first == op2 && operations.second == op1 );
}
};
}} // namespace detail::overlay
#endif // DOXYGEN_NO_DETAIL
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_FOR_ENDPOINT_HPP