319 lines
9.3 KiB
C++
319 lines
9.3 KiB
C++
// 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 <boost/geometry/core/assert.hpp>
|
|
|
|
#include <boost/geometry/arithmetic/arithmetic.hpp>
|
|
#include <boost/geometry/algorithms/detail/overlay/segment_identifier.hpp>
|
|
#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
|
|
#include <boost/geometry/iterators/closing_iterator.hpp>
|
|
#include <boost/geometry/iterators/ever_circling_iterator.hpp>
|
|
#include <boost/geometry/strategies/side.hpp>
|
|
|
|
namespace boost { namespace geometry
|
|
{
|
|
|
|
|
|
#ifndef DOXYGEN_NO_DETAIL
|
|
namespace detail
|
|
{
|
|
|
|
// TODO: move this to /util/
|
|
template <typename T>
|
|
inline std::pair<T, T> 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 <typename Vector>
|
|
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 <typename Vector>
|
|
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 <typename Point, typename SideStrategy>
|
|
struct angle_less
|
|
{
|
|
typedef Point vector_type;
|
|
|
|
angle_less(Point const& origin, SideStrategy const& strategy)
|
|
: m_origin(origin)
|
|
, m_strategy(strategy)
|
|
{}
|
|
|
|
template <typename Angle>
|
|
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 <typename Point, typename SideStrategy>
|
|
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 <typename Angle>
|
|
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 <typename AngleCollection, typename Turns>
|
|
inline void get_left_turns(AngleCollection const& sorted_angles,
|
|
Turns& turns)
|
|
{
|
|
std::set<std::size_t> good_incoming;
|
|
std::set<std::size_t> good_outgoing;
|
|
|
|
for (typename boost::range_iterator<AngleCollection const>::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<AngleCollection const>::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 <typename Point, typename AngleCollection, typename SideStrategy>
|
|
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<Point, SideStrategy> comparator(origin, strategy);
|
|
typename boost::range_iterator<AngleCollection>::type it = sorted.begin();
|
|
|
|
std::size_t cluster_index = 0;
|
|
it->cluster_index = cluster_index;
|
|
typename boost::range_iterator<AngleCollection>::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 <typename AngleCollection>
|
|
inline void block_turns(AngleCollection& sorted, std::size_t cluster_size)
|
|
{
|
|
BOOST_GEOMETRY_ASSERT(boost::size(sorted) >= 4u && cluster_size > 0);
|
|
|
|
std::vector<std::pair<bool, bool> > directions;
|
|
for (std::size_t i = 0; i < cluster_size; i++)
|
|
{
|
|
directions.push_back(std::make_pair(false, false));
|
|
}
|
|
|
|
for (typename boost::range_iterator<AngleCollection const>::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<AngleCollection>::type it = sorted.begin();
|
|
it != sorted.end(); ++it)
|
|
{
|
|
signed_size_type cluster_index = static_cast<signed_size_type>(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<signed_size_type>(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 <typename AngleCollection, typename Point>
|
|
inline bool has_rounding_issues(AngleCollection const& angles, Point const& origin)
|
|
{
|
|
for (typename boost::range_iterator<AngleCollection const>::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
|