Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-16 09:49:48

0001 // Boost.Geometry (aka GGL, Generic Geometry Library)
0002 
0003 // Copyright (c) 2020-2021 Barend Gehrels, Amsterdam, the Netherlands.
0004 // Copyright (c) 2023 Adam Wulkiewicz, Lodz, Poland.
0005 
0006 // This file was modified by Oracle on 2020-2022.
0007 // Modifications copyright (c) 2020-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_PIECE_BORDER_HPP
0015 #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_PIECE_BORDER_HPP
0016 
0017 
0018 #include <array>
0019 
0020 #include <boost/core/addressof.hpp>
0021 #include <boost/range/size.hpp>
0022 
0023 #include <boost/geometry/core/assert.hpp>
0024 #include <boost/geometry/core/config.hpp>
0025 
0026 #include <boost/geometry/algorithms/assign.hpp>
0027 #include <boost/geometry/algorithms/comparable_distance.hpp>
0028 #include <boost/geometry/algorithms/equals.hpp>
0029 #include <boost/geometry/algorithms/expand.hpp>
0030 #include <boost/geometry/algorithms/detail/buffer/buffer_policies.hpp>
0031 #include <boost/geometry/algorithms/detail/expand_by_epsilon.hpp>
0032 #include <boost/geometry/strategies/cartesian/turn_in_ring_winding.hpp>
0033 #include <boost/geometry/geometries/box.hpp>
0034 #include <boost/geometry/geometries/segment.hpp>
0035 
0036 
0037 namespace boost { namespace geometry
0038 {
0039 
0040 #ifndef DOXYGEN_NO_DETAIL
0041 namespace detail
0042 {
0043 template <typename It, typename T, typename Compare>
0044 inline bool get_range_around(It begin, It end, T const& value, Compare const& compare, It& lower, It& upper)
0045 {
0046     lower = end;
0047     upper = end;
0048 
0049     // Get first element not smaller than value
0050     if (begin == end)
0051     {
0052         return false;
0053     }
0054     if (compare(value, *begin))
0055     {
0056         // The value is smaller than the first item, therefore not in range
0057         return false;
0058     }
0059     // *(begin + std::distance(begin, end) - 1))
0060     if (compare(*(end - 1), value))
0061     {
0062         // The last item is larger than the value, therefore not in range
0063         return false;
0064     }
0065 
0066     // Assign the iterators.
0067     // lower >= begin and lower < end
0068     // upper > lower and upper <= end
0069     // lower_bound points to first element NOT LESS than value - but because
0070     // we want the first value LESS than value, we decrease it
0071     lower = std::lower_bound(begin, end, value, compare);
0072     // upper_bound points to first element of which value is LESS
0073     upper = std::upper_bound(begin, end, value, compare);
0074 
0075     if (lower != begin)
0076     {
0077         --lower;
0078     }
0079     if (upper != end)
0080     {
0081         ++upper;
0082     }
0083     return true;
0084 }
0085 
0086 }
0087 
0088 
0089 namespace detail { namespace buffer
0090 {
0091 
0092 //! Contains the border of the piece, consisting of 4 parts:
0093 //! 1: the part of the offsetted ring (referenced, not copied)
0094 //! 2: the part of the original (one or two points)
0095 //! 3: the left part (from original to offsetted)
0096 //! 4: the right part (from offsetted to original)
0097 //! Besides that, it contains some properties of the piece(border);
0098 //!   - convexity
0099 //!   - envelope
0100 //!   - monotonicity of the offsetted ring
0101 //!   - min/max radius of a point buffer
0102 //!   - if it is a "reversed" piece (linear features with partly negative buffers)
0103 template <typename Ring, typename Point>
0104 struct piece_border
0105 {
0106     using coordinate_type = geometry::coordinate_type_t<Point>;
0107     using radius_type = typename default_comparable_distance_result<Point>::type;
0108     using state_type = typename geometry::strategy::buffer::turn_in_ring_winding<coordinate_type>::state_type;
0109 
0110     bool m_reversed;
0111 
0112     // Points from the offsetted ring. They are not copied, this structure
0113     // refers to those points
0114     Ring const* m_ring;
0115     std::size_t m_begin;
0116     std::size_t m_end;
0117 
0118     // Points from the original (one or two, depending on piece shape)
0119     // Note, if there are 2 points, they are REVERSED w.r.t. the original
0120     // Therefore here we can walk in its order.
0121     std::array<Point, 2> m_originals;
0122     std::size_t m_original_size;
0123 
0124     geometry::model::box<Point> m_envelope;
0125     bool m_has_envelope;
0126 
0127     // True if piece is determined as "convex"
0128     bool m_is_convex;
0129 
0130     // True if offsetted part is monotonically changing in x-direction
0131     bool m_is_monotonic_increasing;
0132     bool m_is_monotonic_decreasing;
0133 
0134     radius_type m_min_comparable_radius;
0135     radius_type m_max_comparable_radius;
0136 
0137     piece_border()
0138         : m_reversed(false)
0139         , m_ring(NULL)
0140         , m_begin(0)
0141         , m_end(0)
0142         , m_original_size(0)
0143         , m_has_envelope(false)
0144         , m_is_convex(false)
0145         , m_is_monotonic_increasing(false)
0146         , m_is_monotonic_decreasing(false)
0147         , m_min_comparable_radius(0)
0148         , m_max_comparable_radius(0)
0149     {
0150     }
0151 
0152     // Only used for debugging (SVG)
0153     Ring get_full_ring() const
0154     {
0155         Ring result;
0156         if (ring_or_original_empty())
0157         {
0158             return result;
0159         }
0160         std::copy(m_ring->begin() + m_begin,
0161                   m_ring->begin() + m_end,
0162                   std::back_inserter(result));
0163         std::copy(m_originals.begin(),
0164                   m_originals.begin() + m_original_size,
0165                   std::back_inserter(result));
0166         // Add the closing point
0167         result.push_back(*(m_ring->begin() + m_begin));
0168 
0169         return result;
0170     }
0171 
0172     template <typename Strategy>
0173     void get_properties_of_border(bool is_point_buffer, Point const& center,
0174                                   Strategy const& strategy)
0175     {
0176         m_has_envelope = calculate_envelope(m_envelope, strategy);
0177         if (m_has_envelope)
0178         {
0179             // Take roundings into account, enlarge box
0180             geometry::detail::expand_by_epsilon(m_envelope);
0181         }
0182         if (! ring_or_original_empty() && is_point_buffer)
0183         {
0184             // Determine min/max radius
0185             calculate_radii(center, m_ring->begin() + m_begin, m_ring->begin() + m_end);
0186         }
0187     }
0188 
0189     template <typename Strategy>
0190     void get_properties_of_offsetted_ring_part(Strategy const& strategy)
0191     {
0192         if (! ring_or_original_empty())
0193         {
0194             m_is_convex = is_convex(strategy);
0195             check_monotonicity(m_ring->begin() + m_begin, m_ring->begin() + m_end);
0196         }
0197     }
0198 
0199     void set_offsetted(Ring const& ring, std::size_t begin, std::size_t end)
0200     {
0201         BOOST_GEOMETRY_ASSERT(begin <= end);
0202         BOOST_GEOMETRY_ASSERT(begin < boost::size(ring));
0203         BOOST_GEOMETRY_ASSERT(end <= boost::size(ring));
0204 
0205         m_ring = boost::addressof(ring);
0206         m_begin = begin;
0207         m_end = end;
0208     }
0209 
0210     void add_original_point(Point const& point)
0211     {
0212         BOOST_GEOMETRY_ASSERT(m_original_size < 2);
0213         m_originals[m_original_size++] = point;
0214     }
0215 
0216     template <typename Box, typename Strategy>
0217     bool calculate_envelope(Box& envelope, Strategy const& strategy) const
0218     {
0219         geometry::assign_inverse(envelope);
0220         if (ring_or_original_empty())
0221         {
0222             return false;
0223         }
0224         expand_envelope(envelope, m_ring->begin() + m_begin, m_ring->begin() + m_end, strategy);
0225         expand_envelope(envelope, m_originals.begin(), m_originals.begin() + m_original_size, strategy);
0226         return true;
0227     }
0228 
0229 
0230     // Whatever the return value, the state should be checked.
0231     template <typename TurnPoint, typename State>
0232     bool point_on_piece(TurnPoint const& point,
0233                         bool one_sided, bool is_linear_end_point,
0234                         State& state) const
0235     {
0236         if (ring_or_original_empty())
0237         {
0238             return false;
0239         }
0240 
0241         // Walk over the different parts of the ring, in clockwise order
0242         // For performance reasons: start with the helper part (one segment)
0243         // then the original part (one segment, if any), then the other helper
0244         // part (one segment), and only then the offsetted part
0245         // (probably more segments, check monotonicity)
0246         geometry::strategy::buffer::turn_in_ring_winding<coordinate_type> tir;
0247 
0248         Point const offsetted_front = *(m_ring->begin() + m_begin);
0249         Point const offsetted_back = *(m_ring->begin() + m_end - 1);
0250 
0251         // For onesided buffers, or turns colocated with linear end points,
0252         // the place on the ring is changed to offsetted (because of colocation)
0253         geometry::strategy::buffer::place_on_ring_type const por_original
0254             = adapted_place_on_ring(geometry::strategy::buffer::place_on_ring_original,
0255                                     one_sided, is_linear_end_point);
0256         geometry::strategy::buffer::place_on_ring_type const por_from_offsetted
0257             = adapted_place_on_ring(geometry::strategy::buffer::place_on_ring_from_offsetted,
0258                                     one_sided, is_linear_end_point);
0259         geometry::strategy::buffer::place_on_ring_type const por_to_offsetted
0260             = adapted_place_on_ring(geometry::strategy::buffer::place_on_ring_to_offsetted,
0261                                     one_sided, is_linear_end_point);
0262 
0263         bool continue_processing = true;
0264         if (m_original_size == 1)
0265         {
0266             // One point. Walk from last offsetted to point, and from point to first offsetted
0267             continue_processing = step(point, offsetted_back, m_originals[0],
0268                                        tir, por_from_offsetted, state)
0269                                && step(point, m_originals[0], offsetted_front,
0270                                        tir, por_to_offsetted, state);
0271         }
0272         else if (m_original_size == 2)
0273         {
0274             // Two original points. Walk from last offsetted point to first original point,
0275             // then along original, then from second oginal to first offsetted point
0276             continue_processing = step(point, offsetted_back, m_originals[0],
0277                                        tir, por_from_offsetted, state)
0278                                && step(point, m_originals[0], m_originals[1],
0279                                        tir, por_original, state)
0280                                && step(point, m_originals[1], offsetted_front,
0281                                        tir, por_to_offsetted, state);
0282         }
0283 
0284         if (continue_processing)
0285         {
0286             // Check the offsetted ring (in rounded joins, these might be
0287             // several segments)
0288             walk_offsetted(point, m_ring->begin() + m_begin, m_ring->begin() + m_end,
0289                            tir, state);
0290         }
0291 
0292         return true;
0293     }
0294 
0295     //! Returns true if empty (no ring, or no points, or no original)
0296     bool ring_or_original_empty() const
0297     {
0298         return m_ring == NULL || m_begin >= m_end || m_original_size == 0;
0299     }
0300 
0301 private :
0302 
0303     static geometry::strategy::buffer::place_on_ring_type
0304         adapted_place_on_ring(geometry::strategy::buffer::place_on_ring_type target,
0305                               bool one_sided, bool is_linear_end_point)
0306     {
0307         return one_sided || is_linear_end_point
0308                ? geometry::strategy::buffer::place_on_ring_offsetted
0309                : target;
0310     }
0311 
0312     template
0313     <
0314         typename TurnPoint, typename Iterator,
0315         typename TiRStrategy,
0316         typename State
0317     >
0318     bool walk_offsetted(TurnPoint const& point, Iterator begin, Iterator end,
0319                         TiRStrategy const & strategy,
0320                         State& state) const
0321     {
0322         Iterator it = begin;
0323         Iterator beyond = end;
0324 
0325         // Move iterators if the offsetted ring is monotonic increasing or decreasing
0326         if (m_is_monotonic_increasing)
0327         {
0328             if (! get_range_around(begin, end, point, geometry::less<Point, 0>(), it, beyond))
0329             {
0330                 return true;
0331             }
0332         }
0333         else if (m_is_monotonic_decreasing)
0334         {
0335             if (! get_range_around(begin, end, point, geometry::greater<Point, 0>(), it, beyond))
0336             {
0337                 return true;
0338             }
0339         }
0340 
0341         for (Iterator previous = it++ ; it != beyond ; ++previous, ++it )
0342         {
0343             if (! step(point, *previous, *it, strategy,
0344                        geometry::strategy::buffer::place_on_ring_offsetted, state))
0345             {
0346                 return false;
0347             }
0348         }
0349         return true;
0350     }
0351 
0352     template <typename TurnPoint, typename TiRStrategy, typename State>
0353     bool step(TurnPoint const& point, Point const& p1, Point const& p2,
0354               TiRStrategy const& strategy,
0355               geometry::strategy::buffer::place_on_ring_type place_on_ring, State& state) const
0356     {
0357         return strategy.apply(point, p1, p2, place_on_ring, m_is_convex, state);
0358     }
0359 
0360     template <typename It, typename Box, typename Strategy>
0361     void expand_envelope(Box& envelope, It begin, It end, Strategy const& strategy) const
0362     {
0363         for (It it = begin; it != end; ++it)
0364         {
0365             geometry::expand(envelope, *it, strategy);
0366         }
0367     }
0368 
0369     template <typename Strategy>
0370     bool is_convex(Strategy const& strategy) const
0371     {
0372         if (ring_or_original_empty())
0373         {
0374             // Convexity is undetermined, and for this case it does not matter,
0375             // because it is only used for optimization in point_on_piece,
0376             // but that is not called if the piece border is not valid
0377             return false;
0378         }
0379 
0380         if (m_end - m_begin <= 2)
0381         {
0382             // The offsetted ring part of this piece has only two points.
0383             // If this is true, and the original ring part has only one point,
0384             // a triangle and it is convex. If the original ring part has two
0385             // points, it is a rectangle and theoretically could be concave,
0386             // but because of the way the buffer is generated, that is never
0387             // the case.
0388             return true;
0389         }
0390 
0391         // The offsetted ring part of thie piece has at least three points
0392         // (this is often the case in a piece marked as "join")
0393 
0394         // We can assume all points of the offset ring are different, and also
0395         // that all points on the original are different, and that the offsetted
0396         // ring is different from the original(s)
0397         Point const offsetted_front = *(m_ring->begin() + m_begin);
0398         Point const offsetted_second = *(m_ring->begin() + m_begin + 1);
0399 
0400         // These two points will be reassigned in every is_convex call
0401         Point previous = offsetted_front;
0402         Point current = offsetted_second;
0403 
0404         // Verify the offsetted range (from the second point on), the original,
0405         // and loop through the first two points of the offsetted range
0406         bool const result = is_convex(previous, current, m_ring->begin() + m_begin + 2, m_ring->begin() + m_end, strategy)
0407             && is_convex(previous, current, m_originals.begin(), m_originals.begin() + m_original_size, strategy)
0408             && is_convex(previous, current, offsetted_front, strategy)
0409             && is_convex(previous, current, offsetted_second, strategy);
0410 
0411         return result;
0412     }
0413 
0414     template <typename It, typename Strategy>
0415     bool is_convex(Point& previous, Point& current, It begin, It end, Strategy const& strategy) const
0416     {
0417         for (It it = begin; it != end; ++it)
0418         {
0419             if (! is_convex(previous, current, *it, strategy))
0420             {
0421                 return false;
0422             }
0423         }
0424         return true;
0425     }
0426 
0427     template <typename Strategy>
0428     bool is_convex(Point& previous, Point& current, Point const& next, Strategy const& strategy) const
0429     {
0430         int const side = strategy.side().apply(previous, current, next);
0431         if (side == 1)
0432         {
0433             // Next is on the left side of clockwise ring: piece is not convex
0434             return false;
0435         }
0436         if (! equals::equals_point_point(current, next, strategy))
0437         {
0438             previous = current;
0439             current = next;
0440         }
0441         return true;
0442     }
0443 
0444     template <int Direction>
0445     inline void step_for_monotonicity(Point const& current, Point const& next)
0446     {
0447         if (geometry::get<Direction>(current) >= geometry::get<Direction>(next))
0448         {
0449             m_is_monotonic_increasing = false;
0450         }
0451         if (geometry::get<Direction>(current) <= geometry::get<Direction>(next))
0452         {
0453             m_is_monotonic_decreasing = false;
0454         }
0455     }
0456 
0457     template <typename It>
0458     void check_monotonicity(It begin, It end)
0459     {
0460         m_is_monotonic_increasing = true;
0461         m_is_monotonic_decreasing = true;
0462 
0463         if (begin == end || begin + 1 == end)
0464         {
0465             return;
0466         }
0467 
0468         It it = begin;
0469         for (It previous = it++; it != end; ++previous, ++it)
0470         {
0471             step_for_monotonicity<0>(*previous, *it);
0472         }
0473     }
0474 
0475     template <typename It>
0476     inline void calculate_radii(Point const& center, It begin, It end)
0477     {
0478         using segment_type = geometry::model::referring_segment<Point const>;
0479 
0480         bool first = true;
0481 
0482         // An offsetted point-buffer ring around a point is supposed to be closed,
0483         // therefore walking from start to end is fine.
0484         It it = begin;
0485         for (It previous = it++; it != end; ++previous, ++it)
0486         {
0487             Point const& p0 = *previous;
0488             Point const& p1 = *it;
0489             segment_type const s(p0, p1);
0490             radius_type const d = geometry::comparable_distance(center, s);
0491 
0492             if (first || d < m_min_comparable_radius)
0493             {
0494                 m_min_comparable_radius = d;
0495             }
0496             if (first || d > m_max_comparable_radius)
0497             {
0498                 m_max_comparable_radius = d;
0499             }
0500             first = false;
0501         }
0502     }
0503 
0504 };
0505 
0506 }} // namespace detail::buffer
0507 #endif // DOXYGEN_NO_DETAIL
0508 
0509 
0510 }} // namespace boost::geometry
0511 
0512 #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_PIECE_BORDER_HPP