Merge branch 'feature/relate' of https://github.com/boostorg/geometry into feature/setops

This commit is contained in:
Menelaos Karavelas
2014-02-11 08:38:01 +02:00
5 changed files with 705 additions and 191 deletions

View File

@@ -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);

View File

@@ -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 )
{

View File

@@ -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 )

View File

@@ -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* [])

View File

@@ -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");