Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-16 08:34:09

0001 // Boost.Geometry (aka GGL, Generic Geometry Library)
0002 
0003 // Copyright (c) 2012-2014 Barend Gehrels, Amsterdam, the Netherlands.
0004 // Copyright (c) 2017-2023 Adam Wulkiewicz, Lodz, Poland.
0005 
0006 // This file was modified by Oracle on 2016-2024.
0007 // Modifications copyright (c) 2016-2024 Oracle and/or its affiliates.
0008 // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle
0009 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
0010 
0011 // Use, modification and distribution is subject to the Boost Software License,
0012 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
0013 // http://www.boost.org/LICENSE_1_0.txt)
0014 
0015 #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_BUFFERED_PIECE_COLLECTION_HPP
0016 #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_BUFFERED_PIECE_COLLECTION_HPP
0017 
0018 #include <algorithm>
0019 #include <cstddef>
0020 #include <set>
0021 
0022 #include <boost/core/ignore_unused.hpp>
0023 #include <boost/range/begin.hpp>
0024 #include <boost/range/empty.hpp>
0025 #include <boost/range/end.hpp>
0026 #include <boost/range/size.hpp>
0027 #include <boost/range/value_type.hpp>
0028 
0029 #include <boost/geometry/core/assert.hpp>
0030 #include <boost/geometry/core/coordinate_type.hpp>
0031 #include <boost/geometry/core/point_type.hpp>
0032 
0033 #include <boost/geometry/algorithms/covered_by.hpp>
0034 #include <boost/geometry/algorithms/envelope.hpp>
0035 
0036 #include <boost/geometry/strategies/buffer.hpp>
0037 
0038 #include <boost/geometry/geometries/ring.hpp>
0039 
0040 #include <boost/geometry/algorithms/detail/buffer/buffered_ring.hpp>
0041 #include <boost/geometry/algorithms/detail/buffer/buffer_policies.hpp>
0042 #include <boost/geometry/algorithms/detail/overlay/cluster_info.hpp>
0043 #include <boost/geometry/algorithms/detail/buffer/get_piece_turns.hpp>
0044 #include <boost/geometry/algorithms/detail/buffer/piece_border.hpp>
0045 #include <boost/geometry/algorithms/detail/buffer/turn_in_piece_visitor.hpp>
0046 #include <boost/geometry/algorithms/detail/buffer/turn_in_original_visitor.hpp>
0047 
0048 #include <boost/geometry/algorithms/detail/disjoint/point_box.hpp>
0049 #include <boost/geometry/algorithms/detail/overlay/add_rings.hpp>
0050 #include <boost/geometry/algorithms/detail/overlay/assign_parents.hpp>
0051 #include <boost/geometry/algorithms/detail/overlay/enrichment_info.hpp>
0052 #include <boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp>
0053 #include <boost/geometry/algorithms/detail/overlay/ring_properties.hpp>
0054 #include <boost/geometry/algorithms/detail/overlay/select_rings.hpp>
0055 #include <boost/geometry/algorithms/detail/overlay/traversal_info.hpp>
0056 #include <boost/geometry/algorithms/detail/overlay/traverse.hpp>
0057 #include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
0058 #include <boost/geometry/algorithms/detail/partition.hpp>
0059 #include <boost/geometry/algorithms/detail/sections/sectionalize.hpp>
0060 #include <boost/geometry/algorithms/detail/sections/section_box_policies.hpp>
0061 
0062 #include <boost/geometry/views/detail/closed_clockwise_view.hpp>
0063 #include <boost/geometry/views/enumerate_view.hpp>
0064 #include <boost/geometry/util/range.hpp>
0065 
0066 
0067 namespace boost { namespace geometry
0068 {
0069 
0070 
0071 #ifndef DOXYGEN_NO_DETAIL
0072 namespace detail { namespace buffer
0073 {
0074 
0075 
0076 /*
0077  *  Terminology
0078  *
0079  *  Suppose we make a buffer (using blocked corners) of this rectangle:
0080  *
0081  *         +-------+
0082  *         |       |
0083  *         |  rect |
0084  *         |       |
0085  *         +-------+
0086  *
0087  * For the sides we get these four buffered side-pieces (marked with s)
0088  * and four buffered corner pieces (marked with c)
0089  *
0090  *     c---+---s---+---c
0091  *     |   | piece |   |     <- see below for details of the middle top-side-piece
0092  *     +---+-------+---+
0093  *     |   |       |   |
0094  *     s   |  rect |   s     <- two side pieces left/right of rect
0095  *     |   |       |   |
0096  *     +---+-------+---+
0097  *     |   | piece |   |     <- one side-piece below, and two corner pieces
0098  *     c---+---s---+---c
0099  *
0100  *  The outer part of the picture above, using all pieces,
0101  *    form together the offsetted ring (marked with o below)
0102  *  The 8 pieces are part of the piece collection and use for inside-checks
0103  *  The inner parts form (using 1 or 2 points per piece, often co-located)
0104  *    form together the polygons (marked with r below)
0105  *  The remaining piece-segments are helper-segments (marked with h)
0106  *
0107  *     ooooooooooooooooo
0108  *     o   h       h   o
0109  *     ohhhrrrrrrrrrhhho
0110  *     o   r       r   o
0111  *     o   r       r   o
0112  *     o   r       r   o
0113  *     ohhhrrrrrrrrrhhho
0114  *     o   h       h   o
0115  *     ooooooooooooooooo
0116  *
0117  */
0118 
0119 template
0120 <
0121     typename Ring,
0122     typename Strategy,
0123     typename DistanceStrategy
0124 >
0125 struct buffered_piece_collection
0126 {
0127     using point_type = geometry::point_type_t<Ring>;
0128     using coordinate_type = geometry::coordinate_type_t<Ring>;
0129 
0130     // Ring/polygon type, always clockwise
0131     using clockwise_ring_type = geometry::model::ring<point_type>;
0132 
0133     using box_type = geometry::model::box<point_type>;
0134 
0135     using buffer_turn_info_type = buffer_turn_info
0136     <
0137         point_type,
0138         typename segment_ratio_type<point_type>::type
0139     >;
0140 
0141     using buffer_turn_operation_type = buffer_turn_operation
0142     <
0143         point_type,
0144         typename segment_ratio_type<point_type>::type
0145     >;
0146 
0147     using turn_vector_type = std::vector<buffer_turn_info_type>;
0148 
0149     using piece_border_type = piece_border<Ring, point_type> ;
0150 
0151     struct piece
0152     {
0153         strategy::buffer::piece_type type;
0154         signed_size_type index;
0155 
0156         signed_size_type left_index; // points to previous piece of same ring
0157         signed_size_type right_index; // points to next piece of same ring
0158 
0159         // The next two members (1, 2) form together a complete clockwise ring
0160         // for each piece (with one dupped point)
0161         // The complete clockwise ring is also included as a ring (3)
0162 
0163         // 1: half, part of offsetted_rings
0164 
0165         // Segment identifier of this piece, including its start index
0166         segment_identifier first_seg_id;
0167 
0168         // One-beyond index of this piece, to iterate over a ring
0169         // from:                ring.begin() + pc.first_seg_id.segment_index;
0170         // to (not including):  ring.begin() + pc.beyond_last_segment_index;
0171         // Its ring_id etc are shared with first_seg_id
0172         signed_size_type beyond_last_segment_index;
0173 
0174         // part in offsetted ring which is part of offsetted ring
0175         signed_size_type offsetted_count;
0176 
0177         bool is_flat_start;
0178         bool is_flat_end;
0179 
0180         bool is_deflated;
0181 
0182         // Ring (parts) of this piece, always clockwise
0183         piece_border_type m_piece_border;
0184 
0185         point_type m_label_point;
0186 
0187         // For a point buffer
0188         point_type m_center;
0189 
0190         piece()
0191             : type(strategy::buffer::piece_type_unknown)
0192             , index(-1)
0193             , left_index(-1)
0194             , right_index(-1)
0195             , beyond_last_segment_index(-1)
0196             , offsetted_count(-1)
0197             , is_flat_start(false)
0198             , is_flat_end(false)
0199             , is_deflated(false)
0200         {
0201         }
0202     };
0203 
0204     struct original_ring
0205     {
0206         using sections_type = geometry::sections<box_type, 1>;
0207 
0208         // Creates an empty instance
0209         inline original_ring()
0210             : m_is_interior(false)
0211             , m_has_interiors(false)
0212         {}
0213 
0214         inline original_ring(clockwise_ring_type const& ring,
0215                              bool is_interior, bool has_interiors,
0216                              Strategy const& strategy)
0217             : m_ring(ring)
0218             , m_is_interior(is_interior)
0219             , m_has_interiors(has_interiors)
0220         {
0221             geometry::envelope(m_ring, m_box, strategy);
0222 
0223             // create monotonic sections in x-dimension
0224             // The dimension is critical because the direction is later used
0225             // in the optimization for within checks using winding strategy
0226             // and this strategy is scanning in x direction.
0227             using dimensions = std::integer_sequence<std::size_t, 0>;
0228             geometry::sectionalize
0229                 <
0230                     false, dimensions
0231                 >(m_ring, m_sections, strategy);
0232         }
0233 
0234         clockwise_ring_type m_ring;
0235         box_type m_box;
0236         sections_type m_sections;
0237 
0238         bool m_is_interior;
0239         bool m_has_interiors;
0240     };
0241 
0242     using piece_vector_type = std::vector<piece>;
0243 
0244     piece_vector_type m_pieces;
0245     turn_vector_type m_turns;
0246     signed_size_type m_first_piece_index;
0247     bool m_deflate;
0248     bool m_has_deflated;
0249 
0250     // Offsetted rings, and representations of original ring(s)
0251     // both indexed by multi_index
0252     using ring_collection_t = buffered_ring_collection<buffered_ring<Ring>>;
0253     ring_collection_t offsetted_rings;
0254     std::vector<original_ring> original_rings;
0255     std::vector<point_type> m_linear_end_points;
0256 
0257     buffered_ring_collection<Ring> traversed_rings;
0258     segment_identifier current_segment_id;
0259 
0260     // Monotonic sections (used for offsetted rings around points)
0261     using sections_type = geometry::sections<box_type, 2>;
0262     sections_type monotonic_sections;
0263 
0264     // Define the clusters, mapping cluster_id -> turns
0265     using cluster_type = std::map
0266         <
0267             signed_size_type,
0268             detail::overlay::cluster_info
0269         >;
0270 
0271     cluster_type m_clusters;
0272 
0273     Strategy m_strategy;
0274     DistanceStrategy m_distance_strategy;
0275 
0276     buffered_piece_collection(Strategy const& strategy,
0277                               DistanceStrategy const& distance_strategy)
0278         : m_first_piece_index(-1)
0279         , m_deflate(false)
0280         , m_has_deflated(false)
0281         , m_strategy(strategy)
0282         , m_distance_strategy(distance_strategy)
0283     {}
0284 
0285     inline void check_linear_endpoints(buffer_turn_info_type& turn) const
0286     {
0287         // TODO this is quadratic. But the #endpoints, expected, is low,
0288         // and only applicable for linear features
0289         // (in a multi linestring with many short lines, the #endpoints can be
0290         // much higher)
0291         for (auto const& p : m_linear_end_points)
0292         {
0293             if (detail::equals::equals_point_point(turn.point, p, m_strategy))
0294             {
0295                 turn.is_linear_end_point = true;
0296             }
0297         }
0298     }
0299 
0300     inline void deflate_check_turns()
0301     {
0302         if (! m_has_deflated)
0303         {
0304             return;
0305         }
0306 
0307         // Deflated rings may not travel to themselves, there should at least
0308         // be three turns (which cannot be checked here - TODO: add to traverse)
0309         for (auto& turn : m_turns)
0310         {
0311             if (! turn.is_turn_traversable)
0312             {
0313                 continue;
0314             }
0315             for (auto& op : turn.operations)
0316             {
0317                 if (op.enriched.get_next_turn_index() == static_cast<signed_size_type>(turn.turn_index)
0318                     && m_pieces[op.seg_id.piece_index].is_deflated)
0319                 {
0320                     // Keep traversable, but don't start here
0321                     op.enriched.startable = false;
0322                 }
0323             }
0324         }
0325     }
0326 
0327     // Check if a turn is inside any of the originals
0328     inline void check_turn_in_original()
0329     {
0330         turn_in_original_visitor
0331             <
0332                 turn_vector_type,
0333                 Strategy
0334             > visitor(m_turns, m_strategy);
0335 
0336         geometry::partition
0337             <
0338                 box_type,
0339                 include_turn_policy,
0340                 detail::partition::include_all_policy
0341             >::apply(m_turns, original_rings, visitor,
0342                      turn_get_box<Strategy>(m_strategy),
0343                      turn_in_original_overlaps_box<Strategy>(m_strategy),
0344                      original_get_box<Strategy>(m_strategy),
0345                      original_overlaps_box<Strategy>(m_strategy));
0346 
0347         bool const deflate = m_distance_strategy.negative();
0348 
0349         for (auto& turn : m_turns)
0350         {
0351             if (turn.is_turn_traversable)
0352             {
0353                 if (deflate && turn.count_in_original <= 0)
0354                 {
0355                     // For deflate/negative buffers:
0356                     // it is not in the original, so don't use it
0357                     turn.is_turn_traversable = false;
0358                 }
0359                 else if (! deflate && turn.count_in_original > 0)
0360                 {
0361                     // For inflate: it is in original, so don't use it
0362                     turn.is_turn_traversable = false;
0363                 }
0364             }
0365         }
0366     }
0367 
0368     inline void update_turn_administration()
0369     {
0370         for (auto const& enumerated : util::enumerate(m_turns))
0371         {
0372             // enumerated is const, but its value is a non-const reference
0373             auto& turn = enumerated.value;
0374             turn.turn_index = enumerated.index;
0375 
0376             // Verify if a turn is a linear endpoint
0377             if (! turn.is_linear_end_point)
0378             {
0379                 this->check_linear_endpoints(turn);
0380             }
0381         }
0382     }
0383 
0384     // Calculate properties of piece borders which are not influenced
0385     // by turns themselves:
0386     // - envelopes (essential for partitioning during calc turns)
0387     // - convexity
0388     // - monotonicity
0389     // - min/max radius of point buffers
0390     // - (if pieces are reversed)
0391     inline void update_piece_administration()
0392     {
0393         for (auto& pc : m_pieces)
0394         {
0395             piece_border_type& border = pc.m_piece_border;
0396             buffered_ring<Ring> const& ring = offsetted_rings[pc.first_seg_id.multi_index];
0397 
0398             if (pc.offsetted_count > 0)
0399             {
0400                 if (pc.type != strategy::buffer::buffered_concave)
0401                 {
0402                     border.set_offsetted(ring, pc.first_seg_id.segment_index,
0403                                        pc.beyond_last_segment_index);
0404                 }
0405 
0406                 // Calculate envelopes for piece borders
0407                 border.get_properties_of_border(pc.type == geometry::strategy::buffer::buffered_point,
0408                                                 pc.m_center, m_strategy);
0409                 if (! pc.is_flat_end && ! pc.is_flat_start)
0410                 {
0411                     border.get_properties_of_offsetted_ring_part(m_strategy);
0412                 }
0413             }
0414         }
0415     }
0416 
0417     inline void get_turns()
0418     {
0419         update_piece_administration();
0420 
0421         {
0422             // Calculate the turns
0423             piece_turn_visitor
0424                 <
0425                     piece_vector_type,
0426                     buffered_ring_collection<buffered_ring<Ring> >,
0427                     turn_vector_type,
0428                     Strategy
0429                 > visitor(m_pieces, offsetted_rings, m_turns,
0430                           m_strategy);
0431 
0432             detail::sectionalize::enlarge_sections(monotonic_sections, m_strategy);
0433 
0434             geometry::partition
0435                 <
0436                     box_type
0437                 >::apply(monotonic_sections, visitor,
0438                          detail::section::get_section_box<Strategy>(m_strategy),
0439                          detail::section::overlaps_section_box<Strategy>(m_strategy));
0440         }
0441 
0442         update_turn_administration();
0443     }
0444 
0445     inline void check_turn_in_pieces()
0446     {
0447         // Check if turns are inside pieces
0448         turn_in_piece_visitor
0449             <
0450                 geometry::cs_tag_t<point_type>,
0451                 turn_vector_type, piece_vector_type, DistanceStrategy, Strategy
0452             > visitor(m_turns, m_pieces, m_distance_strategy, m_strategy);
0453 
0454         geometry::partition
0455             <
0456                 box_type
0457             >::apply(m_turns, m_pieces, visitor,
0458                         turn_get_box<Strategy>(m_strategy),
0459                         turn_overlaps_box<Strategy>(m_strategy),
0460                         piece_get_box<Strategy>(m_strategy),
0461                         piece_overlaps_box<Strategy>(m_strategy));
0462     }
0463 
0464     inline void start_new_ring(bool deflate)
0465     {
0466         std::size_t const n = offsetted_rings.size();
0467         current_segment_id.source_index = 0;
0468         current_segment_id.multi_index = static_cast<signed_size_type>(n);
0469         current_segment_id.ring_index = -1;
0470         current_segment_id.segment_index = 0;
0471 
0472         offsetted_rings.resize(n + 1);
0473         original_rings.resize(n + 1);
0474 
0475         m_first_piece_index = static_cast<signed_size_type>(boost::size(m_pieces));
0476         m_deflate = deflate;
0477         if (deflate)
0478         {
0479             // Pieces contain either deflated exterior rings, or inflated
0480             // interior rings which are effectively deflated too
0481             m_has_deflated = true;
0482         }
0483     }
0484 
0485     inline void abort_ring()
0486     {
0487         // Remove all created pieces for this ring, sections, last offsetted
0488         while (! m_pieces.empty()
0489                && m_pieces.back().first_seg_id.multi_index
0490                == current_segment_id.multi_index)
0491         {
0492             m_pieces.pop_back();
0493         }
0494 
0495         offsetted_rings.pop_back();
0496         original_rings.pop_back();
0497 
0498         m_first_piece_index = -1;
0499     }
0500 
0501     inline void update_last_point(point_type const& p,
0502             buffered_ring<Ring>& ring)
0503     {
0504         // For the first point of a new piece, and there were already
0505         // points in the offsetted ring, for some piece types the first point
0506         // is a duplicate of the last point of the previous piece.
0507 
0508         // TODO: disable that, that point should not be added
0509 
0510         // For now, it is made equal because due to numerical instability,
0511         // it can be a tiny bit off, possibly causing a self-intersection
0512 
0513         BOOST_GEOMETRY_ASSERT(boost::size(m_pieces) > 0);
0514         if (! ring.empty()
0515             && current_segment_id.segment_index
0516                 == m_pieces.back().first_seg_id.segment_index)
0517         {
0518             ring.back() = p;
0519         }
0520     }
0521 
0522     inline void set_piece_center(point_type const& center)
0523     {
0524         BOOST_GEOMETRY_ASSERT(! m_pieces.empty());
0525         m_pieces.back().m_center = center;
0526     }
0527 
0528     inline bool finish_ring(strategy::buffer::result_code code)
0529     {
0530         if (code == strategy::buffer::result_error_numerical)
0531         {
0532             abort_ring();
0533             return false;
0534         }
0535 
0536         if (m_first_piece_index == -1)
0537         {
0538             return false;
0539         }
0540 
0541         // Casted version
0542         std::size_t const first_piece_index
0543                 = static_cast<std::size_t>(m_first_piece_index);
0544         signed_size_type const last_piece_index
0545                 = static_cast<signed_size_type>(boost::size(m_pieces)) - 1;
0546 
0547         if (first_piece_index < boost::size(m_pieces))
0548         {
0549             // If pieces were added,
0550             // reassign left-of-first and right-of-last
0551             geometry::range::at(m_pieces, first_piece_index).left_index
0552                     = last_piece_index;
0553             geometry::range::back(m_pieces).right_index = m_first_piece_index;
0554         }
0555 
0556         buffered_ring<Ring>& added = offsetted_rings.back();
0557         if (! boost::empty(added))
0558         {
0559             // Make sure the closing point is identical (they are calculated
0560             // separately by different pieces)
0561             range::back(added) = range::front(added);
0562         }
0563 
0564         for (std::size_t i = first_piece_index; i < boost::size(m_pieces); i++)
0565         {
0566             sectionalize(m_pieces[i], added);
0567         }
0568 
0569         m_first_piece_index = -1;
0570         return true;
0571     }
0572 
0573     template <typename InputRing>
0574     inline void finish_ring(strategy::buffer::result_code code,
0575                             InputRing const& input_ring,
0576                             bool is_interior, bool has_interiors)
0577     {
0578         if (! finish_ring(code))
0579         {
0580             return;
0581         }
0582 
0583         if (! boost::empty(input_ring))
0584         {
0585             // Assign the ring to the original_ring collection
0586             // (note that this Ring type is the
0587             // GeometryOut type, which might differ from the input ring type)
0588             clockwise_ring_type clockwise_ring;
0589 
0590             using view_type = detail::closed_clockwise_view<InputRing const>;
0591             view_type const view(input_ring);
0592 
0593             for (auto it = boost::begin(view); it != boost::end(view); ++it)
0594             {
0595                 clockwise_ring.push_back(*it);
0596             }
0597 
0598             original_rings.back()
0599                 = original_ring(clockwise_ring,
0600                     is_interior, has_interiors,
0601                     m_strategy);
0602         }
0603     }
0604 
0605     inline void set_current_ring_concave()
0606     {
0607         BOOST_GEOMETRY_ASSERT(boost::size(offsetted_rings) > 0);
0608         offsetted_rings.back().has_concave = true;
0609     }
0610 
0611     inline signed_size_type add_point(point_type const& p)
0612     {
0613         BOOST_GEOMETRY_ASSERT(boost::size(offsetted_rings) > 0);
0614 
0615         buffered_ring<Ring>& current_ring = offsetted_rings.back();
0616         update_last_point(p, current_ring);
0617 
0618         current_segment_id.segment_index++;
0619         current_ring.push_back(p);
0620         return static_cast<signed_size_type>(current_ring.size());
0621     }
0622 
0623     //-------------------------------------------------------------------------
0624 
0625     inline piece& create_piece(strategy::buffer::piece_type type,
0626             bool decrease_segment_index_by_one)
0627     {
0628         if (type == strategy::buffer::buffered_concave)
0629         {
0630             offsetted_rings.back().has_concave = true;
0631         }
0632 
0633         piece pc;
0634         pc.type = type;
0635         pc.index = static_cast<signed_size_type>(boost::size(m_pieces));
0636         pc.is_deflated = m_deflate;
0637 
0638         current_segment_id.piece_index = pc.index;
0639 
0640         pc.first_seg_id = current_segment_id;
0641 
0642         // Assign left/right (for first/last piece per ring they will be re-assigned later)
0643         pc.left_index = pc.index - 1;
0644         pc.right_index = pc.index + 1;
0645 
0646         std::size_t const n = boost::size(offsetted_rings.back());
0647         pc.first_seg_id.segment_index = decrease_segment_index_by_one ? n - 1 : n;
0648         pc.beyond_last_segment_index = pc.first_seg_id.segment_index;
0649 
0650         m_pieces.push_back(pc);
0651         return m_pieces.back();
0652     }
0653 
0654     inline void init_rescale_piece(piece& pc)
0655     {
0656         if (pc.first_seg_id.segment_index < 0)
0657         {
0658             // This indicates an error situation: an earlier piece was empty
0659             // It currently does not happen
0660             pc.offsetted_count = 0;
0661             return;
0662         }
0663 
0664         BOOST_GEOMETRY_ASSERT(pc.first_seg_id.multi_index >= 0);
0665         BOOST_GEOMETRY_ASSERT(pc.beyond_last_segment_index >= 0);
0666 
0667         pc.offsetted_count = pc.beyond_last_segment_index - pc.first_seg_id.segment_index;
0668         BOOST_GEOMETRY_ASSERT(pc.offsetted_count >= 0);
0669     }
0670 
0671     inline void add_piece_point(piece& pc, point_type const& point, bool add_to_original)
0672     {
0673         if (add_to_original && pc.type != strategy::buffer::buffered_concave)
0674         {
0675             pc.m_piece_border.add_original_point(point);
0676         }
0677         else
0678         {
0679             pc.m_label_point = point;
0680         }
0681     }
0682 
0683     inline void sectionalize(piece const& pc, buffered_ring<Ring> const& ring)
0684     {
0685         using sectionalizer = geometry::detail::sectionalize::sectionalize_part
0686         <
0687             std::integer_sequence<std::size_t, 0, 1> // x,y dimension
0688         >;
0689 
0690         // Create a ring-identifier. The source-index is the piece index
0691         // The multi_index is as in this collection (the ring), but not used here
0692         // The ring_index is not used
0693         ring_identifier const ring_id(pc.index, pc.first_seg_id.multi_index, -1);
0694 
0695         sectionalizer::apply(monotonic_sections,
0696             boost::begin(ring) + pc.first_seg_id.segment_index,
0697             boost::begin(ring) + pc.beyond_last_segment_index,
0698             m_strategy,
0699             ring_id, 10);
0700     }
0701 
0702     inline void finish_piece(piece& pc)
0703     {
0704         init_rescale_piece(pc);
0705     }
0706 
0707     inline void finish_piece(piece& pc,
0708                     point_type const& point1,
0709                     point_type const& point2,
0710                     point_type const& point3)
0711     {
0712         init_rescale_piece(pc);
0713         if (pc.offsetted_count == 0)
0714         {
0715             return;
0716         }
0717 
0718         add_piece_point(pc, point1, false);
0719         add_piece_point(pc, point2, true);
0720         add_piece_point(pc, point3, false);
0721     }
0722 
0723     inline void finish_piece(piece& pc,
0724                     point_type const& point1,
0725                     point_type const& point2,
0726                     point_type const& point3,
0727                     point_type const& point4)
0728     {
0729         init_rescale_piece(pc);
0730 
0731         // Add the four points. Note that points 2 and 3 are the originals,
0732         // and that they are already passed in reverse order
0733         // (because the offsetted ring is in clockwise order)
0734         add_piece_point(pc, point1, false);
0735         add_piece_point(pc, point2, true);
0736         add_piece_point(pc, point3, true);
0737         add_piece_point(pc, point4, false);
0738     }
0739 
0740     template <typename Range>
0741     inline void add_range_to_piece(piece& pc, Range const& range, bool add_front)
0742     {
0743         BOOST_GEOMETRY_ASSERT(boost::size(range) != 0u);
0744 
0745         auto it = boost::begin(range);
0746 
0747         // If it follows a non-join (so basically the same piece-type) point b1 should be added.
0748         // There should be two intersections later and it should be discarded.
0749         // But for now we need it to calculate intersections
0750         if (add_front)
0751         {
0752             add_point(*it);
0753         }
0754 
0755         for (++it; it != boost::end(range); ++it)
0756         {
0757             pc.beyond_last_segment_index = add_point(*it);
0758         }
0759     }
0760 
0761     inline void add_piece(strategy::buffer::piece_type type, point_type const& p,
0762             point_type const& b1, point_type const& b2)
0763     {
0764         piece& pc = create_piece(type, false);
0765         add_point(b1);
0766         pc.beyond_last_segment_index = add_point(b2);
0767         finish_piece(pc, b2, p, b1);
0768     }
0769 
0770     template <typename Range>
0771     inline void add_piece(strategy::buffer::piece_type type, Range const& range,
0772             bool decrease_segment_index_by_one)
0773     {
0774         piece& pc = create_piece(type, decrease_segment_index_by_one);
0775 
0776         if (boost::size(range) > 0u)
0777         {
0778             add_range_to_piece(pc, range, offsetted_rings.back().empty());
0779         }
0780         finish_piece(pc);
0781     }
0782 
0783     template <typename Range>
0784     inline void add_piece(strategy::buffer::piece_type type,
0785             point_type const& p, Range const& range)
0786     {
0787         piece& pc = create_piece(type, true);
0788 
0789         if (boost::size(range) > 0u)
0790         {
0791             add_range_to_piece(pc, range, offsetted_rings.back().empty());
0792             finish_piece(pc, range.back(), p, range.front());
0793         }
0794         else
0795         {
0796             finish_piece(pc);
0797         }
0798     }
0799 
0800     template <typename Range>
0801     inline void add_side_piece(point_type const& original_point1,
0802             point_type const& original_point2,
0803             Range const& range, bool is_first, bool is_empty)
0804     {
0805         BOOST_GEOMETRY_ASSERT(boost::size(range) >= 2u);
0806 
0807         auto const piece_type = is_empty
0808             ? strategy::buffer::buffered_empty_side
0809             : strategy::buffer::buffered_segment;
0810 
0811         piece& pc = create_piece(piece_type, ! is_first);
0812         add_range_to_piece(pc, range, is_first);
0813 
0814         // Add the four points of the side, starting with the last point of the
0815         // range, and reversing the order of the originals to keep it clockwise
0816         finish_piece(pc, range.back(), original_point2, original_point1, range.front());
0817     }
0818 
0819     template <typename EndcapStrategy, typename Range>
0820     inline void add_endcap(EndcapStrategy const& strategy, Range const& range,
0821             point_type const& end_point)
0822     {
0823         boost::ignore_unused(strategy);
0824 
0825         if (range.empty())
0826         {
0827             return;
0828         }
0829         strategy::buffer::piece_type pt = strategy.get_piece_type();
0830         if (pt == strategy::buffer::buffered_flat_end)
0831         {
0832             // It is flat, should just be added, without helper segments
0833             add_piece(pt, range, true);
0834         }
0835         else
0836         {
0837             // Normal case, it has an "inside", helper segments should be added
0838             add_piece(pt, end_point, range);
0839         }
0840     }
0841 
0842     inline void mark_flat_start(point_type const& point)
0843     {
0844         if (! m_pieces.empty())
0845         {
0846             piece& back = m_pieces.back();
0847             back.is_flat_start = true;
0848 
0849             // This happens to linear buffers, and it will be the very
0850             // first or last point. If that coincides with a turn,
0851             // and the turn was marked as ON_BORDER
0852             // the turn should NOT be within (even though it can be marked
0853             // as such)
0854             m_linear_end_points.push_back(point);
0855         }
0856     }
0857 
0858     inline void mark_flat_end(point_type const& point)
0859     {
0860         if (! m_pieces.empty())
0861         {
0862             piece& back = m_pieces.back();
0863             back.is_flat_end = true;
0864             m_linear_end_points.push_back(point);
0865         }
0866     }
0867 
0868     //-------------------------------------------------------------------------
0869 
0870     inline void handle_colocations()
0871     {
0872         if (! detail::overlay::handle_colocations
0873                 <
0874                     false, false, overlay_buffer,
0875                     ring_collection_t, ring_collection_t
0876                 >(m_turns, m_clusters))
0877         {
0878             return;
0879         }
0880 
0881         detail::overlay::gather_cluster_properties
0882             <
0883                 false, false, overlay_buffer
0884             >(m_clusters, m_turns, detail::overlay::operation_union,
0885             offsetted_rings, offsetted_rings, m_strategy);
0886 
0887         for (auto const& cluster : m_clusters)
0888         {
0889             if (cluster.second.open_count == 0 && cluster.second.spike_count == 0)
0890             {
0891                 // If the cluster is completely closed, mark it as not traversable.
0892                 for (auto const& index : cluster.second.turn_indices)
0893                 {
0894                     m_turns[index].is_turn_traversable = false;
0895                 }
0896             }
0897         }
0898     }
0899 
0900     inline void make_traversable_consistent_per_cluster()
0901     {
0902         for (auto const& cluster : m_clusters)
0903         {
0904             bool is_traversable = false;
0905             for (auto const& index : cluster.second.turn_indices)
0906             {
0907                 if (m_turns[index].is_turn_traversable)
0908                 {
0909                     // If there is one turn traversable in the cluster,
0910                     // then all turns should be traversable.
0911                     is_traversable = true;
0912                     break;
0913                 }
0914             }
0915             if (is_traversable)
0916             {
0917                 for (auto const& index : cluster.second.turn_indices)
0918                 {
0919                     m_turns[index].is_turn_traversable = true;
0920                 }
0921             }
0922         }
0923     }
0924 
0925     inline void enrich()
0926     {
0927         enrich_intersection_points<false, false, overlay_buffer>(m_turns,
0928             m_clusters, offsetted_rings, offsetted_rings,
0929             m_strategy);
0930     }
0931 
0932     // Discards all rings which do have not-OK intersection points only.
0933     // Those can never be traversed and should not be part of the output.
0934     inline void discard_rings()
0935     {
0936         for (auto const& turn : m_turns)
0937         {
0938             if (turn.is_turn_traversable)
0939             {
0940                 offsetted_rings[turn.operations[0].seg_id.multi_index].has_accepted_intersections = true;
0941                 offsetted_rings[turn.operations[1].seg_id.multi_index].has_accepted_intersections = true;
0942             }
0943             else
0944             {
0945                 offsetted_rings[turn.operations[0].seg_id.multi_index].has_discarded_intersections = true;
0946                 offsetted_rings[turn.operations[1].seg_id.multi_index].has_discarded_intersections = true;
0947             }
0948         }
0949     }
0950 
0951     inline bool point_coveredby_original(point_type const& point)
0952     {
0953         signed_size_type count_in_original = 0;
0954 
0955         // Check of the point of this outputted ring is in
0956         // any of the original rings
0957         // This can go quadratic if the input has many rings, and there
0958         // are many untouched deflated rings around
0959         for (auto const& original : original_rings)
0960         {
0961             if (original.m_ring.empty())
0962             {
0963                 continue;
0964             }
0965             if (detail::disjoint::disjoint_point_box(point, original.m_box,m_strategy))
0966             {
0967                 continue;
0968             }
0969 
0970             int const geometry_code
0971                 = detail::within::point_in_geometry(point, original.m_ring, m_strategy);
0972 
0973             if (geometry_code == -1)
0974             {
0975                 // Outside, continue
0976                 continue;
0977             }
0978 
0979             // Apply for possibly nested interior rings
0980             if (original.m_is_interior)
0981             {
0982                 count_in_original--;
0983             }
0984             else if (original.m_has_interiors)
0985             {
0986                 count_in_original++;
0987             }
0988             else
0989             {
0990                 // Exterior ring without interior rings
0991                 return true;
0992             }
0993         }
0994         return count_in_original > 0;
0995     }
0996 
0997     // For a deflate, all rings around inner rings which are untouched
0998     // (no intersections/turns) and which are OUTSIDE the original should
0999     // be discarded
1000     inline void discard_nonintersecting_deflated_rings()
1001     {
1002         for (auto& ring : offsetted_rings)
1003         {
1004             if (! ring.has_intersections()
1005                 && boost::size(ring) > 0u
1006                 && geometry::area(ring, m_strategy) < 0)
1007             {
1008                 if (! point_coveredby_original(geometry::range::front(ring)))
1009                 {
1010                     ring.is_untouched_outside_original = true;
1011                 }
1012             }
1013         }
1014     }
1015 
1016     inline void block_turns()
1017     {
1018         for (auto& turn : m_turns)
1019         {
1020             if (! turn.is_turn_traversable)
1021             {
1022                 // Discard this turn (don't set it to blocked to avoid colocated
1023                 // clusters being discarded afterwards
1024                 turn.discarded = true;
1025             }
1026         }
1027     }
1028 
1029     inline void traverse()
1030     {
1031         using traverser = detail::overlay::traverse
1032             <
1033                 false, false,
1034                 buffered_ring_collection<buffered_ring<Ring> >,
1035                 buffered_ring_collection<buffered_ring<Ring > >,
1036                 overlay_buffer,
1037                 backtrack_for_buffer
1038             >;
1039         std::map<ring_identifier, overlay::ring_turn_info> turn_info_per_ring;
1040 
1041         traversed_rings.clear();
1042         buffer_overlay_visitor visitor;
1043         traverser::apply(offsetted_rings, offsetted_rings,
1044                         m_strategy,
1045                         m_turns, traversed_rings,
1046                         turn_info_per_ring,
1047                         m_clusters, visitor);
1048     }
1049 
1050     inline void reverse()
1051     {
1052         for (auto& ring : offsetted_rings)
1053         {
1054             if (! ring.has_intersections())
1055             {
1056                 std::reverse(ring.begin(), ring.end());
1057             }
1058         }
1059         for (auto& ring : traversed_rings)
1060         {
1061             std::reverse(ring.begin(), ring.end());
1062         }
1063     }
1064 
1065     template <typename GeometryOutput, typename OutputIterator>
1066     inline OutputIterator assign(OutputIterator out) const
1067     {
1068         using area_result_type = typename geometry::area_result
1069             <
1070                 buffered_ring<Ring>, Strategy
1071             >::type;
1072         using properties = detail::overlay::ring_properties
1073             <
1074                 point_type, area_result_type
1075             >;
1076 
1077         std::map<ring_identifier, properties> selected;
1078 
1079         // Select all rings which do not have any self-intersection
1080         // Inner rings, for deflate, which do not have intersections, and
1081         // which are outside originals, are skipped
1082         // (other ones should be traversed)
1083         for (auto const& enumerated : util::enumerate(offsetted_rings))
1084         {
1085             auto const& ring = enumerated.value;
1086             if (! ring.has_intersections()
1087                 && ! ring.is_untouched_outside_original)
1088             {
1089                 properties const p = properties(ring, m_strategy);
1090                 if (p.valid)
1091                 {
1092                     ring_identifier id(0, enumerated.index, -1);
1093                     selected[id] = p;
1094                 }
1095             }
1096         }
1097 
1098         // Select all created rings
1099         for (auto const& enumerated : util::enumerate(traversed_rings))
1100         {
1101             auto const& ring = enumerated.value;
1102             properties p = properties(ring, m_strategy);
1103             if (p.valid)
1104             {
1105                 ring_identifier id(2, enumerated.index, -1);
1106                 selected[id] = p;
1107             }
1108         }
1109 
1110         detail::overlay::assign_parents<overlay_buffer>(offsetted_rings, traversed_rings,
1111                 selected, m_strategy);
1112         return detail::overlay::add_rings<GeometryOutput>(selected, offsetted_rings, traversed_rings, out,
1113                                                           m_strategy);
1114     }
1115 
1116 };
1117 
1118 
1119 }} // namespace detail::buffer
1120 #endif // DOXYGEN_NO_DETAIL
1121 
1122 
1123 }} // namespace boost::geometry
1124 
1125 #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_BUFFERED_PIECE_COLLECTION_HPP