// Boost.Geometry (aka GGL, Generic Geometry Library) // Copyright (c) 2012-2014 Barend Gehrels, Amsterdam, the Netherlands. // This file was modified by Oracle on 2017. // Modifications copyright (c) 2017, Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_GET_LEFT_TURNS_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_GET_LEFT_TURNS_HPP #include #include #include #include #include #include #include namespace boost { namespace geometry { #ifndef DOXYGEN_NO_DETAIL namespace detail { // TODO: move this to /util/ template inline std::pair ordered_pair(T const& first, T const& second) { return first < second ? std::make_pair(first, second) : std::make_pair(second, first); } namespace left_turns { template inline int get_quadrant(Vector const& vector) { // Return quadrant as layouted in the code below: // 3 | 0 // ----- // 2 | 1 return geometry::get<1>(vector) >= 0 ? (geometry::get<0>(vector) < 0 ? 3 : 0) : (geometry::get<0>(vector) < 0 ? 2 : 1) ; } template inline int squared_length(Vector const& vector) { return geometry::get<0>(vector) * geometry::get<0>(vector) + geometry::get<1>(vector) * geometry::get<1>(vector) ; } template struct angle_less { typedef Point vector_type; angle_less(Point const& origin, SideStrategy const& strategy) : m_origin(origin) , m_strategy(strategy) {} template inline bool operator()(Angle const& p, Angle const& q) const { // Vector origin -> p and origin -> q vector_type pv = p.point; vector_type qv = q.point; geometry::subtract_point(pv, m_origin); geometry::subtract_point(qv, m_origin); int const quadrant_p = get_quadrant(pv); int const quadrant_q = get_quadrant(qv); if (quadrant_p != quadrant_q) { return quadrant_p < quadrant_q; } // Same quadrant, check if p is located left of q int const side = m_strategy.apply(m_origin, q.point, p.point); if (side != 0) { return side == 1; } // Collinear, check if one is incoming, incoming angles come first if (p.incoming != q.incoming) { return int(p.incoming) < int(q.incoming); } // Same quadrant/side/direction, return longest first // TODO: maybe not necessary, decide this int const length_p = squared_length(pv); int const length_q = squared_length(qv); if (length_p != length_q) { return squared_length(pv) > squared_length(qv); } // They are still the same. Just compare on seg_id return p.seg_id < q.seg_id; } private: Point m_origin; SideStrategy m_strategy; }; template struct angle_equal_to { typedef Point vector_type; inline angle_equal_to(Point const& origin, SideStrategy const& strategy) : m_origin(origin) , m_strategy(strategy) {} template inline bool operator()(Angle const& p, Angle const& q) const { // Vector origin -> p and origin -> q vector_type pv = p.point; vector_type qv = q.point; geometry::subtract_point(pv, m_origin); geometry::subtract_point(qv, m_origin); if (get_quadrant(pv) != get_quadrant(qv)) { return false; } // Same quadrant, check if p/q are collinear int const side = m_strategy.apply(m_origin, q.point, p.point); return side == 0; } private: Point m_origin; SideStrategy m_strategy; }; template inline void get_left_turns(AngleCollection const& sorted_angles, Turns& turns) { std::set good_incoming; std::set good_outgoing; for (typename boost::range_iterator::type it = sorted_angles.begin(); it != sorted_angles.end(); ++it) { if (!it->blocked) { if (it->incoming) { good_incoming.insert(it->turn_index); } else { good_outgoing.insert(it->turn_index); } } } if (good_incoming.empty() || good_outgoing.empty()) { return; } for (typename boost::range_iterator::type it = sorted_angles.begin(); it != sorted_angles.end(); ++it) { if (good_incoming.count(it->turn_index) == 0 || good_outgoing.count(it->turn_index) == 0) { turns[it->turn_index].remove_on_multi = true; } } } //! Returns the number of clusters template inline std::size_t assign_cluster_indices(AngleCollection& sorted, Point const& origin, SideStrategy const& strategy) { // Assign same cluster_index for all turns in same direction BOOST_GEOMETRY_ASSERT(boost::size(sorted) >= 4u); angle_equal_to comparator(origin, strategy); typename boost::range_iterator::type it = sorted.begin(); std::size_t cluster_index = 0; it->cluster_index = cluster_index; typename boost::range_iterator::type previous = it++; for (; it != sorted.end(); ++it) { if (!comparator(*previous, *it)) { cluster_index++; previous = it; } it->cluster_index = cluster_index; } return cluster_index + 1; } template inline void block_turns(AngleCollection& sorted, std::size_t cluster_size) { BOOST_GEOMETRY_ASSERT(boost::size(sorted) >= 4u && cluster_size > 0); std::vector > directions; for (std::size_t i = 0; i < cluster_size; i++) { directions.push_back(std::make_pair(false, false)); } for (typename boost::range_iterator::type it = sorted.begin(); it != sorted.end(); ++it) { if (it->incoming) { directions[it->cluster_index].first = true; } else { directions[it->cluster_index].second = true; } } for (typename boost::range_iterator::type it = sorted.begin(); it != sorted.end(); ++it) { signed_size_type cluster_index = static_cast(it->cluster_index); signed_size_type previous_index = cluster_index - 1; if (previous_index < 0) { previous_index = cluster_size - 1; } signed_size_type next_index = cluster_index + 1; if (next_index >= static_cast(cluster_size)) { next_index = 0; } if (directions[cluster_index].first && directions[cluster_index].second) { it->blocked = true; } else if (!directions[cluster_index].first && directions[cluster_index].second && directions[previous_index].second) { // Only outgoing, previous was also outgoing: block this one it->blocked = true; } else if (directions[cluster_index].first && !directions[cluster_index].second && !directions[previous_index].first && directions[previous_index].second) { // Only incoming, previous was only outgoing: block this one it->blocked = true; } else if (directions[cluster_index].first && !directions[cluster_index].second && directions[next_index].first && !directions[next_index].second) { // Only incoming, next also incoming, block this one it->blocked = true; } } } #if defined(BOOST_GEOMETRY_BUFFER_ENLARGED_CLUSTERS) template inline bool has_rounding_issues(AngleCollection const& angles, Point const& origin) { for (typename boost::range_iterator::type it = angles.begin(); it != angles.end(); ++it) { // Vector origin -> p and origin -> q typedef Point vector_type; vector_type v = it->point; geometry::subtract_point(v, origin); return geometry::math::abs(geometry::get<0>(v)) <= 1 || geometry::math::abs(geometry::get<1>(v)) <= 1 ; } return false; } #endif } // namespace left_turns } // namespace detail #endif // DOXYGEN_NO_DETAIL }} // namespace boost::geometry #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_GET_LEFT_TURNS_HPP