1 #pragma once
2 
3 #ifdef DEBUG
4 #include <iostream>
5 #include <sstream>
6 #endif
7 
8 #include <mapbox/geometry/wagyu/bound.hpp>
9 #include <mapbox/geometry/wagyu/config.hpp>
10 #include <mapbox/geometry/wagyu/edge.hpp>
11 #include <mapbox/geometry/wagyu/local_minimum.hpp>
12 #include <mapbox/geometry/wagyu/local_minimum_util.hpp>
13 #include <mapbox/geometry/wagyu/ring.hpp>
14 #include <mapbox/geometry/wagyu/scanbeam.hpp>
15 #include <mapbox/geometry/wagyu/util.hpp>
16 
17 namespace mapbox {
18 namespace geometry {
19 namespace wagyu {
20 
21 template <typename T>
22 using active_bound_list = std::vector<bound_ptr<T>>;
23 
24 template <typename T>
25 using active_bound_list_itr = typename active_bound_list<T>::iterator;
26 
27 template <typename T>
28 using active_bound_list_rev_itr = typename active_bound_list<T>::reverse_iterator;
29 
30 #ifdef DEBUG
31 
32 template <class charT, class traits, typename T>
operator <<(std::basic_ostream<charT,traits> & out,const active_bound_list<T> & bnds)33 inline std::basic_ostream<charT, traits>& operator<<(std::basic_ostream<charT, traits>& out,
34                                                      const active_bound_list<T>& bnds) {
35     std::size_t c = 0;
36     for (auto const& bnd : bnds) {
37         out << "Index: " << c++ << std::endl;
38         out << *bnd;
39     }
40     return out;
41 }
42 
43 template <typename T>
output_edges(active_bound_list<T> const & bnds)44 std::string output_edges(active_bound_list<T> const& bnds) {
45     std::ostringstream out;
46     out << "[";
47     bool first = true;
48     for (auto const& bnd : bnds) {
49         if (first) {
50             first = false;
51         } else {
52             out << ",";
53         }
54         out << "[[" << bnd->current_edge->bot.x << "," << bnd->current_edge->bot.y << "],[";
55         out << bnd->current_edge->top.x << "," << bnd->current_edge->top.y << "]]";
56     }
57     out << "]";
58     return out.str();
59 }
60 
61 #endif
62 
63 template <typename T>
is_even_odd_fill_type(bound<T> const & bound,fill_type subject_fill_type,fill_type clip_fill_type)64 bool is_even_odd_fill_type(bound<T> const& bound, fill_type subject_fill_type, fill_type clip_fill_type) {
65     if (bound.poly_type == polygon_type_subject) {
66         return subject_fill_type == fill_type_even_odd;
67     } else {
68         return clip_fill_type == fill_type_even_odd;
69     }
70 }
71 
72 template <typename T>
is_even_odd_alt_fill_type(bound<T> const & bound,fill_type subject_fill_type,fill_type clip_fill_type)73 bool is_even_odd_alt_fill_type(bound<T> const& bound, fill_type subject_fill_type, fill_type clip_fill_type) {
74     if (bound.poly_type == polygon_type_subject) {
75         return clip_fill_type == fill_type_even_odd;
76     } else {
77         return subject_fill_type == fill_type_even_odd;
78     }
79 }
80 
81 template <typename T>
82 struct bound_insert_location {
83 
84     bound<T> const& bound2;
85 
bound_insert_locationmapbox::geometry::wagyu::bound_insert_location86     bound_insert_location(bound<T> const& b) : bound2(b) {
87     }
88 
operator ()mapbox::geometry::wagyu::bound_insert_location89     bool operator()(bound_ptr<T> const& b) {
90         auto const& bound1 = *b;
91         if (values_are_equal(bound2.current_x, bound1.current_x)) {
92             if (bound2.current_edge->top.y > bound1.current_edge->top.y) {
93                 return less_than(static_cast<double>(bound2.current_edge->top.x),
94                                  get_current_x(*(bound1.current_edge), bound2.current_edge->top.y));
95             } else {
96                 return greater_than(static_cast<double>(bound1.current_edge->top.x),
97                                     get_current_x(*(bound2.current_edge), bound1.current_edge->top.y));
98             }
99         } else {
100             return bound2.current_x < bound1.current_x;
101         }
102     }
103 };
104 
105 template <typename T>
insert_bound_into_ABL(bound<T> & left,bound<T> & right,active_bound_list<T> & active_bounds)106 active_bound_list_itr<T> insert_bound_into_ABL(bound<T>& left, bound<T>& right, active_bound_list<T>& active_bounds) {
107 
108     auto itr = std::find_if(active_bounds.begin(), active_bounds.end(), bound_insert_location<T>(left));
109 #ifdef GCC_MISSING_VECTOR_RANGE_INSERT
110     itr = active_bounds.insert(itr, &right);
111     return active_bounds.insert(itr, &left);
112 #else
113     return active_bounds.insert(itr, { &left, &right });
114 #endif
115 }
116 
117 template <typename T>
is_maxima(bound<T> const & bnd,T y)118 inline bool is_maxima(bound<T> const& bnd, T y) {
119     return bnd.next_edge == bnd.edges.end() && bnd.current_edge->top.y == y;
120 }
121 
122 template <typename T>
is_maxima(active_bound_list_itr<T> const & bnd,T y)123 inline bool is_maxima(active_bound_list_itr<T> const& bnd, T y) {
124     return is_maxima(*(*bnd), y);
125 }
126 
127 template <typename T>
is_intermediate(bound<T> const & bnd,T y)128 inline bool is_intermediate(bound<T> const& bnd, T y) {
129     return bnd.next_edge != bnd.edges.end() && bnd.current_edge->top.y == y;
130 }
131 
132 template <typename T>
is_intermediate(active_bound_list_itr<T> const & bnd,T y)133 inline bool is_intermediate(active_bound_list_itr<T> const& bnd, T y) {
134     return is_intermediate(*(*bnd), y);
135 }
136 
137 template <typename T>
current_edge_is_horizontal(active_bound_list_itr<T> const & bnd)138 inline bool current_edge_is_horizontal(active_bound_list_itr<T> const& bnd) {
139     return is_horizontal(*((*bnd)->current_edge));
140 }
141 
142 template <typename T>
next_edge_is_horizontal(active_bound_list_itr<T> const & bnd)143 inline bool next_edge_is_horizontal(active_bound_list_itr<T> const& bnd) {
144     return is_horizontal(*((*bnd)->next_edge));
145 }
146 
147 template <typename T>
next_edge_in_bound(bound<T> & bnd,scanbeam_list<T> & scanbeam)148 void next_edge_in_bound(bound<T>& bnd, scanbeam_list<T>& scanbeam) {
149     auto& current_edge = bnd.current_edge;
150     ++current_edge;
151     if (current_edge != bnd.edges.end()) {
152         ++(bnd.next_edge);
153         bnd.current_x = static_cast<double>(current_edge->bot.x);
154         if (!is_horizontal<T>(*current_edge)) {
155             insert_sorted_scanbeam(scanbeam, current_edge->top.y);
156         }
157     }
158 }
159 
160 template <typename T>
get_maxima_pair(active_bound_list_itr<T> bnd,active_bound_list<T> & active_bounds)161 active_bound_list_itr<T> get_maxima_pair(active_bound_list_itr<T> bnd, active_bound_list<T>& active_bounds) {
162     bound_ptr<T> maximum = (*bnd)->maximum_bound;
163     return std::find(active_bounds.begin(), active_bounds.end(), maximum);
164 }
165 
166 template <typename T>
set_winding_count(active_bound_list_itr<T> bnd_itr,active_bound_list<T> & active_bounds,fill_type subject_fill_type,fill_type clip_fill_type)167 void set_winding_count(active_bound_list_itr<T> bnd_itr,
168                        active_bound_list<T>& active_bounds,
169                        fill_type subject_fill_type,
170                        fill_type clip_fill_type) {
171 
172     auto rev_bnd_itr = active_bound_list_rev_itr<T>(bnd_itr);
173     if (rev_bnd_itr == active_bounds.rend()) {
174         (*bnd_itr)->winding_count = (*bnd_itr)->winding_delta;
175         (*bnd_itr)->winding_count2 = 0;
176         return;
177     }
178 
179     // find the edge of the same polytype that immediately preceeds 'edge' in
180     // AEL
181     while (rev_bnd_itr != active_bounds.rend() && (*rev_bnd_itr)->poly_type != (*bnd_itr)->poly_type) {
182         ++rev_bnd_itr;
183     }
184     if (rev_bnd_itr == active_bounds.rend()) {
185         (*bnd_itr)->winding_count = (*bnd_itr)->winding_delta;
186         (*bnd_itr)->winding_count2 = 0;
187     } else if (is_even_odd_fill_type(*(*bnd_itr), subject_fill_type, clip_fill_type)) {
188         // EvenOdd filling ...
189         (*bnd_itr)->winding_count = (*bnd_itr)->winding_delta;
190         (*bnd_itr)->winding_count2 = (*rev_bnd_itr)->winding_count2;
191     } else {
192         // nonZero, Positive or Negative filling ...
193         if ((*rev_bnd_itr)->winding_count * (*rev_bnd_itr)->winding_delta < 0) {
194             // prev edge is 'decreasing' WindCount (WC) toward zero
195             // so we're outside the previous polygon ...
196             if (std::abs(static_cast<int>((*rev_bnd_itr)->winding_count)) > 1) {
197                 // outside prev poly but still inside another.
198                 // when reversing direction of prev poly use the same WC
199                 if ((*rev_bnd_itr)->winding_delta * (*bnd_itr)->winding_delta < 0) {
200                     (*bnd_itr)->winding_count = (*rev_bnd_itr)->winding_count;
201                 } else {
202                     // otherwise continue to 'decrease' WC ...
203                     (*bnd_itr)->winding_count = (*rev_bnd_itr)->winding_count + (*bnd_itr)->winding_delta;
204                 }
205             } else {
206                 // now outside all polys of same polytype so set own WC ...
207                 (*bnd_itr)->winding_count = (*bnd_itr)->winding_delta;
208             }
209         } else {
210             // prev edge is 'increasing' WindCount (WC) away from zero
211             // so we're inside the previous polygon ...
212             if ((*rev_bnd_itr)->winding_delta * (*bnd_itr)->winding_delta < 0) {
213                 // if wind direction is reversing prev then use same WC
214                 (*bnd_itr)->winding_count = (*rev_bnd_itr)->winding_count;
215             } else {
216                 // otherwise add to WC ...
217                 (*bnd_itr)->winding_count = (*rev_bnd_itr)->winding_count + (*bnd_itr)->winding_delta;
218             }
219         }
220         (*bnd_itr)->winding_count2 = (*rev_bnd_itr)->winding_count2;
221     }
222 
223     // update winding_count2 ...
224     auto bnd_itr_forward = rev_bnd_itr.base();
225     if (is_even_odd_alt_fill_type(*(*bnd_itr), subject_fill_type, clip_fill_type)) {
226         // EvenOdd filling ...
227         while (bnd_itr_forward != bnd_itr) {
228             (*bnd_itr)->winding_count2 = ((*bnd_itr)->winding_count2 == 0 ? 1 : 0);
229             ++bnd_itr_forward;
230         }
231     } else {
232         // nonZero, Positive or Negative filling ...
233         while (bnd_itr_forward != bnd_itr) {
234             (*bnd_itr)->winding_count2 += (*bnd_itr_forward)->winding_delta;
235             ++bnd_itr_forward;
236         }
237     }
238 }
239 
240 template <typename T>
is_contributing(bound<T> const & bnd,clip_type cliptype,fill_type subject_fill_type,fill_type clip_fill_type)241 bool is_contributing(bound<T> const& bnd, clip_type cliptype, fill_type subject_fill_type, fill_type clip_fill_type) {
242     fill_type pft = subject_fill_type;
243     fill_type pft2 = clip_fill_type;
244     if (bnd.poly_type != polygon_type_subject) {
245         pft = clip_fill_type;
246         pft2 = subject_fill_type;
247     }
248 
249     switch (pft) {
250     case fill_type_even_odd:
251         break;
252     case fill_type_non_zero:
253         if (std::abs(static_cast<int>(bnd.winding_count)) != 1) {
254             return false;
255         }
256         break;
257     case fill_type_positive:
258         if (bnd.winding_count != 1) {
259             return false;
260         }
261         break;
262     case fill_type_negative:
263     default:
264         if (bnd.winding_count != -1) {
265             return false;
266         }
267     }
268 
269     switch (cliptype) {
270     case clip_type_intersection:
271         switch (pft2) {
272         case fill_type_even_odd:
273         case fill_type_non_zero:
274             return (bnd.winding_count2 != 0);
275         case fill_type_positive:
276             return (bnd.winding_count2 > 0);
277         case fill_type_negative:
278         default:
279             return (bnd.winding_count2 < 0);
280         }
281         break;
282     case clip_type_union:
283         switch (pft2) {
284         case fill_type_even_odd:
285         case fill_type_non_zero:
286             return (bnd.winding_count2 == 0);
287         case fill_type_positive:
288             return (bnd.winding_count2 <= 0);
289         case fill_type_negative:
290         default:
291             return (bnd.winding_count2 >= 0);
292         }
293         break;
294     case clip_type_difference:
295         if (bnd.poly_type == polygon_type_subject) {
296             switch (pft2) {
297             case fill_type_even_odd:
298             case fill_type_non_zero:
299                 return (bnd.winding_count2 == 0);
300             case fill_type_positive:
301                 return (bnd.winding_count2 <= 0);
302             case fill_type_negative:
303             default:
304                 return (bnd.winding_count2 >= 0);
305             }
306         } else {
307             switch (pft2) {
308             case fill_type_even_odd:
309             case fill_type_non_zero:
310                 return (bnd.winding_count2 != 0);
311             case fill_type_positive:
312                 return (bnd.winding_count2 > 0);
313             case fill_type_negative:
314             default:
315                 return (bnd.winding_count2 < 0);
316             }
317         }
318         break;
319     case clip_type_x_or:
320         return true;
321         break;
322     default:
323         return true;
324     }
325 }
326 
327 template <typename T>
insert_lm_left_and_right_bound(bound<T> & left_bound,bound<T> & right_bound,active_bound_list<T> & active_bounds,ring_manager<T> & rings,scanbeam_list<T> & scanbeam,clip_type cliptype,fill_type subject_fill_type,fill_type clip_fill_type)328 void insert_lm_left_and_right_bound(bound<T>& left_bound,
329                                     bound<T>& right_bound,
330                                     active_bound_list<T>& active_bounds,
331                                     ring_manager<T>& rings,
332                                     scanbeam_list<T>& scanbeam,
333                                     clip_type cliptype,
334                                     fill_type subject_fill_type,
335                                     fill_type clip_fill_type) {
336 
337     // Both left and right bound
338     auto lb_abl_itr = insert_bound_into_ABL(left_bound, right_bound, active_bounds);
339     auto rb_abl_itr = std::next(lb_abl_itr);
340     set_winding_count(lb_abl_itr, active_bounds, subject_fill_type, clip_fill_type);
341     (*rb_abl_itr)->winding_count = (*lb_abl_itr)->winding_count;
342     (*rb_abl_itr)->winding_count2 = (*lb_abl_itr)->winding_count2;
343     if (is_contributing(left_bound, cliptype, subject_fill_type, clip_fill_type)) {
344         add_local_minimum_point(*(*lb_abl_itr), *(*rb_abl_itr), active_bounds, (*lb_abl_itr)->current_edge->bot, rings);
345     }
346 
347     // Add top of edges to scanbeam
348     insert_sorted_scanbeam(scanbeam, (*lb_abl_itr)->current_edge->top.y);
349 
350     if (!current_edge_is_horizontal<T>(rb_abl_itr)) {
351         insert_sorted_scanbeam(scanbeam, (*rb_abl_itr)->current_edge->top.y);
352     }
353 }
354 
355 template <typename T>
insert_local_minima_into_ABL(T const bot_y,local_minimum_ptr_list<T> const & minima_sorted,local_minimum_ptr_list_itr<T> & current_lm,active_bound_list<T> & active_bounds,ring_manager<T> & rings,scanbeam_list<T> & scanbeam,clip_type cliptype,fill_type subject_fill_type,fill_type clip_fill_type)356 void insert_local_minima_into_ABL(T const bot_y,
357                                   local_minimum_ptr_list<T> const& minima_sorted,
358                                   local_minimum_ptr_list_itr<T>& current_lm,
359                                   active_bound_list<T>& active_bounds,
360                                   ring_manager<T>& rings,
361                                   scanbeam_list<T>& scanbeam,
362                                   clip_type cliptype,
363                                   fill_type subject_fill_type,
364                                   fill_type clip_fill_type) {
365     while (current_lm != minima_sorted.end() && bot_y == (*current_lm)->y) {
366         initialize_lm<T>(current_lm);
367         auto& left_bound = (*current_lm)->left_bound;
368         auto& right_bound = (*current_lm)->right_bound;
369         insert_lm_left_and_right_bound(left_bound, right_bound, active_bounds, rings, scanbeam, cliptype,
370                                        subject_fill_type, clip_fill_type);
371         ++current_lm;
372     }
373 }
374 
375 template <typename T>
insert_horizontal_local_minima_into_ABL(T const top_y,local_minimum_ptr_list<T> const & minima_sorted,local_minimum_ptr_list_itr<T> & current_lm,active_bound_list<T> & active_bounds,ring_manager<T> & rings,scanbeam_list<T> & scanbeam,clip_type cliptype,fill_type subject_fill_type,fill_type clip_fill_type)376 void insert_horizontal_local_minima_into_ABL(T const top_y,
377                                              local_minimum_ptr_list<T> const& minima_sorted,
378                                              local_minimum_ptr_list_itr<T>& current_lm,
379                                              active_bound_list<T>& active_bounds,
380                                              ring_manager<T>& rings,
381                                              scanbeam_list<T>& scanbeam,
382                                              clip_type cliptype,
383                                              fill_type subject_fill_type,
384                                              fill_type clip_fill_type) {
385     while (current_lm != minima_sorted.end() && top_y == (*current_lm)->y && (*current_lm)->minimum_has_horizontal) {
386         initialize_lm<T>(current_lm);
387         auto& left_bound = (*current_lm)->left_bound;
388         auto& right_bound = (*current_lm)->right_bound;
389         insert_lm_left_and_right_bound(left_bound, right_bound, active_bounds, rings, scanbeam, cliptype,
390                                        subject_fill_type, clip_fill_type);
391         ++current_lm;
392     }
393 }
394 } // namespace wagyu
395 } // namespace geometry
396 } // namespace mapbox
397