File indexing completed on 2025-12-16 09:49:48
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
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
0050 if (begin == end)
0051 {
0052 return false;
0053 }
0054 if (compare(value, *begin))
0055 {
0056
0057 return false;
0058 }
0059
0060 if (compare(*(end - 1), value))
0061 {
0062
0063 return false;
0064 }
0065
0066
0067
0068
0069
0070
0071 lower = std::lower_bound(begin, end, value, compare);
0072
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
0093
0094
0095
0096
0097
0098
0099
0100
0101
0102
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
0113
0114 Ring const* m_ring;
0115 std::size_t m_begin;
0116 std::size_t m_end;
0117
0118
0119
0120
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
0128 bool m_is_convex;
0129
0130
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
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
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
0180 geometry::detail::expand_by_epsilon(m_envelope);
0181 }
0182 if (! ring_or_original_empty() && is_point_buffer)
0183 {
0184
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
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
0242
0243
0244
0245
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
0252
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
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
0275
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
0287
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
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
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
0375
0376
0377 return false;
0378 }
0379
0380 if (m_end - m_begin <= 2)
0381 {
0382
0383
0384
0385
0386
0387
0388 return true;
0389 }
0390
0391
0392
0393
0394
0395
0396
0397 Point const offsetted_front = *(m_ring->begin() + m_begin);
0398 Point const offsetted_second = *(m_ring->begin() + m_begin + 1);
0399
0400
0401 Point previous = offsetted_front;
0402 Point current = offsetted_second;
0403
0404
0405
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
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
0483
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 }}
0507 #endif
0508
0509
0510 }}
0511
0512 #endif