]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/boost/geometry/algorithms/detail/overlay/sort_by_side.hpp
update sources to v12.2.3
[ceph.git] / ceph / src / boost / boost / geometry / algorithms / detail / overlay / sort_by_side.hpp
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 {
40 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>
51 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>
80 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>
92 inline bool operator()(const T& first, const T& second) const
93 {
94 // Length might be considered too
95 // First order by from/to
96 if (first.direction != second.direction)
97 {
98 return first.direction < second.direction;
99 }
100 // Then by turn index
101 if (first.turn_index != second.turn_index)
102 {
103 return first.turn_index < second.turn_index;
104 }
105 // This can also be the same (for example in buffer), but seg_id is
106 // never the same
107 return first.seg_id < second.seg_id;
108 }
109 };
110
111 struct less_false
112 {
113 template <typename T>
114 inline bool operator()(const T&, const T& ) const
115 {
116 return false;
117 }
118 };
119
120 template <typename Point, typename SideStrategy, typename LessOnSame, typename Compare>
121 struct less_by_side
122 {
123 less_by_side(const Point& p1, const Point& p2, SideStrategy const& strategy)
124 : m_p1(p1)
125 , m_p2(p2)
126 , m_strategy(strategy)
127 {}
128
129 template <typename T>
130 inline bool operator()(const T& first, const T& second) const
131 {
132 LessOnSame on_same;
133 Compare compare;
134
135 int const side_first = m_strategy.apply(m_p1, m_p2, first.point);
136 int const side_second = m_strategy.apply(m_p1, m_p2, second.point);
137
138 if (side_first == 0 && side_second == 0)
139 {
140 // Both collinear. They might point into different directions: <------*------>
141 // If so, order the one going backwards as the very first.
142
143 int const first_code = direction_code(m_p1, m_p2, first.point);
144 int const second_code = direction_code(m_p1, m_p2, second.point);
145
146 // Order by code, backwards first, then forward.
147 return first_code != second_code
148 ? first_code < second_code
149 : on_same(first, second)
150 ;
151 }
152 else if (side_first == 0
153 && direction_code(m_p1, m_p2, first.point) == -1)
154 {
155 // First collinear and going backwards.
156 // Order as the very first, so return always true
157 return true;
158 }
159 else if (side_second == 0
160 && direction_code(m_p1, m_p2, second.point) == -1)
161 {
162 // Second is collinear and going backwards
163 // Order as very last, so return always false
164 return false;
165 }
166
167 // They are not both collinear
168
169 if (side_first != side_second)
170 {
171 return compare(side_first, side_second);
172 }
173
174 // They are both left, both right, and/or both collinear (with each other and/or with p1,p2)
175 // Check mutual side
176 int const side_second_wrt_first = m_strategy.apply(m_p2, first.point, second.point);
177
178 if (side_second_wrt_first == 0)
179 {
180 return on_same(first, second);
181 }
182
183 int const side_first_wrt_second = -side_second_wrt_first;
184
185 // Both are on same side, and not collinear
186 // Union: return true if second is right w.r.t. first, so -1,
187 // so other is 1. union has greater as compare functor
188 // Intersection: v.v.
189 return compare(side_first_wrt_second, side_second_wrt_first);
190 }
191
192 private :
193 Point m_p1, m_p2;
194 SideStrategy const& m_strategy;
195 };
196
197 // Sorts vectors in counter clockwise order (by default)
198 template
199 <
200 bool Reverse1,
201 bool Reverse2,
202 overlay_type OverlayType,
203 typename Point,
204 typename SideStrategy,
205 typename Compare
206 >
207 struct side_sorter
208 {
209 typedef ranked_point<Point> rp;
210
211 private :
212 struct include_union
213 {
214 inline bool operator()(rp const& ranked_point) const
215 {
216 // New candidate if there are no polygons on left side,
217 // but there are on right side
218 return ranked_point.count_left == 0
219 && ranked_point.count_right > 0;
220 }
221 };
222
223 struct include_intersection
224 {
225 inline bool operator()(rp const& ranked_point) const
226 {
227 // New candidate if there are two polygons on right side,
228 // and less on the left side
229 return ranked_point.count_left < 2
230 && ranked_point.count_right >= 2;
231 }
232 };
233
234 public :
235 side_sorter(SideStrategy const& strategy)
236 : m_origin_count(0)
237 , m_origin_segment_distance(0)
238 , m_strategy(strategy)
239 {}
240
241 template <typename Operation, typename Geometry1, typename Geometry2>
242 Point add(Operation const& op, signed_size_type turn_index, signed_size_type op_index,
243 Geometry1 const& geometry1,
244 Geometry2 const& geometry2,
245 bool is_origin)
246 {
247 Point point1, point2, point3;
248 geometry::copy_segment_points<Reverse1, Reverse2>(geometry1, geometry2,
249 op.seg_id, point1, point2, point3);
250 Point const& point_to = op.fraction.is_one() ? point3 : point2;
251
252 m_ranked_points.push_back(rp(point1, turn_index, op_index, dir_from, op));
253 m_ranked_points.push_back(rp(point_to, turn_index, op_index, dir_to, op));
254 if (is_origin)
255 {
256 m_origin = point1;
257 m_origin_count++;
258 }
259 return point1;
260 }
261
262 template <typename Operation, typename Geometry1, typename Geometry2>
263 void add(Operation const& op, signed_size_type turn_index, signed_size_type op_index,
264 segment_identifier const& departure_seg_id,
265 Geometry1 const& geometry1,
266 Geometry2 const& geometry2,
267 bool check_origin)
268 {
269 Point const point1 = add(op, turn_index, op_index, geometry1, geometry2, false);
270
271 if (check_origin)
272 {
273 bool const is_origin
274 = op.seg_id.source_index == departure_seg_id.source_index
275 && op.seg_id.ring_index == departure_seg_id.ring_index
276 && op.seg_id.multi_index == departure_seg_id.multi_index;
277
278 if (is_origin)
279 {
280 int const segment_distance = calculate_segment_distance(op, departure_seg_id, geometry1, geometry2);
281 if (m_origin_count == 0 ||
282 segment_distance < m_origin_segment_distance)
283 {
284 m_origin = point1;
285 m_origin_segment_distance = segment_distance;
286 }
287 m_origin_count++;
288 }
289 }
290 }
291
292 template <typename Operation, typename Geometry1, typename Geometry2>
293 static int calculate_segment_distance(Operation const& op,
294 segment_identifier const& departure_seg_id,
295 Geometry1 const& geometry1,
296 Geometry2 const& geometry2)
297 {
298 if (op.seg_id.segment_index >= departure_seg_id.segment_index)
299 {
300 return op.seg_id.segment_index - departure_seg_id.segment_index;
301 }
302 // Take wrap into account
303 // Suppose ring_count=10 (10 points, 9 segments), dep.seg_id=7, op.seg_id=2, then distance=10-9+2
304 // Generic function (is this used somewhere else too?)
305 ring_identifier const rid(op.seg_id.source_index, op.seg_id.multi_index, op.seg_id.ring_index);
306 int const segment_count
307 (op.seg_id.source_index == 0
308 ? geometry::num_points(detail::overlay::get_ring<typename geometry::tag<Geometry1>::type>::apply(rid, geometry1))
309 : geometry::num_points(detail::overlay::get_ring<typename geometry::tag<Geometry2>::type>::apply(rid, geometry2)));
310 return ((segment_count - 1) - departure_seg_id.segment_index) + op.seg_id.segment_index;
311 }
312
313 void apply(Point const& turn_point)
314 {
315 // We need three compare functors:
316 // 1) to order clockwise (union) or counter clockwise (intersection)
317 // 2) to order by side, resulting in unique ranks for all points
318 // 3) to order by side, resulting in non-unique ranks
319 // to give colinear points
320
321 // Sort by side and assign rank
322 less_by_side<Point, SideStrategy, less_by_index, Compare> less_unique(m_origin, turn_point, m_strategy);
323 less_by_side<Point, SideStrategy, less_false, Compare> less_non_unique(m_origin, turn_point, m_strategy);
324
325 std::sort(m_ranked_points.begin(), m_ranked_points.end(), less_unique);
326
327 std::size_t colinear_rank = 0;
328 for (std::size_t i = 0; i < m_ranked_points.size(); i++)
329 {
330 if (i > 0
331 && less_non_unique(m_ranked_points[i - 1], m_ranked_points[i]))
332 {
333 // It is not collinear
334 colinear_rank++;
335 }
336
337 m_ranked_points[i].rank = colinear_rank;
338 }
339 }
340
341 template <signed_size_type segment_identifier::*Member, typename Map>
342 void find_open_generic(Map& handled, bool check)
343 {
344 for (std::size_t i = 0; i < m_ranked_points.size(); i++)
345 {
346 const rp& ranked = m_ranked_points[i];
347 if (ranked.direction != dir_from)
348 {
349 continue;
350 }
351
352 signed_size_type const& index = ranked.seg_id.*Member;
353 if (check && (index < 0 || index > 1))
354 {
355 // Should not occur
356 continue;
357 }
358 if (! handled[index])
359 {
360 find_polygons_for_source<Member>(index, i);
361 handled[index] = true;
362 }
363 }
364 }
365
366 void find_open()
367 {
368 if (OverlayType == overlay_buffer)
369 {
370 // For buffers, use piece index
371 std::map<signed_size_type, bool> handled;
372 find_open_generic
373 <
374 &segment_identifier::piece_index
375 >(handled, false);
376 }
377 else
378 {
379 // For other operations, by source (there should only source 0,1)
380 bool handled[2] = {false, false};
381 find_open_generic
382 <
383 &segment_identifier::source_index
384 >(handled, true);
385 }
386 }
387
388 void reverse()
389 {
390 if (m_ranked_points.empty())
391 {
392 return;
393 }
394
395 std::size_t const last = 1 + m_ranked_points.back().rank;
396
397 // Move iterator after rank==0
398 bool has_first = false;
399 typename container_type::iterator it = m_ranked_points.begin() + 1;
400 for (; it != m_ranked_points.end() && it->rank == 0; ++it)
401 {
402 has_first = true;
403 }
404
405 if (has_first)
406 {
407 // Reverse first part (having rank == 0), if any,
408 // but skip the very first row
409 std::reverse(m_ranked_points.begin() + 1, it);
410 for (typename container_type::iterator fit = m_ranked_points.begin();
411 fit != it; ++fit)
412 {
413 BOOST_ASSERT(fit->rank == 0);
414 }
415 }
416
417 // Reverse the rest (main rank > 0)
418 std::reverse(it, m_ranked_points.end());
419 for (; it != m_ranked_points.end(); ++it)
420 {
421 BOOST_ASSERT(it->rank > 0);
422 it->rank = last - it->rank;
423 }
424 }
425
426 bool has_origin() const
427 {
428 return m_origin_count > 0;
429 }
430
431 //private :
432
433 typedef std::vector<rp> container_type;
434 container_type m_ranked_points;
435 Point m_origin;
436 std::size_t m_origin_count;
437 int m_origin_segment_distance;
438 SideStrategy m_strategy;
439
440 private :
441
442 //! Check how many open spaces there are
443 template <typename Include>
444 inline std::size_t open_count(Include const& include_functor) const
445 {
446 std::size_t result = 0;
447 std::size_t last_rank = 0;
448 for (std::size_t i = 0; i < m_ranked_points.size(); i++)
449 {
450 rp const& ranked_point = m_ranked_points[i];
451
452 if (ranked_point.rank > last_rank
453 && ranked_point.direction == sort_by_side::dir_to
454 && include_functor(ranked_point))
455 {
456 result++;
457 last_rank = ranked_point.rank;
458 }
459 }
460 return result;
461 }
462
463 std::size_t move(std::size_t index) const
464 {
465 std::size_t const result = index + 1;
466 return result >= m_ranked_points.size() ? 0 : result;
467 }
468
469 //! member is pointer to member (source_index or multi_index)
470 template <signed_size_type segment_identifier::*Member>
471 std::size_t move(signed_size_type member_index, std::size_t index) const
472 {
473 std::size_t result = move(index);
474 while (m_ranked_points[result].seg_id.*Member != member_index)
475 {
476 result = move(result);
477 }
478 return result;
479 }
480
481 void assign_ranks(std::size_t min_rank, std::size_t max_rank, int side_index)
482 {
483 for (std::size_t i = 0; i < m_ranked_points.size(); i++)
484 {
485 rp& ranked = m_ranked_points[i];
486 // Suppose there are 8 ranks, if min=4,max=6: assign 4,5,6
487 // if min=5,max=2: assign from 5,6,7,1,2
488 bool const in_range
489 = max_rank >= min_rank
490 ? ranked.rank >= min_rank && ranked.rank <= max_rank
491 : ranked.rank >= min_rank || ranked.rank <= max_rank
492 ;
493
494 if (in_range)
495 {
496 if (side_index == 1)
497 {
498 ranked.count_left++;
499 }
500 else if (side_index == 2)
501 {
502 ranked.count_right++;
503 }
504 }
505 }
506 }
507
508 template <signed_size_type segment_identifier::*Member>
509 void find_polygons_for_source(signed_size_type the_index,
510 std::size_t start_index)
511 {
512 bool in_polygon = true; // Because start_index is "from", arrives at the turn
513 rp const& start_rp = m_ranked_points[start_index];
514 std::size_t last_from_rank = start_rp.rank;
515 std::size_t previous_rank = start_rp.rank;
516
517 for (std::size_t index = move<Member>(the_index, start_index);
518 ;
519 index = move<Member>(the_index, index))
520 {
521 rp& ranked = m_ranked_points[index];
522
523 if (ranked.rank != previous_rank && ! in_polygon)
524 {
525 assign_ranks(last_from_rank, previous_rank - 1, 1);
526 assign_ranks(last_from_rank + 1, previous_rank, 2);
527 }
528
529 if (index == start_index)
530 {
531 return;
532 }
533
534 if (ranked.direction == dir_from)
535 {
536 last_from_rank = ranked.rank;
537 in_polygon = true;
538 }
539 else if (ranked.direction == dir_to)
540 {
541 in_polygon = false;
542 }
543
544 previous_rank = ranked.rank;
545 }
546 }
547
548 //! Find closed zones and assign it
549 template <typename Include>
550 std::size_t assign_zones(Include const& include_functor)
551 {
552 // Find a starting point (the first rank after an outgoing rank
553 // with no polygons on the left side)
554 std::size_t start_rank = m_ranked_points.size() + 1;
555 std::size_t start_index = 0;
556 std::size_t max_rank = 0;
557 for (std::size_t i = 0; i < m_ranked_points.size(); i++)
558 {
559 rp const& ranked_point = m_ranked_points[i];
560 if (ranked_point.rank > max_rank)
561 {
562 max_rank = ranked_point.rank;
563 }
564 if (ranked_point.direction == sort_by_side::dir_to
565 && include_functor(ranked_point))
566 {
567 start_rank = ranked_point.rank + 1;
568 }
569 if (ranked_point.rank == start_rank && start_index == 0)
570 {
571 start_index = i;
572 }
573 }
574
575 // Assign the zones
576 std::size_t const undefined_rank = max_rank + 1;
577 std::size_t zone_id = 0;
578 std::size_t last_rank = 0;
579 std::size_t rank_at_next_zone = undefined_rank;
580 std::size_t index = start_index;
581 for (std::size_t i = 0; i < m_ranked_points.size(); i++)
582 {
583 rp& ranked_point = m_ranked_points[index];
584
585 // Implement cyclic behavior
586 index++;
587 if (index == m_ranked_points.size())
588 {
589 index = 0;
590 }
591
592 if (ranked_point.rank != last_rank)
593 {
594 if (ranked_point.rank == rank_at_next_zone)
595 {
596 zone_id++;
597 rank_at_next_zone = undefined_rank;
598 }
599
600 if (ranked_point.direction == sort_by_side::dir_to
601 && include_functor(ranked_point))
602 {
603 rank_at_next_zone = ranked_point.rank + 1;
604 if (rank_at_next_zone > max_rank)
605 {
606 rank_at_next_zone = 0;
607 }
608 }
609
610 last_rank = ranked_point.rank;
611 }
612
613 ranked_point.zone = zone_id;
614 }
615 return zone_id;
616 }
617
618 public :
619 inline std::size_t open_count(operation_type for_operation) const
620 {
621 return for_operation == operation_union
622 ? open_count(include_union())
623 : open_count(include_intersection())
624 ;
625 }
626
627 inline std::size_t assign_zones(operation_type for_operation)
628 {
629 return for_operation == operation_union
630 ? assign_zones(include_union())
631 : assign_zones(include_intersection())
632 ;
633 }
634
635 };
636
637
638 }}} // namespace detail::overlay::sort_by_side
639 #endif //DOXYGEN_NO_DETAIL
640
641
642 }} // namespace boost::geometry
643
644 #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SORT_BY_SIDE_HPP