sort_by_side.hpp 21 KB

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