// Boost.Geometry (aka GGL, Generic Geometry Library) // Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. // Copyright (c) 2008-2014 Bruno Lalande, Paris, France. // Copyright (c) 2009-2014 Mateusz Loskot, London, UK. // Copyright (c) 2014-2017 Adam Wulkiewicz, Lodz, Poland. // This file was modified by Oracle on 2017, 2019. // Modifications copyright (c) 2017, 2019 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace boost { namespace geometry { // Since these vectors (though ray would be a better name) are used in the // implementation of equals() for Areal geometries the internal representation // should be consistent with the side strategy. template < typename T, typename Geometry, typename SideStrategy, typename CSTag = typename cs_tag::type > struct collected_vector : nyi::not_implemented_tag {}; // compatible with side_by_triangle cartesian strategy template struct collected_vector < T, Geometry, strategy::side::side_by_triangle, CSTag > { 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(dx) //, dy_0(dy) {} template inline collected_vector(Point const& p1, Point const& p2) : x(get<0>(p1)) , y(get<1>(p1)) , dx(get<0>(p2) - x) , dy(get<1>(p2) - y) //, dx_0(dx) //, dy_0(dy) {} bool normalize() { T magnitude = math::sqrt( boost::numeric_cast(dx * dx + dy * dy)); // NOTE: shouldn't here math::equals() be called? if (magnitude > 0) { dx /= magnitude; dy /= magnitude; return true; } return false; } // For sorting inline bool operator<(collected_vector 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 next_is_collinear(collected_vector const& other) const { return same_direction(other); } // For std::equals inline bool operator==(collected_vector const& other) const { return math::equals(x, other.x) && math::equals(y, other.y) && same_direction(other); } private: inline bool same_direction(collected_vector 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); } T x, y; T dx, dy; //T dx_0, dy_0; }; // Compatible with spherical_side_formula which currently // is the default spherical_equatorial and geographic strategy // so CSTag is spherical_equatorial_tag or geographic_tag template struct collected_vector < T, Geometry, strategy::side::spherical_side_formula, CSTag > { typedef T type; typedef typename geometry::detail::cs_angular_units::type units_type; typedef model::point > point_type; typedef model::point vector_type; collected_vector() {} template collected_vector(Point const& p1, Point const& p2) : origin(get<0>(p1), get<1>(p1)) { origin = detail::return_normalized( origin, strategy::normalize::spherical_point()); using namespace geometry::formula; prev = sph_to_cart3d(p1); next = sph_to_cart3d(p2); direction = cross_product(prev, next); } bool normalize() { T magnitude_sqr = dot_product(direction, direction); // NOTE: shouldn't here math::equals() be called? if (magnitude_sqr > 0) { divide_value(direction, math::sqrt(magnitude_sqr)); return true; } return false; } bool operator<(collected_vector const& other) const { if (math::equals(get<0>(origin), get<0>(other.origin))) { if (math::equals(get<1>(origin), get<1>(other.origin))) { if (math::equals(get<0>(direction), get<0>(other.direction))) { if (math::equals(get<1>(direction), get<1>(other.direction))) { return get<2>(direction) < get<2>(other.direction); } return get<1>(direction) < get<1>(other.direction); } return get<0>(direction) < get<0>(other.direction); } return get<1>(origin) < get<1>(other.origin); } return get<0>(origin) < get<0>(other.origin); } // For consistency with side and intersection strategies used by relops // IMPORTANT: this method should be called for previous vector // and next vector should be passed as parameter bool next_is_collinear(collected_vector const& other) const { return formula::sph_side_value(direction, other.next) == 0; } // For std::equals bool operator==(collected_vector const& other) const { return math::equals(get<0>(origin), get<0>(other.origin)) && math::equals(get<1>(origin), get<1>(other.origin)) && is_collinear(other); } private: // For consistency with side and intersection strategies used by relops bool is_collinear(collected_vector const& other) const { return formula::sph_side_value(direction, other.prev) == 0 && formula::sph_side_value(direction, other.next) == 0; } /*bool same_direction(collected_vector const& other) const { return math::equals_with_epsilon(get<0>(direction), get<0>(other.direction)) && math::equals_with_epsilon(get<1>(direction), get<1>(other.direction)) && math::equals_with_epsilon(get<2>(direction), get<2>(other.direction)); }*/ point_type origin; // used for sorting and equality check vector_type direction; // used for sorting, only in operator< vector_type prev; // used for collinearity check, only in operator== vector_type next; // used for collinearity check }; // Specialization for spherical polar template struct collected_vector < T, Geometry, strategy::side::spherical_side_formula, spherical_polar_tag > : public collected_vector < T, Geometry, strategy::side::spherical_side_formula, spherical_equatorial_tag > { typedef collected_vector < T, Geometry, strategy::side::spherical_side_formula, spherical_equatorial_tag > base_type; collected_vector() {} template collected_vector(Point const& p1, Point const& p2) : base_type(to_equatorial(p1), to_equatorial(p2)) {} private: template Point to_equatorial(Point const& p) { typedef typename coordinate_type::type coord_type; typedef math::detail::constants_on_spheroid < coord_type, typename coordinate_system::type::units > constants; coord_type const pi_2 = constants::half_period() / 2; Point res = p; set<1>(res, pi_2 - get<1>(p)); return res; } }; // TODO: specialize collected_vector for geographic_tag #ifndef DOXYGEN_NO_DETAIL namespace detail { namespace collect_vectors { template struct range_collect_vectors { typedef typename boost::range_value::type item_type; typedef typename item_type::type calculation_type; static inline void apply(Collection& collection, Range const& range) { typedef geometry::detail::normalized_view < Range const > normalized_range_type; apply_impl(collection, normalized_range_type(range)); } private: template static inline void apply_impl(Collection& collection, NormalizedRange const& range) { if (boost::size(range) < 2) { return; } typedef typename boost::range_size::type collection_size_t; collection_size_t c_old_size = boost::size(collection); typedef typename boost::range_iterator::type iterator; bool is_first = true; iterator it = boost::begin(range); for (iterator prev = it++; it != boost::end(range); prev = it++) { typename boost::range_value::type v(*prev, *it); // Normalize the vector -> this results in points+direction // and is comparible between geometries // Avoid non-duplicate points (AND division by zero) if (v.normalize()) { // Avoid non-direction changing points if (is_first || ! collection.back().next_is_collinear(v)) { collection.push_back(v); } is_first = false; } } // If first one has same direction as last one, remove first one collection_size_t collected_count = boost::size(collection) - c_old_size; if ( collected_count > 1 ) { typedef typename boost::range_iterator::type c_iterator; c_iterator first = range::pos(collection, c_old_size); if (collection.back().next_is_collinear(*first) ) { //collection.erase(first); // O(1) instead of O(N) *first = collection.back(); collection.pop_back(); } } } }; // Default version (cartesian) template ::type> struct box_collect_vectors { // Calculate on coordinate type, but if it is integer, // then use double typedef typename boost::range_value::type item_type; typedef typename item_type::type calculation_type; static inline void apply(Collection& collection, Box const& box) { typename point_type::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::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)); } }; // NOTE: This is not fully correct because Box in spherical and geographic // cordinate systems cannot be seen as Polygon template struct box_collect_vectors { static inline void apply(Collection& collection, Box const& box) { typename point_type::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::type item; collection.push_back(item(lower_left, upper_left)); collection.push_back(item(upper_left, upper_right)); collection.push_back(item(upper_right, lower_right)); collection.push_back(item(lower_right, lower_left)); } }; template struct box_collect_vectors : box_collect_vectors {}; template struct box_collect_vectors : box_collect_vectors {}; template struct polygon_collect_vectors { static inline void apply(Collection& collection, Polygon const& polygon) { typedef typename geometry::ring_type::type ring_type; typedef range_collect_vectors per_range; per_range::apply(collection, exterior_ring(polygon)); typename interior_return_type::type rings = interior_rings(polygon); for (typename detail::interior_iterator::type it = boost::begin(rings); it != boost::end(rings); ++it) { per_range::apply(collection, *it); } } }; template struct multi_collect_vectors { static inline void apply(Collection& collection, MultiGeometry const& multi) { for (typename boost::range_iterator::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 struct collect_vectors : detail::collect_vectors::box_collect_vectors {}; template struct collect_vectors : detail::collect_vectors::range_collect_vectors {}; template struct collect_vectors : detail::collect_vectors::range_collect_vectors {}; template struct collect_vectors : detail::collect_vectors::polygon_collect_vectors {}; template struct collect_vectors : detail::collect_vectors::multi_collect_vectors < MultiPolygon, Collection, detail::collect_vectors::polygon_collect_vectors < typename boost::range_value::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 inline void collect_vectors(Collection& collection, Geometry const& geometry) { concepts::check(); dispatch::collect_vectors < typename tag::type, Collection, Geometry >::apply(collection, geometry); } }} // namespace boost::geometry #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_EQUALS_COLLECT_VECTORS_HPP