1 // Boost.Geometry (aka GGL, Generic Geometry Library) 2 3 // Copyright (c) 2015 Barend Gehrels, Amsterdam, the Netherlands. 4 5 // This file was modified by Oracle on 2017. 6 // Modifications copyright (c) 2017 Oracle and/or its affiliates. 7 8 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle 9 10 // Use, modification and distribution is subject to the Boost Software License, 11 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at 12 // http://www.boost.org/LICENSE_1_0.txt) 13 14 #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SORT_BY_SIDE_HPP 15 #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SORT_BY_SIDE_HPP 16 17 #include <algorithm> 18 #include <map> 19 #include <vector> 20 21 #include <boost/geometry/algorithms/num_points.hpp> 22 #include <boost/geometry/algorithms/detail/overlay/copy_segment_point.hpp> 23 #include <boost/geometry/algorithms/detail/overlay/get_ring.hpp> 24 #include <boost/geometry/algorithms/detail/direction_code.hpp> 25 #include <boost/geometry/algorithms/detail/overlay/turn_info.hpp> 26 27 namespace boost { namespace geometry 28 { 29 30 #ifndef DOXYGEN_NO_DETAIL 31 namespace detail { namespace overlay { namespace sort_by_side 32 { 33 34 enum direction_type { dir_unknown = -1, dir_from = 0, dir_to = 1 }; 35 36 // Point-wrapper, adding some properties 37 template <typename Point> 38 struct ranked_point 39 { ranked_pointboost::geometry::detail::overlay::sort_by_side::ranked_point40 ranked_point() 41 : rank(0) 42 , turn_index(-1) 43 , operation_index(-1) 44 , direction(dir_unknown) 45 , count_left(0) 46 , count_right(0) 47 , operation(operation_none) 48 {} 49 50 template <typename Op> ranked_pointboost::geometry::detail::overlay::sort_by_side::ranked_point51 ranked_point(const Point& p, signed_size_type ti, int oi, 52 direction_type d, Op op) 53 : point(p) 54 , rank(0) 55 , zone(-1) 56 , turn_index(ti) 57 , operation_index(oi) 58 , direction(d) 59 , count_left(0) 60 , count_right(0) 61 , operation(op.operation) 62 , seg_id(op.seg_id) 63 {} 64 65 Point point; 66 std::size_t rank; 67 signed_size_type zone; // index of closed zone, in uu turn there would be 2 zones 68 signed_size_type turn_index; 69 int operation_index; // 0,1 70 direction_type direction; 71 std::size_t count_left; 72 std::size_t count_right; 73 operation_type operation; 74 segment_identifier seg_id; 75 }; 76 77 struct less_by_turn_index 78 { 79 template <typename T> operator ()boost::geometry::detail::overlay::sort_by_side::less_by_turn_index80 inline bool operator()(const T& first, const T& second) const 81 { 82 return first.turn_index == second.turn_index 83 ? first.index < second.index 84 : first.turn_index < second.turn_index 85 ; 86 } 87 }; 88 89 struct less_by_index 90 { 91 template <typename T> operator ()boost::geometry::detail::overlay::sort_by_side::less_by_index92 inline bool operator()(const T& first, const T& second) const 93 { 94 // First order by from/to 95 if (first.direction != second.direction) 96 { 97 return first.direction < second.direction; 98 } 99 // All the same, order by turn index (we might consider length too) 100 return first.turn_index < second.turn_index; 101 } 102 }; 103 104 struct less_false 105 { 106 template <typename T> operator ()boost::geometry::detail::overlay::sort_by_side::less_false107 inline bool operator()(const T&, const T& ) const 108 { 109 return false; 110 } 111 }; 112 113 template <typename Point, typename SideStrategy, typename LessOnSame, typename Compare> 114 struct less_by_side 115 { less_by_sideboost::geometry::detail::overlay::sort_by_side::less_by_side116 less_by_side(const Point& p1, const Point& p2, SideStrategy const& strategy) 117 : m_p1(p1) 118 , m_p2(p2) 119 , m_strategy(strategy) 120 {} 121 122 template <typename T> operator ()boost::geometry::detail::overlay::sort_by_side::less_by_side123 inline bool operator()(const T& first, const T& second) const 124 { 125 LessOnSame on_same; 126 Compare compare; 127 128 int const side_first = m_strategy.apply(m_p1, m_p2, first.point); 129 int const side_second = m_strategy.apply(m_p1, m_p2, second.point); 130 131 if (side_first == 0 && side_second == 0) 132 { 133 // Both collinear. They might point into different directions: <------*------> 134 // If so, order the one going backwards as the very first. 135 136 int const first_code = direction_code(m_p1, m_p2, first.point); 137 int const second_code = direction_code(m_p1, m_p2, second.point); 138 139 // Order by code, backwards first, then forward. 140 return first_code != second_code 141 ? first_code < second_code 142 : on_same(first, second) 143 ; 144 } 145 else if (side_first == 0 146 && direction_code(m_p1, m_p2, first.point) == -1) 147 { 148 // First collinear and going backwards. 149 // Order as the very first, so return always true 150 return true; 151 } 152 else if (side_second == 0 153 && direction_code(m_p1, m_p2, second.point) == -1) 154 { 155 // Second is collinear and going backwards 156 // Order as very last, so return always false 157 return false; 158 } 159 160 // They are not both collinear 161 162 if (side_first != side_second) 163 { 164 return compare(side_first, side_second); 165 } 166 167 // They are both left, both right, and/or both collinear (with each other and/or with p1,p2) 168 // Check mutual side 169 int const side_second_wrt_first = m_strategy.apply(m_p2, first.point, second.point); 170 171 if (side_second_wrt_first == 0) 172 { 173 return on_same(first, second); 174 } 175 176 int const side_first_wrt_second = -side_second_wrt_first; 177 178 // Both are on same side, and not collinear 179 // Union: return true if second is right w.r.t. first, so -1, 180 // so other is 1. union has greater as compare functor 181 // Intersection: v.v. 182 return compare(side_first_wrt_second, side_second_wrt_first); 183 } 184 185 private : 186 Point m_p1, m_p2; 187 SideStrategy const& m_strategy; 188 }; 189 190 // Sorts vectors in counter clockwise order (by default) 191 template 192 < 193 bool Reverse1, 194 bool Reverse2, 195 overlay_type OverlayType, 196 typename Point, 197 typename SideStrategy, 198 typename Compare 199 > 200 struct side_sorter 201 { 202 typedef ranked_point<Point> rp; 203 204 private : 205 struct include_union 206 { operator ()boost::geometry::detail::overlay::sort_by_side::side_sorter::include_union207 inline bool operator()(rp const& ranked_point) const 208 { 209 // New candidate if there are no polygons on left side, 210 // but there are on right side 211 return ranked_point.count_left == 0 212 && ranked_point.count_right > 0; 213 } 214 }; 215 216 struct include_intersection 217 { operator ()boost::geometry::detail::overlay::sort_by_side::side_sorter::include_intersection218 inline bool operator()(rp const& ranked_point) const 219 { 220 // New candidate if there are two polygons on right side, 221 // and less on the left side 222 return ranked_point.count_left < 2 223 && ranked_point.count_right >= 2; 224 } 225 }; 226 227 public : side_sorterboost::geometry::detail::overlay::sort_by_side::side_sorter228 side_sorter(SideStrategy const& strategy) 229 : m_origin_count(0) 230 , m_origin_segment_distance(0) 231 , m_strategy(strategy) 232 {} 233 234 template <typename Operation, typename Geometry1, typename Geometry2> addboost::geometry::detail::overlay::sort_by_side::side_sorter235 Point add(Operation const& op, signed_size_type turn_index, signed_size_type op_index, 236 Geometry1 const& geometry1, 237 Geometry2 const& geometry2, 238 bool is_origin) 239 { 240 Point point1, point2, point3; 241 geometry::copy_segment_points<Reverse1, Reverse2>(geometry1, geometry2, 242 op.seg_id, point1, point2, point3); 243 Point const& point_to = op.fraction.is_one() ? point3 : point2; 244 245 m_ranked_points.push_back(rp(point1, turn_index, op_index, dir_from, op)); 246 m_ranked_points.push_back(rp(point_to, turn_index, op_index, dir_to, op)); 247 if (is_origin) 248 { 249 m_origin = point1; 250 m_origin_count++; 251 } 252 return point1; 253 } 254 255 template <typename Operation, typename Geometry1, typename Geometry2> addboost::geometry::detail::overlay::sort_by_side::side_sorter256 void add(Operation const& op, signed_size_type turn_index, signed_size_type op_index, 257 segment_identifier const& departure_seg_id, 258 Geometry1 const& geometry1, 259 Geometry2 const& geometry2, 260 bool check_origin) 261 { 262 Point const point1 = add(op, turn_index, op_index, geometry1, geometry2, false); 263 264 if (check_origin) 265 { 266 bool const is_origin 267 = op.seg_id.source_index == departure_seg_id.source_index 268 && op.seg_id.ring_index == departure_seg_id.ring_index 269 && op.seg_id.multi_index == departure_seg_id.multi_index; 270 271 if (is_origin) 272 { 273 int const segment_distance = calculate_segment_distance(op, departure_seg_id, geometry1, geometry2); 274 if (m_origin_count == 0 || 275 segment_distance < m_origin_segment_distance) 276 { 277 m_origin = point1; 278 m_origin_segment_distance = segment_distance; 279 } 280 m_origin_count++; 281 } 282 } 283 } 284 285 template <typename Operation, typename Geometry1, typename Geometry2> calculate_segment_distanceboost::geometry::detail::overlay::sort_by_side::side_sorter286 static int calculate_segment_distance(Operation const& op, 287 segment_identifier const& departure_seg_id, 288 Geometry1 const& geometry1, 289 Geometry2 const& geometry2) 290 { 291 if (op.seg_id.segment_index >= departure_seg_id.segment_index) 292 { 293 return op.seg_id.segment_index - departure_seg_id.segment_index; 294 } 295 // Take wrap into account 296 // Suppose ring_count=10 (10 points, 9 segments), dep.seg_id=7, op.seg_id=2, then distance=10-9+2 297 // Generic function (is this used somewhere else too?) 298 ring_identifier const rid(op.seg_id.source_index, op.seg_id.multi_index, op.seg_id.ring_index); 299 int const segment_count 300 (op.seg_id.source_index == 0 301 ? geometry::num_points(detail::overlay::get_ring<typename geometry::tag<Geometry1>::type>::apply(rid, geometry1)) 302 : geometry::num_points(detail::overlay::get_ring<typename geometry::tag<Geometry2>::type>::apply(rid, geometry2))); 303 return ((segment_count - 1) - departure_seg_id.segment_index) + op.seg_id.segment_index; 304 } 305 applyboost::geometry::detail::overlay::sort_by_side::side_sorter306 void apply(Point const& turn_point) 307 { 308 // We need three compare functors: 309 // 1) to order clockwise (union) or counter clockwise (intersection) 310 // 2) to order by side, resulting in unique ranks for all points 311 // 3) to order by side, resulting in non-unique ranks 312 // to give colinear points 313 314 // Sort by side and assign rank 315 less_by_side<Point, SideStrategy, less_by_index, Compare> less_unique(m_origin, turn_point, m_strategy); 316 less_by_side<Point, SideStrategy, less_false, Compare> less_non_unique(m_origin, turn_point, m_strategy); 317 318 std::sort(m_ranked_points.begin(), m_ranked_points.end(), less_unique); 319 320 std::size_t colinear_rank = 0; 321 for (std::size_t i = 0; i < m_ranked_points.size(); i++) 322 { 323 if (i > 0 324 && less_non_unique(m_ranked_points[i - 1], m_ranked_points[i])) 325 { 326 // It is not collinear 327 colinear_rank++; 328 } 329 330 m_ranked_points[i].rank = colinear_rank; 331 } 332 } 333 334 template <signed_size_type segment_identifier::*Member, typename Map> find_open_genericboost::geometry::detail::overlay::sort_by_side::side_sorter335 void find_open_generic(Map& handled, bool check) 336 { 337 for (std::size_t i = 0; i < m_ranked_points.size(); i++) 338 { 339 const rp& ranked = m_ranked_points[i]; 340 if (ranked.direction != dir_from) 341 { 342 continue; 343 } 344 345 signed_size_type const& index = ranked.seg_id.*Member; 346 if (check && (index < 0 || index > 1)) 347 { 348 // Should not occur 349 continue; 350 } 351 if (! handled[index]) 352 { 353 find_polygons_for_source<Member>(index, i); 354 handled[index] = true; 355 } 356 } 357 } 358 find_openboost::geometry::detail::overlay::sort_by_side::side_sorter359 void find_open() 360 { 361 if (OverlayType == overlay_buffer) 362 { 363 // For buffers, use piece index 364 std::map<signed_size_type, bool> handled; 365 find_open_generic 366 < 367 &segment_identifier::piece_index 368 >(handled, false); 369 } 370 else 371 { 372 // For other operations, by source (there should only source 0,1) 373 bool handled[2] = {false, false}; 374 find_open_generic 375 < 376 &segment_identifier::source_index 377 >(handled, true); 378 } 379 } 380 reverseboost::geometry::detail::overlay::sort_by_side::side_sorter381 void reverse() 382 { 383 if (m_ranked_points.empty()) 384 { 385 return; 386 } 387 388 std::size_t const last = 1 + m_ranked_points.back().rank; 389 390 // Move iterator after rank==0 391 bool has_first = false; 392 typename container_type::iterator it = m_ranked_points.begin() + 1; 393 for (; it != m_ranked_points.end() && it->rank == 0; ++it) 394 { 395 has_first = true; 396 } 397 398 if (has_first) 399 { 400 // Reverse first part (having rank == 0), if any, 401 // but skip the very first row 402 std::reverse(m_ranked_points.begin() + 1, it); 403 for (typename container_type::iterator fit = m_ranked_points.begin(); 404 fit != it; ++fit) 405 { 406 BOOST_ASSERT(fit->rank == 0); 407 } 408 } 409 410 // Reverse the rest (main rank > 0) 411 std::reverse(it, m_ranked_points.end()); 412 for (; it != m_ranked_points.end(); ++it) 413 { 414 BOOST_ASSERT(it->rank > 0); 415 it->rank = last - it->rank; 416 } 417 } 418 has_originboost::geometry::detail::overlay::sort_by_side::side_sorter419 bool has_origin() const 420 { 421 return m_origin_count > 0; 422 } 423 424 //private : 425 426 typedef std::vector<rp> container_type; 427 container_type m_ranked_points; 428 Point m_origin; 429 std::size_t m_origin_count; 430 int m_origin_segment_distance; 431 SideStrategy m_strategy; 432 433 private : 434 435 //! Check how many open spaces there are 436 template <typename Include> open_countboost::geometry::detail::overlay::sort_by_side::side_sorter437 inline std::size_t open_count(Include const& include_functor) const 438 { 439 std::size_t result = 0; 440 std::size_t last_rank = 0; 441 for (std::size_t i = 0; i < m_ranked_points.size(); i++) 442 { 443 rp const& ranked_point = m_ranked_points[i]; 444 445 if (ranked_point.rank > last_rank 446 && ranked_point.direction == sort_by_side::dir_to 447 && include_functor(ranked_point)) 448 { 449 result++; 450 last_rank = ranked_point.rank; 451 } 452 } 453 return result; 454 } 455 moveboost::geometry::detail::overlay::sort_by_side::side_sorter456 std::size_t move(std::size_t index) const 457 { 458 std::size_t const result = index + 1; 459 return result >= m_ranked_points.size() ? 0 : result; 460 } 461 462 //! member is pointer to member (source_index or multi_index) 463 template <signed_size_type segment_identifier::*Member> moveboost::geometry::detail::overlay::sort_by_side::side_sorter464 std::size_t move(signed_size_type member_index, std::size_t index) const 465 { 466 std::size_t result = move(index); 467 while (m_ranked_points[result].seg_id.*Member != member_index) 468 { 469 result = move(result); 470 } 471 return result; 472 } 473 assign_ranksboost::geometry::detail::overlay::sort_by_side::side_sorter474 void assign_ranks(std::size_t min_rank, std::size_t max_rank, int side_index) 475 { 476 for (std::size_t i = 0; i < m_ranked_points.size(); i++) 477 { 478 rp& ranked = m_ranked_points[i]; 479 // Suppose there are 8 ranks, if min=4,max=6: assign 4,5,6 480 // if min=5,max=2: assign from 5,6,7,1,2 481 bool const in_range 482 = max_rank >= min_rank 483 ? ranked.rank >= min_rank && ranked.rank <= max_rank 484 : ranked.rank >= min_rank || ranked.rank <= max_rank 485 ; 486 487 if (in_range) 488 { 489 if (side_index == 1) 490 { 491 ranked.count_left++; 492 } 493 else if (side_index == 2) 494 { 495 ranked.count_right++; 496 } 497 } 498 } 499 } 500 501 template <signed_size_type segment_identifier::*Member> find_polygons_for_sourceboost::geometry::detail::overlay::sort_by_side::side_sorter502 void find_polygons_for_source(signed_size_type the_index, 503 std::size_t start_index) 504 { 505 bool in_polygon = true; // Because start_index is "from", arrives at the turn 506 rp const& start_rp = m_ranked_points[start_index]; 507 std::size_t last_from_rank = start_rp.rank; 508 std::size_t previous_rank = start_rp.rank; 509 510 for (std::size_t index = move<Member>(the_index, start_index); 511 ; 512 index = move<Member>(the_index, index)) 513 { 514 rp& ranked = m_ranked_points[index]; 515 516 if (ranked.rank != previous_rank && ! in_polygon) 517 { 518 assign_ranks(last_from_rank, previous_rank - 1, 1); 519 assign_ranks(last_from_rank + 1, previous_rank, 2); 520 } 521 522 if (index == start_index) 523 { 524 return; 525 } 526 527 if (ranked.direction == dir_from) 528 { 529 last_from_rank = ranked.rank; 530 in_polygon = true; 531 } 532 else if (ranked.direction == dir_to) 533 { 534 in_polygon = false; 535 } 536 537 previous_rank = ranked.rank; 538 } 539 } 540 541 //! Find closed zones and assign it 542 template <typename Include> assign_zonesboost::geometry::detail::overlay::sort_by_side::side_sorter543 std::size_t assign_zones(Include const& include_functor) 544 { 545 // Find a starting point (the first rank after an outgoing rank 546 // with no polygons on the left side) 547 std::size_t start_rank = m_ranked_points.size() + 1; 548 std::size_t start_index = 0; 549 std::size_t max_rank = 0; 550 for (std::size_t i = 0; i < m_ranked_points.size(); i++) 551 { 552 rp const& ranked_point = m_ranked_points[i]; 553 if (ranked_point.rank > max_rank) 554 { 555 max_rank = ranked_point.rank; 556 } 557 if (ranked_point.direction == sort_by_side::dir_to 558 && include_functor(ranked_point)) 559 { 560 start_rank = ranked_point.rank + 1; 561 } 562 if (ranked_point.rank == start_rank && start_index == 0) 563 { 564 start_index = i; 565 } 566 } 567 568 // Assign the zones 569 std::size_t const undefined_rank = max_rank + 1; 570 std::size_t zone_id = 0; 571 std::size_t last_rank = 0; 572 std::size_t rank_at_next_zone = undefined_rank; 573 std::size_t index = start_index; 574 for (std::size_t i = 0; i < m_ranked_points.size(); i++) 575 { 576 rp& ranked_point = m_ranked_points[index]; 577 578 // Implement cyclic behavior 579 index++; 580 if (index == m_ranked_points.size()) 581 { 582 index = 0; 583 } 584 585 if (ranked_point.rank != last_rank) 586 { 587 if (ranked_point.rank == rank_at_next_zone) 588 { 589 zone_id++; 590 rank_at_next_zone = undefined_rank; 591 } 592 593 if (ranked_point.direction == sort_by_side::dir_to 594 && include_functor(ranked_point)) 595 { 596 rank_at_next_zone = ranked_point.rank + 1; 597 if (rank_at_next_zone > max_rank) 598 { 599 rank_at_next_zone = 0; 600 } 601 } 602 603 last_rank = ranked_point.rank; 604 } 605 606 ranked_point.zone = zone_id; 607 } 608 return zone_id; 609 } 610 611 public : open_countboost::geometry::detail::overlay::sort_by_side::side_sorter612 inline std::size_t open_count(operation_type for_operation) const 613 { 614 return for_operation == operation_union 615 ? open_count(include_union()) 616 : open_count(include_intersection()) 617 ; 618 } 619 assign_zonesboost::geometry::detail::overlay::sort_by_side::side_sorter620 inline std::size_t assign_zones(operation_type for_operation) 621 { 622 return for_operation == operation_union 623 ? assign_zones(include_union()) 624 : assign_zones(include_intersection()) 625 ; 626 } 627 628 }; 629 630 631 }}} // namespace detail::overlay::sort_by_side 632 #endif //DOXYGEN_NO_DETAIL 633 634 635 }} // namespace boost::geometry 636 637 #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SORT_BY_SIDE_HPP 638