diff --git a/include/boost/geometry/algorithms/detail/relate/areal_areal.hpp b/include/boost/geometry/algorithms/detail/relate/areal_areal.hpp index 90274fd48..794c0c54c 100644 --- a/include/boost/geometry/algorithms/detail/relate/areal_areal.hpp +++ b/include/boost/geometry/algorithms/detail/relate/areal_areal.hpp @@ -133,22 +133,20 @@ struct areal_areal if ( turns.empty() ) return; -// -// { -// // for different multi or same ring id: x, u, i, c -// // for same multi and different ring id: c, i, u, x -// std::sort(turns.begin(), turns.end(), turns::less_seg_dist_op<0,2,3,1,4,0,0>()); -// -// turns_analyser analyser; -// analyse_each_turn(result, analyser, -// turns.begin(), turns.end(), -// geometry1, geometry2, -// boundary_checker1); -// -// if ( result.interrupt ) -// return; -// } -// + //{ + // // for different multi or same ring id: u, i, x, c + // // for same multi and different ring id: c, x, i, u + // std::sort(turns.begin(), turns.end(), turns::less_seg_dist_op<0,1,2,3,4,0,0>()); + + // turns_analyser analyser; + // analyse_each_turn(result, analyser, + // turns.begin(), turns.end(), + // geometry1, geometry2); + + // if ( result.interrupt ) + // return; + //} + //// TODO: CALCULATE THE FOLLOWING ONLY IF IT'S REQUIRED BY THE RESULT! //// AND ONLY IF IT WAS NOT SET BY THE no_turns_la_areal_pred // @@ -326,7 +324,9 @@ struct areal_areal else if ( op == overlay::operation_blocked ) { update(m_result); - update(m_result); + // NOTE: currently E^E is set without any checks before turns are gathered + // so currently the following line is redundant + //update(m_result); } } @@ -387,7 +387,7 @@ struct areal_areal { // 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()) ) + if ( ! turn_on_the_same_ip(m_exit_watcher.get_exit_turn(), *it) ) { m_exit_watcher.reset_detected_exit(); @@ -425,7 +425,7 @@ struct areal_areal if ( m_interior_detected ) { // real interior overlap - if ( !detail::equals::equals_point_point(it->point, m_previous_turn_ptr->point) ) + if ( ! turn_on_the_same_ip(*m_previous_turn_ptr, *it) ) { update(res); m_interior_detected = false; @@ -467,7 +467,7 @@ struct areal_areal || op == overlay::operation_continue ) // operation_boundary/operation_boundary_intersection { bool no_enters_detected = m_exit_watcher.is_outside(); - m_exit_watcher.enter(it->point, other_id); + m_exit_watcher.enter(*it); if ( op == overlay::operation_intersection ) { @@ -640,7 +640,7 @@ struct areal_areal || it->operations[op_id].is_collinear ) { // notify the exit watcher about the possible exit - m_exit_watcher.exit(it->point, other_id, op); + m_exit_watcher.exit(*it); } } @@ -837,7 +837,7 @@ struct areal_areal } private: - exit_watcher m_exit_watcher; + exit_watcher m_exit_watcher; segment_watcher m_seg_watcher; TurnInfo * m_previous_turn_ptr; overlay::operation_type m_previous_operation; diff --git a/include/boost/geometry/algorithms/detail/relate/follow_helpers.hpp b/include/boost/geometry/algorithms/detail/relate/follow_helpers.hpp index eed9a8228..7e42a356f 100644 --- a/include/boost/geometry/algorithms/detail/relate/follow_helpers.hpp +++ b/include/boost/geometry/algorithms/detail/relate/follow_helpers.hpp @@ -127,6 +127,9 @@ struct for_each_disjoint_geometry_if } }; +// WARNING! This class stores pointers! +// Passing a reference to local variable will result in undefined behavior! + // TODO: rename to point_id_ref? template class point_identifier @@ -159,6 +162,8 @@ private: const Point * pt_ptr; }; +// WARNING! This class stores pointers! +// Passing a reference to local variable will result in undefined behavior! class same_multi_index { public: @@ -181,6 +186,8 @@ private: const segment_identifier * sid_ptr; }; +// WARNING! This class stores pointers! +// Passing a reference to local variable will result in undefined behavior! class segment_watcher { public: @@ -199,25 +206,35 @@ private: const segment_identifier * m_seg_id_ptr; }; -template +// WARNING! This class stores pointers! +// Passing a reference to local variable will result in undefined behavior! +template class exit_watcher { - typedef point_identifier point_info; + static const std::size_t op_id = OpId; + static const std::size_t other_op_id = (OpId + 1) % 2; + + typedef typename TurnInfo::point_type point_type; + typedef point_identifier point_info; public: exit_watcher() : exit_operation(overlay::operation_none) + , exit_turn(0) {} - void enter(Point const& point, segment_identifier const& other_id) + void enter(TurnInfo const& turn) { - other_entry_points.push_back(point_info(other_id, point)); + other_entry_points.push_back( + point_info(turn.operations[other_op_id].seg_id, turn.point) ); } - void exit(Point const& point, - segment_identifier const& other_id, - overlay::operation_type exit_op) + void exit(TurnInfo const& turn) { + segment_identifier const& seg_id = turn.operations[op_id].seg_id; + segment_identifier const& other_id = turn.operations[other_op_id].seg_id; + overlay::operation_type exit_op = turn.operations[op_id].operation; + 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(), @@ -230,7 +247,7 @@ public: // 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); + exit_turn = boost::addressof(turn); // erase the corresponding entry point other_entry_points.erase(entry_it); @@ -248,10 +265,18 @@ public: return exit_operation; } - Point const& get_exit_point() const + point_type const& get_exit_point() const { BOOST_ASSERT(exit_operation != overlay::operation_none); - return exit_id.point(); + BOOST_ASSERT(exit_turn); + return exit_turn->point; + } + + TurnInfo const& get_exit_turn() const + { + BOOST_ASSERT(exit_operation != overlay::operation_none); + BOOST_ASSERT(exit_turn); + return *exit_turn; } void reset_detected_exit() @@ -267,10 +292,32 @@ public: private: overlay::operation_type exit_operation; - point_info exit_id; + const TurnInfo * exit_turn; std::vector other_entry_points; // TODO: use map here or sorted vector? }; +template +inline bool turn_on_the_same_ip(Turn const& prev_turn, Turn const& curr_turn) +{ + segment_identifier const& prev_seg_id = prev_turn.operations[OpId].seg_id; + segment_identifier const& curr_seg_id = curr_turn.operations[OpId].seg_id; + + if ( prev_seg_id.multi_index != curr_seg_id.multi_index + || prev_seg_id.ring_index != curr_seg_id.ring_index ) + { + return false; + } + + if ( prev_seg_id.segment_index != curr_seg_id.segment_index + && ( ! geometry::math::equals(curr_turn.operations[OpId].enriched.distance, 0) + || prev_seg_id.segment_index + 1 != curr_seg_id.segment_index ) ) + { + return false; + } + + return detail::equals::equals_point_point(prev_turn.point, curr_turn.point); +} + template diff --git a/include/boost/geometry/algorithms/detail/relate/linear_areal.hpp b/include/boost/geometry/algorithms/detail/relate/linear_areal.hpp index 948f5eb96..59de09059 100644 --- a/include/boost/geometry/algorithms/detail/relate/linear_areal.hpp +++ b/include/boost/geometry/algorithms/detail/relate/linear_areal.hpp @@ -442,7 +442,7 @@ struct linear_areal { // 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()) ) + if ( ! turn_on_the_same_ip(m_exit_watcher.get_exit_turn(), *it) ) { m_exit_watcher.reset_detected_exit(); @@ -480,7 +480,7 @@ struct linear_areal if ( m_interior_detected ) { // real interior overlap - if ( !detail::equals::equals_point_point(it->point, m_previous_turn_ptr->point) ) + if ( ! turn_on_the_same_ip(*m_previous_turn_ptr, *it) ) { update(res); m_interior_detected = false; @@ -522,7 +522,7 @@ struct linear_areal || op == overlay::operation_continue ) // operation_boundary/operation_boundary_intersection { bool no_enters_detected = m_exit_watcher.is_outside(); - m_exit_watcher.enter(it->point, other_id); + m_exit_watcher.enter(*it); if ( op == overlay::operation_intersection ) { @@ -664,7 +664,7 @@ struct linear_areal update(res); // notify the exit_watcher that we started inside - m_exit_watcher.enter(it->point, other_id); + m_exit_watcher.enter(*it); } else { @@ -695,7 +695,7 @@ struct linear_areal || it->operations[op_id].is_collinear ) { // notify the exit watcher about the possible exit - m_exit_watcher.exit(it->point, other_id, op); + m_exit_watcher.exit(*it); } } @@ -892,7 +892,7 @@ struct linear_areal } private: - exit_watcher m_exit_watcher; + exit_watcher m_exit_watcher; segment_watcher m_seg_watcher; TurnInfo * m_previous_turn_ptr; overlay::operation_type m_previous_operation; diff --git a/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp b/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp index ba9e0f137..a485845e4 100644 --- a/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp +++ b/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp @@ -245,7 +245,7 @@ struct linear_linear // x, u, i, c std::sort(turns.begin(), turns.end(), turns::less_seg_dist_op<0,2,3,1,4,0,0>()); - turns_analyser<0, turn_type> analyser; + turns_analyser analyser; analyse_each_turn(result, analyser, turns.begin(), turns.end(), geometry1, geometry2, @@ -259,7 +259,7 @@ struct linear_linear // x, u, i, c std::sort(turns.begin(), turns.end(), turns::less_seg_dist_op<0,2,3,1,4,0,1>()); - turns_analyser<1, turn_type> analyser; + turns_analyser analyser; analyse_each_turn(result, analyser, turns.begin(), turns.end(), geometry2, geometry1, @@ -311,7 +311,7 @@ struct linear_linear }; // This analyser should be used like Input or SinglePass Iterator - template + template class turns_analyser { typedef typename TurnInfo::point_type turn_point_type; @@ -361,7 +361,7 @@ struct linear_linear { // 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()) ) + if ( ! turn_on_the_same_ip(m_exit_watcher.get_exit_turn(), *it) ) { m_exit_watcher.reset_detected_exit(); @@ -413,7 +413,7 @@ struct linear_linear if ( op == overlay::operation_intersection ) { bool was_outside = m_exit_watcher.is_outside(); - m_exit_watcher.enter(it->point, other_id); + m_exit_watcher.enter(*it); // interiors overlaps update(res); @@ -479,7 +479,7 @@ struct linear_linear // to exit we must be currently inside and the current segment must be collinear if ( !was_outside && is_collinear ) { - m_exit_watcher.exit(it->point, other_id, op); + m_exit_watcher.exit(*it); } bool op_blocked = op == overlay::operation_blocked; @@ -638,7 +638,7 @@ struct linear_linear } private: - exit_watcher m_exit_watcher; + exit_watcher m_exit_watcher; segment_watcher m_seg_watcher; TurnInfo * m_previous_turn_ptr; overlay::operation_type m_previous_operation; diff --git a/test/algorithms/relate.cpp b/test/algorithms/relate.cpp index b9e0d2565..4be48128f 100644 --- a/test/algorithms/relate.cpp +++ b/test/algorithms/relate.cpp @@ -40,7 +40,7 @@ #include //TEST -//#include +#include namespace bgdr = bg::detail::relate; @@ -318,6 +318,10 @@ void test_linestring_linestring() test_geometry("LINESTRING(1 0,5 0,7 0,8 1)", "LINESTRING(0 0,10 0,10 10,4 -1)", "1F10F0102"); + // self-IP going out and in on the same point + test_geometry("LINESTRING(2 0,5 0,5 5,6 5,5 0,8 0)", "LINESTRING(1 0,9 0)", + "1F10FF102"); + // duplicated points test_geometry("LINESTRING(1 1, 2 2, 2 2)", "LINESTRING(0 0, 2 2, 4 2)", "1FF0FF102"); test_geometry("LINESTRING(1 1, 1 1, 2 2)", "LINESTRING(0 0, 2 2, 4 2)", "1FF0FF102"); @@ -530,6 +534,13 @@ void test_linestring_polygon() "POLYGON((0 0,0 10,10 10,10 0,0 0),(2 2,5 5,2 8,2 2))", "F1FFFF212"); + // self-IP going on the boundary then into the exterior and to the boundary again + test_geometry("LINESTRING(2 10,5 10,5 15,6 15,5 10,8 10)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", + "F11F0F212"); + // self-IP going on the boundary then into the interior and to the boundary again + test_geometry("LINESTRING(2 10,5 10,5 5,6 5,5 10,8 10)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", + "11FF0F212"); + // ccw { typedef bg::model::polygon ccwpoly; @@ -666,6 +677,30 @@ void polygon_polygon() test_geometry("POLYGON((0 0,0 10,10 10,10 0,0 0))", "POLYGON((0 20,0 30,10 30,10 20,0 20))", "FF2FF1212"); + + // NON-SIMPLE + + // equal non-simple / hole + test_geometry("POLYGON((0 0,0 10,10 10,10 0,0 0),(5 5,10 5,5 6,5 5))", + "POLYGON((0 0,0 10,10 10,10 5,5 6,5 5,10 5,10 0,0 0))", + "2FFF1FFF2"); + to_svg("POLYGON((0 0,0 10,10 10,10 0,0 0),(5 5,10 5,5 6,5 5))", + "POLYGON((0 0,0 10,10 10,10 5,5 6,5 5,10 5,10 0,0 0))", + "a.svg"); + + to_svg("POLYGON((0 0,0 10,10 10,10 0,0 0),(5 5,10 5,5 6,5 5))", + "POLYGON((0 0,5 5,10 5,10 0,0 0))", + "b1.svg"); + to_svg("POLYGON((0 0,5 5,10 5,10 0,0 0))", + "POLYGON((0 0,0 10,10 10,10 0,0 0),(5 5,10 5,5 6,5 5))", + "b2.svg"); + + to_svg("POLYGON((0 0,0 10,10 10,10 0,0 0))", + "POLYGON((0 0,0 10,10 10,10 5,5 6,5 5,10 5,10 0,0 0))", + "c1.svg"); + to_svg("POLYGON((0 0,0 10,10 10,10 5,5 6,5 5,10 5,10 0,0 0))", + "POLYGON((0 0,0 10,10 10,10 0,0 0))", + "c2.svg"); } template