1 #pragma once
2 
3 #ifdef DEBUG
4 #include <iostream>
5 // Example debug print for backtrace - only works on IOS
6 #include <execinfo.h>
7 #include <stdio.h>
8 //
9 // void* callstack[128];
10 // int i, frames = backtrace(callstack, 128);
11 // char** strs = backtrace_symbols(callstack, frames);
12 // for (i = 0; i < frames; ++i) {
13 //     printf("%s\n", strs[i]);
14 // }
15 // free(strs);
16 #endif
17 
18 #include <algorithm>
19 
20 #include <mapbox/geometry/wagyu/active_bound_list.hpp>
21 #include <mapbox/geometry/wagyu/config.hpp>
22 #include <mapbox/geometry/wagyu/edge.hpp>
23 #include <mapbox/geometry/wagyu/ring.hpp>
24 #include <mapbox/geometry/wagyu/util.hpp>
25 
26 namespace mapbox {
27 namespace geometry {
28 namespace wagyu {
29 
30 template <typename T>
set_hole_state(bound<T> & bnd,active_bound_list<T> const & active_bounds,ring_manager<T> & rings)31 void set_hole_state(bound<T>& bnd,
32                     active_bound_list<T> const& active_bounds,
33                     ring_manager<T>& rings) {
34     auto bnd_itr = std::find(active_bounds.rbegin(), active_bounds.rend(), &bnd);
35     ++bnd_itr;
36     bound_ptr<T> bndTmp = nullptr;
37     // Find first non line ring to the left of current bound.
38     while (bnd_itr != active_bounds.rend()) {
39         if (*bnd_itr == nullptr) {
40             ++bnd_itr;
41             continue;
42         }
43         if ((*bnd_itr)->ring) {
44             if (!bndTmp) {
45                 bndTmp = (*bnd_itr);
46             } else if (bndTmp->ring == (*bnd_itr)->ring) {
47                 bndTmp = nullptr;
48             }
49         }
50         ++bnd_itr;
51     }
52     if (!bndTmp) {
53         bnd.ring->parent = nullptr;
54         rings.children.push_back(bnd.ring);
55     } else {
56         bnd.ring->parent = bndTmp->ring;
57         bndTmp->ring->children.push_back(bnd.ring);
58     }
59 }
60 
61 template <typename T>
update_current_hp_itr(T scanline_y,ring_manager<T> & rings)62 void update_current_hp_itr(T scanline_y, ring_manager<T>& rings) {
63     while (rings.current_hp_itr->y > scanline_y) {
64         ++rings.current_hp_itr;
65     }
66 }
67 
68 template <typename T>
69 struct hot_pixel_sorter {
operator ()mapbox::geometry::wagyu::hot_pixel_sorter70     inline bool operator()(mapbox::geometry::point<T> const& pt1,
71                            mapbox::geometry::point<T> const& pt2) {
72         if (pt1.y == pt2.y) {
73             return pt1.x < pt2.x;
74         } else {
75             return pt1.y > pt2.y;
76         }
77     }
78 };
79 
80 // Due to the nature of floating point calculations
81 // and the high likely hood of values around X.5, we
82 // need to fudge what is X.5 some for our rounding.
83 const double rounding_offset = 1e-12;
84 const double rounding_offset_y = 5e-13;
85 
86 template <typename T>
round_towards_min(double val)87 T round_towards_min(double val) {
88     // 0.5 rounds to 0
89     // 0.0 rounds to 0
90     // -0.5 rounds to -1
91     return static_cast<T>(std::ceil(val - 0.5 + rounding_offset));
92 }
93 
94 template <typename T>
round_towards_max(double val)95 T round_towards_max(double val) {
96     // 0.5 rounds to 1
97     // 0.0 rounds to 0
98     // -0.5 rounds to 0
99     return static_cast<T>(std::floor(val + 0.5 + rounding_offset));
100 }
101 
102 template <typename T>
get_edge_min_x(edge<T> const & edge,const T current_y)103 inline T get_edge_min_x(edge<T> const& edge, const T current_y) {
104     if (is_horizontal(edge)) {
105         if (edge.bot.x < edge.top.x) {
106             return edge.bot.x;
107         } else {
108             return edge.top.x;
109         }
110     } else if (edge.dx > 0.0) {
111         if (current_y == edge.top.y) {
112             return edge.top.x;
113         } else {
114             double lower_range_y = static_cast<double>(current_y - edge.bot.y) - 0.5;
115             double return_val = static_cast<double>(edge.bot.x) + edge.dx * lower_range_y;
116             T value = round_towards_min<T>(return_val);
117             return value;
118         }
119     } else {
120         if (current_y == edge.bot.y) {
121             return edge.bot.x;
122         } else {
123             double return_val =
124                 static_cast<double>(edge.bot.x) +
125                 edge.dx * (static_cast<double>(current_y - edge.bot.y) + 0.5 - rounding_offset_y);
126             T value = round_towards_min<T>(return_val);
127             return value;
128         }
129     }
130 }
131 
132 template <typename T>
get_edge_max_x(edge<T> const & edge,const T current_y)133 inline T get_edge_max_x(edge<T> const& edge, const T current_y) {
134     if (is_horizontal(edge)) {
135         if (edge.bot.x > edge.top.x) {
136             return edge.bot.x;
137         } else {
138             return edge.top.x;
139         }
140     } else if (edge.dx < 0.0) {
141         if (current_y == edge.top.y) {
142             return edge.top.x;
143         } else {
144             double lower_range_y = static_cast<double>(current_y - edge.bot.y) - 0.5;
145             double return_val = static_cast<double>(edge.bot.x) + edge.dx * lower_range_y;
146             T value = round_towards_max<T>(return_val);
147             return value;
148         }
149     } else {
150         if (current_y == edge.bot.y) {
151             return edge.bot.x;
152         } else {
153             double return_val =
154                 static_cast<double>(edge.bot.x) +
155                 edge.dx * (static_cast<double>(current_y - edge.bot.y) + 0.5 - rounding_offset_y);
156             T value = round_towards_max<T>(return_val);
157             return value;
158         }
159     }
160 }
161 
162 template <typename T>
hot_pixel_set_left_to_right(T y,T start_x,T end_x,bound<T> & bnd,ring_manager<T> & rings,hot_pixel_itr<T> & itr,hot_pixel_itr<T> & end,bool add_end_point)163 void hot_pixel_set_left_to_right(T y,
164                                  T start_x,
165                                  T end_x,
166                                  bound<T>& bnd,
167                                  ring_manager<T>& rings,
168                                  hot_pixel_itr<T>& itr,
169                                  hot_pixel_itr<T>& end,
170                                  bool add_end_point) {
171     T x_min = get_edge_min_x(*(bnd.current_edge), y);
172     x_min = std::max(x_min, start_x);
173     T x_max = get_edge_max_x(*(bnd.current_edge), y);
174     x_max = std::min(x_max, end_x);
175     for (; itr != end; ++itr) {
176         if (itr->x < x_min) {
177             continue;
178         }
179         if (itr->x > x_max) {
180             break;
181         }
182         if (!add_end_point && itr->x == end_x) {
183             continue;
184         }
185         point_ptr<T> op = bnd.ring->points;
186         bool to_front = (bnd.side == edge_left);
187         if (to_front && (*itr == *op)) {
188             continue;
189         } else if (!to_front && (*itr == *op->prev)) {
190             continue;
191         }
192         point_ptr<T> new_point = create_new_point(bnd.ring, *itr, op, rings);
193         if (to_front) {
194             bnd.ring->points = new_point;
195         }
196     }
197 }
198 
199 template <typename T>
hot_pixel_set_right_to_left(T y,T start_x,T end_x,bound<T> & bnd,ring_manager<T> & rings,hot_pixel_rev_itr<T> & itr,hot_pixel_rev_itr<T> & end,bool add_end_point)200 void hot_pixel_set_right_to_left(T y,
201                                  T start_x,
202                                  T end_x,
203                                  bound<T>& bnd,
204                                  ring_manager<T>& rings,
205                                  hot_pixel_rev_itr<T>& itr,
206                                  hot_pixel_rev_itr<T>& end,
207                                  bool add_end_point) {
208     T x_min = get_edge_min_x(*(bnd.current_edge), y);
209     x_min = std::max(x_min, end_x);
210     T x_max = get_edge_max_x(*(bnd.current_edge), y);
211     x_max = std::min(x_max, start_x);
212     for (; itr != end; ++itr) {
213         if (itr->x > x_max) {
214             continue;
215         }
216         if (itr->x < x_min) {
217             break;
218         }
219         if (!add_end_point && itr->x == end_x) {
220             continue;
221         }
222         point_ptr<T> op = bnd.ring->points;
223         bool to_front = (bnd.side == edge_left);
224         if (to_front && (*itr == *op)) {
225             continue;
226         } else if (!to_front && (*itr == *op->prev)) {
227             continue;
228         }
229         point_ptr<T> new_point = create_new_point(bnd.ring, *itr, op, rings);
230         if (to_front) {
231             bnd.ring->points = new_point;
232         }
233     }
234 }
235 
236 template <typename T>
sort_hot_pixels(ring_manager<T> & rings)237 void sort_hot_pixels(ring_manager<T>& rings) {
238     std::sort(rings.hot_pixels.begin(), rings.hot_pixels.end(), hot_pixel_sorter<T>());
239     auto last = std::unique(rings.hot_pixels.begin(), rings.hot_pixels.end());
240     rings.hot_pixels.erase(last, rings.hot_pixels.end());
241 }
242 
243 template <typename T>
insert_hot_pixels_in_path(bound<T> & bnd,mapbox::geometry::point<T> const & end_pt,ring_manager<T> & rings,bool add_end_point)244 void insert_hot_pixels_in_path(bound<T>& bnd,
245                                mapbox::geometry::point<T> const& end_pt,
246                                ring_manager<T>& rings,
247                                bool add_end_point) {
248     if (end_pt == bnd.last_point) {
249         return;
250     }
251 
252     T start_y = bnd.last_point.y;
253     T start_x = bnd.last_point.x;
254     T end_y = end_pt.y;
255     T end_x = end_pt.x;
256 
257     auto itr = rings.current_hp_itr;
258     while (itr->y <= start_y && itr != rings.hot_pixels.begin()) {
259         --itr;
260     }
261     if (start_x > end_x) {
262         for (; itr != rings.hot_pixels.end();) {
263             if (itr->y > start_y) {
264                 ++itr;
265                 continue;
266             }
267             if (itr->y < end_y) {
268                 break;
269             }
270             T y = itr->y;
271             auto last_itr = hot_pixel_rev_itr<T>(itr);
272             while (itr != rings.hot_pixels.end() && itr->y == y) {
273                 ++itr;
274             }
275             auto first_itr = hot_pixel_rev_itr<T>(itr);
276             bool add_end_point_itr = (y != end_pt.y || add_end_point);
277             hot_pixel_set_right_to_left(y, start_x, end_x, bnd, rings, first_itr, last_itr,
278                                         add_end_point_itr);
279         }
280     } else {
281         for (; itr != rings.hot_pixels.end();) {
282             if (itr->y > start_y) {
283                 ++itr;
284                 continue;
285             }
286             if (itr->y < end_y) {
287                 break;
288             }
289             T y = itr->y;
290             auto first_itr = itr;
291             while (itr != rings.hot_pixels.end() && itr->y == y) {
292                 ++itr;
293             }
294             auto last_itr = itr;
295             bool add_end_point_itr = (y != end_pt.y || add_end_point);
296             hot_pixel_set_left_to_right(y, start_x, end_x, bnd, rings, first_itr, last_itr,
297                                         add_end_point_itr);
298         }
299     }
300     bnd.last_point = end_pt;
301 }
302 
303 template <typename T>
add_to_hot_pixels(mapbox::geometry::point<T> const & pt,ring_manager<T> & rings)304 void add_to_hot_pixels(mapbox::geometry::point<T> const& pt, ring_manager<T>& rings) {
305     rings.hot_pixels.push_back(pt);
306 }
307 
308 template <typename T>
add_first_point(bound<T> & bnd,active_bound_list<T> & active_bounds,mapbox::geometry::point<T> const & pt,ring_manager<T> & rings)309 void add_first_point(bound<T>& bnd,
310                      active_bound_list<T>& active_bounds,
311                      mapbox::geometry::point<T> const& pt,
312                      ring_manager<T>& rings) {
313 
314     ring_ptr<T> r = create_new_ring(rings);
315     bnd.ring = r;
316     r->points = create_new_point(r, pt, rings);
317     set_hole_state(bnd, active_bounds, rings);
318     bnd.last_point = pt;
319 }
320 
321 template <typename T>
add_point_to_ring(bound<T> & bnd,mapbox::geometry::point<T> const & pt,ring_manager<T> & rings)322 void add_point_to_ring(bound<T>& bnd,
323                        mapbox::geometry::point<T> const& pt,
324                        ring_manager<T>& rings) {
325     assert(bnd.ring);
326     // Handle hot pixels
327     insert_hot_pixels_in_path(bnd, pt, rings, false);
328 
329     // bnd.ring->points is the 'Left-most' point & bnd.ring->points->prev is the
330     // 'Right-most'
331     point_ptr<T> op = bnd.ring->points;
332     bool to_front = (bnd.side == edge_left);
333     if (to_front && (pt == *op)) {
334         return;
335     } else if (!to_front && (pt == *op->prev)) {
336         return;
337     }
338     point_ptr<T> new_point = create_new_point(bnd.ring, pt, bnd.ring->points, rings);
339     if (to_front) {
340         bnd.ring->points = new_point;
341     }
342 }
343 
344 template <typename T>
add_point(bound<T> & bnd,active_bound_list<T> & active_bounds,mapbox::geometry::point<T> const & pt,ring_manager<T> & rings)345 void add_point(bound<T>& bnd,
346                active_bound_list<T>& active_bounds,
347                mapbox::geometry::point<T> const& pt,
348                ring_manager<T>& rings) {
349     if (bnd.ring == nullptr) {
350         add_first_point(bnd, active_bounds, pt, rings);
351     } else {
352         add_point_to_ring(bnd, pt, rings);
353     }
354 }
355 
356 template <typename T>
add_local_minimum_point(bound<T> & b1,bound<T> & b2,active_bound_list<T> & active_bounds,mapbox::geometry::point<T> const & pt,ring_manager<T> & rings)357 void add_local_minimum_point(bound<T>& b1,
358                              bound<T>& b2,
359                              active_bound_list<T>& active_bounds,
360                              mapbox::geometry::point<T> const& pt,
361                              ring_manager<T>& rings) {
362     if (is_horizontal(*b2.current_edge) || (b1.current_edge->dx > b2.current_edge->dx)) {
363         add_point(b1, active_bounds, pt, rings);
364         b2.last_point = pt;
365         b2.ring = b1.ring;
366         b1.side = edge_left;
367         b2.side = edge_right;
368     } else {
369         add_point(b2, active_bounds, pt, rings);
370         b1.last_point = pt;
371         b1.ring = b2.ring;
372         b1.side = edge_right;
373         b2.side = edge_left;
374     }
375 }
376 
377 template <typename T>
get_dx(point<T> const & pt1,point<T> const & pt2)378 inline double get_dx(point<T> const& pt1, point<T> const& pt2) {
379     if (pt1.y == pt2.y) {
380         return std::numeric_limits<double>::infinity();
381     } else {
382         return static_cast<double>(pt2.x - pt1.x) / static_cast<double>(pt2.y - pt1.y);
383     }
384 }
385 
386 template <typename T>
first_is_bottom_point(const_point_ptr<T> btmPt1,const_point_ptr<T> btmPt2)387 bool first_is_bottom_point(const_point_ptr<T> btmPt1, const_point_ptr<T> btmPt2) {
388     point_ptr<T> p = btmPt1->prev;
389     while ((*p == *btmPt1) && (p != btmPt1)) {
390         p = p->prev;
391     }
392     double dx1p = std::fabs(get_dx(*btmPt1, *p));
393 
394     p = btmPt1->next;
395     while ((*p == *btmPt1) && (p != btmPt1)) {
396         p = p->next;
397     }
398     double dx1n = std::fabs(get_dx(*btmPt1, *p));
399 
400     p = btmPt2->prev;
401     while ((*p == *btmPt2) && (p != btmPt2)) {
402         p = p->prev;
403     }
404     double dx2p = std::fabs(get_dx(*btmPt2, *p));
405 
406     p = btmPt2->next;
407     while ((*p == *btmPt2) && (p != btmPt2)) {
408         p = p->next;
409     }
410     double dx2n = std::fabs(get_dx(*btmPt2, *p));
411 
412     if (values_are_equal(std::max(dx1p, dx1n), std::max(dx2p, dx2n)) &&
413         values_are_equal(std::min(dx1p, dx1n), std::min(dx2p, dx2n))) {
414         std::size_t s = 0;
415         mapbox::geometry::box<T> bbox({ 0, 0 }, { 0, 0 });
416         return area_from_point(btmPt1, s, bbox) > 0.0; // if otherwise identical use orientation
417     } else {
418         return (greater_than_or_equal(dx1p, dx2p) && greater_than_or_equal(dx1p, dx2n)) ||
419                (greater_than_or_equal(dx1n, dx2p) && greater_than_or_equal(dx1n, dx2n));
420     }
421 }
422 
423 template <typename T>
get_bottom_point(point_ptr<T> pp)424 point_ptr<T> get_bottom_point(point_ptr<T> pp) {
425     point_ptr<T> dups = nullptr;
426     point_ptr<T> p = pp->next;
427     while (p != pp) {
428         if (p->y > pp->y) {
429             pp = p;
430             dups = nullptr;
431         } else if (p->y == pp->y && p->x <= pp->x) {
432             if (p->x < pp->x) {
433                 dups = nullptr;
434                 pp = p;
435             } else {
436                 if (p->next != pp && p->prev != pp) {
437                     dups = p;
438                 }
439             }
440         }
441         p = p->next;
442     }
443     if (dups) {
444         // there appears to be at least 2 vertices at bottom_point so ...
445         while (dups != p) {
446             if (!first_is_bottom_point(p, dups)) {
447                 pp = dups;
448             }
449             dups = dups->next;
450             while (*dups != *pp) {
451                 dups = dups->next;
452             }
453         }
454     }
455     return pp;
456 }
457 
458 template <typename T>
get_lower_most_ring(ring_ptr<T> outRec1,ring_ptr<T> outRec2)459 ring_ptr<T> get_lower_most_ring(ring_ptr<T> outRec1, ring_ptr<T> outRec2) {
460     // work out which polygon fragment has the correct hole state ...
461     if (!outRec1->bottom_point) {
462         outRec1->bottom_point = get_bottom_point(outRec1->points);
463     }
464     if (!outRec2->bottom_point) {
465         outRec2->bottom_point = get_bottom_point(outRec2->points);
466     }
467     point_ptr<T> OutPt1 = outRec1->bottom_point;
468     point_ptr<T> OutPt2 = outRec2->bottom_point;
469     if (OutPt1->y > OutPt2->y) {
470         return outRec1;
471     } else if (OutPt1->y < OutPt2->y) {
472         return outRec2;
473     } else if (OutPt1->x < OutPt2->x) {
474         return outRec1;
475     } else if (OutPt1->x > OutPt2->x) {
476         return outRec2;
477     } else if (OutPt1->next == OutPt1) {
478         return outRec2;
479     } else if (OutPt2->next == OutPt2) {
480         return outRec1;
481     } else if (first_is_bottom_point(OutPt1, OutPt2)) {
482         return outRec1;
483     } else {
484         return outRec2;
485     }
486 }
487 
488 template <typename T>
ring1_child_below_ring2(ring_ptr<T> ring1,ring_ptr<T> ring2)489 bool ring1_child_below_ring2(ring_ptr<T> ring1, ring_ptr<T> ring2) {
490     do {
491         ring1 = ring1->parent;
492         if (ring1 == ring2) {
493             return true;
494         }
495     } while (ring1);
496     return false;
497 }
498 
499 template <typename T>
update_points_ring(ring_ptr<T> ring)500 void update_points_ring(ring_ptr<T> ring) {
501     point_ptr<T> op = ring->points;
502     do {
503         op->ring = ring;
504         op = op->prev;
505     } while (op != ring->points);
506 }
507 
508 template <typename T>
append_ring(bound<T> & b1,bound<T> & b2,active_bound_list<T> & active_bounds,ring_manager<T> & manager)509 void append_ring(bound<T>& b1,
510                  bound<T>& b2,
511                  active_bound_list<T>& active_bounds,
512                  ring_manager<T>& manager) {
513     // get the start and ends of both output polygons ...
514     ring_ptr<T> outRec1 = b1.ring;
515     ring_ptr<T> outRec2 = b2.ring;
516 
517     ring_ptr<T> keep_ring;
518     bound_ptr<T> keep_bound;
519     ring_ptr<T> remove_ring;
520     bound_ptr<T> remove_bound;
521     if (ring1_child_below_ring2(outRec1, outRec2)) {
522         keep_ring = outRec2;
523         keep_bound = &b2;
524         remove_ring = outRec1;
525         remove_bound = &b1;
526     } else if (ring1_child_below_ring2(outRec2, outRec1)) {
527         keep_ring = outRec1;
528         keep_bound = &b1;
529         remove_ring = outRec2;
530         remove_bound = &b2;
531     } else if (outRec1 == get_lower_most_ring(outRec1, outRec2)) {
532         keep_ring = outRec1;
533         keep_bound = &b1;
534         remove_ring = outRec2;
535         remove_bound = &b2;
536     } else {
537         keep_ring = outRec2;
538         keep_bound = &b2;
539         remove_ring = outRec1;
540         remove_bound = &b1;
541     }
542 
543     // get the start and ends of both output polygons and
544     // join b2 poly onto b1 poly and delete pointers to b2 ...
545 
546     point_ptr<T> p1_lft = keep_ring->points;
547     point_ptr<T> p1_rt = p1_lft->prev;
548     point_ptr<T> p2_lft = remove_ring->points;
549     point_ptr<T> p2_rt = p2_lft->prev;
550 
551     // join b2 poly onto b1 poly and delete pointers to b2 ...
552     if (keep_bound->side == edge_left) {
553         if (remove_bound->side == edge_left) {
554             // z y x a b c
555             reverse_ring(p2_lft);
556             p2_lft->next = p1_lft;
557             p1_lft->prev = p2_lft;
558             p1_rt->next = p2_rt;
559             p2_rt->prev = p1_rt;
560             keep_ring->points = p2_rt;
561         } else {
562             // x y z a b c
563             p2_rt->next = p1_lft;
564             p1_lft->prev = p2_rt;
565             p2_lft->prev = p1_rt;
566             p1_rt->next = p2_lft;
567             keep_ring->points = p2_lft;
568         }
569     } else {
570         if (remove_bound->side == edge_right) {
571             // a b c z y x
572             reverse_ring(p2_lft);
573             p1_rt->next = p2_rt;
574             p2_rt->prev = p1_rt;
575             p2_lft->next = p1_lft;
576             p1_lft->prev = p2_lft;
577         } else {
578             // a b c x y z
579             p1_rt->next = p2_lft;
580             p2_lft->prev = p1_rt;
581             p1_lft->prev = p2_rt;
582             p2_rt->next = p1_lft;
583         }
584     }
585 
586     keep_ring->bottom_point = nullptr;
587     bool keep_is_hole = ring_is_hole(keep_ring);
588     bool remove_is_hole = ring_is_hole(remove_ring);
589 
590     remove_ring->points = nullptr;
591     remove_ring->bottom_point = nullptr;
592     if (keep_is_hole != remove_is_hole) {
593         ring1_replaces_ring2(keep_ring->parent, remove_ring, manager);
594     } else {
595         ring1_replaces_ring2(keep_ring, remove_ring, manager);
596     }
597 
598     update_points_ring(keep_ring);
599 
600     // nb: safe because we only get here via AddLocalMaxPoly
601     keep_bound->ring = nullptr;
602     remove_bound->ring = nullptr;
603 
604     for (auto& b : active_bounds) {
605         if (b == nullptr) {
606             continue;
607         }
608         if (b->ring == remove_ring) {
609             b->ring = keep_ring;
610             b->side = keep_bound->side;
611             break; // Not sure why there is a break here but was transfered logic from angus
612         }
613     }
614 }
615 
616 template <typename T>
add_local_maximum_point(bound<T> & b1,bound<T> & b2,mapbox::geometry::point<T> const & pt,ring_manager<T> & rings,active_bound_list<T> & active_bounds)617 void add_local_maximum_point(bound<T>& b1,
618                              bound<T>& b2,
619                              mapbox::geometry::point<T> const& pt,
620                              ring_manager<T>& rings,
621                              active_bound_list<T>& active_bounds) {
622     insert_hot_pixels_in_path(b2, pt, rings, false);
623     add_point(b1, active_bounds, pt, rings);
624     if (b1.ring == b2.ring) {
625         b1.ring = nullptr;
626         b2.ring = nullptr;
627         // I am not certain that order is important here?
628     } else if (b1.ring->ring_index < b2.ring->ring_index) {
629         append_ring(b1, b2, active_bounds, rings);
630     } else {
631         append_ring(b2, b1, active_bounds, rings);
632     }
633 }
634 
635 enum point_in_polygon_result : std::int8_t {
636     point_on_polygon = -1,
637     point_inside_polygon = 0,
638     point_outside_polygon = 1
639 };
640 
641 template <typename T>
point_in_polygon(point<T> const & pt,point_ptr<T> op)642 point_in_polygon_result point_in_polygon(point<T> const& pt, point_ptr<T> op) {
643     // returns 0 if false, +1 if true, -1 if pt ON polygon boundary
644     point_in_polygon_result result = point_outside_polygon;
645     point_ptr<T> startOp = op;
646     do {
647         if (op->next->y == pt.y) {
648             if ((op->next->x == pt.x) ||
649                 (op->y == pt.y && ((op->next->x > pt.x) == (op->x < pt.x)))) {
650                 return point_on_polygon;
651             }
652         }
653         if ((op->y < pt.y) != (op->next->y < pt.y)) {
654             if (op->x >= pt.x) {
655                 if (op->next->x > pt.x) {
656                     // Switch between point outside polygon and point inside
657                     // polygon
658                     if (result == point_outside_polygon) {
659                         result = point_inside_polygon;
660                     } else {
661                         result = point_outside_polygon;
662                     }
663                 } else {
664                     double d =
665                         static_cast<double>(op->x - pt.x) *
666                             static_cast<double>(op->next->y - pt.y) -
667                         static_cast<double>(op->next->x - pt.x) * static_cast<double>(op->y - pt.y);
668                     if (value_is_zero(d)) {
669                         return point_on_polygon;
670                     }
671                     if ((d > 0) == (op->next->y > op->y)) {
672                         // Switch between point outside polygon and point inside
673                         // polygon
674                         if (result == point_outside_polygon) {
675                             result = point_inside_polygon;
676                         } else {
677                             result = point_outside_polygon;
678                         }
679                     }
680                 }
681             } else {
682                 if (op->next->x > pt.x) {
683                     double d =
684                         static_cast<double>(op->x - pt.x) *
685                             static_cast<double>(op->next->y - pt.y) -
686                         static_cast<double>(op->next->x - pt.x) * static_cast<double>(op->y - pt.y);
687                     if (value_is_zero(d)) {
688                         return point_on_polygon;
689                     }
690                     if ((d > 0) == (op->next->y > op->y)) {
691                         // Switch between point outside polygon and point inside
692                         // polygon
693                         if (result == point_outside_polygon) {
694                             result = point_inside_polygon;
695                         } else {
696                             result = point_outside_polygon;
697                         }
698                     }
699                 }
700             }
701         }
702         op = op->next;
703     } while (startOp != op);
704     return result;
705 }
706 
707 template <typename T>
point_in_polygon(mapbox::geometry::point<double> const & pt,point_ptr<T> op)708 point_in_polygon_result point_in_polygon(mapbox::geometry::point<double> const& pt,
709                                          point_ptr<T> op) {
710     // returns 0 if false, +1 if true, -1 if pt ON polygon boundary
711     point_in_polygon_result result = point_outside_polygon;
712     point_ptr<T> startOp = op;
713     do {
714         double op_x = static_cast<double>(op->x);
715         double op_y = static_cast<double>(op->y);
716         double op_next_x = static_cast<double>(op->next->x);
717         double op_next_y = static_cast<double>(op->next->y);
718         if (values_are_equal(op_next_y, pt.y)) {
719             if (values_are_equal(op_next_x, pt.x) ||
720                 (values_are_equal(op_y, pt.y) && ((op_next_x > pt.x) == (op_x < pt.x)))) {
721                 return point_on_polygon;
722             }
723         }
724         if ((op_y < pt.y) != (op_next_y < pt.y)) {
725             if (greater_than_or_equal(op_x, pt.x)) {
726                 if (op_next_x > pt.x) {
727                     // Switch between point outside polygon and point inside
728                     // polygon
729                     if (result == point_outside_polygon) {
730                         result = point_inside_polygon;
731                     } else {
732                         result = point_outside_polygon;
733                     }
734                 } else {
735                     double d =
736                         (op_x - pt.x) * (op_next_y - pt.y) - (op_next_x - pt.x) * (op_y - pt.y);
737                     if (value_is_zero(d)) {
738                         return point_on_polygon;
739                     }
740                     if ((d > 0.0) == (op_next_y > op_y)) {
741                         // Switch between point outside polygon and point inside
742                         // polygon
743                         if (result == point_outside_polygon) {
744                             result = point_inside_polygon;
745                         } else {
746                             result = point_outside_polygon;
747                         }
748                     }
749                 }
750             } else {
751                 if (op_next_x > pt.x) {
752                     double d =
753                         (op_x - pt.x) * (op_next_y - pt.y) - (op_next_x - pt.x) * (op_y - pt.y);
754                     if (value_is_zero(d)) {
755                         return point_on_polygon;
756                     }
757                     if ((d > 0.0) == (op_next_y > op_y)) {
758                         // Switch between point outside polygon and point inside
759                         // polygon
760                         if (result == point_outside_polygon) {
761                             result = point_inside_polygon;
762                         } else {
763                             result = point_outside_polygon;
764                         }
765                     }
766                 }
767             }
768         }
769         op = op->next;
770     } while (startOp != op);
771     return result;
772 }
773 
774 template <typename T>
is_convex(point_ptr<T> edge)775 bool is_convex(point_ptr<T> edge) {
776     point_ptr<T> prev = edge->prev;
777     point_ptr<T> next = edge->next;
778     T v1x = edge->x - prev->x;
779     T v1y = edge->y - prev->y;
780     T v2x = next->x - edge->x;
781     T v2y = next->y - edge->y;
782     T cross = v1x * v2y - v2x * v1y;
783     if (cross < 0 && edge->ring->area() > 0) {
784         return true;
785     } else if (cross > 0 && edge->ring->area() < 0) {
786         return true;
787     } else {
788         return false;
789     }
790 }
791 
792 template <typename T>
centroid_of_points(point_ptr<T> edge)793 mapbox::geometry::point<double> centroid_of_points(point_ptr<T> edge) {
794     point_ptr<T> prev = edge->prev;
795     point_ptr<T> next = edge->next;
796     return { static_cast<double>(prev->x + edge->x + next->x) / 3.0, static_cast<double>(prev->y + edge->y + next->y) / 3.0 };
797 }
798 
799 template <typename T>
inside_or_outside_special(point_ptr<T> first_pt,point_ptr<T> other_poly)800 point_in_polygon_result inside_or_outside_special(point_ptr<T> first_pt, point_ptr<T> other_poly) {
801 
802     // We are going to loop through all the points
803     // of the original triangle. The goal is to find a convex edge
804     // that with its next and previous forms a triangle with its centroid
805     // that is within the first ring. Then we will check the other polygon
806     // to see if it is within this polygon.
807     point_ptr<T> itr = first_pt;
808     do {
809         if (is_convex(itr)) {
810             auto pt = centroid_of_points(itr);
811             if (point_inside_polygon == point_in_polygon(pt, first_pt)) {
812                 return point_in_polygon(pt, other_poly);
813             }
814         }
815         itr = itr->next;
816     } while (itr != first_pt);
817 
818     throw std::runtime_error("Could not find a point within the polygon to test");
819 }
820 
821 template <typename T>
box2_contains_box1(mapbox::geometry::box<T> const & box1,mapbox::geometry::box<T> const & box2)822 bool box2_contains_box1(mapbox::geometry::box<T> const& box1,
823                         mapbox::geometry::box<T> const& box2) {
824     return (box2.max.x >= box1.max.x && box2.max.y >= box1.max.y && box2.min.x <= box1.min.x &&
825             box2.min.y <= box1.min.y);
826 }
827 
828 template <typename T>
poly2_contains_poly1(ring_ptr<T> ring1,ring_ptr<T> ring2)829 bool poly2_contains_poly1(ring_ptr<T> ring1, ring_ptr<T> ring2) {
830     if (!box2_contains_box1(ring1->bbox, ring2->bbox)) {
831         return false;
832     }
833     if (std::fabs(ring2->area()) < std::fabs(ring1->area())) {
834         return false;
835     }
836     point_ptr<T> outpt1 = ring1->points->next;
837     point_ptr<T> outpt2 = ring2->points->next;
838     point_ptr<T> op = outpt1;
839     do {
840         // nb: PointInPolygon returns 0 if false, +1 if true, -1 if pt on polygon
841         point_in_polygon_result res = point_in_polygon(*op, outpt2);
842         if (res != point_on_polygon) {
843             return res == point_inside_polygon;
844         }
845         op = op->next;
846     } while (op != outpt1);
847     point_in_polygon_result res = inside_or_outside_special(outpt1, outpt2);
848     return res == point_inside_polygon;
849 }
850 }
851 }
852 }
853