traversal.hpp 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
  3. // Copyright (c) 2023-2024 Adam Wulkiewicz, Lodz, Poland.
  4. // This file was modified by Oracle on 2017-2020.
  5. // Modifications copyright (c) 2017-2020 Oracle and/or its affiliates.
  6. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
  7. // Use, modification and distribution is subject to the Boost Software License,
  8. // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  9. // http://www.boost.org/LICENSE_1_0.txt)
  10. #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSAL_HPP
  11. #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSAL_HPP
  12. #include <cstddef>
  13. #include <set>
  14. #include <boost/range/begin.hpp>
  15. #include <boost/range/end.hpp>
  16. #include <boost/range/value_type.hpp>
  17. #include <boost/geometry/algorithms/detail/overlay/cluster_info.hpp>
  18. #include <boost/geometry/algorithms/detail/overlay/cluster_exits.hpp>
  19. #include <boost/geometry/algorithms/detail/overlay/is_self_turn.hpp>
  20. #include <boost/geometry/algorithms/detail/overlay/sort_by_side.hpp>
  21. #include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
  22. #include <boost/geometry/core/assert.hpp>
  23. #include <boost/geometry/util/constexpr.hpp>
  24. #if defined(BOOST_GEOMETRY_DEBUG_INTERSECTION) \
  25. || defined(BOOST_GEOMETRY_OVERLAY_REPORT_WKT) \
  26. || defined(BOOST_GEOMETRY_DEBUG_TRAVERSE)
  27. # include <string>
  28. # include <boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp>
  29. # include <boost/geometry/io/wkt/wkt.hpp>
  30. #endif
  31. namespace boost { namespace geometry
  32. {
  33. #ifndef DOXYGEN_NO_DETAIL
  34. namespace detail { namespace overlay
  35. {
  36. template <typename Turn, typename Operation>
  37. #ifdef BOOST_GEOMETRY_DEBUG_TRAVERSE
  38. inline void debug_traverse(Turn const& turn, Operation op,
  39. std::string const& header, bool condition = true)
  40. {
  41. if (! condition)
  42. {
  43. return;
  44. }
  45. std::cout << " " << header
  46. << " at " << op.seg_id
  47. << " meth: " << method_char(turn.method)
  48. << " op: " << operation_char(op.operation)
  49. << " vis: " << visited_char(op.visited)
  50. << " of: " << operation_char(turn.operations[0].operation)
  51. << operation_char(turn.operations[1].operation)
  52. << " " << geometry::wkt(turn.point)
  53. << std::endl;
  54. if (boost::contains(header, "Finished"))
  55. {
  56. std::cout << std::endl;
  57. }
  58. }
  59. #else
  60. inline void debug_traverse(Turn const& , Operation, const char*, bool = true)
  61. {
  62. }
  63. #endif
  64. template
  65. <
  66. bool Reverse1,
  67. bool Reverse2,
  68. overlay_type OverlayType,
  69. typename Geometry1,
  70. typename Geometry2,
  71. typename Turns,
  72. typename Clusters,
  73. typename RobustPolicy,
  74. typename Strategy,
  75. typename Visitor
  76. >
  77. struct traversal
  78. {
  79. private :
  80. static const operation_type target_operation = operation_from_overlay<OverlayType>::value;
  81. typedef typename sort_by_side::side_compare<target_operation>::type side_compare_type;
  82. typedef typename boost::range_value<Turns>::type turn_type;
  83. typedef typename turn_type::turn_operation_type turn_operation_type;
  84. typedef typename geometry::point_type<Geometry1>::type point_type;
  85. typedef sort_by_side::side_sorter
  86. <
  87. Reverse1, Reverse2, OverlayType,
  88. point_type, Strategy, side_compare_type
  89. > sbs_type;
  90. public :
  91. inline traversal(Geometry1 const& geometry1, Geometry2 const& geometry2,
  92. Turns& turns, Clusters const& clusters,
  93. RobustPolicy const& robust_policy, Strategy const& strategy,
  94. Visitor& visitor)
  95. : m_geometry1(geometry1)
  96. , m_geometry2(geometry2)
  97. , m_turns(turns)
  98. , m_clusters(clusters)
  99. , m_robust_policy(robust_policy)
  100. , m_strategy(strategy)
  101. , m_visitor(visitor)
  102. {
  103. }
  104. template <typename TurnInfoMap>
  105. inline void finalize_visit_info(TurnInfoMap& turn_info_map)
  106. {
  107. for (auto& turn : m_turns)
  108. {
  109. for (int i = 0; i < 2; i++)
  110. {
  111. turn_operation_type& op = turn.operations[i];
  112. if (op.visited.visited()
  113. || op.visited.started()
  114. || op.visited.finished() )
  115. {
  116. ring_identifier const ring_id = ring_id_by_seg_id(op.seg_id);
  117. turn_info_map[ring_id].has_traversed_turn = true;
  118. if (op.operation == operation_continue)
  119. {
  120. // Continue operations should mark the other operation
  121. // as traversed too
  122. turn_operation_type& other_op = turn.operations[1 - i];
  123. ring_identifier const other_ring_id
  124. = ring_id_by_seg_id(other_op.seg_id);
  125. turn_info_map[other_ring_id].has_traversed_turn = true;
  126. }
  127. }
  128. op.visited.finalize();
  129. }
  130. }
  131. }
  132. //! Sets visited for ALL turns traveling to the same turn
  133. inline void set_visited_in_cluster(signed_size_type cluster_id,
  134. signed_size_type rank)
  135. {
  136. auto mit = m_clusters.find(cluster_id);
  137. BOOST_ASSERT(mit != m_clusters.end());
  138. cluster_info const& cinfo = mit->second;
  139. for (auto turn_index : cinfo.turn_indices)
  140. {
  141. turn_type& turn = m_turns[turn_index];
  142. for (auto& op : turn.operations)
  143. {
  144. if (op.visited.none() && op.enriched.rank == rank)
  145. {
  146. op.visited.set_visited();
  147. }
  148. }
  149. }
  150. }
  151. inline void set_visited(turn_type& turn, turn_operation_type& op)
  152. {
  153. if (op.operation == detail::overlay::operation_continue)
  154. {
  155. // On "continue", all go in same direction so set "visited" for ALL
  156. for (int i = 0; i < 2; i++)
  157. {
  158. turn_operation_type& turn_op = turn.operations[i];
  159. if (turn_op.visited.none())
  160. {
  161. turn_op.visited.set_visited();
  162. }
  163. }
  164. }
  165. else
  166. {
  167. op.visited.set_visited();
  168. }
  169. if (turn.is_clustered())
  170. {
  171. set_visited_in_cluster(turn.cluster_id, op.enriched.rank);
  172. }
  173. }
  174. inline bool is_visited(turn_type const& , turn_operation_type const& op,
  175. signed_size_type , int) const
  176. {
  177. return op.visited.visited();
  178. }
  179. template <signed_size_type segment_identifier::*Member>
  180. inline bool select_source_generic(turn_type const& turn,
  181. segment_identifier const& current,
  182. segment_identifier const& previous) const
  183. {
  184. turn_operation_type const& op0 = turn.operations[0];
  185. turn_operation_type const& op1 = turn.operations[1];
  186. bool const switch_source = op0.enriched.region_id != -1
  187. && op0.enriched.region_id == op1.enriched.region_id;
  188. #if defined(BOOST_GEOMETRY_DEBUG_TRAVERSAL_SWITCH_DETECTOR)
  189. if (switch_source)
  190. {
  191. std::cout << "Switch source at " << &turn << std::endl;
  192. }
  193. else
  194. {
  195. std::cout << "DON'T SWITCH SOURCES at " << &turn << std::endl;
  196. }
  197. #endif
  198. return switch_source
  199. ? current.*Member != previous.*Member
  200. : current.*Member == previous.*Member;
  201. }
  202. inline bool select_source(turn_type const& turn,
  203. segment_identifier const& candidate_seg_id,
  204. segment_identifier const& previous_seg_id) const
  205. {
  206. // For uu/ii, only switch sources if indicated
  207. if BOOST_GEOMETRY_CONSTEXPR (OverlayType == overlay_buffer)
  208. {
  209. // Buffer does not use source_index (always 0).
  210. return select_source_generic<&segment_identifier::multi_index>(
  211. turn, candidate_seg_id, previous_seg_id);
  212. }
  213. else // else prevents unreachable code warning
  214. {
  215. if (is_self_turn<OverlayType>(turn))
  216. {
  217. // Also, if it is a self-turn, stay on same ring (multi/ring)
  218. return select_source_generic<&segment_identifier::multi_index>(
  219. turn, candidate_seg_id, previous_seg_id);
  220. }
  221. // Use source_index
  222. return select_source_generic<&segment_identifier::source_index>(
  223. turn, candidate_seg_id, previous_seg_id);
  224. }
  225. }
  226. inline bool traverse_possible(signed_size_type turn_index) const
  227. {
  228. if (turn_index == -1)
  229. {
  230. return false;
  231. }
  232. turn_type const& turn = m_turns[turn_index];
  233. // It is not a dead end if there is an operation to continue, or of
  234. // there is a cluster (assuming for now we can get out of the cluster)
  235. return turn.is_clustered()
  236. || turn.has(target_operation)
  237. || turn.has(operation_continue);
  238. }
  239. inline std::size_t get_shortcut_level(turn_operation_type const& op,
  240. signed_size_type start_turn_index,
  241. signed_size_type origin_turn_index,
  242. std::size_t level = 1) const
  243. {
  244. signed_size_type next_turn_index = op.enriched.get_next_turn_index();
  245. if (next_turn_index == -1)
  246. {
  247. return 0;
  248. }
  249. if (next_turn_index == start_turn_index)
  250. {
  251. // This operation finishes the ring
  252. return 0;
  253. }
  254. if (next_turn_index == origin_turn_index)
  255. {
  256. // This operation travels to itself
  257. return level;
  258. }
  259. if (level > 10)
  260. {
  261. // Avoid infinite recursion
  262. return 0;
  263. }
  264. turn_type const& next_turn = m_turns[next_turn_index];
  265. for (int i = 0; i < 2; i++)
  266. {
  267. turn_operation_type const& next_op = next_turn.operations[i];
  268. if (next_op.operation == target_operation
  269. && ! next_op.visited.finished()
  270. && ! next_op.visited.visited())
  271. {
  272. // Recursively continue verifying
  273. if (get_shortcut_level(next_op, start_turn_index,
  274. origin_turn_index, level + 1))
  275. {
  276. return level + 1;
  277. }
  278. }
  279. }
  280. return 0;
  281. }
  282. inline
  283. bool select_cc_operation(turn_type const& turn,
  284. signed_size_type start_turn_index,
  285. int& selected_op_index) const
  286. {
  287. // For "cc", take either one, but if there is a starting one,
  288. // take that one. If next is dead end, skip that one.
  289. // If both are valid candidates, take the one with minimal remaining
  290. // distance (important for #mysql_23023665 in buffer).
  291. signed_size_type next[2] = {0};
  292. bool possible[2] = {0};
  293. bool close[2] = {0};
  294. for (int i = 0; i < 2; i++)
  295. {
  296. next[i] = turn.operations[i].enriched.get_next_turn_index();
  297. possible[i] = traverse_possible(next[i]);
  298. close[i] = possible[i] && next[i] == start_turn_index;
  299. }
  300. if (close[0] != close[1])
  301. {
  302. // One of the operations will finish the ring. Take that one.
  303. selected_op_index = close[0] ? 0 : 1;
  304. debug_traverse(turn, turn.operations[selected_op_index], "Candidate cc closing");
  305. return true;
  306. }
  307. if BOOST_GEOMETRY_CONSTEXPR (OverlayType == overlay_buffer)
  308. {
  309. if (possible[0] && possible[1])
  310. {
  311. // Buffers sometimes have multiple overlapping pieces, where remaining
  312. // distance could lead to the wrong choice. Take the matching operation.
  313. bool is_target[2] = {0};
  314. for (int i = 0; i < 2; i++)
  315. {
  316. turn_operation_type const& next_op = m_turns[next[i]].operations[i];
  317. is_target[i] = next_op.operation == target_operation;
  318. }
  319. if (is_target[0] != is_target[1])
  320. {
  321. // Take the matching operation
  322. selected_op_index = is_target[0] ? 0 : 1;
  323. debug_traverse(turn, turn.operations[selected_op_index], "Candidate cc target");
  324. return true;
  325. }
  326. }
  327. }
  328. static bool const is_union = target_operation == operation_union;
  329. typename turn_operation_type::comparable_distance_type
  330. best_remaining_distance = 0;
  331. bool result = false;
  332. for (int i = 0; i < 2; i++)
  333. {
  334. if (!possible[i])
  335. {
  336. continue;
  337. }
  338. turn_operation_type const& op = turn.operations[i];
  339. if (! result
  340. || (is_union && op.remaining_distance > best_remaining_distance)
  341. || (!is_union && op.remaining_distance < best_remaining_distance))
  342. {
  343. debug_traverse(turn, op, "First candidate cc", ! result);
  344. debug_traverse(turn, op, "Candidate cc override (remaining)",
  345. result && op.remaining_distance < best_remaining_distance);
  346. selected_op_index = i;
  347. best_remaining_distance = op.remaining_distance;
  348. result = true;
  349. }
  350. }
  351. return result;
  352. }
  353. inline
  354. bool select_noncc_operation(turn_type const& turn,
  355. segment_identifier const& previous_seg_id,
  356. int& selected_op_index) const
  357. {
  358. bool result = false;
  359. for (int i = 0; i < 2; i++)
  360. {
  361. turn_operation_type const& op = turn.operations[i];
  362. if (op.operation == target_operation
  363. && ! op.visited.finished()
  364. && ! op.visited.visited()
  365. && (! result || select_source(turn, op.seg_id, previous_seg_id)))
  366. {
  367. selected_op_index = i;
  368. debug_traverse(turn, op, "Candidate");
  369. result = true;
  370. }
  371. }
  372. return result;
  373. }
  374. inline
  375. bool select_preferred_operation(turn_type const& turn,
  376. signed_size_type turn_index,
  377. signed_size_type start_turn_index,
  378. int& selected_op_index) const
  379. {
  380. bool option[2] = {0};
  381. bool finishing[2] = {0};
  382. bool preferred[2] = {0};
  383. std::size_t shortcut_level[2] = {0};
  384. for (int i = 0; i < 2; i++)
  385. {
  386. turn_operation_type const& op = turn.operations[i];
  387. if (op.operation == target_operation
  388. && ! op.visited.finished()
  389. && ! op.visited.visited())
  390. {
  391. option[i] = true;
  392. if (op.enriched.get_next_turn_index() == start_turn_index)
  393. {
  394. finishing[i] = true;
  395. }
  396. else
  397. {
  398. shortcut_level[i] = get_shortcut_level(op, start_turn_index,
  399. turn_index);
  400. }
  401. if (op.enriched.prefer_start)
  402. {
  403. preferred[i] = true;
  404. }
  405. }
  406. }
  407. if (option[0] != option[1])
  408. {
  409. // Only one operation is acceptable, take that one
  410. selected_op_index = option[0] ? 0 : 1;
  411. return true;
  412. }
  413. if (option[0] && option[1])
  414. {
  415. // Both operations are acceptable
  416. if (finishing[0] != finishing[1])
  417. {
  418. // Prefer operation finishing the ring
  419. selected_op_index = finishing[0] ? 0 : 1;
  420. return true;
  421. }
  422. if (shortcut_level[0] != shortcut_level[1])
  423. {
  424. // If a turn can travel to itself again (without closing the
  425. // ring), take the shortest one
  426. selected_op_index = shortcut_level[0] < shortcut_level[1] ? 0 : 1;
  427. return true;
  428. }
  429. if (preferred[0] != preferred[1])
  430. {
  431. // Only one operation is preferred (== was not intersection)
  432. selected_op_index = preferred[0] ? 0 : 1;
  433. return true;
  434. }
  435. }
  436. for (int i = 0; i < 2; i++)
  437. {
  438. if (option[i])
  439. {
  440. selected_op_index = 0;
  441. return true;
  442. }
  443. }
  444. return false;
  445. }
  446. inline
  447. bool select_operation(turn_type const& turn,
  448. signed_size_type turn_index,
  449. signed_size_type start_turn_index,
  450. segment_identifier const& previous_seg_id,
  451. int& selected_op_index) const
  452. {
  453. bool result = false;
  454. selected_op_index = -1;
  455. if (turn.both(operation_continue))
  456. {
  457. result = select_cc_operation(turn, start_turn_index,
  458. selected_op_index);
  459. }
  460. else if BOOST_GEOMETRY_CONSTEXPR (OverlayType == overlay_dissolve)
  461. {
  462. result = select_preferred_operation(turn, turn_index,
  463. start_turn_index, selected_op_index);
  464. }
  465. else
  466. {
  467. result = select_noncc_operation(turn, previous_seg_id,
  468. selected_op_index);
  469. }
  470. if (result)
  471. {
  472. debug_traverse(turn, turn.operations[selected_op_index], "Accepted");
  473. }
  474. return result;
  475. }
  476. inline int starting_operation_index(turn_type const& turn) const
  477. {
  478. for (int i = 0; i < 2; i++)
  479. {
  480. if (turn.operations[i].visited.started())
  481. {
  482. return i;
  483. }
  484. }
  485. return -1;
  486. }
  487. inline bool both_finished(turn_type const& turn) const
  488. {
  489. for (int i = 0; i < 2; i++)
  490. {
  491. if (! turn.operations[i].visited.finished())
  492. {
  493. return false;
  494. }
  495. }
  496. return true;
  497. }
  498. // Returns a priority, the one with the highst priority will be selected
  499. // 0: not OK
  500. // 1: OK following spike out
  501. // 2: OK but next turn is in same cluster
  502. // 3: OK
  503. // 4: OK and start turn matches
  504. // 5: OK and start turn and start operation both match, this is the best
  505. inline int priority_of_turn_in_cluster_union(sort_by_side::rank_type selected_rank,
  506. typename sbs_type::rp const& ranked_point,
  507. cluster_info const& cinfo,
  508. signed_size_type start_turn_index, int start_op_index) const
  509. {
  510. if (ranked_point.rank != selected_rank
  511. || ranked_point.direction != sort_by_side::dir_to)
  512. {
  513. return 0;
  514. }
  515. auto const& turn = m_turns[ranked_point.turn_index];
  516. auto const& op = turn.operations[ranked_point.operation_index];
  517. // Check finalized: TODO: this should be finetuned, it is not necessary
  518. if (op.visited.finalized())
  519. {
  520. return 0;
  521. }
  522. if BOOST_GEOMETRY_CONSTEXPR (OverlayType != overlay_dissolve)
  523. {
  524. if (op.enriched.count_left != 0 || op.enriched.count_right == 0)
  525. {
  526. // Check counts: in some cases interior rings might be generated with
  527. // polygons on both sides. For dissolve it can be anything.
  528. // If this forms a spike, going to/from the cluster point in the same
  529. // (opposite) direction, it can still be used.
  530. return cinfo.spike_count > 0 ? 1 : 0;
  531. }
  532. }
  533. bool const to_start = ranked_point.turn_index == start_turn_index;
  534. bool const to_start_index = ranked_point.operation_index == start_op_index;
  535. bool const next_in_same_cluster
  536. = cinfo.turn_indices.count(op.enriched.get_next_turn_index()) > 0;
  537. // Return the priority as described above
  538. return to_start && to_start_index ? 5
  539. : to_start ? 4
  540. : next_in_same_cluster ? 2
  541. : 3
  542. ;
  543. }
  544. template <typename RankedPoint>
  545. inline turn_operation_type const& operation_from_rank(RankedPoint const& rp) const
  546. {
  547. return m_turns[rp.turn_index].operations[rp.operation_index];
  548. }
  549. inline sort_by_side::rank_type select_rank(sbs_type const& sbs) const
  550. {
  551. static bool const is_intersection
  552. = target_operation == operation_intersection;
  553. // Take the first outgoing rank corresponding to incoming region,
  554. // or take another region if it is not isolated
  555. auto const& in_op = operation_from_rank(sbs.m_ranked_points.front());
  556. for (std::size_t i = 0; i < sbs.m_ranked_points.size(); i++)
  557. {
  558. auto const& rp = sbs.m_ranked_points[i];
  559. if (rp.rank == 0 || rp.direction == sort_by_side::dir_from)
  560. {
  561. continue;
  562. }
  563. auto const& out_op = operation_from_rank(rp);
  564. if (out_op.operation != target_operation
  565. && out_op.operation != operation_continue)
  566. {
  567. continue;
  568. }
  569. if (in_op.enriched.region_id == out_op.enriched.region_id
  570. || (is_intersection && ! out_op.enriched.isolated))
  571. {
  572. // Region corresponds to incoming region, or (for intersection)
  573. // there is a non-isolated other region which should be taken
  574. return rp.rank;
  575. }
  576. }
  577. return -1;
  578. }
  579. inline bool select_from_cluster_union(signed_size_type& turn_index,
  580. cluster_info const& cinfo,
  581. int& op_index, sbs_type const& sbs,
  582. signed_size_type start_turn_index, int start_op_index) const
  583. {
  584. sort_by_side::rank_type const selected_rank = select_rank(sbs);
  585. int current_priority = 0;
  586. for (std::size_t i = 1; i < sbs.m_ranked_points.size(); i++)
  587. {
  588. auto const& ranked_point = sbs.m_ranked_points[i];
  589. if (ranked_point.rank > selected_rank)
  590. {
  591. break;
  592. }
  593. int const priority = priority_of_turn_in_cluster_union(selected_rank,
  594. ranked_point, cinfo, start_turn_index, start_op_index);
  595. if (priority > current_priority)
  596. {
  597. current_priority = priority;
  598. turn_index = ranked_point.turn_index;
  599. op_index = ranked_point.operation_index;
  600. }
  601. }
  602. return current_priority > 0;
  603. }
  604. inline bool analyze_cluster_intersection(signed_size_type& turn_index,
  605. int& op_index, sbs_type const& sbs) const
  606. {
  607. // Select the rank based on regions and isolation
  608. sort_by_side::rank_type const selected_rank = select_rank(sbs);
  609. if (selected_rank <= 0)
  610. {
  611. return false;
  612. }
  613. // From these ranks, select the index: the first, or the one with
  614. // the smallest remaining distance
  615. typename turn_operation_type::comparable_distance_type
  616. min_remaining_distance = 0;
  617. std::size_t selected_index = sbs.m_ranked_points.size();
  618. for (std::size_t i = 0; i < sbs.m_ranked_points.size(); i++)
  619. {
  620. auto const& ranked_point = sbs.m_ranked_points[i];
  621. if (ranked_point.rank > selected_rank)
  622. {
  623. break;
  624. }
  625. else if (ranked_point.rank == selected_rank)
  626. {
  627. auto const& op = operation_from_rank(ranked_point);
  628. if (op.visited.finalized())
  629. {
  630. // This direction is already traveled,
  631. // it cannot be traveled again
  632. continue;
  633. }
  634. if (selected_index == sbs.m_ranked_points.size()
  635. || op.remaining_distance < min_remaining_distance)
  636. {
  637. // It was unassigned or it is better
  638. selected_index = i;
  639. min_remaining_distance = op.remaining_distance;
  640. }
  641. }
  642. }
  643. if (selected_index == sbs.m_ranked_points.size())
  644. {
  645. // Should not happen, there must be points with the selected rank
  646. return false;
  647. }
  648. auto const& ranked_point = sbs.m_ranked_points[selected_index];
  649. turn_index = ranked_point.turn_index;
  650. op_index = ranked_point.operation_index;
  651. return true;
  652. }
  653. inline bool fill_sbs(sbs_type& sbs,
  654. signed_size_type turn_index,
  655. std::set<signed_size_type> const& cluster_indices,
  656. segment_identifier const& previous_seg_id) const
  657. {
  658. for (auto cluster_turn_index : cluster_indices)
  659. {
  660. turn_type const& cluster_turn = m_turns[cluster_turn_index];
  661. if (cluster_turn.discarded)
  662. {
  663. // Defensive check, discarded turns should not be in cluster
  664. continue;
  665. }
  666. for (int i = 0; i < 2; i++)
  667. {
  668. sbs.add(cluster_turn,
  669. cluster_turn.operations[i],
  670. cluster_turn_index, i, previous_seg_id,
  671. m_geometry1, m_geometry2,
  672. cluster_turn_index == turn_index);
  673. }
  674. }
  675. if (! sbs.has_origin())
  676. {
  677. return false;
  678. }
  679. turn_type const& turn = m_turns[turn_index];
  680. sbs.apply(turn.point);
  681. return true;
  682. }
  683. inline bool select_turn_from_cluster(signed_size_type& turn_index,
  684. int& op_index,
  685. signed_size_type start_turn_index, int start_op_index,
  686. segment_identifier const& previous_seg_id) const
  687. {
  688. bool const is_union = target_operation == operation_union;
  689. turn_type const& turn = m_turns[turn_index];
  690. BOOST_ASSERT(turn.is_clustered());
  691. auto mit = m_clusters.find(turn.cluster_id);
  692. BOOST_ASSERT(mit != m_clusters.end());
  693. cluster_info const& cinfo = mit->second;
  694. sbs_type sbs(m_strategy);
  695. if (! fill_sbs(sbs, turn_index, cinfo.turn_indices, previous_seg_id))
  696. {
  697. return false;
  698. }
  699. if BOOST_GEOMETRY_CONSTEXPR (is_union)
  700. {
  701. if (cinfo.open_count == 0 && cinfo.spike_count > 0)
  702. {
  703. // Leave the cluster from the spike.
  704. for (std::size_t i = 0; i + 1 < sbs.m_ranked_points.size(); i++)
  705. {
  706. auto const& current = sbs.m_ranked_points[i];
  707. auto const& next = sbs.m_ranked_points[i + 1];
  708. if (current.rank == next.rank
  709. && current.direction == detail::overlay::sort_by_side::dir_from
  710. && next.direction == detail::overlay::sort_by_side::dir_to)
  711. {
  712. turn_index = next.turn_index;
  713. op_index = next.operation_index;
  714. return true;
  715. }
  716. }
  717. }
  718. }
  719. cluster_exits<OverlayType, Turns, sbs_type> exits(m_turns, cinfo.turn_indices, sbs);
  720. if (exits.apply(turn_index, op_index))
  721. {
  722. return true;
  723. }
  724. bool result = false;
  725. if BOOST_GEOMETRY_CONSTEXPR (is_union)
  726. {
  727. result = select_from_cluster_union(turn_index, cinfo,
  728. op_index, sbs,
  729. start_turn_index, start_op_index);
  730. if (! result)
  731. {
  732. // There no way out found, try second pass in collected cluster exits
  733. result = exits.apply(turn_index, op_index, false);
  734. }
  735. }
  736. else
  737. {
  738. result = analyze_cluster_intersection(turn_index, op_index, sbs);
  739. }
  740. return result;
  741. }
  742. // Analyzes a non-clustered "ii" intersection, as if it is clustered.
  743. inline bool analyze_ii_intersection(signed_size_type& turn_index, int& op_index,
  744. turn_type const& current_turn,
  745. segment_identifier const& previous_seg_id)
  746. {
  747. sbs_type sbs(m_strategy);
  748. // Add this turn to the sort-by-side sorter
  749. for (int i = 0; i < 2; i++)
  750. {
  751. sbs.add(current_turn,
  752. current_turn.operations[i],
  753. turn_index, i, previous_seg_id,
  754. m_geometry1, m_geometry2,
  755. true);
  756. }
  757. if (! sbs.has_origin())
  758. {
  759. return false;
  760. }
  761. sbs.apply(current_turn.point);
  762. bool result = analyze_cluster_intersection(turn_index, op_index, sbs);
  763. return result;
  764. }
  765. inline void change_index_for_self_turn(signed_size_type& to_vertex_index,
  766. turn_type const& start_turn,
  767. turn_operation_type const& start_op,
  768. int start_op_index) const
  769. {
  770. if BOOST_GEOMETRY_CONSTEXPR (OverlayType != overlay_buffer
  771. && OverlayType != overlay_dissolve)
  772. {
  773. return;
  774. }
  775. else // else prevents unreachable code warning
  776. {
  777. const bool allow_uu = OverlayType != overlay_buffer;
  778. // It travels to itself, can happen. If this is a buffer, it can
  779. // sometimes travel to itself in the following configuration:
  780. //
  781. // +---->--+
  782. // | |
  783. // | +---*----+ *: one turn, with segment index 2/7
  784. // | | | |
  785. // | +---C | C: closing point (start/end)
  786. // | |
  787. // +------------+
  788. //
  789. // If it starts on segment 2 and travels to itself on segment 2, that
  790. // should be corrected to 7 because that is the shortest path
  791. //
  792. // Also a uu turn (touching with another buffered ring) might have this
  793. // apparent configuration, but there it should
  794. // always travel the whole ring
  795. turn_operation_type const& other_op
  796. = start_turn.operations[1 - start_op_index];
  797. bool const correct
  798. = (allow_uu || ! start_turn.both(operation_union))
  799. && start_op.seg_id.source_index == other_op.seg_id.source_index
  800. && start_op.seg_id.multi_index == other_op.seg_id.multi_index
  801. && start_op.seg_id.ring_index == other_op.seg_id.ring_index
  802. && start_op.seg_id.segment_index == to_vertex_index;
  803. #if defined(BOOST_GEOMETRY_DEBUG_TRAVERSE)
  804. std::cout << " WARNING: self-buffer "
  805. << " correct=" << correct
  806. << " turn=" << operation_char(start_turn.operations[0].operation)
  807. << operation_char(start_turn.operations[1].operation)
  808. << " start=" << start_op.seg_id.segment_index
  809. << " from=" << to_vertex_index
  810. << " to=" << other_op.enriched.travels_to_vertex_index
  811. << std::endl;
  812. #endif
  813. if (correct)
  814. {
  815. to_vertex_index = other_op.enriched.travels_to_vertex_index;
  816. }
  817. }
  818. }
  819. bool select_turn_from_enriched(signed_size_type& turn_index,
  820. segment_identifier& previous_seg_id,
  821. signed_size_type& to_vertex_index,
  822. signed_size_type start_turn_index,
  823. int start_op_index,
  824. turn_type const& previous_turn,
  825. turn_operation_type const& previous_op,
  826. bool is_start) const
  827. {
  828. to_vertex_index = -1;
  829. if (previous_op.enriched.next_ip_index < 0)
  830. {
  831. // There is no next IP on this segment
  832. if (previous_op.enriched.travels_to_vertex_index < 0
  833. || previous_op.enriched.travels_to_ip_index < 0)
  834. {
  835. return false;
  836. }
  837. to_vertex_index = previous_op.enriched.travels_to_vertex_index;
  838. if (is_start &&
  839. previous_op.enriched.travels_to_ip_index == start_turn_index)
  840. {
  841. change_index_for_self_turn(to_vertex_index, previous_turn,
  842. previous_op, start_op_index);
  843. }
  844. turn_index = previous_op.enriched.travels_to_ip_index;
  845. previous_seg_id = previous_op.seg_id;
  846. }
  847. else
  848. {
  849. // Take the next IP on this segment
  850. turn_index = previous_op.enriched.next_ip_index;
  851. previous_seg_id = previous_op.seg_id;
  852. }
  853. return true;
  854. }
  855. bool select_turn(signed_size_type start_turn_index, int start_op_index,
  856. signed_size_type& turn_index,
  857. int& op_index,
  858. int previous_op_index,
  859. signed_size_type previous_turn_index,
  860. segment_identifier const& previous_seg_id,
  861. bool is_start, bool has_points)
  862. {
  863. turn_type const& current_turn = m_turns[turn_index];
  864. bool const back_at_start_cluster
  865. = has_points
  866. && current_turn.is_clustered()
  867. && m_turns[start_turn_index].cluster_id == current_turn.cluster_id;
  868. if BOOST_GEOMETRY_CONSTEXPR (target_operation == operation_intersection)
  869. {
  870. // Intersection or difference
  871. if (has_points && (turn_index == start_turn_index || back_at_start_cluster))
  872. {
  873. // Intersection can always be finished if returning
  874. turn_index = start_turn_index;
  875. op_index = start_op_index;
  876. return true;
  877. }
  878. if (! current_turn.is_clustered()
  879. && current_turn.both(operation_intersection)
  880. && analyze_ii_intersection(turn_index, op_index,
  881. current_turn, previous_seg_id))
  882. {
  883. return true;
  884. }
  885. }
  886. else if (turn_index == start_turn_index || back_at_start_cluster)
  887. {
  888. // Union or buffer: cannot return immediately to starting turn, because it then
  889. // might miss a formed multi polygon with a touching point.
  890. auto const& current_op = current_turn.operations[op_index];
  891. signed_size_type const next_turn_index = current_op.enriched.get_next_turn_index();
  892. bool const to_other_turn = next_turn_index >= 0 && m_turns[next_turn_index].cluster_id != current_turn.cluster_id;
  893. if (! to_other_turn)
  894. {
  895. // Return to starting point
  896. turn_index = start_turn_index;
  897. op_index = start_op_index;
  898. return true;
  899. }
  900. }
  901. if (current_turn.is_clustered())
  902. {
  903. if (! select_turn_from_cluster(turn_index, op_index,
  904. start_turn_index, start_op_index, previous_seg_id))
  905. {
  906. return false;
  907. }
  908. if (is_start && turn_index == previous_turn_index)
  909. {
  910. op_index = previous_op_index;
  911. }
  912. }
  913. else
  914. {
  915. op_index = starting_operation_index(current_turn);
  916. if (op_index == -1)
  917. {
  918. if (both_finished(current_turn))
  919. {
  920. return false;
  921. }
  922. if (! select_operation(current_turn, turn_index,
  923. start_turn_index,
  924. previous_seg_id,
  925. op_index))
  926. {
  927. return false;
  928. }
  929. }
  930. }
  931. return true;
  932. }
  933. private :
  934. Geometry1 const& m_geometry1;
  935. Geometry2 const& m_geometry2;
  936. Turns& m_turns;
  937. Clusters const& m_clusters;
  938. RobustPolicy const& m_robust_policy;
  939. Strategy m_strategy;
  940. Visitor& m_visitor;
  941. };
  942. }} // namespace detail::overlay
  943. #endif // DOXYGEN_NO_DETAIL
  944. }} // namespace boost::geometry
  945. #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSAL_HPP