diff options
author | Chanho Park <chanho61.park@samsung.com> | 2014-12-11 18:55:56 +0900 |
---|---|---|
committer | Chanho Park <chanho61.park@samsung.com> | 2014-12-11 18:55:56 +0900 |
commit | 08c1e93fa36a49f49325a07fe91ff92c964c2b6c (patch) | |
tree | 7a7053ceb8874b28ec4b868d4c49b500008a102e /boost/geometry/algorithms/detail/overlay | |
parent | bb4dd8289b351fae6b55e303f189127a394a1edd (diff) | |
download | boost-08c1e93fa36a49f49325a07fe91ff92c964c2b6c.tar.gz boost-08c1e93fa36a49f49325a07fe91ff92c964c2b6c.tar.bz2 boost-08c1e93fa36a49f49325a07fe91ff92c964c2b6c.zip |
Imported Upstream version 1.57.0upstream/1.57.0
Diffstat (limited to 'boost/geometry/algorithms/detail/overlay')
38 files changed, 6192 insertions, 1448 deletions
diff --git a/boost/geometry/algorithms/detail/overlay/add_rings.hpp b/boost/geometry/algorithms/detail/overlay/add_rings.hpp index 74595fedd0..5ff0b57d6e 100644 --- a/boost/geometry/algorithms/detail/overlay/add_rings.hpp +++ b/boost/geometry/algorithms/detail/overlay/add_rings.hpp @@ -75,15 +75,15 @@ inline OutputIterator add_rings(SelectionMap const& map, OutputIterator out) { typedef typename SelectionMap::const_iterator iterator; - typedef typename SelectionMap::mapped_type property_type; - typedef typename property_type::area_type area_type; - - area_type const zero = 0; - std::size_t const min_num_points = core_detail::closure::minimum_ring_size - < - geometry::closure - < - typename boost::range_value + typedef typename SelectionMap::mapped_type property_type; + typedef typename property_type::area_type area_type; + + area_type const zero = 0; + std::size_t const min_num_points = core_detail::closure::minimum_ring_size + < + geometry::closure + < + typename boost::range_value < RingCollection const >::type @@ -117,15 +117,14 @@ inline OutputIterator add_rings(SelectionMap const& map, } } - // Only add rings if they satisfy minimal requirements. - // This cannot be done earlier (during traversal), not - // everything is figured out yet (sum of positive/negative rings) - // TODO: individual rings can still contain less than 3 points. - if (geometry::num_points(result) >= min_num_points - && math::larger(geometry::area(result), zero)) - { - *out++ = result; - } + // Only add rings if they satisfy minimal requirements. + // This cannot be done earlier (during traversal), not + // everything is figured out yet (sum of positive/negative rings) + if (geometry::num_points(result) >= min_num_points + && math::larger(geometry::area(result), zero)) + { + *out++ = result; + } } } return out; diff --git a/boost/geometry/algorithms/detail/overlay/append_no_duplicates.hpp b/boost/geometry/algorithms/detail/overlay/append_no_duplicates.hpp index 2c0f88e2aa..0fd1fe4de9 100644 --- a/boost/geometry/algorithms/detail/overlay/append_no_duplicates.hpp +++ b/boost/geometry/algorithms/detail/overlay/append_no_duplicates.hpp @@ -13,7 +13,7 @@ #include <boost/range.hpp> #include <boost/geometry/algorithms/append.hpp> -#include <boost/geometry/algorithms/detail/disjoint.hpp> +#include <boost/geometry/algorithms/detail/equals/point_point.hpp> @@ -29,7 +29,7 @@ template <typename Range, typename Point> inline void append_no_duplicates(Range& range, Point const& point, bool force = false) { if (boost::size(range) == 0 - || force + || force || ! geometry::detail::equals::equals_point_point(*(boost::end(range)-1), point)) { #ifdef BOOST_GEOMETRY_DEBUG_INTERSECTION diff --git a/boost/geometry/algorithms/detail/overlay/append_no_dups_or_spikes.hpp b/boost/geometry/algorithms/detail/overlay/append_no_dups_or_spikes.hpp new file mode 100644 index 0000000000..d44db17ad3 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/append_no_dups_or_spikes.hpp @@ -0,0 +1,160 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// This file was modified by Oracle on 2014. +// Modifications copyright (c) 2014 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// 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) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_APPEND_NO_DUPS_OR_SPIKES_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_APPEND_NO_DUPS_OR_SPIKES_HPP + +#include <boost/range.hpp> + +#include <boost/geometry/algorithms/append.hpp> +#include <boost/geometry/algorithms/detail/point_is_spike_or_equal.hpp> +#include <boost/geometry/algorithms/detail/equals/point_point.hpp> + +#include <boost/geometry/util/range.hpp> + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + +// TODO: move this / rename this +template <typename Point1, typename Point2, typename RobustPolicy> +inline bool points_equal_or_close(Point1 const& point1, + Point2 const& point2, + RobustPolicy const& robust_policy) +{ + if (detail::equals::equals_point_point(point1, point2)) + { + return true; + } + + if (! RobustPolicy::enabled) + { + return false; + } + + // Try using specified robust policy + typedef typename geometry::robust_point_type + < + Point1, + RobustPolicy + >::type robust_point_type; + + robust_point_type point1_rob, point2_rob; + geometry::recalculate(point1_rob, point1, robust_policy); + geometry::recalculate(point2_rob, point2, robust_policy); + + return detail::equals::equals_point_point(point1_rob, point2_rob); +} + + +template <typename Range, typename Point, typename RobustPolicy> +inline void append_no_dups_or_spikes(Range& range, Point const& point, + RobustPolicy const& robust_policy) +{ +#ifdef BOOST_GEOMETRY_DEBUG_INTERSECTION + std::cout << " add: (" + << geometry::get<0>(point) << ", " << geometry::get<1>(point) << ")" + << std::endl; +#endif + // The code below thies condition checks all spikes/dups + // for geometries >= 3 points. + // So we have to check the first potential duplicate differently + if (boost::size(range) == 1 + && points_equal_or_close(*(boost::begin(range)), point, robust_policy)) + { + return; + } + + traits::push_back<Range>::apply(range, point); + + // If a point is equal, or forming a spike, remove the pen-ultimate point + // because this one caused the spike. + // If so, the now-new-pen-ultimate point can again cause a spike + // (possibly at a corner). So keep doing this. + // Besides spikes it will also avoid adding duplicates. + while(boost::size(range) >= 3 + && point_is_spike_or_equal(point, + *(boost::end(range) - 3), + *(boost::end(range) - 2), + robust_policy)) + { + // Use the Concept/traits, so resize and append again + traits::resize<Range>::apply(range, boost::size(range) - 2); + traits::push_back<Range>::apply(range, point); + } +} + +template <typename Range, typename RobustPolicy> +inline void clean_closing_dups_and_spikes(Range& range, + RobustPolicy const& robust_policy) +{ + std::size_t const minsize + = core_detail::closure::minimum_ring_size + < + geometry::closure<Range>::value + >::value; + + if (boost::size(range) <= minsize) + { + return; + } + + typedef typename boost::range_iterator<Range>::type iterator_type; + static bool const closed = geometry::closure<Range>::value == geometry::closed; + +// TODO: the following algorithm could be rewritten to first look for spikes +// and then erase some number of points from the beginning of the Range + + bool found = false; + do + { + found = false; + iterator_type first = boost::begin(range); + iterator_type second = first + 1; + iterator_type ultimate = boost::end(range) - 1; + if (closed) + { + ultimate--; + } + + // Check if closing point is a spike (this is so if the second point is + // considered as a spike w.r.t. the last segment) + if (point_is_spike_or_equal(*second, *ultimate, *first, robust_policy)) + { + range::erase(range, first); + if (closed) + { + // Remove closing last point + range::resize(range, boost::size(range) - 1); + // Add new closing point + range::push_back(range, range::front(range)); + } + found = true; + } + } while(found && boost::size(range) > minsize); +} + + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_APPEND_NO_DUPS_OR_SPIKES_HPP diff --git a/boost/geometry/algorithms/detail/overlay/assign_parents.hpp b/boost/geometry/algorithms/detail/overlay/assign_parents.hpp index 5063f49eb4..67b48cc471 100644 --- a/boost/geometry/algorithms/detail/overlay/assign_parents.hpp +++ b/boost/geometry/algorithms/detail/overlay/assign_parents.hpp @@ -18,6 +18,10 @@ #include <boost/geometry/geometries/box.hpp> +#ifdef BOOST_GEOMETRY_TIME_OVERLAY +# include <boost/timer.hpp> +#endif + namespace boost { namespace geometry { @@ -123,30 +127,30 @@ struct assign_visitor template <typename Item> inline void apply(Item const& outer, Item const& inner, bool first = true) { - if (first && outer.real_area < 0) + if (first && outer.abs_area < inner.abs_area) { - // Reverse arguments + // Apply with reversed arguments apply(inner, outer, false); return; } - if (math::larger(outer.real_area, 0)) + if (m_check_for_orientation + || (math::larger(outer.real_area, 0) + && math::smaller(inner.real_area, 0))) { - if (inner.real_area < 0 || m_check_for_orientation) - { - ring_info_type& inner_in_map = m_ring_map[inner.id]; + ring_info_type& inner_in_map = m_ring_map[inner.id]; - if (geometry::within(inner_in_map.point, outer.envelope) - && within_selected_input(inner_in_map, outer.id, m_geometry1, m_geometry2, m_collection) - ) + if (geometry::within(inner_in_map.point, outer.envelope) + && within_selected_input(inner_in_map, outer.id, m_geometry1, m_geometry2, m_collection) + ) + { + // Assign a parent if there was no earlier parent, or the newly + // found parent is smaller than the previous one + if (inner_in_map.parent.source_index == -1 + || outer.abs_area < inner_in_map.parent_area) { - // Only assign parent if that parent is smaller (or if it is the first) - if (inner_in_map.parent.source_index == -1 - || outer.abs_area < inner_in_map.parent_area) - { - inner_in_map.parent = outer.id; - inner_in_map.parent_area = outer.abs_area; - } + inner_in_map.parent = outer.id; + inner_in_map.parent_area = outer.abs_area; } } } @@ -243,7 +247,7 @@ inline void assign_parents(Geometry1 const& geometry1, // a dramatic improvement (factor 5 for star_comb testcase) ring_identifier id_of_positive = vector[index_positive].id; ring_info_type& outer = ring_map[id_of_positive]; - std::size_t index = 0; + index = 0; for (vector_iterator_type it = boost::begin(vector); it != boost::end(vector); ++it, ++index) { @@ -284,13 +288,21 @@ inline void assign_parents(Geometry1 const& geometry1, { it->second.discarded = true; } - else if (it->second.parent.source_index >= 0 && it->second.get_area() > 0) + else if (it->second.parent.source_index >= 0 + && math::larger(it->second.get_area(), 0)) { - // Discard positive inner ring with parent - it->second.discarded = true; + const ring_info_type& parent = ring_map[it->second.parent]; + + if (math::larger(parent.area, 0)) + { + // Discard positive inner ring with positive parent + it->second.discarded = true; + } + // Remove parent ID from any positive inner ring it->second.parent.source_index = -1; } - else if (it->second.parent.source_index < 0 && it->second.get_area() < 0) + else if (it->second.parent.source_index < 0 + && math::smaller(it->second.get_area(), 0)) { // Reverse negative ring without parent it->second.reversed = true; @@ -309,6 +321,8 @@ inline void assign_parents(Geometry1 const& geometry1, } } + +// Version for one geometry (called by buffer) template < typename Geometry, @@ -320,7 +334,7 @@ inline void assign_parents(Geometry const& geometry, RingMap& ring_map, bool check_for_orientation) { - // Call it with an empty geometry + // Call it with an empty geometry as second geometry (source_id == 1) // (ring_map should be empty for source_id==1) Geometry empty; diff --git a/boost/geometry/algorithms/detail/overlay/backtrack_check_si.hpp b/boost/geometry/algorithms/detail/overlay/backtrack_check_si.hpp index 012b3aca30..90901dee70 100644 --- a/boost/geometry/algorithms/detail/overlay/backtrack_check_si.hpp +++ b/boost/geometry/algorithms/detail/overlay/backtrack_check_si.hpp @@ -55,8 +55,8 @@ inline void clear_visit_info(Turns& turns) struct backtrack_state { bool m_good; - - inline backtrack_state() : m_good(true) {} + + inline backtrack_state() : m_good(true) {} inline void reset() { m_good = true; } inline bool good() const { return m_good; } }; @@ -79,29 +79,30 @@ class backtrack_check_self_intersections public : typedef state state_type; - template <typename Operation, typename Rings, typename Turns> - static inline void apply(std::size_t size_at_start, - Rings& rings, typename boost::range_value<Rings>::type& ring, + template <typename Operation, typename Rings, typename Ring, typename Turns, typename RobustPolicy> + static inline void apply(std::size_t size_at_start, + Rings& rings, Ring& ring, Turns& turns, Operation& operation, std::string const& , Geometry1 const& geometry1, Geometry2 const& geometry2, + RobustPolicy const& robust_policy, state_type& state ) { state.m_good = false; - + // Check self-intersections and throw exception if appropriate if (! state.m_checked) { state.m_checked = true; - has_self_intersections(geometry1); - has_self_intersections(geometry2); + has_self_intersections(geometry1, robust_policy); + has_self_intersections(geometry2, robust_policy); } // Make bad output clean rings.resize(size_at_start); - ring.clear(); + geometry::traits::clear<typename boost::range_value<Rings>::type>::apply(ring); // Reject this as a starting point operation.visited.set_rejected(); @@ -123,7 +124,7 @@ public : typedef backtrack_state state_type; template <typename Operation, typename Rings, typename Turns> - static inline void apply(std::size_t size_at_start, + static inline void apply(std::size_t size_at_start, Rings& rings, typename boost::range_value<Rings>::type& ring, Turns& turns, Operation& operation, std::string const& reason, @@ -133,7 +134,7 @@ public : ) { std::cout << " REJECT " << reason << std::endl; - + state.m_good = false; rings.resize(size_at_start); diff --git a/boost/geometry/algorithms/detail/overlay/calculate_distance_policy.hpp b/boost/geometry/algorithms/detail/overlay/calculate_distance_policy.hpp deleted file mode 100644 index 2003d2350d..0000000000 --- a/boost/geometry/algorithms/detail/overlay/calculate_distance_policy.hpp +++ /dev/null @@ -1,64 +0,0 @@ -// Boost.Geometry (aka GGL, Generic Geometry Library) - -// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. - -// 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) - -#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_CALCULATE_DISTANCE_POLICY_HPP -#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_CALCULATE_DISTANCE_POLICY_HPP - - -#include <boost/geometry/algorithms/comparable_distance.hpp> - - -namespace boost { namespace geometry -{ - - -#ifndef DOXYGEN_NO_DETAIL -namespace detail { namespace overlay -{ - - -/*! - \brief Policy calculating distance - \details get_turn_info has an optional policy to get some - extra information. - This policy calculates the distance (using default distance strategy) - */ -struct calculate_distance_policy -{ - static bool const include_no_turn = false; - static bool const include_degenerate = false; - static bool const include_opposite = false; - - template - < - typename Info, - typename Point1, - typename Point2, - typename IntersectionInfo, - typename DirInfo - > - static inline void apply(Info& info, Point1 const& p1, Point2 const& p2, - IntersectionInfo const&, DirInfo const&) - { - info.operations[0].enriched.distance - = geometry::comparable_distance(info.point, p1); - info.operations[1].enriched.distance - = geometry::comparable_distance(info.point, p2); - } - -}; - - -}} // namespace detail::overlay -#endif //DOXYGEN_NO_DETAIL - - -}} // namespace boost::geometry - - -#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_CALCULATE_DISTANCE_POLICY_HPP diff --git a/boost/geometry/algorithms/detail/overlay/check_enrich.hpp b/boost/geometry/algorithms/detail/overlay/check_enrich.hpp index b210fd04b1..03be18e07a 100644 --- a/boost/geometry/algorithms/detail/overlay/check_enrich.hpp +++ b/boost/geometry/algorithms/detail/overlay/check_enrich.hpp @@ -137,7 +137,7 @@ inline bool check_graph(TurnPoints& turn_points, operation_type for_operation) it != boost::end(meta_turns); ++it) { - if (! (it->turn->blocked() || it->turn->is_discarded())) + if (! (it->turn->blocked() || it->turn->discarded)) { for (int i = 0 ; i < 2; i++) { diff --git a/boost/geometry/algorithms/detail/overlay/convert_ring.hpp b/boost/geometry/algorithms/detail/overlay/convert_ring.hpp index 05bd721e7f..51955b515d 100644 --- a/boost/geometry/algorithms/detail/overlay/convert_ring.hpp +++ b/boost/geometry/algorithms/detail/overlay/convert_ring.hpp @@ -77,12 +77,23 @@ struct convert_ring<polygon_tag> } else { - interior_rings(destination).resize( - interior_rings(destination).size() + 1); - geometry::convert(source, interior_rings(destination).back()); - if (reverse) + // Avoid adding interior rings which are invalid + // because of its number of points: + std::size_t const min_num_points + = core_detail::closure::minimum_ring_size + < + geometry::closure<Destination>::value + >::value; + + if (geometry::num_points(source) >= min_num_points) { - boost::reverse(interior_rings(destination).back()); + interior_rings(destination).resize( + interior_rings(destination).size() + 1); + geometry::convert(source, interior_rings(destination).back()); + if (reverse) + { + boost::reverse(interior_rings(destination).back()); + } } } } diff --git a/boost/geometry/algorithms/detail/overlay/copy_segment_point.hpp b/boost/geometry/algorithms/detail/overlay/copy_segment_point.hpp index 5e18d0453a..20a6d7f48d 100644 --- a/boost/geometry/algorithms/detail/overlay/copy_segment_point.hpp +++ b/boost/geometry/algorithms/detail/overlay/copy_segment_point.hpp @@ -17,8 +17,10 @@ #include <boost/geometry/core/ring_type.hpp> #include <boost/geometry/core/exterior_ring.hpp> #include <boost/geometry/core/interior_rings.hpp> +#include <boost/geometry/core/tags.hpp> #include <boost/geometry/algorithms/convert.hpp> #include <boost/geometry/geometries/concepts/check.hpp> +#include <boost/geometry/util/range.hpp> #include <boost/geometry/views/closeable_view.hpp> #include <boost/geometry/views/reversible_view.hpp> @@ -51,7 +53,7 @@ struct copy_segment_point_range SegmentIdentifier const& seg_id, bool second, PointOut& point) { - int index = seg_id.segment_index; + signed_index_type index = seg_id.segment_index; if (second) { index++; @@ -94,8 +96,8 @@ struct copy_segment_point_polygon >::apply ( seg_id.ring_index < 0 - ? geometry::exterior_ring(polygon) - : geometry::interior_rings(polygon)[seg_id.ring_index], + ? geometry::exterior_ring(polygon) + : range::at(geometry::interior_rings(polygon), seg_id.ring_index), seg_id, second, point ); @@ -110,7 +112,7 @@ struct copy_segment_point_box SegmentIdentifier const& seg_id, bool second, PointOut& point) { - int index = seg_id.segment_index; + signed_index_type index = seg_id.segment_index; if (second) { index++; @@ -124,6 +126,30 @@ struct copy_segment_point_box }; +template +< + typename MultiGeometry, + typename SegmentIdentifier, + typename PointOut, + typename Policy +> +struct copy_segment_point_multi +{ + static inline bool apply(MultiGeometry const& multi, + SegmentIdentifier const& seg_id, bool second, + PointOut& point) + { + + BOOST_ASSERT + ( + seg_id.multi_index >= 0 + && seg_id.multi_index < int(boost::size(multi)) + ); + + // Call the single-version + return Policy::apply(range::at(multi, seg_id.multi_index), seg_id, second, point); + } +}; }} // namespace detail::copy_segments @@ -188,6 +214,66 @@ struct copy_segment_point<box_tag, Box, Reverse, SegmentIdentifier, PointOut> {}; +template +< + typename MultiGeometry, + bool Reverse, + typename SegmentIdentifier, + typename PointOut +> +struct copy_segment_point + < + multi_polygon_tag, + MultiGeometry, + Reverse, + SegmentIdentifier, + PointOut + > + : detail::copy_segments::copy_segment_point_multi + < + MultiGeometry, + SegmentIdentifier, + PointOut, + detail::copy_segments::copy_segment_point_polygon + < + typename boost::range_value<MultiGeometry>::type, + Reverse, + SegmentIdentifier, + PointOut + > + > +{}; + +template +< + typename MultiGeometry, + bool Reverse, + typename SegmentIdentifier, + typename PointOut +> +struct copy_segment_point + < + multi_linestring_tag, + MultiGeometry, + Reverse, + SegmentIdentifier, + PointOut + > + : detail::copy_segments::copy_segment_point_multi + < + MultiGeometry, + SegmentIdentifier, + PointOut, + detail::copy_segments::copy_segment_point_range + < + typename boost::range_value<MultiGeometry>::type, + Reverse, + SegmentIdentifier, + PointOut + > + > +{}; + } // namespace dispatch #endif // DOXYGEN_NO_DISPATCH diff --git a/boost/geometry/algorithms/detail/overlay/copy_segments.hpp b/boost/geometry/algorithms/detail/overlay/copy_segments.hpp index 805f3923e3..ceeb1a3b8b 100644 --- a/boost/geometry/algorithms/detail/overlay/copy_segments.hpp +++ b/boost/geometry/algorithms/detail/overlay/copy_segments.hpp @@ -1,6 +1,12 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. + +// This file was modified by Oracle on 2014. +// Modifications copyright (c) 2014 Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -10,24 +16,32 @@ #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_COPY_SEGMENTS_HPP -#include <boost/array.hpp> -#include <boost/mpl/assert.hpp> #include <vector> +#include <boost/array.hpp> #include <boost/assert.hpp> +#include <boost/mpl/assert.hpp> #include <boost/range.hpp> +#include <boost/type_traits/integral_constant.hpp> #include <boost/geometry/core/tags.hpp> #include <boost/geometry/core/ring_type.hpp> #include <boost/geometry/core/exterior_ring.hpp> #include <boost/geometry/core/interior_rings.hpp> +#include <boost/geometry/core/ring_type.hpp> +#include <boost/geometry/core/tags.hpp> +#include <boost/geometry/algorithms/not_implemented.hpp> #include <boost/geometry/geometries/concepts/check.hpp> #include <boost/geometry/iterators/ever_circling_iterator.hpp> #include <boost/geometry/views/closeable_view.hpp> #include <boost/geometry/views/reversible_view.hpp> #include <boost/geometry/algorithms/detail/overlay/append_no_duplicates.hpp> +#include <boost/geometry/algorithms/detail/overlay/append_no_dups_or_spikes.hpp> + +#include <boost/geometry/util/range.hpp> + namespace boost { namespace geometry { @@ -38,34 +52,38 @@ namespace detail { namespace copy_segments { -template -< - typename Ring, - bool Reverse, - typename SegmentIdentifier, - typename RangeOut -> +template <bool Reverse> struct copy_segments_ring { - typedef typename closeable_view + template + < + typename Ring, + typename SegmentIdentifier, + typename RobustPolicy, + typename RangeOut + > + static inline void apply(Ring const& ring, + SegmentIdentifier const& seg_id, + signed_index_type to_index, + RobustPolicy const& robust_policy, + RangeOut& current_output) + { + typedef typename closeable_view < Ring const, closure<Ring>::value >::type cview_type; - typedef typename reversible_view + typedef typename reversible_view < cview_type const, Reverse ? iterate_reverse : iterate_forward >::type rview_type; - typedef typename boost::range_iterator<rview_type const>::type iterator; - typedef geometry::ever_circling_iterator<iterator> ec_iterator; + typedef typename boost::range_iterator<rview_type const>::type iterator; + typedef geometry::ever_circling_iterator<iterator> ec_iterator; + - static inline void apply(Ring const& ring, - SegmentIdentifier const& seg_id, int to_index, - RangeOut& current_output) - { cview_type cview(ring); rview_type view(cview); @@ -75,10 +93,10 @@ struct copy_segments_ring // So we use the ever-circling iterator and determine when to step out - int const from_index = seg_id.segment_index + 1; + signed_index_type const from_index = seg_id.segment_index + 1; // Sanity check - BOOST_ASSERT(from_index < int(boost::size(view))); + BOOST_ASSERT(from_index < static_cast<signed_index_type>(boost::size(view))); ec_iterator it(boost::begin(view), boost::end(view), boost::begin(view) + from_index); @@ -86,103 +104,130 @@ struct copy_segments_ring // [2..4] -> 4 - 2 + 1 = 3 -> {2,3,4} -> OK // [4..2],size=6 -> 6 - 4 + 2 + 1 = 5 -> {4,5,0,1,2} -> OK // [1..1], travel the whole ring round - typedef typename boost::range_difference<Ring>::type size_type; - size_type const count = from_index <= to_index + signed_index_type const count = from_index <= to_index ? to_index - from_index + 1 - : int(boost::size(view)) - from_index + to_index + 1; + : static_cast<signed_index_type>(boost::size(view)) + - from_index + to_index + 1; - for (size_type i = 0; i < count; ++i, ++it) + for (signed_index_type i = 0; i < count; ++i, ++it) { - detail::overlay::append_no_duplicates(current_output, *it); + detail::overlay::append_no_dups_or_spikes(current_output, *it, robust_policy); } } }; -template -< - typename LineString, - bool Reverse, - typename SegmentIdentifier, - typename RangeOut -> -struct copy_segments_linestring +template <bool Reverse, bool RemoveSpikes = true> +class copy_segments_linestring { +private: + // remove spikes + template <typename RangeOut, typename Point, typename RobustPolicy> + static inline void append_to_output(RangeOut& current_output, + Point const& point, + RobustPolicy const& robust_policy, + boost::true_type const&) + { + detail::overlay::append_no_dups_or_spikes(current_output, point, + robust_policy); + } - typedef typename boost::range_iterator<LineString const>::type iterator; + // keep spikes + template <typename RangeOut, typename Point, typename RobustPolicy> + static inline void append_to_output(RangeOut& current_output, + Point const& point, + RobustPolicy const&, + boost::false_type const&) + { + detail::overlay::append_no_duplicates(current_output, point); + } +public: + template + < + typename LineString, + typename SegmentIdentifier, + typename RobustPolicy, + typename RangeOut + > static inline void apply(LineString const& ls, - SegmentIdentifier const& seg_id, int to_index, + SegmentIdentifier const& seg_id, + signed_index_type to_index, + RobustPolicy const& robust_policy, RangeOut& current_output) { - int const from_index = seg_id.segment_index + 1; + signed_index_type const from_index = seg_id.segment_index + 1; // Sanity check - if (from_index > to_index || from_index < 0 || to_index >= int(boost::size(ls))) + if ( from_index > to_index + || from_index < 0 + || to_index >= static_cast<signed_index_type>(boost::size(ls)) ) { return; } - typedef typename boost::range_difference<LineString>::type size_type; - size_type const count = to_index - from_index + 1; + signed_index_type const count = to_index - from_index + 1; - typename boost::range_iterator<LineString const>::type it = boost::begin(ls) + from_index; + typename boost::range_iterator<LineString const>::type + it = boost::begin(ls) + from_index; - for (size_type i = 0; i < count; ++i, ++it) + for (signed_index_type i = 0; i < count; ++i, ++it) { - detail::overlay::append_no_duplicates(current_output, *it); + append_to_output(current_output, *it, robust_policy, + boost::integral_constant<bool, RemoveSpikes>()); } } }; -template -< - typename Polygon, - bool Reverse, - typename SegmentIdentifier, - typename RangeOut -> +template <bool Reverse> struct copy_segments_polygon { + template + < + typename Polygon, + typename SegmentIdentifier, + typename RobustPolicy, + typename RangeOut + > static inline void apply(Polygon const& polygon, - SegmentIdentifier const& seg_id, int to_index, + SegmentIdentifier const& seg_id, + signed_index_type to_index, + RobustPolicy const& robust_policy, RangeOut& current_output) { // Call ring-version with the right ring - copy_segments_ring - < - typename geometry::ring_type<Polygon>::type, - Reverse, - SegmentIdentifier, - RangeOut - >::apply - ( - seg_id.ring_index < 0 + copy_segments_ring<Reverse>::apply + ( + seg_id.ring_index < 0 ? geometry::exterior_ring(polygon) - : geometry::interior_rings(polygon)[seg_id.ring_index], - seg_id, to_index, - current_output - ); + : range::at(geometry::interior_rings(polygon), seg_id.ring_index), + seg_id, to_index, + robust_policy, + current_output + ); } }; -template -< - typename Box, - bool Reverse, - typename SegmentIdentifier, - typename RangeOut -> +template <bool Reverse> struct copy_segments_box { + template + < + typename Box, + typename SegmentIdentifier, + typename RobustPolicy, + typename RangeOut + > static inline void apply(Box const& box, - SegmentIdentifier const& seg_id, int to_index, + SegmentIdentifier const& seg_id, + signed_index_type to_index, + RobustPolicy const& robust_policy, RangeOut& current_output) { - int index = seg_id.segment_index + 1; + signed_index_type index = seg_id.segment_index + 1; BOOST_ASSERT(index < 5); - int const count = index <= to_index + signed_index_type const count = index <= to_index ? to_index - index + 1 : 5 - index + to_index + 1; @@ -193,15 +238,48 @@ struct copy_segments_box // (possibly cyclic) copy to output // (see comments in ring-version) - for (int i = 0; i < count; i++, index++) + for (signed_index_type i = 0; i < count; i++, index++) { - detail::overlay::append_no_duplicates(current_output, bp[index % 5]); + detail::overlay::append_no_dups_or_spikes(current_output, + bp[index % 5], robust_policy); } } }; +template<typename Policy> +struct copy_segments_multi +{ + template + < + typename MultiGeometry, + typename SegmentIdentifier, + typename RobustPolicy, + typename RangeOut + > + static inline void apply(MultiGeometry const& multi_geometry, + SegmentIdentifier const& seg_id, + signed_index_type to_index, + RobustPolicy const& robust_policy, + RangeOut& current_output) + { + + BOOST_ASSERT + ( + seg_id.multi_index >= 0 + && seg_id.multi_index < int(boost::size(multi_geometry)) + ); + + // Call the single-version + Policy::apply(range::at(multi_geometry, seg_id.multi_index), + seg_id, to_index, + robust_policy, + current_output); + } +}; + + }} // namespace detail::copy_segments #endif // DOXYGEN_NO_DETAIL @@ -213,82 +291,44 @@ namespace dispatch template < typename Tag, - typename GeometryIn, - bool Reverse, - typename SegmentIdentifier, - typename RangeOut + bool Reverse > -struct copy_segments -{ - BOOST_MPL_ASSERT_MSG - ( - false, NOT_OR_NOT_YET_IMPLEMENTED_FOR_THIS_GEOMETRY_TYPE - , (types<GeometryIn>) - ); -}; +struct copy_segments : not_implemented<Tag> +{}; -template -< - typename Ring, - bool Reverse, - typename SegmentIdentifier, - typename RangeOut -> -struct copy_segments<ring_tag, Ring, Reverse, SegmentIdentifier, RangeOut> - : detail::copy_segments::copy_segments_ring - < - Ring, Reverse, SegmentIdentifier, RangeOut - > +template <bool Reverse> +struct copy_segments<ring_tag, Reverse> + : detail::copy_segments::copy_segments_ring<Reverse> {}; +template <bool Reverse> +struct copy_segments<linestring_tag, Reverse> + : detail::copy_segments::copy_segments_linestring<Reverse> +{}; -template -< - typename LineString, - bool Reverse, - typename SegmentIdentifier, - typename RangeOut -> -struct copy_segments<linestring_tag, LineString, Reverse, SegmentIdentifier, RangeOut> - : detail::copy_segments::copy_segments_linestring - < - LineString, Reverse, SegmentIdentifier, RangeOut - > +template <bool Reverse> +struct copy_segments<polygon_tag, Reverse> + : detail::copy_segments::copy_segments_polygon<Reverse> {}; -template -< - typename Polygon, - bool Reverse, - typename SegmentIdentifier, - typename RangeOut -> -struct copy_segments<polygon_tag, Polygon, Reverse, SegmentIdentifier, RangeOut> - : detail::copy_segments::copy_segments_polygon - < - Polygon, Reverse, SegmentIdentifier, RangeOut - > + +template <bool Reverse> +struct copy_segments<box_tag, Reverse> + : detail::copy_segments::copy_segments_box<Reverse> {}; -template -< - typename Box, - bool Reverse, - typename SegmentIdentifier, - typename RangeOut -> -struct copy_segments<box_tag, Box, Reverse, SegmentIdentifier, RangeOut> - : detail::copy_segments::copy_segments_box +template<bool Reverse> +struct copy_segments<multi_polygon_tag, Reverse> + : detail::copy_segments::copy_segments_multi < - Box, Reverse, SegmentIdentifier, RangeOut + detail::copy_segments::copy_segments_polygon<Reverse> > {}; - } // namespace dispatch #endif // DOXYGEN_NO_DISPATCH @@ -303,10 +343,13 @@ template bool Reverse, typename Geometry, typename SegmentIdentifier, + typename RobustPolicy, typename RangeOut > inline void copy_segments(Geometry const& geometry, - SegmentIdentifier const& seg_id, int to_index, + SegmentIdentifier const& seg_id, + signed_index_type to_index, + RobustPolicy const& robust_policy, RangeOut& range_out) { concept::check<Geometry const>(); @@ -314,11 +357,8 @@ inline void copy_segments(Geometry const& geometry, dispatch::copy_segments < typename tag<Geometry>::type, - Geometry, - Reverse, - SegmentIdentifier, - RangeOut - >::apply(geometry, seg_id, to_index, range_out); + Reverse + >::apply(geometry, seg_id, to_index, robust_policy, range_out); } diff --git a/boost/geometry/algorithms/detail/overlay/do_reverse.hpp b/boost/geometry/algorithms/detail/overlay/do_reverse.hpp new file mode 100644 index 0000000000..15100f8d0b --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/do_reverse.hpp @@ -0,0 +1,47 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2013 Adam Wulkiewicz, Lodz, Poland + +// 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) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_DO_REVERSE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_DO_REVERSE_HPP + +#include <boost/geometry/core/point_order.hpp> + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + +// Metafunction helper for intersection and union +template <order_selector Selector, bool Reverse = false> +struct do_reverse {}; + +template <> +struct do_reverse<clockwise, false> : boost::false_type {}; + +template <> +struct do_reverse<clockwise, true> : boost::true_type {}; + +template <> +struct do_reverse<counterclockwise, false> : boost::true_type {}; + +template <> +struct do_reverse<counterclockwise, true> : boost::false_type {}; + + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_DO_REVERSE_HPP diff --git a/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp b/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp index e4842d35f1..9484479b45 100644 --- a/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp +++ b/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp @@ -27,8 +27,9 @@ #include <boost/geometry/algorithms/detail/ring_identifier.hpp> #include <boost/geometry/algorithms/detail/overlay/copy_segment_point.hpp> -#include <boost/geometry/algorithms/detail/overlay/get_relative_order.hpp> #include <boost/geometry/algorithms/detail/overlay/handle_tangencies.hpp> +#include <boost/geometry/policies/robustness/robust_type.hpp> +#include <boost/geometry/strategies/side.hpp> #ifdef BOOST_GEOMETRY_DEBUG_ENRICH # include <boost/geometry/algorithms/detail/overlay/check_enrich.hpp> #endif @@ -47,17 +48,23 @@ struct indexed_turn_operation { typedef TurnOperation type; - int index; - int operation_index; + std::size_t turn_index; + std::size_t operation_index; bool discarded; - TurnOperation subject; - - inline indexed_turn_operation(int i, int oi, TurnOperation const& s) - : index(i) + // use pointers to avoid copies, const& is not possible because of usage in vector + segment_identifier const* other_seg_id; // segment id of other segment of intersection of two segments + TurnOperation const* subject; + + inline indexed_turn_operation(std::size_t ti, std::size_t oi, + TurnOperation const& s, + segment_identifier const& oid) + : turn_index(ti) , operation_index(oi) , discarded(false) - , subject(s) + , other_seg_id(&oid) + , subject(&s) {} + }; template <typename IndexedTurnOperation> @@ -75,19 +82,22 @@ template typename TurnPoints, typename Indexed, typename Geometry1, typename Geometry2, + typename RobustPolicy, bool Reverse1, bool Reverse2, typename Strategy > -struct sort_on_segment_and_distance +struct sort_on_segment_and_ratio { - inline sort_on_segment_and_distance(TurnPoints const& turn_points + inline sort_on_segment_and_ratio(TurnPoints const& turn_points , Geometry1 const& geometry1 , Geometry2 const& geometry2 + , RobustPolicy const& robust_policy , Strategy const& strategy , bool* clustered) : m_turn_points(turn_points) , m_geometry1(geometry1) , m_geometry2(geometry2) + , m_robust_policy(robust_policy) , m_strategy(strategy) , m_clustered(clustered) { @@ -98,31 +108,47 @@ private : TurnPoints const& m_turn_points; Geometry1 const& m_geometry1; Geometry2 const& m_geometry2; + RobustPolicy const& m_robust_policy; Strategy const& m_strategy; mutable bool* m_clustered; + typedef typename geometry::point_type<Geometry1>::type point_type; + inline bool consider_relative_order(Indexed const& left, Indexed const& right) const { - typedef typename geometry::point_type<Geometry1>::type point_type; point_type pi, pj, ri, rj, si, sj; geometry::copy_segment_points<Reverse1, Reverse2>(m_geometry1, m_geometry2, - left.subject.seg_id, + left.subject->seg_id, pi, pj); geometry::copy_segment_points<Reverse1, Reverse2>(m_geometry1, m_geometry2, - left.subject.other_id, + *left.other_seg_id, ri, rj); geometry::copy_segment_points<Reverse1, Reverse2>(m_geometry1, m_geometry2, - right.subject.other_id, + *right.other_seg_id, si, sj); - int const order = get_relative_order + typedef typename strategy::side::services::default_strategy < - point_type - >::apply(pi, pj,ri, rj, si, sj); - //debug("r/o", order == -1); - return order == -1; + typename cs_tag<point_type>::type + >::type strategy; + + int const side_rj_p = strategy::apply(pi, pj, rj); + int const side_sj_p = strategy::apply(pi, pj, sj); + + // Put the one turning left (1; right == -1) as last + if (side_rj_p != side_sj_p) + { + return side_rj_p < side_sj_p; + } + + int const side_sj_r = strategy::apply(ri, rj, sj); + int const side_rj_s = strategy::apply(si, sj, rj); + + // If they both turn left: the most left as last + // If they both turn right: this is not relevant, but take also here most left + return side_rj_s < side_sj_r; } public : @@ -131,33 +157,30 @@ public : // but to the "indexed_turn_operation" inline bool operator()(Indexed const& left, Indexed const& right) const { - segment_identifier const& sl = left.subject.seg_id; - segment_identifier const& sr = right.subject.seg_id; + segment_identifier const& sl = left.subject->seg_id; + segment_identifier const& sr = right.subject->seg_id; - if (sl == sr - && geometry::math::equals(left.subject.enriched.distance - , right.subject.enriched.distance)) + if (sl == sr) { // Both left and right are located on the SAME segment. - - // First check "real" intersection (crosses) - // -> distance zero due to precision, solve it by sorting - if (m_turn_points[left.index].method == method_crosses - && m_turn_points[right.index].method == method_crosses) + if (left.subject->fraction == right.subject->fraction) { - return consider_relative_order(left, right); - } - - // If that is not the case, cluster it later on. - // Indicate that this is necessary. - *m_clustered = true; + // First check "real" intersection (crosses) + // -> distance zero due to precision, solve it by sorting + if (m_turn_points[left.turn_index].method == method_crosses + && m_turn_points[right.turn_index].method == method_crosses) + { + return consider_relative_order(left, right); + } - return left.index < right.index; + // If that is not the case, cluster it later on. + // Indicate that this is necessary. + *m_clustered = true; + } } return sl == sr - ? left.subject.enriched.distance < right.subject.enriched.distance + ? left.subject->fraction < right.subject->fraction : sl < sr; - } }; @@ -171,13 +194,13 @@ inline void update_discarded(Turns& turn_points, Operations& operations) it != boost::end(operations); ++it) { - if (turn_points[it->index].discarded) + if (turn_points[it->turn_index].discarded) { it->discarded = true; } else if (it->discarded) { - turn_points[it->index].discarded = true; + turn_points[it->turn_index].discarded = true; } } } @@ -195,12 +218,14 @@ template typename Container, typename TurnPoints, typename Geometry1, typename Geometry2, + typename RobustPolicy, typename Strategy > inline void enrich_sort(Container& operations, TurnPoints& turn_points, operation_type for_operation, Geometry1 const& geometry1, Geometry2 const& geometry2, + RobustPolicy const& robust_policy, Strategy const& strategy) { typedef typename IndexType::type operations_type; @@ -208,14 +233,15 @@ inline void enrich_sort(Container& operations, bool clustered = false; std::sort(boost::begin(operations), boost::end(operations), - sort_on_segment_and_distance + sort_on_segment_and_ratio < TurnPoints, IndexType, Geometry1, Geometry2, + RobustPolicy, Reverse1, Reverse2, Strategy - >(turn_points, geometry1, geometry2, strategy, &clustered)); + >(turn_points, geometry1, geometry2, robust_policy, strategy, &clustered)); // DONT'T discard xx / (for union) ix / ii / (for intersection) ux / uu here // It would give way to "lonely" ui turn points, traveling all @@ -230,16 +256,15 @@ inline void enrich_sort(Container& operations, it != boost::end(operations); prev = it++) { - operations_type& prev_op = turn_points[prev->index] + operations_type& prev_op = turn_points[prev->turn_index] .operations[prev->operation_index]; - operations_type& op = turn_points[it->index] + operations_type& op = turn_points[it->turn_index] .operations[it->operation_index]; if (prev_op.seg_id == op.seg_id - && (turn_points[prev->index].method != method_crosses - || turn_points[it->index].method != method_crosses) - && geometry::math::equals(prev_op.enriched.distance, - op.enriched.distance)) + && (turn_points[prev->turn_index].method != method_crosses + || turn_points[it->turn_index].method != method_crosses) + && prev_op.fraction == op.fraction) { if (begin_cluster == boost::end(operations)) { @@ -249,14 +274,14 @@ inline void enrich_sort(Container& operations, else if (begin_cluster != boost::end(operations)) { handle_cluster<IndexType, Reverse1, Reverse2>(begin_cluster, it, turn_points, - for_operation, geometry1, geometry2, strategy); + for_operation, geometry1, geometry2, robust_policy, strategy); begin_cluster = boost::end(operations); } } if (begin_cluster != boost::end(operations)) { handle_cluster<IndexType, Reverse1, Reverse2>(begin_cluster, it, turn_points, - for_operation, geometry1, geometry2, strategy); + for_operation, geometry1, geometry2, robust_policy, strategy); } } @@ -315,19 +340,19 @@ inline void enrich_assign(Container& operations, prev = it++) { operations_type& prev_op - = turn_points[prev->index].operations[prev->operation_index]; + = turn_points[prev->turn_index].operations[prev->operation_index]; operations_type& op - = turn_points[it->index].operations[it->operation_index]; + = turn_points[it->turn_index].operations[it->operation_index]; prev_op.enriched.travels_to_ip_index - = it->index; + = static_cast<int>(it->turn_index); prev_op.enriched.travels_to_vertex_index - = it->subject.seg_id.segment_index; + = it->subject->seg_id.segment_index; if (! first && prev_op.seg_id.segment_index == op.seg_id.segment_index) { - prev_op.enriched.next_ip_index = it->index; + prev_op.enriched.next_ip_index = static_cast<int>(it->turn_index); } first = false; } @@ -340,16 +365,16 @@ inline void enrich_assign(Container& operations, it != boost::end(operations); ++it) { - operations_type& op = turn_points[it->index] + operations_type& op = turn_points[it->turn_index] .operations[it->operation_index]; - std::cout << it->index - << " meth: " << method_char(turn_points[it->index].method) + std::cout << it->turn_index + << " meth: " << method_char(turn_points[it->turn_index].method) << " seg: " << op.seg_id - << " dst: " << boost::numeric_cast<double>(op.enriched.distance) - << " op: " << operation_char(turn_points[it->index].operations[0].operation) - << operation_char(turn_points[it->index].operations[1].operation) - << " dsc: " << (turn_points[it->index].discarded ? "T" : "F") + << " dst: " << op.fraction // needs define + << " op: " << operation_char(turn_points[it->turn_index].operations[0].operation) + << operation_char(turn_points[it->turn_index].operations[1].operation) + << " dsc: " << (turn_points[it->turn_index].discarded ? "T" : "F") << " ->vtx " << op.enriched.travels_to_vertex_index << " ->ip " << op.enriched.travels_to_ip_index << " ->nxt ip " << op.enriched.next_ip_index @@ -370,7 +395,7 @@ inline void create_map(TurnPoints const& turn_points, MappedVector& mapped_vecto typedef typename boost::range_value<TurnPoints>::type turn_point_type; typedef typename turn_point_type::container_type container_type; - int index = 0; + std::size_t index = 0; for (typename boost::range_iterator<TurnPoints const>::type it = boost::begin(turn_points); it != boost::end(turn_points); @@ -379,7 +404,7 @@ inline void create_map(TurnPoints const& turn_points, MappedVector& mapped_vecto // Add operations on this ring, but skip discarded ones if (! it->discarded) { - int op_index = 0; + std::size_t op_index = 0; for (typename boost::range_iterator<container_type const>::type op_it = boost::begin(it->operations); op_it != boost::end(it->operations); @@ -397,7 +422,8 @@ inline void create_map(TurnPoints const& turn_points, MappedVector& mapped_vecto ); mapped_vector[ring_id].push_back ( - IndexedType(index, op_index, *op_it) + IndexedType(index, op_index, *op_it, + it->operations[1 - op_index].seg_id) ); } } @@ -422,6 +448,7 @@ inline void create_map(TurnPoints const& turn_points, MappedVector& mapped_vecto \param for_operation operation_type (union or intersection) \param geometry1 \param_geometry \param geometry2 \param_geometry +\param robust_policy policy to handle robustness issues \param strategy strategy */ template @@ -429,11 +456,13 @@ template bool Reverse1, bool Reverse2, typename TurnPoints, typename Geometry1, typename Geometry2, + typename RobustPolicy, typename Strategy > inline void enrich_intersection_points(TurnPoints& turn_points, detail::overlay::operation_type for_operation, Geometry1 const& geometry1, Geometry2 const& geometry2, + RobustPolicy const& robust_policy, Strategy const& strategy) { typedef typename boost::range_value<TurnPoints>::type turn_point_type; @@ -462,6 +491,10 @@ inline void enrich_intersection_points(TurnPoints& turn_points, { it->discarded = true; } + if (it->both(detail::overlay::operation_none)) + { + it->discarded = true; + } } @@ -484,7 +517,7 @@ inline void enrich_intersection_points(TurnPoints& turn_points, << mit->first << std::endl; #endif detail::overlay::enrich_sort<indexed_turn_operation, Reverse1, Reverse2>(mit->second, turn_points, for_operation, - geometry1, geometry2, strategy); + geometry1, geometry2, robust_policy, strategy); } for (typename mapped_vector_type::iterator mit diff --git a/boost/geometry/algorithms/detail/overlay/enrichment_info.hpp b/boost/geometry/algorithms/detail/overlay/enrichment_info.hpp index 8c8ed96189..ef32edeefa 100644 --- a/boost/geometry/algorithms/detail/overlay/enrichment_info.hpp +++ b/boost/geometry/algorithms/detail/overlay/enrichment_info.hpp @@ -10,9 +10,6 @@ #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ENRICHMENT_INFO_HPP -#include <boost/geometry/strategies/distance.hpp> - - namespace boost { namespace geometry { @@ -31,37 +28,22 @@ namespace detail { namespace overlay template<typename P> struct enrichment_info { - typedef typename strategy::distance::services::return_type - < - typename strategy::distance::services::comparable_type - < - typename strategy::distance::services::default_strategy - < - point_tag, - P - >::type - >::type - >::type distance_type; - inline enrichment_info() : travels_to_vertex_index(-1) , travels_to_ip_index(-1) , next_ip_index(-1) - , distance(distance_type()) {} // vertex to which is free travel after this IP, // so from "segment_index+1" to "travels_to_vertex_index", without IP-s, // can be -1 - int travels_to_vertex_index; + signed_index_type travels_to_vertex_index; // same but now IP index, so "next IP index" but not on THIS segment int travels_to_ip_index; // index of next IP on this segment, -1 if there is no one int next_ip_index; - - distance_type distance; // distance-measurement from segment.first to IP }; diff --git a/boost/geometry/algorithms/detail/overlay/follow.hpp b/boost/geometry/algorithms/detail/overlay/follow.hpp index b110cc9602..acf38d09ab 100644 --- a/boost/geometry/algorithms/detail/overlay/follow.hpp +++ b/boost/geometry/algorithms/detail/overlay/follow.hpp @@ -1,6 +1,11 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. + +// This file was modified by Oracle on 2014. +// Modifications copyright (c) 2014 Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -20,6 +25,7 @@ #include <boost/geometry/algorithms/detail/overlay/turn_info.hpp> #include <boost/geometry/algorithms/covered_by.hpp> +#include <boost/geometry/algorithms/clear.hpp> namespace boost { namespace geometry @@ -32,7 +38,7 @@ namespace detail { namespace overlay namespace following { - + template <typename Turn, typename Operation> static inline bool is_entering(Turn const& /* TODO remove this parameter */, Operation const& op) { @@ -44,43 +50,43 @@ static inline bool is_entering(Turn const& /* TODO remove this parameter */, Ope ; } -template +template < - typename Turn, - typename Operation, - typename LineString, + typename Turn, + typename Operation, + typename LineString, typename Polygon > -static inline bool last_covered_by(Turn const& turn, Operation const& op, +static inline bool last_covered_by(Turn const& turn, Operation const& op, LineString const& linestring, Polygon const& polygon) { - // Check any point between the this one and the first IP + // Check any point between the this one and the first IP typedef typename geometry::point_type<LineString>::type point_type; point_type point_in_between; detail::point_on_border::midpoint_helper < point_type, 0, dimension<point_type>::value - >::apply(point_in_between, linestring[op.seg_id.segment_index], turn.point); + >::apply(point_in_between, *(::boost::begin(linestring) + op.seg_id.segment_index), turn.point); return geometry::covered_by(point_in_between, polygon); } -template +template < - typename Turn, - typename Operation, - typename LineString, + typename Turn, + typename Operation, + typename LineString, typename Polygon > -static inline bool is_leaving(Turn const& turn, Operation const& op, - bool entered, bool first, +static inline bool is_leaving(Turn const& turn, Operation const& op, + bool entered, bool first, LineString const& linestring, Polygon const& polygon) { if (op.operation == operation_union) { - return entered + return entered || turn.method == method_crosses || (first && last_covered_by(turn, op, linestring, polygon)) ; @@ -89,20 +95,20 @@ static inline bool is_leaving(Turn const& turn, Operation const& op, } -template +template < - typename Turn, - typename Operation, - typename LineString, + typename Turn, + typename Operation, + typename LineString, typename Polygon > -static inline bool is_staying_inside(Turn const& turn, Operation const& op, - bool entered, bool first, +static inline bool is_staying_inside(Turn const& turn, Operation const& op, + bool entered, bool first, LineString const& linestring, Polygon const& polygon) { if (turn.method == method_crosses) { - // The normal case, this is completely covered with entering/leaving + // The normal case, this is completely covered with entering/leaving // so stay out of this time consuming "covered_by" return false; } @@ -115,11 +121,11 @@ static inline bool is_staying_inside(Turn const& turn, Operation const& op, return false; } -template +template < - typename Turn, - typename Operation, - typename Linestring, + typename Turn, + typename Operation, + typename Linestring, typename Polygon > static inline bool was_entered(Turn const& turn, Operation const& op, bool first, @@ -134,7 +140,7 @@ static inline bool was_entered(Turn const& turn, Operation const& op, bool first // Template specialization structure to call the right actions for the right type -template<overlay_type OverlayType> +template <overlay_type OverlayType, bool RemoveSpikes = true> struct action_selector { // If you get here the overlay type is not intersection or difference @@ -142,51 +148,86 @@ struct action_selector }; // Specialization for intersection, containing the implementation -template<> -struct action_selector<overlay_intersection> +template <bool RemoveSpikes> +struct action_selector<overlay_intersection, RemoveSpikes> { template < - typename OutputIterator, - typename LineStringOut, - typename LineString, - typename Point, - typename Operation + typename OutputIterator, + typename LineStringOut, + typename LineString, + typename Point, + typename Operation, + typename RobustPolicy > static inline void enter(LineStringOut& current_piece, - LineString const& , + LineString const& , segment_identifier& segment_id, - int , Point const& point, - Operation const& operation, OutputIterator& ) + signed_index_type , Point const& point, + Operation const& operation, + RobustPolicy const& , + OutputIterator& ) { // On enter, append the intersection point and remember starting point + // TODO: we don't check on spikes for linestrings (?). Consider this. detail::overlay::append_no_duplicates(current_piece, point); segment_id = operation.seg_id; } template < - typename OutputIterator, - typename LineStringOut, - typename LineString, - typename Point, - typename Operation + typename OutputIterator, + typename LineStringOut, + typename LineString, + typename Point, + typename Operation, + typename RobustPolicy > static inline void leave(LineStringOut& current_piece, LineString const& linestring, segment_identifier& segment_id, - int index, Point const& point, - Operation const& , OutputIterator& out) + signed_index_type index, Point const& point, + Operation const& , + RobustPolicy const& robust_policy, + OutputIterator& out) { // On leave, copy all segments from starting point, append the intersection point // and add the output piece - geometry::copy_segments<false>(linestring, segment_id, index, current_piece); + detail::copy_segments::copy_segments_linestring + < + false, RemoveSpikes + >::apply(linestring, segment_id, index, robust_policy, current_piece); detail::overlay::append_no_duplicates(current_piece, point); - if (current_piece.size() > 1) + if (::boost::size(current_piece) > 1) { *out++ = current_piece; } - current_piece.clear(); + + geometry::clear(current_piece); + } + + template + < + typename OutputIterator, + typename LineStringOut, + typename LineString, + typename Point, + typename Operation + > + static inline void isolated_point(LineStringOut&, + LineString const&, + segment_identifier&, + signed_index_type, Point const& point, + Operation const& , OutputIterator& out) + { + LineStringOut isolated_point_ls; + geometry::append(isolated_point_ls, point); + +#ifndef BOOST_GEOMETRY_ALLOW_ONE_POINT_LINESTRINGS + geometry::append(isolated_point_ls, point); +#endif // BOOST_GEOMETRY_ALLOW_ONE_POINT_LINESTRINGS + + *out++ = isolated_point_ls; } static inline bool is_entered(bool entered) @@ -194,8 +235,15 @@ struct action_selector<overlay_intersection> return entered; } - template <typename Point, typename Geometry> - static inline bool included(Point const& point, Geometry const& geometry) + template + < + typename Point, + typename Geometry, + typename RobustPolicy + > + static inline bool included(Point const& point, + Geometry const& geometry, + RobustPolicy const& ) { return geometry::covered_by(point, geometry); } @@ -203,45 +251,67 @@ struct action_selector<overlay_intersection> }; // Specialization for difference, which reverses these actions -template<> -struct action_selector<overlay_difference> +template <bool RemoveSpikes> +struct action_selector<overlay_difference, RemoveSpikes> { - typedef action_selector<overlay_intersection> normal_action; + typedef action_selector<overlay_intersection, RemoveSpikes> normal_action; template < - typename OutputIterator, - typename LineStringOut, - typename LineString, - typename Point, - typename Operation + typename OutputIterator, + typename LineStringOut, + typename LineString, + typename Point, + typename Operation, + typename RobustPolicy > - static inline void enter(LineStringOut& current_piece, - LineString const& linestring, - segment_identifier& segment_id, - int index, Point const& point, - Operation const& operation, OutputIterator& out) + static inline void enter(LineStringOut& current_piece, + LineString const& linestring, + segment_identifier& segment_id, + signed_index_type index, Point const& point, + Operation const& operation, + RobustPolicy const& robust_policy, + OutputIterator& out) { - normal_action::leave(current_piece, linestring, segment_id, index, - point, operation, out); + normal_action::leave(current_piece, linestring, segment_id, index, + point, operation, robust_policy, out); } template < - typename OutputIterator, - typename LineStringOut, - typename LineString, - typename Point, - typename Operation + typename OutputIterator, + typename LineStringOut, + typename LineString, + typename Point, + typename Operation, + typename RobustPolicy > static inline void leave(LineStringOut& current_piece, LineString const& linestring, segment_identifier& segment_id, - int index, Point const& point, - Operation const& operation, OutputIterator& out) + signed_index_type index, Point const& point, + Operation const& operation, + RobustPolicy const& robust_policy, + OutputIterator& out) { normal_action::enter(current_piece, linestring, segment_id, index, - point, operation, out); + point, operation, robust_policy, out); + } + + template + < + typename OutputIterator, + typename LineStringOut, + typename LineString, + typename Point, + typename Operation + > + static inline void isolated_point(LineStringOut&, + LineString const&, + segment_identifier&, + signed_index_type, Point const&, + Operation const&, OutputIterator&) + { } static inline bool is_entered(bool entered) @@ -249,10 +319,17 @@ struct action_selector<overlay_difference> return ! normal_action::is_entered(entered); } - template <typename Point, typename Geometry> - static inline bool included(Point const& point, Geometry const& geometry) + template + < + typename Point, + typename Geometry, + typename RobustPolicy + > + static inline bool included(Point const& point, + Geometry const& geometry, + RobustPolicy const& robust_policy) { - return ! normal_action::included(point, geometry); + return ! normal_action::included(point, geometry, robust_policy); } }; @@ -269,12 +346,13 @@ template typename LineStringOut, typename LineString, typename Polygon, - overlay_type OverlayType + overlay_type OverlayType, + bool RemoveSpikes = true > class follow { - template<typename Turn> + template <typename Turn> struct sort_on_segment { // In case of turn point at the same location, we want to have continue/blocked LAST @@ -296,15 +374,15 @@ class follow inline bool use_operation(Turn const& left, Turn const& right) const { - // If they are the same, OK. + // If they are the same, OK. return operation_order(left) < operation_order(right); } inline bool use_distance(Turn const& left, Turn const& right) const { - return geometry::math::equals(left.operations[0].enriched.distance, right.operations[0].enriched.distance) + return left.operations[0].fraction == right.operations[0].fraction ? use_operation(left, right) - : left.operations[0].enriched.distance < right.operations[0].enriched.distance + : left.operations[0].fraction < right.operations[0].fraction ; } @@ -325,16 +403,33 @@ class follow public : - template <typename Point, typename Geometry> - static inline bool included(Point const& point, Geometry const& geometry) + template + < + typename Point, + typename Geometry, + typename RobustPolicy + > + static inline bool included(Point const& point, + Geometry const& geometry, + RobustPolicy const& robust_policy) { - return following::action_selector<OverlayType>::included(point, geometry); + return following::action_selector + < + OverlayType, RemoveSpikes + >::included(point, geometry, robust_policy); } - template<typename Turns, typename OutputIterator> + template + < + typename Turns, + typename OutputIterator, + typename RobustPolicy + > static inline OutputIterator apply(LineString const& linestring, Polygon const& polygon, detail::overlay::operation_type , // TODO: this parameter might be redundant - Turns& turns, OutputIterator out) + Turns& turns, + RobustPolicy const& robust_policy, + OutputIterator out) { typedef typename boost::range_iterator<Turns>::type turn_iterator; typedef typename boost::range_value<Turns>::type turn_type; @@ -343,7 +438,7 @@ public : typename turn_type::container_type >::type turn_operation_iterator_type; - typedef following::action_selector<OverlayType> action; + typedef following::action_selector<OverlayType, RemoveSpikes> action; // Sort intersection points on segments-along-linestring, and distance // (like in enrich is done for poly/poly) @@ -376,27 +471,38 @@ public : debug_traverse(*it, *iit, "-> Entering"); entered = true; - action::enter(current_piece, linestring, current_segment_id, iit->seg_id.segment_index, it->point, *iit, out); + action::enter(current_piece, linestring, current_segment_id, + iit->seg_id.segment_index, it->point, *iit, + robust_policy, + out); } else if (following::is_leaving(*it, *iit, entered, first, linestring, polygon)) { debug_traverse(*it, *iit, "-> Leaving"); entered = false; - action::leave(current_piece, linestring, current_segment_id, iit->seg_id.segment_index, it->point, *iit, out); + action::leave(current_piece, linestring, current_segment_id, + iit->seg_id.segment_index, it->point, *iit, + robust_policy, + out); } first = false; } if (action::is_entered(entered)) { - geometry::copy_segments<false>(linestring, current_segment_id, - boost::size(linestring) - 1, - current_piece); + detail::copy_segments::copy_segments_linestring + < + false, RemoveSpikes + >::apply(linestring, + current_segment_id, + static_cast<signed_index_type>(boost::size(linestring) - 1), + robust_policy, + current_piece); } // Output the last one, if applicable - if (current_piece.size() > 1) + if (::boost::size(current_piece) > 1) { *out++ = current_piece; } diff --git a/boost/geometry/algorithms/detail/overlay/follow_linear_linear.hpp b/boost/geometry/algorithms/detail/overlay/follow_linear_linear.hpp new file mode 100644 index 0000000000..85378e08b0 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/follow_linear_linear.hpp @@ -0,0 +1,536 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_FOLLOW_LINEAR_LINEAR_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_FOLLOW_LINEAR_LINEAR_HPP + +#include <cstddef> +#include <algorithm> +#include <iterator> + +#include <boost/assert.hpp> +#include <boost/range.hpp> + +#include <boost/geometry/core/tag.hpp> +#include <boost/geometry/core/tags.hpp> + +#include <boost/geometry/algorithms/detail/overlay/copy_segments.hpp> +#include <boost/geometry/algorithms/detail/overlay/follow.hpp> +#include <boost/geometry/algorithms/detail/overlay/overlay_type.hpp> +#include <boost/geometry/algorithms/detail/overlay/segment_identifier.hpp> +#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp> + +#include <boost/geometry/algorithms/detail/turns/debug_turn.hpp> + +#include <boost/geometry/algorithms/convert.hpp> +#include <boost/geometry/algorithms/not_implemented.hpp> + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + +namespace following { namespace linear +{ + + + + +// follower for linear/linear geometries set operations + +template <typename Turn, typename Operation> +static inline bool is_entering(Turn const& turn, + Operation const& operation) +{ + if ( turn.method != method_touch && turn.method != method_touch_interior ) + { + return false; + } + return operation.operation == operation_intersection; +} + + + +template <typename Turn, typename Operation> +static inline bool is_staying_inside(Turn const& turn, + Operation const& operation, + bool entered) +{ + if ( !entered ) + { + return false; + } + + if ( turn.method != method_equal && turn.method != method_collinear ) + { + return false; + } + return operation.operation == operation_continue; +} + + + +template <typename Turn, typename Operation> +static inline bool is_leaving(Turn const& turn, + Operation const& operation, + bool entered) +{ + if ( !entered ) + { + return false; + } + + if ( turn.method != method_touch + && turn.method != method_touch_interior + && turn.method != method_equal + && turn.method != method_collinear ) + { + return false; + } + + if ( operation.operation == operation_blocked ) + { + return true; + } + + if ( operation.operation != operation_union ) + { + return false; + } + + return operation.is_collinear; +} + + + +template <typename Turn, typename Operation> +static inline bool is_isolated_point(Turn const& turn, + Operation const& operation, + bool entered) +{ + if ( entered ) + { + return false; + } + + if ( turn.method == method_none ) + { + BOOST_ASSERT( operation.operation == operation_continue ); + return true; + } + + if ( turn.method == method_crosses ) + { + return true; + } + + if ( turn.method != method_touch && turn.method != method_touch_interior ) + { + return false; + } + + if ( operation.operation == operation_blocked ) + { + return true; + } + + if ( operation.operation != operation_union ) + { + return false; + } + + return !operation.is_collinear; +} + + + + + + + + + +template +< + typename LinestringOut, + typename Linestring, + typename Linear, + overlay_type OverlayType, + bool FollowIsolatedPoints, + bool FollowContinueTurns +> +class follow_linestring_linear_linestring +{ +protected: + // allow spikes (false indicates: do not remove spikes) + typedef following::action_selector<OverlayType, false> action; + + template + < + typename TurnIterator, + typename TurnOperationIterator, + typename SegmentIdentifier, + typename OutputIterator + > + static inline OutputIterator + process_turn(TurnIterator it, + TurnOperationIterator op_it, + bool& entered, + std::size_t& enter_count, + Linestring const& linestring, + LinestringOut& current_piece, + SegmentIdentifier& current_segment_id, + OutputIterator oit) + { + // We don't rescale linear/linear + detail::no_rescale_policy robust_policy; + + if ( is_entering(*it, *op_it) ) + { + detail::turns::debug_turn(*it, *op_it, "-> Entering"); + + entered = true; + if ( enter_count == 0 ) + { + action::enter(current_piece, linestring, + current_segment_id, + op_it->seg_id.segment_index, + it->point, *op_it, robust_policy, oit); + } + ++enter_count; + } + else if ( is_leaving(*it, *op_it, entered) ) + { + detail::turns::debug_turn(*it, *op_it, "-> Leaving"); + + --enter_count; + if ( enter_count == 0 ) + { + entered = false; + action::leave(current_piece, linestring, + current_segment_id, + op_it->seg_id.segment_index, + it->point, *op_it, robust_policy, oit); + } + } + else if ( FollowIsolatedPoints + && is_isolated_point(*it, *op_it, entered) ) + { + detail::turns::debug_turn(*it, *op_it, "-> Isolated point"); + + action::isolated_point(current_piece, linestring, + current_segment_id, + op_it->seg_id.segment_index, + it->point, *op_it, oit); + } + else if ( FollowContinueTurns + && is_staying_inside(*it, *op_it, entered) ) + { + detail::turns::debug_turn(*it, *op_it, "-> Staying inside"); + + entered = true; + } + return oit; + } + + template + < + typename SegmentIdentifier, + typename OutputIterator + > + static inline OutputIterator + process_end(bool entered, + Linestring const& linestring, + SegmentIdentifier const& current_segment_id, + LinestringOut& current_piece, + OutputIterator oit) + { + if ( action::is_entered(entered) ) + { + // We don't rescale linear/linear + detail::no_rescale_policy robust_policy; + + detail::copy_segments::copy_segments_linestring + < + false, false // do not reverse; do not remove spikes + >::apply(linestring, + current_segment_id, + static_cast<signed_index_type>(boost::size(linestring) - 1), + robust_policy, + current_piece); + } + + // Output the last one, if applicable + if (::boost::size(current_piece) > 1) + { + *oit++ = current_piece; + } + + return oit; + } + +public: + template <typename TurnIterator, typename OutputIterator> + static inline OutputIterator + apply(Linestring const& linestring, Linear const&, + TurnIterator first, TurnIterator beyond, + OutputIterator oit) + { + // Iterate through all intersection points (they are + // ordered along the each line) + + LinestringOut current_piece; + geometry::segment_identifier current_segment_id(0, -1, -1, -1); + + bool entered = false; + std::size_t enter_count = 0; + + for (TurnIterator it = first; it != beyond; ++it) + { + oit = process_turn(it, boost::begin(it->operations), + entered, enter_count, + linestring, + current_piece, current_segment_id, + oit); + } + + BOOST_ASSERT( enter_count == 0 ); + + return process_end(entered, linestring, + current_segment_id, current_piece, + oit); + } +}; + + + + +template +< + typename LinestringOut, + typename MultiLinestring, + typename Linear, + overlay_type OverlayType, + bool FollowIsolatedPoints, + bool FollowContinueTurns +> +class follow_multilinestring_linear_linestring + : follow_linestring_linear_linestring + < + LinestringOut, + typename boost::range_value<MultiLinestring>::type, + Linear, + OverlayType, + FollowIsolatedPoints, + FollowContinueTurns + > +{ +protected: + typedef typename boost::range_value<MultiLinestring>::type Linestring; + + typedef follow_linestring_linear_linestring + < + LinestringOut, Linestring, Linear, + OverlayType, FollowIsolatedPoints, FollowContinueTurns + > Base; + + typedef following::action_selector<OverlayType> action; + + typedef typename boost::range_iterator + < + MultiLinestring const + >::type linestring_iterator; + + + template <typename OutputIt, overlay_type OT> + struct copy_linestrings_in_range + { + static inline OutputIt + apply(linestring_iterator, linestring_iterator, OutputIt oit) + { + return oit; + } + }; + + template <typename OutputIt> + struct copy_linestrings_in_range<OutputIt, overlay_difference> + { + static inline OutputIt + apply(linestring_iterator first, linestring_iterator beyond, + OutputIt oit) + { + for (linestring_iterator ls_it = first; ls_it != beyond; ++ls_it) + { + LinestringOut line_out; + geometry::convert(*ls_it, line_out); + *oit++ = line_out; + } + return oit; + } + }; + + template <typename TurnIterator> + static inline signed_index_type get_multi_index(TurnIterator it) + { + return boost::begin(it->operations)->seg_id.multi_index; + } + + class has_other_multi_id + { + private: + signed_index_type m_multi_id; + + public: + has_other_multi_id(signed_index_type multi_id) + : m_multi_id(multi_id) {} + + template <typename Turn> + bool operator()(Turn const& turn) const + { + return boost::begin(turn.operations)->seg_id.multi_index + != m_multi_id; + } + }; + +public: + template <typename TurnIterator, typename OutputIterator> + static inline OutputIterator + apply(MultiLinestring const& multilinestring, Linear const& linear, + TurnIterator first, TurnIterator beyond, + OutputIterator oit) + { + BOOST_ASSERT( first != beyond ); + + typedef copy_linestrings_in_range + < + OutputIterator, OverlayType + > copy_linestrings; + + linestring_iterator ls_first = boost::begin(multilinestring); + linestring_iterator ls_beyond = boost::end(multilinestring); + + // Iterate through all intersection points (they are + // ordered along the each linestring) + + signed_index_type current_multi_id = get_multi_index(first); + + oit = copy_linestrings::apply(ls_first, + ls_first + current_multi_id, + oit); + + TurnIterator per_ls_next = first; + do { + TurnIterator per_ls_current = per_ls_next; + + // find turn with different multi-index + per_ls_next = std::find_if(per_ls_current, beyond, + has_other_multi_id(current_multi_id)); + + oit = Base::apply(*(ls_first + current_multi_id), + linear, per_ls_current, per_ls_next, oit); + + signed_index_type next_multi_id(-1); + linestring_iterator ls_next = ls_beyond; + if ( per_ls_next != beyond ) + { + next_multi_id = get_multi_index(per_ls_next); + ls_next = ls_first + next_multi_id; + } + oit = copy_linestrings::apply(ls_first + current_multi_id + 1, + ls_next, + oit); + + current_multi_id = next_multi_id; + } + while ( per_ls_next != beyond ); + + return oit; + } +}; + + + + + + +template +< + typename LinestringOut, + typename Geometry1, + typename Geometry2, + overlay_type OverlayType, + bool FollowIsolatedPoints, + bool FollowContinueTurns, + typename TagOut = typename tag<LinestringOut>::type, + typename TagIn1 = typename tag<Geometry1>::type +> +struct follow + : not_implemented<LinestringOut, Geometry1> +{}; + + + +template +< + typename LinestringOut, + typename Linestring, + typename Linear, + overlay_type OverlayType, + bool FollowIsolatedPoints, + bool FollowContinueTurns +> +struct follow + < + LinestringOut, Linestring, Linear, + OverlayType, FollowIsolatedPoints, FollowContinueTurns, + linestring_tag, linestring_tag + > : follow_linestring_linear_linestring + < + LinestringOut, Linestring, Linear, + OverlayType, FollowIsolatedPoints, FollowContinueTurns + > +{}; + + +template +< + typename LinestringOut, + typename MultiLinestring, + typename Linear, + overlay_type OverlayType, + bool FollowIsolatedPoints, + bool FollowContinueTurns +> +struct follow + < + LinestringOut, MultiLinestring, Linear, + OverlayType, FollowIsolatedPoints, FollowContinueTurns, + linestring_tag, multi_linestring_tag + > : follow_multilinestring_linear_linestring + < + LinestringOut, MultiLinestring, Linear, + OverlayType, FollowIsolatedPoints, FollowContinueTurns + > +{}; + + + +}} // namespace following::linear + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_FOLLOW_LINEAR_LINEAR_HPP diff --git a/boost/geometry/algorithms/detail/overlay/get_intersection_points.hpp b/boost/geometry/algorithms/detail/overlay/get_intersection_points.hpp index 019c3ba3f9..63011c7d48 100644 --- a/boost/geometry/algorithms/detail/overlay/get_intersection_points.hpp +++ b/boost/geometry/algorithms/detail/overlay/get_intersection_points.hpp @@ -17,6 +17,7 @@ #include <boost/geometry/geometries/segment.hpp> +#include <boost/geometry/policies/robustness/robust_point_type.hpp> namespace boost { namespace geometry { @@ -35,32 +36,45 @@ template > struct get_turn_without_info { - typedef strategy_intersection - < - typename cs_tag<typename TurnInfo::point_type>::type, - Point1, - Point2, - typename TurnInfo::point_type - > si; - - typedef typename si::segment_intersection_strategy_type strategy; - - - - template <typename OutputIterator> + template <typename RobustPolicy, typename OutputIterator> static inline OutputIterator apply( - Point1 const& pi, Point1 const& pj, Point1 const& pk, - Point2 const& qi, Point2 const& qj, Point2 const& qk, + Point1 const& pi, Point1 const& pj, Point1 const& /*pk*/, + Point2 const& qi, Point2 const& qj, Point2 const& /*qk*/, + bool /*is_p_first*/, bool /*is_p_last*/, + bool /*is_q_first*/, bool /*is_q_last*/, TurnInfo const& , + RobustPolicy const& robust_policy, OutputIterator out) { + typedef strategy_intersection + < + typename cs_tag<typename TurnInfo::point_type>::type, + Point1, + Point2, + typename TurnInfo::point_type, + RobustPolicy + > si; + + typedef typename si::segment_intersection_strategy_type strategy; + typedef model::referring_segment<Point1 const> segment_type1; typedef model::referring_segment<Point1 const> segment_type2; - segment_type1 p1(pi, pj), p2(pj, pk); - segment_type2 q1(qi, qj), q2(qj, qk); + segment_type1 p1(pi, pj); + segment_type2 q1(qi, qj); - // - typename strategy::return_type result = strategy::apply(p1, q1); + typedef typename geometry::robust_point_type + < + Point1, RobustPolicy + >::type robust_point_type; + + robust_point_type pi_rob, pj_rob, qi_rob, qj_rob; + geometry::recalculate(pi_rob, pi, robust_policy); + geometry::recalculate(pj_rob, pj, robust_policy); + geometry::recalculate(qi_rob, qi, robust_policy); + geometry::recalculate(qj_rob, qj, robust_policy); + typename strategy::return_type result + = strategy::apply(p1, q1, robust_policy, + pi_rob, pj_rob, qi_rob, qj_rob); for (std::size_t i = 0; i < result.template get<0>().count; i++) { @@ -84,10 +98,12 @@ template < typename Geometry1, typename Geometry2, + typename RobustPolicy, typename Turns > inline void get_intersection_points(Geometry1 const& geometry1, Geometry2 const& geometry2, + RobustPolicy const& robust_policy, Turns& turns) { concept::check_concepts_and_equal_dimensions<Geometry1 const, Geometry2 const>(); @@ -99,14 +115,6 @@ inline void get_intersection_points(Geometry1 const& geometry1, typename boost::range_value<Turns>::type > TurnPolicy; - typedef typename strategy_intersection - < - typename cs_tag<Geometry1>::type, - Geometry1, - Geometry2, - typename boost::range_value<Turns>::type - >::segment_intersection_strategy_type segment_intersection_strategy_type; - detail::get_turns::no_interrupt_policy interrupt_policy; boost::mpl::if_c @@ -118,9 +126,7 @@ inline void get_intersection_points(Geometry1 const& geometry1, typename tag<Geometry2>::type, Geometry1, Geometry2, false, false, - Turns, TurnPolicy, - //segment_intersection_strategy_type, - detail::get_turns::no_interrupt_policy + TurnPolicy >, dispatch::get_turns < @@ -128,13 +134,12 @@ inline void get_intersection_points(Geometry1 const& geometry1, typename tag<Geometry2>::type, Geometry1, Geometry2, false, false, - Turns, TurnPolicy, - //segment_intersection_strategy_type, - detail::get_turns::no_interrupt_policy + TurnPolicy > >::type::apply( 0, geometry1, 1, geometry2, + robust_policy, turns, interrupt_policy); } diff --git a/boost/geometry/algorithms/detail/overlay/get_relative_order.hpp b/boost/geometry/algorithms/detail/overlay/get_relative_order.hpp index 522ef68382..d71f4ad51f 100644 --- a/boost/geometry/algorithms/detail/overlay/get_relative_order.hpp +++ b/boost/geometry/algorithms/detail/overlay/get_relative_order.hpp @@ -10,8 +10,6 @@ #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_RELATIVE_ORDER_HPP -#include <boost/geometry/algorithms/distance.hpp> - #include <boost/geometry/strategies/intersection.hpp> @@ -36,15 +34,10 @@ namespace detail { namespace overlay template <typename Point1> struct get_relative_order { - typedef strategy_intersection + typedef typename strategy::side::services::default_strategy < - typename cs_tag<Point1>::type, - Point1, - Point1, - Point1 - > si; - - typedef typename si::side_strategy_type strategy; + typename cs_tag<Point1>::type + >::type strategy; template <typename Point> static inline int value_via_product(Point const& ti, Point const& tj, diff --git a/boost/geometry/algorithms/detail/overlay/get_ring.hpp b/boost/geometry/algorithms/detail/overlay/get_ring.hpp index c2c6980577..131d58d582 100644 --- a/boost/geometry/algorithms/detail/overlay/get_ring.hpp +++ b/boost/geometry/algorithms/detail/overlay/get_ring.hpp @@ -13,11 +13,13 @@ #include <boost/assert.hpp> #include <boost/range.hpp> - -#include <boost/geometry/core/tags.hpp> #include <boost/geometry/core/exterior_ring.hpp> #include <boost/geometry/core/interior_rings.hpp> +#include <boost/geometry/core/ring_type.hpp> +#include <boost/geometry/core/tags.hpp> #include <boost/geometry/algorithms/detail/ring_identifier.hpp> +#include <boost/geometry/geometries/concepts/check.hpp> +#include <boost/geometry/util/range.hpp> namespace boost { namespace geometry @@ -33,16 +35,16 @@ template<typename Tag> struct get_ring {}; -// A container of rings (multi-ring but that does not exist) +// A range of rings (multi-ring but that does not exist) // gets the "void" tag and is dispatched here. template<> struct get_ring<void> { - template<typename Container> - static inline typename boost::range_value<Container>::type const& - apply(ring_identifier const& id, Container const& container) + template<typename Range> + static inline typename boost::range_value<Range>::type const& + apply(ring_identifier const& id, Range const& container) { - return container[id.multi_index]; + return range::at(container, id.multi_index); } }; @@ -87,7 +89,26 @@ struct get_ring<polygon_tag> ); return id.ring_index < 0 ? exterior_ring(polygon) - : interior_rings(polygon)[id.ring_index]; + : range::at(interior_rings(polygon), id.ring_index); + } +}; + + +template<> +struct get_ring<multi_polygon_tag> +{ + template<typename MultiPolygon> + static inline typename ring_type<MultiPolygon>::type const& apply( + ring_identifier const& id, + MultiPolygon const& multi_polygon) + { + BOOST_ASSERT + ( + id.multi_index >= 0 + && id.multi_index < int(boost::size(multi_polygon)) + ); + return get_ring<polygon_tag>::apply(id, + range::at(multi_polygon, id.multi_index)); } }; diff --git a/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp b/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp index b8320d9b7b..240b6de036 100644 --- a/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp +++ b/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp @@ -16,9 +16,19 @@ #include <boost/geometry/algorithms/convert.hpp> #include <boost/geometry/algorithms/detail/overlay/turn_info.hpp> +#include <boost/geometry/algorithms/detail/recalculate.hpp> #include <boost/geometry/geometries/segment.hpp> +#include <boost/geometry/policies/robustness/robust_point_type.hpp> +#include <boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp> + +// Silence warning C4127: conditional expression is constant +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4127) +#endif + namespace boost { namespace geometry { @@ -30,7 +40,7 @@ class turn_info_exception : public geometry::exception public: // NOTE: "char" will be replaced by enum in future version - inline turn_info_exception(char const method) + inline turn_info_exception(char const method) { message = "Boost.Geometry Turn exception: "; message += method; @@ -50,7 +60,6 @@ public: namespace detail { namespace overlay { - struct base_turn_handler { // Returns true if both sides are opposite @@ -91,13 +100,32 @@ struct base_turn_handler { both(ti, condition ? operation_union : operation_intersection); } + + template <typename TurnInfo, typename IntersectionInfo> + static inline void assign_point(TurnInfo& ti, + method_type method, + IntersectionInfo const& info, int index) + { + ti.method = method; + BOOST_ASSERT(index >= 0 && unsigned(index) < info.count); // TODO remove this + geometry::convert(info.intersections[index], ti.point); + ti.operations[0].fraction = info.fractions[index].robust_ra; + ti.operations[1].fraction = info.fractions[index].robust_rb; + } + + template <typename IntersectionInfo> + static inline int non_opposite_to_index(IntersectionInfo const& info) + { + return info.fractions[0].robust_rb < info.fractions[1].robust_rb + ? 1 : 0; + } + }; template < - typename TurnInfo, - typename SideStrategy + typename TurnInfo > struct touch_interior : public base_turn_handler { @@ -108,17 +136,18 @@ struct touch_interior : public base_turn_handler typename Point1, typename Point2, typename IntersectionInfo, - typename DirInfo + typename DirInfo, + typename SidePolicy > static inline void apply( - Point1 const& pi, Point1 const& pj, Point1 const& , - Point2 const& qi, Point2 const& qj, Point2 const& qk, + Point1 const& , Point1 const& , Point1 const& , + Point2 const& , Point2 const& , Point2 const& , TurnInfo& ti, IntersectionInfo const& intersection_info, - DirInfo const& dir_info) + DirInfo const& dir_info, + SidePolicy const& side) { - ti.method = method_touch_interior; - geometry::convert(intersection_info.intersections[0], ti.point); + assign_point(ti, method_touch_interior, intersection_info, 0); // Both segments of q touch segment p somewhere in its interior // 1) We know: if q comes from LEFT or RIGHT @@ -130,7 +159,7 @@ struct touch_interior : public base_turn_handler static int const index_q = 1 - Index; int const side_qi_p = dir_info.sides.template get<index_q, 0>(); - int const side_qk_p = SideStrategy::apply(pi, pj, qk); + int const side_qk_p = side.qk_wrt_p1(); if (side_qi_p == -side_qk_p) { @@ -143,7 +172,7 @@ struct touch_interior : public base_turn_handler return; } - int const side_qk_q = SideStrategy::apply(qi, qj, qk); + int const side_qk_q = side.qk_wrt_q1(); if (side_qi_p == -1 && side_qk_p == -1 && side_qk_q == 1) { @@ -203,8 +232,7 @@ struct touch_interior : public base_turn_handler template < - typename TurnInfo, - typename SideStrategy + typename TurnInfo > struct touch : public base_turn_handler { @@ -227,37 +255,34 @@ struct touch : public base_turn_handler typename Point1, typename Point2, typename IntersectionInfo, - typename DirInfo + typename DirInfo, + typename SidePolicy > static inline void apply( - Point1 const& pi, Point1 const& pj, Point1 const& pk, - Point2 const& qi, Point2 const& qj, Point2 const& qk, + Point1 const& , Point1 const& , Point1 const& , + Point2 const& , Point2 const& , Point2 const& , TurnInfo& ti, IntersectionInfo const& intersection_info, - DirInfo const& dir_info) + DirInfo const& dir_info, + SidePolicy const& side) { - ti.method = method_touch; - geometry::convert(intersection_info.intersections[0], ti.point); + assign_point(ti, method_touch, intersection_info, 0); int const side_qi_p1 = dir_info.sides.template get<1, 0>(); - int const side_qk_p1 = SideStrategy::apply(pi, pj, qk); + int const side_qk_p1 = side.qk_wrt_p1(); // If Qi and Qk are both at same side of Pi-Pj, // or collinear (so: not opposite sides) if (! opposite(side_qi_p1, side_qk_p1)) { - int const side_pk_q2 = SideStrategy::apply(qj, qk, pk); - int const side_pk_p = SideStrategy::apply(pi, pj, pk); - int const side_qk_q = SideStrategy::apply(qi, qj, qk); - - bool const both_continue = side_pk_p == 0 && side_qk_q == 0; - bool const robustness_issue_in_continue = both_continue && side_pk_q2 != 0; + int const side_pk_q2 = side.pk_wrt_q2(); + int const side_pk_p = side.pk_wrt_p1(); + int const side_qk_q = side.qk_wrt_q1(); bool const q_turns_left = side_qk_q == 1; bool const block_q = side_qk_p1 == 0 && ! same(side_qi_p1, side_qk_q) - && ! robustness_issue_in_continue ; // If Pk at same side as Qi/Qk @@ -276,7 +301,7 @@ struct touch : public base_turn_handler return; } - int const side_pk_q1 = SideStrategy::apply(qi, qj, pk); + int const side_pk_q1 = side.pk_wrt_q1(); // Collinear opposite case -> block P @@ -329,9 +354,6 @@ struct touch : public base_turn_handler else { // Pk at other side than Qi/Pk - int const side_qk_q = SideStrategy::apply(qi, qj, qk); - bool const q_turns_left = side_qk_q == 1; - ti.operations[0].operation = q_turns_left ? operation_intersection : operation_union; @@ -347,13 +369,13 @@ struct touch : public base_turn_handler else { // From left to right or from right to left - int const side_pk_p = SideStrategy::apply(pi, pj, pk); + int const side_pk_p = side.pk_wrt_p1(); bool const right_to_left = side_qk_p1 == 1; // If p turns into direction of qi (1,2) if (side_pk_p == side_qi_p1) { - int const side_pk_q1 = SideStrategy::apply(qi, qj, pk); + int const side_pk_q1 = side.pk_wrt_q1(); // Collinear opposite case -> block P if (side_pk_q1 == 0) @@ -374,7 +396,7 @@ struct touch : public base_turn_handler // If p turns into direction of qk (4,5) if (side_pk_p == side_qk_p1) { - int const side_pk_q2 = SideStrategy::apply(qj, qk, pk); + int const side_pk_q2 = side.pk_wrt_q2(); // Collinear case -> lines join, continue if (side_pk_q2 == 0) @@ -413,8 +435,7 @@ struct touch : public base_turn_handler template < - typename TurnInfo, - typename SideStrategy + typename TurnInfo > struct equal : public base_turn_handler { @@ -423,22 +444,24 @@ struct equal : public base_turn_handler typename Point1, typename Point2, typename IntersectionInfo, - typename DirInfo + typename DirInfo, + typename SidePolicy > static inline void apply( - Point1 const& pi, Point1 const& pj, Point1 const& pk, - Point2 const& , Point2 const& qj, Point2 const& qk, + Point1 const& , Point1 const& , Point1 const& , + Point2 const& , Point2 const& , Point2 const& , TurnInfo& ti, - IntersectionInfo const& intersection_info, - DirInfo const& ) + IntersectionInfo const& info, + DirInfo const& , + SidePolicy const& side) { - ti.method = method_equal; - // Copy the SECOND intersection point - geometry::convert(intersection_info.intersections[1], ti.point); + // Copy the intersection point in TO direction + assign_point(ti, method_equal, info, non_opposite_to_index(info)); + + int const side_pk_q2 = side.pk_wrt_q2(); + int const side_pk_p = side.pk_wrt_p1(); + int const side_qk_p = side.qk_wrt_p1(); - int const side_pk_q2 = SideStrategy::apply(qj, qk, pk); - int const side_pk_p = SideStrategy::apply(pi, pj, pk); - int const side_qk_p = SideStrategy::apply(pi, pj, qk); // If pk is collinear with qj-qk, they continue collinearly. // This can be on either side of p1 (== q1), or collinear @@ -447,6 +470,7 @@ struct equal : public base_turn_handler if (side_pk_q2 == 0 && side_pk_p == side_qk_p) { both(ti, operation_continue); + return; } @@ -454,8 +478,6 @@ struct equal : public base_turn_handler // If they turn to same side (not opposite sides) if (! opposite(side_pk_p, side_qk_p)) { - int const side_pk_q2 = SideStrategy::apply(qj, qk, pk); - // If pk is left of q2 or collinear: p: union, q: intersection ui_else_iu(side_pk_q2 != -1, ti); } @@ -485,33 +507,32 @@ struct equal_opposite : public base_turn_handler typename DirInfo > static inline void apply(Point1 const& pi, Point2 const& qi, - /* by value: */ TurnInfo tp, + /* by value: */ TurnInfo tp, OutputIterator& out, IntersectionInfo const& intersection_info, DirInfo const& dir_info) { // For equal-opposite segments, normally don't do anything. - if (AssignPolicy::include_opposite) - { - tp.method = method_equal; - for (int i = 0; i < 2; i++) - { - tp.operations[i].operation = operation_opposite; - } - for (unsigned int i = 0; i < intersection_info.count; i++) - { - geometry::convert(intersection_info.intersections[i], tp.point); - AssignPolicy::apply(tp, pi, qi, intersection_info, dir_info); - *out++ = tp; - } - } + if (AssignPolicy::include_opposite) + { + tp.method = method_equal; + for (int i = 0; i < 2; i++) + { + tp.operations[i].operation = operation_opposite; + } + for (unsigned int i = 0; i < intersection_info.count; i++) + { + assign_point(tp, method_none, intersection_info, i); + AssignPolicy::apply(tp, pi, qi, intersection_info, dir_info); + *out++ = tp; + } + } } }; template < - typename TurnInfo, - typename SideStrategy + typename TurnInfo > struct collinear : public base_turn_handler { @@ -543,7 +564,7 @@ struct collinear : public base_turn_handler ROBUSTNESS: p and q are collinear, so you would expect that side qk//p1 == pk//q1. But that is not always the case in near-epsilon ranges. Then decision logic is different. - If p arrives, q is further, so the angle qk//p1 is (normally) + If p arrives, q is further, so the angle qk//p1 is (normally) more precise than pk//p1 */ @@ -552,24 +573,26 @@ struct collinear : public base_turn_handler typename Point1, typename Point2, typename IntersectionInfo, - typename DirInfo + typename DirInfo, + typename SidePolicy > static inline void apply( - Point1 const& pi, Point1 const& pj, Point1 const& pk, - Point2 const& qi, Point2 const& qj, Point2 const& qk, + Point1 const& , Point1 const& , Point1 const& , + Point2 const& , Point2 const& , Point2 const& , TurnInfo& ti, - IntersectionInfo const& intersection_info, - DirInfo const& dir_info) + IntersectionInfo const& info, + DirInfo const& dir_info, + SidePolicy const& side) { - ti.method = method_collinear; - geometry::convert(intersection_info.intersections[1], ti.point); + // Copy the intersection point in TO direction + assign_point(ti, method_collinear, info, non_opposite_to_index(info)); int const arrival = dir_info.arrival[0]; // Should not be 0, this is checked before BOOST_ASSERT(arrival != 0); - int const side_p = SideStrategy::apply(pi, pj, pk); - int const side_q = SideStrategy::apply(qi, qj, qk); + int const side_p = side.pk_wrt_p1(); + int const side_q = side.qk_wrt_q1(); // If p arrives, use p, else use q int const side_p_or_q = arrival == 1 @@ -577,9 +600,6 @@ struct collinear : public base_turn_handler : side_q ; - int const side_pk = SideStrategy::apply(qi, qj, pk); - int const side_qk = SideStrategy::apply(pi, pj, qk); - // See comments above, // resulting in a strange sort of mathematic rule here: // The arrival-info multiplied by the relevant side @@ -587,15 +607,7 @@ struct collinear : public base_turn_handler int const product = arrival * side_p_or_q; - // Robustness: side_p is supposed to be equal to side_pk (because p/q are collinear) - // and side_q to side_qk - bool const robustness_issue = side_pk != side_p || side_qk != side_q; - - if (robustness_issue) - { - handle_robustness(ti, arrival, side_p, side_q, side_pk, side_qk); - } - else if(product == 0) + if(product == 0) { both(ti, operation_continue); } @@ -605,43 +617,11 @@ struct collinear : public base_turn_handler } } - static inline void handle_robustness(TurnInfo& ti, int arrival, - int side_p, int side_q, int side_pk, int side_qk) - { - // We take the longer one, i.e. if q arrives in p (arrival == -1), - // then p exceeds q and we should take p for a union... - - bool use_p_for_union = arrival == -1; - - // ... unless one of the sides consistently directs to the other side - int const consistent_side_p = side_p == side_pk ? side_p : 0; - int const consistent_side_q = side_q == side_qk ? side_q : 0; - if (arrival == -1 && (consistent_side_p == -1 || consistent_side_q == 1)) - { - use_p_for_union = false; - } - if (arrival == 1 && (consistent_side_p == 1 || consistent_side_q == -1)) - { - use_p_for_union = true; - } - - //std::cout << "ROBUSTNESS -> Collinear " - // << " arr: " << arrival - // << " dir: " << side_p << " " << side_q - // << " rev: " << side_pk << " " << side_qk - // << " cst: " << cside_p << " " << cside_q - // << std::boolalpha << " " << use_p_for_union - // << std::endl; - - ui_else_iu(use_p_for_union, ti); - } - }; template < typename TurnInfo, - typename SideStrategy, typename AssignPolicy > struct collinear_opposite : public base_turn_handler @@ -674,14 +654,19 @@ private : template < int Index, - typename Point, + typename Point1, + typename Point2, typename IntersectionInfo > - static inline bool set_tp(Point const& ri, Point const& rj, Point const& rk, + static inline bool set_tp(Point1 const& , Point1 const& , Point1 const& , int side_rk_r, + bool const handle_robustness, + Point2 const& , Point2 const& , int side_rk_s, TurnInfo& tp, IntersectionInfo const& intersection_info) { - int const side_rk_r = SideStrategy::apply(ri, rj, rk); - operation_type blocked = operation_blocked; + boost::ignore_unused_variable_warning(handle_robustness); + boost::ignore_unused_variable_warning(side_rk_s); + + operation_type blocked = operation_blocked; switch(side_rk_r) { @@ -699,16 +684,16 @@ private : // two operations blocked, so the whole point does not need // to be generated. // So return false to indicate nothing is to be done. - if (AssignPolicy::include_opposite) - { - tp.operations[Index].operation = operation_opposite; - blocked = operation_opposite; - } - else - { - return false; - } - break; + if (AssignPolicy::include_opposite) + { + tp.operations[Index].operation = operation_opposite; + blocked = operation_opposite; + } + else + { + return false; + } + break; } // The other direction is always blocked when collinear opposite @@ -717,18 +702,21 @@ private : // If P arrives within Q, set info on P (which is done above, index=0), // this turn-info belongs to the second intersection point, index=1 // (see e.g. figure CLO1) - geometry::convert(intersection_info.intersections[1 - Index], tp.point); + assign_point(tp, method_collinear, intersection_info, 1 - Index); return true; } public: + static inline void empty_transformer(TurnInfo &) {} + template < typename Point1, typename Point2, typename OutputIterator, typename IntersectionInfo, - typename DirInfo + typename DirInfo, + typename SidePolicy > static inline void apply( Point1 const& pi, Point1 const& pj, Point1 const& pk, @@ -739,46 +727,79 @@ public: OutputIterator& out, IntersectionInfo const& intersection_info, - DirInfo const& dir_info) + DirInfo const& dir_info, + SidePolicy const& side) { - TurnInfo tp = tp_model; + apply(pi, pj, pk, qi, qj, qk, tp_model, out, intersection_info, dir_info, side, empty_transformer); + } - tp.method = method_collinear; +public: + template + < + typename Point1, + typename Point2, + typename OutputIterator, + typename IntersectionInfo, + typename DirInfo, + typename SidePolicy, + typename TurnTransformer + > + static inline void apply( + Point1 const& pi, Point1 const& pj, Point1 const& pk, + Point2 const& qi, Point2 const& qj, Point2 const& qk, + + // Opposite collinear can deliver 2 intersection points, + TurnInfo const& tp_model, + OutputIterator& out, + + IntersectionInfo const& intersection_info, + DirInfo const& dir_info, + SidePolicy const& side, + TurnTransformer turn_transformer, + bool const is_pk_valid = true, bool const is_qk_valid = true) + { + TurnInfo tp = tp_model; // If P arrives within Q, there is a turn dependent on P - if (dir_info.arrival[0] == 1 - && set_tp<0>(pi, pj, pk, tp, intersection_info)) + if ( dir_info.arrival[0] == 1 + && is_pk_valid + && set_tp<0>(pi, pj, pk, side.pk_wrt_p1(), true, qi, qj, side.pk_wrt_q1(), tp, intersection_info) ) { + turn_transformer(tp); + AssignPolicy::apply(tp, pi, qi, intersection_info, dir_info); *out++ = tp; } // If Q arrives within P, there is a turn dependent on Q - if (dir_info.arrival[1] == 1 - && set_tp<1>(qi, qj, qk, tp, intersection_info)) + if ( dir_info.arrival[1] == 1 + && is_qk_valid + && set_tp<1>(qi, qj, qk, side.qk_wrt_q1(), false, pi, pj, side.qk_wrt_p1(), tp, intersection_info) ) { + turn_transformer(tp); + AssignPolicy::apply(tp, pi, qi, intersection_info, dir_info); *out++ = tp; } - if (AssignPolicy::include_opposite) - { - // Handle cases not yet handled above - if ((dir_info.arrival[1] == -1 && dir_info.arrival[0] == 0) - || (dir_info.arrival[0] == -1 && dir_info.arrival[1] == 0)) - { - for (int i = 0; i < 2; i++) - { - tp.operations[i].operation = operation_opposite; - } - for (unsigned int i = 0; i < intersection_info.count; i++) - { - geometry::convert(intersection_info.intersections[i], tp.point); - AssignPolicy::apply(tp, pi, qi, intersection_info, dir_info); - *out++ = tp; - } - } - } + if (AssignPolicy::include_opposite) + { + // Handle cases not yet handled above + if ((dir_info.arrival[1] == -1 && dir_info.arrival[0] == 0) + || (dir_info.arrival[0] == -1 && dir_info.arrival[1] == 0)) + { + for (int i = 0; i < 2; i++) + { + tp.operations[i].operation = operation_opposite; + } + for (unsigned int i = 0; i < intersection_info.count; i++) + { + assign_point(tp, method_collinear, intersection_info, i); + AssignPolicy::apply(tp, pi, qi, intersection_info, dir_info); + *out++ = tp; + } + } + } } }; @@ -786,8 +807,7 @@ public: template < - typename TurnInfo, - typename SideStrategy + typename TurnInfo > struct crosses : public base_turn_handler { @@ -805,8 +825,7 @@ struct crosses : public base_turn_handler IntersectionInfo const& intersection_info, DirInfo const& dir_info) { - ti.method = method_crosses; - geometry::convert(intersection_info.intersections[0], ti.point); + assign_point(ti, method_crosses, intersection_info, 0); // In all casees: // If Q crosses P from left to right @@ -820,14 +839,12 @@ struct crosses : public base_turn_handler } }; -template<typename TurnInfo> -struct only_convert +struct only_convert : public base_turn_handler { - template<typename IntersectionInfo> + template<typename TurnInfo, typename IntersectionInfo> static inline void apply(TurnInfo& ti, IntersectionInfo const& intersection_info) { - ti.method = method_collinear; - geometry::convert(intersection_info.intersections[0], ti.point); + assign_point(ti, method_none, intersection_info, 0); // was collinear ti.operations[0].operation = operation_continue; ti.operations[1].operation = operation_continue; } @@ -845,14 +862,14 @@ struct assign_null_policy static bool const include_degenerate = false; static bool const include_opposite = false; - template - < - typename Info, - typename Point1, - typename Point2, - typename IntersectionInfo, - typename DirInfo - > + template + < + typename Info, + typename Point1, + typename Point2, + typename IntersectionInfo, + typename DirInfo + > static inline void apply(Info& , Point1 const& , Point2 const&, IntersectionInfo const&, DirInfo const&) {} @@ -873,48 +890,39 @@ struct assign_null_policy It also defines if a certain class of points (degenerate, non-turns) should be included. */ -template -< - typename Point1, - typename Point2, - typename TurnInfo, - typename AssignPolicy -> +template<typename AssignPolicy> struct get_turn_info { - typedef strategy_intersection - < - typename cs_tag<typename TurnInfo::point_type>::type, - Point1, - Point2, - typename TurnInfo::point_type - > si; - - typedef typename si::segment_intersection_strategy_type strategy; - // Intersect pi-pj with qi-qj - // The points pk and qk are only used do determine more information - // about the turn. - template <typename OutputIterator> + // The points pk and qk are used do determine more information + // about the turn (turn left/right) + template + < + typename Point1, + typename Point2, + typename TurnInfo, + typename RobustPolicy, + typename OutputIterator + > static inline OutputIterator apply( Point1 const& pi, Point1 const& pj, Point1 const& pk, Point2 const& qi, Point2 const& qj, Point2 const& qk, + bool /*is_p_first*/, bool /*is_p_last*/, + bool /*is_q_first*/, bool /*is_q_last*/, TurnInfo const& tp_model, + RobustPolicy const& robust_policy, OutputIterator out) { - typedef model::referring_segment<Point1 const> segment_type1; - typedef model::referring_segment<Point1 const> segment_type2; - segment_type1 p1(pi, pj), p2(pj, pk); - segment_type2 q1(qi, qj), q2(qj, qk); + typedef intersection_info<Point1, Point2, typename TurnInfo::point_type, RobustPolicy> + inters_info; - typename strategy::return_type result = strategy::apply(p1, q1); + inters_info inters(pi, pj, pk, qi, qj, qk, robust_policy); - char const method = result.template get<1>().how; + char const method = inters.d_info().how; // Copy, to copy possibly extended fields TurnInfo tp = tp_model; - // Select method and apply switch(method) { @@ -922,11 +930,10 @@ struct get_turn_info case 'f' : // collinear, "from" case 's' : // starts from the middle if (AssignPolicy::include_no_turn - && result.template get<0>().count > 0) + && inters.i_info().count > 0) { - only_convert<TurnInfo>::apply(tp, - result.template get<0>()); - AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); + only_convert::apply(tp, inters.i_info()); + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); *out++ = tp; } break; @@ -938,113 +945,94 @@ struct get_turn_info { typedef touch_interior < - TurnInfo, - typename si::side_strategy_type + TurnInfo > policy; // If Q (1) arrives (1) - if (result.template get<1>().arrival[1] == 1) + if ( inters.d_info().arrival[1] == 1 ) { policy::template apply<0>(pi, pj, pk, qi, qj, qk, - tp, result.template get<0>(), result.template get<1>()); + tp, inters.i_info(), inters.d_info(), + inters.sides()); } else { // Swap p/q + side_calculator + < + typename inters_info::robust_point2_type, + typename inters_info::robust_point1_type + > swapped_side_calc(inters.rqi(), inters.rqj(), inters.rqk(), + inters.rpi(), inters.rpj(), inters.rpk()); policy::template apply<1>(qi, qj, qk, pi, pj, pk, - tp, result.template get<0>(), result.template get<1>()); + tp, inters.i_info(), inters.d_info(), + swapped_side_calc); } - AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); *out++ = tp; } break; case 'i' : { - typedef crosses - < - TurnInfo, - typename si::side_strategy_type - > policy; - - policy::apply(pi, pj, pk, qi, qj, qk, - tp, result.template get<0>(), result.template get<1>()); - AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); + crosses<TurnInfo>::apply(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info()); + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); *out++ = tp; } break; case 't' : { // Both touch (both arrive there) - typedef touch - < - TurnInfo, - typename si::side_strategy_type - > policy; - - policy::apply(pi, pj, pk, qi, qj, qk, - tp, result.template get<0>(), result.template get<1>()); - AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); + touch<TurnInfo>::apply(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info(), inters.sides()); + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); *out++ = tp; } break; case 'e': { - if (! result.template get<1>().opposite) + if ( ! inters.d_info().opposite ) { // Both equal // or collinear-and-ending at intersection point - typedef equal - < - TurnInfo, - typename si::side_strategy_type - > policy; - - policy::apply(pi, pj, pk, qi, qj, qk, - tp, result.template get<0>(), result.template get<1>()); - AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); + equal<TurnInfo>::apply(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info(), inters.sides()); + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); *out++ = tp; } - else - { + else + { equal_opposite < TurnInfo, AssignPolicy >::apply(pi, qi, - tp, out, result.template get<0>(), result.template get<1>()); - } + tp, out, inters.i_info(), inters.d_info()); + } } break; case 'c' : { // Collinear - if (! result.template get<1>().opposite) + if ( ! inters.d_info().opposite ) { - if (result.template get<1>().arrival[0] == 0) + if ( inters.d_info().arrival[0] == 0 ) { // Collinear, but similar thus handled as equal - equal - < - TurnInfo, - typename si::side_strategy_type - >::apply(pi, pj, pk, qi, qj, qk, - tp, result.template get<0>(), result.template get<1>()); + equal<TurnInfo>::apply(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info(), inters.sides()); // override assigned method tp.method = method_collinear; } else { - collinear - < - TurnInfo, - typename si::side_strategy_type - >::apply(pi, pj, pk, qi, qj, qk, - tp, result.template get<0>(), result.template get<1>()); + collinear<TurnInfo>::apply(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info(), inters.sides()); } - AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); *out++ = tp; } else @@ -1052,10 +1040,9 @@ struct get_turn_info collinear_opposite < TurnInfo, - typename si::side_strategy_type, AssignPolicy >::apply(pi, pj, pk, qi, qj, qk, - tp, out, result.template get<0>(), result.template get<1>()); + tp, out, inters.i_info(), inters.d_info(), inters.sides()); } } break; @@ -1064,14 +1051,17 @@ struct get_turn_info // degenerate points if (AssignPolicy::include_degenerate) { - only_convert<TurnInfo>::apply(tp, result.template get<0>()); - AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); + only_convert::apply(tp, inters.i_info()); + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); *out++ = tp; } } break; default : { +#if defined(BOOST_GEOMETRY_DEBUG_ROBUSTNESS) + std::cout << "TURN: Unknown method: " << method << std::endl; +#endif #if ! defined(BOOST_GEOMETRY_OVERLAY_NO_THROW) throw turn_info_exception(method); #endif @@ -1091,4 +1081,8 @@ struct get_turn_info }} // namespace boost::geometry +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_HPP diff --git a/boost/geometry/algorithms/detail/overlay/get_turn_info_for_endpoint.hpp b/boost/geometry/algorithms/detail/overlay/get_turn_info_for_endpoint.hpp new file mode 100644 index 0000000000..ca1b9d9d0c --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/get_turn_info_for_endpoint.hpp @@ -0,0 +1,657 @@ +// 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_OVERLAY_GET_TURN_INFO_FOR_ENDPOINT_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_FOR_ENDPOINT_HPP + +#include <boost/geometry/algorithms/detail/overlay/get_turn_info.hpp> +#include <boost/geometry/policies/robustness/no_rescale_policy.hpp> + +namespace boost { namespace geometry { + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay { + +// SEGMENT_INTERSECTION RESULT + +// C H0 H1 A0 A1 O IP1 IP2 + +// D0 and D1 == 0 + +// |--------> 2 0 0 0 0 F i/i x/x +// |--------> +// +// |--------> 2 0 0 0 0 T i/x x/i +// <--------| +// +// |-----> 1 0 0 0 0 T x/x +// <-----| +// + +// |---------> 2 0 0 0 1 T i/x x/i +// <----| +// +// |---------> 2 0 0 0 0 F i/i x/x +// |----> +// +// |---------> 2 0 0 -1 1 F i/i u/x +// |----> +// +// |---------> 2 0 0 -1 0 T i/x u/i +// <----| + +// |-------> 2 0 0 1 -1 F and i/i x/u +// |-------> 2 0 0 -1 1 F symetric i/i u/x +// |-------> +// +// |-------> 2 0 0 -1 -1 T i/u u/i +// <-------| +// +// |-------> 2 0 0 1 1 T i/x x/i +// <-------| +// +// |--------> 2 0 0 -1 1 F i/i u/x +// |----> +// +// |--------> 2 0 0 -1 1 T i/x u/i +// <----| + +// |-----> 1 -1 -1 -1 -1 T u/u +// <-----| +// +// |-----> 1 -1 0 -1 0 F and u/x +// |-----> 1 0 -1 0 -1 F symetric x/u +// |-----> + +// D0 or D1 != 0 + +// ^ +// | +// + 1 -1 1 -1 1 F and u/x (P is vertical) +// |--------> 1 1 -1 1 -1 F symetric x/u (P is horizontal) +// ^ +// | +// + +// +// + +// | +// v +// |--------> 1 1 1 1 1 F x/x (P is vertical) +// +// ^ +// | +// + +// |--------> 1 -1 -1 -1 -1 F u/u (P is vertical) +// +// ^ +// | +// + +// |--------> 1 0 -1 0 -1 F u/u (P is vertical) +// +// + +// | +// v +// |--------> 1 0 1 0 1 F u/x (P is vertical) +// + +class linear_intersections +{ +public: + template <typename Point1, typename Point2, typename IntersectionResult> + linear_intersections(Point1 const& pi, + Point2 const& qi, + IntersectionResult const& result, + bool is_p_last, bool is_q_last) + { + int arrival_a = result.template get<1>().arrival[0]; + int arrival_b = result.template get<1>().arrival[1]; + bool same_dirs = result.template get<1>().dir_a == 0 + && result.template get<1>().dir_b == 0; + + if ( same_dirs ) + { + if ( result.template get<0>().count == 2 ) + { + if ( ! result.template get<1>().opposite ) + { + ips[0].p_operation = operation_intersection; + ips[0].q_operation = operation_intersection; + ips[1].p_operation = union_or_blocked_same_dirs(arrival_a, is_p_last); + ips[1].q_operation = union_or_blocked_same_dirs(arrival_b, is_q_last); + + ips[0].is_pi + = equals::equals_point_point( + pi, result.template get<0>().intersections[0]); + ips[0].is_qi + = equals::equals_point_point( + qi, result.template get<0>().intersections[0]); + ips[1].is_pj = arrival_a != -1; + ips[1].is_qj = arrival_b != -1; + } + else + { + ips[0].p_operation = operation_intersection; + ips[0].q_operation = union_or_blocked_same_dirs(arrival_b, is_q_last); + ips[1].p_operation = union_or_blocked_same_dirs(arrival_a, is_p_last); + ips[1].q_operation = operation_intersection; + + ips[0].is_pi = arrival_b != 1; + ips[0].is_qj = arrival_b != -1; + ips[1].is_pj = arrival_a != -1; + ips[1].is_qi = arrival_a != 1; + } + } + else + { + BOOST_ASSERT(result.template get<0>().count == 1); + ips[0].p_operation = union_or_blocked_same_dirs(arrival_a, is_p_last); + ips[0].q_operation = union_or_blocked_same_dirs(arrival_b, is_q_last); + + ips[0].is_pi = arrival_a == -1; + ips[0].is_qi = arrival_b == -1; + ips[0].is_pj = arrival_a == 0; + ips[0].is_qj = arrival_b == 0; + } + } + else + { + ips[0].p_operation = union_or_blocked_different_dirs(arrival_a, is_p_last); + ips[0].q_operation = union_or_blocked_different_dirs(arrival_b, is_q_last); + + ips[0].is_pi = arrival_a == -1; + ips[0].is_qi = arrival_b == -1; + ips[0].is_pj = arrival_a == 1; + ips[0].is_qj = arrival_b == 1; + } + } + + struct ip_info + { + inline ip_info() + : p_operation(operation_none), q_operation(operation_none) + , is_pi(false), is_pj(false), is_qi(false), is_qj(false) + {} + + operation_type p_operation, q_operation; + bool is_pi, is_pj, is_qi, is_qj; + }; + + template <std::size_t I> + ip_info const& get() const + { + BOOST_STATIC_ASSERT(I < 2); + return ips[I]; + } + +private: + + // only if collinear (same_dirs) + static inline operation_type union_or_blocked_same_dirs(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 union_or_blocked_different_dirs(int arrival, bool is_last) + { + if ( arrival == 1 ) + //return operation_blocked; + return is_last ? operation_blocked : operation_union; + else + return operation_union; + } + + ip_info ips[2]; +}; + +template <typename AssignPolicy, bool EnableFirst, bool EnableLast> +struct get_turn_info_for_endpoint +{ + BOOST_STATIC_ASSERT(EnableFirst || EnableLast); + + template<typename Point1, + typename Point2, + typename TurnInfo, + typename IntersectionInfo, + typename OutputIterator + > + static inline bool apply(Point1 const& pi, Point1 const& pj, Point1 const& pk, + Point2 const& qi, Point2 const& qj, Point2 const& qk, + bool is_p_first, bool is_p_last, + bool is_q_first, bool is_q_last, + TurnInfo const& tp_model, + IntersectionInfo const& inters, + method_type /*method*/, + OutputIterator out) + { + std::size_t ip_count = inters.i_info().count; + // no intersection points + if ( ip_count == 0 ) + return false; + + if ( !is_p_first && !is_p_last && !is_q_first && !is_q_last ) + return false; + + linear_intersections intersections(pi, qi, inters.result(), is_p_last, is_q_last); + + bool append0_last + = analyse_segment_and_assign_ip(pi, pj, pk, qi, qj, qk, + is_p_first, is_p_last, is_q_first, is_q_last, + intersections.template get<0>(), + tp_model, inters, 0, out); + + // NOTE: opposite && ip_count == 1 may be true! + bool opposite = inters.d_info().opposite; + + // don't ignore only for collinear opposite + bool result_ignore_ip0 = append0_last && ( ip_count == 1 || !opposite ); + + if ( intersections.template get<1>().p_operation == operation_none ) + return result_ignore_ip0; + + bool append1_last + = analyse_segment_and_assign_ip(pi, pj, pk, qi, qj, qk, + is_p_first, is_p_last, is_q_first, is_q_last, + intersections.template get<1>(), + tp_model, inters, 1, out); + + // don't ignore only for collinear opposite + bool result_ignore_ip1 = append1_last && !opposite /*&& ip_count == 2*/; + + return result_ignore_ip0 || result_ignore_ip1; + } + + template <typename Point1, + typename Point2, + typename TurnInfo, + typename IntersectionInfo, + typename OutputIterator> + static inline + bool analyse_segment_and_assign_ip(Point1 const& pi, Point1 const& pj, Point1 const& pk, + Point2 const& qi, Point2 const& qj, Point2 const& qk, + bool is_p_first, bool is_p_last, + bool is_q_first, bool is_q_last, + linear_intersections::ip_info const& ip_info, + TurnInfo const& tp_model, + IntersectionInfo const& inters, + int ip_index, + OutputIterator out) + { +#ifdef BOOST_GEOMETRY_DEBUG_GET_TURNS_LINEAR_LINEAR + // may this give false positives for INTs? + typename IntersectionResult::point_type const& + inters_pt = result.template get<0>().intersections[ip_index]; + BOOST_ASSERT(ip_info.is_pi == equals::equals_point_point(pi, inters_pt)); + BOOST_ASSERT(ip_info.is_qi == equals::equals_point_point(qi, inters_pt)); + BOOST_ASSERT(ip_info.is_pj == equals::equals_point_point(pj, inters_pt)); + BOOST_ASSERT(ip_info.is_qj == equals::equals_point_point(qj, inters_pt)); +#endif + + // TODO - calculate first/last only if needed + bool is_p_first_ip = is_p_first && ip_info.is_pi; + bool is_p_last_ip = is_p_last && ip_info.is_pj; + bool is_q_first_ip = is_q_first && ip_info.is_qi; + bool is_q_last_ip = is_q_last && ip_info.is_qj; + bool append_first = EnableFirst && (is_p_first_ip || is_q_first_ip); + bool append_last = EnableLast && (is_p_last_ip || is_q_last_ip); + + operation_type p_operation = ip_info.p_operation; + operation_type q_operation = ip_info.q_operation; + + if ( append_first || append_last ) + { + bool handled = handle_internal<0>(pi, pj, pk, qi, qj, qk, + inters.rpi(), inters.rpj(), inters.rpk(), + inters.rqi(), inters.rqj(), inters.rqk(), + is_p_first_ip, is_p_last_ip, + is_q_first_ip, is_q_last_ip, + ip_info.is_qi, ip_info.is_qj, + tp_model, inters, ip_index, + p_operation, q_operation); + if ( !handled ) + { + handle_internal<1>(qi, qj, qk, pi, pj, pk, + inters.rqi(), inters.rqj(), inters.rqk(), + inters.rpi(), inters.rpj(), inters.rpk(), + is_q_first_ip, is_q_last_ip, + is_p_first_ip, is_p_last_ip, + ip_info.is_pi, ip_info.is_pj, + tp_model, inters, ip_index, + q_operation, p_operation); + } + + if ( p_operation != operation_none ) + { + method_type method = endpoint_ip_method(ip_info.is_pi, ip_info.is_pj, + ip_info.is_qi, ip_info.is_qj); + turn_position p_pos = ip_position(is_p_first_ip, is_p_last_ip); + turn_position q_pos = ip_position(is_q_first_ip, is_q_last_ip); + + // handle spikes + + // P is spike and should be handled + if ( !is_p_last + && ip_info.is_pj // this check is redundant (also in is_spike_p) but faster + && inters.i_info().count == 2 + && inters.is_spike_p() ) + { + assign(pi, qi, inters.result(), ip_index, method, operation_blocked, q_operation, + p_pos, q_pos, is_p_first_ip, is_q_first_ip, true, false, tp_model, out); + assign(pi, qi, inters.result(), ip_index, method, operation_intersection, q_operation, + p_pos, q_pos, is_p_first_ip, is_q_first_ip, true, false, tp_model, out); + } + // Q is spike and should be handled + else if ( !is_q_last + && ip_info.is_qj // this check is redundant (also in is_spike_q) but faster + && inters.i_info().count == 2 + && inters.is_spike_q() ) + { + assign(pi, qi, inters.result(), ip_index, method, p_operation, operation_blocked, + p_pos, q_pos, is_p_first_ip, is_q_first_ip, false, true, tp_model, out); + assign(pi, qi, inters.result(), ip_index, method, p_operation, operation_intersection, + p_pos, q_pos, is_p_first_ip, is_q_first_ip, false, true, tp_model, out); + } + // no spikes + else + { + assign(pi, qi, inters.result(), ip_index, method, p_operation, q_operation, + p_pos, q_pos, is_p_first_ip, is_q_first_ip, false, false, tp_model, out); + } + } + } + + return append_last; + } + + // TODO: IT'S ALSO PROBABLE THAT ALL THIS FUNCTION COULD BE INTEGRATED WITH handle_segment + // however now it's lazily calculated and then it would be always calculated + + template<std::size_t G1Index, + typename Point1, + typename Point2, + typename RobustPoint1, + typename RobustPoint2, + typename TurnInfo, + typename IntersectionInfo + > + static inline bool handle_internal(Point1 const& /*i1*/, Point1 const& /*j1*/, Point1 const& /*k1*/, + Point2 const& i2, Point2 const& j2, Point2 const& /*k2*/, + RobustPoint1 const& ri1, RobustPoint1 const& rj1, RobustPoint1 const& /*rk1*/, + RobustPoint2 const& ri2, RobustPoint2 const& rj2, RobustPoint2 const& rk2, + bool first1, bool last1, bool first2, bool last2, + bool ip_i2, bool ip_j2, TurnInfo const& tp_model, + IntersectionInfo const& inters, int ip_index, + operation_type & op1, operation_type & op2) + { + boost::ignore_unused_variable_warning(i2); + boost::ignore_unused_variable_warning(j2); + boost::ignore_unused_variable_warning(ip_index); + boost::ignore_unused_variable_warning(tp_model); + + if ( !first2 && !last2 ) + { + if ( first1 ) + { +#ifdef BOOST_GEOMETRY_DEBUG_GET_TURNS_LINEAR_LINEAR + // may this give false positives for INTs? + typename IntersectionResult::point_type const& + inters_pt = inters.i_info().intersections[ip_index]; + BOOST_ASSERT(ip_i2 == equals::equals_point_point(i2, inters_pt)); + BOOST_ASSERT(ip_j2 == equals::equals_point_point(j2, inters_pt)); +#endif + 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 ) + { + side_calculator<RobustPoint1, RobustPoint2, RobustPoint2> + side_calc(ri2, ri1, rj1, ri2, rj2, rk2); + + std::pair<operation_type, operation_type> + operations = operations_of_equal(side_calc); + +// TODO: must the above be calculated? +// wouldn't it be enough to check if segments are collinear? + + if ( operations_both(operations, operation_continue) ) + { + if ( op1 != operation_union + || op2 != operation_union + || ! ( G1Index == 0 ? inters.is_spike_q() : inters.is_spike_p() ) ) + { + // THIS IS WRT THE ORIGINAL SEGMENTS! NOT THE ONES ABOVE! + bool opposite = inters.d_info().opposite; + + op1 = operation_intersection; + op2 = opposite ? operation_union : operation_intersection; + } + } + else + { + BOOST_ASSERT(operations_combination(operations, operation_intersection, operation_union)); + //op1 = operation_union; + //op2 = operation_union; + } + + return true; + } + // else do nothing - shouldn't be handled this way + } + else if ( last1 ) + { +#ifdef BOOST_GEOMETRY_DEBUG_GET_TURNS_LINEAR_LINEAR + // may this give false positives for INTs? + typename IntersectionResult::point_type const& + inters_pt = inters.i_info().intersections[ip_index]; + BOOST_ASSERT(ip_i2 == equals::equals_point_point(i2, inters_pt)); + BOOST_ASSERT(ip_j2 == equals::equals_point_point(j2, inters_pt)); +#endif + 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 ) + { + side_calculator<RobustPoint1, RobustPoint2, RobustPoint2> + side_calc(ri2, rj1, ri1, ri2, rj2, rk2); + + std::pair<operation_type, operation_type> + operations = operations_of_equal(side_calc); + +// TODO: must the above be calculated? +// wouldn't it be enough to check if segments are collinear? + + if ( operations_both(operations, operation_continue) ) + { + if ( op1 != operation_blocked + || op2 != operation_union + || ! ( G1Index == 0 ? inters.is_spike_q() : inters.is_spike_p() ) ) + { + // THIS IS WRT THE ORIGINAL SEGMENTS! NOT THE ONES ABOVE! + bool second_going_out = inters.i_info().count > 1; + + op1 = operation_blocked; + op2 = second_going_out ? operation_union : operation_intersection; + } + } + else + { + BOOST_ASSERT(operations_combination(operations, 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) + { + if ( (ip_pi || ip_pj) && (ip_qi || ip_qj) ) + return method_touch; + else + return method_touch_interior; + } + + static inline turn_position ip_position(bool is_ip_first_i, bool is_ip_last_j) + { + return is_ip_first_i ? position_front : + ( is_ip_last_j ? position_back : position_middle ); + } + + template <typename Point1, + typename Point2, + typename IntersectionResult, + typename TurnInfo, + typename OutputIterator> + static inline void assign(Point1 const& pi, Point2 const& qi, + IntersectionResult const& result, + int ip_index, + method_type method, + operation_type op0, operation_type op1, + turn_position pos0, turn_position pos1, + bool is_p_first_ip, bool is_q_first_ip, + bool is_p_spike, bool is_q_spike, + TurnInfo const& tp_model, + OutputIterator out) + { + TurnInfo tp = tp_model; + + //geometry::convert(ip, tp.point); + //tp.method = method; + base_turn_handler::assign_point(tp, method, result.template get<0>(), ip_index); + + tp.operations[0].operation = op0; + tp.operations[1].operation = op1; + tp.operations[0].position = pos0; + tp.operations[1].position = pos1; + + if ( result.template get<0>().count > 1 ) + { + // NOTE: is_collinear is NOT set for the first endpoint + // for which there is no preceding segment + + //BOOST_ASSERT( result.template get<1>().dir_a == 0 && result.template get<1>().dir_b == 0 ); + if ( ! is_p_first_ip ) + { + tp.operations[0].is_collinear = op0 != operation_intersection + || is_p_spike; + } + + if ( ! is_q_first_ip ) + { + tp.operations[1].is_collinear = op1 != operation_intersection + || is_q_spike; + } + } + else //if ( result.template get<0>().count == 1 ) + { + if ( op0 == operation_blocked && op1 == operation_intersection ) + { + tp.operations[0].is_collinear = true; + } + else if ( op0 == operation_intersection && op1 == operation_blocked ) + { + tp.operations[1].is_collinear = true; + } + } + + AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); + *out++ = tp; + } + + template <typename SidePolicy> + static inline std::pair<operation_type, operation_type> operations_of_equal(SidePolicy const& side) + { + int const side_pk_q2 = side.pk_wrt_q2(); + int const side_pk_p = side.pk_wrt_p1(); + int const side_qk_p = side.qk_wrt_p1(); + + // If pk is collinear with qj-qk, they continue collinearly. + // This can be on either side of p1 (== q1), or collinear + // The second condition checks if they do not continue + // oppositely + if ( side_pk_q2 == 0 && side_pk_p == side_qk_p ) + { + return std::make_pair(operation_continue, operation_continue); + } + + // If they turn to same side (not opposite sides) + if ( ! base_turn_handler::opposite(side_pk_p, side_qk_p) ) + { + // If pk is left of q2 or collinear: p: union, q: intersection + if ( side_pk_q2 != -1 ) + { + return std::make_pair(operation_union, operation_intersection); + } + else + { + return std::make_pair(operation_intersection, operation_union); + } + } + else + { + // They turn opposite sides. If p turns left (or collinear), + // p: union, q: intersection + if ( side_pk_p != -1 ) + { + return std::make_pair(operation_union, operation_intersection); + } + else + { + return std::make_pair(operation_intersection, operation_union); + } + } + } + + static inline bool operations_both( + std::pair<operation_type, operation_type> const& operations, + operation_type const op) + { + return operations.first == op && operations.second == op; + } + + static inline bool operations_combination( + std::pair<operation_type, operation_type> const& operations, + operation_type const op1, operation_type const op2) + { + return ( operations.first == op1 && operations.second == op2 ) + || ( operations.first == op2 && operations.second == op1 ); + } +}; + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_FOR_ENDPOINT_HPP diff --git a/boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp b/boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp new file mode 100644 index 0000000000..eead0d719b --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp @@ -0,0 +1,329 @@ +// 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_OVERLAY_GET_TURN_INFO_HELPERS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_HELPERS_HPP + +#include <boost/geometry/policies/robustness/no_rescale_policy.hpp> + +namespace boost { namespace geometry { + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay { + +enum turn_position { position_middle, position_front, position_back }; + +template <typename SegmentRatio> +struct turn_operation_linear + : public turn_operation<SegmentRatio> +{ + turn_operation_linear() + : position(position_middle) + , is_collinear(false) + {} + + turn_position position; + bool is_collinear; // valid only for Linear geometry +}; + +template <typename PointP, typename PointQ, + typename Pi = PointP, typename Pj = PointP, typename Pk = PointP, + typename Qi = PointQ, typename Qj = PointQ, typename Qk = PointQ +> +struct side_calculator +{ + typedef boost::geometry::strategy::side::side_by_triangle<> side; // todo: get from coordinate system + + inline side_calculator(Pi const& pi, Pj const& pj, Pk const& pk, + Qi const& qi, Qj const& qj, Qk const& qk) + : m_pi(pi), m_pj(pj), m_pk(pk) + , m_qi(qi), m_qj(qj), m_qk(qk) + {} + + inline int pk_wrt_p1() const { return side::apply(m_pi, m_pj, m_pk); } + inline int pk_wrt_q1() const { return side::apply(m_qi, m_qj, m_pk); } + inline int qk_wrt_p1() const { return side::apply(m_pi, m_pj, m_qk); } + inline int qk_wrt_q1() const { return side::apply(m_qi, m_qj, m_qk); } + + inline int pk_wrt_q2() const { return side::apply(m_qj, m_qk, m_pk); } + inline int qk_wrt_p2() const { return side::apply(m_pj, m_pk, m_qk); } + + Pi const& m_pi; + Pj const& m_pj; + Pk const& m_pk; + Qi const& m_qi; + Qj const& m_qj; + Qk const& m_qk; +}; + +template <typename Point1, typename Point2, typename RobustPolicy> +struct robust_points +{ + typedef typename geometry::robust_point_type + < + Point1, RobustPolicy + >::type robust_point1_type; + // TODO: define robust_point2_type using Point2? + typedef robust_point1_type robust_point2_type; + + inline robust_points(Point1 const& pi, Point1 const& pj, Point1 const& pk, + Point2 const& qi, Point2 const& qj, Point2 const& qk, + RobustPolicy const& robust_policy) + { + geometry::recalculate(m_rpi, pi, robust_policy); + geometry::recalculate(m_rpj, pj, robust_policy); + geometry::recalculate(m_rpk, pk, robust_policy); + geometry::recalculate(m_rqi, qi, robust_policy); + geometry::recalculate(m_rqj, qj, robust_policy); + geometry::recalculate(m_rqk, qk, robust_policy); + } + + robust_point1_type m_rpi, m_rpj, m_rpk; + robust_point2_type m_rqi, m_rqj, m_rqk; +}; + +template <typename Point1, typename Point2, typename RobustPolicy> +class intersection_info_base + : private robust_points<Point1, Point2, RobustPolicy> +{ + typedef robust_points<Point1, Point2, RobustPolicy> base_t; + +public: + typedef Point1 point1_type; + typedef Point2 point2_type; + + typedef typename base_t::robust_point1_type robust_point1_type; + typedef typename base_t::robust_point2_type robust_point2_type; + + typedef side_calculator<robust_point1_type, robust_point2_type> side_calculator_type; + + intersection_info_base(Point1 const& pi, Point1 const& pj, Point1 const& pk, + Point2 const& qi, Point2 const& qj, Point2 const& qk, + RobustPolicy const& robust_policy) + : base_t(pi, pj, pk, qi, qj, qk, robust_policy) + , m_side_calc(base_t::m_rpi, base_t::m_rpj, base_t::m_rpk, + base_t::m_rqi, base_t::m_rqj, base_t::m_rqk) + , m_pi(pi), m_pj(pj), m_pk(pk) + , m_qi(qi), m_qj(qj), m_qk(qk) + {} + + inline Point1 const& pi() const { return m_pi; } + inline Point1 const& pj() const { return m_pj; } + inline Point1 const& pk() const { return m_pk; } + + inline Point2 const& qi() const { return m_qi; } + inline Point2 const& qj() const { return m_qj; } + inline Point2 const& qk() const { return m_qk; } + + inline robust_point1_type const& rpi() const { return base_t::m_rpi; } + inline robust_point1_type const& rpj() const { return base_t::m_rpj; } + inline robust_point1_type const& rpk() const { return base_t::m_rpk; } + + inline robust_point2_type const& rqi() const { return base_t::m_rqi; } + inline robust_point2_type const& rqj() const { return base_t::m_rqj; } + inline robust_point2_type const& rqk() const { return base_t::m_rqk; } + + inline side_calculator_type const& sides() const { return m_side_calc; } + +private: + side_calculator_type m_side_calc; + + point1_type const& m_pi; + point1_type const& m_pj; + point1_type const& m_pk; + point2_type const& m_qi; + point2_type const& m_qj; + point2_type const& m_qk; +}; + +template <typename Point1, typename Point2> +class intersection_info_base<Point1, Point2, detail::no_rescale_policy> +{ +public: + typedef Point1 point1_type; + typedef Point2 point2_type; + + typedef Point1 robust_point1_type; + typedef Point2 robust_point2_type; + + typedef side_calculator<Point1, Point2> side_calculator_type; + + intersection_info_base(Point1 const& pi, Point1 const& pj, Point1 const& pk, + Point2 const& qi, Point2 const& qj, Point2 const& qk, + no_rescale_policy const& /*robust_policy*/) + : m_side_calc(pi, pj, pk, qi, qj, qk) + {} + + inline Point1 const& pi() const { return m_side_calc.m_pi; } + inline Point1 const& pj() const { return m_side_calc.m_pj; } + inline Point1 const& pk() const { return m_side_calc.m_pk; } + + inline Point2 const& qi() const { return m_side_calc.m_qi; } + inline Point2 const& qj() const { return m_side_calc.m_qj; } + inline Point2 const& qk() const { return m_side_calc.m_qk; } + + inline Point1 const& rpi() const { return pi(); } + inline Point1 const& rpj() const { return pj(); } + inline Point1 const& rpk() const { return pk(); } + + inline Point2 const& rqi() const { return qi(); } + inline Point2 const& rqj() const { return qj(); } + inline Point2 const& rqk() const { return qk(); } + + inline side_calculator_type const& sides() const { return m_side_calc; } + +private: + side_calculator_type m_side_calc; +}; + + +template <typename Point1, typename Point2, typename TurnPoint, typename RobustPolicy> +class intersection_info + : public intersection_info_base<Point1, Point2, RobustPolicy> +{ + typedef intersection_info_base<Point1, Point2, RobustPolicy> base_t; + + typedef typename strategy_intersection + < + typename cs_tag<TurnPoint>::type, + Point1, + Point2, + TurnPoint, + RobustPolicy + >::segment_intersection_strategy_type strategy; + +public: + typedef model::referring_segment<Point1 const> segment_type1; + typedef model::referring_segment<Point2 const> segment_type2; + typedef typename base_t::side_calculator_type side_calculator_type; + + typedef typename strategy::return_type result_type; + typedef typename boost::tuples::element<0, result_type>::type i_info_type; // intersection_info + typedef typename boost::tuples::element<1, result_type>::type d_info_type; // dir_info + + intersection_info(Point1 const& pi, Point1 const& pj, Point1 const& pk, + Point2 const& qi, Point2 const& qj, Point2 const& qk, + RobustPolicy const& robust_policy) + : base_t(pi, pj, pk, qi, qj, qk, robust_policy) + , m_result(strategy::apply(segment_type1(pi, pj), + segment_type2(qi, qj), + robust_policy)) + , m_robust_policy(robust_policy) + {} + + inline result_type const& result() const { return m_result; } + inline i_info_type const& i_info() const { return m_result.template get<0>(); } + inline d_info_type const& d_info() const { return m_result.template get<1>(); } + + // TODO: it's more like is_spike_ip_p + inline bool is_spike_p() const + { + if ( base_t::sides().pk_wrt_p1() == 0 ) + { + if ( ! is_ip_j<0>() ) + return false; + + int const qk_p1 = base_t::sides().qk_wrt_p1(); + int const qk_p2 = base_t::sides().qk_wrt_p2(); + + if ( qk_p1 == -qk_p2 ) + { + if ( qk_p1 == 0 ) + { + return is_spike_of_collinear(base_t::pi(), base_t::pj(), base_t::pk()); + } + + return true; + } + } + + return false; + } + + // TODO: it's more like is_spike_ip_q + inline bool is_spike_q() const + { + if ( base_t::sides().qk_wrt_q1() == 0 ) + { + if ( ! is_ip_j<1>() ) + return false; + + int const pk_q1 = base_t::sides().pk_wrt_q1(); + int const pk_q2 = base_t::sides().pk_wrt_q2(); + + if ( pk_q1 == -pk_q2 ) + { + if ( pk_q1 == 0 ) + { + return is_spike_of_collinear(base_t::qi(), base_t::qj(), base_t::qk()); + } + + return true; + } + } + + return false; + } + +private: + template <typename Point> + inline bool is_spike_of_collinear(Point const& i, Point const& j, Point const& k) const + { + typedef model::referring_segment<Point const> seg_t; + + typedef strategy_intersection + < + typename cs_tag<Point>::type, Point, Point, Point, RobustPolicy + > si; + + typedef typename si::segment_intersection_strategy_type strategy; + + typename strategy::return_type result + = strategy::apply(seg_t(i, j), seg_t(j, k), m_robust_policy); + + return result.template get<0>().count == 2; + } + + template <std::size_t OpId> + bool is_ip_j() const + { + int arrival = d_info().arrival[OpId]; + bool same_dirs = d_info().dir_a == 0 && d_info().dir_b == 0; + + if ( same_dirs ) + { + if ( i_info().count == 2 ) + { + return arrival != -1; + } + else + { + return arrival == 0; + } + } + else + { + return arrival == 1; + } + } + + result_type m_result; + RobustPolicy const& m_robust_policy; +}; + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_HELPERS_HPP diff --git a/boost/geometry/algorithms/detail/overlay/get_turn_info_la.hpp b/boost/geometry/algorithms/detail/overlay/get_turn_info_la.hpp new file mode 100644 index 0000000000..873567bbf5 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/get_turn_info_la.hpp @@ -0,0 +1,805 @@ +// 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_OVERLAY_GET_TURN_INFO_LA_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_LA_HPP + +#include <boost/geometry/algorithms/detail/overlay/get_turn_info.hpp> +#include <boost/geometry/algorithms/detail/overlay/get_turn_info_for_endpoint.hpp> + +// TEMP, for spikes detector +//#include <boost/geometry/algorithms/detail/overlay/get_turn_info_ll.hpp> + +namespace boost { namespace geometry { + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay { + +template<typename AssignPolicy> +struct get_turn_info_linear_areal +{ + static const bool handle_spikes = true; + + template + < + typename Point1, + typename Point2, + typename TurnInfo, + typename RobustPolicy, + typename OutputIterator + > + static inline OutputIterator apply( + Point1 const& pi, Point1 const& pj, Point1 const& pk, + Point2 const& qi, Point2 const& qj, Point2 const& qk, + bool is_p_first, bool is_p_last, + bool is_q_first, bool is_q_last, + TurnInfo const& tp_model, + RobustPolicy const& robust_policy, + OutputIterator out) + { + typedef intersection_info<Point1, Point2, typename TurnInfo::point_type, RobustPolicy> + inters_info; + + inters_info inters(pi, pj, pk, qi, qj, qk, robust_policy); + + char const method = inters.d_info().how; + + // Copy, to copy possibly extended fields + TurnInfo tp = tp_model; + + // Select method and apply + switch(method) + { + case 'a' : // collinear, "at" + case 'f' : // collinear, "from" + case 's' : // starts from the middle + get_turn_info_for_endpoint<true, true>( + pi, pj, pk, qi, qj, qk, + is_p_first, is_p_last, is_q_first, is_q_last, + tp_model, inters, method_none, out); + break; + + case 'd' : // disjoint: never do anything + break; + + case 'm' : + { + if ( get_turn_info_for_endpoint<false, true>( + pi, pj, pk, qi, qj, qk, + is_p_first, is_p_last, is_q_first, is_q_last, + tp_model, inters, method_touch_interior, out) ) + { + // do nothing + } + else + { + typedef touch_interior + < + TurnInfo + > policy; + + // If Q (1) arrives (1) + if ( inters.d_info().arrival[1] == 1 ) + { + policy::template apply<0>(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info(), + inters.sides()); + } + else + { + // Swap p/q + side_calculator + < + typename inters_info::robust_point2_type, + typename inters_info::robust_point1_type + > swapped_side_calc(inters.rqi(), inters.rqj(), inters.rqk(), + inters.rpi(), inters.rpj(), inters.rpk()); + policy::template apply<1>(qi, qj, qk, pi, pj, pk, + tp, inters.i_info(), inters.d_info(), + swapped_side_calc); + } + + if ( tp.operations[1].operation == operation_blocked ) + { + tp.operations[0].is_collinear = true; + } + + replace_method_and_operations_tm(tp.method, + tp.operations[0].operation, + tp.operations[1].operation); + + // this function assumes that 'u' must be set for a spike + calculate_spike_operation(tp.operations[0].operation, + inters, is_p_last); + + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + + *out++ = tp; + } + } + break; + case 'i' : + { + crosses<TurnInfo>::apply(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info()); + + replace_operations_i(tp.operations[0].operation, tp.operations[1].operation); + + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + *out++ = tp; + } + break; + case 't' : + { + // Both touch (both arrive there) + if ( get_turn_info_for_endpoint<false, true>( + pi, pj, pk, qi, qj, qk, + is_p_first, is_p_last, is_q_first, is_q_last, + tp_model, inters, method_touch, out) ) + { + // do nothing + } + else + { + touch<TurnInfo>::apply(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info(), inters.sides()); + + if ( tp.operations[1].operation == operation_blocked ) + { + tp.operations[0].is_collinear = true; + } + + // workarounds for touch<> not taking spikes into account starts here + // those was discovered empirically + // touch<> is not symmetrical! + // P spikes and Q spikes may produce various operations! + // Only P spikes are valid for L/A + // TODO: this is not optimal solution - think about rewriting touch<> + + if ( tp.operations[0].operation == operation_blocked ) + { + // a spike on P on the same line with Q1 + if ( inters.is_spike_p() ) + { + if ( inters.sides().qk_wrt_p1() == 0 ) + { + tp.operations[0].is_collinear = true; + } + else + { + tp.operations[0].operation = operation_union; + } + } + } + else if ( tp.operations[0].operation == operation_continue + && tp.operations[1].operation == operation_continue ) + { + // P spike on the same line with Q2 (opposite) + if ( inters.sides().pk_wrt_q1() == -inters.sides().qk_wrt_q1() + && inters.is_spike_p() ) + { + tp.operations[0].operation = operation_union; + tp.operations[1].operation = operation_union; + } + } + else if ( tp.operations[0].operation == operation_none + && tp.operations[1].operation == operation_none ) + { + // spike not handled by touch<> + if ( inters.is_spike_p() ) + { + tp.operations[0].operation = operation_intersection; + tp.operations[1].operation = operation_union; + + if ( inters.sides().pk_wrt_q2() == 0 ) + { + tp.operations[0].operation = operation_continue; // will be converted to i + tp.operations[0].is_collinear = true; + } + } + } + + // workarounds for touch<> not taking spikes into account ends here + + replace_method_and_operations_tm(tp.method, + tp.operations[0].operation, + tp.operations[1].operation); + + bool ignore_spike + = calculate_spike_operation(tp.operations[0].operation, + inters, is_p_last); + +// TODO: move this into the append_xxx and call for each turn? + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + + if ( ! handle_spikes + || ignore_spike + || ! append_opposite_spikes<append_touches>( // for 'i' or 'c' i??? + tp, inters, is_p_last, is_q_last, out) ) + { + *out++ = tp; + } + } + } + break; + case 'e': + { + if ( get_turn_info_for_endpoint<true, true>( + pi, pj, pk, qi, qj, qk, + is_p_first, is_p_last, is_q_first, is_q_last, + tp_model, inters, method_equal, out) ) + { + // do nothing + } + else + { + tp.operations[0].is_collinear = true; + + if ( ! inters.d_info().opposite ) + { + // Both equal + // or collinear-and-ending at intersection point + equal<TurnInfo>::apply(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info(), inters.sides()); + + turn_transformer_ec<false> transformer(method_touch); + transformer(tp); + +// TODO: move this into the append_xxx and call for each turn? + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + + // conditionally handle spikes + if ( ! handle_spikes + || ! append_collinear_spikes(tp, inters, is_p_last, is_q_last, + method_touch, append_equal, out) ) + { + *out++ = tp; // no spikes + } + } + else + { + equal_opposite + < + TurnInfo, + AssignPolicy + >::apply(pi, qi, + tp, out, inters.i_info(), inters.d_info()); + } + } + } + break; + case 'c' : + { + // Collinear + if ( get_turn_info_for_endpoint<true, true>( + pi, pj, pk, qi, qj, qk, + is_p_first, is_p_last, is_q_first, is_q_last, + tp_model, inters, method_collinear, out) ) + { + // do nothing + } + else + { + tp.operations[0].is_collinear = true; + + if ( ! inters.d_info().opposite ) + { + method_type method_replace = method_touch_interior; + append_version_c version = append_collinear; + + if ( inters.d_info().arrival[0] == 0 ) + { + // Collinear, but similar thus handled as equal + equal<TurnInfo>::apply(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info(), inters.sides()); + + method_replace = method_touch; + version = append_equal; + } + else + { + collinear<TurnInfo>::apply(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info(), inters.sides()); + + //method_replace = method_touch_interior; + //version = append_collinear; + } + + turn_transformer_ec<false> transformer(method_replace); + transformer(tp); + +// TODO: move this into the append_xxx and call for each turn? + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + + // conditionally handle spikes + if ( ! handle_spikes + || ! append_collinear_spikes(tp, inters, is_p_last, is_q_last, + method_replace, version, out) ) + { + // no spikes + *out++ = tp; + } + } + else + { + // Is this always 'm' ? + turn_transformer_ec<false> transformer(method_touch_interior); + + // conditionally handle spikes + if ( handle_spikes ) + { + append_opposite_spikes<append_collinear_opposite>( + tp, inters, is_p_last, is_q_last, out); + } + + // TODO: ignore for spikes? + // E.g. pass is_p_valid = !is_p_last && !is_pj_spike, + // the same with is_q_valid + + collinear_opposite + < + TurnInfo, + AssignPolicy + >::apply(pi, pj, pk, qi, qj, qk, + tp, out, inters.i_info(), inters.d_info(), + inters.sides(), transformer); + } + } + } + break; + case '0' : + { + // degenerate points + if (AssignPolicy::include_degenerate) + { + only_convert::apply(tp, inters.i_info()); + + if ( is_p_first + && equals::equals_point_point(pi, tp.point) ) + { + tp.operations[0].position = position_front; + } + else if ( is_p_last + && equals::equals_point_point(pj, tp.point) ) + { + tp.operations[0].position = position_back; + } + // tp.operations[1].position = position_middle; + + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + *out++ = tp; + } + } + break; + default : + { +#if defined(BOOST_GEOMETRY_DEBUG_ROBUSTNESS) + std::cout << "TURN: Unknown method: " << method << std::endl; +#endif +#if ! defined(BOOST_GEOMETRY_OVERLAY_NO_THROW) + throw turn_info_exception(method); +#endif + } + break; + } + + return out; + } + + template <typename Operation, + typename IntersectionInfo> + static inline bool calculate_spike_operation(Operation & op, + IntersectionInfo const& inters, + bool is_p_last) + { + bool is_p_spike = ( op == operation_union || op == operation_intersection ) + && ! is_p_last + && inters.is_spike_p(); + + if ( is_p_spike ) + { + bool going_in = false, going_out = false; + + int const pk_q1 = inters.sides().pk_wrt_q1(); + int const pk_q2 = inters.sides().pk_wrt_q2(); + + if ( inters.sides().qk_wrt_q1() <= 0 ) // Q turning R or C + { + going_in = pk_q1 < 0 && pk_q2 < 0; // Pk on the right of both + going_out = pk_q1 > 0 || pk_q2 > 0; // Pk on the left of one of them + } + else + { + going_in = pk_q1 < 0 || pk_q2 < 0; // Pk on the right of one of them + going_out = pk_q1 > 0 && pk_q2 > 0; // Pk on the left of both + } + + if ( going_in ) + { + op = operation_intersection; + return true; + } + else if ( going_out ) + { + op = operation_union; + return true; + } + } + + return false; + } + + enum append_version_c { append_equal, append_collinear }; + + template <typename TurnInfo, + typename IntersectionInfo, + typename OutIt> + static inline bool append_collinear_spikes(TurnInfo & tp, + IntersectionInfo const& inters, + bool is_p_last, bool /*is_q_last*/, + method_type method, append_version_c version, + OutIt out) + { + // method == touch || touch_interior + // both position == middle + + bool is_p_spike = ( version == append_equal ? + ( tp.operations[0].operation == operation_union + || tp.operations[0].operation == operation_intersection ) : + tp.operations[0].operation == operation_continue ) + && ! is_p_last + && inters.is_spike_p(); + + // TODO: throw an exception for spike in Areal? + /*bool is_q_spike = tp.operations[1].operation == spike_op + && ! is_q_last + && inters.is_spike_q();*/ + + if ( is_p_spike ) + { + tp.method = method; + tp.operations[0].operation = operation_blocked; + tp.operations[1].operation = operation_union; + *out++ = tp; + tp.operations[0].operation = operation_continue; // boundary + //tp.operations[1].operation = operation_union; + *out++ = tp; + + return true; + } + + return false; + } + + enum append_version_o { append_touches, append_collinear_opposite }; + + template <append_version_o Version, + typename TurnInfo, + typename IntersectionInfo, + typename OutIt> + static inline bool append_opposite_spikes(TurnInfo & tp, + IntersectionInfo const& inters, + bool is_p_last, bool /*is_q_last*/, + OutIt out) + { + bool is_p_spike = ( Version == append_touches ? + ( tp.operations[0].operation == operation_continue + || tp.operations[0].operation == operation_intersection ) : // i ??? + true ) + && ! is_p_last + && inters.is_spike_p(); + // TODO: throw an exception for spike in Areal? + /*bool is_q_spike = ( Version == append_touches ? + ( tp.operations[1].operation == operation_continue + || tp.operations[1].operation == operation_intersection ) : + true ) + && ! is_q_last + && inters.is_spike_q();*/ + + if ( is_p_spike && ( Version == append_touches || inters.d_info().arrival[0] == 1 ) ) + { + if ( Version == append_touches ) + { + tp.operations[0].is_collinear = true; + //tp.operations[1].is_collinear = false; + tp.method = method_touch; + } + else + { + tp.operations[0].is_collinear = true; + //tp.operations[1].is_collinear = false; + + BOOST_ASSERT(inters.i_info().count > 1); + base_turn_handler::assign_point(tp, method_touch_interior, inters.i_info(), 1); + + AssignPolicy::apply(tp, inters.pi(), inters.qi(), inters.i_info(), inters.d_info()); + } + + tp.operations[0].operation = operation_blocked; + tp.operations[1].operation = operation_continue; // boundary + *out++ = tp; + tp.operations[0].operation = operation_continue; // boundary + //tp.operations[1].operation = operation_continue; // boundary + *out++ = tp; + + return true; + } + + return false; + } + + static inline void replace_method_and_operations_tm(method_type & method, + operation_type & op0, + operation_type & op1) + { + if ( op0 == operation_blocked && op1 == operation_blocked ) + { + // NOTE: probably only if methods are WRT IPs, not segments! + method = (method == method_touch ? method_equal : method_collinear); + } + + // Assuming G1 is always Linear + if ( op0 == operation_blocked ) + { + op0 = operation_continue; + } + + if ( op1 == operation_blocked ) + { + op1 = operation_continue; + } + else if ( op1 == operation_intersection ) + { + op1 = operation_union; + } + + // spikes in 'm' + if ( method == method_error ) + { + method = method_touch_interior; + op0 = operation_union; + op1 = operation_union; + } + } + + template <bool IsFront> + class turn_transformer_ec + { + public: + explicit turn_transformer_ec(method_type method_t_or_m) + : m_method(method_t_or_m) + {} + + template <typename Turn> + void operator()(Turn & turn) const + { + operation_type & op0 = turn.operations[0].operation; + operation_type & op1 = turn.operations[1].operation; + + // NOTE: probably only if methods are WRT IPs, not segments! + if ( IsFront + || op0 == operation_intersection || op0 == operation_union + || op1 == operation_intersection || op1 == operation_union ) + { + turn.method = m_method; + } + + turn.operations[0].is_collinear = op0 != operation_blocked; + + // Assuming G1 is always Linear + if ( op0 == operation_blocked ) + { + op0 = operation_continue; + } + + if ( op1 == operation_blocked ) + { + op1 = operation_continue; + } + else if ( op1 == operation_intersection ) + { + op1 = operation_union; + } + } + + private: + method_type m_method; + }; + + static inline void replace_operations_i(operation_type & /*op0*/, operation_type & op1) + { + // assuming Linear is always the first one + op1 = operation_union; + } + + // NOTE: Spikes may NOT be handled for Linear endpoints because it's not + // possible to define a spike on an endpoint. Areal geometries must + // NOT have spikes at all. One thing that could be done is to throw + // an exception when spike is detected in Areal geometry. + + template <bool EnableFirst, + bool EnableLast, + typename Point1, + typename Point2, + typename TurnInfo, + typename IntersectionInfo, + typename OutputIterator> + static inline bool get_turn_info_for_endpoint( + Point1 const& pi, Point1 const& /*pj*/, Point1 const& /*pk*/, + Point2 const& qi, Point2 const& /*qj*/, Point2 const& /*qk*/, + bool is_p_first, bool is_p_last, + bool /*is_q_first*/, bool is_q_last, + TurnInfo const& tp_model, + IntersectionInfo const& inters, + method_type /*method*/, + OutputIterator out) + { + namespace ov = overlay; + typedef ov::get_turn_info_for_endpoint<AssignPolicy, EnableFirst, EnableLast> get_info_e; + + const std::size_t ip_count = inters.i_info().count; + // no intersection points + if ( ip_count == 0 ) + return false; + + if ( !is_p_first && !is_p_last ) + return false; + +// TODO: is_q_last could probably be replaced by false and removed from parameters + + linear_intersections intersections(pi, qi, inters.result(), is_p_last, is_q_last); + linear_intersections::ip_info const& ip0 = intersections.template get<0>(); + linear_intersections::ip_info const& ip1 = intersections.template get<1>(); + + const bool opposite = inters.d_info().opposite; + + // ANALYSE AND ASSIGN FIRST + + // IP on the first point of Linear Geometry + if ( EnableFirst && is_p_first && ip0.is_pi && !ip0.is_qi ) // !q0i prevents duplication + { + TurnInfo tp = tp_model; + tp.operations[0].position = position_front; + tp.operations[1].position = position_middle; + + if ( opposite ) // opposite -> collinear + { + tp.operations[0].operation = operation_continue; + tp.operations[1].operation = operation_union; + tp.method = ip0.is_qj ? method_touch : method_touch_interior; + } + else + { + method_type replaced_method = method_touch_interior; + + if ( ip0.is_qj ) + { + side_calculator + < + typename IntersectionInfo::robust_point1_type, + typename IntersectionInfo::robust_point2_type, + typename IntersectionInfo::robust_point2_type + > side_calc(inters.rqi(), inters.rpi(), inters.rpj(), + inters.rqi(), inters.rqj(), inters.rqk()); + + std::pair<operation_type, operation_type> + operations = get_info_e::operations_of_equal(side_calc); + + tp.operations[0].operation = operations.first; + tp.operations[1].operation = operations.second; + + replaced_method = method_touch; + } + else + { + side_calculator + < + typename IntersectionInfo::robust_point1_type, + typename IntersectionInfo::robust_point2_type, + typename IntersectionInfo::robust_point2_type, + typename IntersectionInfo::robust_point1_type, + typename IntersectionInfo::robust_point1_type, + typename IntersectionInfo::robust_point2_type, + typename IntersectionInfo::robust_point1_type, + typename IntersectionInfo::robust_point2_type + > side_calc(inters.rqi(), inters.rpi(), inters.rpj(), + inters.rqi(), inters.rpi(), inters.rqj()); + + std::pair<operation_type, operation_type> + operations = get_info_e::operations_of_equal(side_calc); + + tp.operations[0].operation = operations.first; + tp.operations[1].operation = operations.second; + } + + turn_transformer_ec<true> transformer(replaced_method); + transformer(tp); + } + + // equals<> or collinear<> will assign the second point, + // we'd like to assign the first one + base_turn_handler::assign_point(tp, tp.method, inters.i_info(), 0); + + // NOTE: is_collinear is not set for the first endpoint of L + // for which there is no preceding segment + // here is_p_first_ip == true + tp.operations[0].is_collinear = false; + + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + *out++ = tp; + } + + // ANALYSE AND ASSIGN LAST + + // IP on the last point of Linear Geometry + if ( EnableLast + && is_p_last + && ( ip_count > 1 ? (ip1.is_pj && !ip1.is_qi) : (ip0.is_pj && !ip0.is_qi) ) ) // prevents duplication + { + TurnInfo tp = tp_model; + + if ( inters.i_info().count > 1 ) + { + //BOOST_ASSERT( result.template get<1>().dir_a == 0 && result.template get<1>().dir_b == 0 ); + tp.operations[0].is_collinear = true; + tp.operations[1].operation = opposite ? operation_continue : operation_union; + } + else //if ( result.template get<0>().count == 1 ) + { + side_calculator + < + typename IntersectionInfo::robust_point1_type, + typename IntersectionInfo::robust_point2_type, + typename IntersectionInfo::robust_point2_type + > side_calc(inters.rqi(), inters.rpj(), inters.rpi(), + inters.rqi(), inters.rqj(), inters.rqk()); + + std::pair<operation_type, operation_type> + operations = get_info_e::operations_of_equal(side_calc); + + tp.operations[0].operation = operations.first; + tp.operations[1].operation = operations.second; + + turn_transformer_ec<false> transformer(method_none); + transformer(tp); + + tp.operations[0].is_collinear = tp.both(operation_continue); + } + + tp.method = ( ip_count > 1 ? ip1.is_qj : ip0.is_qj ) ? method_touch : method_touch_interior; + tp.operations[0].operation = operation_blocked; + tp.operations[0].position = position_back; + tp.operations[1].position = position_middle; + + // equals<> or collinear<> will assign the second point, + // we'd like to assign the first one + int ip_index = ip_count > 1 ? 1 : 0; + base_turn_handler::assign_point(tp, tp.method, inters.i_info(), ip_index); + + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + *out++ = tp; + + return true; + } + + // don't ignore anything for now + return false; + } +}; + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_LA_HPP diff --git a/boost/geometry/algorithms/detail/overlay/get_turn_info_ll.hpp b/boost/geometry/algorithms/detail/overlay/get_turn_info_ll.hpp new file mode 100644 index 0000000000..4f0384877f --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/get_turn_info_ll.hpp @@ -0,0 +1,720 @@ +// 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_OVERLAY_GET_TURN_INFO_LL_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_LL_HPP + +#include <boost/geometry/algorithms/detail/overlay/get_turn_info.hpp> +#include <boost/geometry/algorithms/detail/overlay/get_turn_info_for_endpoint.hpp> + +namespace boost { namespace geometry { + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay { + +template<typename AssignPolicy> +struct get_turn_info_linear_linear +{ + static const bool handle_spikes = true; + + template + < + typename Point1, + typename Point2, + typename TurnInfo, + typename RobustPolicy, + typename OutputIterator + > + static inline OutputIterator apply( + Point1 const& pi, Point1 const& pj, Point1 const& pk, + Point2 const& qi, Point2 const& qj, Point2 const& qk, + bool is_p_first, bool is_p_last, + bool is_q_first, bool is_q_last, + TurnInfo const& tp_model, + RobustPolicy const& robust_policy, + OutputIterator out) + { + typedef intersection_info<Point1, Point2, typename TurnInfo::point_type, RobustPolicy> + inters_info; + + inters_info inters(pi, pj, pk, qi, qj, qk, robust_policy); + + char const method = inters.d_info().how; + + // Copy, to copy possibly extended fields + TurnInfo tp = tp_model; + + // Select method and apply + switch(method) + { + case 'a' : // collinear, "at" + case 'f' : // collinear, "from" + case 's' : // starts from the middle + get_turn_info_for_endpoint<AssignPolicy, true, true> + ::apply(pi, pj, pk, qi, qj, qk, + is_p_first, is_p_last, is_q_first, is_q_last, + tp_model, inters, method_none, out); + break; + + case 'd' : // disjoint: never do anything + break; + + case 'm' : + { + if ( get_turn_info_for_endpoint<AssignPolicy, false, true> + ::apply(pi, pj, pk, qi, qj, qk, + is_p_first, is_p_last, is_q_first, is_q_last, + tp_model, inters, method_touch_interior, out) ) + { + // do nothing + } + else + { + typedef touch_interior + < + TurnInfo + > policy; + + // If Q (1) arrives (1) + if ( inters.d_info().arrival[1] == 1) + { + policy::template apply<0>(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info(), + inters.sides()); + } + else + { + // Swap p/q + side_calculator + < + typename inters_info::robust_point2_type, + typename inters_info::robust_point1_type + > swapped_side_calc(inters.rqi(), inters.rqj(), inters.rqk(), + inters.rpi(), inters.rpj(), inters.rpk()); + + policy::template apply<1>(qi, qj, qk, pi, pj, pk, + tp, inters.i_info(), inters.d_info(), + swapped_side_calc); + } + + if ( tp.operations[0].operation == operation_blocked ) + { + tp.operations[1].is_collinear = true; + } + if ( tp.operations[1].operation == operation_blocked ) + { + tp.operations[0].is_collinear = true; + } + + replace_method_and_operations_tm(tp.method, + tp.operations[0].operation, + tp.operations[1].operation); + + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + *out++ = tp; + } + } + break; + case 'i' : + { + crosses<TurnInfo>::apply(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info()); + + replace_operations_i(tp.operations[0].operation, tp.operations[1].operation); + + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + *out++ = tp; + } + break; + case 't' : + { + // Both touch (both arrive there) + if ( get_turn_info_for_endpoint<AssignPolicy, false, true> + ::apply(pi, pj, pk, qi, qj, qk, + is_p_first, is_p_last, is_q_first, is_q_last, + tp_model, inters, method_touch, out) ) + { + // do nothing + } + else + { + touch<TurnInfo>::apply(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info(), inters.sides()); + + // workarounds for touch<> not taking spikes into account starts here + // those was discovered empirically + // touch<> is not symmetrical! + // P spikes and Q spikes may produce various operations! + // TODO: this is not optimal solution - think about rewriting touch<> + + if ( tp.operations[0].operation == operation_blocked + && tp.operations[1].operation == operation_blocked ) + { + // two touching spikes on the same line + if ( inters.is_spike_p() && inters.is_spike_q() ) + { + tp.operations[0].operation = operation_union; + tp.operations[1].operation = operation_union; + } + else + { + tp.operations[0].is_collinear = true; + tp.operations[1].is_collinear = true; + } + } + else if ( tp.operations[0].operation == operation_blocked ) + { + // a spike on P on the same line with Q1 + if ( inters.is_spike_p() ) + { + if ( inters.sides().qk_wrt_p1() == 0 ) + { + tp.operations[0].is_collinear = true; + } + else + { + tp.operations[0].operation = operation_union; + } + } + else + { + tp.operations[1].is_collinear = true; + } + } + else if ( tp.operations[1].operation == operation_blocked ) + { + // a spike on Q on the same line with P1 + if ( inters.is_spike_q() ) + { + if ( inters.sides().pk_wrt_q1() == 0 ) + { + tp.operations[1].is_collinear = true; + } + else + { + tp.operations[1].operation = operation_union; + } + } + else + { + tp.operations[0].is_collinear = true; + } + } + else if ( tp.operations[0].operation == operation_continue + && tp.operations[1].operation == operation_continue ) + { + // P spike on the same line with Q2 (opposite) + if ( inters.sides().pk_wrt_q1() == -inters.sides().qk_wrt_q1() + && inters.is_spike_p() ) + { + tp.operations[0].operation = operation_union; + tp.operations[1].operation = operation_union; + } + } + else if ( tp.operations[0].operation == operation_none + && tp.operations[1].operation == operation_none ) + { + // spike not handled by touch<> + bool const is_p = inters.is_spike_p(); + bool const is_q = inters.is_spike_q(); + + if ( is_p || is_q ) + { + tp.operations[0].operation = operation_union; + tp.operations[1].operation = operation_union; + + if ( inters.sides().pk_wrt_q2() == 0 ) + { + tp.operations[0].operation = operation_continue; // will be converted to i + if ( is_p ) + { + tp.operations[0].is_collinear = true; + } + } + + if ( inters.sides().qk_wrt_p2() == 0 ) + { + tp.operations[1].operation = operation_continue; // will be converted to i + if ( is_q ) + { + tp.operations[1].is_collinear = true; + } + } + } + } + + // workarounds for touch<> not taking spikes into account ends here + + replace_method_and_operations_tm(tp.method, + tp.operations[0].operation, + tp.operations[1].operation); + +// TODO: move this into the append_xxx and call for each turn? + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + + if ( ! handle_spikes + || ! append_opposite_spikes<append_touches>(tp, inters, + is_p_last, is_q_last, + out) ) + { + *out++ = tp; + } + } + } + break; + case 'e': + { + if ( get_turn_info_for_endpoint<AssignPolicy, true, true> + ::apply(pi, pj, pk, qi, qj, qk, + is_p_first, is_p_last, is_q_first, is_q_last, + tp_model, inters, method_equal, out) ) + { + // do nothing + } + else + { + tp.operations[0].is_collinear = true; + tp.operations[1].is_collinear = true; + + if ( ! inters.d_info().opposite ) + { + // Both equal + // or collinear-and-ending at intersection point + equal<TurnInfo>::apply(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info(), inters.sides()); + + // transform turn + turn_transformer_ec transformer(method_touch); + transformer(tp); + +// TODO: move this into the append_xxx and call for each turn? + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + + // conditionally handle spikes + if ( ! handle_spikes + || ! append_collinear_spikes(tp, inters, + is_p_last, is_q_last, + method_touch, operation_union, + out) ) + { + *out++ = tp; // no spikes + } + } + else + { + // TODO: ignore for spikes or generate something else than opposite? + + equal_opposite + < + TurnInfo, + AssignPolicy + >::apply(pi, qi, tp, out, inters.i_info(), inters.d_info()); + } + } + } + break; + case 'c' : + { + // Collinear + if ( get_turn_info_for_endpoint<AssignPolicy, true, true> + ::apply(pi, pj, pk, qi, qj, qk, + is_p_first, is_p_last, is_q_first, is_q_last, + tp_model, inters, method_collinear, out) ) + { + // do nothing + } + else + { + // NOTE: this is for spikes since those are set in the turn_transformer_ec + tp.operations[0].is_collinear = true; + tp.operations[1].is_collinear = true; + + if ( ! inters.d_info().opposite ) + { + method_type method_replace = method_touch_interior; + operation_type spike_op = operation_continue; + + if ( inters.d_info().arrival[0] == 0 ) + { + // Collinear, but similar thus handled as equal + equal<TurnInfo>::apply(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info(), inters.sides()); + + method_replace = method_touch; + spike_op = operation_union; + } + else + { + collinear<TurnInfo>::apply(pi, pj, pk, qi, qj, qk, + tp, inters.i_info(), inters.d_info(), inters.sides()); + + //method_replace = method_touch_interior; + //spike_op = operation_continue; + } + + // transform turn + turn_transformer_ec transformer(method_replace); + transformer(tp); + +// TODO: move this into the append_xxx and call for each turn? + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + + // conditionally handle spikes + if ( ! handle_spikes + || ! append_collinear_spikes(tp, inters, + is_p_last, is_q_last, + method_replace, spike_op, + out) ) + { + // no spikes + *out++ = tp; + } + } + else + { + // If this always 'm' ? + turn_transformer_ec transformer(method_touch_interior); + + // conditionally handle spikes + if ( handle_spikes ) + { + append_opposite_spikes<append_collinear_opposite>(tp, inters, + is_p_last, is_q_last, + out); + } + + // TODO: ignore for spikes? + // E.g. pass is_p_valid = !is_p_last && !is_pj_spike, + // the same with is_q_valid + + collinear_opposite + < + TurnInfo, + AssignPolicy + >::apply(pi, pj, pk, qi, qj, qk, + tp, out, inters.i_info(), inters.d_info(), inters.sides(), + transformer, !is_p_last, !is_q_last); + } + } + } + break; + case '0' : + { + // degenerate points + if (AssignPolicy::include_degenerate) + { + only_convert::apply(tp, inters.i_info()); + + // if any, only one of those should be true + if ( is_p_first + && equals::equals_point_point(pi, tp.point) ) + { + tp.operations[0].position = position_front; + } + else if ( is_p_last + && equals::equals_point_point(pj, tp.point) ) + { + tp.operations[0].position = position_back; + } + else if ( is_q_first + && equals::equals_point_point(qi, tp.point) ) + { + tp.operations[1].position = position_front; + } + else if ( is_q_last + && equals::equals_point_point(qj, tp.point) ) + { + tp.operations[1].position = position_back; + } + + AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + *out++ = tp; + } + } + break; + default : + { +#if defined(BOOST_GEOMETRY_DEBUG_ROBUSTNESS) + std::cout << "TURN: Unknown method: " << method << std::endl; +#endif +#if ! defined(BOOST_GEOMETRY_OVERLAY_NO_THROW) + throw turn_info_exception(method); +#endif + } + break; + } + + return out; + } + + template <typename TurnInfo, + typename IntersectionInfo, + typename OutIt> + static inline bool append_collinear_spikes(TurnInfo & tp, + IntersectionInfo const& inters_info, + bool is_p_last, bool is_q_last, + method_type method, operation_type spike_op, + OutIt out) + { + // method == touch || touch_interior + // both position == middle + + bool is_p_spike = tp.operations[0].operation == spike_op + && ! is_p_last + && inters_info.is_spike_p(); + bool is_q_spike = tp.operations[1].operation == spike_op + && ! is_q_last + && inters_info.is_spike_q(); + + if ( is_p_spike && is_q_spike ) + { + tp.method = method; + tp.operations[0].operation = operation_blocked; + tp.operations[1].operation = operation_blocked; + *out++ = tp; + tp.operations[0].operation = operation_intersection; + tp.operations[1].operation = operation_intersection; + *out++ = tp; + + return true; + } + else if ( is_p_spike ) + { + tp.method = method; + tp.operations[0].operation = operation_blocked; + tp.operations[1].operation = operation_union; + *out++ = tp; + tp.operations[0].operation = operation_intersection; + //tp.operations[1].operation = operation_union; + *out++ = tp; + + return true; + } + else if ( is_q_spike ) + { + tp.method = method; + tp.operations[0].operation = operation_union; + tp.operations[1].operation = operation_blocked; + *out++ = tp; + //tp.operations[0].operation = operation_union; + tp.operations[1].operation = operation_intersection; + *out++ = tp; + + return true; + } + + return false; + } + + enum append_version { append_touches, append_collinear_opposite }; + + template <append_version Version, + typename TurnInfo, + typename IntersectionInfo, + typename OutIt> + static inline bool append_opposite_spikes(TurnInfo & tp, + IntersectionInfo const& inters, + bool is_p_last, bool is_q_last, + OutIt out) + { + bool is_p_spike = ( Version == append_touches ? + ( tp.operations[0].operation == operation_continue + || tp.operations[0].operation == operation_intersection ) : + true ) + && ! is_p_last + && inters.is_spike_p(); + bool is_q_spike = ( Version == append_touches ? + ( tp.operations[1].operation == operation_continue + || tp.operations[1].operation == operation_intersection ) : + true ) + && ! is_q_last + && inters.is_spike_q(); + + bool res = false; + + if ( is_p_spike && ( Version == append_touches || inters.d_info().arrival[0] == 1 ) ) + { + if ( Version == append_touches ) + { + tp.operations[0].is_collinear = true; + tp.operations[1].is_collinear = false; + tp.method = method_touch; + } + else // Version == append_collinear_opposite + { + tp.operations[0].is_collinear = true; + tp.operations[1].is_collinear = false; + + BOOST_ASSERT(inters.i_info().count > 1); + + base_turn_handler::assign_point(tp, method_touch_interior, + inters.i_info(), 1); + + AssignPolicy::apply(tp, inters.pi(), inters.qi(), + inters.i_info(), inters.d_info()); + } + + tp.operations[0].operation = operation_blocked; + tp.operations[1].operation = operation_intersection; + *out++ = tp; + tp.operations[0].operation = operation_intersection; + //tp.operations[1].operation = operation_intersection; + *out++ = tp; + + res = true; + } + + if ( is_q_spike && ( Version == append_touches || inters.d_info().arrival[1] == 1 ) ) + { + if ( Version == append_touches ) + { + tp.operations[0].is_collinear = false; + tp.operations[1].is_collinear = true; + tp.method = method_touch; + } + else // Version == append_collinear_opposite + { + tp.operations[0].is_collinear = false; + tp.operations[1].is_collinear = true; + + BOOST_ASSERT(inters.i_info().count > 0); + + base_turn_handler::assign_point(tp, method_touch_interior, inters.i_info(), 0); + + AssignPolicy::apply(tp, inters.pi(), inters.qi(), + inters.i_info(), inters.d_info()); + } + + tp.operations[0].operation = operation_intersection; + tp.operations[1].operation = operation_blocked; + *out++ = tp; + //tp.operations[0].operation = operation_intersection; + tp.operations[1].operation = operation_intersection; + *out++ = tp; + + res = true; + } + + return res; + } + + static inline void replace_method_and_operations_tm(method_type & method, + operation_type & op0, + operation_type & op1) + { + if ( op0 == operation_blocked && op1 == operation_blocked ) + { + // NOTE: probably only if methods are WRT IPs, not segments! + method = (method == method_touch ? method_equal : method_collinear); + op0 = operation_continue; + op1 = operation_continue; + } + else + { + if ( op0 == operation_continue || op0 == operation_blocked ) + { + op0 = operation_intersection; + } + else if ( op0 == operation_intersection ) + { + op0 = operation_union; + } + + if ( op1 == operation_continue || op1 == operation_blocked ) + { + op1 = operation_intersection; + } + else if ( op1 == operation_intersection ) + { + op1 = operation_union; + } + + // spikes in 'm' + if ( method == method_error ) + { + method = method_touch_interior; + op0 = operation_union; + op1 = operation_union; + } + } + } + + class turn_transformer_ec + { + public: + explicit turn_transformer_ec(method_type method_t_or_m) + : m_method(method_t_or_m) + {} + + template <typename Turn> + void operator()(Turn & turn) const + { + operation_type & op0 = turn.operations[0].operation; + operation_type & op1 = turn.operations[1].operation; + + BOOST_ASSERT(op0 != operation_blocked || op1 != operation_blocked ); + + if ( op0 == operation_blocked ) + { + op0 = operation_intersection; + } + else if ( op0 == operation_intersection ) + { + op0 = operation_union; + } + + if ( op1 == operation_blocked ) + { + op1 = operation_intersection; + } + else if ( op1 == operation_intersection ) + { + op1 = operation_union; + } + + if ( op0 == operation_intersection || op0 == operation_union + || op1 == operation_intersection || op1 == operation_union ) + { + turn.method = m_method; + } + +// TODO: is this correct? +// it's equivalent to comparing to operation_blocked at the beginning of the function + turn.operations[0].is_collinear = op0 != operation_intersection; + turn.operations[1].is_collinear = op1 != operation_intersection; + } + + private: + method_type m_method; + }; + + static inline void replace_operations_i(operation_type & op0, operation_type & op1) + { + if ( op0 == operation_intersection ) + { + op0 = operation_union; + } + + if ( op1 == operation_intersection ) + { + op1 = operation_union; + } + } +}; + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_LL_HPP diff --git a/boost/geometry/algorithms/detail/overlay/get_turns.hpp b/boost/geometry/algorithms/detail/overlay/get_turns.hpp index 26629043cb..a96538c43a 100644 --- a/boost/geometry/algorithms/detail/overlay/get_turns.hpp +++ b/boost/geometry/algorithms/detail/overlay/get_turns.hpp @@ -1,11 +1,17 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2014 Adam Wulkiewicz, Lodz, Poland. + +// This file was modified by Oracle on 2014. +// Modifications copyright (c) 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_OVERLAY_GET_TURNS_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURNS_HPP @@ -14,19 +20,17 @@ #include <map> #include <boost/array.hpp> +#include <boost/concept_check.hpp> #include <boost/mpl/if.hpp> #include <boost/range.hpp> -#include <boost/typeof/typeof.hpp> - -#include <boost/tuple/tuple.hpp> #include <boost/geometry/core/access.hpp> #include <boost/geometry/core/coordinate_dimension.hpp> -#include <boost/geometry/core/reverse_dispatch.hpp> - #include <boost/geometry/core/exterior_ring.hpp> #include <boost/geometry/core/interior_rings.hpp> +#include <boost/geometry/core/reverse_dispatch.hpp> #include <boost/geometry/core/ring_type.hpp> +#include <boost/geometry/core/tags.hpp> #include <boost/geometry/geometries/concepts/check.hpp> @@ -44,17 +48,22 @@ #include <boost/geometry/strategies/intersection.hpp> #include <boost/geometry/strategies/intersection_result.hpp> -#include <boost/geometry/algorithms/detail/disjoint.hpp> +#include <boost/geometry/algorithms/detail/disjoint/box_box.hpp> +#include <boost/geometry/algorithms/detail/disjoint/point_point.hpp> + +#include <boost/geometry/algorithms/detail/interior_iterator.hpp> #include <boost/geometry/algorithms/detail/partition.hpp> -#include <boost/geometry/algorithms/detail/overlay/get_turn_info.hpp> +#include <boost/geometry/algorithms/detail/recalculate.hpp> +#include <boost/geometry/algorithms/detail/overlay/get_turn_info.hpp> +#include <boost/geometry/algorithms/detail/overlay/get_turn_info_ll.hpp> +#include <boost/geometry/algorithms/detail/overlay/get_turn_info_la.hpp> #include <boost/geometry/algorithms/detail/overlay/segment_identifier.hpp> - #include <boost/geometry/algorithms/detail/sections/range_by_section.hpp> +#include <boost/geometry/algorithms/detail/sections/sectionalize.hpp> #include <boost/geometry/algorithms/expand.hpp> -#include <boost/geometry/algorithms/detail/sections/sectionalize.hpp> #ifdef BOOST_GEOMETRY_DEBUG_INTERSECTION # include <sstream> @@ -65,6 +74,12 @@ namespace boost { namespace geometry { +// Silence warning C4127: conditional expression is constant +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4127) +#endif + #ifndef DOXYGEN_NO_DETAIL namespace detail { namespace get_turns @@ -88,9 +103,7 @@ template typename Geometry1, typename Geometry2, bool Reverse1, bool Reverse2, typename Section1, typename Section2, - typename Turns, - typename TurnPolicy, - typename InterruptPolicy + typename TurnPolicy > class get_turns_in_sections { @@ -138,16 +151,21 @@ class get_turns_in_sections // About first condition: will be optimized by compiler (static) // It checks if it is areal (box,ring,(multi)polygon int const n = int(section.range_count); + + boost::ignore_unused_variable_warning(n); + boost::ignore_unused_variable_warning(index1); + boost::ignore_unused_variable_warning(index2); + return boost::is_same < typename tag_cast < - typename geometry::point_type<Geometry1>::type, + typename geometry::tag<Geometry>::type, areal_tag - >::type, + >::type, areal_tag >::value - && index1 == 0 + && index1 == 0 && index2 >= n - 2 ; } @@ -155,13 +173,27 @@ class get_turns_in_sections public : // Returns true if terminated, false if interrupted + template <typename Turns, typename RobustPolicy, typename InterruptPolicy> static inline bool apply( int source_id1, Geometry1 const& geometry1, Section1 const& sec1, int source_id2, Geometry2 const& geometry2, Section2 const& sec2, bool skip_larger, + RobustPolicy const& robust_policy, Turns& turns, InterruptPolicy& interrupt_policy) { + boost::ignore_unused_variable_warning(interrupt_policy); + + if ((sec1.duplicate && (sec1.count + 1) < sec1.range_count) + || (sec2.duplicate && (sec2.count + 1) < sec2.range_count)) + { + // Skip sections containig only duplicates. + // They are still important (can indicate non-disjointness) + // but they will be found processing adjacent sections. + // Do NOT skip if they are the ONLY section + return true; + } + cview_type1 cview1(range_by_section(geometry1, sec1)); cview_type2 cview2(range_by_section(geometry2, sec2)); view_type1 view1(cview1); @@ -186,7 +218,7 @@ public : range1_iterator prev1, it1, end1; get_start_point_iterator(sec1, view1, prev1, it1, end1, - index1, ndi1, dir1, sec2.bounding_box); + index1, ndi1, dir1, sec2.bounding_box, robust_policy); // We need a circular iterator because it might run through the closing point. // One circle is actually enough but this one is just convenient. @@ -197,12 +229,12 @@ public : // section 2: [--------------] // section 1: |----|---|---|---|---| for (prev1 = it1++, next1++; - it1 != end1 && ! exceeding<0>(dir1, *prev1, sec2.bounding_box); + it1 != end1 && ! exceeding<0>(dir1, *prev1, sec2.bounding_box, robust_policy); ++prev1, ++it1, ++index1, ++next1, ++ndi1) { ever_circling_iterator<range1_iterator> nd_next1( begin_range_1, end_range_1, next1, true); - advance_to_non_duplicate_next(nd_next1, it1, sec1); + advance_to_non_duplicate_next(nd_next1, it1, sec1, robust_policy); int index2 = sec2.begin_index; int ndi2 = sec2.non_duplicate_index; @@ -210,12 +242,12 @@ public : range2_iterator prev2, it2, end2; get_start_point_iterator(sec2, view2, prev2, it2, end2, - index2, ndi2, dir2, sec1.bounding_box); + index2, ndi2, dir2, sec1.bounding_box, robust_policy); ever_circling_iterator<range2_iterator> next2(begin_range_2, end_range_2, it2, true); next2++; for (prev2 = it2++, next2++; - it2 != end2 && ! exceeding<0>(dir2, *prev2, sec1.bounding_box); + it2 != end2 && ! exceeding<0>(dir2, *prev2, sec1.bounding_box, robust_policy); ++prev2, ++it2, ++index2, ++next2, ++ndi2) { bool skip = same_source; @@ -241,24 +273,28 @@ public : // Move to the "non duplicate next" ever_circling_iterator<range2_iterator> nd_next2( begin_range_2, end_range_2, next2, true); - advance_to_non_duplicate_next(nd_next2, it2, sec2); + advance_to_non_duplicate_next(nd_next2, it2, sec2, robust_policy); typedef typename boost::range_value<Turns>::type turn_info; - typedef typename turn_info::point_type ip; turn_info ti; - ti.operations[0].seg_id = segment_identifier(source_id1, - sec1.ring_id.multi_index, sec1.ring_id.ring_index, index1), - ti.operations[1].seg_id = segment_identifier(source_id2, - sec2.ring_id.multi_index, sec2.ring_id.ring_index, index2), - - ti.operations[0].other_id = ti.operations[1].seg_id; - ti.operations[1].other_id = ti.operations[0].seg_id; + ti.operations[0].seg_id + = segment_identifier(source_id1, sec1.ring_id.multi_index, + sec1.ring_id.ring_index, index1); + ti.operations[1].seg_id + = segment_identifier(source_id2, sec2.ring_id.multi_index, + sec2.ring_id.ring_index, index2); std::size_t const size_before = boost::size(turns); + bool const is_1_first = sec1.is_non_duplicate_first && index1 == sec1.begin_index; + bool const is_1_last = sec1.is_non_duplicate_last && index1+1 >= sec1.end_index; + bool const is_2_first = sec2.is_non_duplicate_first && index2 == sec2.begin_index; + bool const is_2_last = sec2.is_non_duplicate_last && index2+1 >= sec2.end_index; + TurnPolicy::apply(*prev1, *it1, *nd_next1, *prev2, *it2, *nd_next2, - ti, std::back_inserter(turns)); + is_1_first, is_1_last, is_2_first, is_2_last, + ti, robust_policy, std::back_inserter(turns)); if (InterruptPolicy::enabled) { @@ -283,24 +319,34 @@ private : typedef typename model::referring_segment<point2_type const> segment2_type; - template <size_t Dim, typename Point, typename Box> - static inline bool preceding(int dir, Point const& point, Box const& box) + template <size_t Dim, typename Point, typename Box, typename RobustPolicy> + static inline bool preceding(int dir, Point const& point, Box const& box, RobustPolicy const& robust_policy) { - return (dir == 1 && get<Dim>(point) < get<min_corner, Dim>(box)) - || (dir == -1 && get<Dim>(point) > get<max_corner, Dim>(box)); + typename robust_point_type<Point, RobustPolicy>::type robust_point; + geometry::recalculate(robust_point, point, robust_policy); + return (dir == 1 && get<Dim>(robust_point) < get<min_corner, Dim>(box)) + || (dir == -1 && get<Dim>(robust_point) > get<max_corner, Dim>(box)); } - template <size_t Dim, typename Point, typename Box> - static inline bool exceeding(int dir, Point const& point, Box const& box) + template <size_t Dim, typename Point, typename Box, typename RobustPolicy> + static inline bool exceeding(int dir, Point const& point, Box const& box, RobustPolicy const& robust_policy) { - return (dir == 1 && get<Dim>(point) > get<max_corner, Dim>(box)) - || (dir == -1 && get<Dim>(point) < get<min_corner, Dim>(box)); + typename robust_point_type<Point, RobustPolicy>::type robust_point; + geometry::recalculate(robust_point, point, robust_policy); + return (dir == 1 && get<Dim>(robust_point) > get<max_corner, Dim>(box)) + || (dir == -1 && get<Dim>(robust_point) < get<min_corner, Dim>(box)); } - template <typename Iterator, typename RangeIterator, typename Section> + template <typename Iterator, typename RangeIterator, typename Section, typename RobustPolicy> static inline void advance_to_non_duplicate_next(Iterator& next, - RangeIterator const& it, Section const& section) + RangeIterator const& it, Section const& section, RobustPolicy const& robust_policy) { + typedef typename robust_point_type<point1_type, RobustPolicy>::type robust_point_type; + robust_point_type robust_point_from_it; + robust_point_type robust_point_from_next; + geometry::recalculate(robust_point_from_it, *it, robust_policy); + geometry::recalculate(robust_point_from_next, *next, robust_policy); + // To see where the next segments bend to, in case of touch/intersections // on end points, we need (in case of degenerate/duplicate points) an extra // iterator which moves to the REAL next point, so non duplicate. @@ -311,10 +357,14 @@ private : // So advance to the "non duplicate next" // (the check is defensive, to avoid endless loops) std::size_t check = 0; - while(! detail::disjoint::disjoint_point_point(*it, *next) + while(! detail::disjoint::disjoint_point_point + ( + robust_point_from_it, robust_point_from_next + ) && check++ < section.range_count) { next++; + geometry::recalculate(robust_point_from_next, *next, robust_policy); } } @@ -322,14 +372,14 @@ private : // because of the logistics of "index" (the section-iterator automatically // skips to the begin-point, we loose the index or have to recalculate it) // So we mimic it here - template <typename Range, typename Section, typename Box> + template <typename Range, typename Section, typename Box, typename RobustPolicy> static inline void get_start_point_iterator(Section & section, Range const& range, typename boost::range_iterator<Range const>::type& it, typename boost::range_iterator<Range const>::type& prev, typename boost::range_iterator<Range const>::type& end, int& index, int& ndi, - int dir, Box const& other_bounding_box) + int dir, Box const& other_bounding_box, RobustPolicy const& robust_policy) { it = boost::begin(range) + section.begin_index; end = boost::begin(range) + section.end_index + 1; @@ -337,7 +387,7 @@ private : // Mimic section-iterator: // Skip to point such that section interects other box prev = it++; - for(; it != end && preceding<0>(dir, *it, other_bounding_box); + for(; it != end && preceding<0>(dir, *it, other_bounding_box, robust_policy); prev = it++, index++, ndi++) {} // Go back one step because we want to start completely preceding @@ -369,6 +419,7 @@ template bool Reverse1, bool Reverse2, typename Turns, typename TurnPolicy, + typename RobustPolicy, typename InterruptPolicy > struct section_visitor @@ -377,14 +428,17 @@ struct section_visitor Geometry1 const& m_geometry1; int m_source_id2; Geometry2 const& m_geometry2; + RobustPolicy const& m_rescale_policy; Turns& m_turns; InterruptPolicy& m_interrupt_policy; section_visitor(int id1, Geometry1 const& g1, int id2, Geometry2 const& g2, + RobustPolicy const& robust_policy, Turns& turns, InterruptPolicy& ip) : m_source_id1(id1), m_geometry1(g1) , m_source_id2(id2), m_geometry2(g2) + , m_rescale_policy(robust_policy) , m_turns(turns) , m_interrupt_policy(ip) {} @@ -400,13 +454,12 @@ struct section_visitor Geometry2, Reverse1, Reverse2, Section, Section, - Turns, - TurnPolicy, - InterruptPolicy + TurnPolicy >::apply( m_source_id1, m_geometry1, sec1, m_source_id2, m_geometry2, sec2, false, + m_rescale_policy, m_turns, m_interrupt_policy); } return true; @@ -418,37 +471,45 @@ template < typename Geometry1, typename Geometry2, bool Reverse1, bool Reverse2, - typename Turns, - typename TurnPolicy, - typename InterruptPolicy + typename TurnPolicy > class get_turns_generic { public: + template <typename RobustPolicy, typename Turns, typename InterruptPolicy> static inline void apply( int source_id1, Geometry1 const& geometry1, int source_id2, Geometry2 const& geometry2, - Turns& turns, InterruptPolicy& interrupt_policy) + RobustPolicy const& robust_policy, + Turns& turns, + InterruptPolicy& interrupt_policy) { // First create monotonic sections... typedef typename boost::range_value<Turns>::type ip_type; typedef typename ip_type::point_type point_type; - typedef model::box<point_type> box_type; + + typedef model::box + < + typename geometry::robust_point_type + < + point_type, RobustPolicy + >::type + > box_type; typedef typename geometry::sections<box_type, 2> sections_type; sections_type sec1, sec2; - geometry::sectionalize<Reverse1>(geometry1, sec1, 0); - geometry::sectionalize<Reverse2>(geometry2, sec2, 1); + geometry::sectionalize<Reverse1>(geometry1, robust_policy, true, sec1, 0); + geometry::sectionalize<Reverse2>(geometry2, robust_policy, true, sec2, 1); // ... and then partition them, intersecting overlapping sections in visitor method section_visitor < Geometry1, Geometry2, Reverse1, Reverse2, - Turns, TurnPolicy, InterruptPolicy - > visitor(source_id1, geometry1, source_id2, geometry2, turns, interrupt_policy); + Turns, TurnPolicy, RobustPolicy, InterruptPolicy + > visitor(source_id1, geometry1, source_id2, geometry2, robust_policy, turns, interrupt_policy); geometry::partition < @@ -463,13 +524,10 @@ template < typename Range, typename Box, bool ReverseRange, bool ReverseBox, - typename Turns, - typename TurnPolicy, - typename InterruptPolicy + typename TurnPolicy > struct get_turns_cs { - typedef typename boost::range_value<Turns>::type turn_info; typedef typename geometry::point_type<Range>::type point_type; typedef typename geometry::point_type<Box>::type box_point_type; @@ -491,14 +549,16 @@ struct get_turns_cs >::type iterator_type; + template <typename RobustPolicy, typename Turns, typename InterruptPolicy> static inline void apply( int source_id1, Range const& range, int source_id2, Box const& box, + RobustPolicy const& robust_policy, Turns& turns, InterruptPolicy& interrupt_policy, int multi_index = -1, int ring_index = -1) { - if (boost::size(range) <= 1) + if ( boost::size(range) <= 1) { return; } @@ -509,6 +569,8 @@ struct get_turns_cs cview_type cview(range); view_type view(cview); + typename boost::range_size<view_type>::type segments_count1 = boost::size(view) - 1; + iterator_type it = boost::begin(view); ever_circling_iterator<iterator_type> next( @@ -557,9 +619,13 @@ struct get_turns_cs get_turns_with_box(seg_id, source_id2, *prev, *it, *next, bp[0], bp[1], bp[2], bp[3], + // NOTE: some dummy values could be passed below since this would be called only for Polygons and Boxes + index == 0, + unsigned(index) == segments_count1, + robust_policy, turns, interrupt_policy); - // Future performance enhancement: - // return if told by the interrupt policy + // Future performance enhancement: + // return if told by the interrupt policy } } } @@ -585,6 +651,7 @@ private: else return 0; } + template <typename RobustPolicy, typename Turns, typename InterruptPolicy> static inline void get_turns_with_box(segment_identifier const& seg_id, int source_id2, // Points from a range: point_type const& rp0, @@ -595,34 +662,45 @@ private: box_point_type const& bp1, box_point_type const& bp2, box_point_type const& bp3, + bool const is_range_first, + bool const is_range_last, + RobustPolicy const& robust_policy, // Output Turns& turns, InterruptPolicy& interrupt_policy) { + boost::ignore_unused_variable_warning(interrupt_policy); + // Depending on code some relations can be left out typedef typename boost::range_value<Turns>::type turn_info; turn_info ti; ti.operations[0].seg_id = seg_id; - ti.operations[0].other_id = ti.operations[1].seg_id; - ti.operations[1].other_id = seg_id; ti.operations[1].seg_id = segment_identifier(source_id2, -1, -1, 0); TurnPolicy::apply(rp0, rp1, rp2, bp0, bp1, bp2, - ti, std::back_inserter(turns)); + is_range_first, is_range_last, + true, false, + ti, robust_policy, std::back_inserter(turns)); ti.operations[1].seg_id = segment_identifier(source_id2, -1, -1, 1); TurnPolicy::apply(rp0, rp1, rp2, bp1, bp2, bp3, - ti, std::back_inserter(turns)); + is_range_first, is_range_last, + false, false, + ti, robust_policy, std::back_inserter(turns)); ti.operations[1].seg_id = segment_identifier(source_id2, -1, -1, 2); TurnPolicy::apply(rp0, rp1, rp2, bp2, bp3, bp0, - ti, std::back_inserter(turns)); + is_range_first, is_range_last, + false, false, + ti, robust_policy, std::back_inserter(turns)); ti.operations[1].seg_id = segment_identifier(source_id2, -1, -1, 3); TurnPolicy::apply(rp0, rp1, rp2, bp3, bp0, bp1, - ti, std::back_inserter(turns)); + is_range_first, is_range_last, + false, true, + ti, robust_policy, std::back_inserter(turns)); if (InterruptPolicy::enabled) { @@ -638,15 +716,15 @@ template < typename Polygon, typename Box, bool Reverse, bool ReverseBox, - typename Turns, - typename TurnPolicy, - typename InterruptPolicy + typename TurnPolicy > struct get_turns_polygon_cs { + template <typename RobustPolicy, typename Turns, typename InterruptPolicy> static inline void apply( int source_id1, Polygon const& polygon, int source_id2, Box const& box, + RobustPolicy const& robust_policy, Turns& turns, InterruptPolicy& interrupt_policy, int multi_index = -1) { @@ -656,32 +734,118 @@ struct get_turns_polygon_cs < ring_type, Box, Reverse, ReverseBox, - Turns, - TurnPolicy, - InterruptPolicy + TurnPolicy > intersector_type; intersector_type::apply( source_id1, geometry::exterior_ring(polygon), - source_id2, box, turns, interrupt_policy, + source_id2, box, + robust_policy, + turns, interrupt_policy, multi_index, -1); int i = 0; - typename interior_return_type<Polygon const>::type rings - = interior_rings(polygon); - for (BOOST_AUTO_TPL(it, boost::begin(rings)); it != boost::end(rings); - ++it, ++i) + typename interior_return_type<Polygon const>::type + rings = interior_rings(polygon); + for (typename detail::interior_iterator<Polygon const>::type + it = boost::begin(rings); it != boost::end(rings); ++it, ++i) { intersector_type::apply( source_id1, *it, - source_id2, box, turns, interrupt_policy, + source_id2, box, + robust_policy, + turns, interrupt_policy, multi_index, i); } } }; + +template +< + typename Multi, typename Box, + bool Reverse, bool ReverseBox, + typename TurnPolicy +> +struct get_turns_multi_polygon_cs +{ + template <typename RobustPolicy, typename Turns, typename InterruptPolicy> + static inline void apply( + int source_id1, Multi const& multi, + int source_id2, Box const& box, + RobustPolicy const& robust_policy, + Turns& turns, InterruptPolicy& interrupt_policy) + { + typedef typename boost::range_iterator + < + Multi const + >::type iterator_type; + + int i = 0; + for (iterator_type it = boost::begin(multi); + it != boost::end(multi); + ++it, ++i) + { + // Call its single version + get_turns_polygon_cs + < + typename boost::range_value<Multi>::type, Box, + Reverse, ReverseBox, + TurnPolicy + >::apply(source_id1, *it, source_id2, box, + robust_policy, turns, interrupt_policy, i); + } + } +}; + + +// GET_TURN_INFO_TYPE + +template <typename Geometry> +struct topological_tag_base +{ + typedef typename tag_cast<typename tag<Geometry>::type, pointlike_tag, linear_tag, areal_tag>::type type; +}; + +template <typename Geometry1, typename Geometry2, typename AssignPolicy, + typename Tag1 = typename tag<Geometry1>::type, typename Tag2 = typename tag<Geometry2>::type, + typename TagBase1 = typename topological_tag_base<Geometry1>::type, typename TagBase2 = typename topological_tag_base<Geometry2>::type> +struct get_turn_info_type + : overlay::get_turn_info<AssignPolicy> +{}; + +template <typename Geometry1, typename Geometry2, typename AssignPolicy, typename Tag1, typename Tag2> +struct get_turn_info_type<Geometry1, Geometry2, AssignPolicy, Tag1, Tag2, linear_tag, linear_tag> + : overlay::get_turn_info_linear_linear<AssignPolicy> +{}; + +template <typename Geometry1, typename Geometry2, typename AssignPolicy, typename Tag1, typename Tag2> +struct get_turn_info_type<Geometry1, Geometry2, AssignPolicy, Tag1, Tag2, linear_tag, areal_tag> + : overlay::get_turn_info_linear_areal<AssignPolicy> +{}; + +template <typename Geometry1, typename Geometry2, typename SegmentRatio, + typename Tag1 = typename tag<Geometry1>::type, typename Tag2 = typename tag<Geometry2>::type, + typename TagBase1 = typename topological_tag_base<Geometry1>::type, typename TagBase2 = typename topological_tag_base<Geometry2>::type> +struct turn_operation_type +{ + typedef overlay::turn_operation<SegmentRatio> type; +}; + +template <typename Geometry1, typename Geometry2, typename SegmentRatio, typename Tag1, typename Tag2> +struct turn_operation_type<Geometry1, Geometry2, SegmentRatio, Tag1, Tag2, linear_tag, linear_tag> +{ + typedef overlay::turn_operation_linear<SegmentRatio> type; +}; + +template <typename Geometry1, typename Geometry2, typename SegmentRatio, typename Tag1, typename Tag2> +struct turn_operation_type<Geometry1, Geometry2, SegmentRatio, Tag1, Tag2, linear_tag, areal_tag> +{ + typedef overlay::turn_operation_linear<SegmentRatio> type; +}; + }} // namespace detail::get_turns #endif // DOXYGEN_NO_DETAIL @@ -697,18 +861,14 @@ template typename GeometryTag1, typename GeometryTag2, typename Geometry1, typename Geometry2, bool Reverse1, bool Reverse2, - typename Turns, - typename TurnPolicy, - typename InterruptPolicy + typename TurnPolicy > struct get_turns : detail::get_turns::get_turns_generic < Geometry1, Geometry2, Reverse1, Reverse2, - Turns, - TurnPolicy, - InterruptPolicy + TurnPolicy > {}; @@ -717,23 +877,19 @@ template < typename Polygon, typename Box, bool ReversePolygon, bool ReverseBox, - typename Turns, - typename TurnPolicy, - typename InterruptPolicy + typename TurnPolicy > struct get_turns < polygon_tag, box_tag, Polygon, Box, ReversePolygon, ReverseBox, - Turns, - TurnPolicy, - InterruptPolicy + TurnPolicy > : detail::get_turns::get_turns_polygon_cs < Polygon, Box, ReversePolygon, ReverseBox, - Turns, TurnPolicy, InterruptPolicy + TurnPolicy > {}; @@ -742,22 +898,18 @@ template < typename Ring, typename Box, bool ReverseRing, bool ReverseBox, - typename Turns, - typename TurnPolicy, - typename InterruptPolicy + typename TurnPolicy > struct get_turns < ring_tag, box_tag, Ring, Box, ReverseRing, ReverseBox, - Turns, - TurnPolicy, - InterruptPolicy + TurnPolicy > : detail::get_turns::get_turns_cs < Ring, Box, ReverseRing, ReverseBox, - Turns, TurnPolicy, InterruptPolicy + TurnPolicy > {}; @@ -765,28 +917,52 @@ struct get_turns template < + typename MultiPolygon, + typename Box, + bool ReverseMultiPolygon, bool ReverseBox, + typename TurnPolicy +> +struct get_turns + < + multi_polygon_tag, box_tag, + MultiPolygon, Box, + ReverseMultiPolygon, ReverseBox, + TurnPolicy + > + : detail::get_turns::get_turns_multi_polygon_cs + < + MultiPolygon, Box, + ReverseMultiPolygon, ReverseBox, + TurnPolicy + > +{}; + + +template +< typename GeometryTag1, typename GeometryTag2, typename Geometry1, typename Geometry2, bool Reverse1, bool Reverse2, - typename Turns, - typename TurnPolicy, - typename InterruptPolicy + typename TurnPolicy > struct get_turns_reversed { + template <typename RobustPolicy, typename Turns, typename InterruptPolicy> static inline void apply( int source_id1, Geometry1 const& g1, int source_id2, Geometry2 const& g2, - Turns& turns, InterruptPolicy& interrupt_policy) + RobustPolicy const& robust_policy, + Turns& turns, + InterruptPolicy& interrupt_policy) { get_turns < GeometryTag2, GeometryTag1, Geometry2, Geometry1, Reverse2, Reverse1, - Turns, TurnPolicy, - InterruptPolicy - >::apply(source_id2, g2, source_id1, g1, turns, interrupt_policy); + TurnPolicy + >::apply(source_id2, g2, source_id1, g1, robust_policy, + turns, interrupt_policy); } }; @@ -804,6 +980,7 @@ struct get_turns_reversed \tparam Turns type of turn-container (e.g. vector of "intersection/turn point"'s) \param geometry1 \param_geometry \param geometry2 \param_geometry +\param robust_policy policy to handle robustness issues \param turns container which will contain turn points \param interrupt_policy policy determining if process is stopped when intersection is found @@ -814,31 +991,20 @@ template typename AssignPolicy, typename Geometry1, typename Geometry2, + typename RobustPolicy, typename Turns, typename InterruptPolicy > inline void get_turns(Geometry1 const& geometry1, Geometry2 const& geometry2, + RobustPolicy const& robust_policy, Turns& turns, InterruptPolicy& interrupt_policy) { concept::check_concepts_and_equal_dimensions<Geometry1 const, Geometry2 const>(); - typedef typename strategy_intersection - < - typename cs_tag<Geometry1>::type, - Geometry1, - Geometry2, - typename boost::range_value<Turns>::type - >::segment_intersection_strategy_type segment_intersection_strategy_type; - - typedef detail::overlay::get_turn_info - < - typename point_type<Geometry1>::type, - typename point_type<Geometry2>::type, - typename boost::range_value<Turns>::type, - AssignPolicy - > TurnPolicy; + typedef detail::overlay::get_turn_info<AssignPolicy> TurnPolicy; + //typedef detail::get_turns::get_turn_info_type<Geometry1, Geometry2, AssignPolicy> TurnPolicy; boost::mpl::if_c < @@ -849,8 +1015,7 @@ inline void get_turns(Geometry1 const& geometry1, typename tag<Geometry2>::type, Geometry1, Geometry2, Reverse1, Reverse2, - Turns, TurnPolicy, - InterruptPolicy + TurnPolicy >, dispatch::get_turns < @@ -858,15 +1023,18 @@ inline void get_turns(Geometry1 const& geometry1, typename tag<Geometry2>::type, Geometry1, Geometry2, Reverse1, Reverse2, - Turns, TurnPolicy, - InterruptPolicy + TurnPolicy > >::type::apply( 0, geometry1, 1, geometry2, + robust_policy, turns, interrupt_policy); } +#if defined(_MSC_VER) +#pragma warning(pop) +#endif }} // namespace boost::geometry diff --git a/boost/geometry/algorithms/detail/overlay/handle_tangencies.hpp b/boost/geometry/algorithms/detail/overlay/handle_tangencies.hpp index 1e878ca525..085933dd7a 100644 --- a/boost/geometry/algorithms/detail/overlay/handle_tangencies.hpp +++ b/boost/geometry/algorithms/detail/overlay/handle_tangencies.hpp @@ -14,7 +14,17 @@ #include <boost/geometry/algorithms/detail/ring_identifier.hpp> #include <boost/geometry/algorithms/detail/overlay/copy_segment_point.hpp> #include <boost/geometry/algorithms/detail/overlay/turn_info.hpp> +#include <boost/geometry/algorithms/detail/recalculate.hpp> +#include <boost/geometry/policies/robustness/robust_point_type.hpp> +#include <boost/geometry/policies/robustness/segment_ratio_type.hpp> +#include <boost/geometry/policies/robustness/robust_type.hpp> + +#if defined(BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES) +#include <boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp> +#endif + +#include <boost/geometry/geometries/point.hpp> #include <boost/geometry/geometries/segment.hpp> @@ -31,6 +41,7 @@ template typename TurnPoints, typename Indexed, typename Geometry1, typename Geometry2, + typename RobustPolicy, bool Reverse1, bool Reverse2, typename Strategy > @@ -39,10 +50,12 @@ struct sort_in_cluster inline sort_in_cluster(TurnPoints const& turn_points , Geometry1 const& geometry1 , Geometry2 const& geometry2 + , RobustPolicy const& robust_policy , Strategy const& strategy) : m_turn_points(turn_points) , m_geometry1(geometry1) , m_geometry2(geometry2) + , m_rescale_policy(robust_policy) , m_strategy(strategy) {} @@ -51,49 +64,100 @@ private : TurnPoints const& m_turn_points; Geometry1 const& m_geometry1; Geometry2 const& m_geometry2; + RobustPolicy const& m_rescale_policy; Strategy const& m_strategy; typedef typename Indexed::type turn_operation_type; typedef typename geometry::point_type<Geometry1>::type point_type; - typedef model::referring_segment<point_type const> segment_type; + + typedef typename geometry::robust_point_type + < + point_type, + RobustPolicy + >::type robust_point_type; + + // Still necessary in some situations, + // for example #case_102_multi, #case_107_multi, #case_recursive_boxes_3 + inline void get_situation_map(Indexed const& left, Indexed const& right, + robust_point_type& pi_rob, robust_point_type& pj_rob, + robust_point_type& ri_rob, robust_point_type& rj_rob, + robust_point_type& si_rob, robust_point_type& sj_rob) const + { + point_type pi, pj, ri, rj, si, sj; + + geometry::copy_segment_points<Reverse1, Reverse2>(m_geometry1, m_geometry2, + left.subject->seg_id, + pi, pj); + geometry::copy_segment_points<Reverse1, Reverse2>(m_geometry1, m_geometry2, + *left.other_seg_id, + ri, rj); + geometry::copy_segment_points<Reverse1, Reverse2>(m_geometry1, m_geometry2, + *right.other_seg_id, + si, sj); + + geometry::recalculate(pi_rob, pi, m_rescale_policy); + geometry::recalculate(pj_rob, pj, m_rescale_policy); + geometry::recalculate(ri_rob, ri, m_rescale_policy); + geometry::recalculate(rj_rob, rj, m_rescale_policy); + geometry::recalculate(si_rob, si, m_rescale_policy); + geometry::recalculate(sj_rob, sj, m_rescale_policy); + } + +#if BOOST_GEOMETRY_HANDLE_TANGENCIES_WITH_OVERLAP_INFO + // This method was still called but did no effect whatsoever on the results, + // with or without robustness-rescaling. + // Probable cause: we rescale in this file ourselves, ignoring passed policy + // TODO: check this more. + // Besides this, it currently does not compile for yet unknown reasons + // (does not find specialization for segment_ratio_type) + // It is currently only called from the Unit Test "multi_intersection.cpp" // Determine how p/r and p/s are located. - template <typename P> - static inline void overlap_info(P const& pi, P const& pj, - P const& ri, P const& rj, - P const& si, P const& sj, - bool& pr_overlap, bool& ps_overlap, bool& rs_overlap) + inline void overlap_info( + robust_point_type const& pi, robust_point_type const& pj, + robust_point_type const& ri, robust_point_type const& rj, + robust_point_type const& si, robust_point_type const& sj, + bool& pr_overlap, bool& ps_overlap, bool& rs_overlap) const { // Determine how p/r and p/s are located. // One of them is coming from opposite direction. + typedef segment_intersection_points + < + point_type, + typename segment_ratio_type + < + point_type, RobustPolicy + >::type + > intersection_return_type; + typedef strategy::intersection::relate_cartesian_segments < policies::relate::segments_intersection_points < - segment_type, - segment_type, - segment_intersection_points<point_type> + intersection_return_type > > policy; + typedef model::referring_segment<robust_point_type const> segment_type; segment_type p(pi, pj); segment_type r(ri, rj); segment_type s(si, sj); // Get the intersection point (or two points) - segment_intersection_points<point_type> pr = policy::apply(p, r); - segment_intersection_points<point_type> ps = policy::apply(p, s); - segment_intersection_points<point_type> rs = policy::apply(r, s); + intersection_return_type pr = policy::apply(p, r, m_rescale_policy, pi, pj, ri, rj); + intersection_return_type ps = policy::apply(p, s, m_rescale_policy, pi, pj, si, sj); + intersection_return_type rs = policy::apply(r, s, m_rescale_policy, ri, rj, si, sj); // Check on overlap pr_overlap = pr.count == 2; ps_overlap = ps.count == 2; rs_overlap = rs.count == 2; } +#endif -#ifdef BOOST_GEOMETRY_DEBUG_ENRICH +#ifdef BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES inline void debug_consider(int order, Indexed const& left, Indexed const& right, std::string const& header, bool skip = true, @@ -102,19 +166,15 @@ private : { if (skip) return; - point_type pi, pj, ri, rj, si, sj; - geometry::copy_segment_points<Reverse1, Reverse2>(m_geometry1, m_geometry2, - left.subject.seg_id, - pi, pj); - geometry::copy_segment_points<Reverse1, Reverse2>(m_geometry1, m_geometry2, - left.subject.other_id, - ri, rj); - geometry::copy_segment_points<Reverse1, Reverse2>(m_geometry1, m_geometry2, - right.subject.other_id, - si, sj); + std::cout << "Case: " << header << " for " << left.turn_index << " / " << right.turn_index << std::endl; + + robust_point_type pi, pj, ri, rj, si, sj; + get_situation_map(left, right, pi, pj, ri, rj, si, sj); +#if BOOST_GEOMETRY_HANDLE_TANGENCIES_WITH_OVERLAP_INFO bool prc = false, psc = false, rsc = false; overlap_info(pi, pj, ri, rj, si, sj, prc, psc, rsc); +#endif int const side_ri_p = m_strategy.apply(pi, pj, ri); int const side_rj_p = m_strategy.apply(pi, pj, rj); @@ -123,8 +183,7 @@ private : int const side_si_r = m_strategy.apply(ri, rj, si); int const side_sj_r = m_strategy.apply(ri, rj, sj); - std::cout << "Case: " << header << " for " << left.index << " / " << right.index << std::endl; -#ifdef BOOST_GEOMETRY_DEBUG_ENRICH_MORE +#ifdef BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES_MORE std::cout << " Segment p:" << geometry::wkt(pi) << " .. " << geometry::wkt(pj) << std::endl; std::cout << " Segment r:" << geometry::wkt(ri) << " .. " << geometry::wkt(rj) << std::endl; std::cout << " Segment s:" << geometry::wkt(si) << " .. " << geometry::wkt(sj) << std::endl; @@ -136,13 +195,15 @@ private : std::cout << header //<< " order: " << order - << " ops: " << operation_char(left.subject.operation) - << "/" << operation_char(right.subject.operation) + << " ops: " << operation_char(left.subject->operation) + << "/" << operation_char(right.subject->operation) << " ri//p: " << side_ri_p << " si//p: " << side_si_p << " si//r: " << side_si_r +#if BOOST_GEOMETRY_HANDLE_TANGENCIES_WITH_OVERLAP_INFO << " cnts: " << int(prc) << "," << int(psc) << "," << int(rsc) - //<< " idx: " << left.index << "/" << right.index +#endif + //<< " idx: " << left.turn_index << "/" << right.turn_index ; if (! extra.empty()) @@ -167,23 +228,23 @@ private : , std::string const& // header ) const { - bool ret = left.index < right.index; + bool ret = left.turn_index < right.turn_index; // In combination of u/x, x/u: take first union, then blocked. // Solves #88, #61, #56, #80 - if (left.subject.operation == operation_union - && right.subject.operation == operation_blocked) + if (left.subject->operation == operation_union + && right.subject->operation == operation_blocked) { ret = true; } - else if (left.subject.operation == operation_blocked - && right.subject.operation == operation_union) + else if (left.subject->operation == operation_blocked + && right.subject->operation == operation_union) { ret = false; } else { -#ifdef BOOST_GEOMETRY_DEBUG_ENRICH +#if defined(BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES) std::cout << "ux/ux unhandled" << std::endl; #endif } @@ -201,32 +262,32 @@ private : { bool ret = false; - if (left.subject.operation == operation_union - && right.subject.operation == operation_union) + if (left.subject->operation == operation_union + && right.subject->operation == operation_union) { ret = order == 1; } - else if (left.subject.operation == operation_union - && right.subject.operation == operation_blocked) + else if (left.subject->operation == operation_union + && right.subject->operation == operation_blocked) { ret = true; } - else if (right.subject.operation == operation_union - && left.subject.operation == operation_blocked) + else if (right.subject->operation == operation_union + && left.subject->operation == operation_blocked) { ret = false; } - else if (left.subject.operation == operation_union) + else if (left.subject->operation == operation_union) { ret = true; } - else if (right.subject.operation == operation_union) + else if (right.subject->operation == operation_union) { ret = false; } else { -#ifdef BOOST_GEOMETRY_DEBUG_ENRICH +#if defined(BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES) // this still happens in the traverse.cpp test std::cout << " iu/ux unhandled" << std::endl; #endif @@ -245,43 +306,61 @@ private : { //debug_consider(order, left, right, header, false, "iu/ix"); - return left.subject.operation == operation_intersection - && right.subject.operation == operation_intersection ? order == 1 - : left.subject.operation == operation_intersection ? false - : right.subject.operation == operation_intersection ? true + return left.subject->operation == operation_intersection + && right.subject->operation == operation_intersection ? order == 1 + : left.subject->operation == operation_intersection ? false + : right.subject->operation == operation_intersection ? true : order == 1; } + inline bool consider_ix_ix(Indexed const& left, Indexed const& right + , std::string const& // header + ) const + { + // Take first intersection, then blocked. + if (left.subject->operation == operation_intersection + && right.subject->operation == operation_blocked) + { + return true; + } + else if (left.subject->operation == operation_blocked + && right.subject->operation == operation_intersection) + { + return false; + } + + // Default case, should not occur + +#if defined(BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES) + std::cout << "ix/ix unhandled" << std::endl; +#endif + //debug_consider(0, left, right, header, false, "-> return", ret); + + return left.turn_index < right.turn_index; + } + inline bool consider_iu_iu(Indexed const& left, Indexed const& right, - std::string const& header) const + std::string const& header, bool redo = false) const { //debug_consider(0, left, right, header); // In general, order it like "union, intersection". - if (left.subject.operation == operation_intersection - && right.subject.operation == operation_union) + if (left.subject->operation == operation_intersection + && right.subject->operation == operation_union) { //debug_consider(0, left, right, header, false, "i,u", false); return false; } - else if (left.subject.operation == operation_union - && right.subject.operation == operation_intersection) + else if (left.subject->operation == operation_union + && right.subject->operation == operation_intersection) { //debug_consider(0, left, right, header, false, "u,i", true); return true; } - point_type pi, pj, ri, rj, si, sj; - geometry::copy_segment_points<Reverse1, Reverse2>(m_geometry1, m_geometry2, - left.subject.seg_id, - pi, pj); - geometry::copy_segment_points<Reverse1, Reverse2>(m_geometry1, m_geometry2, - left.subject.other_id, - ri, rj); - geometry::copy_segment_points<Reverse1, Reverse2>(m_geometry1, m_geometry2, - right.subject.other_id, - si, sj); + robust_point_type pi, pj, ri, rj, si, sj; + get_situation_map(left, right, pi, pj, ri, rj, si, sj); int const side_ri_p = m_strategy.apply(pi, pj, ri); int const side_si_p = m_strategy.apply(pi, pj, si); @@ -291,8 +370,8 @@ private : if (side_ri_p * side_si_p == 1 && side_si_r != 0) { // Take the most left one - if (left.subject.operation == operation_union - && right.subject.operation == operation_union) + if (left.subject->operation == operation_union + && right.subject->operation == operation_union) { bool ret = side_si_r == 1; //debug_consider(0, left, right, header, false, "same side", ret); @@ -311,14 +390,18 @@ private : debug_consider(0, left, right, header, false, "opp.", ret); return ret; } -#ifdef BOOST_GEOMETRY_DEBUG_ENRICH +#if defined(BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES) std::cout << " iu/iu coming from opposite unhandled" << std::endl; #endif } +#if BOOST_GEOMETRY_HANDLE_TANGENCIES_WITH_OVERLAP_INFO // We need EXTRA information here: are p/r/s overlapping? bool pr_ov = false, ps_ov = false, rs_ov = false; overlap_info(pi, pj, ri, rj, si, sj, pr_ov, ps_ov, rs_ov); +#else + // std::cout << "Boost.Geometry: skipping overlap_info" << std::endl; +#endif // One coming from right (#83,#90) // One coming from left (#90, #94, #95) @@ -326,12 +409,14 @@ private : { bool ret = false; +#if BOOST_GEOMETRY_HANDLE_TANGENCIES_WITH_OVERLAP_INFO if (pr_ov || ps_ov) { int r = side_ri_p != 0 ? side_ri_p : side_si_p; ret = r * side_si_r == 1; } else +#endif { ret = side_si_r == 1; } @@ -348,6 +433,7 @@ private : // Take the one NOT overlapping bool ret = false; bool found = false; +#if BOOST_GEOMETRY_HANDLE_TANGENCIES_WITH_OVERLAP_INFO if (pr_ov && ! ps_ov) { ret = true; @@ -358,6 +444,7 @@ private : ret = false; found = true; } +#endif debug_consider(0, left, right, header, false, "aligned", ret); if (found) @@ -366,11 +453,18 @@ private : } } -#ifdef BOOST_GEOMETRY_DEBUG_ENRICH +#if defined(BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES) std::cout << " iu/iu unhandled" << std::endl; - debug_consider(0, left, right, header, false, "unhandled", left.index < right.index); + debug_consider(0, left, right, header, false, "unhandled", left.turn_index < right.turn_index); #endif - return left.index < right.index; + if (! redo) + { + // In some cases behaviour is not symmetrical. TODO: fix this properly + // OR: alternatively we might consider calling all these functions one-way anyway + return ! consider_iu_iu(right, left, header, true); + } + + return left.turn_index < right.turn_index; } inline bool consider_ii(Indexed const& left, Indexed const& right, @@ -378,16 +472,8 @@ private : { debug_consider(0, left, right, header); - point_type pi, pj, ri, rj, si, sj; - geometry::copy_segment_points<Reverse1, Reverse2>(m_geometry1, m_geometry2, - left.subject.seg_id, - pi, pj); - geometry::copy_segment_points<Reverse1, Reverse2>(m_geometry1, m_geometry2, - left.subject.other_id, - ri, rj); - geometry::copy_segment_points<Reverse1, Reverse2>(m_geometry1, m_geometry2, - right.subject.other_id, - si, sj); + robust_point_type pi, pj, ri, rj, si, sj; + get_situation_map(left, right, pi, pj, ri, rj, si, sj); int const side_ri_p = m_strategy.apply(pi, pj, ri); int const side_si_p = m_strategy.apply(pi, pj, si); @@ -402,84 +488,89 @@ private : bool const ret = side_si_r != 1; return ret; } - return left.index < right.index; + return left.turn_index < right.turn_index; } public : inline bool operator()(Indexed const& left, Indexed const& right) const { - bool const default_order = left.index < right.index; + bool const default_order = left.turn_index < right.turn_index; - if ((m_turn_points[left.index].discarded || left.discarded) - && (m_turn_points[right.index].discarded || right.discarded)) + if ((m_turn_points[left.turn_index].discarded || left.discarded) + && (m_turn_points[right.turn_index].discarded || right.discarded)) { return default_order; } - else if (m_turn_points[left.index].discarded || left.discarded) + else if (m_turn_points[left.turn_index].discarded || left.discarded) { // Be careful to sort discarded first, then all others return true; } - else if (m_turn_points[right.index].discarded || right.discarded) + else if (m_turn_points[right.turn_index].discarded || right.discarded) { // See above so return false here such that right (discarded) // is sorted before left (not discarded) return false; } - else if (m_turn_points[left.index].combination(operation_blocked, operation_union) - && m_turn_points[right.index].combination(operation_blocked, operation_union)) + else if (m_turn_points[left.turn_index].combination(operation_blocked, operation_union) + && m_turn_points[right.turn_index].combination(operation_blocked, operation_union)) { // ux/ux return consider_ux_ux(left, right, "ux/ux"); } - else if (m_turn_points[left.index].both(operation_union) - && m_turn_points[right.index].both(operation_union)) + else if (m_turn_points[left.turn_index].both(operation_union) + && m_turn_points[right.turn_index].both(operation_union)) { // uu/uu, Order is arbitrary // Note: uu/uu is discarded now before so this point will // not be reached. return default_order; } - else if (m_turn_points[left.index].combination(operation_intersection, operation_union) - && m_turn_points[right.index].combination(operation_intersection, operation_union)) + else if (m_turn_points[left.turn_index].combination(operation_intersection, operation_union) + && m_turn_points[right.turn_index].combination(operation_intersection, operation_union)) { return consider_iu_iu(left, right, "iu/iu"); } - else if (m_turn_points[left.index].both(operation_intersection) - && m_turn_points[right.index].both(operation_intersection)) + else if (m_turn_points[left.turn_index].combination(operation_intersection, operation_blocked) + && m_turn_points[right.turn_index].combination(operation_intersection, operation_blocked)) + { + return consider_ix_ix(left, right, "ix/ix"); + } + else if (m_turn_points[left.turn_index].both(operation_intersection) + && m_turn_points[right.turn_index].both(operation_intersection)) { return consider_ii(left, right, "ii/ii"); } - else if (m_turn_points[left.index].combination(operation_union, operation_blocked) - && m_turn_points[right.index].combination(operation_intersection, operation_union)) + else if (m_turn_points[left.turn_index].combination(operation_union, operation_blocked) + && m_turn_points[right.turn_index].combination(operation_intersection, operation_union)) { return consider_iu_ux(left, right, -1, "ux/iu"); } - else if (m_turn_points[left.index].combination(operation_intersection, operation_union) - && m_turn_points[right.index].combination(operation_union, operation_blocked)) + else if (m_turn_points[left.turn_index].combination(operation_intersection, operation_union) + && m_turn_points[right.turn_index].combination(operation_union, operation_blocked)) { return consider_iu_ux(left, right, 1, "iu/ux"); } - else if (m_turn_points[left.index].combination(operation_intersection, operation_blocked) - && m_turn_points[right.index].combination(operation_intersection, operation_union)) + else if (m_turn_points[left.turn_index].combination(operation_intersection, operation_blocked) + && m_turn_points[right.turn_index].combination(operation_intersection, operation_union)) { return consider_iu_ix(left, right, 1, "ix/iu"); } - else if (m_turn_points[left.index].combination(operation_intersection, operation_union) - && m_turn_points[right.index].combination(operation_intersection, operation_blocked)) + else if (m_turn_points[left.turn_index].combination(operation_intersection, operation_union) + && m_turn_points[right.turn_index].combination(operation_intersection, operation_blocked)) { return consider_iu_ix(left, right, -1, "iu/ix"); } - else if (m_turn_points[left.index].method != method_equal - && m_turn_points[right.index].method == method_equal + else if (m_turn_points[left.turn_index].method != method_equal + && m_turn_points[right.turn_index].method == method_equal ) { // If one of them was EQUAL or CONTINUES, it should always come first return false; } - else if (m_turn_points[left.index].method == method_equal - && m_turn_points[right.index].method != method_equal + else if (m_turn_points[left.turn_index].method == method_equal + && m_turn_points[right.turn_index].method != method_equal ) { return true; @@ -487,13 +578,13 @@ public : // Now we have no clue how to sort. -#ifdef BOOST_GEOMETRY_DEBUG_ENRICH - std::cout << " Consider: " << operation_char(m_turn_points[left.index].operations[0].operation) - << operation_char(m_turn_points[left.index].operations[1].operation) - << "/" << operation_char(m_turn_points[right.index].operations[0].operation) - << operation_char(m_turn_points[right.index].operations[1].operation) - << " " << " Take " << left.index << " < " << right.index - << std::cout; +#if defined(BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES) + std::cout << " Consider: " << operation_char(m_turn_points[left.turn_index].operations[0].operation) + << operation_char(m_turn_points[left.turn_index].operations[1].operation) + << "/" << operation_char(m_turn_points[right.turn_index].operations[0].operation) + << operation_char(m_turn_points[right.turn_index].operations[1].operation) + << " " << " Take " << left.turn_index << " < " << right.turn_index + << std::endl; #endif return default_order; @@ -523,8 +614,8 @@ inline void inspect_cluster(Iterator begin_cluster, Iterator end_cluster, std::map<std::pair<operation_type, operation_type>, int> inspection; for (Iterator it = begin_cluster; it != end_cluster; ++it) { - operation_type first = turn_points[it->index].operations[0].operation; - operation_type second = turn_points[it->index].operations[1].operation; + operation_type first = turn_points[it->turn_index].operations[0].operation; + operation_type second = turn_points[it->turn_index].operations[1].operation; if (first > second) { std::swap(first, second); @@ -553,7 +644,7 @@ inline void inspect_cluster(Iterator begin_cluster, Iterator end_cluster, // Because (in case of not discarding iu) correctly ordering of ii/iu appears impossible for (Iterator it = begin_cluster; it != end_cluster; ++it) { - if (turn_points[it->index].combination(operation_intersection, operation_union)) + if (turn_points[it->turn_index].combination(operation_intersection, operation_union)) { it->discarded = true; } @@ -569,7 +660,7 @@ inline void inspect_cluster(Iterator begin_cluster, Iterator end_cluster, if (! it->discarded) { nd_count++; - if (turn_points[it->index].both(operation_continue)) + if (turn_points[it->turn_index].both(operation_continue)) { cc_count++; } @@ -585,7 +676,7 @@ inline void inspect_cluster(Iterator begin_cluster, Iterator end_cluster, { for (Iterator it = begin_cluster; it != end_cluster; ++it) { - if (turn_points[it->index].both(operation_continue)) + if (turn_points[it->turn_index].both(operation_continue)) { it->discarded = true; } @@ -602,12 +693,14 @@ template typename TurnPoints, typename Geometry1, typename Geometry2, + typename RobustPolicy, typename Strategy > inline void handle_cluster(Iterator begin_cluster, Iterator end_cluster, TurnPoints& turn_points, operation_type for_operation, Geometry1 const& geometry1, Geometry2 const& geometry2, + RobustPolicy& robust_policy, Strategy const& strategy) { // First inspect and (possibly) discard rows @@ -622,31 +715,31 @@ inline void handle_cluster(Iterator begin_cluster, Iterator end_cluster, TurnPoints, IndexType, Geometry1, Geometry2, + RobustPolicy, Reverse1, Reverse2, Strategy - >(turn_points, geometry1, geometry2, strategy)); - + >(turn_points, geometry1, geometry2, robust_policy, strategy)); -#ifdef BOOST_GEOMETRY_DEBUG_ENRICH +#if defined(BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES) typedef typename IndexType::type operations_type; - operations_type const& op = turn_points[begin_cluster->index].operations[begin_cluster->operation_index]; - std::cout << "Clustered points on equal distance " << op.enriched.distance << std::endl; - std::cout << "->Indexes "; + operations_type const& op = turn_points[begin_cluster->turn_index].operations[begin_cluster->operation_index]; + std::cout << std::endl << "Clustered points on equal distance " << op.fraction << std::endl; + std::cout << "->Indexes "; for (Iterator it = begin_cluster; it != end_cluster; ++it) { - std::cout << " " << it->index; + std::cout << " " << it->turn_index; } std::cout << std::endl << "->Methods: "; for (Iterator it = begin_cluster; it != end_cluster; ++it) { - std::cout << " " << method_char(turn_points[it->index].method); + std::cout << " " << method_char(turn_points[it->turn_index].method); } std::cout << std::endl << "->Operations: "; for (Iterator it = begin_cluster; it != end_cluster; ++it) { - std::cout << " " << operation_char(turn_points[it->index].operations[0].operation) - << operation_char(turn_points[it->index].operations[1].operation); + std::cout << " " << operation_char(turn_points[it->turn_index].operations[0].operation) + << operation_char(turn_points[it->turn_index].operations[1].operation); } std::cout << std::endl << "->Discarded: "; for (Iterator it = begin_cluster; it != end_cluster; ++it) @@ -656,7 +749,7 @@ inline void handle_cluster(Iterator begin_cluster, Iterator end_cluster, std::cout << std::endl; //<< "\tOn segments: " << prev_op.seg_id << " / " << prev_op.other_id //<< " and " << op.seg_id << " / " << op.other_id - //<< geometry::distance(turn_points[prev->index].point, turn_points[it->index].point) + //<< geometry::distance(turn_points[prev->turn_index].point, turn_points[it->turn_index].point) #endif } diff --git a/boost/geometry/algorithms/detail/overlay/intersection_box_box.hpp b/boost/geometry/algorithms/detail/overlay/intersection_box_box.hpp new file mode 100644 index 0000000000..dd041b0d7d --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/intersection_box_box.hpp @@ -0,0 +1,84 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. + +// 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) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_INTERSECTION_BOX_BOX_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_INTERSECTION_BOX_BOX_HPP + + +#include <boost/geometry/core/access.hpp> +#include <boost/geometry/core/coordinate_type.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace intersection +{ + +template <std::size_t Dimension, std::size_t DimensionCount> +struct intersection_box_box +{ + template + < + typename Box1, typename Box2, + typename RobustPolicy, + typename BoxOut, + typename Strategy + > + static inline bool apply(Box1 const& box1, + Box2 const& box2, + RobustPolicy const& robust_policy, + BoxOut& box_out, + Strategy const& strategy) + { + typedef typename coordinate_type<BoxOut>::type ct; + + ct min1 = get<min_corner, Dimension>(box1); + ct min2 = get<min_corner, Dimension>(box2); + ct max1 = get<max_corner, Dimension>(box1); + ct max2 = get<max_corner, Dimension>(box2); + + if (max1 < min2 || max2 < min1) + { + return false; + } + // Set dimensions of output coordinate + set<min_corner, Dimension>(box_out, min1 < min2 ? min2 : min1); + set<max_corner, Dimension>(box_out, max1 > max2 ? max2 : max1); + + return intersection_box_box<Dimension + 1, DimensionCount> + ::apply(box1, box2, robust_policy, box_out, strategy); + } +}; + +template <std::size_t DimensionCount> +struct intersection_box_box<DimensionCount, DimensionCount> +{ + template + < + typename Box1, typename Box2, + typename RobustPolicy, + typename BoxOut, + typename Strategy + > + static inline bool apply(Box1 const&, Box2 const&, + RobustPolicy const&, BoxOut&, Strategy const&) + { + return true; + } +}; + + +}} // namespace detail::intersection +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_INTERSECTION_BOX_BOX_HPP diff --git a/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp b/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp index 8bca790d74..a13a627456 100644 --- a/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp +++ b/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp @@ -1,6 +1,11 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. + +// This file was modified by Oracle on 2014. +// Modifications copyright (c) 2014 Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -28,8 +33,17 @@ #include <boost/geometry/algorithms/detail/overlay/overlay.hpp> #include <boost/geometry/algorithms/detail/overlay/overlay_type.hpp> #include <boost/geometry/algorithms/detail/overlay/follow.hpp> + +#include <boost/geometry/policies/robustness/robust_point_type.hpp> +#include <boost/geometry/policies/robustness/segment_ratio_type.hpp> +#include <boost/geometry/policies/robustness/get_rescale_policy.hpp> + #include <boost/geometry/views/segment_view.hpp> +#include <boost/geometry/algorithms/detail/overlay/linear_linear.hpp> +#include <boost/geometry/algorithms/detail/overlay/pointlike_pointlike.hpp> + + #if defined(BOOST_GEOMETRY_DEBUG_FOLLOW) #include <boost/foreach.hpp> #endif @@ -41,31 +55,64 @@ namespace boost { namespace geometry namespace detail { namespace intersection { -template -< - typename Segment1, typename Segment2, - typename OutputIterator, typename PointOut, - typename Strategy -> +template <typename PointOut> struct intersection_segment_segment_point { + template + < + typename Segment1, typename Segment2, + typename RobustPolicy, + typename OutputIterator, typename Strategy + > static inline OutputIterator apply(Segment1 const& segment1, - Segment2 const& segment2, OutputIterator out, + Segment2 const& segment2, + RobustPolicy const& robust_policy, + OutputIterator out, Strategy const& ) { typedef typename point_type<PointOut>::type point_type; + typedef typename geometry::robust_point_type + < + typename geometry::point_type<Segment1>::type, + RobustPolicy + >::type robust_point_type; + + // TODO: rescale segment -> robust points + robust_point_type pi_rob, pj_rob, qi_rob, qj_rob; + { + // Workaround: + point_type pi, pj, qi, qj; + assign_point_from_index<0>(segment1, pi); + assign_point_from_index<1>(segment1, pj); + assign_point_from_index<0>(segment2, qi); + assign_point_from_index<1>(segment2, qj); + geometry::recalculate(pi_rob, pi, robust_policy); + geometry::recalculate(pj_rob, pj, robust_policy); + geometry::recalculate(qi_rob, qi, robust_policy); + geometry::recalculate(qj_rob, qj, robust_policy); + } + // Get the intersection point (or two points) - segment_intersection_points<point_type> is - = strategy::intersection::relate_cartesian_segments + typedef segment_intersection_points + < + point_type, + typename segment_ratio_type + < + point_type, RobustPolicy + >::type + > intersection_return_type; + + typedef strategy::intersection::relate_cartesian_segments < policies::relate::segments_intersection_points < - Segment1, - Segment2, - segment_intersection_points<point_type> + intersection_return_type > - >::apply(segment1, segment2); + > policy; + + intersection_return_type is = policy::apply(segment1, segment2, + robust_policy, pi_rob, pj_rob, qi_rob, qj_rob); for (std::size_t i = 0; i < is.count; i++) { @@ -77,24 +124,31 @@ struct intersection_segment_segment_point } }; -template -< - typename Linestring1, typename Linestring2, - typename OutputIterator, typename PointOut, - typename Strategy -> +template <typename PointOut> struct intersection_linestring_linestring_point { + template + < + typename Linestring1, typename Linestring2, + typename RobustPolicy, + typename OutputIterator, typename Strategy + > static inline OutputIterator apply(Linestring1 const& linestring1, - Linestring2 const& linestring2, OutputIterator out, + Linestring2 const& linestring2, + RobustPolicy const& robust_policy, + OutputIterator out, Strategy const& ) { typedef typename point_type<PointOut>::type point_type; - typedef detail::overlay::turn_info<point_type> turn_info; + typedef detail::overlay::turn_info + < + point_type, + typename segment_ratio_type<point_type, RobustPolicy>::type + > turn_info; std::deque<turn_info> turns; - geometry::get_intersection_points(linestring1, linestring2, turns); + geometry::get_intersection_points(linestring1, linestring2, robust_policy, turns); for (typename boost::range_iterator<std::deque<turn_info> const>::type it = boost::begin(turns); it != boost::end(turns); ++it) @@ -112,25 +166,15 @@ struct intersection_linestring_linestring_point */ template < - typename LineString, typename Areal, bool ReverseAreal, - typename OutputIterator, typename LineStringOut, - overlay_type OverlayType, - typename Strategy + typename LineStringOut, + overlay_type OverlayType > struct intersection_of_linestring_with_areal { - typedef detail::overlay::follow - < - LineStringOut, - LineString, - Areal, - OverlayType - > follower; - #if defined(BOOST_GEOMETRY_DEBUG_FOLLOW) template <typename Turn, typename Operation> - static inline void debug_follow(Turn const& turn, Operation op, + static inline void debug_follow(Turn const& turn, Operation op, int index) { std::cout << index @@ -145,7 +189,14 @@ struct intersection_of_linestring_with_areal } #endif + template + < + typename LineString, typename Areal, + typename RobustPolicy, + typename OutputIterator, typename Strategy + > static inline OutputIterator apply(LineString const& linestring, Areal const& areal, + RobustPolicy const& robust_policy, OutputIterator out, Strategy const& ) { @@ -154,9 +205,20 @@ struct intersection_of_linestring_with_areal return out; } - typedef typename point_type<LineStringOut>::type point_type; + typedef detail::overlay::follow + < + LineStringOut, + LineString, + Areal, + OverlayType + > follower; - typedef detail::overlay::traversal_turn_info<point_type> turn_info; + typedef typename point_type<LineStringOut>::type point_type; + typedef detail::overlay::traversal_turn_info + < + point_type, + typename geometry::segment_ratio_type<point_type, RobustPolicy>::type + > turn_info; std::deque<turn_info> turns; detail::get_turns::no_interrupt_policy policy; @@ -164,12 +226,12 @@ struct intersection_of_linestring_with_areal < false, (OverlayType == overlay_intersection ? ReverseAreal : !ReverseAreal), - detail::overlay::calculate_distance_policy - >(linestring, areal, turns, policy); + detail::overlay::assign_null_policy + >(linestring, areal, robust_policy, turns, policy); if (turns.empty()) { - // No intersection points, it is either completely + // No intersection points, it is either completely // inside (interior + borders) // or completely outside @@ -181,8 +243,7 @@ struct intersection_of_linestring_with_areal return out; } - - if (follower::included(border_point, areal)) + if (follower::included(border_point, areal, robust_policy)) { LineStringOut copy; geometry::convert(linestring, copy); @@ -203,7 +264,7 @@ struct intersection_of_linestring_with_areal ( linestring, areal, geometry::detail::overlay::operation_intersection, - turns, out + turns, robust_policy, out ); } }; @@ -220,18 +281,22 @@ namespace dispatch template < - // tag dispatching: - typename TagIn1, typename TagIn2, typename TagOut, - // orientation - // metafunction finetuning helpers: - bool Areal1, bool Areal2, bool ArealOut, // real types typename Geometry1, typename Geometry2, - bool Reverse1, bool Reverse2, bool ReverseOut, - typename OutputIterator, typename GeometryOut, overlay_type OverlayType, - typename Strategy + // orientation + bool Reverse1 = detail::overlay::do_reverse<geometry::point_order<Geometry1>::value>::value, + bool Reverse2 = detail::overlay::do_reverse<geometry::point_order<Geometry2>::value>::value, + bool ReverseOut = detail::overlay::do_reverse<geometry::point_order<GeometryOut>::value>::value, + // tag dispatching: + typename TagIn1 = typename geometry::tag<Geometry1>::type, + typename TagIn2 = typename geometry::tag<Geometry2>::type, + typename TagOut = typename geometry::tag<GeometryOut>::type, + // metafunction finetuning helpers: + bool Areal1 = geometry::is_areal<Geometry1>::value, + bool Areal2 = geometry::is_areal<Geometry2>::value, + bool ArealOut = geometry::is_areal<GeometryOut>::value > struct intersection_insert { @@ -245,124 +310,106 @@ struct intersection_insert template < - typename TagIn1, typename TagIn2, typename TagOut, typename Geometry1, typename Geometry2, - bool Reverse1, bool Reverse2, bool ReverseOut, - typename OutputIterator, typename GeometryOut, overlay_type OverlayType, - typename Strategy + bool Reverse1, bool Reverse2, bool ReverseOut, + typename TagIn1, typename TagIn2, typename TagOut > struct intersection_insert < - TagIn1, TagIn2, TagOut, - true, true, true, Geometry1, Geometry2, - Reverse1, Reverse2, ReverseOut, - OutputIterator, GeometryOut, + GeometryOut, OverlayType, - Strategy + Reverse1, Reverse2, ReverseOut, + TagIn1, TagIn2, TagOut, + true, true, true > : detail::overlay::overlay - <Geometry1, Geometry2, Reverse1, Reverse2, ReverseOut, OutputIterator, GeometryOut, OverlayType, Strategy> + <Geometry1, Geometry2, Reverse1, Reverse2, ReverseOut, GeometryOut, OverlayType> {}; // Any areal type with box: template < - typename TagIn, typename TagOut, typename Geometry, typename Box, - bool Reverse1, bool Reverse2, bool ReverseOut, - typename OutputIterator, typename GeometryOut, overlay_type OverlayType, - typename Strategy + bool Reverse1, bool Reverse2, bool ReverseOut, + typename TagIn, typename TagOut > struct intersection_insert < - TagIn, box_tag, TagOut, - true, true, true, Geometry, Box, - Reverse1, Reverse2, ReverseOut, - OutputIterator, GeometryOut, + GeometryOut, OverlayType, - Strategy + Reverse1, Reverse2, ReverseOut, + TagIn, box_tag, TagOut, + true, true, true > : detail::overlay::overlay - <Geometry, Box, Reverse1, Reverse2, ReverseOut, OutputIterator, GeometryOut, OverlayType, Strategy> + <Geometry, Box, Reverse1, Reverse2, ReverseOut, GeometryOut, OverlayType> {}; template < typename Segment1, typename Segment2, - bool Reverse1, bool Reverse2, bool ReverseOut, - typename OutputIterator, typename GeometryOut, + typename GeometryOut, overlay_type OverlayType, - typename Strategy + bool Reverse1, bool Reverse2, bool ReverseOut > struct intersection_insert < - segment_tag, segment_tag, point_tag, - false, false, false, Segment1, Segment2, + GeometryOut, + OverlayType, Reverse1, Reverse2, ReverseOut, - OutputIterator, GeometryOut, - OverlayType, Strategy - > : detail::intersection::intersection_segment_segment_point - < - Segment1, Segment2, - OutputIterator, GeometryOut, - Strategy - > + segment_tag, segment_tag, point_tag, + false, false, false + > : detail::intersection::intersection_segment_segment_point<GeometryOut> {}; template < typename Linestring1, typename Linestring2, - bool Reverse1, bool Reverse2, bool ReverseOut, - typename OutputIterator, typename GeometryOut, + typename GeometryOut, overlay_type OverlayType, - typename Strategy + bool Reverse1, bool Reverse2, bool ReverseOut > struct intersection_insert < - linestring_tag, linestring_tag, point_tag, - false, false, false, Linestring1, Linestring2, + GeometryOut, + OverlayType, Reverse1, Reverse2, ReverseOut, - OutputIterator, GeometryOut, - OverlayType, Strategy - > : detail::intersection::intersection_linestring_linestring_point - < - Linestring1, Linestring2, - OutputIterator, GeometryOut, - Strategy - > + linestring_tag, linestring_tag, point_tag, + false, false, false + > : detail::intersection::intersection_linestring_linestring_point<GeometryOut> {}; template < typename Linestring, typename Box, - bool Reverse1, bool Reverse2, bool ReverseOut, - typename OutputIterator, typename GeometryOut, - overlay_type OverlayType, - typename Strategy + typename GeometryOut, + bool Reverse1, bool Reverse2, bool ReverseOut > struct intersection_insert < - linestring_tag, box_tag, linestring_tag, - false, true, false, Linestring, Box, + GeometryOut, + overlay_intersection, Reverse1, Reverse2, ReverseOut, - OutputIterator, GeometryOut, - OverlayType, - Strategy + linestring_tag, box_tag, linestring_tag, + false, true, false > { + template <typename RobustPolicy, typename OutputIterator, typename Strategy> static inline OutputIterator apply(Linestring const& linestring, - Box const& box, OutputIterator out, Strategy const& ) + Box const& box, + RobustPolicy const& , + OutputIterator out, Strategy const& ) { typedef typename point_type<GeometryOut>::type point_type; strategy::intersection::liang_barsky<Box, point_type> lb_strategy; @@ -375,27 +422,23 @@ struct intersection_insert template < typename Linestring, typename Polygon, - bool ReverseLinestring, bool ReversePolygon, bool ReverseOut, - typename OutputIterator, typename GeometryOut, + typename GeometryOut, overlay_type OverlayType, - typename Strategy + bool ReverseLinestring, bool ReversePolygon, bool ReverseOut > struct intersection_insert < - linestring_tag, polygon_tag, linestring_tag, - false, true, false, Linestring, Polygon, - ReverseLinestring, ReversePolygon, ReverseOut, - OutputIterator, GeometryOut, + GeometryOut, OverlayType, - Strategy + ReverseLinestring, ReversePolygon, ReverseOut, + linestring_tag, polygon_tag, linestring_tag, + false, true, false > : detail::intersection::intersection_of_linestring_with_areal < - Linestring, Polygon, ReversePolygon, - OutputIterator, GeometryOut, - OverlayType, - Strategy + GeometryOut, + OverlayType > {}; @@ -403,51 +446,48 @@ struct intersection_insert template < typename Linestring, typename Ring, - bool ReverseLinestring, bool ReverseRing, bool ReverseOut, - typename OutputIterator, typename GeometryOut, + typename GeometryOut, overlay_type OverlayType, - typename Strategy + bool ReverseLinestring, bool ReverseRing, bool ReverseOut > struct intersection_insert < - linestring_tag, ring_tag, linestring_tag, - false, true, false, Linestring, Ring, - ReverseLinestring, ReverseRing, ReverseOut, - OutputIterator, GeometryOut, + GeometryOut, OverlayType, - Strategy + ReverseLinestring, ReverseRing, ReverseOut, + linestring_tag, ring_tag, linestring_tag, + false, true, false > : detail::intersection::intersection_of_linestring_with_areal < - Linestring, Ring, ReverseRing, - OutputIterator, GeometryOut, - OverlayType, - Strategy + GeometryOut, + OverlayType > {}; template < typename Segment, typename Box, - bool Reverse1, bool Reverse2, bool ReverseOut, - typename OutputIterator, typename GeometryOut, + typename GeometryOut, overlay_type OverlayType, - typename Strategy + bool Reverse1, bool Reverse2, bool ReverseOut > struct intersection_insert < - segment_tag, box_tag, linestring_tag, - false, true, false, Segment, Box, - Reverse1, Reverse2, ReverseOut, - OutputIterator, GeometryOut, + GeometryOut, OverlayType, - Strategy + Reverse1, Reverse2, ReverseOut, + segment_tag, box_tag, linestring_tag, + false, true, false > { + template <typename RobustPolicy, typename OutputIterator, typename Strategy> static inline OutputIterator apply(Segment const& segment, - Box const& box, OutputIterator out, Strategy const& ) + Box const& box, + RobustPolicy const& ,// TODO: propagate to clip_range_with_box + OutputIterator out, Strategy const& ) { geometry::segment_view<Segment> range(segment); @@ -460,37 +500,42 @@ struct intersection_insert template < - typename Tag1, typename Tag2, - bool Areal1, bool Areal2, typename Geometry1, typename Geometry2, - bool Reverse1, bool Reverse2, bool ReverseOut, - typename OutputIterator, typename PointOut, + typename PointOut, overlay_type OverlayType, - typename Strategy + bool Reverse1, bool Reverse2, bool ReverseOut, + typename Tag1, typename Tag2, + bool Areal1, bool Areal2 > struct intersection_insert < - Tag1, Tag2, point_tag, - Areal1, Areal2, false, Geometry1, Geometry2, - Reverse1, Reverse2, ReverseOut, - OutputIterator, PointOut, + PointOut, OverlayType, - Strategy + Reverse1, Reverse2, ReverseOut, + Tag1, Tag2, point_tag, + Areal1, Areal2, false > { + template <typename RobustPolicy, typename OutputIterator, typename Strategy> static inline OutputIterator apply(Geometry1 const& geometry1, - Geometry2 const& geometry2, OutputIterator out, Strategy const& ) + Geometry2 const& geometry2, + RobustPolicy const& robust_policy, + OutputIterator out, Strategy const& ) { - typedef detail::overlay::turn_info<PointOut> turn_info; + typedef detail::overlay::turn_info + < + PointOut, + typename segment_ratio_type<PointOut, RobustPolicy>::type + > turn_info; std::vector<turn_info> turns; detail::get_turns::no_interrupt_policy policy; geometry::get_turns < false, false, detail::overlay::assign_null_policy - >(geometry1, geometry2, turns, policy); + >(geometry1, geometry2, robust_policy, turns, policy); for (typename std::vector<turn_info>::const_iterator it = turns.begin(); it != turns.end(); ++it) { @@ -504,35 +549,156 @@ struct intersection_insert template < - typename GeometryTag1, typename GeometryTag2, typename GeometryTag3, - bool Areal1, bool Areal2, bool ArealOut, - typename Geometry1, typename Geometry2, - bool Reverse1, bool Reverse2, bool ReverseOut, - typename OutputIterator, typename GeometryOut, + typename Geometry1, typename Geometry2, typename GeometryOut, overlay_type OverlayType, - typename Strategy + bool Reverse1, bool Reverse2, bool ReverseOut > struct intersection_insert_reversed { + template <typename RobustPolicy, typename OutputIterator, typename Strategy> static inline OutputIterator apply(Geometry1 const& g1, - Geometry2 const& g2, OutputIterator out, + Geometry2 const& g2, + RobustPolicy const& robust_policy, + OutputIterator out, Strategy const& strategy) { return intersection_insert < - GeometryTag2, GeometryTag1, GeometryTag3, - Areal2, Areal1, ArealOut, - Geometry2, Geometry1, - Reverse2, Reverse1, ReverseOut, - OutputIterator, GeometryOut, + Geometry2, Geometry1, GeometryOut, OverlayType, - Strategy - >::apply(g2, g1, out, strategy); + Reverse2, Reverse1, ReverseOut + >::apply(g2, g1, robust_policy, out, strategy); } }; +// dispatch for non-areal geometries +template +< + typename Geometry1, typename Geometry2, typename GeometryOut, + overlay_type OverlayType, + bool Reverse1, bool Reverse2, bool ReverseOut, + typename TagIn1, typename TagIn2 +> +struct intersection_insert + < + Geometry1, Geometry2, GeometryOut, + OverlayType, + Reverse1, Reverse2, ReverseOut, + TagIn1, TagIn2, linestring_tag, + false, false, false + > : intersection_insert + < + Geometry1, Geometry2, GeometryOut, + OverlayType, + Reverse1, Reverse2, ReverseOut, + typename tag_cast<TagIn1, pointlike_tag, linear_tag>::type, + typename tag_cast<TagIn2, pointlike_tag, linear_tag>::type, + linestring_tag, + false, false, false + > +{}; + + +// dispatch for difference/intersection of linear geometries +template +< + typename Linear1, typename Linear2, typename LineStringOut, + overlay_type OverlayType, + bool Reverse1, bool Reverse2, bool ReverseOut +> +struct intersection_insert + < + Linear1, Linear2, LineStringOut, OverlayType, + Reverse1, Reverse2, ReverseOut, + linear_tag, linear_tag, linestring_tag, + false, false, false + > : detail::overlay::linear_linear_linestring + < + Linear1, Linear2, LineStringOut, OverlayType + > +{}; + + +// dispatch for difference/intersection of point-like geometries + +template +< + typename Point1, typename Point2, typename PointOut, + overlay_type OverlayType, + bool Reverse1, bool Reverse2, bool ReverseOut +> +struct intersection_insert + < + Point1, Point2, PointOut, OverlayType, + Reverse1, Reverse2, ReverseOut, + point_tag, point_tag, point_tag, + false, false, false + > : detail::overlay::point_point_point + < + Point1, Point2, PointOut, OverlayType + > +{}; + + +template +< + typename MultiPoint, typename Point, typename PointOut, + overlay_type OverlayType, + bool Reverse1, bool Reverse2, bool ReverseOut +> +struct intersection_insert + < + MultiPoint, Point, PointOut, OverlayType, + Reverse1, Reverse2, ReverseOut, + multi_point_tag, point_tag, point_tag, + false, false, false + > : detail::overlay::multipoint_point_point + < + MultiPoint, Point, PointOut, OverlayType + > +{}; + + +template +< + typename Point, typename MultiPoint, typename PointOut, + overlay_type OverlayType, + bool Reverse1, bool Reverse2, bool ReverseOut +> +struct intersection_insert + < + Point, MultiPoint, PointOut, OverlayType, + Reverse1, Reverse2, ReverseOut, + point_tag, multi_point_tag, point_tag, + false, false, false + > : detail::overlay::point_multipoint_point + < + Point, MultiPoint, PointOut, OverlayType + > +{}; + + +template +< + typename MultiPoint1, typename MultiPoint2, typename PointOut, + overlay_type OverlayType, + bool Reverse1, bool Reverse2, bool ReverseOut +> +struct intersection_insert + < + MultiPoint1, MultiPoint2, PointOut, OverlayType, + Reverse1, Reverse2, ReverseOut, + multi_point_tag, multi_point_tag, point_tag, + false, false, false + > : detail::overlay::multipoint_multipoint_point + < + MultiPoint1, MultiPoint2, PointOut, OverlayType + > +{}; + + } // namespace dispatch #endif // DOXYGEN_NO_DISPATCH @@ -548,50 +714,37 @@ template bool ReverseSecond, overlay_type OverlayType, typename Geometry1, typename Geometry2, + typename RobustPolicy, typename OutputIterator, typename Strategy > inline OutputIterator insert(Geometry1 const& geometry1, Geometry2 const& geometry2, + RobustPolicy robust_policy, OutputIterator out, Strategy const& strategy) { return boost::mpl::if_c + < + geometry::reverse_dispatch<Geometry1, Geometry2>::type::value, + geometry::dispatch::intersection_insert_reversed < - geometry::reverse_dispatch<Geometry1, Geometry2>::type::value, - geometry::dispatch::intersection_insert_reversed - < - typename geometry::tag<Geometry1>::type, - typename geometry::tag<Geometry2>::type, - typename geometry::tag<GeometryOut>::type, - geometry::is_areal<Geometry1>::value, - geometry::is_areal<Geometry2>::value, - geometry::is_areal<GeometryOut>::value, - Geometry1, Geometry2, - overlay::do_reverse<geometry::point_order<Geometry1>::value>::value, - overlay::do_reverse<geometry::point_order<Geometry2>::value, ReverseSecond>::value, - overlay::do_reverse<geometry::point_order<GeometryOut>::value>::value, - OutputIterator, GeometryOut, - OverlayType, - Strategy - >, - geometry::dispatch::intersection_insert - < - typename geometry::tag<Geometry1>::type, - typename geometry::tag<Geometry2>::type, - typename geometry::tag<GeometryOut>::type, - geometry::is_areal<Geometry1>::value, - geometry::is_areal<Geometry2>::value, - geometry::is_areal<GeometryOut>::value, - Geometry1, Geometry2, - geometry::detail::overlay::do_reverse<geometry::point_order<Geometry1>::value>::value, - geometry::detail::overlay::do_reverse<geometry::point_order<Geometry2>::value, ReverseSecond>::value, - geometry::detail::overlay::do_reverse<geometry::point_order<GeometryOut>::value>::value, - OutputIterator, GeometryOut, - OverlayType, - Strategy - > - >::type::apply(geometry1, geometry2, out, strategy); + Geometry1, Geometry2, + GeometryOut, + OverlayType, + overlay::do_reverse<geometry::point_order<Geometry1>::value>::value, + overlay::do_reverse<geometry::point_order<Geometry2>::value, ReverseSecond>::value, + overlay::do_reverse<geometry::point_order<GeometryOut>::value>::value + >, + geometry::dispatch::intersection_insert + < + Geometry1, Geometry2, + GeometryOut, + OverlayType, + geometry::detail::overlay::do_reverse<geometry::point_order<Geometry1>::value>::value, + geometry::detail::overlay::do_reverse<geometry::point_order<Geometry2>::value, ReverseSecond>::value + > + >::type::apply(geometry1, geometry2, robust_policy, out, strategy); } @@ -630,10 +783,14 @@ inline OutputIterator intersection_insert(Geometry1 const& geometry1, concept::check<Geometry1 const>(); concept::check<Geometry2 const>(); + typedef typename Strategy::rescale_policy_type rescale_policy_type; + rescale_policy_type robust_policy + = geometry::get_rescale_policy<rescale_policy_type>(geometry1, geometry2); + return detail::intersection::insert < GeometryOut, false, overlay_intersection - >(geometry1, geometry2, out, strategy); + >(geometry1, geometry2, robust_policy, out, strategy); } @@ -667,12 +824,18 @@ inline OutputIterator intersection_insert(Geometry1 const& geometry1, concept::check<Geometry1 const>(); concept::check<Geometry2 const>(); + typedef typename geometry::rescale_policy_type + < + typename geometry::point_type<Geometry1>::type // TODO from both + >::type rescale_policy_type; + typedef strategy_intersection < typename cs_tag<GeometryOut>::type, Geometry1, Geometry2, - typename geometry::point_type<GeometryOut>::type + typename geometry::point_type<GeometryOut>::type, + rescale_policy_type > strategy; return intersection_insert<GeometryOut>(geometry1, geometry2, out, diff --git a/boost/geometry/algorithms/detail/overlay/linear_linear.hpp b/boost/geometry/algorithms/detail/overlay/linear_linear.hpp new file mode 100644 index 0000000000..3a7a7a7f3e --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/linear_linear.hpp @@ -0,0 +1,326 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_LINEAR_LINEAR_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_LINEAR_LINEAR_HPP + +#include <algorithm> +#include <vector> + +#include <boost/range.hpp> + +#include <boost/geometry/core/tag.hpp> +#include <boost/geometry/core/tags.hpp> + +#include <boost/geometry/algorithms/detail/relate/turns.hpp> + +#include <boost/geometry/algorithms/detail/turns/compare_turns.hpp> +#include <boost/geometry/algorithms/detail/turns/filter_continue_turns.hpp> +#include <boost/geometry/algorithms/detail/turns/remove_duplicate_turns.hpp> + +#include <boost/geometry/algorithms/detail/overlay/overlay_type.hpp> +#include <boost/geometry/algorithms/detail/overlay/follow_linear_linear.hpp> + +#include <boost/geometry/algorithms/convert.hpp> + + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + + +template +< + typename LineStringOut, + overlay_type OverlayType, + typename Geometry, + typename GeometryTag +> +struct linear_linear_no_intersections; + + +template <typename LineStringOut, typename LineString> +struct linear_linear_no_intersections + < + LineStringOut, overlay_difference, LineString, linestring_tag + > +{ + template <typename OutputIterator> + static inline OutputIterator apply(LineString const& linestring, + OutputIterator oit) + { + LineStringOut ls_out; + geometry::convert(linestring, ls_out); + *oit++ = ls_out; + return oit; + } +}; + + +template <typename LineStringOut, typename MultiLineString> +struct linear_linear_no_intersections + < + LineStringOut, + overlay_difference, + MultiLineString, + multi_linestring_tag + > +{ + template <typename OutputIterator> + static inline OutputIterator apply(MultiLineString const& multilinestring, + OutputIterator oit) + { + for (typename boost::range_iterator<MultiLineString const>::type + it = boost::begin(multilinestring); + it != boost::end(multilinestring); ++it) + { + LineStringOut ls_out; + geometry::convert(*it, ls_out); + *oit++ = ls_out; + } + return oit; + } +}; + + +template <typename LineStringOut, typename Geometry, typename GeometryTag> +struct linear_linear_no_intersections + < + LineStringOut, overlay_intersection, Geometry, GeometryTag + > +{ + template <typename OutputIterator> + static inline OutputIterator apply(Geometry const&, + OutputIterator oit) + { + return oit; + } +}; + + + + + + + +template +< + typename Linear1, + typename Linear2, + typename LinestringOut, + overlay_type OverlayType, + bool EnableFilterContinueTurns = false, + bool EnableRemoveDuplicateTurns = false, + bool EnableDegenerateTurns = true, +#ifdef BOOST_GEOMETRY_INTERSECTION_DO_NOT_INCLUDE_ISOLATED_POINTS + bool EnableFollowIsolatedPoints = false +#else + bool EnableFollowIsolatedPoints = true +#endif +> +class linear_linear_linestring +{ +protected: + struct assign_policy + { + static bool const include_no_turn = false; + static bool const include_degenerate = EnableDegenerateTurns; + static bool const include_opposite = false; + + template + < + typename Info, + typename Point1, + typename Point2, + typename IntersectionInfo, + typename DirInfo + > + static inline void apply(Info& , Point1 const& , Point2 const& , + IntersectionInfo const& , DirInfo const& ) + { + } + }; + + + template + < + typename Turns, + typename LinearGeometry1, + typename LinearGeometry2 + > + static inline void compute_turns(Turns& turns, + LinearGeometry1 const& linear1, + LinearGeometry2 const& linear2) + { + turns.clear(); + geometry::detail::relate::turns::get_turns + < + LinearGeometry1, + LinearGeometry2, + detail::get_turns::get_turn_info_type + < + LinearGeometry1, + LinearGeometry2, + assign_policy + > + >::apply(turns, linear1, linear2); + } + + + template + < + overlay_type OverlayTypeForFollow, + bool FollowIsolatedPoints, + typename Turns, + typename LinearGeometry1, + typename LinearGeometry2, + typename OutputIterator + > + static inline OutputIterator + sort_and_follow_turns(Turns& turns, + LinearGeometry1 const& linear1, + LinearGeometry2 const& linear2, + OutputIterator oit) + { + // remove turns that have no added value + turns::filter_continue_turns + < + Turns, + EnableFilterContinueTurns && OverlayType != overlay_intersection + >::apply(turns); + + // sort by seg_id, distance, and operation + std::sort(boost::begin(turns), boost::end(turns), + detail::turns::less_seg_fraction_other_op<>()); + + // remove duplicate turns + turns::remove_duplicate_turns + < + Turns, EnableRemoveDuplicateTurns + >::apply(turns); + + return detail::overlay::following::linear::follow + < + LinestringOut, + LinearGeometry1, + LinearGeometry2, + OverlayTypeForFollow, + FollowIsolatedPoints, + !EnableFilterContinueTurns || OverlayType == overlay_intersection + >::apply(linear1, linear2, boost::begin(turns), boost::end(turns), + oit); + } + +public: + template + < + typename RobustPolicy, typename OutputIterator, typename Strategy + > + static inline OutputIterator apply(Linear1 const& linear1, + Linear2 const& linear2, + RobustPolicy const&, + OutputIterator oit, + Strategy const& ) + { + typedef typename detail::relate::turns::get_turns + < + Linear1, Linear2 + >::turn_info turn_info; + + typedef std::vector<turn_info> turns_container; + + turns_container turns; + compute_turns(turns, linear1, linear2); + + if ( turns.empty() ) + { + // the two linear geometries are disjoint + return linear_linear_no_intersections + < + LinestringOut, + OverlayType, + Linear1, + typename tag<Linear1>::type + >::apply(linear1, oit); + } + + return sort_and_follow_turns + < + OverlayType, + EnableFollowIsolatedPoints + && OverlayType == overlay_intersection + >(turns, linear1, linear2, oit); + } +}; + + + + +template +< + typename Linear1, + typename Linear2, + typename LinestringOut, + bool EnableFilterContinueTurns, + bool EnableRemoveDuplicateTurns, + bool EnableDegenerateTurns, + bool EnableFollowIsolatedPoints +> +struct linear_linear_linestring + < + Linear1, Linear2, LinestringOut, overlay_union, + EnableFilterContinueTurns, EnableRemoveDuplicateTurns, + EnableDegenerateTurns, EnableFollowIsolatedPoints + > +{ + template + < + typename RobustPolicy, typename OutputIterator, typename Strategy + > + static inline OutputIterator apply(Linear1 const& linear1, + Linear2 const& linear2, + RobustPolicy const& robust_policy, + OutputIterator oit, + Strategy const& strategy) + { + oit = linear_linear_no_intersections + < + LinestringOut, + overlay_difference, + Linear1, + typename tag<Linear1>::type + >::apply(linear1, oit); + + return linear_linear_linestring + < + Linear2, Linear1, LinestringOut, overlay_difference, + EnableFilterContinueTurns, EnableRemoveDuplicateTurns, + EnableDegenerateTurns, EnableFollowIsolatedPoints + >::apply(linear2, linear1, robust_policy, oit, strategy); + } +}; + + + + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_LINEAR_LINEAR_HPP diff --git a/boost/geometry/algorithms/detail/overlay/overlay.hpp b/boost/geometry/algorithms/detail/overlay/overlay.hpp index 41665e0af1..44b5a0df3c 100644 --- a/boost/geometry/algorithms/detail/overlay/overlay.hpp +++ b/boost/geometry/algorithms/detail/overlay/overlay.hpp @@ -1,6 +1,7 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2013 Adam Wulkiewicz, Lodz, Poland // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -17,7 +18,6 @@ #include <boost/mpl/assert.hpp> -#include <boost/geometry/algorithms/detail/overlay/calculate_distance_policy.hpp> #include <boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp> #include <boost/geometry/algorithms/detail/overlay/enrichment_info.hpp> #include <boost/geometry/algorithms/detail/overlay/get_turns.hpp> @@ -26,6 +26,7 @@ #include <boost/geometry/algorithms/detail/overlay/traversal_info.hpp> #include <boost/geometry/algorithms/detail/overlay/turn_info.hpp> +#include <boost/geometry/algorithms/detail/recalculate.hpp> #include <boost/geometry/algorithms/num_points.hpp> #include <boost/geometry/algorithms/reverse.hpp> @@ -34,12 +35,19 @@ #include <boost/geometry/algorithms/detail/overlay/assign_parents.hpp> #include <boost/geometry/algorithms/detail/overlay/ring_properties.hpp> #include <boost/geometry/algorithms/detail/overlay/select_rings.hpp> +#include <boost/geometry/algorithms/detail/overlay/do_reverse.hpp> + +#include <boost/geometry/policies/robustness/segment_ratio_type.hpp> #ifdef BOOST_GEOMETRY_DEBUG_ASSEMBLE # include <boost/geometry/io/dsv/write.hpp> #endif +#ifdef BOOST_GEOMETRY_TIME_OVERLAY +# include <boost/timer.hpp> +#endif + namespace boost { namespace geometry { @@ -66,19 +74,17 @@ inline void map_turns(Map& map, TurnPoints const& turn_points) typedef typename boost::range_value<TurnPoints>::type turn_point_type; typedef typename turn_point_type::container_type container_type; - int index = 0; for (typename boost::range_iterator<TurnPoints const>::type it = boost::begin(turn_points); it != boost::end(turn_points); - ++it, ++index) + ++it) { if (! skip(*it)) { - int op_index = 0; for (typename boost::range_iterator<container_type const>::type op_it = boost::begin(it->operations); op_it != boost::end(it->operations); - ++op_it, ++op_index) + ++op_it) { ring_identifier ring_id ( @@ -110,6 +116,12 @@ inline OutputIterator return_if_one_input_is_empty(Geometry1 const& geometry1, typedef ring_properties<typename geometry::point_type<Geometry1>::type> properties; +// Silence warning C4127: conditional expression is constant +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4127) +#endif + // Union: return either of them // Intersection: return nothing // Difference: return first of them @@ -120,6 +132,11 @@ inline OutputIterator return_if_one_input_is_empty(Geometry1 const& geometry1, return out; } +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + + std::map<ring_identifier, int> empty; std::map<ring_identifier, properties> all_of_one_of_them; @@ -134,25 +151,26 @@ template < typename Geometry1, typename Geometry2, bool Reverse1, bool Reverse2, bool ReverseOut, - typename OutputIterator, typename GeometryOut, - overlay_type Direction, - typename Strategy + typename GeometryOut, + overlay_type Direction > struct overlay { + template <typename RobustPolicy, typename OutputIterator, typename Strategy> static inline OutputIterator apply( Geometry1 const& geometry1, Geometry2 const& geometry2, + RobustPolicy const& robust_policy, OutputIterator out, Strategy const& ) { - if (geometry::num_points(geometry1) == 0 - && geometry::num_points(geometry2) == 0) + if ( geometry::num_points(geometry1) == 0 + && geometry::num_points(geometry2) == 0 ) { return out; } - if (geometry::num_points(geometry1) == 0 - || geometry::num_points(geometry2) == 0) + if ( geometry::num_points(geometry1) == 0 + || geometry::num_points(geometry2) == 0 ) { return return_if_one_input_is_empty < @@ -161,7 +179,11 @@ struct overlay } typedef typename geometry::point_type<GeometryOut>::type point_type; - typedef detail::overlay::traversal_turn_info<point_type> turn_info; + typedef detail::overlay::traversal_turn_info + < + point_type, + typename geometry::segment_ratio_type<point_type, RobustPolicy>::type + > turn_info; typedef std::deque<turn_info> container_type; typedef std::deque @@ -182,8 +204,8 @@ std::cout << "get turns" << std::endl; geometry::get_turns < Reverse1, Reverse2, - detail::overlay::calculate_distance_policy - >(geometry1, geometry2, turn_points, policy); + detail::overlay::assign_null_policy + >(geometry1, geometry2, robust_policy, turn_points, policy); #ifdef BOOST_GEOMETRY_TIME_OVERLAY std::cout << "get_turns: " << timer.elapsed() << std::endl; @@ -198,6 +220,7 @@ std::cout << "enrich" << std::endl; ? geometry::detail::overlay::operation_union : geometry::detail::overlay::operation_intersection, geometry1, geometry2, + robust_policy, side_strategy); #ifdef BOOST_GEOMETRY_TIME_OVERLAY @@ -218,6 +241,7 @@ std::cout << "traverse" << std::endl; Direction == overlay_union ? geometry::detail::overlay::operation_union : geometry::detail::overlay::operation_intersection, + robust_policy, turn_points, rings ); @@ -248,8 +272,8 @@ std::cout << "traverse" << std::endl; ring_identifier id(2, 0, -1); for (typename boost::range_iterator<ring_container_type>::type it = boost::begin(rings); - it != boost::end(rings); - ++it) + it != boost::end(rings); + ++it) { selected[id] = properties(*it, true); selected[id].reversed = ReverseOut; @@ -273,24 +297,6 @@ std::cout << "traverse" << std::endl; }; -// Metafunction helper for intersection and union -template <order_selector Selector, bool Reverse = false> -struct do_reverse {}; - -template <> -struct do_reverse<clockwise, false> : boost::false_type {}; - -template <> -struct do_reverse<clockwise, true> : boost::true_type {}; - -template <> -struct do_reverse<counterclockwise, false> : boost::true_type {}; - -template <> -struct do_reverse<counterclockwise, true> : boost::false_type {}; - - - }} // namespace detail::overlay #endif // DOXYGEN_NO_DETAIL diff --git a/boost/geometry/algorithms/detail/overlay/pointlike_pointlike.hpp b/boost/geometry/algorithms/detail/overlay/pointlike_pointlike.hpp new file mode 100644 index 0000000000..0af062d271 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/pointlike_pointlike.hpp @@ -0,0 +1,435 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_POINTLIKE_POINTLIKE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_POINTLIKE_POINTLIKE_HPP + +#include <algorithm> +#include <vector> + +#include <boost/assert.hpp> +#include <boost/range.hpp> + +#include <boost/geometry/core/point_type.hpp> +#include <boost/geometry/core/tag.hpp> +#include <boost/geometry/core/tags.hpp> + +#include <boost/geometry/algorithms/convert.hpp> +#include <boost/geometry/algorithms/not_implemented.hpp> + +#include <boost/geometry/algorithms/detail/relate/less.hpp> +#include <boost/geometry/algorithms/detail/equals/point_point.hpp> +#include <boost/geometry/algorithms/detail/overlay/overlay_type.hpp> + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + + +// struct for copying points of the pointlike geometries to output +template +< + typename PointOut, + typename GeometryIn, + typename TagIn = typename tag<GeometryIn>::type +> +struct copy_points + : not_implemented<PointOut, GeometryIn> +{}; + +template <typename PointOut, typename PointIn> +struct copy_points<PointOut, PointIn, point_tag> +{ + template <typename OutputIterator> + static inline void apply(PointIn const& point_in, + OutputIterator& oit) + { + PointOut point_out; + geometry::convert(point_in, point_out); + *oit++ = point_out; + } +}; + + +template <typename PointOut, typename MultiPointIn> +struct copy_points<PointOut, MultiPointIn, multi_point_tag> +{ + template <typename OutputIterator> + static inline void apply(MultiPointIn const& multi_point_in, + OutputIterator& oit) + { + for (typename boost::range_iterator<MultiPointIn const>::type + it = boost::begin(multi_point_in); + it != boost::end(multi_point_in); ++it) + { + PointOut point_out; + geometry::convert(*it, point_out); + *oit++ = point_out; + } + } +}; + + + +// action struct for difference/intersection +template <typename PointOut, overlay_type OverlayType> +struct action_selector_pl_pl +{}; + +template <typename PointOut> +struct action_selector_pl_pl<PointOut, overlay_intersection> +{ + template + < + typename Point, + typename OutputIterator + > + static inline void apply(Point const& point, + bool is_common, + OutputIterator& oit) + { + if ( is_common ) + { + copy_points<PointOut, Point>::apply(point, oit); + } + } +}; + + + +template <typename PointOut> +struct action_selector_pl_pl<PointOut, overlay_difference> +{ + template + < + typename Point, + typename OutputIterator + > + static inline void apply(Point const& point, + bool is_common, + OutputIterator& oit) + { + if ( !is_common ) + { + copy_points<PointOut, Point>::apply(point, oit); + } + } +}; + + +//=========================================================================== + +// difference/intersection of point-point +template +< + typename Point1, + typename Point2, + typename PointOut, + overlay_type OverlayType +> +struct point_point_point +{ + template <typename RobustPolicy, typename OutputIterator, typename Strategy> + static inline OutputIterator apply(Point1 const& point1, + Point2 const& point2, + RobustPolicy const& , + OutputIterator oit, + Strategy const&) + { + action_selector_pl_pl + < + PointOut, OverlayType + >::apply(point1, + detail::equals::equals_point_point(point1, point2), + oit); + + return oit; + } +}; + + + +// difference of multipoint-point +// +// the apply method in the following struct is called only for +// difference; for intersection the reversal will +// always call the point-multipoint version +template +< + typename MultiPoint, + typename Point, + typename PointOut, + overlay_type OverlayType +> +struct multipoint_point_point +{ + template <typename RobustPolicy, typename OutputIterator, typename Strategy> + static inline OutputIterator apply(MultiPoint const& multipoint, + Point const& point, + RobustPolicy const& , + OutputIterator oit, + Strategy const&) + { + BOOST_ASSERT( OverlayType == overlay_difference ); + + for (typename boost::range_iterator<MultiPoint const>::type + it = boost::begin(multipoint); + it != boost::end(multipoint); ++it) + { + action_selector_pl_pl + < + PointOut, OverlayType + >::apply(*it, + detail::equals::equals_point_point(*it, point), + oit); + } + + return oit; + } +}; + + +// difference/intersection of point-multipoint +template +< + typename Point, + typename MultiPoint, + typename PointOut, + overlay_type OverlayType +> +struct point_multipoint_point +{ + template <typename RobustPolicy, typename OutputIterator, typename Strategy> + static inline OutputIterator apply(Point const& point, + MultiPoint const& multipoint, + RobustPolicy const& , + OutputIterator oit, + Strategy const&) + { + typedef action_selector_pl_pl<PointOut, OverlayType> action; + + for (typename boost::range_iterator<MultiPoint const>::type + it = boost::begin(multipoint); + it != boost::end(multipoint); ++it) + { + if ( detail::equals::equals_point_point(*it, point) ) + { + action::apply(point, true, oit); + return oit; + } + } + + action::apply(point, false, oit); + return oit; + } +}; + + + +// difference/intersection of multipoint-multipoint +template +< + typename MultiPoint1, + typename MultiPoint2, + typename PointOut, + overlay_type OverlayType +> +struct multipoint_multipoint_point +{ + template <typename RobustPolicy, typename OutputIterator, typename Strategy> + static inline OutputIterator apply(MultiPoint1 const& multipoint1, + MultiPoint2 const& multipoint2, + RobustPolicy const& robust_policy, + OutputIterator oit, + Strategy const& strategy) + { + if ( OverlayType != overlay_difference + && boost::size(multipoint1) > boost::size(multipoint2) ) + { + return multipoint_multipoint_point + < + MultiPoint2, MultiPoint1, PointOut, OverlayType + >::apply(multipoint2, multipoint1, robust_policy, oit, strategy); + } + + std::vector<typename point_type<MultiPoint2>::type> + points2(boost::begin(multipoint2), boost::end(multipoint2)); + + std::sort(points2.begin(), points2.end(), detail::relate::less()); + + for (typename boost::range_iterator<MultiPoint1 const>::type + it1 = boost::begin(multipoint1); + it1 != boost::end(multipoint1); ++it1) + { + bool found = std::binary_search(points2.begin(), points2.end(), + *it1, detail::relate::less()); + + action_selector_pl_pl + < + PointOut, OverlayType + >::apply(*it1, found, oit); + } + return oit; + } +}; + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + + +//=========================================================================== + + +#ifndef DOXYGEN_NO_DISPATCH +namespace detail_dispatch { namespace overlay +{ + +// dispatch struct for pointlike-pointlike difference/intersection +// computation +template +< + typename PointLike1, + typename PointLike2, + typename PointOut, + overlay_type OverlayType, + typename Tag1, + typename Tag2 +> +struct pointlike_pointlike_point + : not_implemented<PointLike1, PointLike2, PointOut> +{}; + + +template +< + typename Point1, + typename Point2, + typename PointOut, + overlay_type OverlayType +> +struct pointlike_pointlike_point + < + Point1, Point2, PointOut, OverlayType, + point_tag, point_tag + > : detail::overlay::point_point_point + < + Point1, Point2, PointOut, OverlayType + > +{}; + + +template +< + typename Point, + typename MultiPoint, + typename PointOut, + overlay_type OverlayType +> +struct pointlike_pointlike_point + < + Point, MultiPoint, PointOut, OverlayType, + point_tag, multi_point_tag + > : detail::overlay::point_multipoint_point + < + Point, MultiPoint, PointOut, OverlayType + > +{}; + + +template +< + typename MultiPoint, + typename Point, + typename PointOut, + overlay_type OverlayType +> +struct pointlike_pointlike_point + < + MultiPoint, Point, PointOut, OverlayType, + multi_point_tag, point_tag + > : detail::overlay::multipoint_point_point + < + MultiPoint, Point, PointOut, OverlayType + > +{}; + + +template +< + typename MultiPoint1, + typename MultiPoint2, + typename PointOut, + overlay_type OverlayType +> +struct pointlike_pointlike_point + < + MultiPoint1, MultiPoint2, PointOut, OverlayType, + multi_point_tag, multi_point_tag + > : detail::overlay::multipoint_multipoint_point + < + MultiPoint1, MultiPoint2, PointOut, OverlayType + > +{}; + + +}} // namespace detail_dispatch::overlay +#endif // DOXYGEN_NO_DISPATCH + + +//=========================================================================== + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + + +// generic pointlike-pointlike union implementation +template +< + typename PointLike1, + typename PointLike2, + typename PointOut +> +struct union_pointlike_pointlike_point +{ + template <typename RobustPolicy, typename OutputIterator, typename Strategy> + static inline OutputIterator apply(PointLike1 const& pointlike1, + PointLike2 const& pointlike2, + RobustPolicy const& robust_policy, + OutputIterator oit, + Strategy const& strategy) + { + copy_points<PointOut, PointLike1>::apply(pointlike1, oit); + + return detail_dispatch::overlay::pointlike_pointlike_point + < + PointLike2, PointLike1, PointOut, overlay_difference, + typename tag<PointLike2>::type, + typename tag<PointLike1>::type + >::apply(pointlike2, pointlike1, robust_policy, oit, strategy); + } + +}; + + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_POINTLIKE_POINTLIKE_HPP diff --git a/boost/geometry/algorithms/detail/overlay/segment_identifier.hpp b/boost/geometry/algorithms/detail/overlay/segment_identifier.hpp index 007113ffba..516ec349e8 100644 --- a/boost/geometry/algorithms/detail/overlay/segment_identifier.hpp +++ b/boost/geometry/algorithms/detail/overlay/segment_identifier.hpp @@ -14,19 +14,19 @@ # define BOOST_GEOMETRY_DEBUG_SEGMENT_IDENTIFIER #endif +#if defined(BOOST_GEOMETRY_DEBUG_SEGMENT_IDENTIFIER) +#include <iostream> +#endif -#include <vector> - - -#include <boost/geometry/core/access.hpp> -#include <boost/geometry/core/coordinate_dimension.hpp> +#include <boost/geometry/algorithms/detail/signed_index_type.hpp> namespace boost { namespace geometry { + // Internal struct to uniquely identify a segment // on a linestring,ring // or polygon (needs ring_index) @@ -40,7 +40,10 @@ struct segment_identifier , segment_index(-1) {} - inline segment_identifier(int src, int mul, int rin, int seg) + inline segment_identifier(signed_index_type src, + signed_index_type mul, + signed_index_type rin, + signed_index_type seg) : source_index(src) , multi_index(mul) , ring_index(rin) @@ -68,20 +71,20 @@ struct segment_identifier #if defined(BOOST_GEOMETRY_DEBUG_SEGMENT_IDENTIFIER) friend std::ostream& operator<<(std::ostream &os, segment_identifier const& seg_id) { - std::cout + os << "s:" << seg_id.source_index - << ", v:" << seg_id.segment_index // ~vertex + << ", v:" << seg_id.segment_index // v:vertex because s is used for source ; - if (seg_id.ring_index >= 0) std::cout << ", r:" << seg_id.ring_index; - if (seg_id.multi_index >= 0) std::cout << ", m:" << seg_id.multi_index; + if (seg_id.ring_index >= 0) os << ", r:" << seg_id.ring_index; + if (seg_id.multi_index >= 0) os << ", m:" << seg_id.multi_index; return os; } #endif - int source_index; - int multi_index; - int ring_index; - int segment_index; + signed_index_type source_index; + signed_index_type multi_index; + signed_index_type ring_index; + signed_index_type segment_index; }; diff --git a/boost/geometry/algorithms/detail/overlay/select_rings.hpp b/boost/geometry/algorithms/detail/overlay/select_rings.hpp index f664b19514..385658a190 100644 --- a/boost/geometry/algorithms/detail/overlay/select_rings.hpp +++ b/boost/geometry/algorithms/detail/overlay/select_rings.hpp @@ -1,6 +1,7 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2014 Adam Wulkiewicz, Lodz, Poland. // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -9,11 +10,16 @@ #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SELECT_RINGS_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SELECT_RINGS_HPP + #include <map> +#include <boost/range.hpp> + +#include <boost/geometry/core/tags.hpp> #include <boost/geometry/algorithms/area.hpp> #include <boost/geometry/algorithms/within.hpp> +#include <boost/geometry/algorithms/detail/interior_iterator.hpp> #include <boost/geometry/algorithms/detail/point_on_border.hpp> #include <boost/geometry/algorithms/detail/ring_identifier.hpp> #include <boost/geometry/algorithms/detail/overlay/ring_properties.hpp> @@ -40,14 +46,14 @@ namespace dispatch struct select_rings<box_tag, Box> { template <typename Geometry, typename Map> - static inline void apply(Box const& box, Geometry const& , + static inline void apply(Box const& box, Geometry const& , ring_identifier const& id, Map& map, bool midpoint) { map[id] = typename Map::mapped_type(box, midpoint); } template <typename Map> - static inline void apply(Box const& box, + static inline void apply(Box const& box, ring_identifier const& id, Map& map, bool midpoint) { map[id] = typename Map::mapped_type(box, midpoint); @@ -68,7 +74,7 @@ namespace dispatch } template <typename Map> - static inline void apply(Ring const& ring, + static inline void apply(Ring const& ring, ring_identifier const& id, Map& map, bool midpoint) { if (boost::size(ring) > 0) @@ -91,9 +97,10 @@ namespace dispatch per_ring::apply(exterior_ring(polygon), geometry, id, map, midpoint); - typename interior_return_type<Polygon const>::type rings - = interior_rings(polygon); - for (BOOST_AUTO_TPL(it, boost::begin(rings)); it != boost::end(rings); ++it) + typename interior_return_type<Polygon const>::type + rings = interior_rings(polygon); + for (typename detail::interior_iterator<Polygon const>::type + it = boost::begin(rings); it != boost::end(rings); ++it) { id.ring_index++; per_ring::apply(*it, geometry, id, map, midpoint); @@ -109,16 +116,42 @@ namespace dispatch per_ring::apply(exterior_ring(polygon), id, map, midpoint); - typename interior_return_type<Polygon const>::type rings - = interior_rings(polygon); - for (BOOST_AUTO_TPL(it, boost::begin(rings)); it != boost::end(rings); ++it) + typename interior_return_type<Polygon const>::type + rings = interior_rings(polygon); + for (typename detail::interior_iterator<Polygon const>::type + it = boost::begin(rings); it != boost::end(rings); ++it) { id.ring_index++; per_ring::apply(*it, id, map, midpoint); } } }; -} + + template <typename Multi> + struct select_rings<multi_polygon_tag, Multi> + { + template <typename Geometry, typename Map> + static inline void apply(Multi const& multi, Geometry const& geometry, + ring_identifier id, Map& map, bool midpoint) + { + typedef typename boost::range_iterator + < + Multi const + >::type iterator_type; + + typedef select_rings<polygon_tag, typename boost::range_value<Multi>::type> per_polygon; + + id.multi_index = 0; + for (iterator_type it = boost::begin(multi); it != boost::end(multi); ++it) + { + id.ring_index = -1; + per_polygon::apply(*it, geometry, id, map, midpoint); + id.multi_index++; + } + } + }; + +} // namespace dispatch template<overlay_type OverlayType> @@ -213,7 +246,7 @@ inline void update_selection_map(Geometry1 const& geometry1, typename SelectionMap::mapped_type properties = it->second; // Copy by value // Calculate the "within code" (previously this was done earlier but is - // must efficienter here - it can be even more efficient doing it all at once, + // much efficienter here - it can be even more efficient doing it all at once, // using partition, TODO) // So though this is less elegant than before, it avoids many unused point-in-poly calculations switch(id.source_index) @@ -248,7 +281,7 @@ template typename IntersectionMap, typename SelectionMap > inline void select_rings(Geometry1 const& geometry1, Geometry2 const& geometry2, - IntersectionMap const& intersection_map, + IntersectionMap const& intersection_map, SelectionMap& selection_map, bool midpoint) { typedef typename geometry::tag<Geometry1>::type tag1; @@ -271,16 +304,16 @@ template typename IntersectionMap, typename SelectionMap > inline void select_rings(Geometry const& geometry, - IntersectionMap const& intersection_map, + IntersectionMap const& intersection_map, SelectionMap& selection_map, bool midpoint) { typedef typename geometry::tag<Geometry>::type tag; SelectionMap map_with_all; - dispatch::select_rings<tag, Geometry>::apply(geometry, + dispatch::select_rings<tag, Geometry>::apply(geometry, ring_identifier(0, -1, -1), map_with_all, midpoint); - update_selection_map<OverlayType>(geometry, geometry, intersection_map, + update_selection_map<OverlayType>(geometry, geometry, intersection_map, map_with_all, selection_map); } diff --git a/boost/geometry/algorithms/detail/overlay/self_turn_points.hpp b/boost/geometry/algorithms/detail/overlay/self_turn_points.hpp index 9c4c99394e..8dffeae283 100644 --- a/boost/geometry/algorithms/detail/overlay/self_turn_points.hpp +++ b/boost/geometry/algorithms/detail/overlay/self_turn_points.hpp @@ -9,16 +9,18 @@ #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SELF_TURN_POINTS_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SELF_TURN_POINTS_HPP + #include <cstddef> #include <boost/range.hpp> #include <boost/geometry/core/access.hpp> #include <boost/geometry/core/coordinate_dimension.hpp> +#include <boost/geometry/core/tags.hpp> #include <boost/geometry/geometries/concepts/check.hpp> -#include <boost/geometry/algorithms/detail/disjoint.hpp> +#include <boost/geometry/algorithms/detail/disjoint/box_box.hpp> #include <boost/geometry/algorithms/detail/partition.hpp> #include <boost/geometry/algorithms/detail/overlay/get_turns.hpp> @@ -55,17 +57,21 @@ template typename Geometry, typename Turns, typename TurnPolicy, + typename RobustPolicy, typename InterruptPolicy > struct self_section_visitor { Geometry const& m_geometry; + RobustPolicy const& m_rescale_policy; Turns& m_turns; InterruptPolicy& m_interrupt_policy; inline self_section_visitor(Geometry const& g, + RobustPolicy const& rp, Turns& turns, InterruptPolicy& ip) : m_geometry(g) + , m_rescale_policy(rp) , m_turns(turns) , m_interrupt_policy(ip) {} @@ -82,12 +88,12 @@ struct self_section_visitor Geometry, Geometry, false, false, Section, Section, - Turns, TurnPolicy, - InterruptPolicy + TurnPolicy >::apply( 0, m_geometry, sec1, 0, m_geometry, sec2, false, + m_rescale_policy, m_turns, m_interrupt_policy); } if (m_interrupt_policy.has_intersections) @@ -103,17 +109,13 @@ struct self_section_visitor -template -< - typename Geometry, - typename Turns, - typename TurnPolicy, - typename InterruptPolicy -> +template<typename TurnPolicy> struct get_turns { + template <typename Geometry, typename RobustPolicy, typename Turns, typename InterruptPolicy> static inline bool apply( Geometry const& geometry, + RobustPolicy const& robust_policy, Turns& turns, InterruptPolicy& interrupt_policy) { @@ -127,20 +129,20 @@ struct get_turns > sections_type; sections_type sec; - geometry::sectionalize<false>(geometry, sec); + geometry::sectionalize<false>(geometry, robust_policy, false, sec); self_section_visitor < Geometry, - Turns, TurnPolicy, InterruptPolicy - > visitor(geometry, turns, interrupt_policy); + Turns, TurnPolicy, RobustPolicy, InterruptPolicy + > visitor(geometry, robust_policy, turns, interrupt_policy); try { geometry::partition < - box_type, - detail::get_turns::get_section_box, + box_type, + detail::get_turns::get_section_box, detail::get_turns::ovelaps_section_box >::apply(sec, visitor); } @@ -166,9 +168,7 @@ template < typename GeometryTag, typename Geometry, - typename Turns, - typename TurnPolicy, - typename InterruptPolicy + typename TurnPolicy > struct self_get_turn_points { @@ -178,44 +178,32 @@ struct self_get_turn_points template < typename Ring, - typename Turns, - typename TurnPolicy, - typename InterruptPolicy + typename TurnPolicy > struct self_get_turn_points < ring_tag, Ring, - Turns, - TurnPolicy, - InterruptPolicy + TurnPolicy > - : detail::self_get_turn_points::get_turns - < - Ring, - Turns, - TurnPolicy, - InterruptPolicy - > + : detail::self_get_turn_points::get_turns<TurnPolicy> {}; template < typename Box, - typename Turns, - typename TurnPolicy, - typename InterruptPolicy + typename TurnPolicy > struct self_get_turn_points < box_tag, Box, - Turns, - TurnPolicy, - InterruptPolicy + TurnPolicy > { + template <typename RobustPolicy, typename Turns, typename InterruptPolicy> static inline bool apply( Box const& , + RobustPolicy const& , Turns& , InterruptPolicy& ) { @@ -227,24 +215,28 @@ struct self_get_turn_points template < typename Polygon, - typename Turns, - typename TurnPolicy, - typename InterruptPolicy + typename TurnPolicy > struct self_get_turn_points < polygon_tag, Polygon, - Turns, - TurnPolicy, - InterruptPolicy + TurnPolicy + > + : detail::self_get_turn_points::get_turns<TurnPolicy> +{}; + + +template +< + typename MultiPolygon, + typename TurnPolicy +> +struct self_get_turn_points + < + multi_polygon_tag, MultiPolygon, + TurnPolicy > - : detail::self_get_turn_points::get_turns - < - Polygon, - Turns, - TurnPolicy, - InterruptPolicy - > + : detail::self_get_turn_points::get_turns<TurnPolicy> {}; @@ -259,6 +251,7 @@ struct self_get_turn_points \tparam Turns type of intersection container (e.g. vector of "intersection/turn point"'s) \param geometry geometry + \param robust_policy policy to handle robustness issues \param turns container which will contain intersection points \param interrupt_policy policy determining if process is stopped when intersection is found @@ -267,38 +260,24 @@ template < typename AssignPolicy, typename Geometry, + typename RobustPolicy, typename Turns, typename InterruptPolicy > inline void self_turns(Geometry const& geometry, + RobustPolicy const& robust_policy, Turns& turns, InterruptPolicy& interrupt_policy) { concept::check<Geometry const>(); - typedef typename strategy_intersection - < - typename cs_tag<Geometry>::type, - Geometry, - Geometry, - typename boost::range_value<Turns>::type - >::segment_intersection_strategy_type strategy_type; - - typedef detail::overlay::get_turn_info - < - typename point_type<Geometry>::type, - typename point_type<Geometry>::type, - typename boost::range_value<Turns>::type, - detail::overlay::assign_null_policy - > TurnPolicy; + typedef detail::overlay::get_turn_info<detail::overlay::assign_null_policy> turn_policy; dispatch::self_get_turn_points < typename tag<Geometry>::type, Geometry, - Turns, - TurnPolicy, - InterruptPolicy - >::apply(geometry, turns, interrupt_policy); + turn_policy + >::apply(geometry, robust_policy, turns, interrupt_policy); } diff --git a/boost/geometry/algorithms/detail/overlay/stream_info.hpp b/boost/geometry/algorithms/detail/overlay/stream_info.hpp index eebe381944..51fd1b3dca 100644 --- a/boost/geometry/algorithms/detail/overlay/stream_info.hpp +++ b/boost/geometry/algorithms/detail/overlay/stream_info.hpp @@ -35,7 +35,6 @@ namespace detail { namespace overlay template <typename P> std::ostream& operator<<(std::ostream &os, turn_info<P> const& info) { - typename geometry::coordinate_type<P>::type d = info.distance; os << "\t" << " src " << info.seg_id.source_index << " seg " << info.seg_id.segment_index @@ -54,7 +53,7 @@ namespace detail { namespace overlay << " nxt seg " << info.travels_to_vertex_index << " , ip " << info.travels_to_ip_index << " , or " << info.next_ip_index - << " dst " << double(d) + << " frac " << info.fraction << info.visit_state; if (info.flagged) { diff --git a/boost/geometry/algorithms/detail/overlay/traversal_info.hpp b/boost/geometry/algorithms/detail/overlay/traversal_info.hpp index 810a27af04..6ee32c17c0 100644 --- a/boost/geometry/algorithms/detail/overlay/traversal_info.hpp +++ b/boost/geometry/algorithms/detail/overlay/traversal_info.hpp @@ -24,15 +24,21 @@ namespace detail { namespace overlay { -template <typename P> -struct traversal_turn_operation : public turn_operation +template <typename Point, typename SegmentRatio> +struct traversal_turn_operation : public turn_operation<SegmentRatio> { - enrichment_info<P> enriched; + enrichment_info<Point> enriched; visit_info visited; }; -template <typename P> -struct traversal_turn_info : public turn_info<P, traversal_turn_operation<P> > +template <typename Point, typename SegmentRatio> +struct traversal_turn_info + : public turn_info + < + Point, + SegmentRatio, + traversal_turn_operation<Point, SegmentRatio> + > {}; diff --git a/boost/geometry/algorithms/detail/overlay/traverse.hpp b/boost/geometry/algorithms/detail/overlay/traverse.hpp index 12daafa0cf..59d2ba703e 100644 --- a/boost/geometry/algorithms/detail/overlay/traverse.hpp +++ b/boost/geometry/algorithms/detail/overlay/traverse.hpp @@ -13,11 +13,12 @@ #include <boost/range.hpp> -#include <boost/geometry/algorithms/detail/overlay/append_no_duplicates.hpp> #include <boost/geometry/algorithms/detail/overlay/backtrack_check_si.hpp> #include <boost/geometry/algorithms/detail/overlay/copy_segments.hpp> #include <boost/geometry/algorithms/detail/overlay/turn_info.hpp> +#include <boost/geometry/algorithms/num_points.hpp> #include <boost/geometry/core/access.hpp> +#include <boost/geometry/core/closure.hpp> #include <boost/geometry/core/coordinate_dimension.hpp> #include <boost/geometry/geometries/concepts/check.hpp> @@ -38,7 +39,7 @@ namespace detail { namespace overlay template <typename Turn, typename Operation> #ifdef BOOST_GEOMETRY_DEBUG_TRAVERSE -inline void debug_traverse(Turn const& turn, Operation op, +inline void debug_traverse(Turn const& turn, Operation op, std::string const& header) { std::cout << header @@ -57,7 +58,7 @@ inline void debug_traverse(Turn const& turn, Operation op, } } #else -inline void debug_traverse(Turn const& , Operation, std::string const& ) +inline void debug_traverse(Turn const& , Operation, const char*) { } #endif @@ -92,14 +93,16 @@ template typename G1, typename G2, typename Turns, - typename IntersectionInfo + typename IntersectionInfo, + typename RobustPolicy > inline bool assign_next_ip(G1 const& g1, G2 const& g2, Turns& turns, typename boost::range_iterator<Turns>::type& ip, GeometryOut& current_output, IntersectionInfo& info, - segment_identifier& seg_id) + segment_identifier& seg_id, + RobustPolicy const& robust_policy) { info.visited.set_visited(); set_visited_for_continue(*ip, info); @@ -107,7 +110,7 @@ inline bool assign_next_ip(G1 const& g1, G2 const& g2, // If there is no next IP on this segment if (info.enriched.next_ip_index < 0) { - if (info.enriched.travels_to_vertex_index < 0 + if (info.enriched.travels_to_vertex_index < 0 || info.enriched.travels_to_ip_index < 0) { return false; @@ -120,12 +123,14 @@ inline bool assign_next_ip(G1 const& g1, G2 const& g2, { geometry::copy_segments<Reverse1>(g1, info.seg_id, info.enriched.travels_to_vertex_index, + robust_policy, current_output); } else { geometry::copy_segments<Reverse2>(g2, info.seg_id, info.enriched.travels_to_vertex_index, + robust_policy, current_output); } seg_id = info.seg_id; @@ -137,12 +142,16 @@ inline bool assign_next_ip(G1 const& g1, G2 const& g2, seg_id = info.seg_id; } - detail::overlay::append_no_duplicates(current_output, ip->point); + detail::overlay::append_no_dups_or_spikes(current_output, ip->point, + robust_policy); + return true; } -inline bool select_source(operation_type operation, int source1, int source2) +inline bool select_source(operation_type operation, + signed_index_type source1, + signed_index_type source2) { return (operation == operation_intersection && source1 != source2) || (operation == operation_union && source1 == source2) @@ -227,12 +236,14 @@ template class traverse { public : - template <typename Turns, typename Rings> + template <typename RobustPolicy, typename Turns, typename Rings> static inline void apply(Geometry1 const& geometry1, Geometry2 const& geometry2, detail::overlay::operation_type operation, + RobustPolicy const& robust_policy, Turns& turns, Rings& rings) { + typedef typename boost::range_value<Rings>::type ring_type; typedef typename boost::range_iterator<Turns>::type turn_iterator; typedef typename boost::range_value<Turns>::type turn_type; typedef typename boost::range_iterator @@ -240,6 +251,12 @@ public : typename turn_type::container_type >::type turn_operation_iterator_type; + std::size_t const min_num_points + = core_detail::closure::minimum_ring_size + < + geometry::closure<ring_type>::value + >::value; + std::size_t size_at_start = boost::size(rings); typename Backtrack::state_type state; @@ -253,7 +270,7 @@ public : ++it) { // Skip discarded ones - if (! (it->is_discarded() || it->blocked())) + if (! (it->discarded || ! it->selectable_start || it->blocked())) { for (turn_operation_iterator_type iit = boost::begin(it->operations); state.good() && iit != boost::end(it->operations); @@ -267,9 +284,9 @@ public : { set_visited_for_continue(*it, *iit); - typename boost::range_value<Rings>::type current_output; - detail::overlay::append_no_duplicates(current_output, - it->point, true); + ring_type current_output; + detail::overlay::append_no_dups_or_spikes(current_output, + it->point, robust_policy); turn_iterator current = it; turn_operation_iterator_type current_iit = iit; @@ -279,13 +296,14 @@ public : geometry1, geometry2, turns, current, current_output, - *iit, current_seg_id)) + *iit, current_seg_id, + robust_policy)) { Backtrack::apply( - size_at_start, + size_at_start, rings, current_output, turns, *current_iit, "No next IP", - geometry1, geometry2, state); + geometry1, geometry2, robust_policy, state); } if (! detail::overlay::select_next_ip( @@ -295,10 +313,10 @@ public : current_iit)) { Backtrack::apply( - size_at_start, + size_at_start, rings, current_output, turns, *iit, "Dead end at start", - geometry1, geometry2, state); + geometry1, geometry2, robust_policy, state); } else { @@ -308,7 +326,7 @@ public : detail::overlay::debug_traverse(*current, *current_iit, "Selected "); - unsigned int i = 0; + typename boost::range_size<Turns>::type i = 0; while (current_iit != iit && state.good()) { @@ -317,10 +335,10 @@ public : // It visits a visited node again, without passing the start node. // This makes it suspicious for endless loops Backtrack::apply( - size_at_start, + size_at_start, rings, current_output, turns, *iit, "Visit again", - geometry1, geometry2, state); + geometry1, geometry2, robust_policy, state); } else { @@ -339,7 +357,8 @@ public : detail::overlay::assign_next_ip<Reverse1, Reverse2>( geometry1, geometry2, turns, current, current_output, - *current_iit, current_seg_id); + *current_iit, current_seg_id, + robust_policy); if (! detail::overlay::select_next_ip( operation, @@ -351,12 +370,15 @@ public : // Should not occur in self-intersecting polygons without spikes // Might occur in polygons with spikes Backtrack::apply( - size_at_start, + size_at_start, rings, current_output, turns, *iit, "Dead end", - geometry1, geometry2, state); + geometry1, geometry2, robust_policy, state); + } + else + { + detail::overlay::debug_traverse(*current, *current_iit, "Selected "); } - detail::overlay::debug_traverse(*current, *current_iit, "Selected "); if (i++ > 2 + 2 * turns.size()) { @@ -364,10 +386,10 @@ public : // than turn points. // Turn points marked as "ii" can be visited twice. Backtrack::apply( - size_at_start, + size_at_start, rings, current_output, turns, *iit, "Endless loop", - geometry1, geometry2, state); + geometry1, geometry2, robust_policy, state); } } } @@ -376,7 +398,11 @@ public : { iit->visited.set_finished(); detail::overlay::debug_traverse(*current, *iit, "->Finished"); - rings.push_back(current_output); + if (geometry::num_points(current_output) >= min_num_points) + { + clean_closing_dups_and_spikes(current_output, robust_policy); + rings.push_back(current_output); + } } } } diff --git a/boost/geometry/algorithms/detail/overlay/turn_info.hpp b/boost/geometry/algorithms/detail/overlay/turn_info.hpp index 89a60b21ab..26669a4b1f 100644 --- a/boost/geometry/algorithms/detail/overlay/turn_info.hpp +++ b/boost/geometry/algorithms/detail/overlay/turn_info.hpp @@ -54,11 +54,12 @@ enum method_type The class is to be included in the turn_info class, either direct or a derived or similar class with more (e.g. enrichment) information. */ +template <typename SegmentRatio> struct turn_operation { operation_type operation; segment_identifier seg_id; - segment_identifier other_id; + SegmentRatio fraction; inline turn_operation() : operation(operation_none) @@ -78,7 +79,8 @@ struct turn_operation template < typename Point, - typename Operation = turn_operation, + typename SegmentRatio, + typename Operation = turn_operation<SegmentRatio>, typename Container = boost::array<Operation, 2> > struct turn_info @@ -90,6 +92,7 @@ struct turn_info Point point; method_type method; bool discarded; + bool selectable_start; // Can be used as starting-turn in traverse Container operations; @@ -97,13 +100,14 @@ struct turn_info inline turn_info() : method(method_none) , discarded(false) + , selectable_start(true) {} inline bool both(operation_type type) const { return has12(type, type); } - + inline bool has(operation_type type) const { return this->operations[0].operation == type @@ -115,8 +119,6 @@ struct turn_info return has12(type1, type2) || has12(type2, type1); } - - inline bool is_discarded() const { return discarded; } inline bool blocked() const { return both(operation_blocked); diff --git a/boost/geometry/algorithms/detail/overlay/visit_info.hpp b/boost/geometry/algorithms/detail/overlay/visit_info.hpp index 6be63f42b4..4284a801a1 100644 --- a/boost/geometry/algorithms/detail/overlay/visit_info.hpp +++ b/boost/geometry/algorithms/detail/overlay/visit_info.hpp @@ -10,11 +10,6 @@ #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_VISIT_INFO_HPP -#ifdef BOOST_GEOMETRY_USE_MSM -# include <boost/geometry/algorithms/detail/overlay/msm_state.hpp> -#endif - - namespace boost { namespace geometry { @@ -22,9 +17,6 @@ namespace boost { namespace geometry namespace detail { namespace overlay { - -#if ! defined(BOOST_GEOMETRY_USE_MSM) - class visit_info { private : @@ -66,8 +58,6 @@ public: } } - - #ifdef BOOST_GEOMETRY_DEBUG_INTERSECTION friend std::ostream& operator<<(std::ostream &os, visit_info const& v) { @@ -82,50 +72,6 @@ public: }; -#else - - -class visit_info -{ - -private : - -#ifndef USE_MSM_MINI - mutable -#endif - traverse_state state; - -public : - inline visit_info() - { - state.start(); - } - - inline void set_none() { state.process_event(none()); } // Not Yet Implemented! - inline void set_visited() { state.process_event(visit()); } - inline void set_started() { state.process_event(starting()); } - inline void set_finished() { state.process_event(finish()); } - -#ifdef USE_MSM_MINI - inline bool none() const { return state.flag_none(); } - inline bool visited() const { return state.flag_visited(); } - inline bool started() const { return state.flag_started(); } -#else - inline bool none() const { return state.is_flag_active<is_init>(); } - inline bool visited() const { return state.is_flag_active<is_visited>(); } - inline bool started() const { return state.is_flag_active<is_started>(); } -#endif - -#ifdef BOOST_GEOMETRY_DEBUG_INTERSECTION - friend std::ostream& operator<<(std::ostream &os, visit_info const& v) - { - return os; - } -#endif -}; -#endif - - }} // namespace detail::overlay #endif //DOXYGEN_NO_DETAIL |