diff options
Diffstat (limited to 'boost/geometry/algorithms/detail')
51 files changed, 11816 insertions, 0 deletions
diff --git a/boost/geometry/algorithms/detail/as_range.hpp b/boost/geometry/algorithms/detail/as_range.hpp new file mode 100644 index 0000000000..d0dfb07e43 --- /dev/null +++ b/boost/geometry/algorithms/detail/as_range.hpp @@ -0,0 +1,107 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2012 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2012 Mateusz Loskot, London, UK. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, 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_AS_RANGE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_AS_RANGE_HPP + + +#include <boost/type_traits.hpp> + +#include <boost/geometry/core/exterior_ring.hpp> +#include <boost/geometry/core/tag.hpp> +#include <boost/geometry/core/tags.hpp> + +#include <boost/geometry/util/add_const_if_c.hpp> + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +template <typename GeometryTag, typename Geometry, typename Range, bool IsConst> +struct as_range +{ + static inline typename add_const_if_c<IsConst, Range>::type& get( + typename add_const_if_c<IsConst, Geometry>::type& input) + { + return input; + } +}; + + +template <typename Geometry, typename Range, bool IsConst> +struct as_range<polygon_tag, Geometry, Range, IsConst> +{ + static inline typename add_const_if_c<IsConst, Range>::type& get( + typename add_const_if_c<IsConst, Geometry>::type& input) + { + return exterior_ring(input); + } +}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + +// Will probably be replaced by the more generic "view_as", therefore in detail +namespace detail +{ + +/*! +\brief Function getting either the range (ring, linestring) itself +or the outer ring (polygon) +\details Utility to handle polygon's outer ring as a range +\ingroup utility +*/ +template <typename Range, typename Geometry> +inline Range& as_range(Geometry& input) +{ + return dispatch::as_range + < + typename tag<Geometry>::type, + Geometry, + Range, + false + >::get(input); +} + + +/*! +\brief Function getting either the range (ring, linestring) itself +or the outer ring (polygon), const version +\details Utility to handle polygon's outer ring as a range +\ingroup utility +*/ +template <typename Range, typename Geometry> +inline Range const& as_range(Geometry const& input) +{ + return dispatch::as_range + < + typename tag<Geometry>::type, + Geometry, + Range, + true + >::get(input); +} + +} + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_AS_RANGE_HPP diff --git a/boost/geometry/algorithms/detail/assign_box_corners.hpp b/boost/geometry/algorithms/detail/assign_box_corners.hpp new file mode 100644 index 0000000000..1fd41733f2 --- /dev/null +++ b/boost/geometry/algorithms/detail/assign_box_corners.hpp @@ -0,0 +1,93 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2012 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2012 Mateusz Loskot, London, UK. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, 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_ASSIGN_BOX_CORNERS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_ASSIGN_BOX_CORNERS_HPP + + +#include <cstddef> + +#include <boost/geometry/geometries/concepts/check.hpp> +#include <boost/geometry/algorithms/detail/assign_values.hpp> + + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail +{ +// Note: this is moved to namespace detail because the names and parameter orders +// are not yet 100% clear. + +/*! +\brief Assign the four points of a 2D box +\ingroup assign +\note The order is crucial. Most logical is LOWER, UPPER and sub-order LEFT, RIGHT + so this is how it is implemented. +\tparam Box \tparam_box +\tparam Point \tparam_point +\param box \param_box +\param lower_left point being assigned to lower left coordinates of the box +\param lower_right point being assigned to lower right coordinates of the box +\param upper_left point being assigned to upper left coordinates of the box +\param upper_right point being assigned to upper right coordinates of the box + +\qbk{ +[heading Example] +[assign_box_corners] [assign_box_corners_output] +} +*/ +template <typename Box, typename Point> +inline void assign_box_corners(Box const& box, + Point& lower_left, Point& lower_right, + Point& upper_left, Point& upper_right) +{ + concept::check<Box const>(); + concept::check<Point>(); + + detail::assign::assign_box_2d_corner + <min_corner, min_corner>(box, lower_left); + detail::assign::assign_box_2d_corner + <max_corner, min_corner>(box, lower_right); + detail::assign::assign_box_2d_corner + <min_corner, max_corner>(box, upper_left); + detail::assign::assign_box_2d_corner + <max_corner, max_corner>(box, upper_right); +} + +template <bool Reverse, typename Box, typename Range> +inline void assign_box_corners_oriented(Box const& box, Range& corners) +{ + if (Reverse) + { + // make counterclockwise ll,lr,ur,ul + assign_box_corners(box, corners[0], corners[1], corners[3], corners[2]); + } + else + { + // make clockwise ll,ul,ur,lr + assign_box_corners(box, corners[0], corners[3], corners[1], corners[2]); + } +} + + +} // namespace detail +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_ASSIGN_BOX_CORNERS_HPP diff --git a/boost/geometry/algorithms/detail/assign_indexed_point.hpp b/boost/geometry/algorithms/detail/assign_indexed_point.hpp new file mode 100644 index 0000000000..a1cffb80a7 --- /dev/null +++ b/boost/geometry/algorithms/detail/assign_indexed_point.hpp @@ -0,0 +1,94 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2012 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2012 Mateusz Loskot, London, UK. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, 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_ASSIGN_INDEXED_POINT_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_ASSIGN_INDEXED_POINT_HPP + + +#include <cstddef> + +#include <boost/geometry/geometries/concepts/check.hpp> +#include <boost/geometry/algorithms/detail/assign_values.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail +{ + +/*! +\brief Assign a box or segment with the value of a point +\ingroup assign +\tparam Index indicates which box-corner, min_corner (0) or max_corner (1) + or which point of segment (0/1) +\tparam Point \tparam_point +\tparam Geometry \tparam_box_or_segment +\param point \param_point +\param geometry \param_box_or_segment + +\qbk{ +[heading Example] +[assign_point_to_index] [assign_point_to_index_output] +} +*/ +template <std::size_t Index, typename Geometry, typename Point> +inline void assign_point_to_index(Point const& point, Geometry& geometry) +{ + concept::check<Point const>(); + concept::check<Geometry>(); + + detail::assign::assign_point_to_index + < + Geometry, Point, Index, 0, dimension<Geometry>::type::value + >::apply(point, geometry); +} + + +/*! +\brief Assign a point with a point of a box or segment +\ingroup assign +\tparam Index indicates which box-corner, min_corner (0) or max_corner (1) + or which point of segment (0/1) +\tparam Geometry \tparam_box_or_segment +\tparam Point \tparam_point +\param geometry \param_box_or_segment +\param point \param_point + +\qbk{ +[heading Example] +[assign_point_from_index] [assign_point_from_index_output] +} +*/ +template <std::size_t Index, typename Point, typename Geometry> +inline void assign_point_from_index(Geometry const& geometry, Point& point) +{ + concept::check<Geometry const>(); + concept::check<Point>(); + + detail::assign::assign_point_from_index + < + Geometry, Point, Index, 0, dimension<Geometry>::type::value + >::apply(geometry, point); +} + + +} // namespace detail +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_ASSIGN_INDEXED_POINT_HPP diff --git a/boost/geometry/algorithms/detail/assign_values.hpp b/boost/geometry/algorithms/detail/assign_values.hpp new file mode 100644 index 0000000000..ed4713493f --- /dev/null +++ b/boost/geometry/algorithms/detail/assign_values.hpp @@ -0,0 +1,443 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2012 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2012 Mateusz Loskot, London, UK. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, 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_ASSIGN_VALUES_HPP +#define BOOST_GEOMETRY_ALGORITHMS_ASSIGN_VALUES_HPP + + +#include <cstddef> + +#include <boost/concept/requires.hpp> +#include <boost/concept_check.hpp> +#include <boost/mpl/assert.hpp> +#include <boost/mpl/if.hpp> +#include <boost/numeric/conversion/bounds.hpp> +#include <boost/numeric/conversion/cast.hpp> +#include <boost/type_traits.hpp> + +#include <boost/geometry/arithmetic/arithmetic.hpp> +#include <boost/geometry/algorithms/append.hpp> +#include <boost/geometry/algorithms/clear.hpp> +#include <boost/geometry/core/access.hpp> +#include <boost/geometry/core/exterior_ring.hpp> +#include <boost/geometry/core/tags.hpp> + +#include <boost/geometry/geometries/concepts/check.hpp> + + +#include <boost/geometry/util/for_each_coordinate.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace assign +{ + + +template +< + typename Box, std::size_t Index, + std::size_t Dimension, std::size_t DimensionCount +> +struct initialize +{ + typedef typename coordinate_type<Box>::type coordinate_type; + + static inline void apply(Box& box, coordinate_type const& value) + { + geometry::set<Index, Dimension>(box, value); + initialize<Box, Index, Dimension + 1, DimensionCount>::apply(box, value); + } +}; + + +template <typename Box, std::size_t Index, std::size_t DimensionCount> +struct initialize<Box, Index, DimensionCount, DimensionCount> +{ + typedef typename coordinate_type<Box>::type coordinate_type; + + static inline void apply(Box&, coordinate_type const& ) + {} +}; + + +template <typename Point> +struct assign_zero_point +{ + static inline void apply(Point& point) + { + geometry::assign_value(point, 0); + } +}; + + +template <typename BoxOrSegment> +struct assign_inverse_box_or_segment +{ + typedef typename point_type<BoxOrSegment>::type point_type; + + static inline void apply(BoxOrSegment& geometry) + { + typedef typename coordinate_type<point_type>::type bound_type; + + initialize + < + BoxOrSegment, 0, 0, dimension<BoxOrSegment>::type::value + >::apply( + geometry, boost::numeric::bounds<bound_type>::highest()); + initialize + < + BoxOrSegment, 1, 0, dimension<BoxOrSegment>::type::value + >::apply( + geometry, boost::numeric::bounds<bound_type>::lowest()); + } +}; + + +template <typename BoxOrSegment> +struct assign_zero_box_or_segment +{ + static inline void apply(BoxOrSegment& geometry) + { + typedef typename coordinate_type<BoxOrSegment>::type coordinate_type; + + initialize + < + BoxOrSegment, 0, 0, dimension<BoxOrSegment>::type::value + >::apply(geometry, coordinate_type()); + initialize + < + BoxOrSegment, 1, 0, dimension<BoxOrSegment>::type::value + >::apply(geometry, coordinate_type()); + } +}; + + +template +< + std::size_t Corner1, std::size_t Corner2, + typename Box, typename Point +> +inline void assign_box_2d_corner(Box const& box, Point& point) +{ + // Be sure both are 2-Dimensional + assert_dimension<Box, 2>(); + assert_dimension<Point, 2>(); + + // Copy coordinates + typedef typename coordinate_type<Point>::type coordinate_type; + + geometry::set<0>(point, boost::numeric_cast<coordinate_type>(get<Corner1, 0>(box))); + geometry::set<1>(point, boost::numeric_cast<coordinate_type>(get<Corner2, 1>(box))); +} + + + +template +< + typename Geometry, typename Point, + std::size_t Index, + std::size_t Dimension, std::size_t DimensionCount +> +struct assign_point_to_index +{ + + static inline void apply(Point const& point, Geometry& geometry) + { + geometry::set<Index, Dimension>(geometry, boost::numeric_cast + < + typename coordinate_type<Geometry>::type + >(geometry::get<Dimension>(point))); + + assign_point_to_index + < + Geometry, Point, Index, Dimension + 1, DimensionCount + >::apply(point, geometry); + } +}; + +template +< + typename Geometry, typename Point, + std::size_t Index, + std::size_t DimensionCount +> +struct assign_point_to_index + < + Geometry, Point, + Index, + DimensionCount, DimensionCount + > +{ + static inline void apply(Point const& , Geometry& ) + { + } +}; + + +template +< + typename Geometry, typename Point, + std::size_t Index, + std::size_t Dimension, std::size_t DimensionCount +> +struct assign_point_from_index +{ + + static inline void apply(Geometry const& geometry, Point& point) + { + geometry::set<Dimension>( point, boost::numeric_cast + < + typename coordinate_type<Point>::type + >(geometry::get<Index, Dimension>(geometry))); + + assign_point_from_index + < + Geometry, Point, Index, Dimension + 1, DimensionCount + >::apply(geometry, point); + } +}; + +template +< + typename Geometry, typename Point, + std::size_t Index, + std::size_t DimensionCount +> +struct assign_point_from_index + < + Geometry, Point, + Index, + DimensionCount, DimensionCount + > +{ + static inline void apply(Geometry const&, Point&) + { + } +}; + + +template <typename Geometry> +struct assign_2d_box_or_segment +{ + typedef typename coordinate_type<Geometry>::type coordinate_type; + + // Here we assign 4 coordinates to a box of segment + // -> Most logical is: x1,y1,x2,y2 + // In case the user reverses x1/x2 or y1/y2, for a box, we could reverse them (THAT IS NOT IMPLEMENTED) + + template <typename Type> + static inline void apply(Geometry& geometry, + Type const& x1, Type const& y1, Type const& x2, Type const& y2) + { + geometry::set<0, 0>(geometry, boost::numeric_cast<coordinate_type>(x1)); + geometry::set<0, 1>(geometry, boost::numeric_cast<coordinate_type>(y1)); + geometry::set<1, 0>(geometry, boost::numeric_cast<coordinate_type>(x2)); + geometry::set<1, 1>(geometry, boost::numeric_cast<coordinate_type>(y2)); + } +}; + + +}} // namespace detail::assign +#endif // DOXYGEN_NO_DETAIL + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template <typename GeometryTag, typename Geometry, std::size_t DimensionCount> +struct assign +{ + BOOST_MPL_ASSERT_MSG + ( + false, NOT_OR_NOT_YET_IMPLEMENTED_FOR_THIS_GEOMETRY_TYPE + , (types<Geometry>) + ); +}; + +template <typename Point> +struct assign<point_tag, Point, 2> +{ + typedef typename coordinate_type<Point>::type coordinate_type; + + template <typename T> + static inline void apply(Point& point, T const& c1, T const& c2) + { + set<0>(point, boost::numeric_cast<coordinate_type>(c1)); + set<1>(point, boost::numeric_cast<coordinate_type>(c2)); + } +}; + +template <typename Point> +struct assign<point_tag, Point, 3> +{ + typedef typename coordinate_type<Point>::type coordinate_type; + + template <typename T> + static inline void apply(Point& point, T const& c1, T const& c2, T const& c3) + { + set<0>(point, boost::numeric_cast<coordinate_type>(c1)); + set<1>(point, boost::numeric_cast<coordinate_type>(c2)); + set<2>(point, boost::numeric_cast<coordinate_type>(c3)); + } +}; + +template <typename Box> +struct assign<box_tag, Box, 2> + : detail::assign::assign_2d_box_or_segment<Box> +{}; + +template <typename Segment> +struct assign<segment_tag, Segment, 2> + : detail::assign::assign_2d_box_or_segment<Segment> +{}; + + + +template <typename GeometryTag, typename Geometry> +struct assign_zero {}; + + +template <typename Point> +struct assign_zero<point_tag, Point> + : detail::assign::assign_zero_point<Point> +{}; + +template <typename Box> +struct assign_zero<box_tag, Box> + : detail::assign::assign_zero_box_or_segment<Box> +{}; + +template <typename Segment> +struct assign_zero<segment_tag, Segment> + : detail::assign::assign_zero_box_or_segment<Segment> +{}; + + +template <typename GeometryTag, typename Geometry> +struct assign_inverse {}; + +template <typename Box> +struct assign_inverse<box_tag, Box> + : detail::assign::assign_inverse_box_or_segment<Box> +{}; + +template <typename Segment> +struct assign_inverse<segment_tag, Segment> + : detail::assign::assign_inverse_box_or_segment<Segment> +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +/*! +\brief Assign two coordinates to a geometry (usually a 2D point) +\ingroup assign +\tparam Geometry \tparam_geometry +\tparam Type \tparam_numeric to specify the coordinates +\param geometry \param_geometry +\param c1 \param_x +\param c2 \param_y + +\qbk{distinguish, 2 coordinate values} +\qbk{ +[heading Example] +[assign_2d_point] [assign_2d_point_output] + +[heading See also] +\* [link geometry.reference.algorithms.make.make_2_2_coordinate_values make] +} + */ +template <typename Geometry, typename Type> +inline void assign_values(Geometry& geometry, Type const& c1, Type const& c2) +{ + concept::check<Geometry>(); + + dispatch::assign + < + typename tag<Geometry>::type, + Geometry, + geometry::dimension<Geometry>::type::value + >::apply(geometry, c1, c2); +} + +/*! +\brief Assign three values to a geometry (usually a 3D point) +\ingroup assign +\tparam Geometry \tparam_geometry +\tparam Type \tparam_numeric to specify the coordinates +\param geometry \param_geometry +\param c1 \param_x +\param c2 \param_y +\param c3 \param_z + +\qbk{distinguish, 3 coordinate values} +\qbk{ +[heading Example] +[assign_3d_point] [assign_3d_point_output] + +[heading See also] +\* [link geometry.reference.algorithms.make.make_3_3_coordinate_values make] +} + */ +template <typename Geometry, typename Type> +inline void assign_values(Geometry& geometry, + Type const& c1, Type const& c2, Type const& c3) +{ + concept::check<Geometry>(); + + dispatch::assign + < + typename tag<Geometry>::type, + Geometry, + geometry::dimension<Geometry>::type::value + >::apply(geometry, c1, c2, c3); +} + +/*! +\brief Assign four values to a geometry (usually a box or segment) +\ingroup assign +\tparam Geometry \tparam_geometry +\tparam Type \tparam_numeric to specify the coordinates +\param geometry \param_geometry +\param c1 First coordinate (usually x1) +\param c2 Second coordinate (usually y1) +\param c3 Third coordinate (usually x2) +\param c4 Fourth coordinate (usually y2) + +\qbk{distinguish, 4 coordinate values} + */ +template <typename Geometry, typename Type> +inline void assign_values(Geometry& geometry, + Type const& c1, Type const& c2, Type const& c3, Type const& c4) +{ + concept::check<Geometry>(); + + dispatch::assign + < + typename tag<Geometry>::type, + Geometry, + geometry::dimension<Geometry>::type::value + >::apply(geometry, c1, c2, c3, c4); +} + + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_ASSIGN_VALUES_HPP diff --git a/boost/geometry/algorithms/detail/calculate_null.hpp b/boost/geometry/algorithms/detail/calculate_null.hpp new file mode 100644 index 0000000000..4b48d62fc2 --- /dev/null +++ b/boost/geometry/algorithms/detail/calculate_null.hpp @@ -0,0 +1,38 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2012 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2012 Mateusz Loskot, London, UK. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, 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_CALCULATE_NULL_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_CALCULATE_NULL_HPP + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail +{ + +template<typename ReturnType, typename Geometry, typename Strategy> +struct calculate_null +{ + static inline ReturnType apply(Geometry const& , Strategy const&) + { + return ReturnType(); + } +}; + +} // namespace detail +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_CALCULATE_NULL_HPP diff --git a/boost/geometry/algorithms/detail/calculate_sum.hpp b/boost/geometry/algorithms/detail/calculate_sum.hpp new file mode 100644 index 0000000000..2ad349080b --- /dev/null +++ b/boost/geometry/algorithms/detail/calculate_sum.hpp @@ -0,0 +1,64 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2012 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2012 Mateusz Loskot, London, UK. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, 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_CALCULATE_SUM_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_CALCULATE_SUM_HPP + + +#include <boost/range.hpp> +#include <boost/typeof/typeof.hpp> + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail +{ + + +template +< + typename ReturnType, + typename Polygon, + typename Strategy, + typename Policy +> +class calculate_polygon_sum +{ + template <typename Rings> + static inline ReturnType sum_interior_rings(Rings const& rings, Strategy const& strategy) + { + ReturnType sum = ReturnType(); + for (BOOST_AUTO_TPL(it, boost::begin(rings)); it != boost::end(rings); ++it) + { + sum += Policy::apply(*it, strategy); + } + return sum; + } + +public : + static inline ReturnType apply(Polygon const& poly, Strategy const& strategy) + { + return Policy::apply(exterior_ring(poly), strategy) + + sum_interior_rings(interior_rings(poly), strategy) + ; + } +}; + + +} // namespace detail +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_CALCULATE_SUM_HPP diff --git a/boost/geometry/algorithms/detail/convert_indexed_to_indexed.hpp b/boost/geometry/algorithms/detail/convert_indexed_to_indexed.hpp new file mode 100644 index 0000000000..d39824a61d --- /dev/null +++ b/boost/geometry/algorithms/detail/convert_indexed_to_indexed.hpp @@ -0,0 +1,80 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2012 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2012 Mateusz Loskot, London, UK. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, 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_CONVERT_INDEXED_TO_INDEXED_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_CONVERT_INDEXED_TO_INDEXED_HPP + + +#include <cstddef> + +#include <boost/numeric/conversion/cast.hpp> +#include <boost/geometry/core/access.hpp> +#include <boost/geometry/core/coordinate_dimension.hpp> +#include <boost/geometry/core/coordinate_type.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace conversion +{ + + +template +< + typename Source, + typename Destination, + std::size_t Dimension, + std::size_t DimensionCount +> +struct indexed_to_indexed +{ + static inline void apply(Source const& source, Destination& destination) + { + typedef typename coordinate_type<Destination>::type coordinate_type; + + geometry::set<min_corner, Dimension>(destination, + boost::numeric_cast<coordinate_type>( + geometry::get<min_corner, Dimension>(source))); + geometry::set<max_corner, Dimension>(destination, + boost::numeric_cast<coordinate_type>( + geometry::get<max_corner, Dimension>(source))); + + indexed_to_indexed + < + Source, Destination, + Dimension + 1, DimensionCount + >::apply(source, destination); + } +}; + +template +< + typename Source, + typename Destination, + std::size_t DimensionCount +> +struct indexed_to_indexed<Source, Destination, DimensionCount, DimensionCount> +{ + static inline void apply(Source const& , Destination& ) + {} +}; + + +}} // namespace detail::conversion +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_CONVERT_INDEXED_TO_INDEXED_HPP diff --git a/boost/geometry/algorithms/detail/convert_point_to_point.hpp b/boost/geometry/algorithms/detail/convert_point_to_point.hpp new file mode 100644 index 0000000000..c7d37b6ca4 --- /dev/null +++ b/boost/geometry/algorithms/detail/convert_point_to_point.hpp @@ -0,0 +1,68 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2012 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2012 Mateusz Loskot, London, UK. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, 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_CONVERT_POINT_TO_POINT_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_CONVERT_POINT_TO_POINT_HPP + +// Note: extracted from "convert.hpp" to avoid circular references convert/append + +#include <cstddef> + +#include <boost/numeric/conversion/cast.hpp> +#include <boost/geometry/core/access.hpp> +#include <boost/geometry/core/coordinate_dimension.hpp> +#include <boost/geometry/core/coordinate_type.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace conversion +{ + + +template <typename Source, typename Destination, std::size_t Dimension, std::size_t DimensionCount> +struct point_to_point +{ + static inline void apply(Source const& source, Destination& destination) + { + typedef typename coordinate_type<Destination>::type coordinate_type; + + set<Dimension>(destination, boost::numeric_cast<coordinate_type>(get<Dimension>(source))); + point_to_point<Source, Destination, Dimension + 1, DimensionCount>::apply(source, destination); + } +}; + +template <typename Source, typename Destination, std::size_t DimensionCount> +struct point_to_point<Source, Destination, DimensionCount, DimensionCount> +{ + static inline void apply(Source const& , Destination& ) + {} +}; + + +template <typename Source, typename Destination> +inline void convert_point_to_point(Source const& source, Destination& destination) +{ + point_to_point<Source, Destination, 0, dimension<Destination>::value>::apply(source, destination); +} + + + +}} // namespace detail::conversion +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_CONVERT_POINT_TO_POINT_HPP diff --git a/boost/geometry/algorithms/detail/disjoint.hpp b/boost/geometry/algorithms/detail/disjoint.hpp new file mode 100644 index 0000000000..2ced5b1ce3 --- /dev/null +++ b/boost/geometry/algorithms/detail/disjoint.hpp @@ -0,0 +1,225 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2012 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2012 Mateusz Loskot, London, UK. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, 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_DISJOINT_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISJOINT_HPP + +// Note: contrary to most files, the geometry::detail::disjoint namespace +// is partly implemented in a separate file, to avoid circular references +// disjoint -> get_turns -> disjoint + +#include <cstddef> + +#include <boost/range.hpp> + +#include <boost/geometry/core/access.hpp> +#include <boost/geometry/core/coordinate_dimension.hpp> +#include <boost/geometry/core/reverse_dispatch.hpp> + + +#include <boost/geometry/util/math.hpp> + + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace disjoint +{ + + +struct disjoint_interrupt_policy +{ + static bool const enabled = true; + bool has_intersections; + + inline disjoint_interrupt_policy() + : has_intersections(false) + {} + + template <typename Range> + inline bool apply(Range const& range) + { + // If there is any IP in the range, it is NOT disjoint + if (boost::size(range) > 0) + { + has_intersections = true; + return true; + } + return false; + } +}; + + + +template +< + typename Point1, typename Point2, + std::size_t Dimension, std::size_t DimensionCount +> +struct point_point +{ + static inline bool apply(Point1 const& p1, Point2 const& p2) + { + if (! geometry::math::equals(get<Dimension>(p1), get<Dimension>(p2))) + { + return true; + } + return point_point + < + Point1, Point2, + Dimension + 1, DimensionCount + >::apply(p1, p2); + } +}; + + +template <typename Point1, typename Point2, std::size_t DimensionCount> +struct point_point<Point1, Point2, DimensionCount, DimensionCount> +{ + static inline bool apply(Point1 const& , Point2 const& ) + { + return false; + } +}; + + +template +< + typename Point, typename Box, + std::size_t Dimension, std::size_t DimensionCount +> +struct point_box +{ + static inline bool apply(Point const& point, Box const& box) + { + if (get<Dimension>(point) < get<min_corner, Dimension>(box) + || get<Dimension>(point) > get<max_corner, Dimension>(box)) + { + return true; + } + return point_box + < + Point, Box, + Dimension + 1, DimensionCount + >::apply(point, box); + } +}; + + +template <typename Point, typename Box, std::size_t DimensionCount> +struct point_box<Point, Box, DimensionCount, DimensionCount> +{ + static inline bool apply(Point const& , Box const& ) + { + return false; + } +}; + + +template +< + typename Box1, typename Box2, + std::size_t Dimension, std::size_t DimensionCount +> +struct box_box +{ + static inline bool apply(Box1 const& box1, Box2 const& box2) + { + if (get<max_corner, Dimension>(box1) < get<min_corner, Dimension>(box2)) + { + return true; + } + if (get<min_corner, Dimension>(box1) > get<max_corner, Dimension>(box2)) + { + return true; + } + return box_box + < + Box1, Box2, + Dimension + 1, DimensionCount + >::apply(box1, box2); + } +}; + + +template <typename Box1, typename Box2, std::size_t DimensionCount> +struct box_box<Box1, Box2, DimensionCount, DimensionCount> +{ + static inline bool apply(Box1 const& , Box2 const& ) + { + return false; + } +}; + + + +/*! + \brief Internal utility function to detect of boxes are disjoint + \note Is used from other algorithms, declared separately + to avoid circular references + */ +template <typename Box1, typename Box2> +inline bool disjoint_box_box(Box1 const& box1, Box2 const& box2) +{ + return box_box + < + Box1, Box2, + 0, dimension<Box1>::type::value + >::apply(box1, box2); +} + + + +/*! + \brief Internal utility function to detect of points are disjoint + \note To avoid circular references + */ +template <typename Point1, typename Point2> +inline bool disjoint_point_point(Point1 const& point1, Point2 const& point2) +{ + return point_point + < + Point1, Point2, + 0, dimension<Point1>::type::value + >::apply(point1, point2); +} + + +}} // namespace detail::disjoint +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace equals +{ + +/*! + \brief Internal utility function to detect of points are disjoint + \note To avoid circular references + */ +template <typename Point1, typename Point2> +inline bool equals_point_point(Point1 const& point1, Point2 const& point2) +{ + return ! detail::disjoint::disjoint_point_point(point1, point2); +} + + +}} // namespace detail::equals +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISJOINT_HPP diff --git a/boost/geometry/algorithms/detail/equals/collect_vectors.hpp b/boost/geometry/algorithms/detail/equals/collect_vectors.hpp new file mode 100644 index 0000000000..9c2fe28057 --- /dev/null +++ b/boost/geometry/algorithms/detail/equals/collect_vectors.hpp @@ -0,0 +1,315 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2012 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2012 Mateusz Loskot, London, UK. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, 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_EQUALS_COLLECT_VECTORS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_EQUALS_COLLECT_VECTORS_HPP + + +#include <boost/numeric/conversion/cast.hpp> +#include <boost/range.hpp> +#include <boost/typeof/typeof.hpp> + +#include <boost/geometry/multi/core/tags.hpp> + +#include <boost/geometry/core/cs.hpp> +#include <boost/geometry/core/interior_rings.hpp> +#include <boost/geometry/geometries/concepts/check.hpp> + +#include <boost/geometry/util/math.hpp> + + + +namespace boost { namespace geometry +{ + +// TODO: if Boost.LA of Emil Dotchevski is accepted, adapt this +template <typename T> +struct collected_vector +{ + typedef T type; + + inline collected_vector() + {} + + inline collected_vector(T const& px, T const& py, + T const& pdx, T const& pdy) + : x(px) + , y(py) + , dx(pdx) + , dy(pdy) + , dx_0(T()) + , dy_0(T()) + {} + + T x, y; + T dx, dy; + T dx_0, dy_0; + + // For sorting + inline bool operator<(collected_vector<T> const& other) const + { + if (math::equals(x, other.x)) + { + if (math::equals(y, other.y)) + { + if (math::equals(dx, other.dx)) + { + return dy < other.dy; + } + return dx < other.dx; + } + return y < other.y; + } + return x < other.x; + } + + inline bool same_direction(collected_vector<T> const& other) const + { + // For high precision arithmetic, we have to be + // more relaxed then using == + // Because 2/sqrt( (0,0)<->(2,2) ) == 1/sqrt( (0,0)<->(1,1) ) + // is not always true (at least, it is not for ttmath) + return math::equals_with_epsilon(dx, other.dx) + && math::equals_with_epsilon(dy, other.dy); + } + + // For std::equals + inline bool operator==(collected_vector<T> const& other) const + { + return math::equals(x, other.x) + && math::equals(y, other.y) + && same_direction(other); + } +}; + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace collect_vectors +{ + + +template <typename Range, typename Collection> +struct range_collect_vectors +{ + typedef typename boost::range_value<Collection>::type item_type; + typedef typename item_type::type calculation_type; + + static inline void apply(Collection& collection, Range const& range) + { + if (boost::size(range) < 2) + { + return; + } + + typedef typename boost::range_iterator<Range const>::type iterator; + + bool first = true; + iterator it = boost::begin(range); + + for (iterator prev = it++; + it != boost::end(range); + prev = it++) + { + typename boost::range_value<Collection>::type v; + + v.x = get<0>(*prev); + v.y = get<1>(*prev); + v.dx = get<0>(*it) - v.x; + v.dy = get<1>(*it) - v.y; + v.dx_0 = v.dx; + v.dy_0 = v.dy; + + // Normalize the vector -> this results in points+direction + // and is comparible between geometries + calculation_type magnitude = sqrt( + boost::numeric_cast<calculation_type>(v.dx * v.dx + v.dy * v.dy)); + + // Avoid non-duplicate points (AND division by zero) + if (magnitude > 0) + { + v.dx /= magnitude; + v.dy /= magnitude; + + // Avoid non-direction changing points + if (first || ! v.same_direction(collection.back())) + { + collection.push_back(v); + } + first = false; + } + } + + // If first one has same direction as last one, remove first one + if (boost::size(collection) > 1 + && collection.front().same_direction(collection.back())) + { + collection.erase(collection.begin()); + } + } +}; + + +template <typename Box, typename Collection> +struct box_collect_vectors +{ + // Calculate on coordinate type, but if it is integer, + // then use double + typedef typename boost::range_value<Collection>::type item_type; + typedef typename item_type::type calculation_type; + + static inline void apply(Collection& collection, Box const& box) + { + typename point_type<Box>::type lower_left, lower_right, + upper_left, upper_right; + geometry::detail::assign_box_corners(box, lower_left, lower_right, + upper_left, upper_right); + + typedef typename boost::range_value<Collection>::type item; + + collection.push_back(item(get<0>(lower_left), get<1>(lower_left), 0, 1)); + collection.push_back(item(get<0>(upper_left), get<1>(upper_left), 1, 0)); + collection.push_back(item(get<0>(upper_right), get<1>(upper_right), 0, -1)); + collection.push_back(item(get<0>(lower_right), get<1>(lower_right), -1, 0)); + } +}; + + +template <typename Polygon, typename Collection> +struct polygon_collect_vectors +{ + static inline void apply(Collection& collection, Polygon const& polygon) + { + typedef typename geometry::ring_type<Polygon>::type ring_type; + + typedef range_collect_vectors<ring_type, Collection> per_range; + per_range::apply(collection, exterior_ring(polygon)); + + typename interior_return_type<Polygon const>::type rings + = interior_rings(polygon); + for (BOOST_AUTO_TPL(it, boost::begin(rings)); it != boost::end(rings); ++it) + { + per_range::apply(collection, *it); + } + } +}; + + +template <typename MultiGeometry, typename Collection, typename SinglePolicy> +struct multi_collect_vectors +{ + static inline void apply(Collection& collection, MultiGeometry const& multi) + { + for (typename boost::range_iterator<MultiGeometry const>::type + it = boost::begin(multi); + it != boost::end(multi); + ++it) + { + SinglePolicy::apply(collection, *it); + } + } +}; + + +}} // namespace detail::collect_vectors +#endif // DOXYGEN_NO_DETAIL + + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +template +< + typename Tag, + typename Collection, + typename Geometry +> +struct collect_vectors +{ + static inline void apply(Collection&, Geometry const&) + {} +}; + + +template <typename Collection, typename Box> +struct collect_vectors<box_tag, Collection, Box> + : detail::collect_vectors::box_collect_vectors<Box, Collection> +{}; + + + +template <typename Collection, typename Ring> +struct collect_vectors<ring_tag, Collection, Ring> + : detail::collect_vectors::range_collect_vectors<Ring, Collection> +{}; + + +template <typename Collection, typename LineString> +struct collect_vectors<linestring_tag, Collection, LineString> + : detail::collect_vectors::range_collect_vectors<LineString, Collection> +{}; + + +template <typename Collection, typename Polygon> +struct collect_vectors<polygon_tag, Collection, Polygon> + : detail::collect_vectors::polygon_collect_vectors<Polygon, Collection> +{}; + + +template <typename Collection, typename MultiPolygon> +struct collect_vectors<multi_polygon_tag, Collection, MultiPolygon> + : detail::collect_vectors::multi_collect_vectors + < + MultiPolygon, + Collection, + detail::collect_vectors::polygon_collect_vectors + < + typename boost::range_value<MultiPolygon>::type, + Collection + > + > +{}; + + + +} // namespace dispatch +#endif + + +/*! + \ingroup collect_vectors + \tparam Collection Collection type, should be e.g. std::vector<> + \tparam Geometry geometry type + \param collection the collection of vectors + \param geometry the geometry to make collect_vectors +*/ +template <typename Collection, typename Geometry> +inline void collect_vectors(Collection& collection, Geometry const& geometry) +{ + concept::check<Geometry const>(); + + dispatch::collect_vectors + < + typename tag<Geometry>::type, + Collection, + Geometry + >::apply(collection, geometry); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_EQUALS_COLLECT_VECTORS_HPP diff --git a/boost/geometry/algorithms/detail/for_each_range.hpp b/boost/geometry/algorithms/detail/for_each_range.hpp new file mode 100644 index 0000000000..b10017c209 --- /dev/null +++ b/boost/geometry/algorithms/detail/for_each_range.hpp @@ -0,0 +1,148 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2012 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2012 Mateusz Loskot, London, UK. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, 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_FOR_EACH_RANGE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_FOR_EACH_RANGE_HPP + + +#include <boost/mpl/assert.hpp> +#include <boost/concept/requires.hpp> + +#include <boost/geometry/core/tag.hpp> +#include <boost/geometry/core/tag_cast.hpp> + +#include <boost/geometry/util/add_const_if_c.hpp> + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace for_each +{ + + +template <typename Range, typename Actor, bool IsConst> +struct fe_range_range +{ + static inline void apply( + typename add_const_if_c<IsConst, Range>::type& range, + Actor& actor) + { + actor.apply(range); + } +}; + + +template <typename Polygon, typename Actor, bool IsConst> +struct fe_range_polygon +{ + static inline void apply( + typename add_const_if_c<IsConst, Polygon>::type& polygon, + Actor& actor) + { + actor.apply(exterior_ring(polygon)); + + // TODO: If some flag says true, also do the inner rings. + // for convex hull, it's not necessary + } +}; + +template <typename Box, typename Actor, bool IsConst> +struct fe_range_box +{ + static inline void apply( + typename add_const_if_c<IsConst, Box>::type& box, + Actor& actor) + { + actor.apply(box_view<Box>(box)); + } +}; + + +}} // namespace detail::for_each +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +template +< + typename Tag, + typename Geometry, + typename Actor, + bool IsConst +> +struct for_each_range +{ + BOOST_MPL_ASSERT_MSG + ( + false, NOT_OR_NOT_YET_IMPLEMENTED_FOR_THIS_GEOMETRY_TYPE + , (types<Geometry>) + ); +}; + + +template <typename Linestring, typename Actor, bool IsConst> +struct for_each_range<linestring_tag, Linestring, Actor, IsConst> + : detail::for_each::fe_range_range<Linestring, Actor, IsConst> +{}; + + +template <typename Ring, typename Actor, bool IsConst> +struct for_each_range<ring_tag, Ring, Actor, IsConst> + : detail::for_each::fe_range_range<Ring, Actor, IsConst> +{}; + + +template <typename Polygon, typename Actor, bool IsConst> +struct for_each_range<polygon_tag, Polygon, Actor, IsConst> + : detail::for_each::fe_range_polygon<Polygon, Actor, IsConst> +{}; + +template <typename Box, typename Actor, bool IsConst> +struct for_each_range<box_tag, Box, Actor, IsConst> + : detail::for_each::fe_range_box<Box, Actor, IsConst> +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + +namespace detail +{ + +template <typename Geometry, typename Actor> +inline void for_each_range(Geometry const& geometry, Actor& actor) +{ + dispatch::for_each_range + < + typename tag<Geometry>::type, + Geometry, + Actor, + true + >::apply(geometry, actor); +} + + +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_FOR_EACH_RANGE_HPP diff --git a/boost/geometry/algorithms/detail/has_self_intersections.hpp b/boost/geometry/algorithms/detail/has_self_intersections.hpp new file mode 100644 index 0000000000..1e6215ed93 --- /dev/null +++ b/boost/geometry/algorithms/detail/has_self_intersections.hpp @@ -0,0 +1,120 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2011-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_HAS_SELF_INTERSECTIONS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_HAS_SELF_INTERSECTIONS_HPP + +#include <deque> + +#include <boost/range.hpp> +#include <boost/geometry/core/point_type.hpp> +#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp> +#include <boost/geometry/algorithms/detail/overlay/get_turns.hpp> +#include <boost/geometry/algorithms/detail/overlay/self_turn_points.hpp> + +#include <boost/geometry/multi/algorithms/detail/overlay/self_turn_points.hpp> + +#ifdef BOOST_GEOMETRY_DEBUG_HAS_SELF_INTERSECTIONS +# include <boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp> +# include <boost/geometry/io/dsv/write.hpp> +#endif + + +namespace boost { namespace geometry +{ + + +#if ! defined(BOOST_GEOMETRY_OVERLAY_NO_THROW) + +/*! +\brief Overlay Invalid Input Exception +\ingroup overlay +\details The overlay_invalid_input_exception is thrown at invalid input + */ +class overlay_invalid_input_exception : public geometry::exception +{ +public: + + inline overlay_invalid_input_exception() {} + + virtual char const* what() const throw() + { + return "Boost.Geometry Overlay invalid input exception"; + } +}; + +#endif + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + + +template <typename Geometry> +inline bool has_self_intersections(Geometry const& geometry) +{ + typedef typename point_type<Geometry>::type point_type; + typedef detail::overlay::turn_info<point_type> turn_info; + std::deque<turn_info> turns; + detail::disjoint::disjoint_interrupt_policy policy; + geometry::self_turns<detail::overlay::assign_null_policy>(geometry, turns, policy); + +#ifdef BOOST_GEOMETRY_DEBUG_HAS_SELF_INTERSECTIONS + bool first = true; +#endif + for(typename std::deque<turn_info>::const_iterator it = boost::begin(turns); + it != boost::end(turns); ++it) + { + turn_info const& info = *it; + bool const both_union_turn = + info.operations[0].operation == detail::overlay::operation_union + && info.operations[1].operation == detail::overlay::operation_union; + bool const both_intersection_turn = + info.operations[0].operation == detail::overlay::operation_intersection + && info.operations[1].operation == detail::overlay::operation_intersection; + + bool const valid = (both_union_turn || both_intersection_turn) + && (info.method == detail::overlay::method_touch + || info.method == detail::overlay::method_touch_interior); + + if (! valid) + { +#ifdef BOOST_GEOMETRY_DEBUG_HAS_SELF_INTERSECTIONS + if (first) + { + std::cout << "turn points: " << std::endl; + first = false; + } + std::cout << method_char(info.method); + for (int i = 0; i < 2; i++) + { + std::cout << " " << operation_char(info.operations[i].operation); + } + std::cout << " " << geometry::dsv(info.point) << std::endl; +#endif + +#if ! defined(BOOST_GEOMETRY_OVERLAY_NO_THROW) + throw overlay_invalid_input_exception(); +#endif + } + + } + return false; +} + + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_HAS_SELF_INTERSECTIONS_HPP + diff --git a/boost/geometry/algorithms/detail/not.hpp b/boost/geometry/algorithms/detail/not.hpp new file mode 100644 index 0000000000..abc3a4e168 --- /dev/null +++ b/boost/geometry/algorithms/detail/not.hpp @@ -0,0 +1,50 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2012 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2012 Mateusz Loskot, London, UK. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, 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_NOT_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_NOT_HPP + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail +{ + + + +/*! +\brief Structure negating the result of specified policy +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\tparam Policy +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\return Negation of the result of the policy + */ +template <typename Geometry1, typename Geometry2, typename Policy> +struct not_ +{ + static inline bool apply(Geometry1 const &geometry1, Geometry2 const& geometry2) + { + return ! Policy::apply(geometry1, geometry2); + } +}; + + +} // namespace detail +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_NOT_HPP diff --git a/boost/geometry/algorithms/detail/overlay/add_rings.hpp b/boost/geometry/algorithms/detail/overlay/add_rings.hpp new file mode 100644 index 0000000000..eb3e60e483 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/add_rings.hpp @@ -0,0 +1,134 @@ +// 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_ADD_RINGS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ADD_RINGS_HPP + +#include <boost/geometry/algorithms/detail/overlay/convert_ring.hpp> +#include <boost/geometry/algorithms/detail/overlay/get_ring.hpp> + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + +template +< + typename GeometryOut, + typename Geometry1, + typename Geometry2, + typename RingCollection +> +inline void convert_and_add(GeometryOut& result, + Geometry1 const& geometry1, Geometry2 const& geometry2, + RingCollection const& collection, + ring_identifier id, + bool reversed, bool append) +{ + typedef typename geometry::tag<Geometry1>::type tag1; + typedef typename geometry::tag<Geometry2>::type tag2; + typedef typename geometry::tag<GeometryOut>::type tag_out; + + if (id.source_index == 0) + { + convert_ring<tag_out>::apply(result, + get_ring<tag1>::apply(id, geometry1), + append, reversed); + } + else if (id.source_index == 1) + { + convert_ring<tag_out>::apply(result, + get_ring<tag2>::apply(id, geometry2), + append, reversed); + } + else if (id.source_index == 2) + { + convert_ring<tag_out>::apply(result, + get_ring<void>::apply(id, collection), + append, reversed); + } +} + +template +< + typename GeometryOut, + typename SelectionMap, + typename Geometry1, + typename Geometry2, + typename RingCollection, + typename OutputIterator +> +inline OutputIterator add_rings(SelectionMap const& map, + Geometry1 const& geometry1, Geometry2 const& geometry2, + RingCollection const& collection, + OutputIterator out) +{ + typedef typename SelectionMap::const_iterator iterator; + + for (iterator it = boost::begin(map); + it != boost::end(map); + ++it) + { + if (! it->second.discarded + && it->second.parent.source_index == -1) + { + GeometryOut result; + convert_and_add(result, geometry1, geometry2, collection, + it->first, it->second.reversed, false); + + // Add children + for (typename std::vector<ring_identifier>::const_iterator child_it + = it->second.children.begin(); + child_it != it->second.children.end(); + ++child_it) + { + iterator mit = map.find(*child_it); + if (mit != map.end() + && ! mit->second.discarded) + { + convert_and_add(result, geometry1, geometry2, collection, + *child_it, mit->second.reversed, true); + } + } + *out++ = result; + } + } + return out; +} + + +template +< + typename GeometryOut, + typename SelectionMap, + typename Geometry, + typename RingCollection, + typename OutputIterator +> +inline OutputIterator add_rings(SelectionMap const& map, + Geometry const& geometry, + RingCollection const& collection, + OutputIterator out) +{ + Geometry empty; + return add_rings<GeometryOut>(map, geometry, empty, collection, out); +} + + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ADD_RINGS_HPP diff --git a/boost/geometry/algorithms/detail/overlay/append_no_duplicates.hpp b/boost/geometry/algorithms/detail/overlay/append_no_duplicates.hpp new file mode 100644 index 0000000000..2c0f88e2aa --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/append_no_duplicates.hpp @@ -0,0 +1,53 @@ +// 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_APPEND_NO_DUPLICATES_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_APPEND_NO_DUPLICATES_HPP + + +#include <boost/range.hpp> + +#include <boost/geometry/algorithms/append.hpp> +#include <boost/geometry/algorithms/detail/disjoint.hpp> + + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + +template <typename Range, typename Point> +inline void append_no_duplicates(Range& range, Point const& point, bool force = false) +{ + if (boost::size(range) == 0 + || force + || ! geometry::detail::equals::equals_point_point(*(boost::end(range)-1), point)) + { +#ifdef BOOST_GEOMETRY_DEBUG_INTERSECTION + std::cout << " add: (" + << geometry::get<0>(point) << ", " << geometry::get<1>(point) << ")" + << std::endl; +#endif + geometry::append(range, point); + } +} + + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_APPEND_NO_DUPLICATES_HPP diff --git a/boost/geometry/algorithms/detail/overlay/assign_parents.hpp b/boost/geometry/algorithms/detail/overlay/assign_parents.hpp new file mode 100644 index 0000000000..133530563e --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/assign_parents.hpp @@ -0,0 +1,337 @@ +// 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_ASSIGN_PARENTS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ASSIGN_PARENTS_HPP + +#include <boost/geometry/algorithms/area.hpp> +#include <boost/geometry/algorithms/envelope.hpp> +#include <boost/geometry/algorithms/expand.hpp> +#include <boost/geometry/algorithms/detail/partition.hpp> +#include <boost/geometry/algorithms/detail/overlay/get_ring.hpp> +#include <boost/geometry/algorithms/within.hpp> + +#include <boost/geometry/geometries/box.hpp> + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + + + +template +< + typename Item, + typename Geometry1, typename Geometry2, + typename RingCollection +> +static inline bool within_selected_input(Item const& item2, ring_identifier const& ring_id, + Geometry1 const& geometry1, Geometry2 const& geometry2, + RingCollection const& collection) +{ + typedef typename geometry::tag<Geometry1>::type tag1; + typedef typename geometry::tag<Geometry2>::type tag2; + + switch (ring_id.source_index) + { + case 0 : + return geometry::within(item2.point, + get_ring<tag1>::apply(ring_id, geometry1)); + break; + case 1 : + return geometry::within(item2.point, + get_ring<tag2>::apply(ring_id, geometry2)); + break; + case 2 : + return geometry::within(item2.point, + get_ring<void>::apply(ring_id, collection)); + break; + } + return false; +} + + +template <typename Point> +struct ring_info_helper +{ + typedef typename geometry::default_area_result<Point>::type area_type; + + ring_identifier id; + area_type real_area; + area_type abs_area; + model::box<Point> envelope; + + inline ring_info_helper() + : real_area(0), abs_area(0) + {} + + inline ring_info_helper(ring_identifier i, area_type a) + : id(i), real_area(a), abs_area(geometry::math::abs(a)) + {} +}; + + +struct ring_info_helper_get_box +{ + template <typename Box, typename InputItem> + static inline void apply(Box& total, InputItem const& item) + { + geometry::expand(total, item.envelope); + } +}; + +struct ring_info_helper_ovelaps_box +{ + template <typename Box, typename InputItem> + static inline bool apply(Box const& box, InputItem const& item) + { + return ! geometry::detail::disjoint::disjoint_box_box(box, item.envelope); + } +}; + +template <typename Geometry1, typename Geometry2, typename Collection, typename RingMap> +struct assign_visitor +{ + typedef typename RingMap::mapped_type ring_info_type; + + Geometry1 const& m_geometry1; + Geometry2 const& m_geometry2; + Collection const& m_collection; + RingMap& m_ring_map; + bool m_check_for_orientation; + + + inline assign_visitor(Geometry1 const& g1, Geometry2 const& g2, Collection const& c, + RingMap& map, bool check) + : m_geometry1(g1) + , m_geometry2(g2) + , m_collection(c) + , m_ring_map(map) + , m_check_for_orientation(check) + {} + + template <typename Item> + inline void apply(Item const& outer, Item const& inner, bool first = true) + { + if (first && outer.real_area < 0) + { + // Reverse arguments + apply(inner, outer, false); + return; + } + + if (outer.real_area > 0) + { + if (inner.real_area < 0 || m_check_for_orientation) + { + 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) + ) + { + // 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; + } + } + } + } + } +}; + + + + +template +< + typename Geometry1, typename Geometry2, + typename RingCollection, + typename RingMap +> +inline void assign_parents(Geometry1 const& geometry1, + Geometry2 const& geometry2, + RingCollection const& collection, + RingMap& ring_map, + bool check_for_orientation = false) +{ + typedef typename geometry::tag<Geometry1>::type tag1; + typedef typename geometry::tag<Geometry2>::type tag2; + + typedef typename RingMap::mapped_type ring_info_type; + typedef typename ring_info_type::point_type point_type; + typedef model::box<point_type> box_type; + + typedef typename RingMap::iterator map_iterator_type; + + { + typedef ring_info_helper<point_type> helper; + typedef std::vector<helper> vector_type; + typedef typename boost::range_iterator<vector_type const>::type vector_iterator_type; + +#ifdef BOOST_GEOMETRY_TIME_OVERLAY + boost::timer timer; +#endif + + + std::size_t count_total = ring_map.size(); + std::size_t count_positive = 0; + std::size_t index_positive = 0; // only used if count_positive>0 + std::size_t index = 0; + + // Copy to vector (with new approach this might be obsolete as well, using the map directly) + vector_type vector(count_total); + + for (map_iterator_type it = boost::begin(ring_map); + it != boost::end(ring_map); ++it, ++index) + { + vector[index] = helper(it->first, it->second.get_area()); + helper& item = vector[index]; + switch(it->first.source_index) + { + case 0 : + geometry::envelope(get_ring<tag1>::apply(it->first, geometry1), + item.envelope); + break; + case 1 : + geometry::envelope(get_ring<tag2>::apply(it->first, geometry2), + item.envelope); + break; + case 2 : + geometry::envelope(get_ring<void>::apply(it->first, collection), + item.envelope); + break; + } + if (item.real_area > 0) + { + count_positive++; + index_positive = index; + } + } + +#ifdef BOOST_GEOMETRY_TIME_OVERLAY + std::cout << " ap: created helper vector: " << timer.elapsed() << std::endl; +#endif + + if (! check_for_orientation) + { + if (count_positive == count_total) + { + // Optimization for only positive rings + // -> no assignment of parents or reversal necessary, ready here. + return; + } + + if (count_positive == 1) + { + // Optimization for one outer ring + // -> assign this as parent to all others (all interior rings) + // In unions, this is probably the most occuring case and gives + // 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; + for (vector_iterator_type it = boost::begin(vector); + it != boost::end(vector); ++it, ++index) + { + if (index != index_positive) + { + ring_info_type& inner = ring_map[it->id]; + inner.parent = id_of_positive; + outer.children.push_back(it->id); + } + } + return; + } + } + + assign_visitor + < + Geometry1, Geometry2, + RingCollection, RingMap + > visitor(geometry1, geometry2, collection, ring_map, check_for_orientation); + + geometry::partition + < + box_type, ring_info_helper_get_box, ring_info_helper_ovelaps_box + >::apply(vector, visitor); + +#ifdef BOOST_GEOMETRY_TIME_OVERLAY + std::cout << " ap: quadradic loop: " << timer.elapsed() << std::endl; + std::cout << " ap: check_for_orientation " << check_for_orientation << std::endl; +#endif + } + + if (check_for_orientation) + { + for (map_iterator_type it = boost::begin(ring_map); + it != boost::end(ring_map); ++it) + { + if (geometry::math::equals(it->second.get_area(), 0)) + { + it->second.discarded = true; + } + else if (it->second.parent.source_index >= 0 && it->second.get_area() > 0) + { + // Discard positive inner ring with parent + it->second.discarded = true; + it->second.parent.source_index = -1; + } + else if (it->second.parent.source_index < 0 && it->second.get_area() < 0) + { + // Reverse negative ring without parent + it->second.reversed = true; + } + } + } + + // Assign childlist + for (map_iterator_type it = boost::begin(ring_map); + it != boost::end(ring_map); ++it) + { + if (it->second.parent.source_index >= 0) + { + ring_map[it->second.parent].children.push_back(it->first); + } + } +} + +template +< + typename Geometry, + typename RingCollection, + typename RingMap +> +inline void assign_parents(Geometry const& geometry, + RingCollection const& collection, + RingMap& ring_map) +{ + // Call it with an empty geometry + // (ring_map should be empty for source_id==1) + + Geometry empty; + assign_parents(geometry, empty, collection, ring_map, true); +} + + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ASSIGN_PARENTS_HPP diff --git a/boost/geometry/algorithms/detail/overlay/backtrack_check_si.hpp b/boost/geometry/algorithms/detail/overlay/backtrack_check_si.hpp new file mode 100644 index 0000000000..012b3aca30 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/backtrack_check_si.hpp @@ -0,0 +1,170 @@ +// 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_BACKTRACK_CHECK_SI_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_BACKTRACK_CHECK_SI_HPP + +#include <cstddef> +#include <string> + +#include <boost/range.hpp> + +#include <boost/geometry/core/access.hpp> +#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp> +#include <boost/geometry/algorithms/detail/has_self_intersections.hpp> +#if defined(BOOST_GEOMETRY_DEBUG_INTERSECTION) || defined(BOOST_GEOMETRY_OVERLAY_REPORT_WKT) +# include <boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp> +# include <boost/geometry/io/wkt/wkt.hpp> +#endif + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + +template <typename Turns> +inline void clear_visit_info(Turns& turns) +{ + typedef typename boost::range_value<Turns>::type tp_type; + + for (typename boost::range_iterator<Turns>::type + it = boost::begin(turns); + it != boost::end(turns); + ++it) + { + for (typename boost::range_iterator + < + typename tp_type::container_type + >::type op_it = boost::begin(it->operations); + op_it != boost::end(it->operations); + ++op_it) + { + op_it->visited.clear(); + } + it->discarded = false; + } +} + +struct backtrack_state +{ + bool m_good; + + inline backtrack_state() : m_good(true) {} + inline void reset() { m_good = true; } + inline bool good() const { return m_good; } +}; + + +template +< + typename Geometry1, + typename Geometry2 +> +class backtrack_check_self_intersections +{ + struct state : public backtrack_state + { + bool m_checked; + inline state() + : m_checked() + {} + }; +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, + Turns& turns, Operation& operation, + std::string const& , + Geometry1 const& geometry1, + Geometry2 const& geometry2, + 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); + } + + // Make bad output clean + rings.resize(size_at_start); + ring.clear(); + + // Reject this as a starting point + operation.visited.set_rejected(); + + // And clear all visit info + clear_visit_info(turns); + } +}; + +#ifdef BOOST_GEOMETRY_OVERLAY_REPORT_WKT +template +< + typename Geometry1, + typename Geometry2 +> +class backtrack_debug +{ +public : + typedef backtrack_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, + Turns& turns, Operation& operation, + std::string const& reason, + Geometry1 const& geometry1, + Geometry2 const& geometry2, + state_type& state + ) + { + std::cout << " REJECT " << reason << std::endl; + + state.m_good = false; + + rings.resize(size_at_start); + ring.clear(); + operation.visited.set_rejected(); + clear_visit_info(turns); + + int c = 0; + for (int i = 0; i < turns.size(); i++) + { + for (int j = 0; j < 2; j++) + { + if (turns[i].operations[j].visited.rejected()) + { + c++; + } + } + } + std::cout << "BACKTRACK (" << reason << " )" + << " " << c << " of " << turns.size() << " rejected" + << std::endl; + std::cout + << geometry::wkt(geometry1) << std::endl + << geometry::wkt(geometry2) << std::endl; + } +}; +#endif // BOOST_GEOMETRY_OVERLAY_REPORT_WKT + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_BACKTRACK_CHECK_SI_HPP diff --git a/boost/geometry/algorithms/detail/overlay/calculate_distance_policy.hpp b/boost/geometry/algorithms/detail/overlay/calculate_distance_policy.hpp new file mode 100644 index 0000000000..3e6a8897f5 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/calculate_distance_policy.hpp @@ -0,0 +1,55 @@ +// 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; + + template <typename Point1, typename Point2, typename Info> + static inline void apply(Info& info, Point1 const& p1, Point2 const& p2) + { + 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 new file mode 100644 index 0000000000..b210fd04b1 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/check_enrich.hpp @@ -0,0 +1,172 @@ +// 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_CHECK_ENRICH_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_CHECK_ENRICH_HPP + + +#include <cstddef> + + +#include <boost/assert.hpp> +#include <boost/range.hpp> + + + +#include <boost/geometry/algorithms/detail/ring_identifier.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + + +template<typename Turn> +struct meta_turn +{ + int index; + Turn const* turn; + bool handled[2]; + + inline meta_turn(int i, Turn const& t) + : index(i), turn(&t) + { + handled[0] = false; + handled[1] = false; + } +}; + + +template <typename MetaTurn> +inline void display(MetaTurn const& meta_turn, std::string const& reason = "") +{ +#ifdef BOOST_GEOMETRY_DEBUG_ENRICH + std::cout << meta_turn.index + << "\tMethods: " << method_char(meta_turn.turn->method) + << " operations: " << operation_char(meta_turn.turn->operations[0].operation) + << operation_char(meta_turn.turn->operations[1].operation) + << " travels to " << meta_turn.turn->operations[0].enriched.travels_to_ip_index + << " and " << meta_turn.turn->operations[1].enriched.travels_to_ip_index + //<< " -> " << op_index + << " " << reason + << std::endl; +#endif +} + + +template <typename MetaTurns, typename MetaTurn> +inline void check_detailed(MetaTurns& meta_turns, MetaTurn const& meta_turn, + int op_index, int cycle, int start, operation_type for_operation, + bool& error) +{ + display(meta_turn); + int const ip_index = meta_turn.turn->operations[op_index].enriched.travels_to_ip_index; + if (ip_index >= 0) + { + bool found = false; + + if (ip_index == start) + { + display(meta_turns[ip_index], " FINISH"); + return; + } + + // check on continuing, or on same-operation-on-same-geometry + if (! meta_turns[ip_index].handled[op_index] + && (meta_turns[ip_index].turn->operations[op_index].operation == operation_continue + || meta_turns[ip_index].turn->operations[op_index].operation == for_operation) + ) + { + meta_turns[ip_index].handled[op_index] = true; + check_detailed(meta_turns, meta_turns[ip_index], op_index, cycle, start, for_operation, error); + found = true; + } + // check on other geometry + if (! found) + { + int const other_index = 1 - op_index; + if (! meta_turns[ip_index].handled[other_index] + && meta_turns[ip_index].turn->operations[other_index].operation == for_operation) + { + meta_turns[ip_index].handled[other_index] = true; + check_detailed(meta_turns, meta_turns[ip_index], other_index, cycle, start, for_operation, error); + found = true; + } + } + + if (! found) + { + display(meta_turns[ip_index], " STOP"); + error = true; +#ifndef BOOST_GEOMETRY_DEBUG_ENRICH + //std::cout << " STOP"; +#endif + } + } +} + + +template <typename TurnPoints> +inline bool check_graph(TurnPoints& turn_points, operation_type for_operation) +{ + typedef typename boost::range_value<TurnPoints>::type turn_point_type; + + bool error = false; + int index = 0; + + std::vector<meta_turn<turn_point_type> > meta_turns; + for (typename boost::range_iterator<TurnPoints const>::type + it = boost::begin(turn_points); + it != boost::end(turn_points); + ++it, ++index) + { + meta_turns.push_back(meta_turn<turn_point_type>(index, *it)); + } + + int cycle = 0; + for (typename boost::range_iterator<std::vector<meta_turn<turn_point_type> > > ::type + it = boost::begin(meta_turns); + it != boost::end(meta_turns); + ++it) + { + if (! (it->turn->blocked() || it->turn->is_discarded())) + { + for (int i = 0 ; i < 2; i++) + { + if (! it->handled[i] + && it->turn->operations[i].operation == for_operation) + { +#ifdef BOOST_GEOMETRY_DEBUG_ENRICH + std::cout << "CYCLE " << cycle << std::endl; +#endif + it->handled[i] = true; + check_detailed(meta_turns, *it, i, cycle++, it->index, for_operation, error); +#ifdef BOOST_GEOMETRY_DEBUG_ENRICH + std::cout <<" END CYCLE " << it->index << std::endl; +#endif + } + } + } + } + return error; +} + + + +}} // namespace detail::overlay +#endif //DOXYGEN_NO_DETAIL + + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_CHECK_ENRICH_HPP diff --git a/boost/geometry/algorithms/detail/overlay/clip_linestring.hpp b/boost/geometry/algorithms/detail/overlay/clip_linestring.hpp new file mode 100644 index 0000000000..fc4f657322 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/clip_linestring.hpp @@ -0,0 +1,242 @@ +// 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_CLIP_LINESTRING_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_CLIP_LINESTRING_HPP + +#include <boost/range.hpp> + +#include <boost/geometry/algorithms/clear.hpp> +#include <boost/geometry/algorithms/convert.hpp> + +#include <boost/geometry/algorithms/detail/overlay/append_no_duplicates.hpp> + +#include <boost/geometry/util/select_coordinate_type.hpp> +#include <boost/geometry/geometries/segment.hpp> + +namespace boost { namespace geometry +{ + +namespace strategy { namespace intersection +{ + +/*! + \brief Strategy: line clipping algorithm after Liang Barsky + \ingroup overlay + \details The Liang-Barsky line clipping algorithm clips a line with a clipping box. + It is slightly adapted in the sense that it returns which points are clipped + \tparam B input box type of clipping box + \tparam P input/output point-type of segments to be clipped + \note The algorithm is currently only implemented for 2D Cartesian points + \note Though it is implemented in namespace strategy, and theoretically another + strategy could be used, it is not (yet) updated to the general strategy concepts, + and not (yet) splitted into a file in folder strategies + \author Barend Gehrels, and the following recourses + - A tutorial: http://www.skytopia.com/project/articles/compsci/clipping.html + - a German applet (link broken): http://ls7-www.cs.uni-dortmund.de/students/projectgroups/acit/lineclip.shtml +*/ +template<typename Box, typename Point> +class liang_barsky +{ +private: + typedef model::referring_segment<Point> segment_type; + + template <typename T> + inline bool check_edge(T const& p, T const& q, T& t1, T& t2) const + { + bool visible = true; + + if(p < 0) + { + T const r = q / p; + if (r > t2) + visible = false; + else if (r > t1) + t1 = r; + } + else if(p > 0) + { + T const r = q / p; + if (r < t1) + visible = false; + else if (r < t2) + t2 = r; + } + else + { + if (q < 0) + visible = false; + } + + return visible; + } + +public: + + inline bool clip_segment(Box const& b, segment_type& s, bool& sp1_clipped, bool& sp2_clipped) const + { + typedef typename select_coordinate_type<Box, Point>::type coordinate_type; + + coordinate_type t1 = 0; + coordinate_type t2 = 1; + + coordinate_type const dx = get<1, 0>(s) - get<0, 0>(s); + coordinate_type const dy = get<1, 1>(s) - get<0, 1>(s); + + coordinate_type const p1 = -dx; + coordinate_type const p2 = dx; + coordinate_type const p3 = -dy; + coordinate_type const p4 = dy; + + coordinate_type const q1 = get<0, 0>(s) - get<min_corner, 0>(b); + coordinate_type const q2 = get<max_corner, 0>(b) - get<0, 0>(s); + coordinate_type const q3 = get<0, 1>(s) - get<min_corner, 1>(b); + coordinate_type const q4 = get<max_corner, 1>(b) - get<0, 1>(s); + + if (check_edge(p1, q1, t1, t2) // left + && check_edge(p2, q2, t1, t2) // right + && check_edge(p3, q3, t1, t2) // bottom + && check_edge(p4, q4, t1, t2)) // top + { + sp1_clipped = t1 > 0; + sp2_clipped = t2 < 1; + + if (sp2_clipped) + { + set<1, 0>(s, get<0, 0>(s) + t2 * dx); + set<1, 1>(s, get<0, 1>(s) + t2 * dy); + } + + if(sp1_clipped) + { + set<0, 0>(s, get<0, 0>(s) + t1 * dx); + set<0, 1>(s, get<0, 1>(s) + t1 * dy); + } + + return true; + } + + return false; + } + + template<typename Linestring, typename OutputIterator> + inline void apply(Linestring& line_out, OutputIterator out) const + { + if (!boost::empty(line_out)) + { + *out = line_out; + ++out; + geometry::clear(line_out); + } + } +}; + + +}} // namespace strategy::intersection + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace intersection +{ + +/*! + \brief Clips a linestring with a box + \details A linestring is intersected (clipped) by the specified box + and the resulting linestring, or pieces of linestrings, are sent to the specified output operator. + \tparam OutputLinestring type of the output linestrings + \tparam OutputIterator an output iterator which outputs linestrings + \tparam Linestring linestring-type, for example a vector of points, matching the output-iterator type, + the points should also match the input-iterator type + \tparam Box box type + \tparam Strategy strategy, a clipping strategy which should implement the methods "clip_segment" and "apply" +*/ +template +< + typename OutputLinestring, + typename OutputIterator, + typename Range, + typename Box, + typename Strategy +> +OutputIterator clip_range_with_box(Box const& b, Range const& range, + OutputIterator out, Strategy const& strategy) +{ + if (boost::begin(range) == boost::end(range)) + { + return out; + } + + typedef typename point_type<OutputLinestring>::type point_type; + + OutputLinestring line_out; + + typedef typename boost::range_iterator<Range const>::type iterator_type; + iterator_type vertex = boost::begin(range); + for(iterator_type previous = vertex++; + vertex != boost::end(range); + ++previous, ++vertex) + { + point_type p1, p2; + geometry::convert(*previous, p1); + geometry::convert(*vertex, p2); + + // Clip the segment. Five situations: + // 1. Segment is invisible, finish line if any (shouldn't occur) + // 2. Segment is completely visible. Add (p1)-p2 to line + // 3. Point 1 is invisible (clipped), point 2 is visible. Start new line from p1-p2... + // 4. Point 1 is visible, point 2 is invisible (clipped). End the line with ...p2 + // 5. Point 1 and point 2 are both invisible (clipped). Start/finish an independant line p1-p2 + // + // This results in: + // a. if p1 is clipped, start new line + // b. if segment is partly or completely visible, add the segment + // c. if p2 is clipped, end the line + + bool c1 = false; + bool c2 = false; + model::referring_segment<point_type> s(p1, p2); + + if (!strategy.clip_segment(b, s, c1, c2)) + { + strategy.apply(line_out, out); + } + else + { + // a. If necessary, finish the line and add a start a new one + if (c1) + { + strategy.apply(line_out, out); + } + + // b. Add p1 only if it is the first point, then add p2 + if (boost::empty(line_out)) + { + detail::overlay::append_no_duplicates(line_out, p1, true); + } + detail::overlay::append_no_duplicates(line_out, p2); + + // c. If c2 is clipped, finish the line + if (c2) + { + strategy.apply(line_out, out); + } + } + + } + + // Add last part + strategy.apply(line_out, out); + return out; +} + +}} // namespace detail::intersection +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_CLIP_LINESTRING_HPP diff --git a/boost/geometry/algorithms/detail/overlay/convert_ring.hpp b/boost/geometry/algorithms/detail/overlay/convert_ring.hpp new file mode 100644 index 0000000000..05bd721e7f --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/convert_ring.hpp @@ -0,0 +1,99 @@ +// 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_CONVERT_RING_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_CONVERT_RING_HPP + + +#include <boost/mpl/assert.hpp> +#include <boost/range.hpp> +#include <boost/range/algorithm/reverse.hpp> + +#include <boost/geometry/core/tags.hpp> +#include <boost/geometry/core/exterior_ring.hpp> +#include <boost/geometry/core/interior_rings.hpp> +#include <boost/geometry/algorithms/detail/ring_identifier.hpp> + +#include <boost/geometry/algorithms/convert.hpp> + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + + +template<typename Tag> +struct convert_ring +{ + BOOST_MPL_ASSERT_MSG + ( + false, NOT_OR_NOT_YET_IMPLEMENTED_FOR_THIS_GEOMETRY_TAG + , (types<Tag>) + ); +}; + +template<> +struct convert_ring<ring_tag> +{ + template<typename Destination, typename Source> + static inline void apply(Destination& destination, Source const& source, + bool append, bool reverse) + { + if (! append) + { + geometry::convert(source, destination); + if (reverse) + { + boost::reverse(destination); + } + } + } +}; + + +template<> +struct convert_ring<polygon_tag> +{ + template<typename Destination, typename Source> + static inline void apply(Destination& destination, Source const& source, + bool append, bool reverse) + { + if (! append) + { + geometry::convert(source, exterior_ring(destination)); + if (reverse) + { + boost::reverse(exterior_ring(destination)); + } + } + else + { + interior_rings(destination).resize( + interior_rings(destination).size() + 1); + geometry::convert(source, interior_rings(destination).back()); + if (reverse) + { + boost::reverse(interior_rings(destination).back()); + } + } + } +}; + + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_CONVERT_RING_HPP diff --git a/boost/geometry/algorithms/detail/overlay/copy_segment_point.hpp b/boost/geometry/algorithms/detail/overlay/copy_segment_point.hpp new file mode 100644 index 0000000000..379444428f --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/copy_segment_point.hpp @@ -0,0 +1,295 @@ +// 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_COPY_SEGMENT_POINT_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_COPY_SEGMENT_POINT_HPP + + +#include <boost/array.hpp> +#include <boost/mpl/assert.hpp> +#include <boost/range.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/algorithms/convert.hpp> +#include <boost/geometry/geometries/concepts/check.hpp> +#include <boost/geometry/views/closeable_view.hpp> +#include <boost/geometry/views/reversible_view.hpp> + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace copy_segments +{ + + +template <typename Range, bool Reverse, typename SegmentIdentifier, typename PointOut> +struct copy_segment_point_range +{ + typedef typename closeable_view + < + Range const, + closure<Range>::value + >::type cview_type; + + typedef typename reversible_view + < + cview_type const, + Reverse ? iterate_reverse : iterate_forward + >::type rview_type; + + static inline bool apply(Range const& range, + SegmentIdentifier const& seg_id, bool second, + PointOut& point) + { + int index = seg_id.segment_index; + if (second) + { + index++; + if (index >= boost::size(range)) + { + index = 0; + } + } + + // Exception? + if (index >= boost::size(range)) + { + return false; + } + + cview_type cview(range); + rview_type view(cview); + + + geometry::convert(*(boost::begin(view) + index), point); + return true; + } +}; + + +template <typename Polygon, bool Reverse, typename SegmentIdentifier, typename PointOut> +struct copy_segment_point_polygon +{ + static inline bool apply(Polygon const& polygon, + SegmentIdentifier const& seg_id, bool second, + PointOut& point) + { + // Call ring-version with the right ring + return copy_segment_point_range + < + typename geometry::ring_type<Polygon>::type, + Reverse, + SegmentIdentifier, + PointOut + >::apply + ( + seg_id.ring_index < 0 + ? geometry::exterior_ring(polygon) + : geometry::interior_rings(polygon)[seg_id.ring_index], + seg_id, second, + point + ); + } +}; + + +template <typename Box, bool Reverse, typename SegmentIdentifier, typename PointOut> +struct copy_segment_point_box +{ + static inline bool apply(Box const& box, + SegmentIdentifier const& seg_id, bool second, + PointOut& point) + { + int index = seg_id.segment_index; + if (second) + { + index++; + } + + boost::array<typename point_type<Box>::type, 4> bp; + assign_box_corners_oriented<Reverse>(box, bp); + point = bp[index % 4]; + return true; + } +}; + + + + +}} // namespace detail::copy_segments +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +template +< + typename Tag, + typename GeometryIn, + bool Reverse, + typename SegmentIdentifier, + typename PointOut +> +struct copy_segment_point +{ + BOOST_MPL_ASSERT_MSG + ( + false, NOT_OR_NOT_YET_IMPLEMENTED_FOR_THIS_GEOMETRY_TYPE + , (types<GeometryIn>) + ); +}; + + +template <typename LineString, bool Reverse, typename SegmentIdentifier, typename PointOut> +struct copy_segment_point<linestring_tag, LineString, Reverse, SegmentIdentifier, PointOut> + : detail::copy_segments::copy_segment_point_range + < + LineString, Reverse, SegmentIdentifier, PointOut + > +{}; + + +template <typename Ring, bool Reverse, typename SegmentIdentifier, typename PointOut> +struct copy_segment_point<ring_tag, Ring, Reverse, SegmentIdentifier, PointOut> + : detail::copy_segments::copy_segment_point_range + < + Ring, Reverse, SegmentIdentifier, PointOut + > +{}; + +template <typename Polygon, bool Reverse, typename SegmentIdentifier, typename PointOut> +struct copy_segment_point<polygon_tag, Polygon, Reverse, SegmentIdentifier, PointOut> + : detail::copy_segments::copy_segment_point_polygon + < + Polygon, Reverse, SegmentIdentifier, PointOut + > +{}; + + +template <typename Box, bool Reverse, typename SegmentIdentifier, typename PointOut> +struct copy_segment_point<box_tag, Box, Reverse, SegmentIdentifier, PointOut> + : detail::copy_segments::copy_segment_point_box + < + Box, Reverse, SegmentIdentifier, PointOut + > +{}; + + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + + + + +/*! + \brief Helper function, copies a point from a segment + \ingroup overlay + */ +template<bool Reverse, typename Geometry, typename SegmentIdentifier, typename PointOut> +inline bool copy_segment_point(Geometry const& geometry, + SegmentIdentifier const& seg_id, bool second, + PointOut& point_out) +{ + concept::check<Geometry const>(); + + return dispatch::copy_segment_point + < + typename tag<Geometry>::type, + Geometry, + Reverse, + SegmentIdentifier, + PointOut + >::apply(geometry, seg_id, second, point_out); +} + + +/*! + \brief Helper function, to avoid the same construct several times, + copies a point, based on a source-index and two geometries + \ingroup overlay + */ +template +< + bool Reverse1, bool Reverse2, + typename Geometry1, typename Geometry2, + typename SegmentIdentifier, + typename PointOut +> +inline bool copy_segment_point(Geometry1 const& geometry1, Geometry2 const& geometry2, + SegmentIdentifier const& seg_id, bool second, + PointOut& point_out) +{ + concept::check<Geometry1 const>(); + concept::check<Geometry2 const>(); + + if (seg_id.source_index == 0) + { + return dispatch::copy_segment_point + < + typename tag<Geometry1>::type, + Geometry1, + Reverse1, + SegmentIdentifier, + PointOut + >::apply(geometry1, seg_id, second, point_out); + } + else if (seg_id.source_index == 1) + { + return dispatch::copy_segment_point + < + typename tag<Geometry2>::type, + Geometry2, + Reverse2, + SegmentIdentifier, + PointOut + >::apply(geometry2, seg_id, second, point_out); + } + // Exception? + return false; +} + + +/*! + \brief Helper function, to avoid the same construct several times, + copies a point, based on a source-index and two geometries + \ingroup overlay + */ +template +< + bool Reverse1, bool Reverse2, + typename Geometry1, typename Geometry2, + typename SegmentIdentifier, + typename PointOut +> +inline bool copy_segment_points(Geometry1 const& geometry1, Geometry2 const& geometry2, + SegmentIdentifier const& seg_id, + PointOut& point1, PointOut& point2) +{ + concept::check<Geometry1 const>(); + concept::check<Geometry2 const>(); + + return copy_segment_point<Reverse1, Reverse2>(geometry1, geometry2, seg_id, false, point1) + && copy_segment_point<Reverse1, Reverse2>(geometry1, geometry2, seg_id, true, point2); +} + + + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_COPY_SEGMENT_POINT_HPP diff --git a/boost/geometry/algorithms/detail/overlay/copy_segments.hpp b/boost/geometry/algorithms/detail/overlay/copy_segments.hpp new file mode 100644 index 0000000000..b0183a3ac2 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/copy_segments.hpp @@ -0,0 +1,328 @@ +// 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_COPY_SEGMENTS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_COPY_SEGMENTS_HPP + + +#include <boost/array.hpp> +#include <boost/mpl/assert.hpp> +#include <vector> + +#include <boost/assert.hpp> +#include <boost/range.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/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> + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace copy_segments +{ + + +template +< + typename Ring, + bool Reverse, + typename SegmentIdentifier, + typename RangeOut +> +struct copy_segments_ring +{ + typedef typename closeable_view + < + Ring const, + closure<Ring>::value + >::type cview_type; + + 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; + + 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); + + // The problem: sometimes we want to from "3" to "2" + // -> end = "3" -> end == begin + // This is not convenient with iterators. + + // So we use the ever-circling iterator and determine when to step out + + int const from_index = seg_id.segment_index + 1; + + // Sanity check + BOOST_ASSERT(from_index < boost::size(view)); + + ec_iterator it(boost::begin(view), boost::end(view), + boost::begin(view) + from_index); + + // [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 + ? to_index - from_index + 1 + : boost::size(view) - from_index + to_index + 1; + + for (size_type i = 0; i < count; ++i, ++it) + { + detail::overlay::append_no_duplicates(current_output, *it); + } + } +}; + +template +< + typename LineString, + bool Reverse, + typename SegmentIdentifier, + typename RangeOut +> +struct copy_segments_linestring +{ + + typedef typename boost::range_iterator<LineString const>::type iterator; + + static inline void apply(LineString const& ls, + SegmentIdentifier const& seg_id, int to_index, + RangeOut& current_output) + { + int const from_index = seg_id.segment_index + 1; + + // Sanity check + if (from_index > to_index || from_index < 0 || to_index >= boost::size(ls)) + { + return; + } + + typedef typename boost::range_difference<LineString>::type size_type; + size_type const count = to_index - from_index + 1; + + typename boost::range_iterator<LineString const>::type it = boost::begin(ls) + from_index; + + for (size_type i = 0; i < count; ++i, ++it) + { + detail::overlay::append_no_duplicates(current_output, *it); + } + } +}; + +template +< + typename Polygon, + bool Reverse, + typename SegmentIdentifier, + typename RangeOut +> +struct copy_segments_polygon +{ + static inline void apply(Polygon const& polygon, + SegmentIdentifier const& seg_id, int to_index, + 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 + ? geometry::exterior_ring(polygon) + : geometry::interior_rings(polygon)[seg_id.ring_index], + seg_id, to_index, + current_output + ); + } +}; + + +template +< + typename Box, + bool Reverse, + typename SegmentIdentifier, + typename RangeOut +> +struct copy_segments_box +{ + static inline void apply(Box const& box, + SegmentIdentifier const& seg_id, int to_index, + RangeOut& current_output) + { + int index = seg_id.segment_index + 1; + BOOST_ASSERT(index < 5); + + int const count = index <= to_index + ? to_index - index + 1 + : 5 - index + to_index + 1; + + // Create array of points, the fifth one closes it + boost::array<typename point_type<Box>::type, 5> bp; + assign_box_corners_oriented<Reverse>(box, bp); + bp[4] = bp[0]; + + // (possibly cyclic) copy to output + // (see comments in ring-version) + for (int i = 0; i < count; i++, index++) + { + detail::overlay::append_no_duplicates(current_output, bp[index % 5]); + + } + } +}; + + +}} // namespace detail::copy_segments +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template +< + typename Tag, + typename GeometryIn, + bool Reverse, + typename SegmentIdentifier, + typename RangeOut +> +struct copy_segments +{ + BOOST_MPL_ASSERT_MSG + ( + false, NOT_OR_NOT_YET_IMPLEMENTED_FOR_THIS_GEOMETRY_TYPE + , (types<GeometryIn>) + ); +}; + + +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 +< + 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 +< + 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 +< + typename Box, + bool Reverse, + typename SegmentIdentifier, + typename RangeOut +> +struct copy_segments<box_tag, Box, Reverse, SegmentIdentifier, RangeOut> + : detail::copy_segments::copy_segments_box + < + Box, Reverse, SegmentIdentifier, RangeOut + > +{}; + + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +/*! + \brief Copy segments from a geometry, starting with the specified segment (seg_id) + until the specified index (to_index) + \ingroup overlay + */ +template +< + bool Reverse, + typename Geometry, + typename SegmentIdentifier, + typename RangeOut +> +inline void copy_segments(Geometry const& geometry, + SegmentIdentifier const& seg_id, int to_index, + RangeOut& range_out) +{ + concept::check<Geometry const>(); + + dispatch::copy_segments + < + typename tag<Geometry>::type, + Geometry, + Reverse, + SegmentIdentifier, + RangeOut + >::apply(geometry, seg_id, to_index, range_out); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_COPY_SEGMENTS_HPP diff --git a/boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp b/boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp new file mode 100644 index 0000000000..0bec816946 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp @@ -0,0 +1,64 @@ +// 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_DEBUG_TURN_INFO_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_DEBUG_TURN_INFO_HPP + +#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp> +#include <boost/geometry/algorithms/detail/overlay/visit_info.hpp> + + +namespace boost { namespace geometry +{ + +inline char method_char(detail::overlay::method_type const& method) +{ + using namespace detail::overlay; + switch(method) + { + case method_none : return '-'; + case method_disjoint : return 'd'; + case method_crosses : return 'i'; + case method_touch : return 't'; + case method_touch_interior : return 'm'; + case method_collinear : return 'c'; + case method_equal : return 'e'; + default : return '?'; + } +} + +inline char operation_char(detail::overlay::operation_type const& operation) +{ + using namespace detail::overlay; + switch(operation) + { + case operation_none : return '-'; + case operation_union : return 'u'; + case operation_intersection : return 'i'; + case operation_blocked : return 'x'; + case operation_continue : return 'c'; + default : return '?'; + } +} + +inline char visited_char(detail::overlay::visit_info const& v) +{ + if (v.rejected()) return 'R'; + if (v.started()) return 's'; + if (v.visited()) return 'v'; + if (v.none()) return '-'; + if (v.finished()) return 'f'; + return '?'; +} + + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_DEBUG_TURN_INFO_HPP diff --git a/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp b/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp new file mode 100644 index 0000000000..e4842d35f1 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp @@ -0,0 +1,523 @@ +// 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_ENRICH_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ENRICH_HPP + +#include <cstddef> +#include <algorithm> +#include <map> +#include <set> +#include <vector> + +#ifdef BOOST_GEOMETRY_DEBUG_ENRICH +# include <iostream> +# include <boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp> +# include <boost/geometry/io/wkt/wkt.hpp> +# define BOOST_GEOMETRY_DEBUG_IDENTIFIER +#endif + +#include <boost/assert.hpp> +#include <boost/range.hpp> + +#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> +#ifdef BOOST_GEOMETRY_DEBUG_ENRICH +# include <boost/geometry/algorithms/detail/overlay/check_enrich.hpp> +#endif + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + +// Wraps "turn_operation" from turn_info.hpp, +// giving it extra information +template <typename TurnOperation> +struct indexed_turn_operation +{ + typedef TurnOperation type; + + int index; + int operation_index; + bool discarded; + TurnOperation subject; + + inline indexed_turn_operation(int i, int oi, TurnOperation const& s) + : index(i) + , operation_index(oi) + , discarded(false) + , subject(s) + {} +}; + +template <typename IndexedTurnOperation> +struct remove_discarded +{ + inline bool operator()(IndexedTurnOperation const& operation) const + { + return operation.discarded; + } +}; + + +template +< + typename TurnPoints, + typename Indexed, + typename Geometry1, typename Geometry2, + bool Reverse1, bool Reverse2, + typename Strategy +> +struct sort_on_segment_and_distance +{ + inline sort_on_segment_and_distance(TurnPoints const& turn_points + , Geometry1 const& geometry1 + , Geometry2 const& geometry2 + , Strategy const& strategy + , bool* clustered) + : m_turn_points(turn_points) + , m_geometry1(geometry1) + , m_geometry2(geometry2) + , m_strategy(strategy) + , m_clustered(clustered) + { + } + +private : + + TurnPoints const& m_turn_points; + Geometry1 const& m_geometry1; + Geometry2 const& m_geometry2; + Strategy const& m_strategy; + mutable bool* m_clustered; + + 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, + 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); + + int const order = get_relative_order + < + point_type + >::apply(pi, pj,ri, rj, si, sj); + //debug("r/o", order == -1); + return order == -1; + } + +public : + + // Note that left/right do NOT correspond to m_geometry1/m_geometry2 + // 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; + + if (sl == sr + && geometry::math::equals(left.subject.enriched.distance + , right.subject.enriched.distance)) + { + // 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) + { + return consider_relative_order(left, right); + } + + // If that is not the case, cluster it later on. + // Indicate that this is necessary. + *m_clustered = true; + + return left.index < right.index; + } + return sl == sr + ? left.subject.enriched.distance < right.subject.enriched.distance + : sl < sr; + + } +}; + + +template<typename Turns, typename Operations> +inline void update_discarded(Turns& turn_points, Operations& operations) +{ + // Vice-versa, set discarded to true for discarded operations; + // AND set discarded points to true + for (typename boost::range_iterator<Operations>::type it = boost::begin(operations); + it != boost::end(operations); + ++it) + { + if (turn_points[it->index].discarded) + { + it->discarded = true; + } + else if (it->discarded) + { + turn_points[it->index].discarded = true; + } + } +} + + +// Sorts IP-s of this ring on segment-identifier, and if on same segment, +// on distance. +// Then assigns for each IP which is the next IP on this segment, +// plus the vertex-index to travel to, plus the next IP +// (might be on another segment) +template +< + typename IndexType, + bool Reverse1, bool Reverse2, + typename Container, + typename TurnPoints, + typename Geometry1, typename Geometry2, + typename Strategy +> +inline void enrich_sort(Container& operations, + TurnPoints& turn_points, + operation_type for_operation, + Geometry1 const& geometry1, Geometry2 const& geometry2, + Strategy const& strategy) +{ + typedef typename IndexType::type operations_type; + + bool clustered = false; + std::sort(boost::begin(operations), + boost::end(operations), + sort_on_segment_and_distance + < + TurnPoints, + IndexType, + Geometry1, Geometry2, + Reverse1, Reverse2, + Strategy + >(turn_points, geometry1, geometry2, 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 + // the way round. See #105 + + if (clustered) + { + typedef typename boost::range_iterator<Container>::type nc_iterator; + nc_iterator it = boost::begin(operations); + nc_iterator begin_cluster = boost::end(operations); + for (nc_iterator prev = it++; + it != boost::end(operations); + prev = it++) + { + operations_type& prev_op = turn_points[prev->index] + .operations[prev->operation_index]; + operations_type& op = turn_points[it->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)) + { + if (begin_cluster == boost::end(operations)) + { + begin_cluster = prev; + } + } + else if (begin_cluster != boost::end(operations)) + { + handle_cluster<IndexType, Reverse1, Reverse2>(begin_cluster, it, turn_points, + for_operation, geometry1, geometry2, 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); + } + } + + update_discarded(turn_points, operations); +} + + +template +< + typename IndexType, + typename Container, + typename TurnPoints +> +inline void enrich_discard(Container& operations, TurnPoints& turn_points) +{ + update_discarded(turn_points, operations); + + // Then delete discarded operations from vector + remove_discarded<IndexType> predicate; + operations.erase( + std::remove_if(boost::begin(operations), + boost::end(operations), + predicate), + boost::end(operations)); +} + +template +< + typename IndexType, + typename Container, + typename TurnPoints, + typename Geometry1, + typename Geometry2, + typename Strategy +> +inline void enrich_assign(Container& operations, + TurnPoints& turn_points, + operation_type , + Geometry1 const& , Geometry2 const& , + Strategy const& ) +{ + typedef typename IndexType::type operations_type; + typedef typename boost::range_iterator<Container const>::type iterator_type; + + + if (operations.size() > 0) + { + // Assign travel-to-vertex/ip index for each turning point. + // Because IP's are circular, PREV starts at the very last one, + // being assigned from the first one. + // "next ip on same segment" should not be considered circular. + bool first = true; + iterator_type it = boost::begin(operations); + for (iterator_type prev = it + (boost::size(operations) - 1); + it != boost::end(operations); + prev = it++) + { + operations_type& prev_op + = turn_points[prev->index].operations[prev->operation_index]; + operations_type& op + = turn_points[it->index].operations[it->operation_index]; + + prev_op.enriched.travels_to_ip_index + = it->index; + prev_op.enriched.travels_to_vertex_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; + } + first = false; + } + } + + // DEBUG +#ifdef BOOST_GEOMETRY_DEBUG_ENRICH + { + for (iterator_type it = boost::begin(operations); + it != boost::end(operations); + ++it) + { + operations_type& op = turn_points[it->index] + .operations[it->operation_index]; + + std::cout << it->index + << " meth: " << method_char(turn_points[it->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") + << " ->vtx " << op.enriched.travels_to_vertex_index + << " ->ip " << op.enriched.travels_to_ip_index + << " ->nxt ip " << op.enriched.next_ip_index + //<< " vis: " << visited_char(op.visited) + << std::endl; + ; + } + } +#endif + // END DEBUG + +} + + +template <typename IndexedType, typename TurnPoints, typename MappedVector> +inline void create_map(TurnPoints const& turn_points, MappedVector& mapped_vector) +{ + 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) + { + // Add operations on this ring, but skip discarded ones + if (! it->discarded) + { + 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) + { + // We should NOT skip blocked operations here + // because they can be relevant for "the other side" + // NOT if (op_it->operation != operation_blocked) + + ring_identifier ring_id + ( + op_it->seg_id.source_index, + op_it->seg_id.multi_index, + op_it->seg_id.ring_index + ); + mapped_vector[ring_id].push_back + ( + IndexedType(index, op_index, *op_it) + ); + } + } + } +} + + +}} // namespace detail::overlay +#endif //DOXYGEN_NO_DETAIL + + + +/*! +\brief All intersection points are enriched with successor information +\ingroup overlay +\tparam TurnPoints type of intersection container + (e.g. vector of "intersection/turn point"'s) +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\tparam Strategy side strategy type +\param turn_points container containing intersectionpoints +\param for_operation operation_type (union or intersection) +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\param strategy strategy + */ +template +< + bool Reverse1, bool Reverse2, + typename TurnPoints, + typename Geometry1, typename Geometry2, + typename Strategy +> +inline void enrich_intersection_points(TurnPoints& turn_points, + detail::overlay::operation_type for_operation, + Geometry1 const& geometry1, Geometry2 const& geometry2, + Strategy const& strategy) +{ + typedef typename boost::range_value<TurnPoints>::type turn_point_type; + typedef typename turn_point_type::turn_operation_type turn_operation_type; + typedef detail::overlay::indexed_turn_operation + < + turn_operation_type + > indexed_turn_operation; + + typedef std::map + < + ring_identifier, + std::vector<indexed_turn_operation> + > mapped_vector_type; + + // DISCARD ALL UU + // #76 is the reason that this is necessary... + // With uu, at all points there is the risk that rings are being traversed twice or more. + // Without uu, all rings having only uu will be untouched and gathered by assemble + for (typename boost::range_iterator<TurnPoints>::type + it = boost::begin(turn_points); + it != boost::end(turn_points); + ++it) + { + if (it->both(detail::overlay::operation_union)) + { + it->discarded = true; + } + } + + + // Create a map of vectors of indexed operation-types to be able + // to sort intersection points PER RING + mapped_vector_type mapped_vector; + + detail::overlay::create_map<indexed_turn_operation>(turn_points, mapped_vector); + + + // No const-iterator; contents of mapped copy is temporary, + // and changed by enrich + for (typename mapped_vector_type::iterator mit + = mapped_vector.begin(); + mit != mapped_vector.end(); + ++mit) + { +#ifdef BOOST_GEOMETRY_DEBUG_ENRICH + std::cout << "ENRICH-sort Ring " + << mit->first << std::endl; +#endif + detail::overlay::enrich_sort<indexed_turn_operation, Reverse1, Reverse2>(mit->second, turn_points, for_operation, + geometry1, geometry2, strategy); + } + + for (typename mapped_vector_type::iterator mit + = mapped_vector.begin(); + mit != mapped_vector.end(); + ++mit) + { +#ifdef BOOST_GEOMETRY_DEBUG_ENRICH + std::cout << "ENRICH-discard Ring " + << mit->first << std::endl; +#endif + detail::overlay::enrich_discard<indexed_turn_operation>(mit->second, turn_points); + } + + for (typename mapped_vector_type::iterator mit + = mapped_vector.begin(); + mit != mapped_vector.end(); + ++mit) + { +#ifdef BOOST_GEOMETRY_DEBUG_ENRICH + std::cout << "ENRICH-assign Ring " + << mit->first << std::endl; +#endif + detail::overlay::enrich_assign<indexed_turn_operation>(mit->second, turn_points, for_operation, + geometry1, geometry2, strategy); + } + +#ifdef BOOST_GEOMETRY_DEBUG_ENRICH + //detail::overlay::check_graph(turn_points, for_operation); +#endif + +} + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ENRICH_HPP diff --git a/boost/geometry/algorithms/detail/overlay/enrichment_info.hpp b/boost/geometry/algorithms/detail/overlay/enrichment_info.hpp new file mode 100644 index 0000000000..8c8ed96189 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/enrichment_info.hpp @@ -0,0 +1,76 @@ +// 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_ENRICHMENT_INFO_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ENRICHMENT_INFO_HPP + + +#include <boost/geometry/strategies/distance.hpp> + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + + +/*! +\brief Keeps info to enrich intersection info (per source) +\details Class to keep information necessary for traversal phase (a phase + of the overlay process). The information is gathered during the + enrichment phase + */ +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; + + // 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 +}; + + +}} // namespace detail::overlay +#endif //DOXYGEN_NO_DETAIL + + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ENRICHMENT_INFO_HPP diff --git a/boost/geometry/algorithms/detail/overlay/follow.hpp b/boost/geometry/algorithms/detail/overlay/follow.hpp new file mode 100644 index 0000000000..087092a5f2 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/follow.hpp @@ -0,0 +1,416 @@ +// 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_FOLLOW_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_FOLLOW_HPP + +#include <cstddef> + +#include <boost/range.hpp> +#include <boost/mpl/assert.hpp> + +#include <boost/geometry/algorithms/detail/point_on_border.hpp> +#include <boost/geometry/algorithms/detail/overlay/append_no_duplicates.hpp> +#include <boost/geometry/algorithms/detail/overlay/copy_segments.hpp> +#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp> + +#include <boost/geometry/algorithms/covered_by.hpp> + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +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) +{ + // (Blocked means: blocked for polygon/polygon intersection, because + // they are reversed. But for polygon/line it is similar to continue) + return op.operation == operation_intersection + || op.operation == operation_continue + || op.operation == operation_blocked + ; +} + +template +< + typename Turn, + typename Operation, + typename LineString, + typename Polygon +> +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 + 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); + + return geometry::covered_by(point_in_between, polygon); +} + + +template +< + typename Turn, + typename Operation, + typename LineString, + typename Polygon +> +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 + || turn.method == method_crosses + || (first && last_covered_by(turn, op, linestring, polygon)) + ; + } + return false; +} + + +template +< + typename Turn, + typename Operation, + typename LineString, + typename Polygon +> +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 + // so stay out of this time consuming "covered_by" + return false; + } + + if (is_entering(turn, op)) + { + return entered || (first && last_covered_by(turn, op, linestring, polygon)); + } + + return false; +} + +template +< + typename Turn, + typename Operation, + typename Linestring, + typename Polygon +> +static inline bool was_entered(Turn const& turn, Operation const& op, bool first, + Linestring const& linestring, Polygon const& polygon) +{ + if (first && (turn.method == method_collinear || turn.method == method_equal)) + { + return last_covered_by(turn, op, linestring, polygon); + } + return false; +} + + +// Template specialization structure to call the right actions for the right type +template<overlay_type OverlayType> +struct action_selector +{ + // If you get here the overlay type is not intersection or difference + // BOOST_MPL_ASSERT(false); +}; + +// Specialization for intersection, containing the implementation +template<> +struct action_selector<overlay_intersection> +{ + template + < + typename OutputIterator, + typename LineStringOut, + typename LineString, + typename Point, + typename Operation + > + static inline void enter(LineStringOut& current_piece, + LineString const& , + segment_identifier& segment_id, + int , Point const& point, + Operation const& operation, OutputIterator& ) + { + // On enter, append the intersection point and remember starting point + detail::overlay::append_no_duplicates(current_piece, point); + segment_id = operation.seg_id; + } + + template + < + typename OutputIterator, + typename LineStringOut, + typename LineString, + typename Point, + typename Operation + > + static inline void leave(LineStringOut& current_piece, + LineString const& linestring, + segment_identifier& segment_id, + int index, Point const& point, + Operation const& , 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::overlay::append_no_duplicates(current_piece, point); + if (current_piece.size() > 1) + { + *out++ = current_piece; + } + current_piece.clear(); + } + + static inline bool is_entered(bool entered) + { + return entered; + } + + template <typename Point, typename Geometry> + static inline bool included(Point const& point, Geometry const& geometry) + { + return geometry::covered_by(point, geometry); + } + +}; + +// Specialization for difference, which reverses these actions +template<> +struct action_selector<overlay_difference> +{ + typedef action_selector<overlay_intersection> normal_action; + + template + < + typename OutputIterator, + typename LineStringOut, + typename LineString, + typename Point, + typename Operation + > + static inline void enter(LineStringOut& current_piece, + LineString const& linestring, + segment_identifier& segment_id, + int index, Point const& point, + Operation const& operation, OutputIterator& out) + { + normal_action::leave(current_piece, linestring, segment_id, index, + point, operation, out); + } + + template + < + typename OutputIterator, + typename LineStringOut, + typename LineString, + typename Point, + typename Operation + > + static inline void leave(LineStringOut& current_piece, + LineString const& linestring, + segment_identifier& segment_id, + int index, Point const& point, + Operation const& operation, OutputIterator& out) + { + normal_action::enter(current_piece, linestring, segment_id, index, + point, operation, out); + } + + static inline bool is_entered(bool entered) + { + return ! normal_action::is_entered(entered); + } + + template <typename Point, typename Geometry> + static inline bool included(Point const& point, Geometry const& geometry) + { + return ! normal_action::included(point, geometry); + } + +}; + +} + +/*! +\brief Follows a linestring from intersection point to intersection point, outputting which + is inside, or outside, a ring or polygon +\ingroup overlay + */ +template +< + typename LineStringOut, + typename LineString, + typename Polygon, + overlay_type OverlayType +> +class follow +{ + + template<typename Turn> + struct sort_on_segment + { + // In case of turn point at the same location, we want to have continue/blocked LAST + // because that should be followed (intersection) or skipped (difference). + // By chance the enumeration is ordered like that but we keep the safe way here. + inline int operation_order(Turn const& turn) const + { + operation_type const& operation = turn.operations[0].operation; + switch(operation) + { + case operation_none : return 0; + case operation_union : return 1; + case operation_intersection : return 2; + case operation_blocked : return 3; + case operation_continue : return 4; + } + return -1; + }; + + inline bool use_operation(Turn const& left, Turn const& right) const + { + // 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) + ? use_operation(left, right) + : left.operations[0].enriched.distance < right.operations[0].enriched.distance + ; + } + + inline bool operator()(Turn const& left, Turn const& right) const + { + segment_identifier const& sl = left.operations[0].seg_id; + segment_identifier const& sr = right.operations[0].seg_id; + + return sl == sr + ? use_distance(left, right) + : sl < sr + ; + + } + }; + + + +public : + + template <typename Point, typename Geometry> + static inline bool included(Point const& point, Geometry const& geometry) + { + return following::action_selector<OverlayType>::included(point, geometry); + } + + template<typename Turns, typename OutputIterator> + static inline OutputIterator apply(LineString const& linestring, Polygon const& polygon, + detail::overlay::operation_type , // TODO: this parameter might be redundant + Turns& turns, OutputIterator out) + { + typedef typename boost::range_iterator<Turns>::type turn_iterator; + typedef typename boost::range_value<Turns>::type turn_type; + typedef typename boost::range_iterator + < + typename turn_type::container_type + >::type turn_operation_iterator_type; + + typedef following::action_selector<OverlayType> action; + + // Sort intersection points on segments-along-linestring, and distance + // (like in enrich is done for poly/poly) + std::sort(boost::begin(turns), boost::end(turns), sort_on_segment<turn_type>()); + + LineStringOut current_piece; + geometry::segment_identifier current_segment_id(0, -1, -1, -1); + + // Iterate through all intersection points (they are ordered along the line) + bool entered = false; + bool first = true; + for (turn_iterator it = boost::begin(turns); it != boost::end(turns); ++it) + { + turn_operation_iterator_type iit = boost::begin(it->operations); + + if (following::was_entered(*it, *iit, first, linestring, polygon)) + { + debug_traverse(*it, *iit, "-> Was entered"); + entered = true; + } + + if (following::is_staying_inside(*it, *iit, entered, first, linestring, polygon)) + { + debug_traverse(*it, *iit, "-> Staying inside"); + + entered = true; + } + else if (following::is_entering(*it, *iit)) + { + debug_traverse(*it, *iit, "-> Entering"); + + entered = true; + action::enter(current_piece, linestring, current_segment_id, iit->seg_id.segment_index, it->point, *iit, 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); + } + first = false; + } + + if (action::is_entered(entered)) + { + geometry::copy_segments<false>(linestring, current_segment_id, + boost::size(linestring) - 1, + current_piece); + } + + // Output the last one, if applicable + if (current_piece.size() > 1) + { + *out++ = current_piece; + } + return out; + } + +}; + + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_FOLLOW_HPP diff --git a/boost/geometry/algorithms/detail/overlay/get_intersection_points.hpp b/boost/geometry/algorithms/detail/overlay/get_intersection_points.hpp new file mode 100644 index 0000000000..019c3ba3f9 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/get_intersection_points.hpp @@ -0,0 +1,146 @@ +// 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_GET_INTERSECTION_POINTS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_INTERSECTION_POINTS_HPP + + +#include <cstddef> + +#include <boost/geometry/algorithms/convert.hpp> +#include <boost/geometry/algorithms/detail/overlay/get_turns.hpp> + +#include <boost/geometry/geometries/segment.hpp> + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace get_intersection_points +{ + + +template +< + typename Point1, + typename Point2, + typename TurnInfo +> +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> + static inline OutputIterator apply( + Point1 const& pi, Point1 const& pj, Point1 const& pk, + Point2 const& qi, Point2 const& qj, Point2 const& qk, + TurnInfo const& , + 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); + + // + typename strategy::return_type result = strategy::apply(p1, q1); + + for (std::size_t i = 0; i < result.template get<0>().count; i++) + { + + TurnInfo tp; + geometry::convert(result.template get<0>().intersections[i], tp.point); + *out++ = tp; + } + + return out; + } +}; + +}} // namespace detail::get_intersection_points +#endif // DOXYGEN_NO_DETAIL + + + + +template +< + typename Geometry1, + typename Geometry2, + typename Turns +> +inline void get_intersection_points(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Turns& turns) +{ + concept::check_concepts_and_equal_dimensions<Geometry1 const, Geometry2 const>(); + + typedef detail::get_intersection_points::get_turn_without_info + < + typename point_type<Geometry1>::type, + typename point_type<Geometry2>::type, + 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 + < + reverse_dispatch<Geometry1, Geometry2>::type::value, + dispatch::get_turns_reversed + < + typename tag<Geometry1>::type, + typename tag<Geometry2>::type, + Geometry1, Geometry2, + false, false, + Turns, TurnPolicy, + //segment_intersection_strategy_type, + detail::get_turns::no_interrupt_policy + >, + dispatch::get_turns + < + typename tag<Geometry1>::type, + typename tag<Geometry2>::type, + Geometry1, Geometry2, + false, false, + Turns, TurnPolicy, + //segment_intersection_strategy_type, + detail::get_turns::no_interrupt_policy + > + >::type::apply( + 0, geometry1, + 1, geometry2, + turns, interrupt_policy); +} + + + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_INTERSECTION_POINTS_HPP diff --git a/boost/geometry/algorithms/detail/overlay/get_relative_order.hpp b/boost/geometry/algorithms/detail/overlay/get_relative_order.hpp new file mode 100644 index 0000000000..522ef68382 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/get_relative_order.hpp @@ -0,0 +1,108 @@ +// 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_GET_RELATIVE_ORDER_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_RELATIVE_ORDER_HPP + + +#include <boost/geometry/algorithms/distance.hpp> + +#include <boost/geometry/strategies/intersection.hpp> + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + + +/*! + \brief Get relative order + \details Can indicate which of two segments R and S, + both crossing a common segment P, comes first. + If the two segments cross P very close (e.g. in a spike), + the distance between the intersection points can be zero, + but we still need to know which comes first. + Therefore, it is useful that using sides we are able to discover this. + */ +template <typename Point1> +struct get_relative_order +{ + typedef strategy_intersection + < + typename cs_tag<Point1>::type, + Point1, + Point1, + Point1 + > si; + + typedef typename si::side_strategy_type strategy; + + template <typename Point> + static inline int value_via_product(Point const& ti, Point const& tj, + Point const& ui, Point const& uj, int factor) + { + int const side_ti_u = strategy::apply(ti, tj, ui); + int const side_tj_u = strategy::apply(ti, tj, uj); + +#ifdef BOOST_GEOMETRY_DEBUG_RELATIVE_ORDER + std::cout << (factor == 1 ? " r//s " : " s//r ") + << side_ti_u << " / " << side_tj_u; +#endif + + return side_ti_u * side_tj_u >= 0 + ? factor * (side_ti_u != 0 ? side_ti_u : side_tj_u) + : 0; + } + + + static inline int apply( + Point1 const& pi, Point1 const& pj, + Point1 const& ri, Point1 const& rj, + Point1 const& si, Point1 const& sj) + { + int const side_ri_p = strategy::apply(pi, pj, ri); + int const side_si_p = strategy::apply(pi, pj, si); + +#ifdef BOOST_GEOMETRY_DEBUG_RELATIVE_ORDER + int const side_rj_p = strategy::apply(pi, pj, rj); + int const side_sj_p = strategy::apply(pi, pj, sj); + std::cout << "r//p: " << side_ri_p << " / " << side_rj_p; + std::cout << " s//p: " << side_si_p << " / " << side_sj_p; +#endif + + int value = value_via_product(si, sj, ri, rj, 1); + if (value == 0) + { + value = value_via_product(ri, rj, si, sj, -1); + } + + int const order = side_ri_p * side_ri_p * side_si_p * value; + +#ifdef BOOST_GEOMETRY_DEBUG_RELATIVE_ORDER + std::cout + << " o: " << order + << std::endl << std::endl; +#endif + + return order; + } +}; + + +}} // namespace detail::overlay +#endif //DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_RELATIVE_ORDER_HPP diff --git a/boost/geometry/algorithms/detail/overlay/get_ring.hpp b/boost/geometry/algorithms/detail/overlay/get_ring.hpp new file mode 100644 index 0000000000..ad47665500 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/get_ring.hpp @@ -0,0 +1,102 @@ +// 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_GET_RING_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_RING_HPP + + +#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/algorithms/detail/ring_identifier.hpp> + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + + +template<typename Tag> +struct get_ring +{}; + +// A container 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) + { + return container[id.multi_index]; + } +}; + + + + +template<> +struct get_ring<ring_tag> +{ + template<typename Ring> + static inline Ring const& apply(ring_identifier const& , Ring const& ring) + { + return ring; + } +}; + + +template<> +struct get_ring<box_tag> +{ + template<typename Box> + static inline Box const& apply(ring_identifier const& , + Box const& box) + { + return box; + } +}; + + +template<> +struct get_ring<polygon_tag> +{ + template<typename Polygon> + static inline typename ring_return_type<Polygon const>::type const apply( + ring_identifier const& id, + Polygon const& polygon) + { + BOOST_ASSERT + ( + id.ring_index >= -1 + && id.ring_index < boost::size(interior_rings(polygon)) + ); + return id.ring_index < 0 + ? exterior_ring(polygon) + : interior_rings(polygon)[id.ring_index]; + } +}; + + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_RING_HPP diff --git a/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp b/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp new file mode 100644 index 0000000000..663d70d9af --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp @@ -0,0 +1,956 @@ +// 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_GET_TURN_INFO_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_HPP + + +#include <boost/assert.hpp> +#include <boost/geometry/core/access.hpp> +#include <boost/geometry/strategies/intersection.hpp> + +#include <boost/geometry/algorithms/convert.hpp> +#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp> + +#include <boost/geometry/geometries/segment.hpp> + + +namespace boost { namespace geometry +{ + +#if ! defined(BOOST_GEOMETRY_OVERLAY_NO_THROW) +class turn_info_exception : public geometry::exception +{ + std::string message; +public: + + // NOTE: "char" will be replaced by enum in future version + inline turn_info_exception(char const method) + { + message = "Boost.Geometry Turn exception: "; + message += method; + } + + virtual ~turn_info_exception() throw() + {} + + virtual char const* what() const throw() + { + return message.c_str(); + } +}; +#endif + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + + +struct base_turn_handler +{ + // Returns true if both sides are opposite + static inline bool opposite(int side1, int side2) + { + // We cannot state side1 == -side2, because 0 == -0 + // So either side1*side2==-1 or side1==-side2 && side1 != 0 + return side1 * side2 == -1; + } + + // Same side of a segment (not being 0) + static inline bool same(int side1, int side2) + { + return side1 * side2 == 1; + } + + // Both continue + template <typename TurnInfo> + static inline void both(TurnInfo& ti, operation_type const op) + { + ti.operations[0].operation = op; + ti.operations[1].operation = op; + } + + // If condition, first union/second intersection, else vice versa + template <typename TurnInfo> + static inline void ui_else_iu(bool condition, TurnInfo& ti) + { + ti.operations[0].operation = condition + ? operation_union : operation_intersection; + ti.operations[1].operation = condition + ? operation_intersection : operation_union; + } + + // If condition, both union, else both intersection + template <typename TurnInfo> + static inline void uu_else_ii(bool condition, TurnInfo& ti) + { + both(ti, condition ? operation_union : operation_intersection); + } +}; + + +template +< + typename TurnInfo, + typename SideStrategy +> +struct touch_interior : public base_turn_handler +{ + // Index: 0, P is the interior, Q is touching and vice versa + template + < + int Index, + typename Point1, + typename Point2, + typename IntersectionInfo, + typename DirInfo + > + static inline void apply( + Point1 const& pi, Point1 const& pj, Point1 const& , + Point2 const& qi, Point2 const& qj, Point2 const& qk, + TurnInfo& ti, + IntersectionInfo const& intersection_info, + DirInfo const& dir_info) + { + ti.method = method_touch_interior; + geometry::convert(intersection_info.intersections[0], ti.point); + + // Both segments of q touch segment p somewhere in its interior + // 1) We know: if q comes from LEFT or RIGHT + // (i.e. dir_info.sides.get<Index,0>() == 1 or -1) + // 2) Important is: if q_k goes to LEFT, RIGHT, COLLINEAR + // and, if LEFT/COLL, if it is lying LEFT or RIGHT w.r.t. q_i + + static int const index_p = Index; + 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); + + if (side_qi_p == -side_qk_p) + { + // Q crosses P from left->right or from right->left (test "ML1") + // Union: folow P (left->right) or Q (right->left) + // Intersection: other turn + int index = side_qk_p == -1 ? index_p : index_q; + ti.operations[index].operation = operation_union; + ti.operations[1 - index].operation = operation_intersection; + return; + } + + int const side_qk_q = SideStrategy::apply(qi, qj, qk); + + if (side_qi_p == -1 && side_qk_p == -1 && side_qk_q == 1) + { + // Q turns left on the right side of P (test "MR3") + // Both directions for "intersection" + both(ti, operation_intersection); + } + else if (side_qi_p == 1 && side_qk_p == 1 && side_qk_q == -1) + { + // Q turns right on the left side of P (test "ML3") + // Union: take both operation + // Intersection: skip + both(ti, operation_union); + } + else if (side_qi_p == side_qk_p && side_qi_p == side_qk_q) + { + // Q turns left on the left side of P (test "ML2") + // or Q turns right on the right side of P (test "MR2") + // Union: take left turn (Q if Q turns left, P if Q turns right) + // Intersection: other turn + int index = side_qk_q == 1 ? index_q : index_p; + ti.operations[index].operation = operation_union; + ti.operations[1 - index].operation = operation_intersection; + } + else if (side_qk_p == 0) + { + // Q intersects on interior of P and continues collinearly + if (side_qk_q == side_qi_p) + { + // Collinearly in the same direction + // (Q comes from left of P and turns left, + // OR Q comes from right of P and turns right) + // Omit intersection point. + // Union: just continue + // Intersection: just continue + both(ti, operation_continue); + } + else + { + // Opposite direction, which is never travelled. + // If Q turns left, P continues for intersection + // If Q turns right, P continues for union + ti.operations[Index].operation = side_qk_q == 1 + ? operation_intersection + : operation_union; + ti.operations[1 - Index].operation = operation_blocked; + } + } + else + { + // Should not occur! + ti.method = method_error; + } + } +}; + + +template +< + typename TurnInfo, + typename SideStrategy +> +struct touch : public base_turn_handler +{ + static inline bool between(int side1, int side2, int turn) + { + return side1 == side2 && ! opposite(side1, turn); + } + + /*static inline void block_second(bool block, TurnInfo& ti) + { + if (block) + { + ti.operations[1].operation = operation_blocked; + } + }*/ + + + template + < + typename Point1, + typename Point2, + typename IntersectionInfo, + typename DirInfo + > + static inline void apply( + Point1 const& pi, Point1 const& pj, Point1 const& pk, + Point2 const& qi, Point2 const& qj, Point2 const& qk, + TurnInfo& ti, + IntersectionInfo const& intersection_info, + DirInfo const& dir_info) + { + ti.method = method_touch; + geometry::convert(intersection_info.intersections[0], ti.point); + + int const side_qi_p1 = dir_info.sides.template get<1, 0>(); + int const side_qk_p1 = SideStrategy::apply(pi, pj, qk); + + + // 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 q_turns_left = side_qk_q == 1; + bool const block_q = side_qk_p1 == 0 + && ! same(side_qi_p1, side_qk_q); + + // If Pk at same side as Qi/Qk + // (the "or" is for collinear case) + // or Q is fully collinear && P turns not to left + if (side_pk_p == side_qi_p1 + || side_pk_p == side_qk_p1 + || (side_qi_p1 == 0 && side_qk_p1 == 0 && side_pk_p != -1) + ) + { + // Collinear -> lines join, continue + // (#BRL2) + if (side_pk_q2 == 0 && ! block_q) + { + both(ti, operation_continue); + return; + } + + int const side_pk_q1 = SideStrategy::apply(qi, qj, pk); + + + // Collinear opposite case -> block P + // (#BRL4, #BLR8) + if (side_pk_q1 == 0) + { + ti.operations[0].operation = operation_blocked; + // Q turns right -> union (both independant), + // Q turns left -> intersection + ti.operations[1].operation = block_q ? operation_blocked + : q_turns_left ? operation_intersection + : operation_union; + return; + } + + // Pk between Qi and Qk + // (#BRL3, #BRL7) + if (between(side_pk_q1, side_pk_q2, side_qk_q)) + { + ui_else_iu(q_turns_left, ti); + if (block_q) + { + ti.operations[1].operation = operation_blocked; + } + //block_second(block_q, ti); + return; + } + + // Pk between Qk and P, so left of Qk (if Q turns right) and vv + // (#BRL1) + if (side_pk_q2 == -side_qk_q) + { + ui_else_iu(! q_turns_left, ti); + return; + } + + // + // (#BRL5, #BRL9) + if (side_pk_q1 == -side_qk_q) + { + uu_else_ii(! q_turns_left, ti); + if (block_q) + { + ti.operations[1].operation = operation_blocked; + } + //block_second(block_q, ti); + return; + } + } + 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; + ti.operations[1].operation = block_q + ? operation_blocked + : side_qi_p1 == 1 || side_qk_p1 == 1 + ? operation_union + : operation_intersection; + + return; + } + } + else + { + // From left to right or from right to left + int const side_pk_p = SideStrategy::apply(pi, pj, pk); + 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); + + // Collinear opposite case -> block P + if (side_pk_q1 == 0) + { + ti.operations[0].operation = operation_blocked; + ti.operations[1].operation = right_to_left + ? operation_union : operation_intersection; + return; + } + + if (side_pk_q1 == side_qk_p1) + { + uu_else_ii(right_to_left, ti); + return; + } + } + + // 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); + + // Collinear case -> lines join, continue + if (side_pk_q2 == 0) + { + both(ti, operation_continue); + return; + } + if (side_pk_q2 == side_qk_p1) + { + ui_else_iu(right_to_left, ti); + return; + } + } + // otherwise (3) + ui_else_iu(! right_to_left, ti); + return; + } + +#ifdef BOOST_GEOMETRY_DEBUG_GET_TURNS + // Normally a robustness issue. + // TODO: more research if still occuring + std::cout << "Not yet handled" << std::endl + << "pi " << get<0>(pi) << " , " << get<1>(pi) + << " pj " << get<0>(pj) << " , " << get<1>(pj) + << " pk " << get<0>(pk) << " , " << get<1>(pk) + << std::endl + << "qi " << get<0>(qi) << " , " << get<1>(qi) + << " qj " << get<0>(qj) << " , " << get<1>(qj) + << " qk " << get<0>(qk) << " , " << get<1>(qk) + << std::endl; +#endif + + } +}; + + +template +< + typename TurnInfo, + typename SideStrategy +> +struct equal : public base_turn_handler +{ + template + < + typename Point1, + typename Point2, + typename IntersectionInfo, + typename DirInfo + > + static inline void apply( + Point1 const& pi, Point1 const& pj, Point1 const& pk, + Point2 const& , Point2 const& qj, Point2 const& qk, + TurnInfo& ti, + IntersectionInfo const& intersection_info, + DirInfo const& ) + { + ti.method = method_equal; + // Copy the SECOND intersection point + geometry::convert(intersection_info.intersections[1], ti.point); + + 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 + // The second condition checks if they do not continue + // oppositely + if (side_pk_q2 == 0 && side_pk_p == side_qk_p) + { + both(ti, operation_continue); + return; + } + + + // 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); + } + else + { + // They turn opposite sides. If p turns left (or collinear), + // p: union, q: intersection + ui_else_iu(side_pk_p != -1, ti); + } + } +}; + + +template +< + typename TurnInfo, + typename SideStrategy +> +struct collinear : public base_turn_handler +{ + /* + arrival P pk//p1 qk//q1 product* case result + 1 1 1 CLL1 ui + -1 1 -1 CLL2 iu + 1 1 1 CLR1 ui + -1 -1 1 CLR2 ui + + 1 -1 -1 CRL1 iu + -1 1 -1 CRL2 iu + 1 -1 -1 CRR1 iu + -1 -1 1 CRR2 ui + + 1 0 0 CC1 cc + -1 0 0 CC2 cc + + *product = arrival * (pk//p1 or qk//q1) + + Stated otherwise: + - if P arrives: look at turn P + - if Q arrives: look at turn Q + - if P arrives and P turns left: union for P + - if P arrives and P turns right: intersection for P + - if Q arrives and Q turns left: union for Q (=intersection for P) + - if Q arrives and Q turns right: intersection for Q (=union for P) + */ + template + < + typename Point1, + typename Point2, + typename IntersectionInfo, + typename DirInfo + > + static inline void apply( + Point1 const& pi, Point1 const& pj, Point1 const& pk, + Point2 const& qi, Point2 const& qj, Point2 const& qk, + TurnInfo& ti, + IntersectionInfo const& intersection_info, + DirInfo const& dir_info) + { + ti.method = method_collinear; + geometry::convert(intersection_info.intersections[1], ti.point); + + int const arrival = dir_info.arrival[0]; + // Should not be 0, this is checked before + BOOST_ASSERT(arrival != 0); + + // If p arrives, use p, else use q + int const side_p_or_q = arrival == 1 + ? SideStrategy::apply(pi, pj, pk) + : SideStrategy::apply(qi, qj, qk) + ; + + // See comments above, + // resulting in a strange sort of mathematic rule here: + // The arrival-info multiplied by the relevant side + // delivers a consistent result. + + int const product = arrival * side_p_or_q; + + if(product == 0) + { + both(ti, operation_continue); + } + else + { + ui_else_iu(product == 1, ti); + } + } +}; + +template +< + typename TurnInfo, + typename SideStrategy, + typename AssignPolicy +> +struct collinear_opposite : public base_turn_handler +{ +private : + /* + arrival P arrival Q pk//p1 qk//q1 case result2 result + -------------------------------------------------------------- + 1 1 1 -1 CLO1 ix xu + 1 1 1 0 CLO2 ix (xx) + 1 1 1 1 CLO3 ix xi + + 1 1 0 -1 CCO1 (xx) xu + 1 1 0 0 CCO2 (xx) (xx) + 1 1 0 1 CCO3 (xx) xi + + 1 1 -1 -1 CRO1 ux xu + 1 1 -1 0 CRO2 ux (xx) + 1 1 -1 1 CRO3 ux xi + + -1 1 -1 CXO1 xu + -1 1 0 CXO2 (xx) + -1 1 1 CXO3 xi + + 1 -1 1 CXO1 ix + 1 -1 0 CXO2 (xx) + 1 -1 -1 CXO3 ux + */ + + template + < + int Index, + typename Point, + typename IntersectionInfo + > + static inline bool set_tp(Point const& ri, Point const& rj, Point const& rk, + TurnInfo& tp, IntersectionInfo const& intersection_info) + { + int const side_rk_r = SideStrategy::apply(ri, rj, rk); + switch(side_rk_r) + { + + case 1 : + // Turning left on opposite collinear: intersection + tp.operations[Index].operation = operation_intersection; + break; + case -1 : + // Turning right on opposite collinear: union + tp.operations[Index].operation = operation_union; + break; + case 0 : + // No turn on opposite collinear: block, do not traverse + // But this "xx" is ignored here, it is useless to include + // two operation blocked, so the whole point does not need + // to be generated. + // So return false to indicate nothing is to be done. + return false; + } + + // The other direction is always blocked when collinear opposite + tp.operations[1 - Index].operation = operation_blocked; + + // 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); + return true; + } + +public: + template + < + typename Point1, + typename Point2, + typename OutputIterator, + typename IntersectionInfo, + typename DirInfo + > + 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) + { + /* + std::cout << "arrivals: " + << dir_info.arrival[0] + << "/" << dir_info.arrival[1] + << std::endl; + */ + + TurnInfo tp = tp_model; + + tp.method = method_collinear; + + // If P arrives within Q, there is a turn dependant on P + if (dir_info.arrival[0] == 1 + && set_tp<0>(pi, pj, pk, tp, intersection_info)) + { + AssignPolicy::apply(tp, pi, qi); + *out++ = tp; + } + + // If Q arrives within P, there is a turn dependant on Q + if (dir_info.arrival[1] == 1 + && set_tp<1>(qi, qj, qk, tp, intersection_info)) + { + AssignPolicy::apply(tp, pi, qi); + *out++ = tp; + } + } +}; + + +template +< + typename TurnInfo, + typename SideStrategy +> +struct crosses : public base_turn_handler +{ + template + < + typename Point1, + typename Point2, + typename IntersectionInfo, + typename DirInfo + > + static inline void apply( + Point1 const& , Point1 const& , Point1 const& , + Point2 const& , Point2 const& , Point2 const& , + TurnInfo& ti, + IntersectionInfo const& intersection_info, + DirInfo const& dir_info) + { + ti.method = method_crosses; + geometry::convert(intersection_info.intersections[0], ti.point); + + // In all casees: + // If Q crosses P from left to right + // Union: take P + // Intersection: take Q + // Otherwise: vice versa + int const side_qi_p1 = dir_info.sides.template get<1, 0>(); + int const index = side_qi_p1 == 1 ? 0 : 1; + ti.operations[index].operation = operation_union; + ti.operations[1 - index].operation = operation_intersection; + } +}; + +template<typename TurnInfo> +struct only_convert +{ + template<typename IntersectionInfo> + static inline void apply(TurnInfo& ti, IntersectionInfo const& intersection_info) + { + ti.method = method_collinear; + geometry::convert(intersection_info.intersections[0], ti.point); + ti.operations[0].operation = operation_continue; + ti.operations[1].operation = operation_continue; + } +}; + +/*! +\brief Policy doing nothing +\details get_turn_info can have an optional policy to get/assign some + extra information. By default it does not, and this class + is that default. + */ +struct assign_null_policy +{ + static bool const include_no_turn = false; + static bool const include_degenerate = false; + + template <typename Point1, typename Point2, typename Info> + static inline void apply(Info& , Point1 const& , Point2 const& ) + {} + +}; + + +/*! + \brief Turn information: intersection point, method, and turn information + \details Information necessary for traversal phase (a phase + of the overlay process). The information is gathered during the + get_turns (segment intersection) phase. + \tparam Point1 point type of first segment + \tparam Point2 point type of second segment + \tparam TurnInfo type of class getting intersection and turn info + \tparam AssignPolicy policy to assign extra info, + e.g. to calculate distance from segment's first points + to intersection points. + It also defines if a certain class of points + (degenerate, non-turns) should be included. + */ +template +< + typename Point1, + typename Point2, + typename TurnInfo, + 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; + + + + template <typename OutputIterator> + static inline OutputIterator apply( + Point1 const& pi, Point1 const& pj, Point1 const& pk, + Point2 const& qi, Point2 const& qj, Point2 const& qk, + TurnInfo const& tp_model, + 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); + + typename strategy::return_type result = strategy::apply(p1, q1); + + char const method = result.template get<1>().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 + if (AssignPolicy::include_no_turn + && result.template get<0>().count > 0) + { + only_convert<TurnInfo>::apply(tp, + result.template get<0>()); + AssignPolicy::apply(tp, pi, qi); + *out++ = tp; + } + break; + + case 'd' : // disjoint: never do anything + break; + + case 'm' : + { + typedef touch_interior + < + TurnInfo, + typename si::side_strategy_type + > policy; + + // If Q (1) arrives (1) + if (result.template get<1>().arrival[1] == 1) + { + policy::template apply<0>(pi, pj, pk, qi, qj, qk, + tp, result.template get<0>(), result.template get<1>()); + } + else + { + // Swap p/q + policy::template apply<1>(qi, qj, qk, pi, pj, pk, + tp, result.template get<0>(), result.template get<1>()); + } + AssignPolicy::apply(tp, pi, qi); + *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); + *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); + *out++ = tp; + } + break; + case 'e': + { + if (! result.template get<1>().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); + *out++ = tp; + } + // If they ARE opposite, don't do anything. + } + break; + case 'c' : + { + // Collinear + if (! result.template get<1>().opposite) + { + + if (result.template get<1>().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>()); + + // 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>()); + } + + AssignPolicy::apply(tp, pi, qi); + *out++ = tp; + } + else + { + 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>()); + } + } + break; + case '0' : + { + // degenerate points + if (AssignPolicy::include_degenerate) + { + only_convert<TurnInfo>::apply(tp, result.template get<0>()); + AssignPolicy::apply(tp, pi, qi); + *out++ = tp; + } + } + break; + default : + { +#if ! defined(BOOST_GEOMETRY_OVERLAY_NO_THROW) + throw turn_info_exception(method); +#endif + } + break; + } + + return out; + } +}; + + +}} // namespace detail::overlay +#endif //DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_HPP diff --git a/boost/geometry/algorithms/detail/overlay/get_turns.hpp b/boost/geometry/algorithms/detail/overlay/get_turns.hpp new file mode 100644 index 0000000000..5740a8b7ba --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/get_turns.hpp @@ -0,0 +1,866 @@ +// 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_GET_TURNS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURNS_HPP + + +#include <cstddef> +#include <map> + +#include <boost/array.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/ring_type.hpp> + +#include <boost/geometry/geometries/concepts/check.hpp> + +#include <boost/geometry/util/math.hpp> +#include <boost/geometry/views/closeable_view.hpp> +#include <boost/geometry/views/reversible_view.hpp> +#include <boost/geometry/views/detail/range_type.hpp> + +#include <boost/geometry/geometries/box.hpp> +#include <boost/geometry/geometries/segment.hpp> + +#include <boost/geometry/iterators/ever_circling_iterator.hpp> + +#include <boost/geometry/strategies/cartesian/cart_intersect.hpp> +#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/partition.hpp> +#include <boost/geometry/algorithms/detail/overlay/get_turn_info.hpp> + +#include <boost/geometry/algorithms/detail/overlay/segment_identifier.hpp> + + +#include <boost/geometry/algorithms/detail/sections/range_by_section.hpp> + +#include <boost/geometry/algorithms/expand.hpp> +#include <boost/geometry/algorithms/detail/sections/sectionalize.hpp> + +#ifdef BOOST_GEOMETRY_DEBUG_INTERSECTION +# include <sstream> +# include <boost/geometry/io/dsv/write.hpp> +#endif + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace get_turns +{ + + +struct no_interrupt_policy +{ + static bool const enabled = false; + + template <typename Range> + static inline bool apply(Range const&) + { + return false; + } +}; + + +template +< + typename Geometry1, typename Geometry2, + bool Reverse1, bool Reverse2, + typename Section1, typename Section2, + typename Turns, + typename TurnPolicy, + typename InterruptPolicy +> +class get_turns_in_sections +{ + typedef typename closeable_view + < + typename range_type<Geometry1>::type const, + closure<Geometry1>::value + >::type cview_type1; + typedef typename closeable_view + < + typename range_type<Geometry2>::type const, + closure<Geometry2>::value + >::type cview_type2; + + typedef typename reversible_view + < + cview_type1 const, + Reverse1 ? iterate_reverse : iterate_forward + >::type view_type1; + typedef typename reversible_view + < + cview_type2 const, + Reverse2 ? iterate_reverse : iterate_forward + >::type view_type2; + + typedef typename boost::range_iterator + < + view_type1 const + >::type range1_iterator; + + typedef typename boost::range_iterator + < + view_type2 const + >::type range2_iterator; + + + template <typename Geometry, typename Section> + static inline bool neighbouring(Section const& section, + int index1, int index2) + { + // About n-2: + // (square: range_count=5, indices 0,1,2,3 + // -> 0-3 are adjacent, don't check on intersections) + // Also tested for open polygons, and/or duplicates + // 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); + return boost::is_same + < + typename tag_cast + < + typename geometry::point_type<Geometry1>::type, + areal_tag + >::type, + areal_tag + >::value + && index1 == 0 + && index2 >= n - 2 + ; + } + + +public : + // Returns true if terminated, false if interrupted + 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, + Turns& turns, + InterruptPolicy& interrupt_policy) + { + cview_type1 cview1(range_by_section(geometry1, sec1)); + cview_type2 cview2(range_by_section(geometry2, sec2)); + view_type1 view1(cview1); + view_type2 view2(cview2); + + range1_iterator begin_range_1 = boost::begin(view1); + range1_iterator end_range_1 = boost::end(view1); + + range2_iterator begin_range_2 = boost::begin(view2); + range2_iterator end_range_2 = boost::end(view2); + + int const dir1 = sec1.directions[0]; + int const dir2 = sec2.directions[0]; + int index1 = sec1.begin_index; + int ndi1 = sec1.non_duplicate_index; + + bool const same_source = + source_id1 == source_id2 + && sec1.ring_id.multi_index == sec2.ring_id.multi_index + && sec1.ring_id.ring_index == sec2.ring_id.ring_index; + + range1_iterator prev1, it1, end1; + + get_start_point_iterator(sec1, view1, prev1, it1, end1, + index1, ndi1, dir1, sec2.bounding_box); + + // We need a circular iterator because it might run through the closing point. + // One circle is actually enough but this one is just convenient. + ever_circling_iterator<range1_iterator> next1(begin_range_1, end_range_1, it1, true); + next1++; + + // Walk through section and stop if we exceed the other box + // section 2: [--------------] + // section 1: |----|---|---|---|---| + for (prev1 = it1++, next1++; + it1 != end1 && ! exceeding<0>(dir1, *prev1, sec2.bounding_box); + ++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); + + int index2 = sec2.begin_index; + int ndi2 = sec2.non_duplicate_index; + + range2_iterator prev2, it2, end2; + + get_start_point_iterator(sec2, view2, prev2, it2, end2, + index2, ndi2, dir2, sec1.bounding_box); + 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); + ++prev2, ++it2, ++index2, ++next2, ++ndi2) + { + bool skip = same_source; + if (skip) + { + // If sources are the same (possibly self-intersecting): + // skip if it is a neighbouring segment. + // (including first-last segment + // and two segments with one or more degenerate/duplicate + // (zero-length) segments in between) + + // Also skip if index1 < index2 to avoid getting all + // intersections twice (only do this on same source!) + + skip = (skip_larger && index1 >= index2) + || ndi2 == ndi1 + 1 + || neighbouring<Geometry1>(sec1, index1, index2) + ; + } + + if (! skip) + { + // 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); + + 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; + + std::size_t const size_before = boost::size(turns); + + TurnPolicy::apply(*prev1, *it1, *nd_next1, *prev2, *it2, *nd_next2, + ti, std::back_inserter(turns)); + + if (InterruptPolicy::enabled) + { + if (interrupt_policy.apply( + std::make_pair(boost::begin(turns) + size_before, + boost::end(turns)))) + { + return false; + } + } + } + } + } + return true; + } + + +private : + typedef typename geometry::point_type<Geometry1>::type point1_type; + typedef typename geometry::point_type<Geometry2>::type point2_type; + typedef typename model::referring_segment<point1_type const> segment1_type; + 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) + { + return (dir == 1 && get<Dim>(point) < get<min_corner, Dim>(box)) + || (dir == -1 && get<Dim>(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) + { + return (dir == 1 && get<Dim>(point) > get<max_corner, Dim>(box)) + || (dir == -1 && get<Dim>(point) < get<min_corner, Dim>(box)); + } + + template <typename Iterator, typename RangeIterator, typename Section> + static inline void advance_to_non_duplicate_next(Iterator& next, + RangeIterator const& it, Section const& section) + { + // 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. + // This needs an extra comparison (disjoint). + // (Note that within sections, non duplicate points are already asserted, + // by the sectionalize process). + + // 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) + && check++ < section.range_count) + { + next++; + } + } + + // It is NOT possible to have section-iterators here + // 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> + 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) + { + it = boost::begin(range) + section.begin_index; + end = boost::begin(range) + section.end_index + 1; + + // Mimic section-iterator: + // Skip to point such that section interects other box + prev = it++; + for(; it != end && preceding<0>(dir, *it, other_bounding_box); + prev = it++, index++, ndi++) + {} + // Go back one step because we want to start completely preceding + it = prev; + } +}; + +struct get_section_box +{ + template <typename Box, typename InputItem> + static inline void apply(Box& total, InputItem const& item) + { + geometry::expand(total, item.bounding_box); + } +}; + +struct ovelaps_section_box +{ + template <typename Box, typename InputItem> + static inline bool apply(Box const& box, InputItem const& item) + { + return ! detail::disjoint::disjoint_box_box(box, item.bounding_box); + } +}; + +template +< + typename Geometry1, typename Geometry2, + bool Reverse1, bool Reverse2, + typename Turns, + typename TurnPolicy, + typename InterruptPolicy +> +struct section_visitor +{ + int m_source_id1; + Geometry1 const& m_geometry1; + int m_source_id2; + Geometry2 const& m_geometry2; + Turns& m_turns; + InterruptPolicy& m_interrupt_policy; + + section_visitor(int id1, Geometry1 const& g1, + int id2, Geometry2 const& g2, + Turns& turns, InterruptPolicy& ip) + : m_source_id1(id1), m_geometry1(g1) + , m_source_id2(id2), m_geometry2(g2) + , m_turns(turns) + , m_interrupt_policy(ip) + {} + + template <typename Section> + inline bool apply(Section const& sec1, Section const& sec2) + { + if (! detail::disjoint::disjoint_box_box(sec1.bounding_box, sec2.bounding_box)) + { + return get_turns_in_sections + < + Geometry1, + Geometry2, + Reverse1, Reverse2, + Section, Section, + Turns, + TurnPolicy, + InterruptPolicy + >::apply( + m_source_id1, m_geometry1, sec1, + m_source_id2, m_geometry2, sec2, + false, + m_turns, m_interrupt_policy); + } + return true; + } + +}; + +template +< + typename Geometry1, typename Geometry2, + bool Reverse1, bool Reverse2, + typename Turns, + typename TurnPolicy, + typename InterruptPolicy +> +class get_turns_generic +{ + +public: + static inline void apply( + int source_id1, Geometry1 const& geometry1, + int source_id2, Geometry2 const& geometry2, + 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 typename geometry::sections<box_type, 2> sections_type; + + sections_type sec1, sec2; + + geometry::sectionalize<Reverse1>(geometry1, sec1, 0); + geometry::sectionalize<Reverse2>(geometry2, 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); + + geometry::partition + < + box_type, get_section_box, ovelaps_section_box + >::apply(sec1, sec2, visitor); + } +}; + + +// Get turns for a range with a box, following Cohen-Sutherland (cs) approach +template +< + typename Range, typename Box, + bool ReverseRange, bool ReverseBox, + typename Turns, + typename TurnPolicy, + typename InterruptPolicy +> +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; + + typedef typename closeable_view + < + Range const, + closure<Range>::value + >::type cview_type; + + typedef typename reversible_view + < + cview_type const, + ReverseRange ? iterate_reverse : iterate_forward + >::type view_type; + + typedef typename boost::range_iterator + < + view_type const + >::type iterator_type; + + + static inline void apply( + int source_id1, Range const& range, + int source_id2, Box const& box, + Turns& turns, + InterruptPolicy& , + int multi_index = -1, int ring_index = -1) + { + if (boost::size(range) <= 1) + { + return; + } + + boost::array<box_point_type,4> bp; + assign_box_corners_oriented<ReverseBox>(box, bp); + + cview_type cview(range); + view_type view(cview); + + iterator_type it = boost::begin(view); + + ever_circling_iterator<iterator_type> next( + boost::begin(view), boost::end(view), it, true); + next++; + next++; + + //bool first = true; + + //char previous_side[2] = {0, 0}; + + int index = 0; + + for (iterator_type prev = it++; + it != boost::end(view); + prev = it++, next++, index++) + { + segment_identifier seg_id(source_id1, + multi_index, ring_index, index); + + /*if (first) + { + previous_side[0] = get_side<0>(box, *prev); + previous_side[1] = get_side<1>(box, *prev); + } + + char current_side[2]; + current_side[0] = get_side<0>(box, *it); + current_side[1] = get_side<1>(box, *it); + + // There can NOT be intersections if + // 1) EITHER the two points are lying on one side of the box (! 0 && the same) + // 2) OR same in Y-direction + // 3) OR all points are inside the box (0) + if (! ( + (current_side[0] != 0 && current_side[0] == previous_side[0]) + || (current_side[1] != 0 && current_side[1] == previous_side[1]) + || (current_side[0] == 0 + && current_side[1] == 0 + && previous_side[0] == 0 + && previous_side[1] == 0) + ) + )*/ + if (true) + { + get_turns_with_box(seg_id, source_id2, + *prev, *it, *next, + bp[0], bp[1], bp[2], bp[3], + turns); + // Future performance enhancement: + // return if told by the interrupt policy + } + } + } + +private: + template<std::size_t Index, typename Point> + static inline int get_side(Box const& box, Point const& point) + { + // Inside -> 0 + // Outside -> -1 (left/below) or 1 (right/above) + // On border -> -2 (left/lower) or 2 (right/upper) + // The only purpose of the value is to not be the same, + // and to denote if it is inside (0) + + typename coordinate_type<Point>::type const& c = get<Index>(point); + typename coordinate_type<Box>::type const& left = get<min_corner, Index>(box); + typename coordinate_type<Box>::type const& right = get<max_corner, Index>(box); + + if (geometry::math::equals(c, left)) return -2; + else if (geometry::math::equals(c, right)) return 2; + else if (c < left) return -1; + else if (c > right) return 1; + else return 0; + } + + static inline void get_turns_with_box(segment_identifier const& seg_id, int source_id2, + // Points from a range: + point_type const& rp0, + point_type const& rp1, + point_type const& rp2, + // Points from the box + box_point_type const& bp0, + box_point_type const& bp1, + box_point_type const& bp2, + box_point_type const& bp3, + // Output + Turns& turns) + { + // 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)); + + 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)); + + 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)); + + 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)); + } + +}; + + +template +< + typename Polygon, typename Box, + bool Reverse, bool ReverseBox, + typename Turns, + typename TurnPolicy, + typename InterruptPolicy +> +struct get_turns_polygon_cs +{ + static inline void apply( + int source_id1, Polygon const& polygon, + int source_id2, Box const& box, + Turns& turns, InterruptPolicy& interrupt_policy, + int multi_index = -1) + { + typedef typename geometry::ring_type<Polygon>::type ring_type; + + typedef detail::get_turns::get_turns_cs + < + ring_type, Box, + Reverse, ReverseBox, + Turns, + TurnPolicy, + InterruptPolicy + > intersector_type; + + intersector_type::apply( + source_id1, geometry::exterior_ring(polygon), + source_id2, box, 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) + { + intersector_type::apply( + source_id1, *it, + source_id2, box, turns, interrupt_policy, + multi_index, i); + } + + } +}; + +}} // namespace detail::get_turns +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +// Because this is "detail" method, and most implementations will use "generic", +// we take the freedom to derive it from "generic". +template +< + typename GeometryTag1, typename GeometryTag2, + typename Geometry1, typename Geometry2, + bool Reverse1, bool Reverse2, + typename Turns, + typename TurnPolicy, + typename InterruptPolicy +> +struct get_turns + : detail::get_turns::get_turns_generic + < + Geometry1, Geometry2, + Reverse1, Reverse2, + Turns, + TurnPolicy, + InterruptPolicy + > +{}; + + +template +< + typename Polygon, typename Box, + bool ReversePolygon, bool ReverseBox, + typename Turns, + typename TurnPolicy, + typename InterruptPolicy +> +struct get_turns + < + polygon_tag, box_tag, + Polygon, Box, + ReversePolygon, ReverseBox, + Turns, + TurnPolicy, + InterruptPolicy + > : detail::get_turns::get_turns_polygon_cs + < + Polygon, Box, + ReversePolygon, ReverseBox, + Turns, TurnPolicy, InterruptPolicy + > +{}; + + +template +< + typename Ring, typename Box, + bool ReverseRing, bool ReverseBox, + typename Turns, + typename TurnPolicy, + typename InterruptPolicy +> +struct get_turns + < + ring_tag, box_tag, + Ring, Box, + ReverseRing, ReverseBox, + Turns, + TurnPolicy, + InterruptPolicy + > : detail::get_turns::get_turns_cs + < + Ring, Box, ReverseRing, ReverseBox, + Turns, TurnPolicy, InterruptPolicy + > + +{}; + + +template +< + typename GeometryTag1, typename GeometryTag2, + typename Geometry1, typename Geometry2, + bool Reverse1, bool Reverse2, + typename Turns, + typename TurnPolicy, + typename InterruptPolicy +> +struct get_turns_reversed +{ + static inline void apply( + int source_id1, Geometry1 const& g1, + int source_id2, Geometry2 const& g2, + 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); + } +}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + + +/*! +\brief \brief_calc2{turn points} +\ingroup overlay +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\tparam Turns type of turn-container (e.g. vector of "intersection/turn point"'s) +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\param turns container which will contain turn points +\param interrupt_policy policy determining if process is stopped + when intersection is found + */ +template +< + bool Reverse1, bool Reverse2, + typename AssignPolicy, + typename Geometry1, + typename Geometry2, + typename Turns, + typename InterruptPolicy +> +inline void get_turns(Geometry1 const& geometry1, + Geometry2 const& geometry2, + 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; + + boost::mpl::if_c + < + reverse_dispatch<Geometry1, Geometry2>::type::value, + dispatch::get_turns_reversed + < + typename tag<Geometry1>::type, + typename tag<Geometry2>::type, + Geometry1, Geometry2, + Reverse1, Reverse2, + Turns, TurnPolicy, + InterruptPolicy + >, + dispatch::get_turns + < + typename tag<Geometry1>::type, + typename tag<Geometry2>::type, + Geometry1, Geometry2, + Reverse1, Reverse2, + Turns, TurnPolicy, + InterruptPolicy + > + >::type::apply( + 0, geometry1, + 1, geometry2, + turns, interrupt_policy); +} + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURNS_HPP diff --git a/boost/geometry/algorithms/detail/overlay/handle_tangencies.hpp b/boost/geometry/algorithms/detail/overlay/handle_tangencies.hpp new file mode 100644 index 0000000000..1e878ca525 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/handle_tangencies.hpp @@ -0,0 +1,672 @@ +// 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_HANDLE_TANGENCIES_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_HANDLE_TANGENCIES_HPP + +#include <algorithm> + +#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/geometries/segment.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + + +template +< + typename TurnPoints, + typename Indexed, + typename Geometry1, typename Geometry2, + bool Reverse1, bool Reverse2, + typename Strategy +> +struct sort_in_cluster +{ + inline sort_in_cluster(TurnPoints const& turn_points + , Geometry1 const& geometry1 + , Geometry2 const& geometry2 + , Strategy const& strategy) + : m_turn_points(turn_points) + , m_geometry1(geometry1) + , m_geometry2(geometry2) + , m_strategy(strategy) + {} + +private : + + TurnPoints const& m_turn_points; + Geometry1 const& m_geometry1; + Geometry2 const& m_geometry2; + 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; + + // 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) + { + // Determine how p/r and p/s are located. + // One of them is coming from opposite direction. + + typedef strategy::intersection::relate_cartesian_segments + < + policies::relate::segments_intersection_points + < + segment_type, + segment_type, + segment_intersection_points<point_type> + > + > policy; + + 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); + + // Check on overlap + pr_overlap = pr.count == 2; + ps_overlap = ps.count == 2; + rs_overlap = rs.count == 2; + } + + +#ifdef BOOST_GEOMETRY_DEBUG_ENRICH + inline void debug_consider(int order, Indexed const& left, + Indexed const& right, std::string const& header, + bool skip = true, + std::string const& extra = "", bool ret = false + ) const + { + 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); + + bool prc = false, psc = false, rsc = false; + overlap_info(pi, pj, ri, rj, si, sj, prc, psc, rsc); + + int const side_ri_p = m_strategy.apply(pi, pj, ri); + int const side_rj_p = m_strategy.apply(pi, pj, rj); + int const side_si_p = m_strategy.apply(pi, pj, si); + int const side_sj_p = m_strategy.apply(pi, pj, sj); + 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 + 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; + + std::cout << " r//p: " << side_ri_p << " / " << side_rj_p << std::endl; + std::cout << " s//p: " << side_si_p << " / " << side_sj_p << std::endl; + std::cout << " s//r: " << side_si_r << " / " << side_sj_r << std::endl; +#endif + + std::cout << header + //<< " order: " << order + << " 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 + << " cnts: " << int(prc) << "," << int(psc) << "," << int(rsc) + //<< " idx: " << left.index << "/" << right.index + ; + + if (! extra.empty()) + { + std::cout << " " << extra << " " << (ret ? "true" : "false"); + } + std::cout << std::endl; + } +#else + inline void debug_consider(int, Indexed const& , + Indexed const& , std::string const& , + bool = true, + std::string const& = "", bool = false + ) const + {} +#endif + + + // ux/ux + inline bool consider_ux_ux(Indexed const& left, + Indexed const& right + , std::string const& // header + ) const + { + bool ret = left.index < right.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) + { + ret = true; + } + else if (left.subject.operation == operation_blocked + && right.subject.operation == operation_union) + { + ret = false; + } + else + { +#ifdef BOOST_GEOMETRY_DEBUG_ENRICH + std::cout << "ux/ux unhandled" << std::endl; +#endif + } + + //debug_consider(0, left, right, header, false, "-> return ", ret); + + return ret; + } + + inline bool consider_iu_ux(Indexed const& left, + Indexed const& right, + int order // 1: iu first, -1: ux first + , std::string const& // header + ) const + { + bool ret = false; + + 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) + { + ret = true; + } + else if (right.subject.operation == operation_union + && left.subject.operation == operation_blocked) + { + ret = false; + } + else if (left.subject.operation == operation_union) + { + ret = true; + } + else if (right.subject.operation == operation_union) + { + ret = false; + } + else + { +#ifdef BOOST_GEOMETRY_DEBUG_ENRICH + // this still happens in the traverse.cpp test + std::cout << " iu/ux unhandled" << std::endl; +#endif + ret = order == 1; + } + + //debug_consider(0, left, right, header, false, "-> return", ret); + return ret; + } + + inline bool consider_iu_ix(Indexed const& left, + Indexed const& right, + int order // 1: iu first, -1: ix first + , std::string const& // header + ) const + { + //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 + : order == 1; + } + + + inline bool consider_iu_iu(Indexed const& left, Indexed const& right, + std::string const& header) 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) + { + //debug_consider(0, left, right, header, false, "i,u", false); + return false; + } + 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); + + int const side_ri_p = m_strategy.apply(pi, pj, ri); + int const side_si_p = m_strategy.apply(pi, pj, si); + int const side_si_r = m_strategy.apply(ri, rj, si); + + // Both located at same side (#58, pie_21_7_21_0_3) + 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) + { + bool ret = side_si_r == 1; + //debug_consider(0, left, right, header, false, "same side", ret); + return ret; + } + } + + + // Coming from opposite sides (#59, #99) + if (side_ri_p * side_si_p == -1) + { + bool ret = false; + + { + ret = side_ri_p == 1; // #100 + debug_consider(0, left, right, header, false, "opp.", ret); + return ret; + } +#ifdef BOOST_GEOMETRY_DEBUG_ENRICH + std::cout << " iu/iu coming from opposite unhandled" << std::endl; +#endif + } + + // 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); + + // One coming from right (#83,#90) + // One coming from left (#90, #94, #95) + if (side_si_r != 0 && (side_ri_p != 0 || side_si_p != 0)) + { + bool ret = false; + + if (pr_ov || ps_ov) + { + int r = side_ri_p != 0 ? side_ri_p : side_si_p; + ret = r * side_si_r == 1; + } + else + { + ret = side_si_r == 1; + } + + debug_consider(0, left, right, header, false, "left or right", ret); + return ret; + } + + // All aligned (#92, #96) + if (side_ri_p == 0 && side_si_p == 0 && side_si_r == 0) + { + // One of them is coming from opposite direction. + + // Take the one NOT overlapping + bool ret = false; + bool found = false; + if (pr_ov && ! ps_ov) + { + ret = true; + found = true; + } + else if (!pr_ov && ps_ov) + { + ret = false; + found = true; + } + + debug_consider(0, left, right, header, false, "aligned", ret); + if (found) + { + return ret; + } + } + +#ifdef BOOST_GEOMETRY_DEBUG_ENRICH + std::cout << " iu/iu unhandled" << std::endl; + debug_consider(0, left, right, header, false, "unhandled", left.index < right.index); +#endif + return left.index < right.index; + } + + inline bool consider_ii(Indexed const& left, Indexed const& right, + std::string const& header) const + { + 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); + + int const side_ri_p = m_strategy.apply(pi, pj, ri); + int const side_si_p = m_strategy.apply(pi, pj, si); + + // Two other points are (mostly) lying both right of the considered segment + // Take the most left one + int const side_si_r = m_strategy.apply(ri, rj, si); + if (side_ri_p == -1 + && side_si_p == -1 + && side_si_r != 0) + { + bool const ret = side_si_r != 1; + return ret; + } + return left.index < right.index; + } + + +public : + inline bool operator()(Indexed const& left, Indexed const& right) const + { + bool const default_order = left.index < right.index; + + if ((m_turn_points[left.index].discarded || left.discarded) + && (m_turn_points[right.index].discarded || right.discarded)) + { + return default_order; + } + else if (m_turn_points[left.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) + { + // 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)) + { + // 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)) + { + // 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)) + { + 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)) + { + 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)) + { + 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)) + { + 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)) + { + 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)) + { + 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 + ) + { + // 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 + ) + { + return true; + } + + // 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; +#endif + + return default_order; + } +}; + + + +template +< + typename IndexType, + typename Iterator, + typename TurnPoints, + typename Geometry1, + typename Geometry2, + typename Strategy +> +inline void inspect_cluster(Iterator begin_cluster, Iterator end_cluster, + TurnPoints& turn_points, + operation_type , + Geometry1 const& , Geometry2 const& , + Strategy const& ) +{ + int count = 0; + + // Make an analysis about all occuring cases here. + 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; + if (first > second) + { + std::swap(first, second); + } + inspection[std::make_pair(first, second)]++; + count++; + } + + + bool keep_cc = false; + + // Decide about which is going to be discarded here. + if (inspection[std::make_pair(operation_union, operation_union)] == 1 + && inspection[std::make_pair(operation_continue, operation_continue)] == 1) + { + // In case of uu/cc, discard the uu, that indicates a tangency and + // inclusion would disturb the (e.g.) cc-cc-cc ordering + // NOTE: uu is now discarded anyhow. + keep_cc = true; + } + else if (count == 2 + && inspection[std::make_pair(operation_intersection, operation_intersection)] == 1 + && inspection[std::make_pair(operation_union, operation_intersection)] == 1) + { + // In case of ii/iu, discard the iu. The ii should always be visited, + // 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)) + { + it->discarded = true; + } + } + } + + // Discard any continue turn, unless it is the only thing left + // (necessary to avoid cc-only rings, all being discarded + // e.g. traversal case #75) + int nd_count= 0, cc_count = 0; + for (Iterator it = begin_cluster; it != end_cluster; ++it) + { + if (! it->discarded) + { + nd_count++; + if (turn_points[it->index].both(operation_continue)) + { + cc_count++; + } + } + } + + if (nd_count == cc_count) + { + keep_cc = true; + } + + if (! keep_cc) + { + for (Iterator it = begin_cluster; it != end_cluster; ++it) + { + if (turn_points[it->index].both(operation_continue)) + { + it->discarded = true; + } + } + } +} + + +template +< + typename IndexType, + bool Reverse1, bool Reverse2, + typename Iterator, + typename TurnPoints, + typename Geometry1, + typename Geometry2, + 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, + Strategy const& strategy) +{ + // First inspect and (possibly) discard rows + inspect_cluster<IndexType>(begin_cluster, end_cluster, turn_points, + for_operation, geometry1, geometry2, strategy); + + + // Then sort this range (discard rows will be ordered first and will be removed in enrich_assign) + std::sort(begin_cluster, end_cluster, + sort_in_cluster + < + TurnPoints, + IndexType, + Geometry1, Geometry2, + Reverse1, Reverse2, + Strategy + >(turn_points, geometry1, geometry2, strategy)); + + +#ifdef BOOST_GEOMETRY_DEBUG_ENRICH + 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 "; + + for (Iterator it = begin_cluster; it != end_cluster; ++it) + { + std::cout << " " << it->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 << 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 << std::endl << "->Discarded: "; + for (Iterator it = begin_cluster; it != end_cluster; ++it) + { + std::cout << " " << (it->discarded ? "true" : "false"); + } + 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) +#endif + +} + + +}} // namespace detail::overlay +#endif //DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_HANDLE_TANGENCIES_HPP diff --git a/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp b/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp new file mode 100644 index 0000000000..8bca790d74 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp @@ -0,0 +1,690 @@ +// 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_INTERSECTION_INSERT_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_INTERSECTION_INSERT_HPP + + +#include <cstddef> + +#include <boost/mpl/if.hpp> +#include <boost/mpl/assert.hpp> +#include <boost/range/metafunctions.hpp> + + +#include <boost/geometry/core/is_areal.hpp> +#include <boost/geometry/core/point_order.hpp> +#include <boost/geometry/core/reverse_dispatch.hpp> +#include <boost/geometry/geometries/concepts/check.hpp> +#include <boost/geometry/algorithms/convert.hpp> +#include <boost/geometry/algorithms/detail/point_on_border.hpp> +#include <boost/geometry/algorithms/detail/overlay/clip_linestring.hpp> +#include <boost/geometry/algorithms/detail/overlay/get_intersection_points.hpp> +#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/views/segment_view.hpp> + +#if defined(BOOST_GEOMETRY_DEBUG_FOLLOW) +#include <boost/foreach.hpp> +#endif + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace intersection +{ + +template +< + typename Segment1, typename Segment2, + typename OutputIterator, typename PointOut, + typename Strategy +> +struct intersection_segment_segment_point +{ + static inline OutputIterator apply(Segment1 const& segment1, + Segment2 const& segment2, OutputIterator out, + Strategy const& ) + { + typedef typename point_type<PointOut>::type point_type; + + // Get the intersection point (or two points) + segment_intersection_points<point_type> is + = strategy::intersection::relate_cartesian_segments + < + policies::relate::segments_intersection_points + < + Segment1, + Segment2, + segment_intersection_points<point_type> + > + >::apply(segment1, segment2); + + for (std::size_t i = 0; i < is.count; i++) + { + PointOut p; + geometry::convert(is.intersections[i], p); + *out++ = p; + } + return out; + } +}; + +template +< + typename Linestring1, typename Linestring2, + typename OutputIterator, typename PointOut, + typename Strategy +> +struct intersection_linestring_linestring_point +{ + static inline OutputIterator apply(Linestring1 const& linestring1, + Linestring2 const& linestring2, OutputIterator out, + Strategy const& ) + { + typedef typename point_type<PointOut>::type point_type; + + typedef detail::overlay::turn_info<point_type> turn_info; + std::deque<turn_info> turns; + + geometry::get_intersection_points(linestring1, linestring2, turns); + + for (typename boost::range_iterator<std::deque<turn_info> const>::type + it = boost::begin(turns); it != boost::end(turns); ++it) + { + PointOut p; + geometry::convert(it->point, p); + *out++ = p; + } + return out; + } +}; + +/*! +\brief Version of linestring with an areal feature (polygon or multipolygon) +*/ +template +< + typename LineString, typename Areal, + bool ReverseAreal, + typename OutputIterator, typename LineStringOut, + overlay_type OverlayType, + typename Strategy +> +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, + int index) + { + std::cout << index + << " at " << op.seg_id + << " meth: " << method_char(turn.method) + << " op: " << operation_char(op.operation) + << " vis: " << visited_char(op.visited) + << " of: " << operation_char(turn.operations[0].operation) + << operation_char(turn.operations[1].operation) + << " " << geometry::wkt(turn.point) + << std::endl; + } +#endif + + static inline OutputIterator apply(LineString const& linestring, Areal const& areal, + OutputIterator out, + Strategy const& ) + { + if (boost::size(linestring) == 0) + { + return out; + } + + typedef typename point_type<LineStringOut>::type point_type; + + typedef detail::overlay::traversal_turn_info<point_type> turn_info; + std::deque<turn_info> turns; + + detail::get_turns::no_interrupt_policy policy; + geometry::get_turns + < + false, + (OverlayType == overlay_intersection ? ReverseAreal : !ReverseAreal), + detail::overlay::calculate_distance_policy + >(linestring, areal, turns, policy); + + if (turns.empty()) + { + // No intersection points, it is either completely + // inside (interior + borders) + // or completely outside + + // Use border point (on a segment) to check this + // (because turn points might skip some cases) + point_type border_point; + if (! geometry::point_on_border(border_point, linestring, true)) + { + return out; + } + + + if (follower::included(border_point, areal)) + { + LineStringOut copy; + geometry::convert(linestring, copy); + *out++ = copy; + } + return out; + } + +#if defined(BOOST_GEOMETRY_DEBUG_FOLLOW) + int index = 0; + BOOST_FOREACH(turn_info const& turn, turns) + { + debug_follow(turn, turn.operations[0], index++); + } +#endif + + return follower::apply + ( + linestring, areal, + geometry::detail::overlay::operation_intersection, + turns, out + ); + } +}; + + +}} // namespace detail::intersection +#endif // DOXYGEN_NO_DETAIL + + + +#ifndef DOXYGEN_NO_DISPATCH +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 +> +struct intersection_insert +{ + BOOST_MPL_ASSERT_MSG + ( + false, NOT_OR_NOT_YET_IMPLEMENTED_FOR_THIS_GEOMETRY_TYPES_OR_ORIENTATIONS + , (types<Geometry1, Geometry2, GeometryOut>) + ); +}; + + +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 +> +struct intersection_insert + < + TagIn1, TagIn2, TagOut, + true, true, true, + Geometry1, Geometry2, + Reverse1, Reverse2, ReverseOut, + OutputIterator, GeometryOut, + OverlayType, + Strategy + > : detail::overlay::overlay + <Geometry1, Geometry2, Reverse1, Reverse2, ReverseOut, OutputIterator, GeometryOut, OverlayType, Strategy> +{}; + + +// 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 +> +struct intersection_insert + < + TagIn, box_tag, TagOut, + true, true, true, + Geometry, Box, + Reverse1, Reverse2, ReverseOut, + OutputIterator, GeometryOut, + OverlayType, + Strategy + > : detail::overlay::overlay + <Geometry, Box, Reverse1, Reverse2, ReverseOut, OutputIterator, GeometryOut, OverlayType, Strategy> +{}; + + +template +< + typename Segment1, typename Segment2, + bool Reverse1, bool Reverse2, bool ReverseOut, + typename OutputIterator, typename GeometryOut, + overlay_type OverlayType, + typename Strategy +> +struct intersection_insert + < + segment_tag, segment_tag, point_tag, + false, false, false, + Segment1, Segment2, + Reverse1, Reverse2, ReverseOut, + OutputIterator, GeometryOut, + OverlayType, Strategy + > : detail::intersection::intersection_segment_segment_point + < + Segment1, Segment2, + OutputIterator, GeometryOut, + Strategy + > +{}; + + +template +< + typename Linestring1, typename Linestring2, + bool Reverse1, bool Reverse2, bool ReverseOut, + typename OutputIterator, typename GeometryOut, + overlay_type OverlayType, + typename Strategy +> +struct intersection_insert + < + linestring_tag, linestring_tag, point_tag, + false, false, false, + Linestring1, Linestring2, + Reverse1, Reverse2, ReverseOut, + OutputIterator, GeometryOut, + OverlayType, Strategy + > : detail::intersection::intersection_linestring_linestring_point + < + Linestring1, Linestring2, + OutputIterator, GeometryOut, + Strategy + > +{}; + + +template +< + typename Linestring, typename Box, + bool Reverse1, bool Reverse2, bool ReverseOut, + typename OutputIterator, typename GeometryOut, + overlay_type OverlayType, + typename Strategy +> +struct intersection_insert + < + linestring_tag, box_tag, linestring_tag, + false, true, false, + Linestring, Box, + Reverse1, Reverse2, ReverseOut, + OutputIterator, GeometryOut, + OverlayType, + Strategy + > +{ + static inline OutputIterator apply(Linestring const& linestring, + Box const& box, OutputIterator out, Strategy const& ) + { + typedef typename point_type<GeometryOut>::type point_type; + strategy::intersection::liang_barsky<Box, point_type> lb_strategy; + return detail::intersection::clip_range_with_box + <GeometryOut>(box, linestring, out, lb_strategy); + } +}; + + +template +< + typename Linestring, typename Polygon, + bool ReverseLinestring, bool ReversePolygon, bool ReverseOut, + typename OutputIterator, typename GeometryOut, + overlay_type OverlayType, + typename Strategy +> +struct intersection_insert + < + linestring_tag, polygon_tag, linestring_tag, + false, true, false, + Linestring, Polygon, + ReverseLinestring, ReversePolygon, ReverseOut, + OutputIterator, GeometryOut, + OverlayType, + Strategy + > : detail::intersection::intersection_of_linestring_with_areal + < + Linestring, Polygon, + ReversePolygon, + OutputIterator, GeometryOut, + OverlayType, + Strategy + > +{}; + + +template +< + typename Linestring, typename Ring, + bool ReverseLinestring, bool ReverseRing, bool ReverseOut, + typename OutputIterator, typename GeometryOut, + overlay_type OverlayType, + typename Strategy +> +struct intersection_insert + < + linestring_tag, ring_tag, linestring_tag, + false, true, false, + Linestring, Ring, + ReverseLinestring, ReverseRing, ReverseOut, + OutputIterator, GeometryOut, + OverlayType, + Strategy + > : detail::intersection::intersection_of_linestring_with_areal + < + Linestring, Ring, + ReverseRing, + OutputIterator, GeometryOut, + OverlayType, + Strategy + > +{}; + +template +< + typename Segment, typename Box, + bool Reverse1, bool Reverse2, bool ReverseOut, + typename OutputIterator, typename GeometryOut, + overlay_type OverlayType, + typename Strategy +> +struct intersection_insert + < + segment_tag, box_tag, linestring_tag, + false, true, false, + Segment, Box, + Reverse1, Reverse2, ReverseOut, + OutputIterator, GeometryOut, + OverlayType, + Strategy + > +{ + static inline OutputIterator apply(Segment const& segment, + Box const& box, OutputIterator out, Strategy const& ) + { + geometry::segment_view<Segment> range(segment); + + typedef typename point_type<GeometryOut>::type point_type; + strategy::intersection::liang_barsky<Box, point_type> lb_strategy; + return detail::intersection::clip_range_with_box + <GeometryOut>(box, range, out, lb_strategy); + } +}; + +template +< + typename Tag1, typename Tag2, + bool Areal1, bool Areal2, + typename Geometry1, typename Geometry2, + bool Reverse1, bool Reverse2, bool ReverseOut, + typename OutputIterator, typename PointOut, + overlay_type OverlayType, + typename Strategy +> +struct intersection_insert + < + Tag1, Tag2, point_tag, + Areal1, Areal2, false, + Geometry1, Geometry2, + Reverse1, Reverse2, ReverseOut, + OutputIterator, PointOut, + OverlayType, + Strategy + > +{ + static inline OutputIterator apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, OutputIterator out, Strategy const& ) + { + + typedef detail::overlay::turn_info<PointOut> 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); + for (typename std::vector<turn_info>::const_iterator it + = turns.begin(); it != turns.end(); ++it) + { + *out++ = it->point; + } + + return out; + } +}; + + +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, + overlay_type OverlayType, + typename Strategy +> +struct intersection_insert_reversed +{ + static inline OutputIterator apply(Geometry1 const& g1, + Geometry2 const& g2, OutputIterator out, + Strategy const& strategy) + { + return intersection_insert + < + GeometryTag2, GeometryTag1, GeometryTag3, + Areal2, Areal1, ArealOut, + Geometry2, Geometry1, + Reverse2, Reverse1, ReverseOut, + OutputIterator, GeometryOut, + OverlayType, + Strategy + >::apply(g2, g1, out, strategy); + } +}; + + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace intersection +{ + + +template +< + typename GeometryOut, + bool ReverseSecond, + overlay_type OverlayType, + typename Geometry1, typename Geometry2, + typename OutputIterator, + typename Strategy +> +inline OutputIterator insert(Geometry1 const& geometry1, + Geometry2 const& geometry2, + OutputIterator out, + Strategy const& strategy) +{ + return boost::mpl::if_c + < + 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); +} + + +/*! +\brief \brief_calc2{intersection} \brief_strategy +\ingroup intersection +\details \details_calc2{intersection_insert, spatial set theoretic intersection} + \brief_strategy. \details_insert{intersection} +\tparam GeometryOut \tparam_geometry{\p_l_or_c} +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\tparam OutputIterator \tparam_out{\p_l_or_c} +\tparam Strategy \tparam_strategy_overlay +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\param out \param_out{intersection} +\param strategy \param_strategy{intersection} +\return \return_out + +\qbk{distinguish,with strategy} +\qbk{[include reference/algorithms/intersection.qbk]} +*/ +template +< + typename GeometryOut, + typename Geometry1, + typename Geometry2, + typename OutputIterator, + typename Strategy +> +inline OutputIterator intersection_insert(Geometry1 const& geometry1, + Geometry2 const& geometry2, + OutputIterator out, + Strategy const& strategy) +{ + concept::check<Geometry1 const>(); + concept::check<Geometry2 const>(); + + return detail::intersection::insert + < + GeometryOut, false, overlay_intersection + >(geometry1, geometry2, out, strategy); +} + + +/*! +\brief \brief_calc2{intersection} +\ingroup intersection +\details \details_calc2{intersection_insert, spatial set theoretic intersection}. + \details_insert{intersection} +\tparam GeometryOut \tparam_geometry{\p_l_or_c} +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\tparam OutputIterator \tparam_out{\p_l_or_c} +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\param out \param_out{intersection} +\return \return_out + +\qbk{[include reference/algorithms/intersection.qbk]} +*/ +template +< + typename GeometryOut, + typename Geometry1, + typename Geometry2, + typename OutputIterator +> +inline OutputIterator intersection_insert(Geometry1 const& geometry1, + Geometry2 const& geometry2, + OutputIterator out) +{ + concept::check<Geometry1 const>(); + concept::check<Geometry2 const>(); + + typedef strategy_intersection + < + typename cs_tag<GeometryOut>::type, + Geometry1, + Geometry2, + typename geometry::point_type<GeometryOut>::type + > strategy; + + return intersection_insert<GeometryOut>(geometry1, geometry2, out, + strategy()); +} + +}} // namespace detail::intersection +#endif // DOXYGEN_NO_DETAIL + + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_INTERSECTION_INSERT_HPP diff --git a/boost/geometry/algorithms/detail/overlay/overlay.hpp b/boost/geometry/algorithms/detail/overlay/overlay.hpp new file mode 100644 index 0000000000..ab5b6d123d --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/overlay.hpp @@ -0,0 +1,301 @@ +// 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_OVERLAY_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_OVERLAY_HPP + + +#include <deque> +#include <map> + +#include <boost/range.hpp> +#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> +#include <boost/geometry/algorithms/detail/overlay/overlay_type.hpp> +#include <boost/geometry/algorithms/detail/overlay/traverse.hpp> +#include <boost/geometry/algorithms/detail/overlay/traversal_info.hpp> +#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp> + + +#include <boost/geometry/algorithms/num_points.hpp> +#include <boost/geometry/algorithms/reverse.hpp> + +#include <boost/geometry/algorithms/detail/overlay/add_rings.hpp> +#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> + + +#ifdef BOOST_GEOMETRY_DEBUG_ASSEMBLE +# include <boost/geometry/io/dsv/write.hpp> +#endif + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + +// Skip for assemble process +template <typename TurnInfo> +inline bool skip(TurnInfo const& turn_info) +{ + return (turn_info.discarded || turn_info.both(operation_union)) + && ! turn_info.any_blocked() + && ! turn_info.both(operation_intersection) + ; +} + + +template <typename TurnPoints, typename Map> +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) + { + 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) + { + ring_identifier ring_id + ( + op_it->seg_id.source_index, + op_it->seg_id.multi_index, + op_it->seg_id.ring_index + ); + map[ring_id]++; + } + } + } +} + + +template +< + typename GeometryOut, overlay_type Direction, bool ReverseOut, + typename Geometry1, typename Geometry2, + typename OutputIterator +> +inline OutputIterator return_if_one_input_is_empty(Geometry1 const& geometry1, + Geometry2 const& geometry2, + OutputIterator out) +{ + typedef std::deque + < + typename geometry::ring_type<GeometryOut>::type + > ring_container_type; + + typedef ring_properties<typename geometry::point_type<Geometry1>::type> properties; + + // Union: return either of them + // Intersection: return nothing + // Difference: return first of them + if (Direction == overlay_intersection + || (Direction == overlay_difference + && geometry::num_points(geometry1) == 0)) + { + return out; + } + + std::map<ring_identifier, int> empty; + std::map<ring_identifier, properties> all_of_one_of_them; + + select_rings<Direction>(geometry1, geometry2, empty, all_of_one_of_them, false); + ring_container_type rings; + assign_parents(geometry1, geometry2, rings, all_of_one_of_them); + return add_rings<GeometryOut>(all_of_one_of_them, geometry1, geometry2, rings, out); +} + + +template +< + typename Geometry1, typename Geometry2, + bool Reverse1, bool Reverse2, bool ReverseOut, + typename OutputIterator, typename GeometryOut, + overlay_type Direction, + typename Strategy +> +struct overlay +{ + static inline OutputIterator apply( + Geometry1 const& geometry1, Geometry2 const& geometry2, + OutputIterator out, + Strategy const& ) + { + if (geometry::num_points(geometry1) == 0 + && geometry::num_points(geometry2) == 0) + { + return out; + } + + if (geometry::num_points(geometry1) == 0 + || geometry::num_points(geometry2) == 0) + { + return return_if_one_input_is_empty + < + GeometryOut, Direction, ReverseOut + >(geometry1, geometry2, out); + } + + typedef typename geometry::point_type<GeometryOut>::type point_type; + typedef detail::overlay::traversal_turn_info<point_type> turn_info; + typedef std::deque<turn_info> container_type; + + typedef std::deque + < + typename geometry::ring_type<GeometryOut>::type + > ring_container_type; + + container_type turn_points; + +#ifdef BOOST_GEOMETRY_TIME_OVERLAY + boost::timer timer; +#endif + +#ifdef BOOST_GEOMETRY_DEBUG_ASSEMBLE +std::cout << "get turns" << std::endl; +#endif + detail::get_turns::no_interrupt_policy policy; + geometry::get_turns + < + Reverse1, Reverse2, + detail::overlay::calculate_distance_policy + >(geometry1, geometry2, turn_points, policy); + +#ifdef BOOST_GEOMETRY_TIME_OVERLAY + std::cout << "get_turns: " << timer.elapsed() << std::endl; +#endif + +#ifdef BOOST_GEOMETRY_DEBUG_ASSEMBLE +std::cout << "enrich" << std::endl; +#endif + typename Strategy::side_strategy_type side_strategy; + geometry::enrich_intersection_points<Reverse1, Reverse2>(turn_points, + Direction == overlay_union + ? geometry::detail::overlay::operation_union + : geometry::detail::overlay::operation_intersection, + geometry1, geometry2, + side_strategy); + +#ifdef BOOST_GEOMETRY_TIME_OVERLAY + std::cout << "enrich_intersection_points: " << timer.elapsed() << std::endl; +#endif + + +#ifdef BOOST_GEOMETRY_DEBUG_ASSEMBLE +std::cout << "traverse" << std::endl; +#endif + // Traverse through intersection/turn points and create rings of them. + // Note that these rings are always in clockwise order, even in CCW polygons, + // and are marked as "to be reversed" below + ring_container_type rings; + traverse<Reverse1, Reverse2, Geometry1, Geometry2>::apply + ( + geometry1, geometry2, + Direction == overlay_union + ? geometry::detail::overlay::operation_union + : geometry::detail::overlay::operation_intersection, + turn_points, rings + ); + +#ifdef BOOST_GEOMETRY_TIME_OVERLAY + std::cout << "traverse: " << timer.elapsed() << std::endl; +#endif + + + std::map<ring_identifier, int> map; + map_turns(map, turn_points); + +#ifdef BOOST_GEOMETRY_TIME_OVERLAY + std::cout << "map_turns: " << timer.elapsed() << std::endl; +#endif + + typedef ring_properties<typename geometry::point_type<Geometry1>::type> properties; + + std::map<ring_identifier, properties> selected; + select_rings<Direction>(geometry1, geometry2, map, selected, ! turn_points.empty()); + +#ifdef BOOST_GEOMETRY_TIME_OVERLAY + std::cout << "select_rings: " << timer.elapsed() << std::endl; +#endif + + + // Add rings created during traversal + { + ring_identifier id(2, 0, -1); + for (typename boost::range_iterator<ring_container_type>::type + it = boost::begin(rings); + it != boost::end(rings); + ++it) + { + selected[id] = properties(*it, true); + selected[id].reversed = ReverseOut; + id.multi_index++; + } + } + +#ifdef BOOST_GEOMETRY_TIME_OVERLAY + std::cout << "add traversal rings: " << timer.elapsed() << std::endl; +#endif + + + assign_parents(geometry1, geometry2, rings, selected); + +#ifdef BOOST_GEOMETRY_TIME_OVERLAY + std::cout << "assign_parents: " << timer.elapsed() << std::endl; +#endif + + return add_rings<GeometryOut>(selected, geometry1, geometry2, rings, out); + } +}; + + +// 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_OVERLAY_HPP diff --git a/boost/geometry/algorithms/detail/overlay/overlay_type.hpp b/boost/geometry/algorithms/detail/overlay/overlay_type.hpp new file mode 100644 index 0000000000..af62131f0e --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/overlay_type.hpp @@ -0,0 +1,29 @@ +// 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_OVERLAY_TYPE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_OVERLAY_TYPE_HPP + + + +namespace boost { namespace geometry +{ + +enum overlay_type +{ + overlay_union, + overlay_intersection, + overlay_difference, + overlay_dissolve +}; + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_OVERLAY_TYPE_HPP diff --git a/boost/geometry/algorithms/detail/overlay/ring_properties.hpp b/boost/geometry/algorithms/detail/overlay/ring_properties.hpp new file mode 100644 index 0000000000..a6088694da --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/ring_properties.hpp @@ -0,0 +1,78 @@ +// 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_RING_PROPERTIES_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_RING_PROPERTIES_HPP + + +#include <boost/geometry/algorithms/area.hpp> +#include <boost/geometry/algorithms/within.hpp> +#include <boost/geometry/algorithms/detail/point_on_border.hpp> + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + +template <typename Point> +struct ring_properties +{ + typedef Point point_type; + typedef typename default_area_result<Point>::type area_type; + + // Filled by "select_rings" + Point point; + area_type area; + + // Filled by "update_selection_map" + int within_code; + bool reversed; + + // Filled/used by "assign_rings" + bool discarded; + ring_identifier parent; + area_type parent_area; + std::vector<ring_identifier> children; + + inline ring_properties() + : area(area_type()) + , within_code(-1) + , reversed(false) + , discarded(false) + , parent_area(-1) + {} + + template <typename RingOrBox> + inline ring_properties(RingOrBox const& ring_or_box, bool midpoint) + : within_code(-1) + , reversed(false) + , discarded(false) + , parent_area(-1) + { + this->area = geometry::area(ring_or_box); + geometry::point_on_border(this->point, ring_or_box, midpoint); + } + + inline area_type get_area() const + { + return reversed ? -area : area; + } +}; + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_RING_PROPERTIES_HPP diff --git a/boost/geometry/algorithms/detail/overlay/segment_identifier.hpp b/boost/geometry/algorithms/detail/overlay/segment_identifier.hpp new file mode 100644 index 0000000000..007113ffba --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/segment_identifier.hpp @@ -0,0 +1,91 @@ +// 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_SEGMENT_IDENTIFIER_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SEGMENT_IDENTIFIER_HPP + + +#if defined(BOOST_GEOMETRY_DEBUG_OVERLAY) +# define BOOST_GEOMETRY_DEBUG_SEGMENT_IDENTIFIER +#endif + + +#include <vector> + + +#include <boost/geometry/core/access.hpp> +#include <boost/geometry/core/coordinate_dimension.hpp> + + + +namespace boost { namespace geometry +{ + + +// Internal struct to uniquely identify a segment +// on a linestring,ring +// or polygon (needs ring_index) +// or multi-geometry (needs multi_index) +struct segment_identifier +{ + inline segment_identifier() + : source_index(-1) + , multi_index(-1) + , ring_index(-1) + , segment_index(-1) + {} + + inline segment_identifier(int src, int mul, int rin, int seg) + : source_index(src) + , multi_index(mul) + , ring_index(rin) + , segment_index(seg) + {} + + inline bool operator<(segment_identifier const& other) const + { + return source_index != other.source_index ? source_index < other.source_index + : multi_index !=other.multi_index ? multi_index < other.multi_index + : ring_index != other.ring_index ? ring_index < other.ring_index + : segment_index < other.segment_index + ; + } + + inline bool operator==(segment_identifier const& other) const + { + return source_index == other.source_index + && segment_index == other.segment_index + && ring_index == other.ring_index + && multi_index == other.multi_index + ; + } + +#if defined(BOOST_GEOMETRY_DEBUG_SEGMENT_IDENTIFIER) + friend std::ostream& operator<<(std::ostream &os, segment_identifier const& seg_id) + { + std::cout + << "s:" << seg_id.source_index + << ", v:" << seg_id.segment_index // ~vertex + ; + 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; + return os; + } +#endif + + int source_index; + int multi_index; + int ring_index; + int segment_index; +}; + + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SEGMENT_IDENTIFIER_HPP diff --git a/boost/geometry/algorithms/detail/overlay/select_rings.hpp b/boost/geometry/algorithms/detail/overlay/select_rings.hpp new file mode 100644 index 0000000000..f664b19514 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/select_rings.hpp @@ -0,0 +1,295 @@ +// 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_SELECT_RINGS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SELECT_RINGS_HPP + +#include <map> + + +#include <boost/geometry/algorithms/area.hpp> +#include <boost/geometry/algorithms/within.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> +#include <boost/geometry/algorithms/detail/overlay/overlay_type.hpp> + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + + +namespace dispatch +{ + + template <typename Tag, typename Geometry> + struct select_rings + {}; + + template <typename Box> + struct select_rings<box_tag, Box> + { + template <typename Geometry, typename Map> + 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, + ring_identifier const& id, Map& map, bool midpoint) + { + map[id] = typename Map::mapped_type(box, midpoint); + } + }; + + template <typename Ring> + struct select_rings<ring_tag, Ring> + { + template <typename Geometry, typename Map> + static inline void apply(Ring const& ring, Geometry const& , + ring_identifier const& id, Map& map, bool midpoint) + { + if (boost::size(ring) > 0) + { + map[id] = typename Map::mapped_type(ring, midpoint); + } + } + + template <typename Map> + static inline void apply(Ring const& ring, + ring_identifier const& id, Map& map, bool midpoint) + { + if (boost::size(ring) > 0) + { + map[id] = typename Map::mapped_type(ring, midpoint); + } + } + }; + + + template <typename Polygon> + struct select_rings<polygon_tag, Polygon> + { + template <typename Geometry, typename Map> + static inline void apply(Polygon const& polygon, Geometry const& geometry, + ring_identifier id, Map& map, bool midpoint) + { + typedef typename geometry::ring_type<Polygon>::type ring_type; + typedef select_rings<ring_tag, ring_type> per_ring; + + 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) + { + id.ring_index++; + per_ring::apply(*it, geometry, id, map, midpoint); + } + } + + template <typename Map> + static inline void apply(Polygon const& polygon, + ring_identifier id, Map& map, bool midpoint) + { + typedef typename geometry::ring_type<Polygon>::type ring_type; + typedef select_rings<ring_tag, ring_type> per_ring; + + 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) + { + id.ring_index++; + per_ring::apply(*it, id, map, midpoint); + } + } + }; +} + + +template<overlay_type OverlayType> +struct decide +{}; + +template<> +struct decide<overlay_union> +{ + template <typename Code> + static bool include(ring_identifier const& , Code const& code) + { + return code.within_code * -1 == 1; + } + + template <typename Code> + static bool reversed(ring_identifier const& , Code const& ) + { + return false; + } +}; + +template<> +struct decide<overlay_difference> +{ + template <typename Code> + static bool include(ring_identifier const& id, Code const& code) + { + bool is_first = id.source_index == 0; + return code.within_code * -1 * (is_first ? 1 : -1) == 1; + } + + template <typename Code> + static bool reversed(ring_identifier const& id, Code const& code) + { + return include(id, code) && id.source_index == 1; + } +}; + +template<> +struct decide<overlay_intersection> +{ + template <typename Code> + static bool include(ring_identifier const& , Code const& code) + { + return code.within_code * 1 == 1; + } + + template <typename Code> + static bool reversed(ring_identifier const& , Code const& ) + { + return false; + } +}; + + +template +< + overlay_type OverlayType, + typename Geometry1, typename Geometry2, + typename IntersectionMap, typename SelectionMap +> +inline void update_selection_map(Geometry1 const& geometry1, + Geometry2 const& geometry2, + IntersectionMap const& intersection_map, + SelectionMap const& map_with_all, SelectionMap& selection_map) +{ + selection_map.clear(); + + for (typename SelectionMap::const_iterator it = boost::begin(map_with_all); + it != boost::end(map_with_all); + ++it) + { + /* + int union_code = it->second.within_code * -1; + bool is_first = it->first.source_index == 0; + std::cout << it->first << " " << it->second.area + << ": " << it->second.within_code + << " union: " << union_code + << " intersection: " << (it->second.within_code * 1) + << " G1-G2: " << (union_code * (is_first ? 1 : -1)) + << " G2-G1: " << (union_code * (is_first ? -1 : 1)) + << " -> " << (decide<OverlayType>::include(it->first, it->second) ? "INC" : "") + << decide<OverlayType>::reverse(it->first, it->second) + << std::endl; + */ + + bool found = intersection_map.find(it->first) != intersection_map.end(); + if (! found) + { + ring_identifier const id = it->first; + 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, + // using partition, TODO) + // So though this is less elegant than before, it avoids many unused point-in-poly calculations + switch(id.source_index) + { + case 0 : + properties.within_code + = geometry::within(properties.point, geometry2) ? 1 : -1; + break; + case 1 : + properties.within_code + = geometry::within(properties.point, geometry1) ? 1 : -1; + break; + } + + if (decide<OverlayType>::include(id, properties)) + { + properties.reversed = decide<OverlayType>::reversed(id, properties); + selection_map[id] = properties; + } + } + } +} + + +/*! +\brief The function select_rings select rings based on the overlay-type (union,intersection) +*/ +template +< + overlay_type OverlayType, + typename Geometry1, typename Geometry2, + typename IntersectionMap, typename SelectionMap +> +inline void select_rings(Geometry1 const& geometry1, Geometry2 const& geometry2, + IntersectionMap const& intersection_map, + SelectionMap& selection_map, bool midpoint) +{ + typedef typename geometry::tag<Geometry1>::type tag1; + typedef typename geometry::tag<Geometry2>::type tag2; + + SelectionMap map_with_all; + dispatch::select_rings<tag1, Geometry1>::apply(geometry1, geometry2, + ring_identifier(0, -1, -1), map_with_all, midpoint); + dispatch::select_rings<tag2, Geometry2>::apply(geometry2, geometry1, + ring_identifier(1, -1, -1), map_with_all, midpoint); + + update_selection_map<OverlayType>(geometry1, geometry2, intersection_map, + map_with_all, selection_map); +} + +template +< + overlay_type OverlayType, + typename Geometry, + typename IntersectionMap, typename SelectionMap +> +inline void select_rings(Geometry const& geometry, + 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, + ring_identifier(0, -1, -1), map_with_all, midpoint); + + update_selection_map<OverlayType>(geometry, geometry, intersection_map, + map_with_all, selection_map); +} + + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SELECT_RINGS_HPP diff --git a/boost/geometry/algorithms/detail/overlay/self_turn_points.hpp b/boost/geometry/algorithms/detail/overlay/self_turn_points.hpp new file mode 100644 index 0000000000..9c4c99394e --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/self_turn_points.hpp @@ -0,0 +1,308 @@ +// 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_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/geometries/concepts/check.hpp> + +#include <boost/geometry/algorithms/detail/disjoint.hpp> +#include <boost/geometry/algorithms/detail/partition.hpp> +#include <boost/geometry/algorithms/detail/overlay/get_turns.hpp> + +#include <boost/geometry/geometries/box.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace self_get_turn_points +{ + +struct no_interrupt_policy +{ + static bool const enabled = false; + static bool const has_intersections = false; + + + template <typename Range> + static inline bool apply(Range const&) + { + return false; + } +}; + + + + +class self_ip_exception : public geometry::exception {}; + +template +< + typename Geometry, + typename Turns, + typename TurnPolicy, + typename InterruptPolicy +> +struct self_section_visitor +{ + Geometry const& m_geometry; + Turns& m_turns; + InterruptPolicy& m_interrupt_policy; + + inline self_section_visitor(Geometry const& g, + Turns& turns, InterruptPolicy& ip) + : m_geometry(g) + , m_turns(turns) + , m_interrupt_policy(ip) + {} + + template <typename Section> + inline bool apply(Section const& sec1, Section const& sec2) + { + if (! detail::disjoint::disjoint_box_box(sec1.bounding_box, sec2.bounding_box) + && ! sec1.duplicate + && ! sec2.duplicate) + { + detail::get_turns::get_turns_in_sections + < + Geometry, Geometry, + false, false, + Section, Section, + Turns, TurnPolicy, + InterruptPolicy + >::apply( + 0, m_geometry, sec1, + 0, m_geometry, sec2, + false, + m_turns, m_interrupt_policy); + } + if (m_interrupt_policy.has_intersections) + { + // TODO: we should give partition an interrupt policy. + // Now we throw, and catch below, to stop the partition loop. + throw self_ip_exception(); + } + return true; + } + +}; + + + +template +< + typename Geometry, + typename Turns, + typename TurnPolicy, + typename InterruptPolicy +> +struct get_turns +{ + static inline bool apply( + Geometry const& geometry, + Turns& turns, + InterruptPolicy& interrupt_policy) + { + typedef model::box + < + typename geometry::point_type<Geometry>::type + > box_type; + typedef typename geometry::sections + < + box_type, 1 + > sections_type; + + sections_type sec; + geometry::sectionalize<false>(geometry, sec); + + self_section_visitor + < + Geometry, + Turns, TurnPolicy, InterruptPolicy + > visitor(geometry, turns, interrupt_policy); + + try + { + geometry::partition + < + box_type, + detail::get_turns::get_section_box, + detail::get_turns::ovelaps_section_box + >::apply(sec, visitor); + } + catch(self_ip_exception const& ) + { + return false; + } + + return true; + } +}; + + +}} // namespace detail::self_get_turn_points +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template +< + typename GeometryTag, + typename Geometry, + typename Turns, + typename TurnPolicy, + typename InterruptPolicy +> +struct self_get_turn_points +{ +}; + + +template +< + typename Ring, + typename Turns, + typename TurnPolicy, + typename InterruptPolicy +> +struct self_get_turn_points + < + ring_tag, Ring, + Turns, + TurnPolicy, + InterruptPolicy + > + : detail::self_get_turn_points::get_turns + < + Ring, + Turns, + TurnPolicy, + InterruptPolicy + > +{}; + + +template +< + typename Box, + typename Turns, + typename TurnPolicy, + typename InterruptPolicy +> +struct self_get_turn_points + < + box_tag, Box, + Turns, + TurnPolicy, + InterruptPolicy + > +{ + static inline bool apply( + Box const& , + Turns& , + InterruptPolicy& ) + { + return true; + } +}; + + +template +< + typename Polygon, + typename Turns, + typename TurnPolicy, + typename InterruptPolicy +> +struct self_get_turn_points + < + polygon_tag, Polygon, + Turns, + TurnPolicy, + InterruptPolicy + > + : detail::self_get_turn_points::get_turns + < + Polygon, + Turns, + TurnPolicy, + InterruptPolicy + > +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +/*! + \brief Calculate self intersections of a geometry + \ingroup overlay + \tparam Geometry geometry type + \tparam Turns type of intersection container + (e.g. vector of "intersection/turn point"'s) + \param geometry geometry + \param turns container which will contain intersection points + \param interrupt_policy policy determining if process is stopped + when intersection is found + */ +template +< + typename AssignPolicy, + typename Geometry, + typename Turns, + typename InterruptPolicy +> +inline void self_turns(Geometry const& geometry, + 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; + + dispatch::self_get_turn_points + < + typename tag<Geometry>::type, + Geometry, + Turns, + TurnPolicy, + InterruptPolicy + >::apply(geometry, turns, interrupt_policy); +} + + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SELF_TURN_POINTS_HPP diff --git a/boost/geometry/algorithms/detail/overlay/stream_info.hpp b/boost/geometry/algorithms/detail/overlay/stream_info.hpp new file mode 100644 index 0000000000..eebe381944 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/stream_info.hpp @@ -0,0 +1,75 @@ +// 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_STREAM_INFO_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_STREAM_INFO_HPP + + +#include <string> + +#include <boost/array.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + + + static inline std::string dir(int d) + { + return d == 0 ? "-" : (d == 1 ? "L" : d == -1 ? "R" : "#"); + } + static inline std::string how_str(int h) + { + return h == 0 ? "-" : (h == 1 ? "A" : "D"); + } + + 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 + << " (// " << info.other_id.source_index + << "." << info.other_id.segment_index << ")" + << " how " << info.how + << "[" << how_str(info.arrival) + << " " << dir(info.direction) + << (info.opposite ? " o" : "") + << "]" + << " sd " + << dir(info.sides.get<0,0>()) + << dir(info.sides.get<0,1>()) + << dir(info.sides.get<1,0>()) + << dir(info.sides.get<1,1>()) + << " nxt seg " << info.travels_to_vertex_index + << " , ip " << info.travels_to_ip_index + << " , or " << info.next_ip_index + << " dst " << double(d) + << info.visit_state; + if (info.flagged) + { + os << " FLAGGED"; + } + return os; + } + + + +}} // namespace detail::overlay +#endif //DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_STREAM_INFO_HPP diff --git a/boost/geometry/algorithms/detail/overlay/traversal_info.hpp b/boost/geometry/algorithms/detail/overlay/traversal_info.hpp new file mode 100644 index 0000000000..810a27af04 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/traversal_info.hpp @@ -0,0 +1,47 @@ +// 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_TRAVERSAL_INFO_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSAL_INFO_HPP + + +#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp> +#include <boost/geometry/algorithms/detail/overlay/enrichment_info.hpp> +#include <boost/geometry/algorithms/detail/overlay/visit_info.hpp> +#include <boost/geometry/algorithms/detail/overlay/segment_identifier.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + + +template <typename P> +struct traversal_turn_operation : public turn_operation +{ + enrichment_info<P> enriched; + visit_info visited; +}; + +template <typename P> +struct traversal_turn_info : public turn_info<P, traversal_turn_operation<P> > +{}; + + + +}} // namespace detail::overlay +#endif //DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSAL_INFO_HPP diff --git a/boost/geometry/algorithms/detail/overlay/traverse.hpp b/boost/geometry/algorithms/detail/overlay/traverse.hpp new file mode 100644 index 0000000000..12daafa0cf --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/traverse.hpp @@ -0,0 +1,395 @@ +// 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_TRAVERSE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSE_HPP + +#include <cstddef> + +#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/core/access.hpp> +#include <boost/geometry/core/coordinate_dimension.hpp> +#include <boost/geometry/geometries/concepts/check.hpp> + +#if defined(BOOST_GEOMETRY_DEBUG_INTERSECTION) \ + || defined(BOOST_GEOMETRY_OVERLAY_REPORT_WKT) \ + || defined(BOOST_GEOMETRY_DEBUG_TRAVERSE) +# include <string> +# include <boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp> +# include <boost/geometry/io/wkt/wkt.hpp> +#endif + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + +template <typename Turn, typename Operation> +#ifdef BOOST_GEOMETRY_DEBUG_TRAVERSE +inline void debug_traverse(Turn const& turn, Operation op, + std::string const& header) +{ + std::cout << header + << " at " << op.seg_id + << " meth: " << method_char(turn.method) + << " op: " << operation_char(op.operation) + << " vis: " << visited_char(op.visited) + << " of: " << operation_char(turn.operations[0].operation) + << operation_char(turn.operations[1].operation) + << " " << geometry::wkt(turn.point) + << std::endl; + + if (boost::contains(header, "Finished")) + { + std::cout << std::endl; + } +} +#else +inline void debug_traverse(Turn const& , Operation, std::string const& ) +{ +} +#endif + + +template <typename Info, typename Turn> +inline void set_visited_for_continue(Info& info, Turn const& turn) +{ + // On "continue", set "visited" for ALL directions + if (turn.operation == detail::overlay::operation_continue) + { + for (typename boost::range_iterator + < + typename Info::container_type + >::type it = boost::begin(info.operations); + it != boost::end(info.operations); + ++it) + { + if (it->visited.none()) + { + it->visited.set_visited(); + } + } + } +} + + +template +< + bool Reverse1, bool Reverse2, + typename GeometryOut, + typename G1, + typename G2, + typename Turns, + typename IntersectionInfo +> +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) +{ + info.visited.set_visited(); + set_visited_for_continue(*ip, info); + + // If there is no next IP on this segment + if (info.enriched.next_ip_index < 0) + { + if (info.enriched.travels_to_vertex_index < 0 + || info.enriched.travels_to_ip_index < 0) + { + return false; + } + + BOOST_ASSERT(info.enriched.travels_to_vertex_index >= 0); + BOOST_ASSERT(info.enriched.travels_to_ip_index >= 0); + + if (info.seg_id.source_index == 0) + { + geometry::copy_segments<Reverse1>(g1, info.seg_id, + info.enriched.travels_to_vertex_index, + current_output); + } + else + { + geometry::copy_segments<Reverse2>(g2, info.seg_id, + info.enriched.travels_to_vertex_index, + current_output); + } + seg_id = info.seg_id; + ip = boost::begin(turns) + info.enriched.travels_to_ip_index; + } + else + { + ip = boost::begin(turns) + info.enriched.next_ip_index; + seg_id = info.seg_id; + } + + detail::overlay::append_no_duplicates(current_output, ip->point); + return true; +} + + +inline bool select_source(operation_type operation, int source1, int source2) +{ + return (operation == operation_intersection && source1 != source2) + || (operation == operation_union && source1 == source2) + ; +} + + +template +< + typename Turn, + typename Iterator +> +inline bool select_next_ip(operation_type operation, + Turn& turn, + segment_identifier const& seg_id, + Iterator& selected) +{ + if (turn.discarded) + { + return false; + } + bool has_tp = false; + selected = boost::end(turn.operations); + for (Iterator it = boost::begin(turn.operations); + it != boost::end(turn.operations); + ++it) + { + if (it->visited.started()) + { + selected = it; + //std::cout << " RETURN"; + return true; + } + + // In some cases there are two alternatives. + // For "ii", take the other one (alternate) + // UNLESS the other one is already visited + // For "uu", take the same one (see above); + // For "cc", take either one, but if there is a starting one, + // take that one. + if ( (it->operation == operation_continue + && (! has_tp || it->visited.started() + ) + ) + || (it->operation == operation + && ! it->visited.finished() + && (! has_tp + || select_source(operation, + it->seg_id.source_index, seg_id.source_index) + ) + ) + ) + { + selected = it; + debug_traverse(turn, *it, " Candidate"); + has_tp = true; + } + } + + if (has_tp) + { + debug_traverse(turn, *selected, " Accepted"); + } + + + return has_tp; +} + + + +/*! + \brief Traverses through intersection points / geometries + \ingroup overlay + */ +template +< + bool Reverse1, bool Reverse2, + typename Geometry1, + typename Geometry2, + typename Backtrack = backtrack_check_self_intersections<Geometry1, Geometry2> +> +class traverse +{ +public : + template <typename Turns, typename Rings> + static inline void apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + detail::overlay::operation_type operation, + Turns& turns, Rings& rings) + { + typedef typename boost::range_iterator<Turns>::type turn_iterator; + typedef typename boost::range_value<Turns>::type turn_type; + typedef typename boost::range_iterator + < + typename turn_type::container_type + >::type turn_operation_iterator_type; + + std::size_t size_at_start = boost::size(rings); + + typename Backtrack::state_type state; + do + { + state.reset(); + + // Iterate through all unvisited points + for (turn_iterator it = boost::begin(turns); + state.good() && it != boost::end(turns); + ++it) + { + // Skip discarded ones + if (! (it->is_discarded() || it->blocked())) + { + for (turn_operation_iterator_type iit = boost::begin(it->operations); + state.good() && iit != boost::end(it->operations); + ++iit) + { + if (iit->visited.none() + && ! iit->visited.rejected() + && (iit->operation == operation + || iit->operation == detail::overlay::operation_continue) + ) + { + set_visited_for_continue(*it, *iit); + + typename boost::range_value<Rings>::type current_output; + detail::overlay::append_no_duplicates(current_output, + it->point, true); + + turn_iterator current = it; + turn_operation_iterator_type current_iit = iit; + segment_identifier current_seg_id; + + if (! detail::overlay::assign_next_ip<Reverse1, Reverse2>( + geometry1, geometry2, + turns, + current, current_output, + *iit, current_seg_id)) + { + Backtrack::apply( + size_at_start, + rings, current_output, turns, *current_iit, + "No next IP", + geometry1, geometry2, state); + } + + if (! detail::overlay::select_next_ip( + operation, + *current, + current_seg_id, + current_iit)) + { + Backtrack::apply( + size_at_start, + rings, current_output, turns, *iit, + "Dead end at start", + geometry1, geometry2, state); + } + else + { + + iit->visited.set_started(); + detail::overlay::debug_traverse(*it, *iit, "-> Started"); + detail::overlay::debug_traverse(*current, *current_iit, "Selected "); + + + unsigned int i = 0; + + while (current_iit != iit && state.good()) + { + if (current_iit->visited.visited()) + { + // It visits a visited node again, without passing the start node. + // This makes it suspicious for endless loops + Backtrack::apply( + size_at_start, + rings, current_output, turns, *iit, + "Visit again", + geometry1, geometry2, state); + } + else + { + + + // We assume clockwise polygons only, non self-intersecting, closed. + // However, the input might be different, and checking validity + // is up to the library user. + + // Therefore we make here some sanity checks. If the input + // violates the assumptions, the output polygon will not be correct + // but the routine will stop and output the current polygon, and + // will continue with the next one. + + // Below three reasons to stop. + detail::overlay::assign_next_ip<Reverse1, Reverse2>( + geometry1, geometry2, + turns, current, current_output, + *current_iit, current_seg_id); + + if (! detail::overlay::select_next_ip( + operation, + *current, + current_seg_id, + current_iit)) + { + // Should not occur in valid (non-self-intersecting) polygons + // Should not occur in self-intersecting polygons without spikes + // Might occur in polygons with spikes + Backtrack::apply( + size_at_start, + rings, current_output, turns, *iit, + "Dead end", + geometry1, geometry2, state); + } + detail::overlay::debug_traverse(*current, *current_iit, "Selected "); + + if (i++ > 2 + 2 * turns.size()) + { + // Sanity check: there may be never more loops + // than turn points. + // Turn points marked as "ii" can be visited twice. + Backtrack::apply( + size_at_start, + rings, current_output, turns, *iit, + "Endless loop", + geometry1, geometry2, state); + } + } + } + + if (state.good()) + { + iit->visited.set_finished(); + detail::overlay::debug_traverse(*current, *iit, "->Finished"); + rings.push_back(current_output); + } + } + } + } + } + } + } while (! state.good()); + } +}; + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSE_HPP diff --git a/boost/geometry/algorithms/detail/overlay/turn_info.hpp b/boost/geometry/algorithms/detail/overlay/turn_info.hpp new file mode 100644 index 0000000000..aa6b428f19 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/turn_info.hpp @@ -0,0 +1,142 @@ +// 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_TURN_INFO_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TURN_INFO_HPP + + +#include <boost/array.hpp> + +#include <boost/geometry/algorithms/detail/overlay/segment_identifier.hpp> + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + + +enum operation_type +{ + operation_none, + operation_union, + operation_intersection, + operation_blocked, + operation_continue +}; + + +enum method_type +{ + method_none, + method_disjoint, + method_crosses, + method_touch, + method_touch_interior, + method_collinear, + method_equal, + method_error +}; + + +/*! + \brief Turn operation: operation + \details Information necessary for traversal phase (a phase + of the overlay process). The information is gathered during the + get_turns (segment intersection) phase. + 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. + */ +struct turn_operation +{ + operation_type operation; + segment_identifier seg_id; + segment_identifier other_id; + + inline turn_operation() + : operation(operation_none) + {} +}; + + +/*! + \brief Turn information: intersection point, method, and turn information + \details Information necessary for traversal phase (a phase + of the overlay process). The information is gathered during the + get_turns (segment intersection) phase. + \tparam Point point type of intersection point + \tparam Operation gives classes opportunity to add additional info + \tparam Container gives classes opportunity to define how operations are stored + */ +template +< + typename Point, + typename Operation = turn_operation, + typename Container = boost::array<Operation, 2> +> +struct turn_info +{ + typedef Point point_type; + typedef Operation turn_operation_type; + typedef Container container_type; + + Point point; + method_type method; + bool discarded; + + + Container operations; + + inline turn_info() + : method(method_none) + , discarded(false) + {} + + inline bool both(operation_type type) const + { + return has12(type, type); + } + + inline bool combination(operation_type type1, operation_type type2) const + { + return has12(type1, type2) || has12(type2, type1); + } + + + inline bool is_discarded() const { return discarded; } + inline bool blocked() const + { + return both(operation_blocked); + } + inline bool any_blocked() const + { + return this->operations[0].operation == operation_blocked + || this->operations[1].operation == operation_blocked; + } + + +private : + inline bool has12(operation_type type1, operation_type type2) const + { + return this->operations[0].operation == type1 + && this->operations[1].operation == type2 + ; + } + +}; + + +}} // namespace detail::overlay +#endif //DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TURN_INFO_HPP diff --git a/boost/geometry/algorithms/detail/overlay/visit_info.hpp b/boost/geometry/algorithms/detail/overlay/visit_info.hpp new file mode 100644 index 0000000000..6be63f42b4 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/visit_info.hpp @@ -0,0 +1,136 @@ +// 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_VISIT_INFO_HPP +#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 +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + + +#if ! defined(BOOST_GEOMETRY_USE_MSM) + +class visit_info +{ +private : + static const int NONE = 0; + static const int STARTED = 1; + static const int VISITED = 2; + static const int FINISHED = 3; + static const int REJECTED = 4; + + int m_visit_code; + bool m_rejected; + +public: + inline visit_info() + : m_visit_code(0) + , m_rejected(false) + {} + + inline void set_visited() { m_visit_code = VISITED; } + inline void set_started() { m_visit_code = STARTED; } + inline void set_finished() { m_visit_code = FINISHED; } + inline void set_rejected() + { + m_visit_code = REJECTED; + m_rejected = true; + } + + inline bool none() const { return m_visit_code == NONE; } + inline bool visited() const { return m_visit_code == VISITED; } + inline bool started() const { return m_visit_code == STARTED; } + inline bool finished() const { return m_visit_code == FINISHED; } + inline bool rejected() const { return m_rejected; } + + inline void clear() + { + if (! rejected()) + { + m_visit_code = NONE; + } + } + + + +#ifdef BOOST_GEOMETRY_DEBUG_INTERSECTION + friend std::ostream& operator<<(std::ostream &os, visit_info const& v) + { + if (v.m_visit_code != 0) + { + os << " VIS: " << int(v.m_visit_code); + } + return os; + } +#endif + +}; + + +#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 + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_VISIT_INFO_HPP diff --git a/boost/geometry/algorithms/detail/partition.hpp b/boost/geometry/algorithms/detail/partition.hpp new file mode 100644 index 0000000000..7a7de2cdd3 --- /dev/null +++ b/boost/geometry/algorithms/detail/partition.hpp @@ -0,0 +1,425 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2011-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_PARTITION_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_PARTITION_HPP + +#include <vector> +#include <boost/range/algorithm/copy.hpp> +#include <boost/geometry/algorithms/assign.hpp> +#include <boost/geometry/core/coordinate_type.hpp> + +namespace boost { namespace geometry +{ + +namespace detail { namespace partition +{ + +typedef std::vector<std::size_t> index_vector_type; + +template <int Dimension, typename Box> +inline void divide_box(Box const& box, Box& lower_box, Box& upper_box) +{ + typedef typename coordinate_type<Box>::type ctype; + + // Divide input box into two parts, e.g. left/right + ctype two = 2; + ctype mid = (geometry::get<min_corner, Dimension>(box) + + geometry::get<max_corner, Dimension>(box)) / two; + + lower_box = box; + upper_box = box; + geometry::set<max_corner, Dimension>(lower_box, mid); + geometry::set<min_corner, Dimension>(upper_box, mid); +} + +// Divide collection into three subsets: lower, upper and oversized +// (not-fitting) +// (lower == left or bottom, upper == right or top) +template <typename OverlapsPolicy, typename InputCollection, typename Box> +static inline void divide_into_subsets(Box const& lower_box, + Box const& upper_box, + InputCollection const& collection, + index_vector_type const& input, + index_vector_type& lower, + index_vector_type& upper, + index_vector_type& exceeding) +{ + typedef boost::range_iterator + < + index_vector_type const + >::type index_iterator_type; + + for(index_iterator_type it = boost::begin(input); + it != boost::end(input); + ++it) + { + bool const lower_overlapping = OverlapsPolicy::apply(lower_box, + collection[*it]); + bool const upper_overlapping = OverlapsPolicy::apply(upper_box, + collection[*it]); + + if (lower_overlapping && upper_overlapping) + { + exceeding.push_back(*it); + } + else if (lower_overlapping) + { + lower.push_back(*it); + } + else if (upper_overlapping) + { + upper.push_back(*it); + } + else + { + // Is nowhere! Should not occur! + BOOST_ASSERT(true); + } + } +} + +// Match collection with itself +template <typename InputCollection, typename Policy> +static inline void handle_one(InputCollection const& collection, + index_vector_type const& input, + Policy& policy) +{ + typedef boost::range_iterator<index_vector_type const>::type + index_iterator_type; + // Quadratic behaviour at lowest level (lowest quad, or all exceeding) + for(index_iterator_type it1 = boost::begin(input); + it1 != boost::end(input); + ++it1) + { + index_iterator_type it2 = it1; + for(++it2; it2 != boost::end(input); ++it2) + { + policy.apply(collection[*it1], collection[*it2]); + } + } +} + +// Match collection 1 with collection 2 +template <typename InputCollection, typename Policy> +static inline void handle_two( + InputCollection const& collection1, index_vector_type const& input1, + InputCollection const& collection2, index_vector_type const& input2, + Policy& policy) +{ + typedef boost::range_iterator + < + index_vector_type const + >::type index_iterator_type; + + for(index_iterator_type it1 = boost::begin(input1); + it1 != boost::end(input1); + ++it1) + { + for(index_iterator_type it2 = boost::begin(input2); + it2 != boost::end(input2); + ++it2) + { + policy.apply(collection1[*it1], collection2[*it2]); + } + } +} + +template +< + int Dimension, + typename Box, + typename OverlapsPolicy, + typename VisitBoxPolicy +> +class partition_one_collection +{ + typedef std::vector<std::size_t> index_vector_type; + typedef typename coordinate_type<Box>::type ctype; + typedef partition_one_collection + < + 1 - Dimension, + Box, + OverlapsPolicy, + VisitBoxPolicy + > sub_divide; + + template <typename InputCollection, typename Policy> + static inline void next_level(Box const& box, + InputCollection const& collection, + index_vector_type const& input, + int level, int min_elements, + Policy& policy, VisitBoxPolicy& box_policy) + { + if (boost::size(input) > 0) + { + if (boost::size(input) > min_elements && level < 100) + { + sub_divide::apply(box, collection, input, level + 1, + min_elements, policy, box_policy); + } + else + { + handle_one(collection, input, policy); + } + } + } + +public : + template <typename InputCollection, typename Policy> + static inline void apply(Box const& box, + InputCollection const& collection, + index_vector_type const& input, + int level, + int min_elements, + Policy& policy, VisitBoxPolicy& box_policy) + { + box_policy.apply(box, level); + + Box lower_box, upper_box; + divide_box<Dimension>(box, lower_box, upper_box); + + index_vector_type lower, upper, exceeding; + divide_into_subsets<OverlapsPolicy>(lower_box, upper_box, collection, + input, lower, upper, exceeding); + + if (boost::size(exceeding) > 0) + { + // All what is not fitting a partition should be combined + // with each other, and with all which is fitting. + handle_one(collection, exceeding, policy); + handle_two(collection, exceeding, collection, lower, policy); + handle_two(collection, exceeding, collection, upper, policy); + } + + // Recursively call operation both parts + next_level(lower_box, collection, lower, level, min_elements, + policy, box_policy); + next_level(upper_box, collection, upper, level, min_elements, + policy, box_policy); + } +}; + +template +< + int Dimension, + typename Box, + typename OverlapsPolicy, + typename VisitBoxPolicy +> +class partition_two_collections +{ + typedef std::vector<std::size_t> index_vector_type; + typedef typename coordinate_type<Box>::type ctype; + typedef partition_two_collections + < + 1 - Dimension, + Box, + OverlapsPolicy, + VisitBoxPolicy + > sub_divide; + + template <typename InputCollection, typename Policy> + static inline void next_level(Box const& box, + InputCollection const& collection1, + index_vector_type const& input1, + InputCollection const& collection2, + index_vector_type const& input2, + int level, int min_elements, + Policy& policy, VisitBoxPolicy& box_policy) + { + if (boost::size(input1) > 0 && boost::size(input2) > 0) + { + if (boost::size(input1) > min_elements + && boost::size(input2) > min_elements + && level < 100) + { + sub_divide::apply(box, collection1, input1, collection2, + input2, level + 1, min_elements, + policy, box_policy); + } + else + { + box_policy.apply(box, level + 1); + handle_two(collection1, input1, collection2, input2, policy); + } + } + } + +public : + template <typename InputCollection, typename Policy> + static inline void apply(Box const& box, + InputCollection const& collection1, index_vector_type const& input1, + InputCollection const& collection2, index_vector_type const& input2, + int level, + int min_elements, + Policy& policy, VisitBoxPolicy& box_policy) + { + box_policy.apply(box, level); + + Box lower_box, upper_box; + divide_box<Dimension>(box, lower_box, upper_box); + + index_vector_type lower1, upper1, exceeding1; + index_vector_type lower2, upper2, exceeding2; + divide_into_subsets<OverlapsPolicy>(lower_box, upper_box, collection1, + input1, lower1, upper1, exceeding1); + divide_into_subsets<OverlapsPolicy>(lower_box, upper_box, collection2, + input2, lower2, upper2, exceeding2); + + if (boost::size(exceeding1) > 0) + { + // All exceeding from 1 with 2: + handle_two(collection1, exceeding1, collection2, exceeding2, + policy); + + // All exceeding from 1 with lower and upper of 2: + handle_two(collection1, exceeding1, collection2, lower2, policy); + handle_two(collection1, exceeding1, collection2, upper2, policy); + } + if (boost::size(exceeding2) > 0) + { + // All exceeding from 2 with lower and upper of 1: + handle_two(collection1, lower1, collection2, exceeding2, policy); + handle_two(collection1, upper1, collection2, exceeding2, policy); + } + + next_level(lower_box, collection1, lower1, collection2, lower2, level, + min_elements, policy, box_policy); + next_level(upper_box, collection1, upper1, collection2, upper2, level, + min_elements, policy, box_policy); + } +}; + +}} // namespace detail::partition + +struct visit_no_policy +{ + template <typename Box> + static inline void apply(Box const&, int ) + {} +}; + +template +< + typename Box, + typename ExpandPolicy, + typename OverlapsPolicy, + typename VisitBoxPolicy = visit_no_policy +> +class partition +{ + typedef std::vector<std::size_t> index_vector_type; + + template <typename InputCollection> + static inline void expand_to_collection(InputCollection const& collection, + Box& total, index_vector_type& index_vector) + { + std::size_t index = 0; + for(typename boost::range_iterator<InputCollection const>::type it + = boost::begin(collection); + it != boost::end(collection); + ++it, ++index) + { + ExpandPolicy::apply(total, *it); + index_vector.push_back(index); + } + } + +public : + template <typename InputCollection, typename VisitPolicy> + static inline void apply(InputCollection const& collection, + VisitPolicy& visitor, + int min_elements = 16, + VisitBoxPolicy box_visitor = visit_no_policy() + ) + { + if (boost::size(collection) > min_elements) + { + index_vector_type index_vector; + Box total; + assign_inverse(total); + expand_to_collection(collection, total, index_vector); + + detail::partition::partition_one_collection + < + 0, Box, + OverlapsPolicy, + VisitBoxPolicy + >::apply(total, collection, index_vector, 0, min_elements, + visitor, box_visitor); + } + else + { + typedef typename boost::range_iterator + < + InputCollection const + >::type iterator_type; + for(iterator_type it1 = boost::begin(collection); + it1 != boost::end(collection); + ++it1) + { + iterator_type it2 = it1; + for(++it2; it2 != boost::end(collection); ++it2) + { + visitor.apply(*it1, *it2); + } + } + } + } + + template <typename InputCollection, typename VisitPolicy> + static inline void apply(InputCollection const& collection1, + InputCollection const& collection2, + VisitPolicy& visitor, + int min_elements = 16, + VisitBoxPolicy box_visitor = visit_no_policy() + ) + { + if (boost::size(collection1) > min_elements + && boost::size(collection2) > min_elements) + { + index_vector_type index_vector1, index_vector2; + Box total; + assign_inverse(total); + expand_to_collection(collection1, total, index_vector1); + expand_to_collection(collection2, total, index_vector2); + + detail::partition::partition_two_collections + < + 0, Box, OverlapsPolicy, VisitBoxPolicy + >::apply(total, + collection1, index_vector1, + collection2, index_vector2, + 0, min_elements, visitor, box_visitor); + } + else + { + typedef typename boost::range_iterator + < + InputCollection const + >::type iterator_type; + for(iterator_type it1 = boost::begin(collection1); + it1 != boost::end(collection1); + ++it1) + { + for(iterator_type it2 = boost::begin(collection2); + it2 != boost::end(collection2); + ++it2) + { + visitor.apply(*it1, *it2); + } + } + } + } + +}; + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_PARTITION_HPP diff --git a/boost/geometry/algorithms/detail/point_on_border.hpp b/boost/geometry/algorithms/detail/point_on_border.hpp new file mode 100644 index 0000000000..33177924aa --- /dev/null +++ b/boost/geometry/algorithms/detail/point_on_border.hpp @@ -0,0 +1,243 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2012 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2012 Mateusz Loskot, London, UK. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.Dimension. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_POINT_ON_BORDER_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_POINT_ON_BORDER_HPP + + +#include <cstddef> + +#include <boost/range.hpp> + +#include <boost/geometry/core/point_type.hpp> +#include <boost/geometry/core/ring_type.hpp> + +#include <boost/geometry/geometries/concepts/check.hpp> + +#include <boost/geometry/algorithms/assign.hpp> +#include <boost/geometry/algorithms/detail/disjoint.hpp> + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace point_on_border +{ + + +template<typename Point> +struct get_point +{ + static inline bool apply(Point& destination, Point const& source, bool) + { + destination = source; + return true; + } +}; + +template<typename Point, std::size_t Dimension, std::size_t DimensionCount> +struct midpoint_helper +{ + static inline bool apply(Point& p, Point const& p1, Point const& p2) + { + typename coordinate_type<Point>::type const two = 2; + set<Dimension>(p, + (get<Dimension>(p1) + get<Dimension>(p2)) / two); + return midpoint_helper<Point, Dimension + 1, DimensionCount>::apply(p, p1, p2); + } +}; + + +template <typename Point, std::size_t DimensionCount> +struct midpoint_helper<Point, DimensionCount, DimensionCount> +{ + static inline bool apply(Point& , Point const& , Point const& ) + { + return true; + } +}; + + +template<typename Point, typename Range> +struct point_on_range +{ + static inline bool apply(Point& point, Range const& range, bool midpoint) + { + const std::size_t n = boost::size(range); + if (midpoint && n > 1) + { + typedef typename boost::range_iterator + < + Range const + >::type iterator; + + iterator it = boost::begin(range); + iterator prev = it++; + while (it != boost::end(range) + && detail::equals::equals_point_point(*it, *prev)) + { + prev = it++; + } + if (it != boost::end(range)) + { + return midpoint_helper + < + Point, + 0, dimension<Point>::value + >::apply(point, *prev, *it); + } + } + + if (n > 0) + { + point = *boost::begin(range); + return true; + } + return false; + } +}; + + +template<typename Point, typename Polygon> +struct point_on_polygon +{ + static inline bool apply(Point& point, Polygon const& polygon, bool midpoint) + { + return point_on_range + < + Point, + typename ring_type<Polygon>::type + >::apply(point, exterior_ring(polygon), midpoint); + } +}; + + +template<typename Point, typename Box> +struct point_on_box +{ + static inline bool apply(Point& point, Box const& box, bool midpoint) + { + if (midpoint) + { + Point p1, p2; + detail::assign::assign_box_2d_corner<min_corner, min_corner>(box, p1); + detail::assign::assign_box_2d_corner<max_corner, min_corner>(box, p2); + midpoint_helper + < + Point, + 0, dimension<Point>::value + >::apply(point, p1, p2); + } + else + { + detail::assign::assign_box_2d_corner<min_corner, min_corner>(box, point); + } + + return true; + } +}; + + +}} // namespace detail::point_on_border +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +template +< + typename GeometryTag, + typename Point, + typename Geometry + +> +struct point_on_border +{}; + + +template<typename Point> +struct point_on_border<point_tag, Point, Point> + : detail::point_on_border::get_point<Point> +{}; + + +template<typename Point, typename Linestring> +struct point_on_border<linestring_tag, Point, Linestring> + : detail::point_on_border::point_on_range<Point, Linestring> +{}; + + +template<typename Point, typename Ring> +struct point_on_border<ring_tag, Point, Ring> + : detail::point_on_border::point_on_range<Point, Ring> +{}; + + +template<typename Point, typename Polygon> +struct point_on_border<polygon_tag, Point, Polygon> + : detail::point_on_border::point_on_polygon<Point, Polygon> +{}; + + +template<typename Point, typename Box> +struct point_on_border<box_tag, Point, Box> + : detail::point_on_border::point_on_box<Point, Box> +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +/*! +\brief Take point on a border +\ingroup overlay +\tparam Geometry geometry type. This also defines the type of the output point +\param point to assign +\param geometry geometry to take point from +\param midpoint boolean flag, true if the point should not be a vertex, but some point + in between of two vertices +\return TRUE if successful, else false. + It is only false if polygon/line have no points +\note for a polygon, it is always a point on the exterior ring +\note for take_midpoint, it is not taken from two consecutive duplicate vertices, + (unless there are no other). + */ +template <typename Point, typename Geometry> +inline bool point_on_border(Point& point, + Geometry const& geometry, + bool midpoint = false) +{ + concept::check<Point>(); + concept::check<Geometry const>(); + + typedef typename point_type<Geometry>::type point_type; + + return dispatch::point_on_border + < + typename tag<Geometry>::type, + Point, + Geometry + >::apply(point, geometry, midpoint); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_POINT_ON_BORDER_HPP diff --git a/boost/geometry/algorithms/detail/ring_identifier.hpp b/boost/geometry/algorithms/detail/ring_identifier.hpp new file mode 100644 index 0000000000..9209ee0304 --- /dev/null +++ b/boost/geometry/algorithms/detail/ring_identifier.hpp @@ -0,0 +1,70 @@ +// 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_RING_IDENTIFIER_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RING_IDENTIFIER_HPP + + +namespace boost { namespace geometry +{ + + +// Ring Identifier. It is currently: source,multi,ring +struct ring_identifier +{ + + inline ring_identifier() + : source_index(-1) + , multi_index(-1) + , ring_index(-1) + {} + + inline ring_identifier(int src, int mul, int rin) + : source_index(src) + , multi_index(mul) + , ring_index(rin) + {} + + inline bool operator<(ring_identifier const& other) const + { + return source_index != other.source_index ? source_index < other.source_index + : multi_index !=other.multi_index ? multi_index < other.multi_index + : ring_index < other.ring_index + ; + } + + inline bool operator==(ring_identifier const& other) const + { + return source_index == other.source_index + && ring_index == other.ring_index + && multi_index == other.multi_index + ; + } + +#if defined(BOOST_GEOMETRY_DEBUG_IDENTIFIER) + friend std::ostream& operator<<(std::ostream &os, ring_identifier const& ring_id) + { + os << "(s:" << ring_id.source_index; + if (ring_id.ring_index >= 0) os << ", r:" << ring_id.ring_index; + if (ring_id.multi_index >= 0) os << ", m:" << ring_id.multi_index; + os << ")"; + return os; + } +#endif + + + int source_index; + int multi_index; + int ring_index; +}; + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_RING_IDENTIFIER_HPP diff --git a/boost/geometry/algorithms/detail/sections/range_by_section.hpp b/boost/geometry/algorithms/detail/sections/range_by_section.hpp new file mode 100644 index 0000000000..ad62f232bd --- /dev/null +++ b/boost/geometry/algorithms/detail/sections/range_by_section.hpp @@ -0,0 +1,131 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2012 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2012 Mateusz Loskot, London, UK. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, 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_SECTIONS_RANGE_BY_SECTION_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_SECTIONS_RANGE_BY_SECTION_HPP + + +#include <boost/mpl/assert.hpp> +#include <boost/range.hpp> + +#include <boost/geometry/core/access.hpp> +#include <boost/geometry/core/closure.hpp> +#include <boost/geometry/core/exterior_ring.hpp> +#include <boost/geometry/core/interior_rings.hpp> + + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace section +{ + + +template <typename Range, typename Section> +struct full_section_range +{ + static inline Range const& apply(Range const& range, Section const& ) + { + return range; + } +}; + + +template <typename Polygon, typename Section> +struct full_section_polygon +{ + static inline typename ring_return_type<Polygon const>::type apply(Polygon const& polygon, Section const& section) + { + return section.ring_id.ring_index < 0 + ? geometry::exterior_ring(polygon) + : geometry::interior_rings(polygon)[section.ring_id.ring_index]; + } +}; + + +}} // namespace detail::section +#endif + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +template +< + typename Tag, + typename Geometry, + typename Section +> +struct range_by_section +{ + BOOST_MPL_ASSERT_MSG + ( + false, NOT_OR_NOT_YET_IMPLEMENTED_FOR_THIS_GEOMETRY_TYPE + , (types<Geometry>) + ); +}; + + +template <typename LineString, typename Section> +struct range_by_section<linestring_tag, LineString, Section> + : detail::section::full_section_range<LineString, Section> +{}; + + +template <typename Ring, typename Section> +struct range_by_section<ring_tag, Ring, Section> + : detail::section::full_section_range<Ring, Section> +{}; + + +template <typename Polygon, typename Section> +struct range_by_section<polygon_tag, Polygon, Section> + : detail::section::full_section_polygon<Polygon, Section> +{}; + + +} // namespace dispatch +#endif + + +/*! + \brief Get full ring (exterior, one of interiors, one from multi) + indicated by the specified section + \ingroup sectionalize + \tparam Geometry type + \tparam Section type of section to get from + \param geometry geometry to take section of + \param section structure with section + */ +template <typename Geometry, typename Section> +inline typename ring_return_type<Geometry const>::type + range_by_section(Geometry const& geometry, Section const& section) +{ + concept::check<Geometry const>(); + + return dispatch::range_by_section + < + typename tag<Geometry>::type, + Geometry, + Section + >::apply(geometry, section); +} + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_SECTIONS_RANGE_BY_SECTION_HPP diff --git a/boost/geometry/algorithms/detail/sections/sectionalize.hpp b/boost/geometry/algorithms/detail/sections/sectionalize.hpp new file mode 100644 index 0000000000..36bcbdd6e7 --- /dev/null +++ b/boost/geometry/algorithms/detail/sections/sectionalize.hpp @@ -0,0 +1,648 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2012 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2012 Mateusz Loskot, London, UK. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, 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_SECTIONS_SECTIONALIZE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_SECTIONS_SECTIONALIZE_HPP + +#include <cstddef> +#include <vector> + +#include <boost/mpl/assert.hpp> +#include <boost/range.hpp> +#include <boost/typeof/typeof.hpp> + +#include <boost/geometry/algorithms/assign.hpp> +#include <boost/geometry/algorithms/expand.hpp> + +#include <boost/geometry/algorithms/detail/ring_identifier.hpp> + +#include <boost/geometry/core/access.hpp> +#include <boost/geometry/core/closure.hpp> +#include <boost/geometry/core/exterior_ring.hpp> +#include <boost/geometry/core/point_order.hpp> + +#include <boost/geometry/geometries/concepts/check.hpp> +#include <boost/geometry/util/math.hpp> +#include <boost/geometry/views/closeable_view.hpp> +#include <boost/geometry/views/reversible_view.hpp> +#include <boost/geometry/geometries/segment.hpp> + + +namespace boost { namespace geometry +{ + + +/*! + \brief Structure containing section information + \details Section information consists of a bounding box, direction + information (if it is increasing or decreasing, per dimension), + index information (begin-end, ring, multi) and the number of + segments in this section + + \tparam Box box-type + \tparam DimensionCount number of dimensions for this section + \ingroup sectionalize + */ +template <typename Box, std::size_t DimensionCount> +struct section +{ + typedef Box box_type; + + int id; // might be obsolete now, BSG 14-03-2011 TODO decide about this + + int directions[DimensionCount]; + ring_identifier ring_id; + Box bounding_box; + + int begin_index; + int end_index; + std::size_t count; + std::size_t range_count; + bool duplicate; + int non_duplicate_index; + + inline section() + : id(-1) + , begin_index(-1) + , end_index(-1) + , count(0) + , range_count(0) + , duplicate(false) + , non_duplicate_index(-1) + { + assign_inverse(bounding_box); + for (register std::size_t i = 0; i < DimensionCount; i++) + { + directions[i] = 0; + } + } +}; + + +/*! + \brief Structure containing a collection of sections + \note Derived from a vector, proves to be faster than of deque + \note vector might be templated in the future + \ingroup sectionalize + */ +template <typename Box, std::size_t DimensionCount> +struct sections : std::vector<section<Box, DimensionCount> > +{ + typedef Box box_type; + static std::size_t const value = DimensionCount; +}; + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace sectionalize +{ + +template <typename Segment, std::size_t Dimension, std::size_t DimensionCount> +struct get_direction_loop +{ + typedef typename coordinate_type<Segment>::type coordinate_type; + + static inline void apply(Segment const& seg, + int directions[DimensionCount]) + { + coordinate_type const diff = + geometry::get<1, Dimension>(seg) - geometry::get<0, Dimension>(seg); + + coordinate_type zero = coordinate_type(); + directions[Dimension] = diff > zero ? 1 : diff < zero ? -1 : 0; + + get_direction_loop + < + Segment, Dimension + 1, DimensionCount + >::apply(seg, directions); + } +}; + +template <typename Segment, std::size_t DimensionCount> +struct get_direction_loop<Segment, DimensionCount, DimensionCount> +{ + static inline void apply(Segment const&, int [DimensionCount]) + {} +}; + +template <typename T, std::size_t Dimension, std::size_t DimensionCount> +struct copy_loop +{ + static inline void apply(T const source[DimensionCount], + T target[DimensionCount]) + { + target[Dimension] = source[Dimension]; + copy_loop<T, Dimension + 1, DimensionCount>::apply(source, target); + } +}; + +template <typename T, std::size_t DimensionCount> +struct copy_loop<T, DimensionCount, DimensionCount> +{ + static inline void apply(T const [DimensionCount], T [DimensionCount]) + {} +}; + +template <typename T, std::size_t Dimension, std::size_t DimensionCount> +struct compare_loop +{ + static inline bool apply(T const source[DimensionCount], + T const target[DimensionCount]) + { + bool const not_equal = target[Dimension] != source[Dimension]; + + return not_equal + ? false + : compare_loop + < + T, Dimension + 1, DimensionCount + >::apply(source, target); + } +}; + +template <typename T, std::size_t DimensionCount> +struct compare_loop<T, DimensionCount, DimensionCount> +{ + static inline bool apply(T const [DimensionCount], + T const [DimensionCount]) + { + + return true; + } +}; + + +template <typename Segment, std::size_t Dimension, std::size_t DimensionCount> +struct check_duplicate_loop +{ + typedef typename coordinate_type<Segment>::type coordinate_type; + + static inline bool apply(Segment const& seg) + { + if (! geometry::math::equals + ( + geometry::get<0, Dimension>(seg), + geometry::get<1, Dimension>(seg) + ) + ) + { + return false; + } + + return check_duplicate_loop + < + Segment, Dimension + 1, DimensionCount + >::apply(seg); + } +}; + +template <typename Segment, std::size_t DimensionCount> +struct check_duplicate_loop<Segment, DimensionCount, DimensionCount> +{ + static inline bool apply(Segment const&) + { + return true; + } +}; + +template <typename T, std::size_t Dimension, std::size_t DimensionCount> +struct assign_loop +{ + static inline void apply(T dims[DimensionCount], int const value) + { + dims[Dimension] = value; + assign_loop<T, Dimension + 1, DimensionCount>::apply(dims, value); + } +}; + +template <typename T, std::size_t DimensionCount> +struct assign_loop<T, DimensionCount, DimensionCount> +{ + static inline void apply(T [DimensionCount], int const) + { + } +}; + +/// @brief Helper class to create sections of a part of a range, on the fly +template +< + typename Range, // Can be closeable_view + typename Point, + typename Sections, + std::size_t DimensionCount, + std::size_t MaxCount +> +struct sectionalize_part +{ + typedef model::referring_segment<Point const> segment_type; + typedef typename boost::range_value<Sections>::type section_type; + + typedef typename boost::range_iterator<Range const>::type iterator_type; + + static inline void apply(Sections& sections, section_type& section, + int& index, int& ndi, + Range const& range, + ring_identifier ring_id) + { + if (boost::size(range) <= index) + { + return; + } + + if (index == 0) + { + ndi = 0; + } + + iterator_type it = boost::begin(range); + it += index; + + for(iterator_type previous = it++; + it != boost::end(range); + ++previous, ++it, index++) + { + segment_type segment(*previous, *it); + + int direction_classes[DimensionCount] = {0}; + get_direction_loop + < + segment_type, 0, DimensionCount + >::apply(segment, direction_classes); + + // if "dir" == 0 for all point-dimensions, it is duplicate. + // Those sections might be omitted, if wished, lateron + bool duplicate = false; + + if (direction_classes[0] == 0) + { + // Recheck because ALL dimensions should be checked, + // not only first one. + // (DimensionCount might be < dimension<P>::value) + if (check_duplicate_loop + < + segment_type, 0, geometry::dimension<Point>::type::value + >::apply(segment) + ) + { + duplicate = true; + + // Change direction-info to force new section + // Note that wo consecutive duplicate segments will generate + // only one duplicate-section. + // Actual value is not important as long as it is not -1,0,1 + assign_loop + < + int, 0, DimensionCount + >::apply(direction_classes, -99); + } + } + + if (section.count > 0 + && (!compare_loop + < + int, 0, DimensionCount + >::apply(direction_classes, section.directions) + || section.count > MaxCount + ) + ) + { + sections.push_back(section); + section = section_type(); + } + + if (section.count == 0) + { + section.begin_index = index; + section.ring_id = ring_id; + section.duplicate = duplicate; + section.non_duplicate_index = ndi; + section.range_count = boost::size(range); + + copy_loop + < + int, 0, DimensionCount + >::apply(direction_classes, section.directions); + geometry::expand(section.bounding_box, *previous); + } + + geometry::expand(section.bounding_box, *it); + section.end_index = index + 1; + section.count++; + if (! duplicate) + { + ndi++; + } + } + } +}; + + +template +< + typename Range, closure_selector Closure, bool Reverse, + typename Point, + typename Sections, + std::size_t DimensionCount, + std::size_t MaxCount +> +struct sectionalize_range +{ + typedef typename closeable_view<Range const, Closure>::type cview_type; + typedef typename reversible_view + < + cview_type const, + Reverse ? iterate_reverse : iterate_forward + >::type view_type; + + static inline void apply(Range const& range, Sections& sections, + ring_identifier ring_id) + { + typedef model::referring_segment<Point const> segment_type; + + cview_type cview(range); + view_type view(cview); + + std::size_t const n = boost::size(view); + if (n == 0) + { + // Zero points, no section + return; + } + + if (n == 1) + { + // Line with one point ==> no sections + return; + } + + int index = 0; + int ndi = 0; // non duplicate index + + typedef typename boost::range_value<Sections>::type section_type; + section_type section; + + sectionalize_part + < + view_type, Point, Sections, + DimensionCount, MaxCount + >::apply(sections, section, index, ndi, + view, ring_id); + + // Add last section if applicable + if (section.count > 0) + { + sections.push_back(section); + } + } +}; + +template +< + typename Polygon, + bool Reverse, + typename Sections, + std::size_t DimensionCount, + std::size_t MaxCount +> +struct sectionalize_polygon +{ + static inline void apply(Polygon const& poly, Sections& sections, + ring_identifier ring_id) + { + typedef typename point_type<Polygon>::type point_type; + typedef typename ring_type<Polygon>::type ring_type; + typedef sectionalize_range + < + ring_type, closure<Polygon>::value, Reverse, + point_type, Sections, DimensionCount, MaxCount + > sectionalizer_type; + + ring_id.ring_index = -1; + sectionalizer_type::apply(exterior_ring(poly), sections, ring_id);//-1, multi_index); + + ring_id.ring_index++; + typename interior_return_type<Polygon const>::type rings + = interior_rings(poly); + for (BOOST_AUTO_TPL(it, boost::begin(rings)); it != boost::end(rings); + ++it, ++ring_id.ring_index) + { + sectionalizer_type::apply(*it, sections, ring_id); + } + } +}; + +template +< + typename Box, + typename Sections, + std::size_t DimensionCount, + std::size_t MaxCount +> +struct sectionalize_box +{ + static inline void apply(Box const& box, Sections& sections, ring_identifier const& ring_id) + { + typedef typename point_type<Box>::type point_type; + + assert_dimension<Box, 2>(); + + // Add all four sides of the 2D-box as separate section. + // Easiest is to convert it to a polygon. + // However, we don't have the polygon type + // (or polygon would be a helper-type). + // Therefore we mimic a linestring/std::vector of 5 points + + // TODO: might be replaced by assign_box_corners_oriented + // or just "convert" + point_type ll, lr, ul, ur; + geometry::detail::assign_box_corners(box, ll, lr, ul, ur); + + std::vector<point_type> points; + points.push_back(ll); + points.push_back(ul); + points.push_back(ur); + points.push_back(lr); + points.push_back(ll); + + sectionalize_range + < + std::vector<point_type>, closed, false, + point_type, + Sections, + DimensionCount, + MaxCount + >::apply(points, sections, ring_id); + } +}; + +template <typename Sections> +inline void set_section_unique_ids(Sections& sections) +{ + // Set ID's. + int index = 0; + for (typename boost::range_iterator<Sections>::type it = boost::begin(sections); + it != boost::end(sections); + ++it) + { + it->id = index++; + } +} + + +}} // namespace detail::sectionalize +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template +< + typename Tag, + typename Geometry, + bool Reverse, + typename Sections, + std::size_t DimensionCount, + std::size_t MaxCount +> +struct sectionalize +{ + BOOST_MPL_ASSERT_MSG + ( + false, NOT_OR_NOT_YET_IMPLEMENTED_FOR_THIS_GEOMETRY_TYPE + , (types<Geometry>) + ); +}; + +template +< + typename Box, + bool Reverse, + typename Sections, + std::size_t DimensionCount, + std::size_t MaxCount +> +struct sectionalize<box_tag, Box, Reverse, Sections, DimensionCount, MaxCount> + : detail::sectionalize::sectionalize_box + < + Box, + Sections, + DimensionCount, + MaxCount + > +{}; + +template +< + typename LineString, + typename Sections, + std::size_t DimensionCount, + std::size_t MaxCount +> +struct sectionalize + < + linestring_tag, + LineString, + false, + Sections, + DimensionCount, + MaxCount + > + : detail::sectionalize::sectionalize_range + < + LineString, closed, false, + typename point_type<LineString>::type, + Sections, + DimensionCount, + MaxCount + > +{}; + +template +< + typename Ring, + bool Reverse, + typename Sections, + std::size_t DimensionCount, + std::size_t MaxCount +> +struct sectionalize<ring_tag, Ring, Reverse, Sections, DimensionCount, MaxCount> + : detail::sectionalize::sectionalize_range + < + Ring, geometry::closure<Ring>::value, Reverse, + typename point_type<Ring>::type, + Sections, + DimensionCount, + MaxCount + > +{}; + +template +< + typename Polygon, + bool Reverse, + typename Sections, + std::size_t DimensionCount, + std::size_t MaxCount +> +struct sectionalize<polygon_tag, Polygon, Reverse, Sections, DimensionCount, MaxCount> + : detail::sectionalize::sectionalize_polygon + < + Polygon, Reverse, Sections, DimensionCount, MaxCount + > +{}; + +} // namespace dispatch +#endif + + +/*! + \brief Split a geometry into monotonic sections + \ingroup sectionalize + \tparam Geometry type of geometry to check + \tparam Sections type of sections to create + \param geometry geometry to create sections from + \param sections structure with sections + \param source_index index to assign to the ring_identifiers + */ +template<bool Reverse, typename Geometry, typename Sections> +inline void sectionalize(Geometry const& geometry, Sections& sections, int source_index = 0) +{ + concept::check<Geometry const>(); + + // TODO: review use of this constant (see below) as causing problems with GCC 4.6 --mloskot + // A maximum of 10 segments per section seems to give the fastest results + //static std::size_t const max_segments_per_section = 10; + typedef dispatch::sectionalize + < + typename tag<Geometry>::type, + Geometry, + Reverse, + Sections, + Sections::value, + 10 // TODO: max_segments_per_section + > sectionalizer_type; + + sections.clear(); + ring_identifier ring_id; + ring_id.source_index = source_index; + sectionalizer_type::apply(geometry, sections, ring_id); + detail::sectionalize::set_section_unique_ids(sections); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_SECTIONS_SECTIONALIZE_HPP diff --git a/boost/geometry/algorithms/detail/throw_on_empty_input.hpp b/boost/geometry/algorithms/detail/throw_on_empty_input.hpp new file mode 100644 index 0000000000..62328a0d87 --- /dev/null +++ b/boost/geometry/algorithms/detail/throw_on_empty_input.hpp @@ -0,0 +1,53 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2012 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2012 Mateusz Loskot, London, UK. + +// 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_THROW_ON_EMPTY_INPUT_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_THROW_ON_EMPTY_INPUT_HPP + +#include <boost/geometry/core/exception.hpp> +#include <boost/geometry/algorithms/num_points.hpp> + +// BSG 2012-02-06: we use this currently only for distance. +// For other scalar results area,length,perimeter it is commented on purpose. +// Reason is that for distance there is no other choice. distance of two +// empty geometries (or one empty) should NOT return any value. +// But for area it is no problem to be 0. +// Suppose: area(intersection(a,b)). We (probably) don't want a throw there... + +// So decided that at least for Boost 1.49 this is commented for +// scalar results, except distance. + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail +{ + +template <typename Geometry> +inline void throw_on_empty_input(Geometry const& geometry) +{ +#if ! defined(BOOST_GEOMETRY_EMPTY_INPUT_NO_THROW) + if (geometry::num_points(geometry) == 0) + { + throw empty_input_exception(); + } +#endif +} + +} // namespace detail +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_THROW_ON_EMPTY_INPUT_HPP + |