mirror of
https://github.com/boostorg/geometry.git
synced 2026-02-11 11:52:11 +00:00
Merge branch 'feature/relate' of https://github.com/boostorg/geometry into feature/setops
This commit is contained in:
@@ -408,185 +408,6 @@ struct get_turn_info_linear_linear
|
||||
op1 = operation_union;
|
||||
}
|
||||
|
||||
template<typename Point, typename Point1, typename Point2>
|
||||
static inline
|
||||
void handle_segment(bool /*first_a*/, bool last_a, int how_a, int arrival_a,
|
||||
bool /*first_b*/, bool last_b, int how_b, int arrival_b,
|
||||
bool opposite, std::size_t ip_count, bool same_dirs/*collinear*/,
|
||||
Point const& ip0, Point const& /*ip1*/,
|
||||
operation_type & op0_a, operation_type & op0_b,
|
||||
operation_type & op1_a, operation_type & op1_b,
|
||||
bool & i0_a, bool & j0_a, bool & i0_b, bool & j0_b,
|
||||
bool & i1_a, bool & j1_a, bool & i1_b, bool & j1_b,
|
||||
Point1 const& pi, Point1 const& /*pj*/, Point1 const& /*pk*/,
|
||||
Point2 const& qi, Point2 const& /*qj*/, Point2 const& /*qk*/)
|
||||
{
|
||||
namespace ov = overlay;
|
||||
|
||||
i0_a = false; j0_a = false; i0_b = false; j0_b = false;
|
||||
i1_a = false; j1_a = false; i1_b = false; j1_b = false;
|
||||
|
||||
if ( same_dirs )
|
||||
{
|
||||
if ( ip_count == 2 )
|
||||
{
|
||||
BOOST_ASSERT( how_a == 0 && how_b == 0 );
|
||||
|
||||
if ( !opposite )
|
||||
{
|
||||
op0_a = operation_intersection;
|
||||
op0_b = operation_intersection;
|
||||
op1_a = arrival_to_union_or_blocked(arrival_a, last_a);
|
||||
op1_b = arrival_to_union_or_blocked(arrival_b, last_b);
|
||||
|
||||
i0_a = equals::equals_point_point(pi, ip0);
|
||||
i0_b = equals::equals_point_point(qi, ip0);
|
||||
j1_a = arrival_a != -1;
|
||||
j1_b = arrival_b != -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
op0_a = operation_intersection;
|
||||
op0_b = arrival_to_union_or_blocked(arrival_b, last_b);
|
||||
op1_a = arrival_to_union_or_blocked(arrival_a, last_a);
|
||||
op1_b = operation_intersection;
|
||||
|
||||
i0_a = arrival_b != 1;
|
||||
j0_b = arrival_b != -1;
|
||||
j1_a = arrival_a != -1;
|
||||
i1_b = arrival_a != 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT(ip_count == 1);
|
||||
op0_a = arrival_to_union_or_blocked(arrival_a, last_a);
|
||||
op0_b = arrival_to_union_or_blocked(arrival_b, last_b);
|
||||
|
||||
i0_a = how_a == -1;
|
||||
i0_b = how_b == -1;
|
||||
j0_a = arrival_a == 0;
|
||||
j0_b = arrival_b == 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
op0_a = how_to_union_or_blocked(how_a, last_a);
|
||||
op0_b = how_to_union_or_blocked(how_b, last_b);
|
||||
|
||||
i0_a = how_a == -1;
|
||||
i0_b = how_b == -1;
|
||||
j0_a = how_a == 1;
|
||||
j0_b = how_b == 1;
|
||||
}
|
||||
}
|
||||
|
||||
// only if collinear (same_dirs)
|
||||
static inline operation_type arrival_to_union_or_blocked(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 how_to_union_or_blocked(int how, bool is_last)
|
||||
{
|
||||
if ( how == 1 )
|
||||
//return operation_blocked;
|
||||
return is_last ? operation_blocked : operation_union;
|
||||
else
|
||||
return operation_union;
|
||||
}
|
||||
|
||||
// TODO: IT'S ALSO PROBABLE THAT ALL THIS FUNCTION COULD BE INTEGRATED WITH handle_segment
|
||||
|
||||
template<typename Point1,
|
||||
typename Point2,
|
||||
typename Point,
|
||||
typename TurnInfo,
|
||||
typename IntersectionResult
|
||||
>
|
||||
static inline bool handle_internal(Point1 const& i1, Point1 const& j1, Point1 const& /*k1*/,
|
||||
Point2 const& i2, Point2 const& j2, Point2 const& k2,
|
||||
Point const& ip,
|
||||
bool first1, bool last1, bool first2, bool last2,
|
||||
bool ip_i2, bool ip_j2,
|
||||
TurnInfo const& tp_model,
|
||||
IntersectionResult const& result,
|
||||
operation_type & op1, operation_type & op2)
|
||||
{
|
||||
if ( first1 || last1 )
|
||||
{
|
||||
if ( !first2 && !last2 )
|
||||
{
|
||||
BOOST_ASSERT(ip_i2 == equals::equals_point_point(i2, ip));
|
||||
BOOST_ASSERT(ip_j2 == equals::equals_point_point(j2, ip));
|
||||
|
||||
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 )
|
||||
{
|
||||
bool opposite = result.template get<1>().opposite;
|
||||
|
||||
TurnInfo tp = tp_model;
|
||||
if ( first1 )
|
||||
{
|
||||
side_calculator<Point1, Point2> side_calc(i2, i1, j1, i2, j2, k2);
|
||||
equal<TurnInfo>::apply(i2, i1, j1, i2, j2, k2,
|
||||
tp, result.template get<0>(), result.template get<1>(), side_calc);
|
||||
if ( tp.both(operation_continue) )
|
||||
{
|
||||
op1 = operation_intersection;
|
||||
op2 = opposite ? operation_union : operation_intersection;
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT(tp.combination(operation_intersection, operation_union));
|
||||
op1 = operation_union;
|
||||
op2 = operation_union;
|
||||
}
|
||||
}
|
||||
else // last1
|
||||
{
|
||||
side_calculator<Point1, Point2> side_calc(i2, j1, i1, i2, j2, k2);
|
||||
equal<TurnInfo>::apply(i2, j1, i1, i2, j2, k2,
|
||||
tp, result.template get<0>(), result.template get<1>(), side_calc);
|
||||
if ( tp.both(operation_continue) )
|
||||
{
|
||||
op1 = operation_blocked;
|
||||
op2 = opposite ? operation_intersection : operation_union;
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT(tp.combination(operation_intersection, operation_union));
|
||||
op1 = operation_blocked;
|
||||
op2 = operation_union;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// do nothing
|
||||
// shouldn't be handled this way
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename Point1,
|
||||
typename Point2,
|
||||
typename TurnInfo,
|
||||
@@ -733,6 +554,200 @@ struct get_turn_info_linear_linear
|
||||
return result_ignore_ip;
|
||||
}
|
||||
|
||||
template<typename Point, typename Point1, typename Point2>
|
||||
static inline
|
||||
void handle_segment(bool /*first_a*/, bool last_a, int how_a, int arrival_a,
|
||||
bool /*first_b*/, bool last_b, int how_b, int arrival_b,
|
||||
bool opposite, std::size_t ip_count, bool same_dirs/*collinear*/,
|
||||
Point const& ip0, Point const& /*ip1*/,
|
||||
operation_type & op0_a, operation_type & op0_b,
|
||||
operation_type & op1_a, operation_type & op1_b,
|
||||
bool & i0_a, bool & j0_a, bool & i0_b, bool & j0_b,
|
||||
bool & i1_a, bool & j1_a, bool & i1_b, bool & j1_b,
|
||||
Point1 const& pi, Point1 const& /*pj*/, Point1 const& /*pk*/,
|
||||
Point2 const& qi, Point2 const& /*qj*/, Point2 const& /*qk*/)
|
||||
{
|
||||
namespace ov = overlay;
|
||||
|
||||
i0_a = false; j0_a = false; i0_b = false; j0_b = false;
|
||||
i1_a = false; j1_a = false; i1_b = false; j1_b = false;
|
||||
|
||||
if ( same_dirs )
|
||||
{
|
||||
if ( ip_count == 2 )
|
||||
{
|
||||
BOOST_ASSERT( how_a == 0 && how_b == 0 );
|
||||
|
||||
if ( !opposite )
|
||||
{
|
||||
op0_a = operation_intersection;
|
||||
op0_b = operation_intersection;
|
||||
op1_a = arrival_to_union_or_blocked(arrival_a, last_a);
|
||||
op1_b = arrival_to_union_or_blocked(arrival_b, last_b);
|
||||
|
||||
i0_a = equals::equals_point_point(pi, ip0);
|
||||
i0_b = equals::equals_point_point(qi, ip0);
|
||||
j1_a = arrival_a != -1;
|
||||
j1_b = arrival_b != -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
op0_a = operation_intersection;
|
||||
op0_b = arrival_to_union_or_blocked(arrival_b, last_b);
|
||||
op1_a = arrival_to_union_or_blocked(arrival_a, last_a);
|
||||
op1_b = operation_intersection;
|
||||
|
||||
i0_a = arrival_b != 1;
|
||||
j0_b = arrival_b != -1;
|
||||
j1_a = arrival_a != -1;
|
||||
i1_b = arrival_a != 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT(ip_count == 1);
|
||||
op0_a = arrival_to_union_or_blocked(arrival_a, last_a);
|
||||
op0_b = arrival_to_union_or_blocked(arrival_b, last_b);
|
||||
|
||||
i0_a = how_a == -1;
|
||||
i0_b = how_b == -1;
|
||||
j0_a = arrival_a == 0;
|
||||
j0_b = arrival_b == 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
op0_a = how_to_union_or_blocked(how_a, last_a);
|
||||
op0_b = how_to_union_or_blocked(how_b, last_b);
|
||||
|
||||
i0_a = how_a == -1;
|
||||
i0_b = how_b == -1;
|
||||
j0_a = how_a == 1;
|
||||
j0_b = how_b == 1;
|
||||
}
|
||||
}
|
||||
|
||||
// only if collinear (same_dirs)
|
||||
static inline operation_type arrival_to_union_or_blocked(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 how_to_union_or_blocked(int how, bool is_last)
|
||||
{
|
||||
if ( how == 1 )
|
||||
//return operation_blocked;
|
||||
return is_last ? operation_blocked : operation_union;
|
||||
else
|
||||
return operation_union;
|
||||
}
|
||||
|
||||
// TODO: IT'S ALSO PROBABLE THAT ALL THIS FUNCTION COULD BE INTEGRATED WITH handle_segment
|
||||
|
||||
template<typename Point1,
|
||||
typename Point2,
|
||||
typename Point,
|
||||
typename TurnInfo,
|
||||
typename IntersectionResult
|
||||
>
|
||||
static inline bool handle_internal(Point1 const& i1, Point1 const& j1, Point1 const& /*k1*/,
|
||||
Point2 const& i2, Point2 const& j2, Point2 const& k2,
|
||||
Point const& ip,
|
||||
bool first1, bool last1, bool first2, bool last2,
|
||||
bool ip_i2, bool ip_j2,
|
||||
TurnInfo const& tp_model,
|
||||
IntersectionResult const& result,
|
||||
operation_type & op1, operation_type & op2)
|
||||
{
|
||||
if ( !first2 && !last2 )
|
||||
{
|
||||
if ( first1 )
|
||||
{
|
||||
BOOST_ASSERT(ip_i2 == equals::equals_point_point(i2, ip));
|
||||
BOOST_ASSERT(ip_j2 == equals::equals_point_point(j2, ip));
|
||||
|
||||
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 )
|
||||
{
|
||||
bool opposite = result.template get<1>().opposite;
|
||||
|
||||
TurnInfo tp = tp_model;
|
||||
side_calculator<Point1, Point2> side_calc(i2, i1, j1, i2, j2, k2);
|
||||
equal<TurnInfo>::apply(i2, i1, j1, i2, j2, k2,
|
||||
tp, result.template get<0>(), result.template get<1>(), side_calc);
|
||||
|
||||
if ( tp.both(operation_continue) )
|
||||
{
|
||||
op1 = operation_intersection;
|
||||
op2 = opposite ? operation_union : operation_intersection;
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT(tp.combination(operation_intersection, operation_union));
|
||||
//op1 = operation_union;
|
||||
//op2 = operation_union;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
// else do nothing - shouldn't be handled this way
|
||||
}
|
||||
else if ( last1 )
|
||||
{
|
||||
BOOST_ASSERT(ip_i2 == equals::equals_point_point(i2, ip));
|
||||
BOOST_ASSERT(ip_j2 == equals::equals_point_point(j2, ip));
|
||||
|
||||
if ( ip_j2 )
|
||||
{
|
||||
// don't output this IP - for the first point of other geometry segment
|
||||
op1 = operation_none;
|
||||
op2 = operation_none;
|
||||
return true;
|
||||
}
|
||||
else if ( ip_i2 )
|
||||
{
|
||||
bool opposite = result.template get<1>().opposite;
|
||||
|
||||
TurnInfo tp = tp_model;
|
||||
side_calculator<Point1, Point2> side_calc(i2, j1, i1, i2, j2, k2);
|
||||
equal<TurnInfo>::apply(i2, j1, i1, i2, j2, k2,
|
||||
tp, result.template get<0>(), result.template get<1>(), side_calc);
|
||||
|
||||
if ( tp.both(operation_continue) )
|
||||
{
|
||||
op1 = operation_blocked;
|
||||
op2 = opposite ? operation_intersection : operation_union;
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT(tp.combination(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)
|
||||
{
|
||||
int pc = (ip_pi ? 1 : 0) + (ip_pj ? 1 : 0);
|
||||
|
||||
@@ -25,6 +25,135 @@ namespace boost { namespace geometry
|
||||
#ifndef DOXYGEN_NO_DETAIL
|
||||
namespace detail { namespace relate {
|
||||
|
||||
// update_result
|
||||
|
||||
template <field F1, field F2, char D, bool Reverse>
|
||||
struct update_result_dispatch
|
||||
{
|
||||
static inline void apply(result & res)
|
||||
{
|
||||
res.template update<F1, F2, D>();
|
||||
}
|
||||
};
|
||||
|
||||
template <field F1, field F2, char D>
|
||||
struct update_result_dispatch<F1, F2, D, true>
|
||||
{
|
||||
static inline void apply(result & res)
|
||||
{
|
||||
res.template update<F2, F1, D>();
|
||||
}
|
||||
};
|
||||
|
||||
template <field F1, field F2, char D, bool Reverse>
|
||||
inline void update_result(result & res)
|
||||
{
|
||||
update_result_dispatch<F1, F2, D, Reverse>::apply(res);
|
||||
}
|
||||
|
||||
// boundary_checker
|
||||
|
||||
template <typename Geometry,
|
||||
typename Tag = typename geometry::tag<Geometry>::type>
|
||||
class boundary_checker {};
|
||||
|
||||
template <typename Geometry>
|
||||
class boundary_checker<Geometry, linestring_tag>
|
||||
{
|
||||
typedef typename point_type<Geometry>::type point_type;
|
||||
|
||||
public:
|
||||
boundary_checker(Geometry const& g)
|
||||
: has_boundary(! detail::equals::equals_point_point(range::front(g), range::back(g)))
|
||||
, geometry(g)
|
||||
{}
|
||||
|
||||
// TODO: optimization expect ENTRY or EXIT
|
||||
bool is_boundary(point_type const& pt, segment_identifier const& sid)
|
||||
{
|
||||
// TODO: replace with assert?
|
||||
if ( boost::size(geometry) < 2 )
|
||||
return false;
|
||||
|
||||
// TODO: handle also linestrings with points_num == 2 and equals(front, back) - treat like point?
|
||||
|
||||
return has_boundary
|
||||
&& ( ( sid.segment_index == 0
|
||||
&& detail::equals::equals_point_point(pt, range::front(geometry)) )
|
||||
|| ( sid.segment_index + 2 == geometry::num_points(geometry)
|
||||
&& detail::equals::equals_point_point(pt, range::back(geometry)) ) );
|
||||
}
|
||||
|
||||
private:
|
||||
bool has_boundary;
|
||||
Geometry const& geometry;
|
||||
};
|
||||
|
||||
template <typename Geometry>
|
||||
class boundary_checker<Geometry, multi_linestring_tag>
|
||||
{
|
||||
typedef typename point_type<Geometry>::type point_type;
|
||||
|
||||
public:
|
||||
boundary_checker(Geometry const& g)
|
||||
: is_filled(false), geometry(g)
|
||||
{}
|
||||
|
||||
// TODO: optimization expect ENTRY or EXIT
|
||||
bool is_boundary(point_type const& pt, segment_identifier const& sid)
|
||||
{
|
||||
typedef typename boost::range_size<Geometry>::type size_type;
|
||||
size_type multi_count = boost::size(geometry);
|
||||
|
||||
// TODO: replace with assert?
|
||||
if ( multi_count < 1 )
|
||||
return false;
|
||||
|
||||
if ( sid.segment_index != 0 && sid.segment_index + 2 != geometry::num_points(geometry) )
|
||||
return false;
|
||||
|
||||
if ( ! is_filled )
|
||||
{
|
||||
//boundary_points.clear();
|
||||
boundary_points.reserve(multi_count * 2);
|
||||
|
||||
typedef typename boost::range_iterator<Geometry const>::type multi_iterator;
|
||||
for ( multi_iterator it = boost::begin(geometry) ;
|
||||
it != boost::end(geometry) ; ++ it )
|
||||
{
|
||||
// point - no boundary
|
||||
if ( boost::size(*it) < 2 )
|
||||
continue;
|
||||
|
||||
// TODO: handle also linestrings with points_num == 2 and equals(front, back) - treat like point?
|
||||
|
||||
boundary_points.push_back(range::front(geometry));
|
||||
boundary_points.push_back(range::back(geometry));
|
||||
}
|
||||
|
||||
std::sort(boundary_points.begin(), boundary_points.end(), geometry::less<point_type>());
|
||||
|
||||
is_filled = true;
|
||||
}
|
||||
|
||||
std::size_t equal_points_count
|
||||
= boost::size(
|
||||
std::equal_range(boundary_points.begin(),
|
||||
boundary_points.end(),
|
||||
geometry::less<point_type>())
|
||||
);
|
||||
|
||||
return equal_points_count % 2 != 0 && equal_points_count > 0; // the number is odd and > 0
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
bool is_filled;
|
||||
// TODO: store references/pointers instead of points?
|
||||
std::vector<point_type> boundary_points;
|
||||
Geometry const& geometry;
|
||||
};
|
||||
|
||||
// currently works only for linestrings
|
||||
template <typename Geometry1, typename Geometry2>
|
||||
struct linear_linear
|
||||
@@ -62,11 +191,10 @@ struct linear_linear
|
||||
if ( dimension < 10 )
|
||||
res.template set<exterior, exterior, '0' + dimension>();
|
||||
|
||||
// TODO: implement generic function working also for multilinestrings, also use it in point_in_geometry
|
||||
bool has_boundary1 = ! detail::equals::equals_point_point(range::front(geometry1), range::back(geometry1));
|
||||
bool has_boundary2 = ! detail::equals::equals_point_point(range::front(geometry2), range::back(geometry2));
|
||||
|
||||
handle_boundaries(res, geometry1, geometry2, has_boundary1, has_boundary2);
|
||||
// TEMP
|
||||
//bool has_boundary1 = ! detail::equals::equals_point_point(range::front(geometry1), range::back(geometry1));
|
||||
//bool has_boundary2 = ! detail::equals::equals_point_point(range::front(geometry2), range::back(geometry2));
|
||||
//handle_boundaries(res, geometry1, geometry2, has_boundary1, has_boundary2);
|
||||
|
||||
// get and analyse turns
|
||||
|
||||
@@ -74,20 +202,367 @@ struct linear_linear
|
||||
|
||||
turns::get_turns<Geometry1, Geometry2>::apply(turns, geometry1, geometry2);
|
||||
|
||||
if ( turns.empty() )
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
boundary_checker<Geometry1> boundary_checker1(geometry1);
|
||||
boundary_checker<Geometry2> boundary_checker2(geometry2);
|
||||
|
||||
// TODO: turns must be sorted and followed only if it's possible to go out and in on the same point
|
||||
// for linear geometries union operation must be detected which I guess would be quite often
|
||||
std::sort(turns.begin(), turns.end(), turns::less_seg_dist_op<>());
|
||||
std::sort(turns.begin(), turns.end(), turns::less_seg_dist_op<0,1,2,4,3,0,0>());
|
||||
|
||||
analyse_turns(res, turns.begin(), turns.end(), geometry1, geometry2, has_boundary1, has_boundary2);
|
||||
analyse_turns<0, 1>(res, turns.begin(), turns.end(),
|
||||
geometry1, geometry2,
|
||||
boundary_checker1, boundary_checker2);
|
||||
|
||||
std::sort(turns.begin(), turns.end(), turns::less_seg_dist_op<0,1,2,4,3,0,1>());
|
||||
|
||||
analyse_turns<1, 0>(res, turns.begin(), turns.end(),
|
||||
geometry2, geometry1,
|
||||
boundary_checker2, boundary_checker1);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
template <typename TurnIt>
|
||||
// TODO: rename to point_id_ref?
|
||||
template <typename Point>
|
||||
class point_identifier
|
||||
{
|
||||
public:
|
||||
point_identifier() : sid_ptr(0), pt_ptr(0) {}
|
||||
point_identifier(segment_identifier const& sid, Point const& pt)
|
||||
: sid_ptr(boost::addressof(sid))
|
||||
, pt_ptr(boost::addressof(pt))
|
||||
{}
|
||||
segment_identifier const& seg_id() const
|
||||
{
|
||||
BOOST_ASSERT(sid_ptr);
|
||||
return *sid_ptr;
|
||||
}
|
||||
Point const& point() const
|
||||
{
|
||||
BOOST_ASSERT(pt_ptr);
|
||||
return *pt_ptr;
|
||||
}
|
||||
|
||||
//friend bool operator==(point_identifier const& l, point_identifier const& r)
|
||||
//{
|
||||
// return l.seg_id() == r.seg_id()
|
||||
// && detail::equals::equals_point_point(l.point(), r.point());
|
||||
//}
|
||||
|
||||
private:
|
||||
const segment_identifier * sid_ptr;
|
||||
const Point * pt_ptr;
|
||||
};
|
||||
|
||||
class same_ranges
|
||||
{
|
||||
public:
|
||||
same_ranges(segment_identifier const& sid)
|
||||
: sid_ptr(boost::addressof(sid))
|
||||
{}
|
||||
|
||||
bool operator()(segment_identifier const& sid) const
|
||||
{
|
||||
return sid.multi_index == sid_ptr->multi_index
|
||||
&& sid.ring_index == sid_ptr->ring_index;
|
||||
}
|
||||
|
||||
template <typename Point>
|
||||
bool operator()(point_identifier<Point> const& pid) const
|
||||
{
|
||||
return operator()(pid.seg_id());
|
||||
}
|
||||
|
||||
private:
|
||||
const segment_identifier * sid_ptr;
|
||||
};
|
||||
|
||||
class segment_watcher
|
||||
{
|
||||
public:
|
||||
segment_watcher()
|
||||
: m_seg_id_ptr(0)
|
||||
{}
|
||||
|
||||
bool update(segment_identifier const& seg_id)
|
||||
{
|
||||
bool result = m_seg_id_ptr == 0 || !same_ranges(*m_seg_id_ptr)(seg_id);
|
||||
m_seg_id_ptr = boost::addressof(seg_id);
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
const segment_identifier * m_seg_id_ptr;
|
||||
};
|
||||
|
||||
template <typename Point>
|
||||
class exit_watcher
|
||||
{
|
||||
typedef point_identifier<Point> point_identifier;
|
||||
|
||||
public:
|
||||
exit_watcher()
|
||||
: exit_detected(false)
|
||||
{}
|
||||
|
||||
// returns true if before the call we were outside
|
||||
bool enter(Point const& point, segment_identifier const& other_id)
|
||||
{
|
||||
bool result = other_entry_points.empty();
|
||||
other_entry_points.push_back(point_identifier(other_id, point));
|
||||
return result;
|
||||
}
|
||||
|
||||
// returns true if before the call we were outside
|
||||
bool exit(Point const& point,
|
||||
segment_identifier const& other_id,
|
||||
bool is_last_point)
|
||||
{
|
||||
// if we didn't entered anything in the past, we're outside
|
||||
if ( other_entry_points.empty() )
|
||||
return true;
|
||||
|
||||
typedef typename std::vector<point_identifier>::iterator point_iterator;
|
||||
// search for the entry point in the same range of other geometry
|
||||
point_iterator entry_it = std::find_if(other_entry_points.begin(),
|
||||
other_entry_points.end(),
|
||||
same_ranges(other_id));
|
||||
|
||||
// this end point has corresponding entry point
|
||||
if ( entry_it != other_entry_points.end() )
|
||||
{
|
||||
if ( ! is_last_point )
|
||||
{
|
||||
// here we know that we possibly left LS
|
||||
// we must still check if we didn't get back on the same point
|
||||
exit_detected = true;
|
||||
exit_id = point_identifier(other_id, point);
|
||||
}
|
||||
|
||||
// erase the corresponding entry point
|
||||
other_entry_points.erase(entry_it);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_exit_detected() const
|
||||
{
|
||||
return exit_detected;
|
||||
}
|
||||
|
||||
Point const& get_exit_point() const
|
||||
{
|
||||
BOOST_ASSERT(exit_detected);
|
||||
return exit_id.point();
|
||||
}
|
||||
|
||||
overlay::operation_type get_exit_operation() const
|
||||
{
|
||||
BOOST_ASSERT(exit_detected);
|
||||
return exit_operation;
|
||||
}
|
||||
|
||||
void reset_detected_exit()
|
||||
{
|
||||
exit_detected = false;
|
||||
}
|
||||
|
||||
private:
|
||||
bool exit_detected;
|
||||
point_identifier exit_id;
|
||||
std::vector<point_identifier> other_entry_points; // TODO: use map here or sorted vector?
|
||||
};
|
||||
|
||||
template <std::size_t OpId,
|
||||
std::size_t OtherOpId,
|
||||
typename TurnIt,
|
||||
typename Geometry,
|
||||
typename OtherGeometry,
|
||||
typename BoundaryChecker,
|
||||
typename OtherBoundaryChecker>
|
||||
static inline void analyse_turns(result & res,
|
||||
TurnIt first, TurnIt last,
|
||||
Geometry1 const& geometry1, Geometry2 const& geometry2,
|
||||
bool has_boundary1, bool has_boundary2)
|
||||
Geometry const& geometry,
|
||||
OtherGeometry const& other_geometry,
|
||||
BoundaryChecker boundary_checker,
|
||||
OtherBoundaryChecker other_boundary_checker)
|
||||
{
|
||||
// point_type should be the same as the one stored in Turn
|
||||
typedef typename point_type<Geometry1>::type point_type;
|
||||
static const bool reverse_result = OpId != 0;
|
||||
|
||||
if ( first == last )
|
||||
return;
|
||||
|
||||
exit_watcher<point_type> exit_watcher;
|
||||
segment_watcher seg_watcher;
|
||||
|
||||
for ( TurnIt it = first ; it != last ; ++it )
|
||||
{
|
||||
overlay::operation_type op = it->operations[OpId].operation;
|
||||
segment_identifier const& seg_id = it->operations[OpId].seg_id;
|
||||
segment_identifier const& other_id = it->operations[OtherOpId].seg_id;
|
||||
|
||||
bool first_in_range = seg_watcher.update(seg_id); // TODO: could be calculated only for i and u
|
||||
bool fake_enter_detected = false;
|
||||
|
||||
// handle possible exit
|
||||
if ( exit_watcher.is_exit_detected() &&
|
||||
( op == overlay::operation_union || op == overlay::operation_intersection
|
||||
|| op == overlay::operation_continue || op == overlay::operation_blocked ) )
|
||||
{
|
||||
// real exit point - may be multiple
|
||||
// we know that we entered and now we exit
|
||||
if ( !detail::equals::equals_point_point(it->point, exit_watcher.get_exit_point()) )
|
||||
{
|
||||
exit_watcher.reset_detected_exit();
|
||||
|
||||
// not the last IP
|
||||
update_result<interior, exterior, '1', reverse_result>(res);
|
||||
}
|
||||
// fake exit point, reset state
|
||||
// in reality this will be op == overlay::operation_intersection
|
||||
else if ( op == overlay::operation_intersection )
|
||||
{
|
||||
exit_watcher.reset_detected_exit();
|
||||
fake_enter_detected = true;
|
||||
}
|
||||
}
|
||||
|
||||
// i/i, i/x, i/u
|
||||
if ( op == overlay::operation_intersection )
|
||||
{
|
||||
bool was_outside = exit_watcher.enter(it->point, other_id);
|
||||
|
||||
// interiors overlaps
|
||||
update_result<interior, interior, '1', reverse_result>(res);
|
||||
|
||||
// going inside on boundary point
|
||||
if ( boundary_checker.is_boundary(it->point, seg_id) )
|
||||
{
|
||||
// TODO: check operation_blocked here - only for Ls, for MLs it's not enough
|
||||
if ( other_boundary_checker.is_boundary(it->point, other_id) )
|
||||
{
|
||||
update_result<boundary, boundary, '0', reverse_result>(res);
|
||||
}
|
||||
else
|
||||
{
|
||||
update_result<boundary, interior, '0', reverse_result>(res);
|
||||
}
|
||||
}
|
||||
// going inside on non-boundary point
|
||||
else
|
||||
{
|
||||
// if we didn't enter in the past, we were outside
|
||||
if ( was_outside && !fake_enter_detected )
|
||||
{
|
||||
update_result<interior, exterior, '1', reverse_result>(res);
|
||||
|
||||
if ( first_in_range )
|
||||
update_result<boundary, exterior, '0', reverse_result>(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
// u/i, u/u, u/x, x/i, x/u, x/x
|
||||
else if ( op == overlay::operation_union || op == overlay::operation_blocked )
|
||||
{
|
||||
bool is_last_point = op == overlay::operation_blocked;
|
||||
bool was_outside = exit_watcher.exit(it->point, other_id, is_last_point);
|
||||
|
||||
// we're inside, possibly going out right now
|
||||
if ( ! was_outside )
|
||||
{
|
||||
if ( is_last_point )
|
||||
{
|
||||
// check if this is indeed the boundary point
|
||||
// TODO: For Linestring it's enough to check has_boundary
|
||||
// because we know that this is the last point of the range
|
||||
if ( boundary_checker.is_boundary(it->point, seg_id) )
|
||||
{
|
||||
// TODO: check operation_blocked here - only for Ls, for MLs it's not enough
|
||||
if ( other_boundary_checker.is_boundary(it->point, other_id) )
|
||||
{
|
||||
update_result<boundary, boundary, '0', reverse_result>(res);
|
||||
}
|
||||
else
|
||||
{
|
||||
update_result<boundary, interior, '0', reverse_result>(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// we're outside
|
||||
else
|
||||
{
|
||||
update_result<interior, exterior, '1', reverse_result>(res);
|
||||
|
||||
// boundaries don't overlap - just an optimization
|
||||
if ( it->method == overlay::method_crosses )
|
||||
{
|
||||
update_result<interior, interior, '0', reverse_result>(res);
|
||||
|
||||
if ( first_in_range )
|
||||
update_result<boundary, exterior, '0', reverse_result>(res);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: check operation_blocked here - only for Ls, for MLs it's not enough
|
||||
if ( boundary_checker.is_boundary(it->point, seg_id) )
|
||||
{
|
||||
// TODO: check operation_blocked here - only for Ls, for MLs it's not enough
|
||||
if ( other_boundary_checker.is_boundary(it->point, other_id) )
|
||||
update_result<boundary, boundary, '0', reverse_result>(res);
|
||||
else
|
||||
update_result<boundary, interior, '0', reverse_result>(res);
|
||||
|
||||
// first IP on the last segment point - this means that the first point is outside
|
||||
if ( first_in_range && is_last_point )
|
||||
update_result<boundary, exterior, '0', reverse_result>(res);
|
||||
|
||||
// TODO symetric case
|
||||
// last IP and the first segment point -> last IP and op == union
|
||||
}
|
||||
// boundaries don't overlap
|
||||
else
|
||||
{
|
||||
update_result<interior, interior, '0', reverse_result>(res);
|
||||
|
||||
if ( first_in_range )
|
||||
update_result<boundary, exterior, '0', reverse_result>(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( op == overlay::operation_continue )
|
||||
{
|
||||
//update_result<interior, interior, '1', reverse_result>(res);
|
||||
}
|
||||
}
|
||||
|
||||
// here, the possible exit is the real one
|
||||
// we know that we entered and now we exit
|
||||
if ( exit_watcher.is_exit_detected() )
|
||||
{
|
||||
update_result<interior, exterior, '1', reverse_result>(res);
|
||||
|
||||
// TODO: check if the last point is indeed the boundary
|
||||
// For LS it's sufficient to check has_boundary
|
||||
update_result<boundary, exterior, '0', reverse_result>(res);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename TurnIt>
|
||||
static inline void analyse_turns_simple(result & res,
|
||||
TurnIt first, TurnIt last,
|
||||
Geometry1 const& geometry1,
|
||||
Geometry2 const& geometry2,
|
||||
bool has_boundary1, bool has_boundary2)
|
||||
{
|
||||
for ( TurnIt it = first ; it != last ; ++it )
|
||||
{
|
||||
|
||||
@@ -84,6 +84,8 @@ struct point_in_geometry<Geometry, multi_linestring_tag>
|
||||
if ( pip < 0 )
|
||||
return -1;
|
||||
|
||||
// TODO: the following isn't needed for covered_by()
|
||||
|
||||
unsigned boundaries = pip == 0 ? 1 : 0;
|
||||
|
||||
for ( ; it != boost::end(geometry) ; ++it )
|
||||
|
||||
@@ -138,6 +138,9 @@ void test_geometry(std::string const& wkt1, std::string const& wkt2,
|
||||
test_geometry<G1, G2>(wkt1, wkt2, expected);
|
||||
}
|
||||
|
||||
//TEST
|
||||
//#include <to_svg.hpp>
|
||||
|
||||
template <typename T>
|
||||
void test_all()
|
||||
{
|
||||
@@ -178,6 +181,19 @@ void test_all()
|
||||
test_geometry<ls, ls>("LINESTRING(0 0,2 0,3 0,4 0,6 0)", "LINESTRING(2 -1,2 0,3 0,4 0,4 -1)", "tii", "ecc", "tuu");
|
||||
test_geometry<ls, ls>("LINESTRING(0 0,2 0,3 0,4 0,6 0)", "LINESTRING(4 1,4 0,3 0,2 0,2 1)", "tiu", "ecc", "tui");
|
||||
test_geometry<ls, ls>("LINESTRING(0 0,2 0,3 0,4 0,6 0)", "LINESTRING(4 -1,4 0,3 0,2 0,2 -1)", "tiu", "ecc", "tui");
|
||||
|
||||
test_geometry<ls, ls>("LINESTRING(0 0,1 0,2 1,3 5,4 0)", "LINESTRING(1 0,2 1,3 5)", "tii", "ecc", "tux");
|
||||
test_geometry<ls, ls>("LINESTRING(0 0,1 0,2 1,3 5,4 0)", "LINESTRING(3 5,2 1,1 0)", "tix", "ecc", "tui");
|
||||
test_geometry<ls, ls>("LINESTRING(1 0,2 1,3 5)", "LINESTRING(0 0,1 0,2 1,3 5,4 0)", "txu", "ecc", "tii");
|
||||
test_geometry<ls, ls>("LINESTRING(3 5,2 1,1 0)", "LINESTRING(0 0,1 0,2 1,3 5,4 0)", "tiu", "ecc", "txi");
|
||||
|
||||
//if ( boost::is_same<T, double>::value )
|
||||
//{
|
||||
// to_svg<ls, ls>("LINESTRING(0 0,1 0,2 0,2.5 0,3 1)", "LINESTRING(0 0,2 0,2.5 0,3 1)", "test11.svg");
|
||||
// to_svg<ls, ls>("LINESTRING(0 0,1 0,2 0,2.5 0,3 1)", "LINESTRING(3 1,2.5 0,2 0,0 0)", "test12.svg");
|
||||
// to_svg<ls, ls>("LINESTRING(-1 1,0 0,1 0,4 0,5 5,10 5,15 0,20 0,30 0,31 1)", "LINESTRING(30 0,3 0,2.5 1,2 0,1 0,0 0,-1 -1)", "test21.svg");
|
||||
// to_svg<ls, ls>("LINESTRING(-1 1,0 0,1 0,4 0,5 5,10 5,15 0,20 0,30 0,31 1)", "LINESTRING(-1 -1,0 0,1 0,2 0,2.5 1,3 0,30 0)", "test22.svg");
|
||||
//}
|
||||
}
|
||||
|
||||
int test_main(int, char* [])
|
||||
|
||||
@@ -124,13 +124,19 @@ void test_linestring_linestring()
|
||||
test_geometry<ls, ls>("LINESTRING(0 0,2 2,3 3,1 1,5 3)", "LINESTRING(6 3,3 3,0 0)", "1F100F102");
|
||||
test_geometry<ls, ls>("LINESTRING(5 3,1 1,3 3,2 2,0 0)", "LINESTRING(6 3,3 3,0 0)", "1F100F102");
|
||||
|
||||
|
||||
// spike
|
||||
test_geometry<ls, ls>("LINESTRING(0 0,10 0)",
|
||||
"LINESTRING(1 0,9 0,2 0)", "101FF0FF2");
|
||||
|
||||
// loop i/i i/i u/u u/u
|
||||
test_geometry<ls, ls>("LINESTRING(0 0,10 0)",
|
||||
"LINESTRING(1 1,1 0,6 0,6 1,4 1,4 0,9 0,9 1)", "1F1FF0102");
|
||||
|
||||
test_geometry<ls, ls>("LINESTRING(0 5,5 5,10 5,10 10,5 10,5 5,5 0)",
|
||||
"LINESTRING(0 5,5 5,5 10,10 10,10 5,5 5,5 0)", "1FFF0FFF2");
|
||||
|
||||
to_svg<ls, ls>("LINESTRING(0 5,5 5,10 5,10 10,5 10,5 5,5 0)", "LINESTRING(0 5,5 5,5 10,10 10,10 5,5 5,5 0)", "relate1.svg");
|
||||
|
||||
|
||||
// TEST ERROR - wrong result
|
||||
// test_geometry<ls, ls>("LINESTRING(0 5,5 5,10 5,10 10,5 10,5 5,5 0)",
|
||||
// "LINESTRING(0 5,5 5,0 10,10 10,10 5,5 5,5 0)", "1FFF0FFF2");
|
||||
|
||||
Reference in New Issue
Block a user