• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Boost.Geometry (aka GGL, Generic Geometry Library)
2 
3 // Copyright (c) 2015 Barend Gehrels, Amsterdam, the Netherlands.
4 // Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland.
5 
6 // This file was modified by Oracle on 2017, 2019.
7 // Modifications copyright (c) 2017, 2019 Oracle and/or its affiliates.
8 
9 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
10 
11 // Use, modification and distribution is subject to the Boost Software License,
12 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
13 // http://www.boost.org/LICENSE_1_0.txt)
14 
15 #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SORT_BY_SIDE_HPP
16 #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SORT_BY_SIDE_HPP
17 
18 #include <algorithm>
19 #include <map>
20 #include <vector>
21 
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 #include <boost/geometry/util/condition.hpp>
28 
29 namespace boost { namespace geometry
30 {
31 
32 #ifndef DOXYGEN_NO_DETAIL
33 namespace detail { namespace overlay { namespace sort_by_side
34 {
35 
36 enum direction_type { dir_unknown = -1, dir_from = 0, dir_to = 1 };
37 
38 typedef signed_size_type rank_type;
39 
40 
41 // Point-wrapper, adding some properties
42 template <typename Point>
43 struct ranked_point
44 {
ranked_pointboost::geometry::detail::overlay::sort_by_side::ranked_point45     ranked_point()
46         : rank(0)
47         , turn_index(-1)
48         , operation_index(-1)
49         , direction(dir_unknown)
50         , count_left(0)
51         , count_right(0)
52         , operation(operation_none)
53     {}
54 
ranked_pointboost::geometry::detail::overlay::sort_by_side::ranked_point55     ranked_point(Point const& p, signed_size_type ti, int oi,
56                  direction_type d, operation_type op, segment_identifier const& si)
57         : point(p)
58         , rank(0)
59         , zone(-1)
60         , turn_index(ti)
61         , operation_index(oi)
62         , direction(d)
63         , count_left(0)
64         , count_right(0)
65         , operation(op)
66         , seg_id(si)
67     {}
68 
69     Point point;
70     rank_type rank;
71     signed_size_type zone; // index of closed zone, in uu turn there would be 2 zones
72     signed_size_type turn_index;
73     int operation_index; // 0,1
74     direction_type direction;
75     std::size_t count_left;
76     std::size_t count_right;
77     operation_type operation;
78     segment_identifier seg_id;
79 };
80 
81 struct less_by_turn_index
82 {
83     template <typename T>
operator ()boost::geometry::detail::overlay::sort_by_side::less_by_turn_index84     inline bool operator()(const T& first, const T& second) const
85     {
86         return first.turn_index == second.turn_index
87             ? first.index < second.index
88             : first.turn_index < second.turn_index
89             ;
90     }
91 };
92 
93 struct less_by_index
94 {
95     template <typename T>
operator ()boost::geometry::detail::overlay::sort_by_side::less_by_index96     inline bool operator()(const T& first, const T& second) const
97     {
98         // Length might be considered too
99         // First order by from/to
100         if (first.direction != second.direction)
101         {
102             return first.direction < second.direction;
103         }
104         // Then by turn index
105         if (first.turn_index != second.turn_index)
106         {
107             return first.turn_index < second.turn_index;
108         }
109         // This can also be the same (for example in buffer), but seg_id is
110         // never the same
111         return first.seg_id < second.seg_id;
112     }
113 };
114 
115 struct less_false
116 {
117     template <typename T>
operator ()boost::geometry::detail::overlay::sort_by_side::less_false118     inline bool operator()(const T&, const T& ) const
119     {
120         return false;
121     }
122 };
123 
124 template <typename Point, typename SideStrategy, typename LessOnSame, typename Compare>
125 struct less_by_side
126 {
less_by_sideboost::geometry::detail::overlay::sort_by_side::less_by_side127     less_by_side(const Point& p1, const Point& p2, SideStrategy const& strategy)
128         : m_origin(p1)
129         , m_turn_point(p2)
130         , m_strategy(strategy)
131     {}
132 
133     template <typename T>
operator ()boost::geometry::detail::overlay::sort_by_side::less_by_side134     inline bool operator()(const T& first, const T& second) const
135     {
136         typedef typename SideStrategy::cs_tag cs_tag;
137 
138         LessOnSame on_same;
139         Compare compare;
140 
141         int const side_first = m_strategy.apply(m_origin, m_turn_point, first.point);
142         int const side_second = m_strategy.apply(m_origin, m_turn_point, second.point);
143 
144         if (side_first == 0 && side_second == 0)
145         {
146             // Both collinear. They might point into different directions: <------*------>
147             // If so, order the one going backwards as the very first.
148 
149             int const first_code = direction_code<cs_tag>(m_origin, m_turn_point, first.point);
150             int const second_code = direction_code<cs_tag>(m_origin, m_turn_point, second.point);
151 
152             // Order by code, backwards first, then forward.
153             return first_code != second_code
154                 ? first_code < second_code
155                 : on_same(first, second)
156                 ;
157         }
158         else if (side_first == 0
159                 && direction_code<cs_tag>(m_origin, m_turn_point, first.point) == -1)
160         {
161             // First collinear and going backwards.
162             // Order as the very first, so return always true
163             return true;
164         }
165         else if (side_second == 0
166             && direction_code<cs_tag>(m_origin, m_turn_point, second.point) == -1)
167         {
168             // Second is collinear and going backwards
169             // Order as very last, so return always false
170             return false;
171         }
172 
173         // They are not both collinear
174 
175         if (side_first != side_second)
176         {
177             return compare(side_first, side_second);
178         }
179 
180         // They are both left, both right, and/or both collinear (with each other and/or with p1,p2)
181         // Check mutual side
182         int const side_second_wrt_first = m_strategy.apply(m_turn_point, first.point, second.point);
183 
184         if (side_second_wrt_first == 0)
185         {
186             return on_same(first, second);
187         }
188 
189         int const side_first_wrt_second = m_strategy.apply(m_turn_point, second.point, first.point);
190         if (side_second_wrt_first != -side_first_wrt_second)
191         {
192             // (FP) accuracy error in side calculation, the sides are not opposite.
193             // In that case they can be handled as collinear.
194             // If not, then the sort-order might not be stable.
195             return on_same(first, second);
196         }
197 
198         // Both are on same side, and not collinear
199         // Union: return true if second is right w.r.t. first, so -1,
200         // so other is 1. union has greater as compare functor
201         // Intersection: v.v.
202         return compare(side_first_wrt_second, side_second_wrt_first);
203     }
204 
205 private :
206     Point const& m_origin;
207     Point const& m_turn_point;
208     SideStrategy const& m_strategy;
209 };
210 
211 // Sorts vectors in counter clockwise order (by default)
212 template
213 <
214     bool Reverse1,
215     bool Reverse2,
216     overlay_type OverlayType,
217     typename Point,
218     typename SideStrategy,
219     typename Compare
220 >
221 struct side_sorter
222 {
223     typedef ranked_point<Point> rp;
224 
225 private :
226     struct include_union
227     {
operator ()boost::geometry::detail::overlay::sort_by_side::side_sorter::include_union228         inline bool operator()(rp const& ranked_point) const
229         {
230             // New candidate if there are no polygons on left side,
231             // but there are on right side
232             return ranked_point.count_left == 0
233                 && ranked_point.count_right > 0;
234         }
235     };
236 
237     struct include_intersection
238     {
operator ()boost::geometry::detail::overlay::sort_by_side::side_sorter::include_intersection239         inline bool operator()(rp const& ranked_point) const
240         {
241             // New candidate if there are two polygons on right side,
242             // and less on the left side
243             return ranked_point.count_left < 2
244                 && ranked_point.count_right >= 2;
245         }
246     };
247 
248 public :
side_sorterboost::geometry::detail::overlay::sort_by_side::side_sorter249     side_sorter(SideStrategy const& strategy)
250         : m_origin_count(0)
251         , m_origin_segment_distance(0)
252         , m_strategy(strategy)
253     {}
254 
add_segment_fromboost::geometry::detail::overlay::sort_by_side::side_sorter255     void add_segment_from(signed_size_type turn_index, int op_index,
256             Point const& point_from,
257             operation_type op, segment_identifier const& si,
258             bool is_origin)
259     {
260         m_ranked_points.push_back(rp(point_from, turn_index, op_index, dir_from, op, si));
261         if (is_origin)
262         {
263             m_origin = point_from;
264             m_origin_count++;
265         }
266     }
267 
add_segment_toboost::geometry::detail::overlay::sort_by_side::side_sorter268     void add_segment_to(signed_size_type turn_index, int op_index,
269             Point const& point_to,
270             operation_type op, segment_identifier const& si)
271     {
272         m_ranked_points.push_back(rp(point_to, turn_index, op_index, dir_to, op, si));
273     }
274 
add_segmentboost::geometry::detail::overlay::sort_by_side::side_sorter275     void add_segment(signed_size_type turn_index, int op_index,
276             Point const& point_from, Point const& point_to,
277             operation_type op, segment_identifier const& si,
278             bool is_origin)
279     {
280         add_segment_from(turn_index, op_index, point_from, op, si, is_origin);
281         add_segment_to(turn_index, op_index, point_to, op, si);
282     }
283 
284     template <typename Operation, typename Geometry1, typename Geometry2>
addboost::geometry::detail::overlay::sort_by_side::side_sorter285     Point add(Operation const& op, signed_size_type turn_index, int op_index,
286             Geometry1 const& geometry1,
287             Geometry2 const& geometry2,
288             bool is_origin)
289     {
290         Point point1, point2, point3;
291         geometry::copy_segment_points<Reverse1, Reverse2>(geometry1, geometry2,
292                 op.seg_id, point1, point2, point3);
293         Point const& point_to = op.fraction.is_one() ? point3 : point2;
294         add_segment(turn_index, op_index, point1, point_to, op.operation, op.seg_id, is_origin);
295         return point1;
296     }
297 
298     template <typename Operation, typename Geometry1, typename Geometry2>
addboost::geometry::detail::overlay::sort_by_side::side_sorter299     void add(Operation const& op, signed_size_type turn_index, int op_index,
300             segment_identifier const& departure_seg_id,
301             Geometry1 const& geometry1,
302             Geometry2 const& geometry2,
303             bool check_origin)
304     {
305         Point const point1 = add(op, turn_index, op_index, geometry1, geometry2, false);
306 
307         if (check_origin)
308         {
309             bool const is_origin
310                     = op.seg_id.source_index == departure_seg_id.source_index
311                     && op.seg_id.ring_index == departure_seg_id.ring_index
312                     && op.seg_id.multi_index == departure_seg_id.multi_index;
313 
314             if (is_origin)
315             {
316                 signed_size_type const segment_distance = calculate_segment_distance(op, departure_seg_id, geometry1, geometry2);
317                 if (m_origin_count == 0 ||
318                         segment_distance < m_origin_segment_distance)
319                 {
320                     m_origin = point1;
321                     m_origin_segment_distance = segment_distance;
322                 }
323                 m_origin_count++;
324             }
325         }
326     }
327 
328     template <typename Operation, typename Geometry1, typename Geometry2>
calculate_segment_distanceboost::geometry::detail::overlay::sort_by_side::side_sorter329     static signed_size_type calculate_segment_distance(Operation const& op,
330             segment_identifier const& departure_seg_id,
331             Geometry1 const& geometry1,
332             Geometry2 const& geometry2)
333     {
334         if (op.seg_id.segment_index >= departure_seg_id.segment_index)
335         {
336             // dep.seg_id=5, op.seg_id=7, distance=2, being segments 5,6
337             return op.seg_id.segment_index - departure_seg_id.segment_index;
338         }
339         // Take wrap into account
340         // Suppose point_count=10 (10 points, 9 segments), dep.seg_id=7, op.seg_id=2,
341         // then distance=9-7+2=4, being segments 7,8,0,1
342         std::size_t const segment_count
343                     = op.seg_id.source_index == 0
344                     ? segment_count_on_ring(geometry1, op.seg_id)
345                     : segment_count_on_ring(geometry2, op.seg_id);
346         return segment_count - departure_seg_id.segment_index + op.seg_id.segment_index;
347     }
348 
applyboost::geometry::detail::overlay::sort_by_side::side_sorter349     void apply(Point const& turn_point)
350     {
351         // We need three compare functors:
352         // 1) to order clockwise (union) or counter clockwise (intersection)
353         // 2) to order by side, resulting in unique ranks for all points
354         // 3) to order by side, resulting in non-unique ranks
355         //    to give colinear points
356 
357         // Sort by side and assign rank
358         less_by_side<Point, SideStrategy, less_by_index, Compare> less_unique(m_origin, turn_point, m_strategy);
359         less_by_side<Point, SideStrategy, less_false, Compare> less_non_unique(m_origin, turn_point, m_strategy);
360 
361         std::sort(m_ranked_points.begin(), m_ranked_points.end(), less_unique);
362 
363         std::size_t colinear_rank = 0;
364         for (std::size_t i = 0; i < m_ranked_points.size(); i++)
365         {
366             if (i > 0
367                 && less_non_unique(m_ranked_points[i - 1], m_ranked_points[i]))
368             {
369                 // It is not collinear
370                 colinear_rank++;
371             }
372 
373             m_ranked_points[i].rank = colinear_rank;
374         }
375     }
376 
find_open_by_piece_indexboost::geometry::detail::overlay::sort_by_side::side_sorter377     void find_open_by_piece_index()
378     {
379         // For buffers, use piece index
380         std::set<signed_size_type> handled;
381 
382         for (std::size_t i = 0; i < m_ranked_points.size(); i++)
383         {
384             const rp& ranked = m_ranked_points[i];
385             if (ranked.direction != dir_from)
386             {
387                 continue;
388             }
389 
390             signed_size_type const& index = ranked.seg_id.piece_index;
391             if (handled.count(index) > 0)
392             {
393                 continue;
394             }
395             find_polygons_for_source<&segment_identifier::piece_index>(index, i);
396             handled.insert(index);
397         }
398     }
399 
find_open_by_source_indexboost::geometry::detail::overlay::sort_by_side::side_sorter400     void find_open_by_source_index()
401     {
402         // Check for source index 0 and 1
403         bool handled[2] = {false, false};
404         for (std::size_t i = 0; i < m_ranked_points.size(); i++)
405         {
406             const rp& ranked = m_ranked_points[i];
407             if (ranked.direction != dir_from)
408             {
409                 continue;
410             }
411 
412             signed_size_type const& index = ranked.seg_id.source_index;
413             if (index < 0 || index > 1 || handled[index])
414             {
415                 continue;
416             }
417             find_polygons_for_source<&segment_identifier::source_index>(index, i);
418             handled[index] = true;
419         }
420     }
421 
find_openboost::geometry::detail::overlay::sort_by_side::side_sorter422     void find_open()
423     {
424         if (BOOST_GEOMETRY_CONDITION(OverlayType == overlay_buffer))
425         {
426             find_open_by_piece_index();
427         }
428         else
429         {
430             find_open_by_source_index();
431         }
432     }
433 
reverseboost::geometry::detail::overlay::sort_by_side::side_sorter434     void reverse()
435     {
436         if (m_ranked_points.empty())
437         {
438             return;
439         }
440 
441         std::size_t const last = 1 + m_ranked_points.back().rank;
442 
443         // Move iterator after rank==0
444         bool has_first = false;
445         typename container_type::iterator it = m_ranked_points.begin() + 1;
446         for (; it != m_ranked_points.end() && it->rank == 0; ++it)
447         {
448             has_first = true;
449         }
450 
451         if (has_first)
452         {
453             // Reverse first part (having rank == 0), if any,
454             // but skip the very first row
455             std::reverse(m_ranked_points.begin() + 1, it);
456             for (typename container_type::iterator fit = m_ranked_points.begin();
457                  fit != it; ++fit)
458             {
459                 BOOST_ASSERT(fit->rank == 0);
460             }
461         }
462 
463         // Reverse the rest (main rank > 0)
464         std::reverse(it, m_ranked_points.end());
465         for (; it != m_ranked_points.end(); ++it)
466         {
467             BOOST_ASSERT(it->rank > 0);
468             it->rank = last - it->rank;
469         }
470     }
471 
has_originboost::geometry::detail::overlay::sort_by_side::side_sorter472     bool has_origin() const
473     {
474         return m_origin_count > 0;
475     }
476 
477 //private :
478 
479     typedef std::vector<rp> container_type;
480     container_type m_ranked_points;
481     Point m_origin;
482     std::size_t m_origin_count;
483     signed_size_type m_origin_segment_distance;
484     SideStrategy m_strategy;
485 
486 private :
487 
488     //! Check how many open spaces there are
489     template <typename Include>
open_countboost::geometry::detail::overlay::sort_by_side::side_sorter490     inline std::size_t open_count(Include const& include_functor) const
491     {
492         std::size_t result = 0;
493         rank_type last_rank = 0;
494         for (std::size_t i = 0; i < m_ranked_points.size(); i++)
495         {
496             rp const& ranked_point = m_ranked_points[i];
497 
498             if (ranked_point.rank > last_rank
499                 && ranked_point.direction == sort_by_side::dir_to
500                 && include_functor(ranked_point))
501             {
502                 result++;
503                 last_rank = ranked_point.rank;
504             }
505         }
506         return result;
507     }
508 
moveboost::geometry::detail::overlay::sort_by_side::side_sorter509     std::size_t move(std::size_t index) const
510     {
511         std::size_t const result = index + 1;
512         return result >= m_ranked_points.size() ? 0 : result;
513     }
514 
515     //! member is pointer to member (source_index or multi_index)
516     template <signed_size_type segment_identifier::*Member>
moveboost::geometry::detail::overlay::sort_by_side::side_sorter517     std::size_t move(signed_size_type member_index, std::size_t index) const
518     {
519         std::size_t result = move(index);
520         while (m_ranked_points[result].seg_id.*Member != member_index)
521         {
522             result = move(result);
523         }
524         return result;
525     }
526 
assign_ranksboost::geometry::detail::overlay::sort_by_side::side_sorter527     void assign_ranks(rank_type min_rank, rank_type max_rank, int side_index)
528     {
529         for (std::size_t i = 0; i < m_ranked_points.size(); i++)
530         {
531             rp& ranked = m_ranked_points[i];
532             // Suppose there are 8 ranks, if min=4,max=6: assign 4,5,6
533             // if min=5,max=2: assign from 5,6,7,1,2
534             bool const in_range
535                 = max_rank >= min_rank
536                 ? ranked.rank >= min_rank && ranked.rank <= max_rank
537                 : ranked.rank >= min_rank || ranked.rank <= max_rank
538                 ;
539 
540             if (in_range)
541             {
542                 if (side_index == 1)
543                 {
544                     ranked.count_left++;
545                 }
546                 else if (side_index == 2)
547                 {
548                     ranked.count_right++;
549                 }
550             }
551         }
552     }
553 
554     template <signed_size_type segment_identifier::*Member>
find_polygons_for_sourceboost::geometry::detail::overlay::sort_by_side::side_sorter555     void find_polygons_for_source(signed_size_type the_index,
556                 std::size_t start_index)
557     {
558         bool in_polygon = true; // Because start_index is "from", arrives at the turn
559         rp const& start_rp = m_ranked_points[start_index];
560         rank_type last_from_rank = start_rp.rank;
561         rank_type previous_rank = start_rp.rank;
562 
563         for (std::size_t index = move<Member>(the_index, start_index);
564              ;
565              index = move<Member>(the_index, index))
566         {
567             rp& ranked = m_ranked_points[index];
568 
569             if (ranked.rank != previous_rank && ! in_polygon)
570             {
571                 assign_ranks(last_from_rank, previous_rank - 1, 1);
572                 assign_ranks(last_from_rank + 1, previous_rank, 2);
573             }
574 
575             if (index == start_index)
576             {
577                 return;
578             }
579 
580             if (ranked.direction == dir_from)
581             {
582                 last_from_rank = ranked.rank;
583                 in_polygon = true;
584             }
585             else if (ranked.direction == dir_to)
586             {
587                 in_polygon = false;
588             }
589 
590             previous_rank = ranked.rank;
591         }
592     }
593 
594     //! Find closed zones and assign it
595     template <typename Include>
assign_zonesboost::geometry::detail::overlay::sort_by_side::side_sorter596     std::size_t assign_zones(Include const& include_functor)
597     {
598         // Find a starting point (the first rank after an outgoing rank
599         // with no polygons on the left side)
600         rank_type start_rank = m_ranked_points.size() + 1;
601         std::size_t start_index = 0;
602         rank_type max_rank = 0;
603         for (std::size_t i = 0; i < m_ranked_points.size(); i++)
604         {
605             rp const& ranked_point = m_ranked_points[i];
606             if (ranked_point.rank > max_rank)
607             {
608                 max_rank = ranked_point.rank;
609             }
610             if (ranked_point.direction == sort_by_side::dir_to
611                 && include_functor(ranked_point))
612             {
613                 start_rank = ranked_point.rank + 1;
614             }
615             if (ranked_point.rank == start_rank && start_index == 0)
616             {
617                 start_index = i;
618             }
619         }
620 
621         // Assign the zones
622         rank_type const undefined_rank = max_rank + 1;
623         std::size_t zone_id = 0;
624         rank_type last_rank = 0;
625         rank_type rank_at_next_zone = undefined_rank;
626         std::size_t index = start_index;
627         for (std::size_t i = 0; i < m_ranked_points.size(); i++)
628         {
629             rp& ranked_point = m_ranked_points[index];
630 
631             // Implement cyclic behavior
632             index++;
633             if (index == m_ranked_points.size())
634             {
635                 index = 0;
636             }
637 
638             if (ranked_point.rank != last_rank)
639             {
640                 if (ranked_point.rank == rank_at_next_zone)
641                 {
642                     zone_id++;
643                     rank_at_next_zone = undefined_rank;
644                 }
645 
646                 if (ranked_point.direction == sort_by_side::dir_to
647                     && include_functor(ranked_point))
648                 {
649                     rank_at_next_zone = ranked_point.rank + 1;
650                     if (rank_at_next_zone > max_rank)
651                     {
652                         rank_at_next_zone = 0;
653                     }
654                 }
655 
656                 last_rank = ranked_point.rank;
657             }
658 
659             ranked_point.zone = zone_id;
660         }
661         return zone_id;
662     }
663 
664 public :
open_countboost::geometry::detail::overlay::sort_by_side::side_sorter665     inline std::size_t open_count(operation_type for_operation) const
666     {
667         return for_operation == operation_union
668             ? open_count(include_union())
669             : open_count(include_intersection())
670             ;
671     }
672 
assign_zonesboost::geometry::detail::overlay::sort_by_side::side_sorter673     inline std::size_t assign_zones(operation_type for_operation)
674     {
675         return for_operation == operation_union
676             ? assign_zones(include_union())
677             : assign_zones(include_intersection())
678             ;
679     }
680 
681 };
682 
683 
684 //! Metafunction to define side_order (clockwise, ccw) by operation_type
685 template <operation_type OpType>
686 struct side_compare {};
687 
688 template <>
689 struct side_compare<operation_union>
690 {
691     typedef std::greater<int> type;
692 };
693 
694 template <>
695 struct side_compare<operation_intersection>
696 {
697     typedef std::less<int> type;
698 };
699 
700 
701 }}} // namespace detail::overlay::sort_by_side
702 #endif //DOXYGEN_NO_DETAIL
703 
704 
705 }} // namespace boost::geometry
706 
707 #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SORT_BY_SIDE_HPP
708