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