From 8e9a59bac3a68305b471b6830d55d760e9c29191 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Tue, 11 Mar 2014 18:06:47 +0100 Subject: [PATCH] [relate] helpers like segment_watcher, exit_watcher, etc. moved to a separate file, implemented preliminary version of L/A (commented out for now), found bug in L/L, added failing tests --- .../detail/relate/follow_helpers.hpp | 303 +++++++++ .../algorithms/detail/relate/linear_areal.hpp | 628 +++++++----------- .../detail/relate/linear_linear.hpp | 284 +------- test/algorithms/relate.cpp | 14 + 4 files changed, 574 insertions(+), 655 deletions(-) create mode 100644 include/boost/geometry/algorithms/detail/relate/follow_helpers.hpp diff --git a/include/boost/geometry/algorithms/detail/relate/follow_helpers.hpp b/include/boost/geometry/algorithms/detail/relate/follow_helpers.hpp new file mode 100644 index 000000000..b45ae138c --- /dev/null +++ b/include/boost/geometry/algorithms/detail/relate/follow_helpers.hpp @@ -0,0 +1,303 @@ +// 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_RELATE_FOLLOW_HELPERS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_FOLLOW_HELPERS_HPP + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace relate { + +// TODO: +// For 1-point linestrings or with all equal points turns won't be generated! +// Check for those degenerated cases may be connected with this one! + +template ::type, + bool IsMulti = boost::is_base_of::value +> +struct for_each_disjoint_geometry_if + : public not_implemented +{}; + +template +struct for_each_disjoint_geometry_if +{ + template + static inline bool apply(TurnIt first, TurnIt last, + Geometry const& geometry, + Pred & pred) + { + if ( first != last ) + return false; + pred(geometry); + return true; + } +}; + +template +struct for_each_disjoint_geometry_if +{ + template + static inline bool apply(TurnIt first, TurnIt last, + Geometry const& geometry, + Pred & pred) + { + BOOST_ASSERT(first != last); + + const std::size_t count = boost::size(geometry); + boost::ignore_unused_variable_warning(count); + + // O(I) + // gather info about turns generated for contained geometries + std::vector detected_intersections(count, false); + for ( TurnIt it = first ; it != last ; ++it ) + { + int multi_index = it->operations[OpId].seg_id.multi_index; + BOOST_ASSERT(multi_index >= 0); + std::size_t index = static_cast(multi_index); + BOOST_ASSERT(index < count); + detected_intersections[index] = true; + } + + bool found = false; + + // O(N) + // check predicate for each contained geometry without generated turn + for ( std::vector::iterator it = detected_intersections.begin() ; + it != detected_intersections.end() ; ++it ) + { + // if there were no intersections for this multi_index + if ( *it == false ) + { + found = true; + bool cont = pred( + *(boost::begin(geometry) + + std::distance(detected_intersections.begin(), it))); + if ( !cont ) + break; + } + } + + return found; + } +}; + +// TODO: rename to point_id_ref? +template +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 + bool operator()(point_identifier 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 +class exit_watcher +{ + typedef point_identifier point_info; + +public: + exit_watcher() + : exit_operation(overlay::operation_none) + {} + + // 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_info(other_id, point)); + return result; + } + + // returns true if before the call we were outside + bool exit(Point const& point, + segment_identifier const& other_id, + overlay::operation_type exit_op) + { + // if we didn't entered anything in the past, we're outside + if ( other_entry_points.empty() ) + return true; + + typedef typename std::vector::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() ) + { + // here we know that we possibly left LS + // we must still check if we didn't get back on the same point + exit_operation = exit_op; + exit_id = point_info(other_id, point); + + // erase the corresponding entry point + other_entry_points.erase(entry_it); + } + + return false; + } + + overlay::operation_type get_exit_operation() const + { + return exit_operation; + } + + Point const& get_exit_point() const + { + BOOST_ASSERT(exit_operation != overlay::operation_none); + return exit_id.point(); + } + + void reset_detected_exit() + { + exit_operation = overlay::operation_none; + } + +private: + overlay::operation_type exit_operation; + point_info exit_id; + std::vector other_entry_points; // TODO: use map here or sorted vector? +}; + +template +static inline bool is_endpoint_on_boundary(Point const& pt, + BoundaryChecker & boundary_checker) +{ + return boundary_checker.template is_endpoint_boundary(pt); +} + +template +static inline bool is_ip_on_boundary(IntersectionPoint const& ip, + OperationInfo const& operation_info, + BoundaryChecker & boundary_checker, + segment_identifier const& seg_id) +{ + boost::ignore_unused_variable_warning(seg_id); + + bool res = false; + + // IP on the last point of the linestring + if ( (BoundaryQuery == boundary_back || BoundaryQuery == boundary_any) + && operation_info.operation == overlay::operation_blocked ) + { + BOOST_ASSERT(operation_info.position == overlay::position_back); + // check if this point is a boundary + res = boundary_checker.template is_endpoint_boundary(ip); + +#ifdef BOOST_GEOMETRY_DEBUG_RELATE + BOOST_ASSERT(res == boundary_checker.template is_boundary(ip, seg_id)); +#endif + } + // IP on the last point of the linestring + else if ( (BoundaryQuery == boundary_front || BoundaryQuery == boundary_any) + && operation_info.position == overlay::position_front ) + { + // check if this point is a boundary + res = boundary_checker.template is_endpoint_boundary(ip); + +#ifdef BOOST_GEOMETRY_DEBUG_RELATE + BOOST_ASSERT(res == boundary_checker.template is_boundary(ip, seg_id)); +#endif + } + // IP somewhere in the interior + else + { +#ifdef BOOST_GEOMETRY_DEBUG_RELATE + BOOST_ASSERT(res == boundary_checker.template is_boundary(ip, seg_id)); +#endif + } + + return res; +} + +}} // namespace detail::relate +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_FOLLOW_HELPERS_HPP diff --git a/include/boost/geometry/algorithms/detail/relate/linear_areal.hpp b/include/boost/geometry/algorithms/detail/relate/linear_areal.hpp index cf55898be..6f51f1033 100644 --- a/include/boost/geometry/algorithms/detail/relate/linear_areal.hpp +++ b/include/boost/geometry/algorithms/detail/relate/linear_areal.hpp @@ -20,6 +20,7 @@ #include #include #include +#include namespace boost { namespace geometry { @@ -54,15 +55,9 @@ private: template struct linear_areal { - static const bool is_linear1 = boost::is_base_of< - linear_tag, - typename geometry::tag::type - >::value; - static const bool is_areal2 = boost::is_base_of< - areal_tag, - typename geometry::tag::type - >::value; - BOOST_STATIC_ASSERT(is_linear1 && is_areal2); + // check Linear / Areal + BOOST_STATIC_ASSERT(detail::group_dim::value == 1 + && detail::group_dim::value == 2); static const bool interruption_enabled = true; @@ -71,13 +66,13 @@ struct linear_areal // if the result should be transposed, because the order of geometries was reversed // then not transposed result becomes the transposed one, and the opposite - static const bool transpose = !TransposeResult; + static const bool transpose_result = !TransposeResult; template static inline void apply(Geometry1 const& geometry1, Geometry2 const& geometry2, Result & result) { // The result should be FFFFFFFFF - set::value, !transpose>(result);// FFFFFFFFd, d in [1,9] or T + set::value, !transpose_result>(result);// FFFFFFFFd, d in [1,9] or T if ( result.interrupt ) return; @@ -91,12 +86,12 @@ struct linear_areal turns::get_turns::apply(turns, geometry1, geometry2); boundary_checker boundary_checker1(geometry1); - disjoint_linestring_pred, !transpose> pred1(result, boundary_checker1); + disjoint_linestring_pred, !transpose_result> pred1(result, boundary_checker1); for_each_disjoint_geometry_if<0, Geometry1>::apply(turns.begin(), turns.end(), geometry1, pred1); if ( result.interrupt ) return; - disjoint_areal_pred pred2(result); + disjoint_areal_pred pred2(result); for_each_disjoint_geometry_if<1, Geometry2>::apply(turns.begin(), turns.end(), geometry2, pred2); if ( result.interrupt ) return; @@ -113,367 +108,252 @@ struct linear_areal // geometry1, geometry2, // boundary_checker1); } -// -// // This analyser should be used like Input or SinglePass Iterator -// template -// class turns_analyser -// { -// typedef typename TurnInfo::point_type turn_point_type; -// -// static const std::size_t op_id = 0; -// static const std::size_t other_op_id = 1; -// // if the result should be transposed, because the order of geometries was reversed -// // then not transposed result becomes the transposed one, and the opposite -// static const bool transpose_result = !TransposeResult; -// -// public: -// turns_analyser() -// : m_previous_turn_ptr(0) -// , m_previous_operation(overlay::operation_none) -// {} -// -// template -// void apply(Result & res, -// TurnIt first, TurnIt it, TurnIt last, -// Geometry const& geometry, -// OtherGeometry const& other_geometry, -// BoundaryChecker & boundary_checker) -// { -// if ( it != last ) -// { -// overlay::operation_type op = it->operations[op_id].operation; -// -// if ( op != overlay::operation_union -// && op != overlay::operation_intersection -// && op != overlay::operation_blocked ) -// { -// return; -// } -// -// segment_identifier const& seg_id = it->operations[op_id].seg_id; -// segment_identifier const& other_id = it->operations[other_op_id].seg_id; -// -// const bool first_in_range = m_seg_watcher.update(seg_id); -// -// // handle possible exit -// bool fake_enter_detected = false; -// if ( m_exit_watcher.get_exit_operation() == overlay::operation_union ) -// { -// // real exit point - may be multiple -// // we know that we entered and now we exit -// if ( !detail::equals::equals_point_point(it->point, m_exit_watcher.get_exit_point()) ) -// { -// m_exit_watcher.reset_detected_exit(); -// -// // not the last IP -// update(res); -// } -// // fake exit point, reset state -// else if ( op == overlay::operation_intersection ) -// { -// m_exit_watcher.reset_detected_exit(); -// fake_enter_detected = true; -// } -// } -// else if ( m_exit_watcher.get_exit_operation() == overlay::operation_blocked ) -// { -// // ignore multiple BLOCKs -// if ( op == overlay::operation_blocked ) -// return; -// -// m_exit_watcher.reset_detected_exit(); -// } -// -// // if the new linestring started just now, -// // but the previous one went out on the previous point, -// // we must check if the boundary of the previous segment is outside -// // NOTE: couldn't it be integrated with the handling of the union above? -// if ( first_in_range -// && ! fake_enter_detected -// && m_previous_operation == overlay::operation_union ) -// { -// BOOST_ASSERT(it != first); -// BOOST_ASSERT(m_previous_turn_ptr); -// -// segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id; -// -// bool prev_back_b = is_endpoint_on_boundary( -// range::back(sub_geometry::get(geometry, prev_seg_id)), -// boundary_checker); -// -// // if there is a boundary on the last point -// if ( prev_back_b ) -// { -// update(res); -// } -// } -// -// // i/i, i/x, i/u -// if ( op == overlay::operation_intersection ) -// { -// bool was_outside = m_exit_watcher.enter(it->point, other_id); -// -// // interiors overlaps -// update(res); -// -// bool this_b = is_ip_on_boundary(it->point, -// it->operations[op_id], -// boundary_checker, -// seg_id); -// -// // going inside on boundary point -// // may be front only -// if ( this_b ) -// { -// // may be front and back -// bool other_b = is_ip_on_boundary(it->point, -// it->operations[other_op_id], -// other_boundary_checker, -// other_id); -// -// // it's also the boundary of the other geometry -// if ( other_b ) -// { -// update(res); -// } -// else -// { -// update(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(res); -// -// // if it's the first IP then the first point is outside -// if ( first_in_range ) -// { -// bool front_b = is_endpoint_on_boundary( -// range::front(sub_geometry::get(geometry, seg_id)), -// boundary_checker); -// -// // if there is a boundary on the first point -// if ( front_b ) -// { -// update(res); -// } -// } -// } -// } -// } -// // u/i, u/u, u/x, x/i, x/u, x/x -// else if ( op == overlay::operation_union || op == overlay::operation_blocked ) -// { -// bool op_blocked = op == overlay::operation_blocked; -// bool was_outside = m_exit_watcher.exit(it->point, other_id, op); -// -// // we're inside, possibly going out right now -// if ( ! was_outside ) -// { -// if ( op_blocked ) -// { -// // check if this is indeed the boundary point -// // NOTE: is_ip_on_boundary<>() should be called here but the result will be the same -// if ( is_endpoint_on_boundary(it->point, boundary_checker) ) -// { -// // may be front and back -// bool other_b = is_ip_on_boundary(it->point, -// it->operations[other_op_id], -// other_boundary_checker, -// other_id); -// // it's also the boundary of the other geometry -// if ( other_b ) -// { -// update(res); -// } -// else -// { -// update(res); -// } -// } -// } -// } -// // we're outside -// else -// { -// update(res); -// -// // boundaries don't overlap - just an optimization -// if ( it->method == overlay::method_crosses ) -// { -// update(res); -// -// // it's the first point in range -// if ( first_in_range ) -// { -// bool front_b = is_endpoint_on_boundary( -// range::front(sub_geometry::get(geometry, seg_id)), -// boundary_checker); -// -// // if there is a boundary on the first point -// if ( front_b ) -// { -// update(res); -// } -// } -// } -// // method other than crosses, check more conditions -// else -// { -// bool this_b = is_ip_on_boundary(it->point, -// it->operations[op_id], -// boundary_checker, -// seg_id); -// -// bool other_b = is_ip_on_boundary(it->point, -// it->operations[other_op_id], -// other_boundary_checker, -// other_id); -// -// // if current IP is on boundary of the geometry -// if ( this_b ) -// { -// // it's also the boundary of the other geometry -// if ( other_b ) -// update(res); -// else -// update(res); -// } -// // if current IP is not on boundary of the geometry -// else -// { -// // it's also the boundary of the other geometry -// if ( other_b ) -// update(res); -// else -// update(res); -// } -// -// // first IP on the last segment point - this means that the first point is outside -// if ( first_in_range && ( !this_b || op_blocked ) ) -// { -// bool front_b = is_endpoint_on_boundary( -// range::front(sub_geometry::get(geometry, seg_id)), -// boundary_checker); -// -// // if there is a boundary on the first point -// if ( front_b ) -// { -// update(res); -// } -// } -// -// } -// } -// } -// -// // store ref to previously analysed (valid) turn -// m_previous_turn_ptr = boost::addressof(*it); -// // and previously analysed (valid) operation -// m_previous_operation = op; -// } -// // it == last -// else -// { -// // here, the possible exit is the real one -// // we know that we entered and now we exit -// if ( m_exit_watcher.get_exit_operation() == overlay::operation_union // THIS CHECK IS REDUNDANT -// || m_previous_operation == overlay::operation_union ) -// { -// // for sure -// update(res); -// -// BOOST_ASSERT(first != last); -// BOOST_ASSERT(m_previous_turn_ptr); -// -// segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[OpId].seg_id; -// -// bool prev_back_b = is_endpoint_on_boundary( -// range::back(sub_geometry::get(geometry, prev_seg_id)), -// boundary_checker); -// -// // if there is a boundary on the last point -// if ( prev_back_b ) -// { -// update(res); -// } -// } -// } -// } -// -// template -// static inline -// bool is_endpoint_on_boundary(Point const& pt, -// BoundaryChecker & boundary_checker) -// { -// return boundary_checker.template is_endpoint_boundary(pt); -// } -// -// template -// static inline -// bool is_ip_on_boundary(IntersectionPoint const& ip, -// OperationInfo const& operation_info, -// BoundaryChecker & boundary_checker, -// segment_identifier const& seg_id) -// { -// boost::ignore_unused_variable_warning(seg_id); -// -// bool res = false; -// -// // IP on the last point of the linestring -// if ( (BoundaryQuery == boundary_back || BoundaryQuery == boundary_any) -// && operation_info.operation == overlay::operation_blocked ) -// { -// BOOST_ASSERT(operation_info.position == overlay::position_back); -// // check if this point is a boundary -// res = boundary_checker.template is_endpoint_boundary(ip); -// -//#ifdef BOOST_GEOMETRY_DEBUG_RELATE_LINEAR_LINEAR -// BOOST_ASSERT(res == boundary_checker.template is_boundary(ip, seg_id)); -//#endif -// } -// // IP on the last point of the linestring -// else if ( (BoundaryQuery == boundary_front || BoundaryQuery == boundary_any) -// && operation_info.position == overlay::position_front ) -// { -// // check if this point is a boundary -// res = boundary_checker.template is_endpoint_boundary(ip); -// -//#ifdef BOOST_GEOMETRY_DEBUG_RELATE_LINEAR_LINEAR -// BOOST_ASSERT(res == boundary_checker.template is_boundary(ip, seg_id)); -//#endif -// } -// // IP somewhere in the interior -// else -// { -//#ifdef BOOST_GEOMETRY_DEBUG_RELATE_LINEAR_LINEAR -// BOOST_ASSERT(res == boundary_checker.template is_boundary(ip, seg_id)); -//#endif -// } -// -// return res; -// } -// -// private: -// exit_watcher m_exit_watcher; -// segment_watcher m_seg_watcher; -// TurnInfo * m_previous_turn_ptr; -// overlay::operation_type m_previous_operation; -// }; + + //// This analyser should be used like Input or SinglePass Iterator + //template + //class turns_analyser + //{ + // typedef typename TurnInfo::point_type turn_point_type; + + // static const std::size_t op_id = 0; + // static const std::size_t other_op_id = 1; + // // if the result should be transposed, because the order of geometries was reversed + // // then not transposed result becomes the transposed one, and the opposite + // static const bool transpose_result = !TransposeResult; + + //public: + // turns_analyser() + // : m_previous_turn_ptr(0) + // , m_previous_operation(overlay::operation_none) + // {} + + // template + // void apply(Result & res, + // TurnIt first, TurnIt it, TurnIt last, + // Geometry const& geometry, + // OtherGeometry const& other_geometry, + // BoundaryChecker & boundary_checker) + // { + // if ( it != last ) + // { + // overlay::operation_type op = it->operations[op_id].operation; + + // if ( op != overlay::operation_union + // && op != overlay::operation_intersection + // && op != overlay::operation_blocked + // && op != overlay::operation_continue ) // operation_boundary / operation_boundary_intersection + // { + // return; + // } + + // segment_identifier const& seg_id = it->operations[op_id].seg_id; + // segment_identifier const& other_id = it->operations[other_op_id].seg_id; + + // const bool first_in_range = m_seg_watcher.update(seg_id); + + // // handle possible exit + // bool fake_enter_detected = false; + // if ( m_exit_watcher.get_exit_operation() == overlay::operation_union ) + // { + // // real exit point - may be multiple + // // we know that we entered and now we exit + // if ( !detail::equals::equals_point_point(it->point, m_exit_watcher.get_exit_point()) ) + // { + // m_exit_watcher.reset_detected_exit(); + // + // // not the last IP + // update(res); + // } + // // fake exit point, reset state + // else if ( op == overlay::operation_intersection + // || op == overlay::operation_continue ) // operation_boundary + // { + // m_exit_watcher.reset_detected_exit(); + // fake_enter_detected = true; + // } + // } + // else if ( m_exit_watcher.get_exit_operation() == overlay::operation_blocked ) + // { + // // ignore multiple BLOCKs + // if ( op == overlay::operation_blocked ) + // return; + + // m_exit_watcher.reset_detected_exit(); + // } + + // // if the new linestring started just now, + // // but the previous one went out on the previous point, + // // we must check if the boundary of the previous segment is outside + // // NOTE: couldn't it be integrated with the handling of the union above? + // if ( first_in_range + // && ! fake_enter_detected + // && m_previous_operation == overlay::operation_union ) + // { + // BOOST_ASSERT(it != first); + // BOOST_ASSERT(m_previous_turn_ptr); + + // segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id; + + // bool prev_back_b = is_endpoint_on_boundary( + // range::back(sub_geometry::get(geometry, prev_seg_id)), + // boundary_checker); + + // // if there is a boundary on the last point + // if ( prev_back_b ) + // { + // update(res); + // } + // } + + // // i/u, c/u + // if ( op == overlay::operation_intersection + // || op == overlay::operation_continue ) // operation_boundary/operation_boundary_intersection + // { + // bool was_outside = m_exit_watcher.enter(it->point, other_id); + + // if ( op == overlay::operation_intersection ) + // { + // // interiors overlaps + // update(res); + // update(res); + // } + // else // operation_boundary + // { + // update(res); + // } + + // bool this_b = is_ip_on_boundary(it->point, + // it->operations[op_id], + // boundary_checker, + // seg_id); + // // going inside on boundary point + // if ( this_b ) + // { + // update(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(res); + + // // if it's the first IP then the first point is outside + // if ( first_in_range ) + // { + // bool front_b = is_endpoint_on_boundary( + // range::front(sub_geometry::get(geometry, seg_id)), + // boundary_checker); + + // // if there is a boundary on the first point + // if ( front_b ) + // { + // update(res); + // } + // } + // } + // } + // } + // // u/u, x/u + // else if ( op == overlay::operation_union || op == overlay::operation_blocked ) + // { + // bool op_blocked = op == overlay::operation_blocked; + // bool was_outside = m_exit_watcher.exit(it->point, other_id, op); + + // // we're inside, possibly going out right now + // if ( ! was_outside ) + // { + // if ( op_blocked ) + // { + // // check if this is indeed the boundary point + // // NOTE: is_ip_on_boundary<>() should be called here but the result will be the same + // if ( is_endpoint_on_boundary(it->point, boundary_checker) ) + // { + // update(res); + // } + // } + // } + // // we're outside + // else + // { + // update(res); + + // bool this_b = is_ip_on_boundary(it->point, + // it->operations[op_id], + // boundary_checker, + // seg_id); + // // if current IP is on boundary of the geometry + // if ( this_b ) + // { + // update(res); + // } + // // if current IP is not on boundary of the geometry + // else + // { + // update(res); + // } + + // // first IP on the last segment point - this means that the first point is outside + // if ( first_in_range && ( !this_b || op_blocked ) ) + // { + // bool front_b = is_endpoint_on_boundary( + // range::front(sub_geometry::get(geometry, seg_id)), + // boundary_checker); + + // // if there is a boundary on the first point + // if ( front_b ) + // { + // update(res); + // } + // } + // } + // } + + // // store ref to previously analysed (valid) turn + // m_previous_turn_ptr = boost::addressof(*it); + // // and previously analysed (valid) operation + // m_previous_operation = op; + // } + // // it == last + // else + // { + // // here, the possible exit is the real one + // // we know that we entered and now we exit + // if ( m_exit_watcher.get_exit_operation() == overlay::operation_union // THIS CHECK IS REDUNDANT + // || m_previous_operation == overlay::operation_union ) + // { + // // for sure + // update(res); + + // BOOST_ASSERT(first != last); + // BOOST_ASSERT(m_previous_turn_ptr); + + // segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id; + + // bool prev_back_b = is_endpoint_on_boundary( + // range::back(sub_geometry::get(geometry, prev_seg_id)), + // boundary_checker); + + // // if there is a boundary on the last point + // if ( prev_back_b ) + // { + // update(res); + // } + // } + // } + // } + + //private: + // exit_watcher m_exit_watcher; + // segment_watcher m_seg_watcher; + // TurnInfo * m_previous_turn_ptr; + // overlay::operation_type m_previous_operation; + //}; template #include #include +#include namespace boost { namespace geometry { @@ -27,85 +28,6 @@ namespace boost { namespace geometry #ifndef DOXYGEN_NO_DETAIL namespace detail { namespace relate { -// TODO: -// For 1-point linestrings or with all equal points turns won't be generated! -// Check for those degenerated cases may be connected with this one! - -// TODO: -// Move this to separate file, maybe name it differently e.g. for_each_turnless_geometry_if - -template ::type, - bool IsMulti = boost::is_base_of::value -> -struct for_each_disjoint_geometry_if - : public not_implemented -{}; - -template -struct for_each_disjoint_geometry_if -{ - template - static inline bool apply(TurnIt first, TurnIt last, - Geometry const& geometry, - Pred & pred) - { - if ( first != last ) - return false; - pred(geometry); - return true; - } -}; - -template -struct for_each_disjoint_geometry_if -{ - template - static inline bool apply(TurnIt first, TurnIt last, - Geometry const& geometry, - Pred & pred) - { - BOOST_ASSERT(first != last); - - const std::size_t count = boost::size(geometry); - boost::ignore_unused_variable_warning(count); - - // O(I) - // gather info about turns generated for contained geometries - std::vector detected_intersections(count, false); - for ( TurnIt it = first ; it != last ; ++it ) - { - int multi_index = it->operations[OpId].seg_id.multi_index; - BOOST_ASSERT(multi_index >= 0); - std::size_t index = static_cast(multi_index); - BOOST_ASSERT(index < count); - detected_intersections[index] = true; - } - - bool found = false; - - // O(N) - // check predicate for each contained geometry without generated turn - for ( std::vector::iterator it = detected_intersections.begin() ; - it != detected_intersections.end() ; ++it ) - { - // if there were no intersections for this multi_index - if ( *it == false ) - { - found = true; - bool cont = pred( - *(boost::begin(geometry) - + std::distance(detected_intersections.begin(), it))); - if ( !cont ) - break; - } - } - - return found; - } -}; - template class disjoint_linestring_pred { @@ -341,149 +263,6 @@ struct linear_linear } } - // TODO: rename to point_id_ref? - template - 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 - bool operator()(point_identifier 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 - class exit_watcher - { - typedef point_identifier point_info; - - public: - exit_watcher() - : exit_operation(overlay::operation_none) - {} - - // 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_info(other_id, point)); - return result; - } - - // returns true if before the call we were outside - bool exit(Point const& point, - segment_identifier const& other_id, - overlay::operation_type exit_op) - { - // if we didn't entered anything in the past, we're outside - if ( other_entry_points.empty() ) - return true; - - typedef typename std::vector::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() ) - { - // here we know that we possibly left LS - // we must still check if we didn't get back on the same point - exit_operation = exit_op; - exit_id = point_info(other_id, point); - - // erase the corresponding entry point - other_entry_points.erase(entry_it); - } - - return false; - } - - overlay::operation_type get_exit_operation() const - { - return exit_operation; - } - - Point const& get_exit_point() const - { - BOOST_ASSERT(exit_operation != overlay::operation_none); - return exit_id.point(); - } - - void reset_detected_exit() - { - exit_operation = overlay::operation_none; - } - - private: - overlay::operation_type exit_operation; - point_info exit_id; - std::vector other_entry_points; // TODO: use map here or sorted vector? - }; - // This analyser should be used like Input or SinglePass Iterator template class turns_analyser @@ -679,6 +458,7 @@ struct linear_linear // boundaries don't overlap - just an optimization if ( it->method == overlay::method_crosses ) { + // the L1 is going from one side of the L2 to the other through the point update(res); // it's the first point in range @@ -764,7 +544,7 @@ struct linear_linear BOOST_ASSERT(first != last); BOOST_ASSERT(m_previous_turn_ptr); - segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[OpId].seg_id; + segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id; bool prev_back_b = is_endpoint_on_boundary( range::back(sub_geometry::get(geometry, prev_seg_id)), @@ -779,64 +559,6 @@ struct linear_linear } } - template - static inline - bool is_endpoint_on_boundary(Point const& pt, - BoundaryChecker & boundary_checker) - { - return boundary_checker.template is_endpoint_boundary(pt); - } - - template - static inline - bool is_ip_on_boundary(IntersectionPoint const& ip, - OperationInfo const& operation_info, - BoundaryChecker & boundary_checker, - segment_identifier const& seg_id) - { - boost::ignore_unused_variable_warning(seg_id); - - bool res = false; - - // IP on the last point of the linestring - if ( (BoundaryQuery == boundary_back || BoundaryQuery == boundary_any) - && operation_info.operation == overlay::operation_blocked ) - { - BOOST_ASSERT(operation_info.position == overlay::position_back); - // check if this point is a boundary - res = boundary_checker.template is_endpoint_boundary(ip); - -#ifdef BOOST_GEOMETRY_DEBUG_RELATE_LINEAR_LINEAR - BOOST_ASSERT(res == boundary_checker.template is_boundary(ip, seg_id)); -#endif - } - // IP on the last point of the linestring - else if ( (BoundaryQuery == boundary_front || BoundaryQuery == boundary_any) - && operation_info.position == overlay::position_front ) - { - // check if this point is a boundary - res = boundary_checker.template is_endpoint_boundary(ip); - -#ifdef BOOST_GEOMETRY_DEBUG_RELATE_LINEAR_LINEAR - BOOST_ASSERT(res == boundary_checker.template is_boundary(ip, seg_id)); -#endif - } - // IP somewhere in the interior - else - { -#ifdef BOOST_GEOMETRY_DEBUG_RELATE_LINEAR_LINEAR - BOOST_ASSERT(res == boundary_checker.template is_boundary(ip, seg_id)); -#endif - } - - return res; - } - private: exit_watcher m_exit_watcher; segment_watcher m_seg_watcher; diff --git a/test/algorithms/relate.cpp b/test/algorithms/relate.cpp index 27059cfa8..0f3084fa0 100644 --- a/test/algorithms/relate.cpp +++ b/test/algorithms/relate.cpp @@ -290,6 +290,20 @@ void test_linestring_linestring() "LINESTRING(30 0,4 0,3 1,2 0,1 0,0 0,-1 -1)", "101FF0102"); + // self-IP + test_geometry("LINESTRING(1 0,9 0)", + "LINESTRING(0 0,10 0,10 10,5 0,0 10)", + "1FF0FF102"); + test_geometry("LINESTRING(1 0,5 0,9 0)", + "LINESTRING(0 0,10 0,10 10,5 0,0 10)", + "1FF0FF102"); + test_geometry("LINESTRING(1 0,9 0)", + "LINESTRING(0 0,10 0,10 10,5 10,5 -1)", + "1FF0FF102"); + test_geometry("LINESTRING(1 0,9 0)", + "LINESTRING(0 0,10 0,5 0,5 5)", + "1FF0FF102"); + // linear ring test_geometry("LINESTRING(0 0,10 0)", "LINESTRING(5 0,9 0,5 5,1 0,5 0)", "1F1FF01F2"); test_geometry("LINESTRING(0 0,5 0,10 0)", "LINESTRING(5 0,9 0,5 5,1 0,5 0)", "1F1FF01F2");