]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/geometry/include/boost/geometry/algorithms/detail/overlay/sort_by_side.hpp
bump version to 12.2.2-pve1
[ceph.git] / ceph / src / boost / libs / geometry / include / 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 // Use, modification and distribution is subject to the Boost Software License,
6 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
7 // http://www.boost.org/LICENSE_1_0.txt)
8
9 #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SORT_BY_SIDE_HPP
10 #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SORT_BY_SIDE_HPP
11
12 #include <algorithm>
13 #include <vector>
14
15 #include <boost/geometry/algorithms/detail/direction_code.hpp>
16 #include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
17 #include <boost/geometry/strategies/side.hpp>
18
19 namespace boost { namespace geometry
20 {
21
22 #ifndef DOXYGEN_NO_DETAIL
23 namespace detail { namespace overlay { namespace sort_by_side
24 {
25
26 enum direction_type { dir_unknown = -1, dir_from = 0, dir_to = 1 };
27
28 // Point-wrapper, adding some properties
29 template <typename Point>
30 struct ranked_point
31 {
32 ranked_point()
33 : rank(0)
34 , turn_index(-1)
35 , operation_index(-1)
36 , direction(dir_unknown)
37 , count_left(0)
38 , count_right(0)
39 , operation(operation_none)
40 {}
41
42 ranked_point(const Point& p, signed_size_type ti, signed_size_type oi,
43 direction_type d, operation_type op, segment_identifier sid)
44 : point(p)
45 , rank(0)
46 , zone(-1)
47 , turn_index(ti)
48 , operation_index(oi)
49 , direction(d)
50 , count_left(0)
51 , count_right(0)
52 , operation(op)
53 , seg_id(sid)
54 {}
55
56 Point point;
57 std::size_t rank;
58 signed_size_type zone; // index of closed zone, in uu turn there would be 2 zones
59 signed_size_type turn_index;
60 signed_size_type operation_index;
61 direction_type direction;
62 std::size_t count_left;
63 std::size_t count_right;
64 operation_type operation;
65 segment_identifier seg_id;
66 };
67
68 struct less_by_turn_index
69 {
70 template <typename T>
71 inline bool operator()(const T& first, const T& second) const
72 {
73 return first.turn_index == second.turn_index
74 ? first.index < second.index
75 : first.turn_index < second.turn_index
76 ;
77 }
78 };
79
80 struct less_by_index
81 {
82 template <typename T>
83 inline bool operator()(const T& first, const T& second) const
84 {
85 // First order by from/to
86 if (first.direction != second.direction)
87 {
88 return first.direction < second.direction;
89 }
90 // All the same, order by turn index (we might consider length too)
91 return first.turn_index < second.turn_index;
92 }
93 };
94
95 struct less_false
96 {
97 template <typename T>
98 inline bool operator()(const T&, const T& ) const
99 {
100 return false;
101 }
102 };
103
104 template <typename Point, typename LessOnSame, typename Compare>
105 struct less_by_side
106 {
107 typedef typename strategy::side::services::default_strategy
108 <
109 typename cs_tag<Point>::type
110 >::type side;
111
112 less_by_side(const Point& p1, const Point& p2)
113 : m_p1(p1)
114 , m_p2(p2)
115 {}
116
117 template <typename T>
118 inline bool operator()(const T& first, const T& second) const
119 {
120 LessOnSame on_same;
121 Compare compare;
122
123 int const side_first = side::apply(m_p1, m_p2, first.point);
124 int const side_second = side::apply(m_p1, m_p2, second.point);
125
126 if (side_first == 0 && side_second == 0)
127 {
128 // Both collinear. They might point into different directions: <------*------>
129 // If so, order the one going backwards as the very first.
130
131 int const first_code = direction_code(m_p1, m_p2, first.point);
132 int const second_code = direction_code(m_p1, m_p2, second.point);
133
134 // Order by code, backwards first, then forward.
135 return first_code != second_code
136 ? first_code < second_code
137 : on_same(first, second)
138 ;
139 }
140 else if (side_first == 0
141 && direction_code(m_p1, m_p2, first.point) == -1)
142 {
143 // First collinear and going backwards.
144 // Order as the very first, so return always true
145 return true;
146 }
147 else if (side_second == 0
148 && direction_code(m_p1, m_p2, second.point) == -1)
149 {
150 // Second is collinear and going backwards
151 // Order as very last, so return always false
152 return false;
153 }
154
155 // They are not both collinear
156
157 if (side_first != side_second)
158 {
159 return compare(side_first, side_second);
160 }
161
162 // They are both left, both right, and/or both collinear (with each other and/or with p1,p2)
163 // Check mutual side
164 int const side_second_wrt_first = side::apply(m_p2, first.point, second.point);
165
166 if (side_second_wrt_first == 0)
167 {
168 return on_same(first, second);
169 }
170
171 int const side_first_wrt_second = -side_second_wrt_first;
172
173 // Both are on same side, and not collinear
174 // Union: return true if second is right w.r.t. first, so -1,
175 // so other is 1. union has greater as compare functor
176 // Intersection: v.v.
177 return compare(side_first_wrt_second, side_second_wrt_first);
178 }
179
180 private :
181 Point m_p1, m_p2;
182 };
183
184 template <bool Reverse1, bool Reverse2, typename Point, typename Compare>
185 struct side_sorter
186 {
187 typedef ranked_point<Point> rp;
188
189 inline void set_origin(Point const& origin)
190 {
191 m_origin = origin;
192 }
193
194 template <typename Operation, typename Geometry1, typename Geometry2>
195 void add(Operation const& op, signed_size_type turn_index, signed_size_type op_index,
196 Geometry1 const& geometry1,
197 Geometry2 const& geometry2,
198 bool is_origin)
199 {
200 Point point1, point2, point3;
201 geometry::copy_segment_points<Reverse1, Reverse2>(geometry1, geometry2,
202 op.seg_id, point1, point2, point3);
203 Point const& point_to = op.fraction.is_one() ? point3 : point2;
204
205 m_ranked_points.push_back(rp(point1, turn_index, op_index, dir_from, op.operation, op.seg_id));
206 m_ranked_points.push_back(rp(point_to, turn_index, op_index, dir_to, op.operation, op.seg_id));
207
208 if (is_origin)
209 {
210 m_origin = point1;
211 }
212 }
213
214 void apply(Point const& turn_point)
215 {
216 // We need three compare functors:
217 // 1) to order clockwise (union) or counter clockwise (intersection)
218 // 2) to order by side, resulting in unique ranks for all points
219 // 3) to order by side, resulting in non-unique ranks
220 // to give colinear points
221
222 // Sort by side and assign rank
223 less_by_side<Point, less_by_index, Compare> less_unique(m_origin, turn_point);
224 less_by_side<Point, less_false, Compare> less_non_unique(m_origin, turn_point);
225
226 std::sort(m_ranked_points.begin(), m_ranked_points.end(), less_unique);
227
228 std::size_t colinear_rank = 0;
229 for (std::size_t i = 0; i < m_ranked_points.size(); i++)
230 {
231 if (i > 0
232 && less_non_unique(m_ranked_points[i - 1], m_ranked_points[i]))
233 {
234 // It is not collinear
235 colinear_rank++;
236 }
237
238 m_ranked_points[i].rank = colinear_rank;
239 }
240 }
241
242 template <signed_size_type segment_identifier::*Member, typename Map>
243 void find_open_generic(Map& handled)
244 {
245 for (std::size_t i = 0; i < m_ranked_points.size(); i++)
246 {
247 const rp& ranked = m_ranked_points[i];
248 if (ranked.direction != dir_from)
249 {
250 continue;
251 }
252
253 signed_size_type const& index = ranked.seg_id.*Member;
254 if (! handled[index])
255 {
256 find_polygons_for_source<Member>(index, i);
257 handled[index] = true;
258 }
259 }
260 }
261
262 void find_open()
263 {
264 // TODO: we might pass Buffer as overlay_type, instead on the fly below
265 bool one_source = true;
266 for (std::size_t i = 0; i < m_ranked_points.size(); i++)
267 {
268 const rp& ranked = m_ranked_points[i];
269 signed_size_type const& src = ranked.seg_id.source_index;
270 if (src != 0)
271 {
272 one_source = false;
273 break;
274 }
275 }
276
277 if (one_source)
278 {
279 // by multi index
280 std::map<signed_size_type, bool> handled;
281 find_open_generic
282 <
283 &segment_identifier::piece_index
284 >(handled);
285 }
286 else
287 {
288 // by source (there should only source 0,1) TODO assert this
289 bool handled[2] = {false, false};
290 find_open_generic
291 <
292 &segment_identifier::source_index
293 >(handled);
294 }
295
296 assign_zones();
297 }
298
299 void reverse()
300 {
301 if (m_ranked_points.empty())
302 {
303 return;
304 }
305
306 int const last = 1 + m_ranked_points.back().rank;
307
308 // Move iterator after rank==0
309 bool has_first = false;
310 typename container_type::iterator it = m_ranked_points.begin() + 1;
311 for (; it != m_ranked_points.end() && it->rank == 0; ++it)
312 {
313 has_first = true;
314 }
315
316 if (has_first)
317 {
318 // Reverse first part (having rank == 0), if any,
319 // but skip the very first row
320 std::reverse(m_ranked_points.begin() + 1, it);
321 for (typename container_type::iterator fit = m_ranked_points.begin();
322 fit != it; ++fit)
323 {
324 BOOST_ASSERT(fit->rank == 0);
325 }
326 }
327
328 // Reverse the rest (main rank > 0)
329 std::reverse(it, m_ranked_points.end());
330 for (; it != m_ranked_points.end(); ++it)
331 {
332 BOOST_ASSERT(it->rank > 0);
333 it->rank = last - it->rank;
334 }
335 }
336
337 //! Check how many open spaces there are
338 template <typename Turns>
339 std::size_t open_count(Turns const& turns) const
340 {
341 typedef typename boost::range_value<Turns>::type turn_type;
342 typedef typename turn_type::turn_operation_type turn_operation_type;
343
344 std::size_t result = 0;
345 std::size_t last_rank = 0;
346 for (std::size_t i = 0; i < m_ranked_points.size(); i++)
347 {
348 rp const& ranked_point = m_ranked_points[i];
349
350 if (ranked_point.rank > last_rank
351 && ranked_point.direction == sort_by_side::dir_to)
352 {
353 // TODO: take count-left / count_right from rank itself
354 turn_type const& ranked_turn = turns[ranked_point.turn_index];
355 turn_operation_type const& ranked_op = ranked_turn.operations[ranked_point.operation_index];
356 if (ranked_op.enriched.count_left == 0
357 && ranked_op.enriched.count_right > 0)
358 {
359 result++;
360 last_rank = ranked_point.rank;
361 }
362 }
363 }
364 return result;
365 }
366
367 //protected :
368
369 typedef std::vector<rp> container_type;
370 container_type m_ranked_points;
371 Point m_origin;
372
373 private :
374
375
376 std::size_t move(std::size_t index) const
377 {
378 std::size_t const result = index + 1;
379 return result >= m_ranked_points.size() ? 0 : result;
380 }
381
382 //! member is pointer to member (source_index or multi_index)
383 template <signed_size_type segment_identifier::*Member>
384 std::size_t move(signed_size_type member_index, std::size_t index) const
385 {
386 std::size_t result = move(index);
387 while (m_ranked_points[result].seg_id.*Member != member_index)
388 {
389 result = move(result);
390 }
391 return result;
392 }
393
394 void assign_ranks(std::size_t min_rank, std::size_t max_rank, int side_index)
395 {
396 for (std::size_t i = 0; i < m_ranked_points.size(); i++)
397 {
398 rp& ranked = m_ranked_points[i];
399 // Suppose there are 8 ranks, if min=4,max=6: assign 4,5,6
400 // if min=5,max=2: assign from 5,6,7,1,2
401 bool const in_range
402 = max_rank >= min_rank
403 ? ranked.rank >= min_rank && ranked.rank <= max_rank
404 : ranked.rank >= min_rank || ranked.rank <= max_rank
405 ;
406
407 if (in_range)
408 {
409 if (side_index == 1)
410 {
411 ranked.count_left++;
412 }
413 else if (side_index == 2)
414 {
415 ranked.count_right++;
416 }
417 }
418 }
419 }
420
421 template <signed_size_type segment_identifier::*Member>
422 void find_polygons_for_source(signed_size_type the_index,
423 std::size_t start_index)
424 {
425 int state = 1; // 'closed', because start_index is "from", arrives at the turn
426 std::size_t last_from_rank = m_ranked_points[start_index].rank;
427 std::size_t previous_rank = m_ranked_points[start_index].rank;
428
429 for (std::size_t index = move<Member>(the_index, start_index);
430 ;
431 index = move<Member>(the_index, index))
432 {
433 rp& ranked = m_ranked_points[index];
434
435 if (ranked.rank != previous_rank && state == 0)
436 {
437 assign_ranks(last_from_rank, previous_rank - 1, 1);
438 assign_ranks(last_from_rank + 1, previous_rank, 2);
439 }
440
441 if (index == start_index)
442 {
443 return;
444 }
445
446 if (ranked.direction == dir_from)
447 {
448 last_from_rank = ranked.rank;
449 state++;
450 }
451 else if (ranked.direction == dir_to)
452 {
453 state--;
454 }
455
456 previous_rank = ranked.rank;
457 }
458 }
459
460 //! Find closed zones and assign it
461 void assign_zones()
462 {
463 // Find a starting point (the first rank after an outgoing rank
464 // with no polygons on the left side)
465 std::size_t start_rank = m_ranked_points.size() + 1;
466 std::size_t start_index = 0;
467 std::size_t max_rank = 0;
468 for (std::size_t i = 0; i < m_ranked_points.size(); i++)
469 {
470 rp const& ranked_point = m_ranked_points[i];
471 if (ranked_point.rank > max_rank)
472 {
473 max_rank = ranked_point.rank;
474 }
475 if (ranked_point.direction == sort_by_side::dir_to
476 && ranked_point.count_left == 0
477 && ranked_point.count_right > 0)
478 {
479 start_rank = ranked_point.rank + 1;
480 }
481 if (ranked_point.rank == start_rank && start_index == 0)
482 {
483 start_index = i;
484 }
485 }
486
487 // Assign the zones
488 std::size_t const undefined_rank = max_rank + 1;
489 std::size_t zone_id = 0;
490 std::size_t last_rank = 0;
491 std::size_t rank_at_next_zone = undefined_rank;
492 std::size_t index = start_index;
493 for (std::size_t i = 0; i < m_ranked_points.size(); i++)
494 {
495 rp& ranked_point = m_ranked_points[index];
496
497 // Implement cyclic behavior
498 index++;
499 if (index == m_ranked_points.size())
500 {
501 index = 0;
502 }
503
504 if (ranked_point.rank != last_rank)
505 {
506 if (ranked_point.rank == rank_at_next_zone)
507 {
508 zone_id++;
509 rank_at_next_zone = undefined_rank;
510 }
511
512 if (ranked_point.direction == sort_by_side::dir_to
513 && ranked_point.count_left == 0
514 && ranked_point.count_right > 0)
515 {
516 rank_at_next_zone = ranked_point.rank + 1;
517 if (rank_at_next_zone > max_rank)
518 {
519 rank_at_next_zone = 0;
520 }
521 }
522
523 last_rank = ranked_point.rank;
524 }
525
526 ranked_point.zone = zone_id;
527 }
528 }
529
530
531 };
532
533
534 }}} // namespace detail::overlay::sort_by_side
535 #endif //DOXYGEN_NO_DETAIL
536
537
538 }} // namespace boost::geometry
539
540 #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SORT_BY_SIDE_HPP