Sindbad~EG File Manager
// Boost.Geometry (aka GGL, Generic Geometry Library)
// Copyright (c) 2015 Barend Gehrels, Amsterdam, the Netherlands.
// Copyright (c) 2017-2023 Adam Wulkiewicz, Lodz, Poland.
// This file was modified by Oracle on 2017-2023.
// Modifications copyright (c) 2017-2023 Oracle and/or its affiliates.
// Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle
// 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_OVERLAY_SORT_BY_SIDE_HPP
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SORT_BY_SIDE_HPP
#include <algorithm>
#include <map>
#include <set>
#include <vector>
#include <boost/geometry/algorithms/detail/overlay/approximately_equals.hpp>
#include <boost/geometry/algorithms/detail/overlay/copy_segment_point.hpp>
#include <boost/geometry/algorithms/detail/overlay/get_ring.hpp>
#include <boost/geometry/algorithms/detail/direction_code.hpp>
#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
#include <boost/geometry/util/constexpr.hpp>
#include <boost/geometry/util/math.hpp>
#include <boost/geometry/util/select_coordinate_type.hpp>
#include <boost/geometry/util/select_most_precise.hpp>
namespace boost { namespace geometry
{
#ifndef DOXYGEN_NO_DETAIL
namespace detail { namespace overlay { namespace sort_by_side
{
// From means: from intersecting-segment-begin-point to cluster
// To means: from cluster to intersecting-segment-end-point
enum direction_type { dir_unknown = -1, dir_from = 0, dir_to = 1 };
using rank_type = signed_size_type;
// Point-wrapper, adding some properties
template <typename Point>
struct ranked_point
{
ranked_point(Point const& p, signed_size_type ti, int oi,
direction_type d, operation_type op, segment_identifier const& si)
: point(p)
, turn_index(ti)
, operation_index(oi)
, direction(d)
, operation(op)
, seg_id(si)
{}
using point_type = Point;
Point point;
rank_type rank{0};
signed_size_type zone{-1}; // index of closed zone, in uu turn there would be 2 zones
signed_size_type turn_index{-1};
int operation_index{-1}; // 0,1
direction_type direction{dir_unknown};
// The number of polygons on the left side
std::size_t count_left{0};
// The number of polygons on the right side
std::size_t count_right{0};
operation_type operation{operation_none};
segment_identifier seg_id;
};
struct less_by_turn_index
{
template <typename T>
inline bool operator()(T const& first, T const& second) const
{
return std::tie(first.turn_index, first.index)
< std::tie(second.turn_index, second.index);
}
};
struct less_by_index
{
template <typename T>
inline bool operator()(T const& first, T const& second) const
{
// First order by direction (from/to)
// Then by turn index
// This can also be the same (for example in buffer), but seg_id is
// never the same
// (Length might be considered too)
return std::tie(first.direction, first.turn_index, first.seg_id)
< std::tie(second.direction, second.turn_index, second.seg_id);
}
};
struct less_false
{
template <typename T>
inline bool operator()(T const&, T const& ) const
{
return false;
}
};
template <typename PointOrigin, typename PointTurn, typename Strategy, typename LessOnSame, typename Compare>
struct less_by_side
{
less_by_side(PointOrigin const& p1, PointTurn const& p2, Strategy const& strategy)
: m_origin(p1)
, m_turn_point(p2)
, m_strategy(strategy)
{}
template <typename T>
inline bool operator()(T const& first, T const& second) const
{
using cs_tag = typename Strategy::cs_tag;
LessOnSame on_same;
Compare compare;
auto const side_strategy = m_strategy.side();
int const side_first = side_strategy.apply(m_origin, m_turn_point, first.point);
int const side_second = side_strategy.apply(m_origin, m_turn_point, second.point);
if (side_first == 0 && side_second == 0)
{
// Both collinear. They might point into different directions: <------*------>
// If so, order the one going backwards as the very first.
int const first_code = direction_code<cs_tag>(m_origin, m_turn_point, first.point);
int const second_code = direction_code<cs_tag>(m_origin, m_turn_point, second.point);
// Order by code, backwards first, then forward.
return first_code != second_code
? first_code < second_code
: on_same(first, second)
;
}
else if (side_first == 0
&& direction_code<cs_tag>(m_origin, m_turn_point, first.point) == -1)
{
// First collinear and going backwards.
// Order as the very first, so return always true
return true;
}
else if (side_second == 0
&& direction_code<cs_tag>(m_origin, m_turn_point, second.point) == -1)
{
// Second is collinear and going backwards
// Order as very last, so return always false
return false;
}
// They are not both collinear
if (side_first != side_second)
{
return compare(side_first, side_second);
}
// They are both left, both right, and/or both collinear (with each other and/or with p1,p2)
// Check mutual side
int const side_second_wrt_first = side_strategy.apply(m_turn_point, first.point, second.point);
if (side_second_wrt_first == 0)
{
return on_same(first, second);
}
int const side_first_wrt_second = side_strategy.apply(m_turn_point, second.point, first.point);
if (side_second_wrt_first != -side_first_wrt_second)
{
// (FP) accuracy error in side calculation, the sides are not opposite.
// In that case they can be handled as collinear.
// If not, then the sort-order might not be stable.
return on_same(first, second);
}
// Both are on same side, and not collinear
// Union: return true if second is right w.r.t. first, so -1,
// so other is 1. union has greater as compare functor
// Intersection: v.v.
return compare(side_first_wrt_second, side_second_wrt_first);
}
private :
PointOrigin const& m_origin;
PointTurn const& m_turn_point;
// Umbrella strategy containing side strategy
Strategy const& m_strategy;
};
// Sorts vectors in counter clockwise order (by default)
// Purposes:
// - from one entry vector, find the next exit vector
// - find the open counts
// - find zones
template
<
bool Reverse1,
bool Reverse2,
overlay_type OverlayType,
typename Point,
typename Strategy,
typename Compare
>
struct side_sorter
{
using rp = ranked_point<Point>;
private :
struct include_union
{
inline bool operator()(rp const& ranked_point) const
{
// New candidate if there are no polygons on left side,
// but there are on right side
return ranked_point.count_left == 0
&& ranked_point.count_right > 0;
}
};
struct include_intersection
{
inline bool operator()(rp const& ranked_point) const
{
// New candidate if there are two polygons on right side,
// and less on the left side
return ranked_point.count_left < 2
&& ranked_point.count_right >= 2;
}
};
public :
side_sorter(Strategy const& strategy)
: m_origin_count(0)
, m_origin_segment_distance(0)
, m_strategy(strategy)
{}
template <typename Operation>
void add_segment_from(signed_size_type turn_index, int op_index,
Point const& point_from,
Operation const& op,
bool is_origin)
{
m_ranked_points.push_back(rp(point_from, turn_index, op_index,
dir_from, op.operation, op.seg_id));
if (is_origin)
{
m_origin = point_from;
m_origin_count++;
}
}
template <typename Operation>
void add_segment_to(signed_size_type turn_index, int op_index,
Point const& point_to,
Operation const& op)
{
m_ranked_points.push_back(rp(point_to, turn_index, op_index,
dir_to, op.operation, op.seg_id));
}
template <typename Operation>
void add_segment(signed_size_type turn_index, int op_index,
Point const& point_from, Point const& point_to,
Operation const& op, bool is_origin)
{
// The segment is added in two parts (sub-segment).
// In picture:
//
// from -----> * -----> to
//
// where * means: cluster point (intersection point)
// from means: start point of original segment
// to means: end point of original segment
// So from/to is from the perspective of the segment.
// From the perspective of the cluster, it is the other way round
// (from means: from-segment-to-cluster, to means: from-cluster-to-segment)
add_segment_from(turn_index, op_index, point_from, op, is_origin);
add_segment_to(turn_index, op_index, point_to, op);
}
template <typename Operation, typename Geometry1, typename Geometry2>
static Point walk_over_ring(Operation const& op, int offset,
Geometry1 const& geometry1,
Geometry2 const& geometry2)
{
Point point;
geometry::copy_segment_point<Reverse1, Reverse2>(geometry1, geometry2, op.seg_id, offset, point);
return point;
}
template <typename Turn, typename Operation, typename Geometry1, typename Geometry2>
Point add(Turn const& turn, Operation const& op, signed_size_type turn_index, int op_index,
Geometry1 const& geometry1,
Geometry2 const& geometry2,
bool is_origin)
{
Point point_from, point2, point3;
geometry::copy_segment_points<Reverse1, Reverse2>(geometry1, geometry2,
op.seg_id, point_from, point2, point3);
Point point_to = op.fraction.is_one() ? point3 : point2;
// If the point is in the neighbourhood (the limit is arbitrary),
// then take a point (or more) further back.
// The limit of offset avoids theoretical infinite loops.
// In practice it currently walks max 1 point back in all cases.
// Use the coordinate type, but if it is too small (e.g. std::int16), use a double
using ct_type = typename geometry::select_most_precise
<
typename geometry::coordinate_type<Point>::type,
double
>::type;
static auto const tolerance
= common_approximately_equals_epsilon_multiplier<ct_type>::value();
int offset = 0;
while (approximately_equals(point_from, turn.point, tolerance)
&& offset > -10)
{
point_from = walk_over_ring(op, --offset, geometry1, geometry2);
}
// Similarly for the point_to, walk forward
offset = 0;
while (approximately_equals(point_to, turn.point, tolerance)
&& offset < 10)
{
point_to = walk_over_ring(op, ++offset, geometry1, geometry2);
}
add_segment(turn_index, op_index, point_from, point_to, op, is_origin);
return point_from;
}
template <typename Turn, typename Operation, typename Geometry1, typename Geometry2>
void add(Turn const& turn,
Operation const& op, signed_size_type turn_index, int op_index,
segment_identifier const& departure_seg_id,
Geometry1 const& geometry1,
Geometry2 const& geometry2,
bool is_departure)
{
auto const potential_origin = add(turn, op, turn_index, op_index, geometry1, geometry2, false);
if (is_departure)
{
bool const is_origin
= op.seg_id.source_index == departure_seg_id.source_index
&& op.seg_id.ring_index == departure_seg_id.ring_index
&& op.seg_id.multi_index == departure_seg_id.multi_index;
if (is_origin)
{
signed_size_type const sd
= departure_seg_id.source_index == 0
? segment_distance(geometry1, departure_seg_id, op.seg_id)
: segment_distance(geometry2, departure_seg_id, op.seg_id);
if (m_origin_count == 0 || sd < m_origin_segment_distance)
{
m_origin = potential_origin;
m_origin_segment_distance = sd;
}
m_origin_count++;
}
}
}
template <typename PointTurn>
void apply(PointTurn const& turn_point)
{
// We need three compare functors:
// 1) to order clockwise (union) or counter clockwise (intersection)
// 2) to order by side, resulting in unique ranks for all points
// 3) to order by side, resulting in non-unique ranks
// to give colinear points
// Sort by side and assign rank
less_by_side<Point, PointTurn, Strategy, less_by_index, Compare> less_unique(m_origin, turn_point, m_strategy);
less_by_side<Point, PointTurn, Strategy, less_false, Compare> less_non_unique(m_origin, turn_point, m_strategy);
std::sort(m_ranked_points.begin(), m_ranked_points.end(), less_unique);
std::size_t colinear_rank = 0;
for (std::size_t i = 0; i < m_ranked_points.size(); i++)
{
if (i > 0
&& less_non_unique(m_ranked_points[i - 1], m_ranked_points[i]))
{
// It is not collinear
colinear_rank++;
}
m_ranked_points[i].rank = colinear_rank;
}
}
void find_open_by_piece_index()
{
// For buffers, use piece index
std::set<signed_size_type> handled;
for (std::size_t i = 0; i < m_ranked_points.size(); i++)
{
rp const& ranked = m_ranked_points[i];
if (ranked.direction != dir_from)
{
continue;
}
signed_size_type const& index = ranked.seg_id.piece_index;
if (handled.count(index) > 0)
{
continue;
}
find_polygons_for_source<&segment_identifier::piece_index>(index, i);
handled.insert(index);
}
}
void find_open_by_source_index()
{
// Check for source index 0 and 1
bool handled[2] = {false, false};
for (std::size_t i = 0; i < m_ranked_points.size(); i++)
{
rp const& ranked = m_ranked_points[i];
if (ranked.direction != dir_from)
{
continue;
}
signed_size_type const& index = ranked.seg_id.source_index;
if (index < 0 || index > 1 || handled[index])
{
continue;
}
find_polygons_for_source<&segment_identifier::source_index>(index, i);
handled[index] = true;
}
}
void find_open()
{
if BOOST_GEOMETRY_CONSTEXPR (OverlayType == overlay_buffer)
{
find_open_by_piece_index();
}
else
{
find_open_by_source_index();
}
}
void reverse()
{
if (m_ranked_points.empty())
{
return;
}
std::size_t const last = 1 + m_ranked_points.back().rank;
// Move iterator after rank==0
bool has_first = false;
auto it = m_ranked_points.begin() + 1;
for (; it != m_ranked_points.end() && it->rank == 0; ++it)
{
has_first = true;
}
if (has_first)
{
// Reverse first part (having rank == 0), if any,
// but skip the very first row
std::reverse(m_ranked_points.begin() + 1, it);
for (auto fit = m_ranked_points.begin(); fit != it; ++fit)
{
BOOST_ASSERT(fit->rank == 0);
}
}
// Reverse the rest (main rank > 0)
std::reverse(it, m_ranked_points.end());
for (; it != m_ranked_points.end(); ++it)
{
BOOST_ASSERT(it->rank > 0);
it->rank = last - it->rank;
}
}
bool has_origin() const
{
return m_origin_count > 0;
}
//private :
using container_type = std::vector<rp>;
container_type m_ranked_points;
Point m_origin;
std::size_t m_origin_count;
signed_size_type m_origin_segment_distance;
// Umbrella strategy containing side strategy
Strategy m_strategy;
private :
//! Check how many open spaces there are
template <typename Include>
inline std::size_t open_count(Include const& include_functor) const
{
std::size_t result = 0;
rank_type last_rank = 0;
for (std::size_t i = 0; i < m_ranked_points.size(); i++)
{
rp const& ranked_point = m_ranked_points[i];
if (ranked_point.rank > last_rank
&& ranked_point.direction == sort_by_side::dir_to
&& include_functor(ranked_point))
{
result++;
last_rank = ranked_point.rank;
}
}
return result;
}
std::size_t move(std::size_t index) const
{
std::size_t const result = index + 1;
return result >= m_ranked_points.size() ? 0 : result;
}
//! member is pointer to member (source_index or multi_index)
template <signed_size_type segment_identifier::*Member>
std::size_t move(signed_size_type member_index, std::size_t index) const
{
std::size_t result = move(index);
while (m_ranked_points[result].seg_id.*Member != member_index)
{
result = move(result);
}
return result;
}
void assign_ranks(rank_type min_rank, rank_type max_rank, int side_index)
{
for (std::size_t i = 0; i < m_ranked_points.size(); i++)
{
rp& ranked = m_ranked_points[i];
// Suppose there are 8 ranks, if min=4,max=6: assign 4,5,6
// if min=5,max=2: assign from 5,6,7,1,2
bool const in_range
= max_rank >= min_rank
? ranked.rank >= min_rank && ranked.rank <= max_rank
: ranked.rank >= min_rank || ranked.rank <= max_rank
;
if (in_range)
{
if (side_index == 1)
{
ranked.count_left++;
}
else if (side_index == 2)
{
ranked.count_right++;
}
}
}
}
template <signed_size_type segment_identifier::*Member>
void find_polygons_for_source(signed_size_type the_index,
std::size_t start_index)
{
bool in_polygon = true; // Because start_index is "from", arrives at the turn
rp const& start_rp = m_ranked_points[start_index];
rank_type last_from_rank = start_rp.rank;
rank_type previous_rank = start_rp.rank;
for (std::size_t index = move<Member>(the_index, start_index);
;
index = move<Member>(the_index, index))
{
rp& ranked = m_ranked_points[index];
if (ranked.rank != previous_rank && ! in_polygon)
{
assign_ranks(last_from_rank, previous_rank - 1, 1);
assign_ranks(last_from_rank + 1, previous_rank, 2);
}
if (index == start_index)
{
return;
}
if (ranked.direction == dir_from)
{
last_from_rank = ranked.rank;
in_polygon = true;
}
else if (ranked.direction == dir_to)
{
in_polygon = false;
}
previous_rank = ranked.rank;
}
}
//! Find closed zones and assign it
template <typename Include>
std::size_t assign_zones(Include const& include_functor)
{
// Find a starting point (the first rank after an outgoing rank
// with no polygons on the left side)
rank_type start_rank = m_ranked_points.size() + 1;
std::size_t start_index = 0;
rank_type max_rank = 0;
for (std::size_t i = 0; i < m_ranked_points.size(); i++)
{
rp const& ranked_point = m_ranked_points[i];
if (ranked_point.rank > max_rank)
{
max_rank = ranked_point.rank;
}
if (ranked_point.direction == sort_by_side::dir_to
&& include_functor(ranked_point))
{
start_rank = ranked_point.rank + 1;
}
if (ranked_point.rank == start_rank && start_index == 0)
{
start_index = i;
}
}
// Assign the zones
rank_type const undefined_rank = max_rank + 1;
std::size_t zone_id = 0;
rank_type last_rank = 0;
rank_type rank_at_next_zone = undefined_rank;
std::size_t index = start_index;
for (std::size_t i = 0; i < m_ranked_points.size(); i++)
{
rp& ranked_point = m_ranked_points[index];
// Implement cyclic behavior
index++;
if (index == m_ranked_points.size())
{
index = 0;
}
if (ranked_point.rank != last_rank)
{
if (ranked_point.rank == rank_at_next_zone)
{
zone_id++;
rank_at_next_zone = undefined_rank;
}
if (ranked_point.direction == sort_by_side::dir_to
&& include_functor(ranked_point))
{
rank_at_next_zone = ranked_point.rank + 1;
if (rank_at_next_zone > max_rank)
{
rank_at_next_zone = 0;
}
}
last_rank = ranked_point.rank;
}
ranked_point.zone = zone_id;
}
return zone_id;
}
public :
inline std::size_t open_count(operation_type for_operation) const
{
return for_operation == operation_union
? open_count(include_union())
: open_count(include_intersection())
;
}
inline std::size_t assign_zones(operation_type for_operation)
{
return for_operation == operation_union
? assign_zones(include_union())
: assign_zones(include_intersection())
;
}
};
//! Metafunction to define side_order (clockwise, ccw) by operation_type
template <operation_type OpType>
struct side_compare {};
template <>
struct side_compare<operation_union>
{
using type = std::greater<int>;
};
template <>
struct side_compare<operation_intersection>
{
using type = std::less<int>;
};
}}} // namespace detail::overlay::sort_by_side
#endif //DOXYGEN_NO_DETAIL
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SORT_BY_SIDE_HPP
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists