Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2024-11-15 09:10:15

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