diff --git a/include/boost/geometry/algorithms/detail/relate/areal_areal.hpp b/include/boost/geometry/algorithms/detail/relate/areal_areal.hpp index f61f952c9..025532d3a 100644 --- a/include/boost/geometry/algorithms/detail/relate/areal_areal.hpp +++ b/include/boost/geometry/algorithms/detail/relate/areal_areal.hpp @@ -41,10 +41,25 @@ class no_turns_aa_pred { public: no_turns_aa_pred(OtherAreal const& other_areal, Result & res) - : m_result_ptr(boost::addressof(res)) - , m_other_areal_ptr(boost::addressof(other_areal)) + : m_result(res) + , m_other_areal(other_areal) , m_flags(0) - {} + { + // check which relations must be analysed + + if ( ! may_update(m_result) + && ! may_update(m_result) + && ! may_update(m_result) ) + { + m_flags |= 1; + } + + if ( ! may_update(m_result) + && ! may_update(m_result) ) + { + m_flags |= 2; + } + } template bool operator()(Areal const& areal) @@ -55,27 +70,35 @@ public: // TODO: for now ignore, later throw an exception? if ( !ok ) + { return true; + } + + // if those flags are set nothing will change + if ( m_flags == 3 ) + { + return false; + } // check if the areal is inside the other_areal // TODO: This is O(N) // Run in a loop O(NM) - optimize! - int pig = detail::within::point_in_geometry(pt, *m_other_areal_ptr); + int pig = detail::within::point_in_geometry(pt, m_other_areal); //BOOST_ASSERT( pig != 0 ); // inside if ( pig > 0 ) { - update(*m_result_ptr); - update(*m_result_ptr); - update(*m_result_ptr); + update(m_result); + update(m_result); + update(m_result); m_flags |= 1; } // outside else { - update(*m_result_ptr); - update(*m_result_ptr); + update(m_result); + update(m_result); m_flags |= 2; // If the exterior ring is outside, interior rings must be checked @@ -94,25 +117,25 @@ public: // TODO: O(N) // Optimize! - int pig = detail::within::point_in_geometry(range::front(range_ref), *m_other_areal_ptr); + int pig = detail::within::point_in_geometry(range::front(range_ref), m_other_areal); // hole inside if ( pig > 0 ) { - update(*m_result_ptr); - update(*m_result_ptr); - update(*m_result_ptr); + update(m_result); + update(m_result); + update(m_result); m_flags |= 1; } } } - return m_flags != 3 && !m_result_ptr->interrupt; + return m_flags != 3 && !m_result.interrupt; } private: - Result * m_result_ptr; - const OtherAreal * m_other_areal_ptr; + Result & m_result; + OtherAreal const& m_other_areal; int m_flags; }; @@ -164,42 +187,86 @@ struct areal_areal if ( turns.empty() ) return; + if ( may_update(result) + || may_update(result) + || may_update(result) + || may_update(result) + || may_update(result) ) { + // sort turns typedef turns::less<0, turns::less_op_areal_areal> less; std::sort(turns.begin(), turns.end(), less()); - turns_analyser analyser; - analyse_each_turn(result, analyser, - turns.begin(), turns.end(), - geometry1, geometry2); + /*if ( may_update(result) + || may_update(result) + || may_update(result) + || may_update(result) )*/ + { + // analyse sorted turns + turns_analyser analyser; + analyse_each_turn(result, analyser, + turns.begin(), turns.end(), + geometry1, geometry2); - if ( result.interrupt ) - return; + if ( result.interrupt ) + return; + } - uncertain_rings_analyser<0, Result, Geometry1, Geometry2> rings_analyser(result, geometry1, geometry2); - analyse_uncertain_rings<0>::apply(rings_analyser, turns.begin(), turns.end()); + if ( may_update(result) + || may_update(result) + || may_update(result) + || may_update(result) + || may_update(result) ) + { + // analyse rings for which turns were not generated + // or only i/i or u/u was generated + uncertain_rings_analyser<0, Result, Geometry1, Geometry2> rings_analyser(result, geometry1, geometry2); + analyse_uncertain_rings<0>::apply(rings_analyser, turns.begin(), turns.end()); - if ( result.interrupt ) - return; + if ( result.interrupt ) + return; + } } + if ( may_update(result) + || may_update(result) + || may_update(result) + || may_update(result) + || may_update(result) ) { + // sort turns typedef turns::less<1, turns::less_op_areal_areal> less; std::sort(turns.begin(), turns.end(), less()); - turns_analyser analyser; - analyse_each_turn(result, analyser, - turns.begin(), turns.end(), - geometry2, geometry1); + /*if ( may_update(result) + || may_update(result) + || may_update(result) + || may_update(result) )*/ + { + // analyse sorted turns + turns_analyser analyser; + analyse_each_turn(result, analyser, + turns.begin(), turns.end(), + geometry2, geometry1); - if ( result.interrupt ) - return; + if ( result.interrupt ) + return; + } - uncertain_rings_analyser<1, Result, Geometry2, Geometry1> rings_analyser(result, geometry2, geometry1); - analyse_uncertain_rings<1>::apply(rings_analyser, turns.begin(), turns.end()); + if ( may_update(result) + || may_update(result) + || may_update(result) + || may_update(result) + || may_update(result) ) + { + // analyse rings for which turns were not generated + // or only i/i or u/u was generated + uncertain_rings_analyser<1, Result, Geometry2, Geometry1> rings_analyser(result, geometry2, geometry1); + analyse_uncertain_rings<1>::apply(rings_analyser, turns.begin(), turns.end()); - //if ( result.interrupt ) - // return; + //if ( result.interrupt ) + // return; + } } } @@ -219,8 +286,6 @@ struct areal_areal , m_geometry2(geometry2) {} -// TODO: since we update result for some operations here, we may not do it in the analyser! - template inline bool apply(Range const& turns) { @@ -498,10 +563,25 @@ struct areal_areal , m_result(result) , m_flags(0) { - // TODO: initialize flags with the result + // check which relations must be analysed -// TODO: WOULD IT BE POSSIBLE TO DISABLE SOME PARTS OF ANALYSIS DEPENDING ON WHAT IS THE STATE OF THE RESULT? -// E.G. IF I^I IS SET TO SOME MAX IT'S NOT REQUIRED TO ANALYSE THINGS THAT MAY SET THE I^I + if ( ! may_update(m_result) + && ! may_update(m_result) ) + { + m_flags |= 1; + } + + if ( ! may_update(m_result) + && ! may_update(m_result) ) + { + m_flags |= 2; + } + + if ( ! may_update(m_result) + && ! may_update(m_result) ) + { + m_flags |= 4; + } } inline void no_turns(segment_identifier const& seg_id) @@ -619,110 +699,6 @@ struct areal_areal interrupt = m_flags == 7 || m_result.interrupt; // interrupt if the result won't be changed in the future } -// template -// inline bool turns(TurnIt first, TurnIt last) -// { -// std::set other_multi_indexes_ii; -// bool found_uu = false; -// -// for ( TurnIt it = first ; it != last ; ++it ) -// { -// if ( it->operations[0].operation == overlay::operation_intersection -// && it->operations[1].operation == overlay::operation_intersection ) -// { -// // ignore exterior ring -// if ( it->operations[OpId].seg_id.ring_index >= 0 ) -// { -// other_multi_indexes_ii.insert(it->operations[other_id].seg_id.multi_index); -// } -// } -// else if ( it->operations[0].operation == overlay::operation_union -// && it->operations[1].operation == overlay::operation_union ) -// { -// // ignore if u/u is for holes -// //if ( it->operations[OpId].seg_id.ring_index >= 0 -// // && it->operations[other_id].seg_id.ring_index >= 0 ) -// { -// found_uu = true; -// } -// } -// else // ignore -// { -// return true; // don't interrupt -// } -// } -// -// // If we're here there was no other than i/i or u/u turns generated -// // i/i generated for holes of current geometry are stored only -// -// // for each i/i other index test if there is no hole inside current ring -// if ( !other_multi_indexes_ii.empty() ) -// { -// reversible_type rev_view(detail::sub_range(geometry, first->operations[OpId].seg_id)); -// closeable_type ring_view(rev_view); -// -// // NOTE that it doesn't matter if the tested hole is partially contained -// for ( std::set::iterator it = other_multi_indexes_ii.begin() ; -// it != other_multi_indexes_ii.end() ; ++it ) -// { -// ring_identifier other_ring_id(other_id, multi_index, 0); -// -// typename detail::single_geometry_return_type::type -// other_single_ref = detail::single_geometry(other_geometry, other_ring_id); -// -// bool found_inside = false; -// -// // for each interior ring of other geometry -// for ( ; other_ring_id.ring_index < geometry::num_interior_rings(other_single_ref) ; -// ++other_ring_id.ring_index ) -// { -//// TODO: not very optimal since single geometry must be indexed for each ring -// typename detail::sub_range_return_type::type -// other_range_ref = detail::sub_range(other_geometry, other_ring_id); -// -// if ( boost::empty(other_range_ref) ) -// { -// continue; -// } -// -// typename point_type::type const& -// pt = range::front(other_range_ref); -// -// int pig = detail::within::point_in_range(pt, ring_view); -// -// if ( pig > 0 ) -// { -// found_inside = true; -// break; -// } -// } -// -// // nothing is inside the hole, ... WRONG! -// if ( !found_inside ) -// { -// update(m_result); -// update(m_result); -// } -// } -// } -// -// // if u/u were found this means that only u/u were generated for this ring -// if ( found_uu ) -// { -// update(m_result); -// update(m_result); -// m_flags |= 2; -// -// // not necessary since this will be checked in the next iteration -// // but increases the pruning strength -// // NOTE that this is not reflected in flags on purpose -// update(m_result); -// update(m_result); -// } -// -// return m_flags != 3; // interrupt if the result won't be changed in the future -// } - Geometry const& geometry; OtherGeometry const& other_geometry; bool interrupt; diff --git a/include/boost/geometry/algorithms/detail/relate/result.hpp b/include/boost/geometry/algorithms/detail/relate/result.hpp index f32e7db7e..7bb52f615 100644 --- a/include/boost/geometry/algorithms/detail/relate/result.hpp +++ b/include/boost/geometry/algorithms/detail/relate/result.hpp @@ -35,6 +35,8 @@ namespace detail { namespace relate { enum field { interior = 0, boundary = 1, exterior = 2 }; +// matrix + // TODO add height? template @@ -119,6 +121,8 @@ private: struct matrix9 {}; //struct matrix4 {}; +// matrix_width + template struct matrix_width : not_implemented @@ -130,6 +134,8 @@ struct matrix_width static const std::size_t value = 3; }; +// matrix_handler + template class matrix_handler : private matrix::value> @@ -150,6 +156,21 @@ public: this->data() + base_t::size); } + template + inline bool may_update() const + { + BOOST_STATIC_ASSERT('0' <= D && D <= '9'); + + char const c = static_cast(*this).template get(); + return D > c || c > '9'; + } + + //template + //inline char get() const + //{ + // return static_cast(*this).template get(); + //} + template inline void set() { @@ -165,6 +186,8 @@ public: // RUN-TIME MASKS +// mask9 + class mask9 { public: @@ -187,6 +210,8 @@ private: char m_mask[9]; }; +// interrupt() + template struct interrupt_dispatch { @@ -277,6 +302,94 @@ inline bool interrupt(Mask const& mask) ::template apply(mask); } +// may_update() + +template +struct may_update_dispatch +{ + template + static inline bool apply(Mask const& mask, Matrix const& matrix) + { + BOOST_STATIC_ASSERT('0' <= D && D <= '9'); + + char const m = mask.template get(); + + if ( m == 'F' ) + { + return true; + } + else if ( m == 'T' ) + { + char const c = matrix.template get(); + return c == 'F'; // if it's T or between 0 and 9, the result will be the same + } + else if ( m >= '0' && m <= '9' ) + { + char const c = matrix.template get(); + return D > c || c > '9'; + } + + return false; + } +}; + +template ::value> +struct may_update_dispatch_tuple +{ + template + static inline bool apply(Masks const& masks, Matrix const& matrix) + { + typedef typename boost::tuples::element::type mask_type; + mask_type const& mask = boost::get(masks); + return may_update_dispatch::template apply(mask, matrix) + || may_update_dispatch_tuple::template apply(masks, matrix); + } +}; + +template +struct may_update_dispatch_tuple +{ + template + static inline bool apply(Masks const& , Matrix const& ) + { + return false; + } +}; + +template +struct may_update_dispatch< boost::tuple > +{ + typedef boost::tuple mask_type; + + template + static inline bool apply(mask_type const& mask, Matrix const& matrix) + { + return may_update_dispatch_tuple::template apply(mask, matrix); + } +}; + +template +struct may_update_dispatch< boost::tuples::cons > +{ + typedef boost::tuples::cons mask_type; + + template + static inline bool apply(mask_type const& mask, Matrix const& matrix) + { + return may_update_dispatch_tuple::template apply(mask, matrix); + } +}; + +template +inline bool may_update(Mask const& mask, Matrix const& matrix) +{ + return may_update_dispatch + ::template apply(mask, matrix); +} + +// check() + template struct check_dispatch { @@ -371,6 +484,8 @@ inline bool check(Mask const& mask, Matrix const& matrix) return check_dispatch::apply(mask, matrix); } +// matrix_width + template <> struct matrix_width { @@ -404,6 +519,8 @@ struct matrix_width< boost::tuples::cons > value = matrix_width_tuple< boost::tuples::cons >::value; }; +// matrix_handler + template class mask_handler : private matrix::value> @@ -426,6 +543,20 @@ public: && check(m_mask, static_cast(*this)); } + template + inline bool may_update() const + { + return detail::relate::may_update( + m_mask, static_cast(*this) + ); + } + + //template + //inline char get() const + //{ + // return static_cast(*this).template get(); + //} + template inline void set() { @@ -456,7 +587,9 @@ private: Mask const& m_mask; }; -// STATIC MASK +// STATIC MASKS + +// static_mask template struct static_should_handle_element { @@ -488,14 +623,16 @@ struct static_should_handle_element || ( mask_el >= '0' && mask_el <= '9' ); }; -template +// static_interrupt + +template struct static_interrupt_dispatch { static const bool value = false; }; -template -struct static_interrupt_dispatch +template +struct static_interrupt_dispatch { static const char mask_el = StaticMask::template get::value; @@ -516,7 +653,7 @@ struct static_interrupt_sequence StaticMask, V, F1, F2, true, - !boost::mpl::is_sequence::value + boost::mpl::is_sequence::value >::value && static_interrupt_sequence < @@ -533,7 +670,7 @@ struct static_interrupt_sequence }; template -struct static_interrupt_dispatch +struct static_interrupt_dispatch { static const bool value = static_interrupt_sequence @@ -553,11 +690,122 @@ struct static_interrupt StaticMask, V, F1, F2, EnableInterrupt, - !boost::mpl::is_sequence::value + boost::mpl::is_sequence::value >::value; }; -template +// static_may_update + +template +struct static_may_update_dispatch +{ + static const char mask_el = StaticMask::template get::value; + static const int version + = mask_el == 'F' ? 0 + : mask_el == 'T' ? 1 + : mask_el >= '0' && mask_el <= '9' ? 2 + : 3; + + template + static inline bool apply(Matrix const& matrix) + { + return apply_dispatch(matrix, integral_constant()); + } + + // mask_el == 'F' + template + static inline bool apply_dispatch(Matrix const& , integral_constant) + { + return true; + } + // mask_el == 'T' + template + static inline bool apply_dispatch(Matrix const& matrix, integral_constant) + { + char const c = matrix.template get(); + return c == 'F'; // if it's T or between 0 and 9, the result will be the same + } + // mask_el >= '0' && mask_el <= '9' + template + static inline bool apply_dispatch(Matrix const& matrix, integral_constant) + { + char const c = matrix.template get(); + return D > c || c > '9'; + } + // else + template + static inline bool apply_dispatch(Matrix const&, integral_constant) + { + return false; + } +}; + +template +struct static_may_update_sequence +{ + typedef typename boost::mpl::deref::type StaticMask; + + template + static inline bool apply(Matrix const& matrix) + { + return static_may_update_dispatch + < + StaticMask, + D, F1, F2, + boost::mpl::is_sequence::value + >::apply(matrix) + || static_may_update_sequence + < + typename boost::mpl::next::type, + Last, + D, F1, F2 + >::apply(matrix); + } +}; + +template +struct static_may_update_sequence +{ + template + static inline bool apply(Matrix const& matrix) + { + return false; + } +}; + +template +struct static_may_update_dispatch +{ + template + static inline bool apply(Matrix const& matrix) + { + return static_may_update_sequence + < + typename boost::mpl::begin::type, + typename boost::mpl::end::type, + D, F1, F2 + >::apply(matrix); + } +}; + +template +struct static_may_update +{ + template + static inline bool apply(Matrix const& matrix) + { + return static_may_update_dispatch + < + StaticMask, + D, F1, F2, + boost::mpl::is_sequence::value + >::apply(matrix); + } +}; + +// static_check + +template struct static_check_dispatch { template @@ -625,7 +873,7 @@ struct static_check_sequence return static_check_dispatch < StaticMask, - !boost::mpl::is_sequence::value + boost::mpl::is_sequence::value >::apply(matrix) || static_check_sequence < @@ -646,7 +894,7 @@ struct static_check_sequence }; template -struct static_check_dispatch +struct static_check_dispatch { template static inline bool apply(Matrix const& matrix) @@ -668,11 +916,13 @@ struct static_check return static_check_dispatch < StaticMask, - !boost::mpl::is_sequence::value + boost::mpl::is_sequence::value >::apply(matrix); } }; +// static_mask_handler + template class static_mask_handler : private matrix<3> @@ -691,9 +941,29 @@ public: result_type result() const { return (!Interrupt || !interrupt) - && static_check::apply(static_cast(*this)); + && static_check:: + apply(static_cast(*this)); } + template + inline bool may_update() const + { + return static_may_update:: + apply(static_cast(*this)); + } + + template + static inline bool expects() + { + return static_should_handle_element::value; + } + + //template + //inline char get() const + //{ + // return base_t::template get(); + //} + template inline void set() { @@ -987,6 +1257,38 @@ inline void update(Result & res) update_result_dispatch::apply(res); } +template +inline bool may_update(Result const& res) +{ + return res.template may_update(); +} + +template +struct may_update_result_dispatch +{ + template + static inline bool apply(Result const& res) + { + return may_update(res); + } +}; + +template +struct may_update_result_dispatch +{ + template + static inline bool apply(Result const& res) + { + return may_update(res); + } +}; + +template +inline bool may_update(Result const& res) +{ + return may_update_result_dispatch::apply(res); +} + template inline Result return_result() { diff --git a/test/algorithms/relate.cpp b/test/algorithms/relate.cpp index 2c317065b..74fb18b63 100644 --- a/test/algorithms/relate.cpp +++ b/test/algorithms/relate.cpp @@ -823,6 +823,27 @@ void polygon_polygon() test_geometry("POLYGON((0 0,0 10,4 10,6 8,5 5,6 2,4 0,0 0),(5 5,2 6,2 4,5 5))", "POLYGON((5 5,4 8,6 10,10 10,10 0,6 0,4 2,5 5))", "212101212"); + + { + test_geometry("POLYGON((0 0,0 10,10 10,10 0,0 0))", + "POLYGON((5 5,5 10,6 10,6 5,5 5))", + "212F11FF2"); + + test_geometry("POLYGON((0 0,0 10,10 10,10 0,0 0))", + "POLYGON((10 0,10 10,20 10,20 0,10 0))", + "FF2F11212"); + + namespace bgdr = bg::detail::relate; + poly p1, p2, p3; + bg::read_wkt("POLYGON((0 0,0 10,10 10,10 0,0 0))", p1); + bg::read_wkt("POLYGON((10 0,10 10,20 10,20 0,10 0))", p2); + bg::read_wkt("POLYGON((5 5,5 10,6 10,6 5,5 5))", p3); + BOOST_CHECK(bgdr::relate(p1, p2, bgdr::mask9("FT*******") + || bgdr::mask9("F**T*****") + || bgdr::mask9("F***T****"))); // touches() + BOOST_CHECK(bgdr::relate(p1, p3, bgdr::mask9("T*****FF*"))); // contains() + BOOST_CHECK(bgdr::relate(p2, p3, bgdr::mask9("FF*FF****"))); // disjoint() + } } template