Distances on Directed Graphs in R
at main 841 lines 23 kB view raw
1// 2// Author: Stanislaw Adaszewski, 2019 3// C++ port from https://github.com/mapbox/concaveman (js) 4// 5// Comments from js repo added by wheeled 6// 7 8#pragma once 9 10#include <memory> 11#include <stdexcept> 12#include <list> 13#include <array> 14#include <vector> 15#include <string> 16#include <iostream> 17#include <iomanip> 18#include <stdlib.h> 19#include <limits> 20#include <set> 21#include <queue> 22#include <assert.h> 23 24#include "Rcpp.h" 25 26//#define DEBUG // uncomment to dump debug info to screen 27//#define DEBUG_2 // uncomment to dump second-level debug info to screen 28 29template<typename T, typename... Args> 30std::unique_ptr<T> make_unq_ptr(Args&&... args) { 31 return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); 32} 33 34 35template<class T> class compare_first { 36public: 37 bool operator()(const T &a, const T &b) { 38 return (std::get<0>(a) < std::get<0>(b)); 39 } 40}; 41 42 43template<class T> T orient2d( 44 const std::array<T, 2> &p1, 45 const std::array<T, 2> &p2, 46 const std::array<T, 2> &p3) { 47 48 T res = (p2[1] - p1[1]) * (p3[0] - p2[0]) - 49 (p2[0] - p1[0]) * (p3[1] - p2[1]); 50 51 return res; 52} 53 54 55// check if the edges (p1,q1) and (p2,q2) intersect 56template<class T> bool intersects( 57 const std::array<T, 2> &p1, 58 const std::array<T, 2> &q1, 59 const std::array<T, 2> &p2, 60 const std::array<T, 2> &q2) { 61 62 auto res = (p1[0] != q2[0] || p1[1] != q2[1]) && 63 (q1[0] != p2[0] || q1[1] != p2[1]) && 64 (orient2d(p1, q1, p2) > 0) != (orient2d(p1, q1, q2) > 0) && 65 (orient2d(p2, q2, p1) > 0) != (orient2d(p2, q2, q1) > 0); 66 67 return res; 68} 69 70 71// square distance between 2 points 72template<class T> T getSqDist( 73 const std::array<T, 2> &p1, 74 const std::array<T, 2> &p2) { 75 76 auto dx = p1[0] - p2[0]; 77 auto dy = p1[1] - p2[1]; 78 return dx * dx + dy * dy; 79} 80 81 82// square distance from a point to a segment 83template<class T> T sqSegDist( 84 const std::array<T, 2> &p, 85 const std::array<T, 2> &p1, 86 const std::array<T, 2> &p2) { 87 88 auto x = p1[0]; 89 auto y = p1[1]; 90 auto dx = p2[0] - x; 91 auto dy = p2[1] - y; 92 93 if (dx != 0 || dy != 0) { 94 auto t = ((p[0] - x) * dx + (p[1] - y) * dy) / (dx * dx + dy * dy); 95 if (t > 1) { 96 x = p2[0]; 97 y = p2[1]; 98 } else if (t > 0) { 99 x += dx * t; 100 y += dy * t; 101 } 102 } 103 104 dx = p[0] - x; 105 dy = p[1] - y; 106 107 return dx * dx + dy * dy; 108} 109 110 111// segment to segment distance, ported from http://geomalgorithms.com/a07-_distance.html by Dan Sunday 112template<class T> T sqSegSegDist(T x0, T y0, 113 T x1, T y1, 114 T x2, T y2, 115 T x3, T y3) { 116 auto ux = x1 - x0; 117 auto uy = y1 - y0; 118 auto vx = x3 - x2; 119 auto vy = y3 - y2; 120 auto wx = x0 - x2; 121 auto wy = y0 - y2; 122 auto a = ux * ux + uy * uy; 123 auto b = ux * vx + uy * vy; 124 auto c = vx * vx + vy * vy; 125 auto d = ux * wx + uy * wy; 126 auto e = vx * wx + vy * wy; 127 auto D = a * c - b * b; 128 129 T sc, sN, tc, tN; 130 auto sD = D; 131 auto tD = D; 132 133 if (D == 0) { 134 sN = 0; 135 sD = 1; 136 tN = e; 137 tD = c; 138 } else { 139 sN = b * e - c * d; 140 tN = a * e - b * d; 141 if (sN < 0) { 142 sN = 0; 143 tN = e; 144 tD = c; 145 } else if (sN > sD) { 146 sN = sD; 147 tN = e + b; 148 tD = c; 149 } 150 } 151 152 if (tN < 0) { 153 tN = 0; 154 if (-d < 0) sN = 0; 155 else if (-d > a) sN = sD; 156 else { 157 sN = -d; 158 sD = a; 159 } 160 } else if (tN > tD) { 161 tN = tD; 162 if (-d + b < 0) sN = 0; 163 else if (-d + b > a) sN = sD; 164 else { 165 sN = -d + b; 166 sD = a; 167 } 168 } 169 170 sc = ((sN == 0) ? 0 : sN / sD); 171 tc = ((tN == 0) ? 0 : tN / tD); 172 173 auto cx = (1 - sc) * x0 + sc * x1; 174 auto cy = (1 - sc) * y0 + sc * y1; 175 auto cx2 = (1 - tc) * x2 + tc * x3; 176 auto cy2 = (1 - tc) * y2 + tc * y3; 177 auto dx = cx2 - cx; 178 auto dy = cy2 - cy; 179 180 return dx * dx + dy * dy; 181} 182 183 184template<class T, int DIM, int MAX_CHILDREN, class DATA> class rtree { 185public: 186 typedef rtree<T, DIM, MAX_CHILDREN, DATA> type; 187 typedef const type const_type; 188 typedef type *type_ptr; 189 typedef const type *type_const_ptr; 190 typedef std::array<T, DIM * 2> bounds_type; 191 typedef DATA data_type; 192 193 rtree(): 194 m_is_leaf(false), m_data() { 195 for (auto i = 0; i < DIM; i++) { 196 m_bounds[i] = std::numeric_limits<T>::max(); 197 m_bounds[i + DIM] = std::numeric_limits<T>::min(); 198 } 199 } 200 201 rtree(data_type data, const bounds_type &bounds): 202 m_is_leaf(true), m_data(data), m_bounds(bounds) { 203 for (auto i = 0; i < DIM; i++) 204 if (bounds[i] > bounds[i + DIM]) 205 throw std::runtime_error("Bounds minima have to be less than maxima"); 206 } 207 208 void insert(data_type data, const bounds_type &bounds) { 209 if (m_is_leaf) 210 throw std::runtime_error("Cannot insert into leaves"); 211 212 m_bounds = updated_bounds(bounds); 213 if (m_children.size() < MAX_CHILDREN) { 214 auto r = make_unq_ptr<type>(data, bounds); 215 m_children.push_back(std::move(r)); 216 return; 217 } 218 219 std::reference_wrapper<type> best_child = *m_children.begin()->get(); 220 auto best_volume = volume(best_child.get().updated_bounds(bounds)); 221 for (auto it = ++m_children.begin(); it != m_children.end(); it++) { 222 auto v = volume((*it)->updated_bounds(bounds)); 223 if (v < best_volume) { 224 best_volume = v; 225 best_child = *it->get(); 226 } 227 } 228 if (!best_child.get().is_leaf()) { 229 best_child.get().insert(data, bounds); 230#ifdef DEBUG 231 std::cout << "best_child: " << bounds[0] << " " << bounds[1] << std::endl; 232#endif 233 return; 234 } 235 236 auto leaf = make_unq_ptr<type>(best_child.get().data(), 237 best_child.get().bounds()); 238 best_child.get().m_is_leaf = false; 239 best_child.get().m_data = data_type(); 240 best_child.get().m_children.push_back(std::move(leaf)); 241 best_child.get().insert(data, bounds); 242 } 243 244 void intersection(const bounds_type &bounds, 245 std::vector<std::reference_wrapper<const_type>> &res) const { 246 if (!intersects(bounds)) 247 return; 248 if (m_is_leaf) { 249 res.push_back(*this); 250 return; 251 } 252 for (auto &ch : m_children) 253 ch->intersection(bounds, res); 254 } 255 256 std::vector<std::reference_wrapper<const_type>> intersection(const bounds_type& bounds) const { 257 std::vector<std::reference_wrapper<const_type>> res; 258 intersection(bounds, res); 259 return res; 260 } 261 262 bool intersects(const bounds_type &bounds) const { 263 for (auto i = 0; i < DIM; i++) { 264 if (m_bounds[i] > bounds[i + DIM]) 265 return false; 266 if (m_bounds[i + DIM] < bounds[i]) 267 return false; 268 } 269 return true; 270 } 271 272 void erase(data_type data, const bounds_type &bounds) { 273 if (m_is_leaf) 274 throw std::runtime_error("Cannot erase from leaves"); 275 276 if (!intersects(bounds)) 277 return; 278 279 for (auto it = m_children.begin(); it != m_children.end(); ) { 280 if (!(*it)->m_is_leaf) { 281 (*it)->erase(data, bounds); 282 it++; 283 } else if ((*it)->m_data == data && 284 (*it)->m_bounds == bounds) { 285 m_children.erase(it++); 286 } else 287 it++; 288 } 289 } 290 291 void print(int level = 0) { 292 // print the entire tree 293 294 for (auto it = m_children.begin(); it != m_children.end(); ) { 295 auto bounds = (*it)->m_bounds; 296 std::string pad(level, '\t'); 297 if ((*it)->m_is_leaf) { 298 printf ("%s leaf %0.6f %0.6f \n", pad.c_str(), bounds[0], bounds[1]); 299 } 300 else { 301 printf ("%s branch %0.6f %0.6f %0.6f %0.6f \n", pad.c_str(), bounds[0], bounds[1], bounds[2], bounds[3]); 302 (*it)->print(level + 1); 303 } 304 it++; 305 } 306 } 307 308 bounds_type updated_bounds(const bounds_type &child_bounds) const { 309 bounds_type res; 310 for (auto i = 0; i < DIM; i++) { 311 res[i] = std::min(child_bounds[i], m_bounds[i]); 312 res[i + DIM] = std::max(child_bounds[i + DIM], m_bounds[i + DIM]); 313 } 314 return res; 315 } 316 317 static T volume(const bounds_type &bounds) { 318 T res = 1; 319 for (auto i = 0; i < DIM; i++) { 320 auto delta = bounds[i + DIM] - bounds[i]; 321 res *= delta; 322 } 323 return res; 324 } 325 326 const bounds_type& bounds() const { 327 return m_bounds; 328 } 329 330 bool is_leaf() const { 331 return m_is_leaf; 332 } 333 334 data_type data() const { 335 return m_data; 336 } 337 338 const std::list<std::unique_ptr<type>>& children() const { 339 return m_children; 340 } 341 342 static std::string bounds_to_string(const bounds_type &bounds) { 343 std::string res = "( "; 344 for (auto i = 0; i < DIM * 2; i++) { 345 if (i > 0) 346 res += ", "; 347 res += std::to_string(bounds[i]); 348 } 349 res += " )"; 350 return res; 351 } 352 353 void to_string(std::string &res, int tab) const { 354 std::string pad(tab, '\t'); 355 356 if (m_is_leaf) { 357 res += pad + "{ data: " + std::to_string(m_data) + 358 ", bounds: " + bounds_to_string(m_bounds) + 359 " }"; 360 return; 361 } 362 363 res += pad + "{ bounds: " + bounds_to_string(m_bounds) + 364 ", children: [\n"; 365 auto i = 0; 366 for (auto &ch : m_children) { 367 if (i++ > 0) 368 res += "\n"; 369 ch->to_string(res, tab + 1); 370 } 371 res += "\n" + pad + "]}"; 372 } 373 374 std::string to_string() const { 375 std::string res; 376 to_string(res, 0); 377 return res; 378 } 379 380private: 381 bool m_is_leaf; 382 data_type m_data; 383 std::list<std::unique_ptr<type>> m_children; 384 bounds_type m_bounds; 385}; 386 387 388template<class T> struct Node { 389 typedef Node<T> type; 390 typedef type *type_ptr; 391 typedef std::array<T, 2> point_type; 392 393 Node(): p(), 394 minX(), minY(), maxX(), maxY() { 395 396 } 397 398 Node(const point_type &p): Node() { 399 this->p = p; 400 } 401 402 point_type p; 403 T minX; 404 T minY; 405 T maxX; 406 T maxY; 407}; 408 409 410template <class T> class CircularList; 411 412 413template<class T> class CircularElement { 414public: 415 typedef CircularElement<T> type; 416 typedef type *ptr_type; 417 418 template<class... Args> 419 CircularElement(Args&&... args) : m_data(std::forward<Args>(args)...) { 420 } 421 422 T& data() { 423 return m_data; 424 } 425 426 template<class... Args> CircularElement<T>* insert(Args&&... args) { 427 auto elem = new CircularElement<T>(std::forward<Args>(args)...); 428 elem->m_prev = this; 429 elem->m_next = m_next; 430 m_next->m_prev = elem; 431 m_next = elem; 432 return elem; 433 } 434 435 CircularElement<T>* prev() { 436 return m_prev; 437 } 438 439 CircularElement<T>* next() { 440 return m_next; 441 } 442 443private: 444 T m_data; 445 CircularElement<T> *m_prev; 446 CircularElement<T> *m_next; 447 448 friend class CircularList<T>; 449}; 450 451 452template<class T> class CircularList { 453public: 454 typedef CircularElement<T> element_type; 455 456 CircularList(): m_last(nullptr) { 457 458 } 459 460 ~CircularList() { 461#ifdef DEBUG 462 std::cout << "~CircularList()" << std::endl; 463#endif 464 auto node = m_last; 465 while (true) { 466#ifdef DEBUG 467// std::cout << (i++) << std::endl; 468#endif 469 auto tmp = node; 470 node = node->m_next; 471 delete tmp; 472 if (node == m_last) 473 break; 474 } 475 } 476 477 template<class... Args> CircularElement<T>* insert(element_type *prev, Args&&... args) { 478 auto elem = new CircularElement<T>(std::forward<Args>(args)...); 479 480 if (prev == nullptr && m_last != nullptr) 481 throw std::runtime_error("Once the list is non-empty you must specify where to insert"); 482 483 if (prev == nullptr) { 484 elem->m_prev = elem->m_next = elem; 485 } else { 486 elem->m_prev = prev; 487 elem->m_next = prev->m_next; 488 prev->m_next->m_prev = elem; 489 prev->m_next = elem; 490 } 491 492 m_last = elem; 493 494 return elem; 495 } 496 497 498private: 499 element_type *m_last; 500}; 501 502 503// update the bounding box of a node's edge 504template<class T> void updateBBox(typename CircularElement<T>::ptr_type elem) { 505 auto &node(elem->data()); 506 auto p1 = node.p; 507 auto p2 = elem->next()->data().p; 508 node.minX = std::min(p1[0], p2[0]); 509 node.minY = std::min(p1[1], p2[1]); 510 node.maxX = std::max(p1[0], p2[0]); 511 node.maxY = std::max(p1[1], p2[1]); 512} 513 514 515#ifdef DEBUG_2 516template<class T> void snapshot( 517 const std::array<T, 2> &a, 518 const std::array<T, 2> &b, 519 const std::array<T, 2> &c, 520 const std::array<T, 2> &d, 521 const double sqLen, 522 const double maxSqLen, 523 const std::array<T, 2> &trigger, 524 const bool use_trigger) { 525 526 if ( !use_trigger || trigger == b ) { 527 if ( !use_trigger ) 528 printf ("Snapshot untriggered\n"); 529 else 530 printf ("Snapshot trigger: %0.6f %0.6f \n", trigger[0], trigger[1]); 531 printf ("... segment a, b: %0.6f %0.6f, %0.6f %0.6f \n", a[0], a[1], b[0], b[1]); 532 printf ("... segment c, d: %0.6f %0.6f, %0.6f %0.6f \n", c[0], c[1], d[0], d[1]); 533 printf ("... sqDist a-b, b-c, c-d: %e, %e, %e", getSqDist(a, b), getSqDist(b, c), getSqDist(c, d)); 534 printf ("... sqLen, maxSqLen: %e, %e", sqLen, maxSqLen); 535 } 536} 537#endif 538 539 540template<class T, int MAX_CHILDREN> std::vector<std::array<T, 2>> concaveman( 541 const std::vector<std::array<T, 2>> &points, 542 // start with a convex hull of the points 543 const std::vector<int> &hull, 544 // a relative measure of concavity; higher value means simpler hull 545 T concavity=2, 546 // when a segment goes below this length threshold, it won't be drilled down further 547 T lengthThreshold=0 548 ) { 549 550 typedef Node<T> node_type; 551 typedef std::array<T, 2> point_type; 552 typedef CircularElement<node_type> circ_elem_type; 553 typedef CircularList<node_type> circ_list_type; 554 typedef circ_elem_type *circ_elem_ptr_type; 555 556#ifdef DEBUG 557 std::cout << "concaveman()" << std::endl; 558#endif 559 560 // exit if hull includes all points already 561 if (hull.size() == points.size()) { 562 std::vector<point_type> res; 563 for (auto &i : hull) res.push_back(points[i]); 564 return res; 565 } 566 567 // index the points with an R-tree 568 rtree<T, 2, MAX_CHILDREN, point_type> tree; 569 570 for (auto &p : points) 571 tree.insert(p, { p[0], p[1], p[0], p[1] }); 572 573 circ_list_type circList; 574 circ_elem_ptr_type last = nullptr; 575 576 std::list<circ_elem_ptr_type> queue; 577 578 // turn the convex hull into a linked list and populate the initial edge queue with the nodes 579 for (auto &idx : hull) { 580 auto &p = points[idx]; 581 tree.erase(p, { p[0], p[1], p[0], p[1] }); 582 last = circList.insert(last, p); 583 queue.push_back(last); 584 } 585 586#ifdef DEBUG_2 587 tree.print(0); 588#endif 589 590 // loops through the hull? why? 591#ifdef DEBUG 592 std::cout << "Starting hull: "; 593#endif 594 for (auto elem = last->next(); ; elem=elem->next()) { 595#ifdef DEBUG 596 std::cout << elem->data().p[0] << " " << elem->data().p[1] << std::endl; 597#endif 598 if (elem == last) 599 break; 600 } 601 602 // index the segments with an R-tree (for intersection checks) 603 rtree<T, 2, MAX_CHILDREN, circ_elem_ptr_type> segTree; 604 for (auto &elem : queue) { 605 auto &node(elem->data()); 606 updateBBox<node_type>(elem); 607 segTree.insert(elem, { node.minX, 608 node.minY, node.maxX, node.maxY }); 609 } 610 611 auto sqConcavity = concavity * concavity; 612 auto sqLenThreshold = lengthThreshold * lengthThreshold; 613 614 // process edges one by one 615 while (!queue.empty()) { 616 auto elem = *queue.begin(); 617 queue.pop_front(); 618 619 auto a = elem->prev()->data().p; 620 auto b = elem->data().p; 621 auto c = elem->next()->data().p; 622 auto d = elem->next()->next()->data().p; 623 624 // skip the edge if it's already short enough 625 auto sqLen = getSqDist(b, c); 626 if (sqLen < sqLenThreshold) 627 continue; 628 629 auto maxSqLen = sqLen / sqConcavity; 630 631#ifdef DEBUG_2 632 // dump key parameters either on every pass or when a certain point is 'b' 633 point_type trigger = { 151.1373474787800, -33.7733192376544 }; 634 snapshot(a, b, c, d, sqLen, maxSqLen, trigger, true); 635#endif 636 637 // find the best connection point for the current edge to flex inward to 638 bool ok; 639 auto p = findCandidate(tree, a, b, c, d, maxSqLen, segTree, ok); 640 641 // if we found a connection and it satisfies our concavity measure 642 if (ok && std::min(getSqDist(p, b), getSqDist(p, c)) <= maxSqLen) { 643 644#ifdef DEBUG 645 printf ("Modifying hull, p: %0.6f %0.6f \n" ,p[0], p[1]); 646#endif 647 648 // connect the edge endpoints through this point and add 2 new edges to the queue 649 queue.push_back(elem); 650 queue.push_back(elem->insert(p)); 651 652 // update point and segment indexes 653 auto &node = elem->data(); 654 auto &next = elem->next()->data(); 655 656 tree.erase(p, { p[0], p[1], p[0], p[1] }); 657 segTree.erase(elem, { node.minX, node.minY, node.maxX, node.maxY }); 658 659 updateBBox<node_type>(elem); 660 updateBBox<node_type>(elem->next()); 661 662 segTree.insert(elem, { node.minX, node.minY, node.maxX, node.maxY }); 663 segTree.insert(elem->next(), { next.minX, next.minY, next.maxX, next.maxY }); 664 } 665#ifdef DEBUG 666 else 667 printf ("No point found along segment: %0.6f %0.6f, %0.6f %0.6f \n", b[0], b[1], c[0], c[1]); 668#endif 669 } 670 671 // convert the resulting hull linked list to an array of points 672 std::vector<point_type> concave; 673 for (auto elem = last->next(); ; elem = elem->next()) { 674 concave.push_back(elem->data().p); 675 if (elem == last) 676 break; 677 } 678 679 return concave; 680} 681 682 683template<class T, int MAX_CHILDREN> std::array<T, 2> findCandidate( 684 const rtree<T, 2, MAX_CHILDREN, std::array<T, 2>> &tree, 685 const std::array<T, 2> &a, 686 const std::array<T, 2> &b, 687 const std::array<T, 2> &c, 688 const std::array<T, 2> &d, 689 T maxDist, 690 const rtree<T, 2, MAX_CHILDREN, typename CircularElement<Node<T>>::ptr_type> &segTree, 691 bool &ok) { 692 693 typedef std::array<T, 2> point_type; 694 typedef CircularElement<Node<T>> circ_elem_type; 695 typedef rtree<T, 2, MAX_CHILDREN, std::array<T, 2>> tree_type; 696 typedef const tree_type const_tree_type; 697 typedef std::reference_wrapper<const_tree_type> tree_ref_type; 698 typedef std::tuple<T, tree_ref_type> tuple_type; 699 700#ifdef DEBUG 701 std::cout << "findCandidate(), maxDist: " << maxDist << std::endl; 702#endif 703 704 ok = false; 705 706 std::priority_queue<tuple_type, std::vector<tuple_type>, compare_first<tuple_type>> queue; 707 std::reference_wrapper<const_tree_type> node = tree; 708 709 // search through the point R-tree with a depth-first search using a priority queue 710 // in the order of distance to the edge (b, c) 711 while (true) { 712 for (auto &child : node.get().children()) { 713 714 auto bounds = child->bounds(); 715 point_type pt = { bounds[0], bounds[1] }; 716 717 auto dist = child->is_leaf() ? sqSegDist(pt, b, c) : sqSegBoxDist(b, c, *child); 718 if (dist > maxDist) 719 continue; // skip the node if it's farther than we ever need 720 721 queue.push(tuple_type(-dist, *child)); 722 } 723 724 while (!queue.empty() && std::get<1>(queue.top()).get().is_leaf()) { 725 auto item = queue.top(); 726 queue.pop(); 727 728 auto bounds = std::get<1>(item).get().bounds(); 729 point_type p = { bounds[0], bounds[1] }; 730 731 // skip all points that are as close to adjacent edges (a,b) and (c,d), 732 // and points that would introduce self-intersections when connected 733 auto d0 = sqSegDist(p, a, b); 734 auto d1 = sqSegDist(p, c, d); 735 736#ifdef DEBUG_2 737 printf (" p: %0.6f %0.6f sqSegDist: %e, %e, %e \n", bounds[0], bounds[1], d0, std::get<0>(item), d1); 738#endif 739 740 if (-std::get<0>(item) < d0 && -std::get<0>(item) < d1 && 741 noIntersections(b, p, segTree) && 742 noIntersections(c, p, segTree)) { 743 744 ok = true; 745 return std::get<1>(item).get().data(); 746 } 747 748#ifdef DEBUG_2 749 else { 750 bool cond1 = -std::get<0>(item) < d0; 751 bool cond2 = -std::get<0>(item) < d1; 752 bool cond3 = noIntersections(b, p, segTree); 753 bool cond4 = noIntersections(c, p, segTree); 754 std::cout << "Not OK: " << cond1 << " " << cond2 << " " << cond3 << " " << cond4 << std::endl; 755 } 756#endif 757 } 758 759 if (queue.empty()) 760 break; 761 762 node = std::get<1>(queue.top()); 763 queue.pop(); 764 } 765 766 return point_type(); 767} 768 769 770// square distance from a segment bounding box to the given one 771template<class T, int MAX_CHILDREN, class USER_DATA> T sqSegBoxDist( 772 const std::array<T, 2> &a, 773 const std::array<T, 2> &b, 774 const rtree<T, 2, MAX_CHILDREN, USER_DATA> &bbox) { 775 776 if (inside(a, bbox) || inside(b, bbox)) 777 return 0; 778 779 auto &bounds = bbox.bounds(); 780 auto minX = bounds[0]; 781 auto minY = bounds[1]; 782 auto maxX = bounds[2]; 783 auto maxY = bounds[3]; 784 785 auto d1 = sqSegSegDist(a[0], a[1], b[0], b[1], minX, minY, maxX, minY); 786 if (d1 == 0) return 0; 787 788 auto d2 = sqSegSegDist(a[0], a[1], b[0], b[1], minX, minY, minX, maxY); 789 if (d2 == 0) return 0; 790 791 auto d3 = sqSegSegDist(a[0], a[1], b[0], b[1], maxX, minY, maxX, maxY); 792 if (d3 == 0) return 0; 793 794 auto d4 = sqSegSegDist(a[0], a[1], b[0], b[1], minX, maxY, maxX, maxY); 795 if (d4 == 0) return 0; 796 797 return std::min(std::min(d1, d2), std::min(d3, d4)); 798} 799 800 801template<class T, int MAX_CHILDREN, class USER_DATA> bool inside( 802 const std::array<T, 2> &a, 803 const rtree<T, 2, MAX_CHILDREN, USER_DATA> &bbox) { 804 805 auto &bounds = bbox.bounds(); 806 807 auto minX = bounds[0]; 808 auto minY = bounds[1]; 809 auto maxX = bounds[2]; 810 auto maxY = bounds[3]; 811 812 auto res = (a[0] >= minX) && 813 (a[0] <= maxX) && 814 (a[1] >= minY) && 815 (a[1] <= maxY); 816 return res; 817} 818 819 820// check if the edge (a,b) doesn't intersect any other edges 821template<class T, int MAX_CHILDREN> bool noIntersections( 822 const std::array<T, 2> &a, 823 const std::array<T, 2> &b, 824 const rtree<T, 2, MAX_CHILDREN, typename CircularElement<Node<T>>::ptr_type> &segTree) { 825 826 auto minX = std::min(a[0], b[0]); 827 auto minY = std::min(a[1], b[1]); 828 auto maxX = std::max(a[0], b[0]); 829 auto maxY = std::max(a[1], b[1]); 830 831 auto isect = segTree.intersection({ minX, minY, maxX, maxY }); 832 833 for (decltype(segTree) &ch : isect) { 834 auto elem = ch.data(); 835 836 if (intersects(elem->data().p, elem->next()->data().p, a, b)) 837 return false; 838 } 839 840 return true; 841}