// Boost.Geometry (aka GGL, Generic Geometry Library) // Copyright (c) 2007-2011 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 #include #include #include #include #include #include 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::type tag1; typedef typename geometry::tag::type tag2; int code = -1; switch (ring_id.source_index) { case 0 : code = point_in_ring(item2.point, get_ring::apply(ring_id, geometry1)); break; case 1 : code = point_in_ring(item2.point, get_ring::apply(ring_id, geometry2)); break; case 2 : code = point_in_ring(item2.point, get_ring::apply(ring_id, collection)); break; } return code == 1; } template struct ring_info_helper { typedef typename geometry::default_area_result::type area_type; ring_identifier id; area_type real_area; area_type abs_area; model::box 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(abs(a)) {} }; struct ring_info_helper_get_box { template static inline void apply(Box& total, InputItem const& item) { geometry::expand(total, item.envelope); } }; struct ring_info_helper_ovelaps_box { template static inline bool apply(Box const& box, InputItem const& item) { return ! geometry::detail::disjoint::disjoint_box_box(box, item.envelope); } }; template 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 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::type tag1; typedef typename geometry::tag::type tag2; typedef typename RingMap::mapped_type ring_info_type; typedef typename ring_info_type::point_type point_type; typedef model::box box_type; typedef typename RingMap::iterator map_iterator_type; { typedef ring_info_helper helper; typedef std::vector vector_type; typedef typename boost::range_iterator::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::apply(it->first, geometry1), item.envelope); break; case 1 : geometry::envelope(get_ring::apply(it->first, geometry2), item.envelope); break; case 2 : geometry::envelope(get_ring::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