gnss-sim/3rdparty/boost/geometry/algorithms/detail/overlay/assign_parents.hpp

435 lines
14 KiB
C++

// Boost.Geometry (aka GGL, Generic Geometry Library)
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
// Copyright (c) 2017 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_ASSIGN_PARENTS_HPP
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ASSIGN_PARENTS_HPP
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <boost/geometry/core/coordinate_type.hpp>
#include <boost/geometry/algorithms/area_result.hpp>
#include <boost/geometry/algorithms/envelope.hpp>
#include <boost/geometry/algorithms/expand.hpp>
#include <boost/geometry/algorithms/detail/covered_by/implementation.hpp>
#include <boost/geometry/algorithms/detail/partition.hpp>
#include <boost/geometry/algorithms/detail/overlay/get_ring.hpp>
#include <boost/geometry/algorithms/detail/overlay/range_in_geometry.hpp>
#include <boost/geometry/geometries/box.hpp>
#include <boost/geometry/util/for_each_with_index.hpp>
namespace boost { namespace geometry
{
#ifndef DOXYGEN_NO_DETAIL
namespace detail { namespace overlay
{
template
<
typename Item,
typename InnerGeometry,
typename Geometry1, typename Geometry2,
typename RingCollection,
typename Strategy
>
static inline bool within_selected_input(Item const& item2,
InnerGeometry const& inner_geometry,
ring_identifier const& outer_id,
Geometry1 const& geometry1, Geometry2 const& geometry2,
RingCollection const& collection,
Strategy const& strategy)
{
typedef typename geometry::tag<Geometry1>::type tag1;
typedef typename geometry::tag<Geometry2>::type tag2;
// NOTE: range_in_geometry first checks the item2.point and then
// if this point is on boundary it checks points of inner_geometry
// ring until a point inside/outside other geometry ring is found
switch (outer_id.source_index)
{
// covered_by
case 0 :
return range_in_geometry(item2.point, inner_geometry,
get_ring<tag1>::apply(outer_id, geometry1), strategy) >= 0;
case 1 :
return range_in_geometry(item2.point, inner_geometry,
get_ring<tag2>::apply(outer_id, geometry2), strategy) >= 0;
case 2 :
return range_in_geometry(item2.point, inner_geometry,
get_ring<void>::apply(outer_id, collection), strategy) >= 0;
}
return false;
}
template
<
typename Item,
typename Geometry1, typename Geometry2,
typename RingCollection,
typename Strategy
>
static inline bool within_selected_input(Item const& item2,
ring_identifier const& inner_id, ring_identifier const& outer_id,
Geometry1 const& geometry1, Geometry2 const& geometry2,
RingCollection const& collection,
Strategy const& strategy)
{
typedef typename geometry::tag<Geometry1>::type tag1;
typedef typename geometry::tag<Geometry2>::type tag2;
switch (inner_id.source_index)
{
case 0 :
return within_selected_input(item2,
get_ring<tag1>::apply(inner_id, geometry1),
outer_id, geometry1, geometry2, collection, strategy);
case 1 :
return within_selected_input(item2,
get_ring<tag2>::apply(inner_id, geometry2),
outer_id, geometry1, geometry2, collection, strategy);
case 2 :
return within_selected_input(item2,
get_ring<void>::apply(inner_id, collection),
outer_id, geometry1, geometry2, collection, strategy);
}
return false;
}
template <typename Point, typename AreaType>
struct ring_info_helper
{
ring_identifier id;
AreaType real_area;
AreaType abs_area;
model::box<Point> envelope;
inline ring_info_helper()
: real_area(0), abs_area(0)
{}
inline ring_info_helper(ring_identifier i, AreaType const& a)
: id(i), real_area(a), abs_area(geometry::math::abs(a))
{}
};
template <typename Strategy>
struct ring_info_helper_get_box
{
ring_info_helper_get_box(Strategy const& strategy)
: m_strategy(strategy)
{}
template <typename Box, typename InputItem>
inline void apply(Box& total, InputItem const& item) const
{
assert_coordinate_type_equal(total, item.envelope);
geometry::expand(total, item.envelope, m_strategy);
}
Strategy const& m_strategy;
};
template <typename Strategy>
struct ring_info_helper_overlaps_box
{
ring_info_helper_overlaps_box(Strategy const& strategy)
: m_strategy(strategy)
{}
template <typename Box, typename InputItem>
inline bool apply(Box const& box, InputItem const& item) const
{
assert_coordinate_type_equal(box, item.envelope);
return ! geometry::detail::disjoint::disjoint_box_box(
box, item.envelope, m_strategy);
}
Strategy const& m_strategy;
};
// Segments intersection Strategy
template
<
typename Geometry1,
typename Geometry2,
typename Collection,
typename RingMap,
typename Strategy
>
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;
Strategy const& m_strategy;
bool m_check_for_orientation;
inline assign_visitor(Geometry1 const& g1, Geometry2 const& g2, Collection const& c,
RingMap& map, Strategy const& strategy, bool check)
: m_geometry1(g1)
, m_geometry2(g2)
, m_collection(c)
, m_ring_map(map)
, m_strategy(strategy)
, m_check_for_orientation(check)
{}
template <typename Item>
inline bool apply(Item const& outer, Item const& inner, bool first = true)
{
if (first && outer.abs_area < inner.abs_area)
{
// Apply with reversed arguments
apply(inner, outer, false);
return true;
}
if (m_check_for_orientation
|| (math::larger(outer.real_area, 0)
&& math::smaller(inner.real_area, 0)))
{
ring_info_type& inner_in_map = m_ring_map[inner.id];
if (geometry::covered_by(inner_in_map.point, outer.envelope, m_strategy)
&& within_selected_input(inner_in_map, inner.id, outer.id,
m_geometry1, m_geometry2, m_collection,
m_strategy)
)
{
// Assign a parent if there was no earlier parent, or the newly
// found parent is smaller than the previous one
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;
}
}
}
return true;
}
};
template
<
overlay_type OverlayType,
typename Geometry1, typename Geometry2,
typename RingCollection,
typename RingMap,
typename Strategy
>
inline void assign_parents(Geometry1 const& geometry1,
Geometry2 const& geometry2,
RingCollection const& collection,
RingMap& ring_map,
Strategy const& strategy)
{
static bool const is_difference = OverlayType == overlay_difference;
static bool const is_buffer = OverlayType == overlay_buffer;
static bool const is_dissolve = OverlayType == overlay_dissolve;
static bool const check_for_orientation = is_buffer || is_dissolve;
typedef typename geometry::tag<Geometry1>::type tag1;
typedef typename geometry::tag<Geometry2>::type tag2;
typedef typename RingMap::mapped_type ring_info_type;
typedef typename ring_info_type::point_type point_type;
typedef model::box<point_type> box_type;
typedef typename geometry::area_result
<
point_type, Strategy // TODO: point_type is technically incorrect
>::type area_result_type;
{
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
// Copy to vector (this might be obsolete, using the map directly)
using helper = ring_info_helper<point_type, area_result_type>;
std::vector<helper> vector(count_total);
for_each_with_index(ring_map, [&](std::size_t index, auto const& pair)
{
vector[index] = helper(pair.first, pair.second.get_area());
helper& item = vector[index];
switch(pair.first.source_index)
{
case 0 :
geometry::envelope(get_ring<tag1>::apply(pair.first, geometry1),
item.envelope, strategy);
break;
case 1 :
geometry::envelope(get_ring<tag2>::apply(pair.first, geometry2),
item.envelope, strategy);
break;
case 2 :
geometry::envelope(get_ring<void>::apply(pair.first, collection),
item.envelope, strategy);
break;
}
// Expand envelope slightly
expand_by_epsilon(item.envelope);
if (item.real_area > 0)
{
count_positive++;
index_positive = index;
}
});
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 && ! is_difference && ! is_dissolve)
{
// 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)
// In difference or other cases where interior rings might be
// located outside the outer ring, this cannot be done
ring_identifier id_of_positive = vector[index_positive].id;
ring_info_type& outer = ring_map[id_of_positive];
for_each_with_index(vector, [&](std::size_t index, auto const& item)
{
if (index != index_positive)
{
ring_info_type& inner = ring_map[item.id];
inner.parent = id_of_positive;
outer.children.push_back(item.id);
}
});
return;
}
}
assign_visitor
<
Geometry1, Geometry2,
RingCollection, RingMap,
Strategy
> visitor(geometry1, geometry2, collection, ring_map, strategy, check_for_orientation);
geometry::partition
<
box_type
>::apply(vector, visitor,
ring_info_helper_get_box<Strategy>(strategy),
ring_info_helper_overlaps_box<Strategy>(strategy));
}
if (check_for_orientation)
{
for (auto& pair : ring_map)
{
ring_info_type& info = pair.second;
if (geometry::math::equals(info.get_area(), 0))
{
info.discarded = true;
}
else if (info.parent.source_index >= 0)
{
ring_info_type const& parent = ring_map[info.parent];
bool const pos = math::larger(info.get_area(), 0);
bool const parent_pos = math::larger(parent.area, 0);
bool const double_neg = is_dissolve && ! pos && ! parent_pos;
if ((pos && parent_pos) || double_neg)
{
// Discard positive inner ring with positive parent
// Also, for some cases (dissolve), negative inner ring
// with negative parent should be discarded
info.discarded = true;
}
if (pos || info.discarded)
{
// Remove parent ID from any positive or discarded inner rings
info.parent.source_index = -1;
}
}
else if (info.parent.source_index < 0
&& math::smaller(info.get_area(), 0))
{
// Reverse negative ring without parent
info.reversed = true;
}
}
}
// Assign childlist
for (auto& pair : ring_map)
{
if (pair.second.parent.source_index >= 0)
{
ring_map[pair.second.parent].children.push_back(pair.first);
}
}
}
// Version for one geometry (called by buffer/dissolve)
template
<
overlay_type OverlayType,
typename Geometry,
typename RingCollection,
typename RingMap,
typename Strategy
>
inline void assign_parents(Geometry const& geometry,
RingCollection const& collection,
RingMap& ring_map,
Strategy const& strategy)
{
// Call it with an empty geometry as second geometry (source_id == 1)
// (ring_map should be empty for source_id==1)
Geometry empty;
assign_parents<OverlayType>(geometry, empty,
collection, ring_map, strategy);
}
}} // namespace detail::overlay
#endif // DOXYGEN_NO_DETAIL
}} // namespace geometry
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ASSIGN_PARENTS_HPP