1 #pragma once
2 
3 #include <mapbox/geometry/wagyu/active_bound_list.hpp>
4 #include <mapbox/geometry/wagyu/bound.hpp>
5 #include <mapbox/geometry/wagyu/bubble_sort.hpp>
6 #include <mapbox/geometry/wagyu/config.hpp>
7 #include <mapbox/geometry/wagyu/intersect.hpp>
8 #include <mapbox/geometry/wagyu/ring_util.hpp>
9 #include <mapbox/geometry/wagyu/util.hpp>
10 
11 #include <algorithm>
12 
13 namespace mapbox {
14 namespace geometry {
15 namespace wagyu {
16 
17 template <typename T>
18 struct intersect_list_sorter {
operator ()mapbox::geometry::wagyu::intersect_list_sorter19     inline bool operator()(intersect_node<T> const& node1, intersect_node<T> const& node2) {
20         if (!values_are_equal(node2.pt.y, node1.pt.y)) {
21             return node2.pt.y < node1.pt.y;
22         } else {
23             return (node2.bound1->winding_count2 + node2.bound2->winding_count2) >
24                    (node1.bound1->winding_count2 + node1.bound2->winding_count2);
25         }
26     }
27 };
28 
29 template <typename T>
round_point(mapbox::geometry::point<double> const & pt)30 inline mapbox::geometry::point<T> round_point(mapbox::geometry::point<double> const& pt) {
31     return mapbox::geometry::point<T>(round_towards_max<T>(pt.x), round_towards_max<T>(pt.y));
32 }
33 
34 template <typename T>
swap_rings(bound<T> & b1,bound<T> & b2)35 inline void swap_rings(bound<T>& b1, bound<T>& b2) {
36     ring_ptr<T> ring = b1.ring;
37     b1.ring = b2.ring;
38     b2.ring = ring;
39 }
40 
41 template <typename T>
swap_sides(bound<T> & b1,bound<T> & b2)42 inline void swap_sides(bound<T>& b1, bound<T>& b2) {
43     edge_side side = b1.side;
44     b1.side = b2.side;
45     b2.side = side;
46 }
47 
48 template <typename T1, typename T2>
get_edge_intersection(edge<T1> const & e1,edge<T1> const & e2,mapbox::geometry::point<T2> & pt)49 bool get_edge_intersection(edge<T1> const& e1, edge<T1> const& e2, mapbox::geometry::point<T2>& pt) {
50     T2 p0_x = static_cast<T2>(e1.bot.x);
51     T2 p0_y = static_cast<T2>(e1.bot.y);
52     T2 p1_x = static_cast<T2>(e1.top.x);
53     T2 p1_y = static_cast<T2>(e1.top.y);
54     T2 p2_x = static_cast<T2>(e2.bot.x);
55     T2 p2_y = static_cast<T2>(e2.bot.y);
56     T2 p3_x = static_cast<T2>(e2.top.x);
57     T2 p3_y = static_cast<T2>(e2.top.y);
58     T2 s1_x, s1_y, s2_x, s2_y;
59     s1_x = p1_x - p0_x;
60     s1_y = p1_y - p0_y;
61     s2_x = p3_x - p2_x;
62     s2_y = p3_y - p2_y;
63 
64     T2 s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / (-s2_x * s1_y + s1_x * s2_y);
65     T2 t = (s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / (-s2_x * s1_y + s1_x * s2_y);
66 
67     if (s >= 0.0 && s <= 1.0 && t >= 0.0 && t <= 1.0) {
68         pt.x = p0_x + (t * s1_x);
69         pt.y = p0_y + (t * s1_y);
70         return true;
71     }
72     // LCOV_EXCL_START
73     return false;
74     // LCOV_EXCL_END
75 }
76 
77 template <typename T>
78 struct intersection_compare {
operator ()mapbox::geometry::wagyu::intersection_compare79     bool operator()(bound_ptr<T> const& b1, bound_ptr<T> const& b2) {
80         return !(b1->current_x > b2->current_x && !slopes_equal(*(b1->current_edge), *(b2->current_edge)));
81     }
82 };
83 
84 template <typename T>
85 struct on_intersection_swap {
86 
87     intersect_list<T>& intersects;
88 
on_intersection_swapmapbox::geometry::wagyu::on_intersection_swap89     on_intersection_swap(intersect_list<T>& i) : intersects(i) {
90     }
91 
operator ()mapbox::geometry::wagyu::on_intersection_swap92     void operator()(bound_ptr<T> const& b1, bound_ptr<T> const& b2) {
93         mapbox::geometry::point<double> pt;
94         if (!get_edge_intersection<T, double>(*(b1->current_edge), *(b2->current_edge), pt)) {
95             // LCOV_EXCL_START
96             throw std::runtime_error("Trying to find intersection of lines that do not intersect");
97             // LCOV_EXCL_END
98         }
99         intersects.emplace_back(b1, b2, pt);
100     }
101 };
102 
103 template <typename T>
build_intersect_list(active_bound_list<T> & active_bounds,intersect_list<T> & intersects)104 void build_intersect_list(active_bound_list<T>& active_bounds, intersect_list<T>& intersects) {
105     bubble_sort(active_bounds.begin(), active_bounds.end(), intersection_compare<T>(),
106                 on_intersection_swap<T>(intersects));
107 }
108 
109 template <typename T>
intersect_bounds(bound<T> & b1,bound<T> & b2,mapbox::geometry::point<T> const & pt,clip_type cliptype,fill_type subject_fill_type,fill_type clip_fill_type,ring_manager<T> & rings,active_bound_list<T> & active_bounds)110 void intersect_bounds(bound<T>& b1,
111                       bound<T>& b2,
112                       mapbox::geometry::point<T> const& pt,
113                       clip_type cliptype,
114                       fill_type subject_fill_type,
115                       fill_type clip_fill_type,
116                       ring_manager<T>& rings,
117                       active_bound_list<T>& active_bounds) {
118     bool b1Contributing = (b1.ring != nullptr);
119     bool b2Contributing = (b2.ring != nullptr);
120 
121     // update winding counts...
122     // assumes that b1 will be to the Right of b2 ABOVE the intersection
123     if (b1.poly_type == b2.poly_type) {
124         if (is_even_odd_fill_type(b1, subject_fill_type, clip_fill_type)) {
125             std::swap(b1.winding_count, b2.winding_count);
126         } else {
127             if (b1.winding_count + b2.winding_delta == 0) {
128                 b1.winding_count = -b1.winding_count;
129             } else {
130                 b1.winding_count += b2.winding_delta;
131             }
132             if (b2.winding_count - b1.winding_delta == 0) {
133                 b2.winding_count = -b2.winding_count;
134             } else {
135                 b2.winding_count -= b1.winding_delta;
136             }
137         }
138     } else {
139         if (!is_even_odd_fill_type(b2, subject_fill_type, clip_fill_type)) {
140             b1.winding_count2 += b2.winding_delta;
141         } else {
142             b1.winding_count2 = (b1.winding_count2 == 0) ? 1 : 0;
143         }
144         if (!is_even_odd_fill_type(b1, subject_fill_type, clip_fill_type)) {
145             b2.winding_count2 -= b1.winding_delta;
146         } else {
147             b2.winding_count2 = (b2.winding_count2 == 0) ? 1 : 0;
148         }
149     }
150 
151     fill_type b1FillType, b2FillType, b1FillType2, b2FillType2;
152     if (b1.poly_type == polygon_type_subject) {
153         b1FillType = subject_fill_type;
154         b1FillType2 = clip_fill_type;
155     } else {
156         b1FillType = clip_fill_type;
157         b1FillType2 = subject_fill_type;
158     }
159     if (b2.poly_type == polygon_type_subject) {
160         b2FillType = subject_fill_type;
161         b2FillType2 = clip_fill_type;
162     } else {
163         b2FillType = clip_fill_type;
164         b2FillType2 = subject_fill_type;
165     }
166 
167     std::int32_t b1Wc, b2Wc;
168     switch (b1FillType) {
169     case fill_type_positive:
170         b1Wc = b1.winding_count;
171         break;
172     case fill_type_negative:
173         b1Wc = -b1.winding_count;
174         break;
175     case fill_type_even_odd:
176     case fill_type_non_zero:
177     default:
178         b1Wc = std::abs(static_cast<int>(b1.winding_count));
179     }
180     switch (b2FillType) {
181     case fill_type_positive:
182         b2Wc = b2.winding_count;
183         break;
184     case fill_type_negative:
185         b2Wc = -b2.winding_count;
186         break;
187     case fill_type_even_odd:
188     case fill_type_non_zero:
189     default:
190         b2Wc = std::abs(static_cast<int>(b2.winding_count));
191     }
192     if (b1Contributing && b2Contributing) {
193         if ((b1Wc != 0 && b1Wc != 1) || (b2Wc != 0 && b2Wc != 1) ||
194             (b1.poly_type != b2.poly_type && cliptype != clip_type_x_or)) {
195             add_local_maximum_point(b1, b2, pt, rings, active_bounds);
196         } else {
197             add_point(b1, active_bounds, pt, rings);
198             add_point(b2, active_bounds, pt, rings);
199             swap_sides(b1, b2);
200             swap_rings(b1, b2);
201         }
202     } else if (b1Contributing) {
203         if (b2Wc == 0 || b2Wc == 1) {
204             add_point(b1, active_bounds, pt, rings);
205             b2.last_point = pt;
206             swap_sides(b1, b2);
207             swap_rings(b1, b2);
208         }
209     } else if (b2Contributing) {
210         if (b1Wc == 0 || b1Wc == 1) {
211             b1.last_point = pt;
212             add_point(b2, active_bounds, pt, rings);
213             swap_sides(b1, b2);
214             swap_rings(b1, b2);
215         }
216     } else if ((b1Wc == 0 || b1Wc == 1) && (b2Wc == 0 || b2Wc == 1)) {
217         // neither bound is currently contributing ...
218 
219         std::int32_t b1Wc2, b2Wc2;
220         switch (b1FillType2) {
221         case fill_type_positive:
222             b1Wc2 = b1.winding_count2;
223             break;
224         case fill_type_negative:
225             b1Wc2 = -b1.winding_count2;
226             break;
227         case fill_type_even_odd:
228         case fill_type_non_zero:
229         default:
230             b1Wc2 = std::abs(static_cast<int>(b1.winding_count2));
231         }
232         switch (b2FillType2) {
233         case fill_type_positive:
234             b2Wc2 = b2.winding_count2;
235             break;
236         case fill_type_negative:
237             b2Wc2 = -b2.winding_count2;
238             break;
239         case fill_type_even_odd:
240         case fill_type_non_zero:
241         default:
242             b2Wc2 = std::abs(static_cast<int>(b2.winding_count2));
243         }
244 
245         if (b1.poly_type != b2.poly_type) {
246             add_local_minimum_point(b1, b2, active_bounds, pt, rings);
247         } else if (b1Wc == 1 && b2Wc == 1) {
248             switch (cliptype) {
249             case clip_type_intersection:
250                 if (b1Wc2 > 0 && b2Wc2 > 0) {
251                     add_local_minimum_point(b1, b2, active_bounds, pt, rings);
252                 }
253                 break;
254             default:
255             case clip_type_union:
256                 if (b1Wc2 <= 0 && b2Wc2 <= 0) {
257                     add_local_minimum_point(b1, b2, active_bounds, pt, rings);
258                 }
259                 break;
260             case clip_type_difference:
261                 if (((b1.poly_type == polygon_type_clip) && (b1Wc2 > 0) && (b2Wc2 > 0)) ||
262                     ((b1.poly_type == polygon_type_subject) && (b1Wc2 <= 0) && (b2Wc2 <= 0))) {
263                     add_local_minimum_point(b1, b2, active_bounds, pt, rings);
264                 }
265                 break;
266             case clip_type_x_or:
267                 add_local_minimum_point(b1, b2, active_bounds, pt, rings);
268             }
269         } else {
270             swap_sides(b1, b2);
271         }
272     }
273 }
274 
275 template <typename T>
bounds_adjacent(intersect_node<T> const & inode,bound_ptr<T> next)276 bool bounds_adjacent(intersect_node<T> const& inode, bound_ptr<T> next) {
277     return (next == inode.bound2) || (next == inode.bound1);
278 }
279 
280 template <typename T>
281 struct find_first_bound {
282     bound_ptr<T> b1;
283     bound_ptr<T> b2;
284 
find_first_boundmapbox::geometry::wagyu::find_first_bound285     find_first_bound(intersect_node<T> const& inode) : b1(inode.bound1), b2(inode.bound2) {
286     }
287 
operator ()mapbox::geometry::wagyu::find_first_bound288     bool operator()(bound_ptr<T> const& b) {
289         return b == b1 || b == b2;
290     }
291 };
292 
293 template <typename T>
process_intersect_list(intersect_list<T> & intersects,clip_type cliptype,fill_type subject_fill_type,fill_type clip_fill_type,ring_manager<T> & rings,active_bound_list<T> & active_bounds)294 void process_intersect_list(intersect_list<T>& intersects,
295                             clip_type cliptype,
296                             fill_type subject_fill_type,
297                             fill_type clip_fill_type,
298                             ring_manager<T>& rings,
299                             active_bound_list<T>& active_bounds) {
300     for (auto node_itr = intersects.begin(); node_itr != intersects.end(); ++node_itr) {
301         auto b1 = std::find_if(active_bounds.begin(), active_bounds.end(), find_first_bound<T>(*node_itr));
302         auto b2 = std::next(b1);
303         if (!bounds_adjacent(*node_itr, *b2)) {
304             auto next_itr = std::next(node_itr);
305             while (next_itr != intersects.end()) {
306                 auto n1 = std::find_if(active_bounds.begin(), active_bounds.end(), find_first_bound<T>(*next_itr));
307                 auto n2 = std::next(n1);
308                 if (bounds_adjacent(*next_itr, *n2)) {
309                     b1 = n1;
310                     b2 = n2;
311                     break;
312                 }
313                 ++next_itr;
314             }
315             if (next_itr == intersects.end()) {
316                 throw std::runtime_error("Could not properly correct intersection order.");
317             }
318             std::iter_swap(node_itr, next_itr);
319         }
320         mapbox::geometry::point<T> pt = round_point<T>(node_itr->pt);
321         intersect_bounds(*(node_itr->bound1), *(node_itr->bound2), pt, cliptype, subject_fill_type, clip_fill_type,
322                          rings, active_bounds);
323         std::iter_swap(b1, b2);
324     }
325 }
326 
327 template <typename T>
update_current_x(active_bound_list<T> & active_bounds,T top_y)328 void update_current_x(active_bound_list<T>& active_bounds, T top_y) {
329     std::size_t pos = 0;
330     for (auto& bnd : active_bounds) {
331         bnd->pos = pos++;
332         bnd->current_x = get_current_x(*bnd->current_edge, top_y);
333     }
334 }
335 
336 template <typename T>
process_intersections(T top_y,active_bound_list<T> & active_bounds,clip_type cliptype,fill_type subject_fill_type,fill_type clip_fill_type,ring_manager<T> & rings)337 void process_intersections(T top_y,
338                            active_bound_list<T>& active_bounds,
339                            clip_type cliptype,
340                            fill_type subject_fill_type,
341                            fill_type clip_fill_type,
342                            ring_manager<T>& rings) {
343     if (active_bounds.empty()) {
344         return;
345     }
346     update_current_x(active_bounds, top_y);
347     intersect_list<T> intersects;
348     build_intersect_list(active_bounds, intersects);
349 
350     if (intersects.empty()) {
351         return;
352     }
353 
354     // Restore order of active bounds list
355     std::stable_sort(active_bounds.begin(), active_bounds.end(),
356                      [](bound_ptr<T> const& b1, bound_ptr<T> const& b2) { return b1->pos < b2->pos; });
357 
358     // Sort the intersection list
359     std::stable_sort(intersects.begin(), intersects.end(), intersect_list_sorter<T>());
360 
361     process_intersect_list(intersects, cliptype, subject_fill_type, clip_fill_type, rings, active_bounds);
362 }
363 } // namespace wagyu
364 } // namespace geometry
365 } // namespace mapbox
366