AABBtree  0.0.0
A C++ non-recursive ND AABB tree
Loading...
Searching...
No Matches
Tree.hxx
Go to the documentation of this file.
1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
2 * Copyright (c) 2025, Davide Stocco and Enrico Bertolazzi. *
3 * *
4 * The AABBtree project is distributed under the BSD 2-Clause License. *
5 * *
6 * Davide Stocco Enrico Bertolazzi *
7 * University of Trento University of Trento *
8 * e-mail: davide.stocco@unitn.it e-mail: enrico.bertolazzi@unitn.it *
9\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
10
11#pragma once
12
13#ifndef INCLUDE_AABBTREE_TREE_HXX
14#define INCLUDE_AABBTREE_TREE_HXX
15
16#include "AABBtree/Box.hxx"
17#include "AABBtree/Tree.hxx"
18
19namespace AABBtree {
20
32 template <typename Real, Integer N>
33 class Tree {
34 public:
35 static_assert(std::is_floating_point<Real>::value, "Tree Real type must be a floating-point type.");
36 static_assert(std::is_integral<Integer>::value, "Tree dimension type must be an integer type.");
37 static_assert(N > 0, "Tree dimension must be positive.");
38
39 // Constants for numerical computations
40 constexpr static Real EPS{std::numeric_limits<Real>::epsilon()};
41 constexpr static Real INF{std::numeric_limits<Real>::infinity()};
42 constexpr static Real DUMMY_TOL{EPS*static_cast<Real>(100.0)};
43
47 struct Statistics {
48 // Build statistics
63 Real balance_ratio{0.0};
64 Real depth_ratio{0.0};
65
66 // Query statistics
68
78 void reset() noexcept {*this = Statistics{};}
79
84 void print(OutStream & os) const {
85 // Print the tree info
86 os << "────────────────────────────────────────────────────────────────────────────────\n"
87 << "AABB TREE INFO"
88 << "\n\tAmbient dimension : " << N
89 << "\n\tBasic type : " << typeid(Real).name()
90 << "\n\tObjects : " << objects
91 << "\n\tNodes : " << nodes
92 << "\n\tLeafs : " << leafs
93 << "\n\tLong boxes : " << long_boxes
94 << "\n\tDepth : " << depth
95 << "\n\tLeft nodes : " << left_nodes
96 << "\n\tLeft leafs : " << left_leafs
97 << "\n\tLeft long boxes : " << left_long_boxes
98 << "\n\tLeft depth : " << left_depth
99 << "\n\tRight nodes : " << right_nodes
100 << "\n\tRight leafs : " << right_leafs
101 << "\n\tRight long boxes : " << right_long_boxes
102 << "\n\tRight depth : " << right_depth
103 << "\n\tDump counter : " << dump_counter
104 << "\n\tBalance ratio : " << balance_ratio
105 << "\n\tDepth ratio : " << depth_ratio
106 << "\n\tCheck counter : " << check_counter
107 << "\n────────────────────────────────────────────────────────────────────────────────\n";
108 }
109
110 };
111
112 private:
113 // Basic types definitions
119 using QueueItem = std::tuple<Integer, Integer, Real>;
120 using PriorityQueue = std::priority_queue<QueueItem, std::vector<QueueItem>, std::function<bool(QueueItem, QueueItem)>>;
121
135
136 // Tree hierarchy
137 std::unique_ptr<BoxUniquePtrList> m_boxes_ptr{nullptr};
138 std::vector<Node> m_tree_structure;
140 bool m_dumping_mode{true};
141
142 // Tree parameters
146
147 // Statistics
150
151 // Stack for non-recursive tree building and query
154
155 public:
159 ~Tree() = default;
160
164 Tree() = default;
165
170 BoxUniquePtrList const & boxes() const {return *m_boxes_ptr;}
171
177 BoxUniquePtr const & box(Integer const i) const {return (*m_boxes_ptr)[i];}
178
184 constexpr char CMD[]{"AABBtree::Tree::max_nodal_objects(...): "};
185 AABBTREE_ASSERT(n > 0, CMD << "input must be a positive integer.");
187 }
188
194
199 void separation_ratio_tolerance(Real const ratio) {
200 constexpr char CMD[]{"AABBtree::Tree::separation_ratio_tolerance(...): " };
201 AABBTREE_ASSERT(ratio > 0.0 && ratio < 1.0, CMD << "input must be in the range [0, 1].");
203 }
204
210
215 std::vector<Node> const & structure() const {return m_tree_structure;}
216
222 Node const & node(Integer const i) const {return m_tree_structure[i];}
223
228 Integer size() const {return static_cast<Integer>(m_tree_structure.size());}
229
234
239
244 void dumping_mode(bool const mode) {m_dumping_mode = mode;}
245
250 bool is_empty() const {return m_tree_structure.empty();}
251
255 void clear() {
256 m_tree_structure.clear();
257 m_tree_boxes_map.clear();
258 m_stack.clear();
259 }
260
265 void build(std::unique_ptr<BoxUniquePtrList> boxes_ptr) {
266
267 // Collect the original object boxes
268 m_boxes_ptr = std::move(boxes_ptr);
269
271
272 // Clear tree structure
273 Integer num_boxes{static_cast<Integer>(boxes.size())};
274 m_tree_structure.clear();
275 m_tree_structure.reserve(2*num_boxes + 1);
276
277 // Setup the root node
278 Node root;
279 root.parent = -1;
280 root.child_l = -1;
281 root.child_r = -1;
282 root.box_ptr = 0;
283 root.box_num = 0;
284 root.box_tot_num = num_boxes;
285
286 // Setup the boxes map and compute the root box
287 Integer depth{std::ceil(std::log2(num_boxes))};
288 root.box.set_empty();
289 m_tree_boxes_map.reserve(2*depth);
290 for (BoxUniquePtr const & box : boxes) {
291 root.box.extend(*box);
292 m_tree_boxes_map.emplace_back(root.box_num++);
293 }
294 m_tree_structure.emplace_back(root);
295
296 // Setup the stack
297 m_stack.clear();
298 m_stack.reserve(2*num_boxes + 1);
299 m_stack.emplace_back(0);
300
301 // Main loop that divide the nodes iteratively until all constraints satisfied
302 IndexList m_map(m_tree_boxes_map.size());
303 while (!m_stack.empty()) {
304 // Pop the node from stack
305 Integer const id{m_stack.back()}; m_stack.pop_back();
306
307 // Get the node
309
310 // If the node has less than the maximum number of objects, skip it
311 if (node.box_num < m_max_nodal_objects) {continue;}
312
313 // Compute the separation line and tolerance
314 Vector sizes{node.box.max() - node.box.min()};
315 Integer sorting[N]; std::iota(sorting, sorting + N, 0);
316 auto compare = [&sizes] (Integer i, Integer j) -> bool {return sizes[i] > sizes[j];};
317 std::sort(sorting, sorting + N, compare);
318
319 Integer axis, n_long, n_left, n_right, id_ini, id_end;
320 Real separation_line, separation_tolerance;
321
322 Integer n_long_saved{node.box_num+1};
323 Integer n_diff_saved{node.box_num+1};
324 Integer axis_saved{sorting[0]};
325
326 for (Integer dump{0}; dump < N; ++dump) {
327 axis = sorting[dump];
328 separation_line = node.box.baricenter(axis);
329 separation_tolerance = sizes[axis] * m_separation_ratio_tolerance;
330
331 // Separate short and long boxes and compute short boxes baricenter
332 n_long = n_left = n_right = 0;
333 id_ini = node.box_ptr;
334 id_end = node.box_ptr + node.box_num;
335
336 Real baricenter{0.0};
337 while (id_ini < id_end) {
338 Box const & box_id{*boxes[m_tree_boxes_map[id_ini]]};
339 typename Box::Side const side{box_id.which_side(separation_line, separation_tolerance, axis)};
340 switch (side) {
341 case Box::Side::LEFT: // Left boxes are moved to the end
342 ++n_left; --id_end; std::swap(m_tree_boxes_map[id_ini], m_tree_boxes_map[id_end]);
343 break;
344 case Box::Side::RIGHT: // Right boxes are moved to the end
345 ++n_right; --id_end; std::swap(m_tree_boxes_map[id_ini], m_tree_boxes_map[id_end]);
346 break;
347 default:
348 ++n_long; ++id_ini;
349 }
350 baricenter += box_id.max()[axis] + box_id.min()[axis];
351 }
352 baricenter /= 2.0*node.box_num;
353
354 // Save the best solution found if it has a better n_long and n_diff
355 Integer n_diff{std::abs(n_left - n_right)};
356 if (n_long_saved > n_long || n_diff_saved > n_diff) {
357 n_long_saved = n_long;
358 n_diff_saved = n_diff;
359 axis_saved = axis;
360 std::copy_n(m_tree_boxes_map.data()+node.box_ptr, node.box_num, m_map.data()+node.box_ptr);
361 }
362
363 // If the number of long boxes is too high, skip the next iterations
364 if (n_long > m_max_nodal_objects) {continue;}
365
366 // If the dumping mode is not enabled, end the loop
367 if (!m_dumping_mode) {break;}
368
369 // If the left and right children are yet not balanced, dump the splitting axis
370 if (std::min(n_left, n_right) >= m_balance_ratio_tolerance * std::max(n_left, n_right)) {break;}
372 }
373
374 // Use the best solution found
375 std::copy_n(m_map.data()+node.box_ptr, node.box_num, m_tree_boxes_map.data()+node.box_ptr);
376 n_long = n_long_saved;
377 axis = axis_saved;
378 separation_line = node.box.baricenter(axis);
379 separation_tolerance = sizes[axis] * m_separation_ratio_tolerance;
380
381 // Separate the left and right boxes
382 n_left = n_right = 0;
383 id_ini = node.box_ptr + n_long;
384 id_end = node.box_ptr + node.box_num;
385 while (id_ini < id_end) {
386 Box const & box_id{*boxes[m_tree_boxes_map[id_ini]]};
387 typename Box::Side const side{box_id.which_side(separation_line, separation_tolerance, axis)};
388 switch (side) {
389 case Box::Side::LEFT: // Left boxes are moved to the end
390 ++id_ini; ++n_left; // In right position do nothing
391 break;
392 case Box::Side::RIGHT: // Right boxes are moved to the end
393 --id_end; ++n_right; // In right position swap the current box with the last one
394 std::swap(m_tree_boxes_map[id_ini], m_tree_boxes_map[id_end]);
395 break;
396 default:
397 break;
398 }
399 }
400
401 // If the left and right children have few boxes, they are leafs
402 if (n_left <= m_max_nodal_objects && n_right <= m_max_nodal_objects) {continue;}
403
404 // Set the left and right children indexes
405 node.child_l = static_cast<Integer>(this->size() + 0);
406 node.child_r = static_cast<Integer>(this->size() + 1);
407
408 // Finalize the root node setup (left and right children)
409 Node node_l;
410 node_l.parent = id; // Current node
411 node_l.child_l = -1; // Default (leaf)
412 node_l.child_r = -1; // Default (leaf)
413 node_l.box_num = n_left;
414 node_l.box_tot_num = n_left;
415
416 Node node_r;
417 node_r.parent = id; // Current node
418 node_r.child_l = -1; // Default (leaf)
419 node_r.child_r = -1; // Default (leaf)
420 node_r.box_num = n_right;
421 node_r.box_tot_num = n_right;
422
423 // Compute the bounding box of the long boxes, and left and right children
424 Integer j{node.box_ptr};
425
426 node.box_num = n_long;
427 node.box_long.set_empty();
428 for (Integer i{0}; i < n_long; ++i) {node.box_long.extend(*boxes[m_tree_boxes_map[j++]]);}
429
430 node_l.box.set_empty();
431 node_l.box_ptr = j;
432 for (Integer i{0}; i < n_left; ++i) {node_l.box.extend(*boxes[m_tree_boxes_map[j++]]);}
433 node_l.box_long = node_l.box;
434
435 node_r.box.set_empty();
436 node_r.box_ptr = j;
437 for (Integer i{0}; i < n_right; ++i) {node_r.box.extend(*boxes[m_tree_boxes_map[j++]]);}
438 node_r.box_long = node_r.box;
439
440 // Push nodes on tree structure
441 m_tree_structure.emplace_back(node_l);
442 m_tree_structure.emplace_back(node_r);
443
444 // Push children on stack
445 m_stack.emplace_back(node.child_l);
446 m_stack.emplace_back(node.child_r);
447 }
448 }
449
458 template <typename Object>
459 bool intersect(Object const & obj, IndexSet & candidates) const {
460 // Reset statistics
461 m_check_counter = 0;
462
463 // Return if the tree is empty
464 if (this->is_empty()) {return false;}
465
466 // Collect the original object boxes
468
469 // Setup the stack
470 m_stack.clear();
471 m_stack.reserve(2*this->size() + 1);
472 m_stack.emplace_back(0);
473
474 // Main loop that checks the intersection iteratively
475 candidates.clear();
476 while (!m_stack.empty()) {
477
478 // Pop the node from stack
479 Integer const id{m_stack.back()}; m_stack.pop_back();
480
481 // Get the node
482 Node const & node{m_tree_structure[id]};
483
484 // If the object do not intersects the box, skip the node
485 if (++m_check_counter; !node.box.intersects(obj)) {continue;}
486
487 // Intersect the object with the long boxes on the node.
488 // If it is a leaf long boxes are also the nodes of the leaf.
489 if (node.box_num > 0) {
490 if (++m_check_counter; node.box_long.intersects(obj)) {
491 Integer const id_ini{node.box_ptr};
492 Integer const id_end{node.box_ptr + node.box_num};
493 for (Integer i{id_ini}; i < id_end; ++i) {
494 Integer const pos{m_tree_boxes_map[i]};
495 if (++m_check_counter; boxes[pos]->intersects(obj)) candidates.insert(pos);
496 }
497 }
498 }
499
500 // Push children on the stack if they are not leafs
501 if (node.child_l > 0) {m_stack.emplace_back(node.child_l);}
502 if (node.child_r > 0) {m_stack.emplace_back(node.child_r);}
503 }
504
505 // Return true if the object intersects the tree
506 return !candidates.empty();
507 }
508
515 bool intersect(Tree const & tree, IndexMap & candidates) const {
516 // Reset statistics
517 m_check_counter = 0;
518
519 // Return if the tree is empty
520 if (this->is_empty() || tree.is_empty()) {return false;}
521
522 // Collect the original object boxes
523 BoxUniquePtrList const & boxes_1{*m_boxes_ptr};
524 BoxUniquePtrList const & boxes_2{*tree.m_boxes_ptr};
525
526 // Setup the stack
527 m_stack.clear();
528 m_stack.reserve(this->size() + tree.size() + 2);
529 m_stack.emplace_back(0);
530 m_stack.emplace_back(0);
531
532 // Negate the id of the node to distinguish the tree: if 0 --> -1, 1 --> -2, 2 --> -3
533 auto negate = [] (Integer const id) {return -1-id;};
534
535 // Main loop that checks the intersection iteratively
536 candidates.clear();
537 while (!m_stack.empty()) {
538
539 // Pop the node from stack (reversed order)
540 Integer const id_s2{m_stack.back()}; m_stack.pop_back();
541 Integer const id_2{id_s2 < 0 ? negate(id_s2) : id_s2};
542 Integer const id_s1{m_stack.back()}; m_stack.pop_back();
543 Integer const id_1{id_s1 < 0 ? negate(id_s1) : id_s1};
544
545 // Get the node
546 Node const & node_1{m_tree_structure[id_1]};
547 Node const & node_2{tree.m_tree_structure[id_2]};
548
549 // If the boxes are not intersecting, skip the nodes
550 if (++m_check_counter; !node_1.box.intersects(node_2.box)) {continue;}
551
552 // Intersect the long boxes on the nodes: if both are leaf then intersect the corresponding boxes
553 if (node_1.box_num > 0 && node_2.box_num > 0) {
554 if (++m_check_counter; node_1.box_long.intersects(node_2.box_long)) {
555 Integer const id_1_ini{node_1.box_ptr};
556 Integer const id_1_end{node_1.box_ptr + node_1.box_num};
557 Integer const id_2_ini{node_2.box_ptr};
558 Integer const id_2_end{node_2.box_ptr + node_2.box_num};
559 if (node_1.box_num < node_2.box_num) {
560 for (Integer i{id_1_ini}; i < id_1_end; ++i) {
561 Integer const pos_1{m_tree_boxes_map[i]};
562 if (++m_check_counter; !boxes_1[pos_1]->intersects(node_2.box_long)) {continue;}
563 for (Integer j{id_2_ini}; j < id_2_end; ++j) {
564 Integer const pos_2{tree.m_tree_boxes_map[j]};
565 if (++m_check_counter; boxes_1[pos_1]->intersects(*boxes_2[pos_2])) candidates[pos_1].insert(pos_2);
566 }
567 }
568 } else {
569 for (Integer j{id_2_ini}; j < id_2_end; ++j) {
570 Integer const pos_2{tree.m_tree_boxes_map[j]};
571 if (++m_check_counter; !boxes_2[pos_2]->intersects(node_1.box_long)) {continue;}
572 for (Integer i{id_1_ini}; i < id_1_end; ++i) {
573 Integer const pos_1{m_tree_boxes_map[i]};
574 if (++m_check_counter; boxes_1[pos_1]->intersects(*boxes_2[pos_2])) candidates[pos_1].insert(pos_2);
575 }
576 }
577 }
578 }
579 }
580
581 // Check if the nodes are leafs
582 bool const leaf_1{node_1.child_l < 0};
583 bool const leaf_2{node_2.child_l < 0};
584 if (leaf_1 && leaf_2) {continue;} // We are done on this branch
585
586 // Push children of both trees on the stack if they are not leafs
587 auto stack_emplace_back = [this](Integer const node_1, Integer const node_2) {
588 m_stack.emplace_back(node_1); m_stack.emplace_back(node_2);
589 };
590
591 if (leaf_1) {
592 stack_emplace_back(id_1, node_2.child_l);
593 stack_emplace_back(id_1, node_2.child_r);
594 } else if (leaf_2) {
595 stack_emplace_back(node_1.child_l, id_2);
596 stack_emplace_back(node_1.child_r, id_2);
597 } else {
598 if (node_1.box_tot_num > node_2.box_tot_num) { // split first larger tree
599 if (id_s1 >= 0) {
600 stack_emplace_back(node_1.child_l, id_s2);
601 stack_emplace_back(node_1.child_r, id_s2);
602 if (node_1.box_num > 0) stack_emplace_back(negate(id_1), id_2);
603 } else if (id_s2 >= 0) { // and id_s1 < 0
604 stack_emplace_back(id_s1, node_2.child_l);
605 stack_emplace_back(id_s1, node_2.child_r);
606 }
607 } else {
608 if (id_s2 >= 0) {
609 stack_emplace_back(id_s1, node_2.child_l);
610 stack_emplace_back(id_s1, node_2.child_r);
611 if (node_2.box_num > 0) stack_emplace_back(id_1, negate(id_2));
612 } else if (id_s1 >= 0) { // and id_s2 < 2
613 stack_emplace_back(node_1.child_l, id_s2);
614 stack_emplace_back(node_1.child_r, id_s2);
615 }
616 }
617 }
618 }
619
620 // Return true if the trees intersect
621 return !candidates.empty();
622 }
623
629 bool self_intersect(IndexSet & candidates) const {
630 IndexMap candidates_map;
631 bool intersects{this->intersect(*this, candidates_map)};
632 candidates.clear();
633 for (const auto & [key, values] : candidates_map) {
634 for (int value : values) {candidates.emplace(key); candidates.emplace(value);}
635 }
636 return intersects;
637 }
638
647 template <typename Object>
648 Real distance(Object const & obj, IndexSet & candidates) const {
649 // Reset statistics
650 m_check_counter = 0;
651
652 // Return a negative value if the tree is empty
653 if (this->is_empty()) {return -1.0;}
654
655 // Collect the original object boxes
657
658 // Setup the stack
659 m_stack.clear();
660 m_stack.reserve(2*this->size() + 1);
661 m_stack.emplace_back(0);
662 m_stack.emplace_back(0);
663
664 // Main loop that checks the intersection iteratively
665 Real distance{INF};
666 candidates.clear();
667 while (!m_stack.empty())
668 {
669 // Pop the node from stack
670 Integer const id{m_stack.back()}; m_stack.pop_back();
671
672 // Get the node
673 Node const & node {m_tree_structure[id]};
674
675 // Compute the distance between the object and the bounding box
677 Real tmp_distance{node.box.interior_distance(obj)};
678
679 // If the distance is greater than the temporary minimum distance, skip the node
680 if (tmp_distance > distance) {continue;}
681
682 // Compute the distance between the object and the long boxes on the node
683 if (node.box_num > 0) {
684 if (++m_check_counter; node.box_long.interior_distance(obj) <= distance) {
685 Integer const id_ini{node.box_ptr};
686 Integer const id_end{node.box_ptr + node.box_num};
687 for (Integer i{id_ini}; i < id_end; ++i) {
688 Integer const pos{m_tree_boxes_map[i]};
690 tmp_distance = boxes[pos]->interior_distance(obj);
691 if (tmp_distance < distance) {
692 candidates.clear(); candidates.insert(pos); distance = tmp_distance;
693 } else if (tmp_distance == distance) {
694 candidates.insert(pos);
695 }
696 }
697 }
698 }
699
700 // Push children on the stack if thay are not leafs
701 if (node.child_l > 0) {m_stack.emplace_back(node.child_l);}
702 if (node.child_r > 0) {m_stack.emplace_back(node.child_r);}
703 }
704
705 // Return the distance between the point and the tree
706 return distance;
707 }
708
715 Real distance(Tree const & tree, IndexMap & candidates) const {
716 // Reset statistics
717 m_check_counter = 0;
718
719 // Return if the tree is empty
720 if (this->is_empty() || tree.is_empty()) {return -1.0;}
721
722 // Collect the original object boxes
723 BoxUniquePtrList const & boxes_1{*m_boxes_ptr};
724 BoxUniquePtrList const & boxes_2{*tree.m_boxes_ptr};
725
726 // Setup the stack
727 m_stack.clear();
728 m_stack.reserve(this->size() + tree.size() + 2);
729 m_stack.emplace_back(0);
730 m_stack.emplace_back(0);
731
732 // Negate the id of the node to distinguish the tree: if 0 --> -1, 1 --> -2, 2 --> -3
733 auto negate = [] (Integer const id) {return -1-id;};
734
735 // Main loop that checks the intersection iteratively
736 Real distance{INF};
737 candidates.clear();
738 while (!m_stack.empty())
739 {
740 // Pop the node from stack (reversed order)
741 Integer const id_s2{m_stack.back()}; m_stack.pop_back();
742 Integer const id_2{id_s2 >= 0 ? id_s2 : negate(id_s2)};
743 Integer const id_s1{m_stack.back()}; m_stack.pop_back();
744 Integer const id_1{id_s1 >= 0 ? id_s1 : negate(id_s1)};
745
746 // Get the node
747 Node const & node_1{m_tree_structure[id_1]};
748 Node const & node_2{tree.m_tree_structure[id_2]};
749
750 // Compute the distance between the bounding boxes
752 Real tmp_distance{node_1.box.interior_distance(node_2.box)};
753
754 // If the distance is greater than the temporary minimum distance, skip the nodes
755 if (tmp_distance > distance) {continue;}
756
757 // Compute the distance between the long boxes on the nodes
758 if (node_1.box_num > 0 && node_2.box_num > 0) {
759 if (++m_check_counter; node_1.box_long.interior_distance(node_2.box_long) <= distance) {
760 Integer const id_1_ini{node_1.box_ptr};
761 Integer const id_1_end{node_1.box_ptr + node_1.box_num};
762 Integer const id_2_ini{node_2.box_ptr};
763 Integer const id_2_end{node_2.box_ptr + node_2.box_num};
764 if (node_1.box_num < node_2.box_num) {
765 for (Integer i{id_1_ini}; i < id_1_end; ++i) {
766 Integer const pos_1{m_tree_boxes_map[i]};
767 ++m_check_counter; tmp_distance = boxes_1[pos_1]->interior_distance(node_2.box_long);
768 if (tmp_distance > distance) {continue;}
769 for (Integer j{id_2_ini}; j < id_2_end; ++j) {
770 Integer const pos_2{tree.m_tree_boxes_map[j]};
771 ++m_check_counter; tmp_distance = boxes_1[pos_1]->interior_distance(*boxes_2[pos_2]);
772 if (tmp_distance < distance) {
773 candidates.clear(); candidates[pos_1].insert(pos_2); distance = tmp_distance;
774 } else if (tmp_distance == distance) {
775 candidates[pos_1].insert(pos_2);
776 }
777 }
778 }
779 } else {
780 for (Integer j{id_2_ini}; j < id_2_end; ++j) {
781 Integer const pos_2{tree.m_tree_boxes_map[j]};
782 ++m_check_counter; tmp_distance = boxes_2[pos_2]->interior_distance(node_1.box_long);
783 if (tmp_distance > distance) {continue;}
784 for (Integer i{id_1_ini}; i < id_1_end; ++i) {
785 Integer const pos_1{m_tree_boxes_map[i]};
786 ++m_check_counter; tmp_distance = boxes_1[pos_1]->interior_distance(*boxes_2[pos_2]);
787 if (tmp_distance < distance) {
788 candidates.clear(); candidates[pos_1].insert(pos_2); distance = tmp_distance;
789 } else if (tmp_distance == distance) {
790 candidates[pos_1].insert(pos_2);
791 }
792 }
793 }
794 }
795 }
796 }
797
798 // Check if the nodes are leafs
799 bool const leaf_1{node_1.child_l < 0};
800 bool const leaf_2{node_2.child_l < 0};
801 if (leaf_1 && leaf_2) {continue;} // We are done on this branch
802
803 // Push children of both trees on the stack if thay are not leafs
804 auto stack_emplace_back = [this](Integer const node_1, Integer const node_2) {
805 m_stack.emplace_back(node_1); m_stack.emplace_back(node_2);
806 };
807
808 if (leaf_1) {
809 stack_emplace_back(id_1, node_2.child_l);
810 stack_emplace_back(id_1, node_2.child_r);
811 } else if (leaf_2) {
812 stack_emplace_back(node_1.child_l, id_2);
813 stack_emplace_back(node_1.child_r, id_2);
814 } else {
815 if (node_1.box_tot_num > node_2.box_tot_num) { // split first larger tree
816 if (id_s1 >= 0) {
817 stack_emplace_back(node_1.child_l, id_s2);
818 stack_emplace_back(node_1.child_r, id_s2);
819 if (node_1.box_num > 0) stack_emplace_back(negate(id_1), id_2);
820 } else if (id_s2 >= 0) { // and id_s1 < 0
821 stack_emplace_back(id_s1, node_2.child_l);
822 stack_emplace_back(id_s1, node_2.child_r);
823 }
824 } else {
825 if (id_s2 >= 0) {
826 stack_emplace_back(id_s1, node_2.child_l);
827 stack_emplace_back(id_s1, node_2.child_r);
828 if (node_2.box_num > 0) stack_emplace_back(id_1, negate(id_2));
829 } else if (id_s1 >= 0) { // and id_s2 < 2
830 stack_emplace_back(node_1.child_l, id_s2);
831 stack_emplace_back(node_1.child_r, id_s2);
832 }
833 }
834 }
835 }
836
837 // Return the distance between the trees
838 return distance;
839 }
840
852 template <typename Object, typename Function = std::function<Real(Object const &, Box const &)>>
853 Real closest(Object const & obj, Integer const n, IndexSet & candidates, Function distance_function = []
854 (Object const & o, Box const & b) {return b.interior_distance(o);}) const {
855 // Reset statistics
856 m_check_counter = 0;
857
858 // Return if the tree is empty
859 if (this->is_empty()) {return -1.0;}
860
861 // Collect the original object boxes
863
864 // Setup the stack
865 m_stack.clear();
866 m_stack.reserve(2*this->size() + 1);
867 m_stack.emplace_back(0);
868
869 // Candidate vector distance and index
870 using Pair = std::pair<Real, Integer>;
871 auto cmp = [](const Pair & a, const Pair & b) {return a.first < b.first;};
872 std::priority_queue<Pair, std::vector<Pair>, decltype(cmp)> queue(cmp);
873
874 // Main loop that checks the intersection iteratively
875 Real distance{INF}; // Maximum distance in the queue
876 candidates.clear();
877 while (!m_stack.empty())
878 {
879 // Pop the node from stack
880 Integer const id{m_stack.back()}; m_stack.pop_back();
881
882 // Get the node
883 Node const & node{m_tree_structure[id]};
884
885 // Compute the distance between the object and the bounding box
887 Real tmp_distance{distance_function(obj, node.box)};
888
889 // If the distance is greater than the maximum distance, skip the node
890 if (tmp_distance > distance) {continue;}
891
892 // Compute the distance between the object and the long boxes on the node
893 if (node.box_num > 0) {
894 Integer const id_ini{node.box_ptr};
895 Integer const id_end{node.box_ptr + node.box_num};
896 for (Integer i{id_ini}; i < id_end; ++i) {
897 Integer const pos{m_tree_boxes_map[i]};
899 tmp_distance = distance_function(obj, *boxes[pos]);
900 if (tmp_distance < distance) {
901 if (static_cast<Integer>(queue.size()) < n) {
902 queue.emplace(tmp_distance, pos);
903 } else {
904 queue.pop(); queue.emplace(tmp_distance, pos);
905 }
906 distance = queue.top().first;
907 }
908 }
909 }
910
911 // Push children on the stack if thay are not leafs
912 if (node.child_l > 0) {m_stack.emplace_back(node.child_l);}
913 if (node.child_r > 0) {m_stack.emplace_back(node.child_r);}
914 }
915
916 // Extract indices into candidates
917 Real min_distance{queue.empty() ? distance : queue.top().first};
918 while (!queue.empty()) {candidates.insert(queue.top().second); queue.pop();}
919 return min_distance;
920 }
921
932 template <typename Object, typename Function = std::function<Real(Object const &, Box const &)>>
933 bool within_distance(Object const & obj, Real const max_distance, IndexSet & candidates,
934 Function distance_function = [] (Object const & o, Box const & b) {return b.interior_distance(o);}
935 ) const {
936 // Reset statistics
937 m_check_counter = 0;
938
939 // Return if the tree is empty
940 if (this->is_empty()) {return false;}
941
942 // Collect the original object boxes
944
945 // Setup the stack
946 m_stack.clear();
947 m_stack.reserve(2*this->size() + 1);
948 m_stack.emplace_back(0);
949
950 // Main loop that checks the intersection iteratively
951 candidates.clear();
952 while (!m_stack.empty())
953 {
954 // Pop the node from stack
955 Integer const id{m_stack.back()}; m_stack.pop_back();
956
957 // Get the node
958 Node const & node{m_tree_structure[id]};
959
960 // Compute the distance between the object and the bounding box
962 Real distance{distance_function(obj, node.box)};
963
964 // If the distance is greater than the maximum distance, skip the node
965 if (distance > max_distance) {continue;}
966
967 // Compute the distance between the object and the long boxes on the node
968 if (node.box_num > 0) {
969 Integer const id_ini{node.box_ptr};
970 Integer const id_end{node.box_ptr + node.box_num};
971 for (Integer i{id_ini}; i < id_end; ++i) {
972 Integer const pos{m_tree_boxes_map[i]};
974 distance = distance_function(obj, *boxes[pos]);
975 if (distance <= max_distance) {candidates.insert(pos);}
976 }
977 }
978
979 // Push children on the stack if thay are not leafs
980 if (node.child_l > 0) {m_stack.emplace_back(node.child_l);}
981 if (node.child_r > 0) {m_stack.emplace_back(node.child_r);}
982 }
983
984 // Return true if at least one candidate is within the given distance
985 return !candidates.empty();
986 }
987
993 void depth(Integer const i, Integer & d) const {
994 d = 0;
995 if (i < 0) {return;}
996 m_stack.clear();
997 m_stack.reserve(2*this->size() + 1);
998 m_stack.emplace_back(i);
999 std::vector<Integer> depth_stack;
1000 depth_stack.reserve(2*this->size() + 1);
1001 depth_stack.emplace_back(0);
1002 Integer depth{0};
1003 while (!m_stack.empty())
1004 {
1005 Integer const id{m_stack.back()}; m_stack.pop_back();
1006 Node const & node {m_tree_structure[id]};
1007 depth = static_cast<Integer>(depth_stack.back()); depth_stack.pop_back();
1008 if (node.child_l == -1) {d = std::max(d, depth);}
1009 else {m_stack.emplace_back(node.child_l); depth_stack.emplace_back(depth+1);}
1010 if (node.child_r == -1) {d = std::max(d, depth);}
1011 else {m_stack.emplace_back(node.child_r); depth_stack.emplace_back(depth+1);}
1012 }
1013 }
1014
1022 void nodes(Integer const i, Integer & l, Integer & n, Integer & b) const {
1023 l = n = b = 0;
1024 if (i < 0) {return;}
1025 m_stack.clear();
1026 m_stack.reserve(2*this->size() + 1);
1027 m_stack.emplace_back(i);
1028 while (!m_stack.empty())
1029 {
1030 Integer const id{m_stack.back()}; m_stack.pop_back();
1031 Node const & node {m_tree_structure[id]};
1032 if (node.child_l == -1) {++l;} else {m_stack.emplace_back(node.child_l);}
1033 if (node.child_r == -1) {++l;} else {m_stack.emplace_back(node.child_r);}
1034 ++n; b += node.box_num;
1035 }
1036 }
1037
1042 void stats(Statistics & stats) const {
1043 // Reset statistics
1044 stats.reset();
1045
1046 // Compute/copy the build statistics
1047 stats.objects = static_cast<Integer>(m_boxes_ptr->size());
1048 this->nodes(0, stats.leafs, stats.nodes, stats.long_boxes);
1049 this->depth(0, stats.depth);
1050 this->nodes(m_tree_structure[0].child_l, stats.left_leafs, stats.left_nodes, stats.left_long_boxes);
1051 this->depth(m_tree_structure[0].child_l, stats.left_depth);
1052 this->nodes(m_tree_structure[0].child_r, stats.right_leafs, stats.right_nodes, stats.right_long_boxes);
1053 this->depth(m_tree_structure[0].child_r, stats.right_depth);
1054 stats.dump_counter = m_dump_counter;
1055 stats.balance_ratio = static_cast<Real>(stats.left_leafs)/static_cast<Real>(stats.right_leafs);
1056 stats.depth_ratio = static_cast<Real>(stats.left_depth)/static_cast<Real>(stats.right_depth);
1057
1058 // Copy the check counter
1059 stats.check_counter = m_check_counter;
1060 }
1061
1066 void print(OutStream & os) const {
1067 // Retrieve the statistics
1068 Statistics stats; this->stats(stats);
1069 // Print the tree info
1070 stats.print(os);
1071 }
1072
1073 }; // class Tree
1074
1075} // namespace AABBtree
1076
1077#endif // INCLUDE_AABBTREE_TREE_HXX
#define AABBTREE_ASSERT(COND, MSG)
Definition AABBtree.hh:46
A class representing an axis-aligned bounding box (AABB) in N-dimensional space.
Definition Box.hxx:49
Box & extend(Point const &p)
Definition Box.hxx:459
void set_empty()
Definition Box.hxx:320
Real interior_distance(Point const &p) const
Definition Box.hxx:515
Side
Definition Box.hxx:401
@ RIGHT
Definition Box.hxx:401
@ LEFT
Definition Box.hxx:401
Real closest(Object const &obj, Integer const n, IndexSet &candidates, Function distance_function=[](Object const &o, Box const &b) {return b.interior_distance(o);}) const
Definition Tree.hxx:853
Integer max_nodal_objects() const
Definition Tree.hxx:193
PriorityQueue m_queue
Definition Tree.hxx:153
Integer m_check_counter
Definition Tree.hxx:148
bool intersect(Tree const &tree, IndexMap &candidates) const
Definition Tree.hxx:515
void max_nodal_objects(Integer const n)
Definition Tree.hxx:183
void build(std::unique_ptr< BoxUniquePtrList > boxes_ptr)
Definition Tree.hxx:265
Integer size() const
Definition Tree.hxx:228
AABBtree::BoxUniquePtr< Real, N > BoxUniquePtr
Definition Tree.hxx:115
AABBtree::Point< Real, N > Point
Definition Tree.hxx:118
std::unique_ptr< BoxUniquePtrList > m_boxes_ptr
Definition Tree.hxx:137
std::priority_queue< QueueItem, std::vector< QueueItem >, std::function< bool(QueueItem, QueueItem)> > PriorityQueue
Definition Tree.hxx:120
bool self_intersect(IndexSet &candidates) const
Definition Tree.hxx:629
void clear()
Definition Tree.hxx:255
std::tuple< Integer, Integer, Real > QueueItem
Definition Tree.hxx:119
static constexpr Real DUMMY_TOL
Definition Tree.hxx:42
Integer m_max_nodal_objects
Definition Tree.hxx:143
void dumping_mode(bool const mode)
Definition Tree.hxx:244
BoxUniquePtr const & box(Integer const i) const
Definition Tree.hxx:177
AABBtree::BoxUniquePtrList< Real, N > BoxUniquePtrList
Definition Tree.hxx:116
void nodes(Integer const i, Integer &l, Integer &n, Integer &b) const
Definition Tree.hxx:1022
Real separation_ratio_tolerance() const
Definition Tree.hxx:209
void disable_dumping_mode()
Definition Tree.hxx:238
AABBtree::Box< Real, N > Box
Definition Tree.hxx:114
void depth(Integer const i, Integer &d) const
Definition Tree.hxx:993
bool m_dumping_mode
Definition Tree.hxx:140
Real m_balance_ratio_tolerance
Definition Tree.hxx:145
std::vector< Node > const & structure() const
Definition Tree.hxx:215
~Tree()=default
Real m_separation_ratio_tolerance
Definition Tree.hxx:144
Node const & node(Integer const i) const
Definition Tree.hxx:222
Integer m_dump_counter
Definition Tree.hxx:149
Tree()=default
static constexpr Real INF
Definition Tree.hxx:41
IndexList m_tree_boxes_map
Definition Tree.hxx:139
void enable_dumping_mode()
Definition Tree.hxx:233
Real distance(Object const &obj, IndexSet &candidates) const
Definition Tree.hxx:648
static constexpr Real EPS
Definition Tree.hxx:40
void print(OutStream &os) const
Definition Tree.hxx:1066
bool within_distance(Object const &obj, Real const max_distance, IndexSet &candidates, Function distance_function=[](Object const &o, Box const &b) {return b.interior_distance(o);}) const
Definition Tree.hxx:933
Real distance(Tree const &tree, IndexMap &candidates) const
Definition Tree.hxx:715
IndexList m_stack
Definition Tree.hxx:152
void stats(Statistics &stats) const
Definition Tree.hxx:1042
bool is_empty() const
Definition Tree.hxx:250
void separation_ratio_tolerance(Real const ratio)
Definition Tree.hxx:199
BoxUniquePtrList const & boxes() const
Definition Tree.hxx:170
AABBtree::Vector< Real, N > Vector
Definition Tree.hxx:117
bool intersect(Object const &obj, IndexSet &candidates) const
Definition Tree.hxx:459
std::vector< Node > m_tree_structure
Definition Tree.hxx:138
Namespace for the AABBtree library.
Definition AABBtree.hh:70
std::basic_ostream< char > OutStream
Definition AABBtree.hh:84
Eigen::Vector< Real, N > Point
Definition AABBtree.hh:91
std::map< Integer, IndexSet > IndexMap
Definition AABBtree.hh:82
std::set< Integer > IndexSet
Definition AABBtree.hh:81
std::unique_ptr< Box< Real, N > > BoxUniquePtr
Definition AABBtree.hh:88
std::vector< BoxUniquePtr< Real, N > > BoxUniquePtrList
Definition AABBtree.hh:89
std::vector< Integer > IndexList
Definition AABBtree.hh:83
Eigen::Vector< Real, N > Vector
Definition AABBtree.hh:90
AABBTREE_DEFAULT_INTEGER_TYPE Integer
The Integer type used in the AABBtree class.
Definition AABBtree.hh:78
Definition Tree.hxx:125
Integer parent
Definition Tree.hxx:131
Box box_long
Definition Tree.hxx:127
Box box
Definition Tree.hxx:126
Integer child_l
Definition Tree.hxx:132
Integer box_num
Definition Tree.hxx:129
Integer box_tot_num
Definition Tree.hxx:130
Integer child_r
Definition Tree.hxx:133
Integer box_ptr
Definition Tree.hxx:128
Definition Tree.hxx:47
Integer right_long_boxes
Definition Tree.hxx:60
void reset() noexcept
Definition Tree.hxx:78
Integer check_counter
Definition Tree.hxx:67
Integer right_leafs
Definition Tree.hxx:59
Integer depth
Definition Tree.hxx:53
Real depth_ratio
Definition Tree.hxx:64
Integer right_nodes
Definition Tree.hxx:58
Integer nodes
Definition Tree.hxx:50
Integer long_boxes
Definition Tree.hxx:52
Integer left_leafs
Definition Tree.hxx:55
Integer dump_counter
Definition Tree.hxx:62
Integer left_nodes
Definition Tree.hxx:54
Integer leafs
Definition Tree.hxx:51
Integer left_long_boxes
Definition Tree.hxx:56
void print(OutStream &os) const
Definition Tree.hxx:84
Integer right_depth
Definition Tree.hxx:61
Real balance_ratio
Definition Tree.hxx:63
Integer left_depth
Definition Tree.hxx:57
Integer objects
Definition Tree.hxx:49