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