// 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-2021. // Modifications copyright (c) 2017-2021 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 namespace boost { namespace geometry { template struct collected_vector_cartesian { typedef T type; inline collected_vector_cartesian() {} inline collected_vector_cartesian(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_cartesian(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(util::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_cartesian 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_cartesian const& other) const { return same_direction(other); } // For std::equals inline bool operator==(collected_vector_cartesian const& other) const { return math::equals(x, other.x) && math::equals(y, other.y) && same_direction(other); } private: inline bool same_direction(collected_vector_cartesian 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, not for some user defined types) 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_spherical { typedef T type; typedef model::point vector_type; collected_vector_spherical() {} collected_vector_spherical(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); cross = direction = cross_product(prev, next); } bool normalize() { T const 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_spherical 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_spherical const& other) const { return formula::sph_side_value(cross, other.next) == 0; } // For std::equals bool operator==(collected_vector_spherical 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 // NOTE: alternative would be to equal-compare direction's coordinates // or to check if dot product of directions is equal to 1. bool is_collinear(collected_vector_spherical const& other) const { return formula::sph_side_value(cross, other.prev) == 0 && formula::sph_side_value(cross, other.next) == 0; } Point origin; // used for sorting and equality check vector_type direction; // used for sorting, only in operator< vector_type cross; // used for sorting, used for collinearity check vector_type prev; // used for collinearity check, only in operator== vector_type next; // used for collinearity check }; // Version for spherical polar template struct collected_vector_polar : public collected_vector_spherical { using type = T; using base_point_type = model::point>; using base_type = collected_vector_spherical; collected_vector_polar() {} collected_vector_polar(Point const& p1, Point const& p2) : base_type(to_equatorial(p1), to_equatorial(p2)) {} private: static base_point_type to_equatorial(Point const& p) { using coord_type = typename coordinate_type::type; using constants = math::detail::constants_on_spheroid < coord_type, typename coordinate_system::type::units > ; constexpr coord_type pi_2 = constants::half_period() / 2; base_point_type res; set<0>(res, get<0>(p)); set<1>(res, pi_2 - get<1>(p)); return res; } }; // TODO: implement collected_vector type for geographic #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) { apply_impl(collection, detail::closed_clockwise_view(range)); } private: template static inline void apply_impl(Collection& collection, ClosedClockwiseRange const& range) { if (boost::size(range) < 2) { return; } auto c_old_size = boost::size(collection); bool is_first = true; auto it = boost::begin(range); for (auto 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 if (boost::size(collection) > c_old_size + 1) { auto 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)); auto const& rings = interior_rings(polygon); for (auto 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 (auto 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