AABBtree  0.0.1
A C++ non-recursive ND AABB tree
Loading...
Searching...
No Matches
Tree.hxx
Go to the documentation of this file.
1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
2 * Copyright (c) 2026, 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 * davide.stocco@unitn.it 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
35template <typename Real, Integer N> class Tree {
36public:
37 static_assert(std::is_floating_point<Real>::value,
38 "Tree Real type must be a floating-point type.");
39 static_assert(std::is_integral<Integer>::value,
40 "Tree dimension type must be an integer type.");
41 static_assert(N > 0, "Tree dimension must be positive.");
42
46 struct Statistics {
47 // Build statistics
62 Real balance_ratio{0.0};
63 Real depth_ratio{0.0};
64
65 // Query statistics
70
80 void reset() noexcept { *this = Statistics{}; }
81
86 void print(OutStream &os) const {
87 // Print the tree info
88 os << "AABB TREE INFO" << std::endl
89 << "\tAmbient dimension : " << N << std::endl
90 << "\tBasic type : " << typeid(Real).name() << std::endl
91 << "\tObjects : " << objects << std::endl
92 << "\tNodes : " << nodes << std::endl
93 << "\tLeafs : " << leafs << std::endl
94 << "\tLong boxes : " << long_boxes << std::endl
95 << "\tDepth : " << depth << std::endl
96 << "\tLeft nodes : " << left_nodes << std::endl
97 << "\tLeft leafs : " << left_leafs << std::endl
98 << "\tLeft long boxes : " << left_long_boxes << std::endl
99 << "\tLeft depth : " << left_depth << std::endl
100 << "\tRight nodes : " << right_nodes << std::endl
101 << "\tRight leafs : " << right_leafs << std::endl
102 << "\tRight long boxes : " << right_long_boxes << std::endl
103 << "\tRight depth : " << right_depth << std::endl
104 << "\tDump counter : " << dump_counter << std::endl
105 << "\tBalance ratio : " << balance_ratio << std::endl
106 << "\tDepth ratio : " << depth_ratio << std::endl
107 << "\tCheck counter : " << check_counter << std::endl;
108 }
109 };
110
111private:
112 // Basic types definitions
118 using QueueItem = std::tuple<Integer, Integer, Real>;
120 std::priority_queue<QueueItem, std::vector<QueueItem>,
121 std::function<bool(QueueItem, QueueItem)>>;
122
136
137 // Tree hierarchy
138 std::unique_ptr<BoxUniquePtrList> m_boxes_ptr{
139 nullptr};
140 std::vector<Node> m_tree_structure;
143
144 // Tree parameters
148
149 // Statistics
154
155 // Stack for non-recursive tree building and query
158
159public:
163 ~Tree() = default;
164
168 Tree() = default;
169
175 BoxUniquePtrList const &boxes() const { return *m_boxes_ptr; }
176
182 BoxUniquePtr const &box(Integer const i) const { return (*m_boxes_ptr)[i]; }
183
189 constexpr char CMD[]{"AABBtree::Tree::max_dumpings(...): "};
191 CMD << "input must be a positive integer in the range [1, "
192 << N << "].");
194 }
195
201
207 constexpr char CMD[]{"AABBtree::Tree::max_nodal_objects(...): "};
209 CMD << "input must be a positive integer.");
211 }
212
218
223 void separation_ratio_tolerance(Real const ratio) {
224 constexpr char CMD[]{"AABBtree::Tree::separation_ratio_tolerance(...): "};
225 AABBTREE_ASSERT(ratio > 0.0 && ratio < 1.0,
226 CMD << "input must be in the range [0, 1].");
228 }
229
237
242 std::vector<Node> const &structure() const { return m_tree_structure; }
243
249 Node const &node(Integer const i) const { return m_tree_structure[i]; }
250
255 Integer size() const { return static_cast<Integer>(m_tree_structure.size()); }
256
261 bool is_empty() const { return m_tree_structure.empty(); }
262
266 void clear() {
267 m_tree_structure.clear();
268 m_tree_boxes_map.clear();
269 m_stack.clear();
270 }
271
276 void build(std::unique_ptr<BoxUniquePtrList> boxes_ptr) {
277 this->clear();
278 m_boxes_ptr = std::move(boxes_ptr);
279 this->build();
280 }
281
286 void build() {
287
288 // Check if the boxes pointer is valid
289 AABBTREE_ASSERT(m_boxes_ptr != nullptr,
290 "AABBtree::Tree::build(...): The boxes pointer is "
291 "not valid. Please provide a valid pointer to the "
292 "bounding boxes before building the tree.");
293
295
296 // Clear tree structure
297 Integer num_boxes{static_cast<Integer>(boxes.size())};
298 m_tree_structure.clear();
299 m_tree_structure.reserve(2 * num_boxes + 1);
300
301 // Setup the root node
302 Node root;
303 root.parent = -1;
304 root.child_l = -1;
305 root.child_r = -1;
306 root.box_ptr = 0;
307 root.box_num = 0;
308 root.box_tot_num = num_boxes;
309
310 // Setup the boxes map and compute the root box
311 Integer depth{static_cast<Integer>(std::ceil(std::log2(num_boxes)))};
312 root.box.set_empty();
313 m_tree_boxes_map.reserve(2 * depth);
314 for (BoxUniquePtr const &box : boxes) {
315 root.box.extend(*box);
316 m_tree_boxes_map.emplace_back(root.box_num++);
317 }
318 m_tree_structure.emplace_back(root);
319
320 // Setup the stack
321 m_stack.clear();
322 m_stack.reserve(2 * num_boxes + 1);
323 m_stack.emplace_back(0);
324
325 // Main loop that divide the nodes iteratively until all constraints
326 // satisfied
327 IndexList m_map(m_tree_boxes_map.size());
328 while (!m_stack.empty()) {
329 // Pop the node from stack
330 Integer const id{m_stack.back()};
331 m_stack.pop_back();
332
333 // Get the node
335
336 // If the node has less than one object, skip it
337 if (node.box_num <= 1) {
338 continue;
339 }
340
341 // Compute the separation line and tolerance
342 Vector sizes{node.box.max() - node.box.min()};
343 Integer sorting[N];
344 std::iota(sorting, sorting + N, 0);
345 auto compare = [&sizes](Integer i, Integer j) -> bool {
346 return sizes[i] > sizes[j];
347 };
348 std::sort(sorting, sorting + N, compare);
349
350 Integer axis, n_long, n_left, n_right, id_ini, id_end;
351 Real separation_line, separation_tolerance;
352
353 Integer n_long_saved{node.box_num + 1};
354 Integer n_diff_saved{node.box_num + 1};
355 Integer axis_saved{sorting[0]};
356 bool valid_axis_found{false};
357 for (Integer dump{0}; dump < m_max_dumpings; ++dump) {
358 axis = sorting[dump];
359 separation_line = node.box.baricenter(axis);
360 separation_tolerance = sizes[axis] * m_separation_ratio_tolerance;
361
362 // Separate short and long boxes and compute short boxes baricenter
363 n_long = n_left = n_right = 0;
364 id_ini = node.box_ptr;
365 id_end = node.box_ptr + node.box_num;
366
367 Real baricenter{0.0};
368 while (id_ini < id_end) {
369 Box const &box_id{*boxes[m_tree_boxes_map[id_ini]]};
370 typename Box::Side const side{
371 box_id.which_side(separation_line, separation_tolerance, axis)};
372 switch (side) {
373 case Box::Side::LEFT: // Left boxes are moved to the end
374 ++n_left;
375 --id_end;
376 std::swap(m_tree_boxes_map[id_ini], m_tree_boxes_map[id_end]);
377 break;
378 case Box::Side::RIGHT: // Right boxes are moved to the end
379 ++n_right;
380 --id_end;
381 std::swap(m_tree_boxes_map[id_ini], m_tree_boxes_map[id_end]);
382 break;
383 default:
384 ++n_long;
385 ++id_ini;
386 }
387 baricenter += box_id.max()[axis] + box_id.min()[axis];
388 }
389 baricenter /= 2.0 * node.box_num;
390
391 // Save the first and the best solution found so far
392 Integer n_diff{std::abs(n_left - n_right)};
393 if ((n_long <= n_long_saved && n_long <= m_max_nodal_objects &&
394 n_diff < n_diff_saved) ||
395 !valid_axis_found) {
396 n_long_saved = n_long;
397 n_diff_saved = n_diff;
398 axis_saved = axis;
399 std::copy_n(m_tree_boxes_map.data() + node.box_ptr, node.box_num,
400 m_map.data() + node.box_ptr);
401 valid_axis_found = true;
402 }
403
405 }
406
407 // Check if a valid solution has been found
409 valid_axis_found,
410 "AABBtree::Tree::build(...): Failed to find a valid "
411 "splitting axis.");
412
413 // Terminate splitting if the number of long boxes if cannot be split
414 if (n_long_saved == node.box_num) {
415 continue;
416 }
417
418 // Use the best solution found
419 std::copy_n(m_map.data() + node.box_ptr, node.box_num,
420 m_tree_boxes_map.data() + node.box_ptr);
421 n_long = n_long_saved;
422 axis = axis_saved;
423 separation_line = node.box.baricenter(axis);
424 separation_tolerance = sizes[axis] * m_separation_ratio_tolerance;
425
426 // Separate the left and right boxes
427 n_left = n_right = 0;
428 id_ini = node.box_ptr + n_long;
429 id_end = node.box_ptr + node.box_num;
430 while (id_ini < id_end) {
431 Box const &box_id{*boxes[m_tree_boxes_map[id_ini]]};
432 typename Box::Side const side{
433 box_id.which_side(separation_line, separation_tolerance, axis)};
434 switch (side) {
435 case Box::Side::LEFT: // Left boxes are moved to the end
436 ++id_ini;
437 ++n_left; // In right position do nothing
438 break;
439 case Box::Side::RIGHT: // Right boxes are moved to the end
440 --id_end;
441 ++n_right; // In right position swap the current box with the last
442 // one
443 std::swap(m_tree_boxes_map[id_ini], m_tree_boxes_map[id_end]);
444 break;
445 default:
446 break;
447 }
448 }
449
450 // Set the left and right children indexes
451 node.child_l = static_cast<Integer>(this->size() + 0);
452 node.child_r = static_cast<Integer>(this->size() + 1);
453
454 // Finalize the root node setup (left and right children)
455 Node node_l;
456 node_l.parent = id; // Current node
457 node_l.child_l = -1; // Default (leaf)
458 node_l.child_r = -1; // Default (leaf)
459 node_l.box_num = n_left;
460 node_l.box_tot_num = n_left;
461
462 Node node_r;
463 node_r.parent = id; // Current node
464 node_r.child_l = -1; // Default (leaf)
465 node_r.child_r = -1; // Default (leaf)
466 node_r.box_num = n_right;
467 node_r.box_tot_num = n_right;
468
469 // Compute the bounding box of the long boxes, and left and right
470 // children
471 Integer j{node.box_ptr};
472
473 node.box_num = n_long;
474 node.box_long.set_empty();
475 for (Integer i{0}; i < n_long; ++i) {
476 node.box_long.extend(*boxes[m_tree_boxes_map[j++]]);
477 }
478
479 node_l.box.set_empty();
480 node_l.box_ptr = j;
481 for (Integer i{0}; i < n_left; ++i) {
482 node_l.box.extend(*boxes[m_tree_boxes_map[j++]]);
483 }
484 node_l.box_long = node_l.box;
485
486 node_r.box.set_empty();
487 node_r.box_ptr = j;
488 for (Integer i{0}; i < n_right; ++i) {
489 node_r.box.extend(*boxes[m_tree_boxes_map[j++]]);
490 }
491 node_r.box_long = node_r.box;
492
493 // Push nodes on tree structure
494 m_tree_structure.emplace_back(node_l);
495 m_tree_structure.emplace_back(node_r);
496
497 // Push children on stack
498 m_stack.emplace_back(node.child_l);
499 m_stack.emplace_back(node.child_r);
500 }
501 }
502
512 template <typename Object>
513 bool intersect(Object const &obj, IndexSet &candidates) const {
514 // Reset statistics
518
519 // Return if the tree is empty
520 if (this->is_empty()) {
521 return false;
522 }
523
524 // Collect the original object boxes
526
527 // Setup the stack
528 m_stack.clear();
529 m_stack.reserve(2 * this->size() + 1);
530 m_stack.emplace_back(0);
531
532 // Main loop that checks the intersection iteratively
533 candidates.clear();
534 while (!m_stack.empty()) {
535
536 // Pop the node from stack
537 Integer const id{m_stack.back()};
538 m_stack.pop_back();
539
540 // Get the node
541 Node const &node{m_tree_structure[id]};
542
543 // If the object do not intersects the box, skip the node
544 if (++m_node_check_counter; !node.box.intersects(obj)) {
545 continue;
546 }
547
548 // Intersect the object with the long boxes on the node.
549 // If it is a leaf long boxes are also the nodes of the leaf.
550 if (node.box_num > 0) {
551 if (++m_long_check_counter; node.box_long.intersects(obj)) {
552 Integer const id_ini{node.box_ptr};
553 Integer const id_end{node.box_ptr + node.box_num};
554 for (Integer i{id_ini}; i < id_end; ++i) {
555 Integer const pos{m_tree_boxes_map[i]};
556 if (++m_objs_check_counter; boxes[pos]->intersects(obj))
557 candidates.insert(pos);
558 }
559 }
560 }
561
562 // Push children on the stack if they are not leafs
563 if (node.child_l > 0) {
564 m_stack.emplace_back(node.child_l);
565 }
566 if (node.child_r > 0) {
567 m_stack.emplace_back(node.child_r);
568 }
569 }
570
571 // Return true if the object intersects the tree
572 return !candidates.empty();
573 }
574
581 bool intersect(Tree const &tree, IndexMap &candidates) const {
582 // Reset statistics
586
587 // Return if the tree is empty
588 if (this->is_empty() || tree.is_empty()) {
589 return false;
590 }
591
592 // Collect the original object boxes
593 BoxUniquePtrList const &boxes_1{*m_boxes_ptr};
594 BoxUniquePtrList const &boxes_2{*tree.m_boxes_ptr};
595
596 // using Tuple = std::tuple<Integer, Integer, Integer>; // (box_num,
597 // id_s1, id_s2) auto cmp = [](const Tuple & a, const Tuple & b) {return
598 // std::get<0>(a) > std::get<0>(b);}; std::priority_queue<Tuple,
599 // std::vector<Tuple>, decltype(cmp)> queue(cmp);
600 // Setup the stack
601 m_stack.clear();
602 m_stack.reserve(this->size() + tree.size() + 2);
603
604 // Negate the id of the node to distinguish the tree: if 0 --> -1, 1 -->
605 // -2, 2 --> -3
606 auto negate = [](Integer const id) { return -1 - id; };
607
608 // Compute the priority of the nodes based on the number of boxes
609 auto stack_emplace_back = [this](Integer const node_1,
610 Integer const node_2) {
611 // Integer const id_1{node_1 < 0 ? negate(node_1) : node_1};
612 // Integer const id_2{node_2 < 0 ? negate(node_2) : node_2};
613 // queue.emplace(
614 // std::abs(m_tree_structure[id_1].box_tot_num -
615 // tree.m_tree_structure[id_2].box_tot_num)*
616 // std::min(m_tree_structure[id_1].box_num,
617 // tree.m_tree_structure[id_2].box_num), node_1, node_2);
618 m_stack.emplace_back(node_1);
619 m_stack.emplace_back(node_2);
620 };
621 stack_emplace_back(0, 0);
622
623 // Main loop that checks the intersection iteratively
624 candidates.clear();
625 while (!m_stack.empty()) {
626 // while (!queue.empty()) {
627
628 // Pop the node from stack (reversed order)
629 Integer const id_s2{m_stack.back()};
630 m_stack.pop_back();
631 Integer const id_2{id_s2 < 0 ? negate(id_s2) : id_s2};
632 Integer const id_s1{m_stack.back()};
633 m_stack.pop_back();
634 Integer const id_1{id_s1 < 0 ? negate(id_s1) : id_s1};
635 // Tuple top = queue.top(); queue.pop();
636 // Integer const id_s1{std::get<1>(top)};
637 // Integer const id_1{id_s1 < 0 ? negate(id_s1) : id_s1};
638 // Integer const id_s2{std::get<2>(top)};
639 // Integer const id_2{id_s2 < 0 ? negate(id_s2) : id_s2};
640
641 // Get the node
642 Node const &node_1{m_tree_structure[id_1]};
643 Node const &node_2{tree.m_tree_structure[id_2]};
644
645 // If the boxes are not intersecting, skip the nodes
646 if (++m_node_check_counter; !node_1.box.intersects(node_2.box)) {
647 continue;
648 }
649
650 // Intersect the long boxes on the nodes: if both are leaf then
651 // intersect the corresponding boxes
652 if (node_1.box_num > 0 && node_2.box_num > 0) {
654 node_1.box_long.intersects(node_2.box_long)) {
655 Integer const id_1_ini{node_1.box_ptr};
656 Integer const id_1_end{node_1.box_ptr + node_1.box_num};
657 Integer const id_2_ini{node_2.box_ptr};
658 Integer const id_2_end{node_2.box_ptr + node_2.box_num};
659 if (node_1.box_num < node_2.box_num) {
660 for (Integer i{id_1_ini}; i < id_1_end; ++i) {
661 Integer const pos_1{m_tree_boxes_map[i]};
663 !boxes_1[pos_1]->intersects(node_2.box_long)) {
664 continue;
665 }
666 for (Integer j{id_2_ini}; j < id_2_end; ++j) {
667 Integer const pos_2{tree.m_tree_boxes_map[j]};
669 boxes_1[pos_1]->intersects(*boxes_2[pos_2]))
670 candidates[pos_1].insert(pos_2);
671 }
672 }
673 } else {
674 for (Integer j{id_2_ini}; j < id_2_end; ++j) {
675 Integer const pos_2{tree.m_tree_boxes_map[j]};
677 !boxes_2[pos_2]->intersects(node_1.box_long)) {
678 continue;
679 }
680 for (Integer i{id_1_ini}; i < id_1_end; ++i) {
681 Integer const pos_1{m_tree_boxes_map[i]};
683 boxes_1[pos_1]->intersects(*boxes_2[pos_2]))
684 candidates[pos_1].insert(pos_2);
685 }
686 }
687 }
688 }
689 }
690
691 // Check if the nodes are leafs
692 bool const leaf_1{node_1.child_l < 0};
693 bool const leaf_2{node_2.child_l < 0};
694 if (leaf_1 && leaf_2) {
695 continue;
696 } // We are done on this branch
697
698 if (leaf_1) {
699 stack_emplace_back(id_1, node_2.child_l);
700 stack_emplace_back(id_1, node_2.child_r);
701 } else if (leaf_2) {
702 stack_emplace_back(node_1.child_l, id_2);
703 stack_emplace_back(node_1.child_r, id_2);
704 } else {
705 if (node_1.box_tot_num >
706 node_2.box_tot_num) { // split first larger tree
707 if (id_s1 >= 0) {
708 stack_emplace_back(node_1.child_l, id_s2);
709 stack_emplace_back(node_1.child_r, id_s2);
710 if (node_1.box_num > 0)
711 stack_emplace_back(negate(id_1), id_2);
712 } else if (id_s2 >= 0) { // and id_s1 < 0
713 stack_emplace_back(id_s1, node_2.child_l);
714 stack_emplace_back(id_s1, node_2.child_r);
715 }
716 } else {
717 if (id_s2 >= 0) {
718 stack_emplace_back(id_s1, node_2.child_l);
719 stack_emplace_back(id_s1, node_2.child_r);
720 if (node_2.box_num > 0)
721 stack_emplace_back(id_1, negate(id_2));
722 } else if (id_s1 >= 0) { // and id_s2 < 2
723 stack_emplace_back(node_1.child_l, id_s2);
724 stack_emplace_back(node_1.child_r, id_s2);
725 }
726 }
727 }
728 }
729
730 // Return true if the trees intersect
731 return !candidates.empty();
732 }
733
739 bool self_intersect(IndexMap &candidates) const {
740 // Reset statistics
744
745 // Return if the tree is empty
746 if (this->is_empty()) {
747 return false;
748 }
749
750 // Collect the original object boxes
752
753 // using Tuple = std::tuple<Integer, Integer, Integer>; // (box_num,
754 // id_s1, id_s2)
755 m_stack.clear();
756 m_stack.reserve(this->size() + this->size() + 2);
757
758 // Negate the id of the node to distinguish the tree: if 0 --> -1, 1 -->
759 // -2, 2 --> -3
760 auto negate = [](Integer const id) { return -1 - id; };
761
762 // Compute the priority of the nodes based on the number of boxes
763 auto stack_emplace_back = [this](Integer const node_1,
764 Integer const node_2) {
765 m_stack.emplace_back(node_1);
766 m_stack.emplace_back(node_2);
767 };
768 stack_emplace_back(0, 0);
769
770 // Main loop that checks the intersection iteratively
771 candidates.clear();
772 while (!m_stack.empty()) {
773
774 // Pop the node from stack (reversed order)
775 Integer const id_s2{m_stack.back()};
776 m_stack.pop_back();
777 Integer const id_2{id_s2 < 0 ? negate(id_s2) : id_s2};
778 Integer const id_s1{m_stack.back()};
779 m_stack.pop_back();
780 Integer const id_1{id_s1 < 0 ? negate(id_s1) : id_s1};
781
782 // Get the node
783 Node const &node_1{m_tree_structure[id_1]};
784 Node const &node_2{m_tree_structure[id_2]};
785
786 // If the boxes are not intersecting, skip the nodes
787 if (++m_node_check_counter; !node_1.box.intersects(node_2.box)) {
788 continue;
789 }
790
791 // Intersect the long boxes on the nodes: if both are leaf then
792 // intersect the corresponding boxes
793 if (node_1.box_num > 0 && node_2.box_num > 0) {
795 node_1.box_long.intersects(node_2.box_long)) {
796 Integer const id_1_ini{node_1.box_ptr};
797 Integer const id_1_end{node_1.box_ptr + node_1.box_num};
798 Integer const id_2_ini{node_2.box_ptr};
799 Integer const id_2_end{node_2.box_ptr + node_2.box_num};
800 if (node_1.box_num < node_2.box_num) {
801 for (Integer i{id_1_ini}; i < id_1_end; ++i) {
802 Integer const pos_1{m_tree_boxes_map[i]};
804 !boxes[pos_1]->intersects(node_2.box_long)) {
805 continue;
806 }
807 for (Integer j{id_2_ini}; j < id_2_end; ++j) {
808 Integer const pos_2{m_tree_boxes_map[j]};
809 if (pos_1 == pos_2) {
810 continue;
811 } // Do not intersect with itself
813 boxes[pos_1]->intersects(*boxes[pos_2]))
814 candidates[pos_1].insert(pos_2);
815 }
816 }
817 } else {
818 for (Integer j{id_2_ini}; j < id_2_end; ++j) {
819 Integer const pos_2{m_tree_boxes_map[j]};
821 !boxes[pos_2]->intersects(node_1.box_long)) {
822 continue;
823 }
824 for (Integer i{id_1_ini}; i < id_1_end; ++i) {
825 Integer const pos_1{m_tree_boxes_map[i]};
826 if (pos_1 == pos_2) {
827 continue;
828 } // Do not intersect with itself
830 boxes[pos_1]->intersects(*boxes[pos_2]))
831 candidates[pos_1].insert(pos_2);
832 }
833 }
834 }
835 }
836 }
837
838 // Check if the nodes are leafs
839 bool const leaf_1{node_1.child_l < 0};
840 bool const leaf_2{node_2.child_l < 0};
841 if (leaf_1 && leaf_2) {
842 continue;
843 } // We are done on this branch
844
845 if (leaf_1) {
846 stack_emplace_back(id_1, node_2.child_l);
847 stack_emplace_back(id_1, node_2.child_r);
848 } else if (leaf_2) {
849 stack_emplace_back(node_1.child_l, id_2);
850 stack_emplace_back(node_1.child_r, id_2);
851 } else {
852 if (node_1.box_tot_num >
853 node_2.box_tot_num) { // split first larger tree
854 if (id_s1 >= 0) {
855 stack_emplace_back(node_1.child_l, id_s2);
856 stack_emplace_back(node_1.child_r, id_s2);
857 if (node_1.box_num > 0)
858 stack_emplace_back(negate(id_1), id_2);
859 } else if (id_s2 >= 0) { // and id_s1 < 0
860 stack_emplace_back(id_s1, node_2.child_l);
861 stack_emplace_back(id_s1, node_2.child_r);
862 }
863 } else {
864 if (id_s2 >= 0) {
865 stack_emplace_back(id_s1, node_2.child_l);
866 stack_emplace_back(id_s1, node_2.child_r);
867 if (node_2.box_num > 0)
868 stack_emplace_back(id_1, negate(id_2));
869 } else if (id_s1 >= 0) { // and id_s2 < 2
870 stack_emplace_back(node_1.child_l, id_s2);
871 stack_emplace_back(node_1.child_r, id_s2);
872 }
873 }
874 }
875 }
876
877 // Return true if the trees intersect
878 return !candidates.empty();
879 }
880
887 bool self_intersect(IndexSet &candidates) const {
888 IndexMap candidates_map;
889 bool intersects{this->intersect(*this, candidates_map)};
890 candidates.clear();
891 for (const auto &[key, values] : candidates_map) {
892 for (int value : values) {
893 candidates.emplace(key);
894 candidates.emplace(value);
895 }
896 }
897 return intersects;
898 }
899
909 template <typename Object>
910 Real distance(Object const &obj, IndexSet &candidates) const {
911 // Reset statistics
915
916 // Return a negative value if the tree is empty
917 if (this->is_empty()) {
918 return -1.0;
919 }
920
921 // Collect the original object boxes
923
924 // Setup the stack
925 m_stack.clear();
926 m_stack.reserve(2 * this->size() + 1);
927 m_stack.emplace_back(0);
928 m_stack.emplace_back(0);
929
930 // Main loop that checks the intersection iteratively
931 Real distance{std::numeric_limits<Real>::infinity()};
932 candidates.clear();
933 while (!m_stack.empty()) {
934 // Pop the node from stack
935 Integer const id{m_stack.back()};
936 m_stack.pop_back();
937
938 // Get the node
939 Node const &node{m_tree_structure[id]};
940
941 // Compute the distance between the object and the bounding box
943 Real tmp_distance{node.box.interior_distance(obj)};
944
945 // If the distance is greater than the temporary minimum distance, skip
946 // the node
947 if (tmp_distance > distance) {
948 continue;
949 }
950
951 // Compute the distance between the object and the long boxes on the
952 // node
953 if (node.box_num > 0) {
955 node.box_long.interior_distance(obj) <= distance) {
956 Integer const id_ini{node.box_ptr};
957 Integer const id_end{node.box_ptr + node.box_num};
958 for (Integer i{id_ini}; i < id_end; ++i) {
959 Integer const pos{m_tree_boxes_map[i]};
961 tmp_distance = boxes[pos]->interior_distance(obj);
962 if (tmp_distance < distance) {
963 candidates.clear();
964 candidates.insert(pos);
965 distance = tmp_distance;
966 } else if (tmp_distance == distance) {
967 candidates.insert(pos);
968 }
969 }
970 }
971 }
972
973 // Push children on the stack if thay are not leafs
974 if (node.child_l > 0) {
975 m_stack.emplace_back(node.child_l);
976 }
977 if (node.child_r > 0) {
978 m_stack.emplace_back(node.child_r);
979 }
980 }
981
982 // Return the distance between the point and the tree
983 return distance;
984 }
985
992 Real distance(Tree const &tree, IndexMap &candidates) const {
993 // Reset statistics
997
998 // Return if the tree is empty
999 if (this->is_empty() || tree.is_empty()) {
1000 return -1.0;
1001 }
1002
1003 // Collect the original object boxes
1004 BoxUniquePtrList const &boxes_1{*m_boxes_ptr};
1005 BoxUniquePtrList const &boxes_2{*tree.m_boxes_ptr};
1006
1007 // Setup the stack
1008 m_stack.clear();
1009 m_stack.reserve(this->size() + tree.size() + 2);
1010
1011 // Negate the id of the node to distinguish the tree: if 0 --> -1, 1 -->
1012 // -2, 2 --> -3
1013 auto negate = [](Integer const id) { return -1 - id; };
1014
1015 // Setup the stack emplace function
1016 auto stack_emplace_back = [this](Integer const node_1,
1017 Integer const node_2) {
1018 m_stack.emplace_back(node_1);
1019 m_stack.emplace_back(node_2);
1020 };
1021 stack_emplace_back(0, 0);
1022
1023 // Main loop that checks the intersection iteratively
1024 Real distance{std::numeric_limits<Real>::infinity()};
1025 candidates.clear();
1026 while (!m_stack.empty()) {
1027 // Pop the node from stack (reversed order)
1028 Integer const id_s2{m_stack.back()};
1029 m_stack.pop_back();
1030 Integer const id_2{id_s2 >= 0 ? id_s2 : negate(id_s2)};
1031 Integer const id_s1{m_stack.back()};
1032 m_stack.pop_back();
1033 Integer const id_1{id_s1 >= 0 ? id_s1 : negate(id_s1)};
1034
1035 // Get the node
1036 Node const &node_1{m_tree_structure[id_1]};
1037 Node const &node_2{tree.m_tree_structure[id_2]};
1038
1039 // Compute the distance between the bounding boxes
1041 Real tmp_distance{node_1.box.interior_distance(node_2.box)};
1042
1043 // If the distance is greater than the temporary minimum distance, skip
1044 // the nodes
1045 if (tmp_distance > distance) {
1046 continue;
1047 }
1048
1049 // Compute the distance between the long boxes on the nodes
1050 if (node_1.box_num > 0 && node_2.box_num > 0) {
1052 node_1.box_long.interior_distance(node_2.box_long) <= distance) {
1053 Integer const id_1_ini{node_1.box_ptr};
1054 Integer const id_1_end{node_1.box_ptr + node_1.box_num};
1055 Integer const id_2_ini{node_2.box_ptr};
1056 Integer const id_2_end{node_2.box_ptr + node_2.box_num};
1057 if (node_1.box_num < node_2.box_num) {
1058 for (Integer i{id_1_ini}; i < id_1_end; ++i) {
1059 Integer const pos_1{m_tree_boxes_map[i]};
1061 tmp_distance = boxes_1[pos_1]->interior_distance(node_2.box_long);
1062 if (tmp_distance > distance) {
1063 continue;
1064 }
1065 for (Integer j{id_2_ini}; j < id_2_end; ++j) {
1066 Integer const pos_2{tree.m_tree_boxes_map[j]};
1068 tmp_distance =
1069 boxes_1[pos_1]->interior_distance(*boxes_2[pos_2]);
1070 if (tmp_distance < distance) {
1071 candidates.clear();
1072 candidates[pos_1].insert(pos_2);
1073 distance = tmp_distance;
1074 } else if (tmp_distance == distance) {
1075 candidates[pos_1].insert(pos_2);
1076 }
1077 }
1078 }
1079 } else {
1080 for (Integer j{id_2_ini}; j < id_2_end; ++j) {
1081 Integer const pos_2{tree.m_tree_boxes_map[j]};
1083 tmp_distance = boxes_2[pos_2]->interior_distance(node_1.box_long);
1084 if (tmp_distance > distance) {
1085 continue;
1086 }
1087 for (Integer i{id_1_ini}; i < id_1_end; ++i) {
1088 Integer const pos_1{m_tree_boxes_map[i]};
1090 tmp_distance =
1091 boxes_1[pos_1]->interior_distance(*boxes_2[pos_2]);
1092 if (tmp_distance < distance) {
1093 candidates.clear();
1094 candidates[pos_1].insert(pos_2);
1095 distance = tmp_distance;
1096 } else if (tmp_distance == distance) {
1097 candidates[pos_1].insert(pos_2);
1098 }
1099 }
1100 }
1101 }
1102 }
1103 }
1104
1105 // Check if the nodes are leafs
1106 bool const leaf_1{node_1.child_l < 0};
1107 bool const leaf_2{node_2.child_l < 0};
1108 if (leaf_1 && leaf_2) {
1109 continue;
1110 } // We are done on this branch
1111
1112 // Push children of both trees on the stack if thay are not leafs
1113 if (leaf_1) {
1114 stack_emplace_back(id_1, node_2.child_l);
1115 stack_emplace_back(id_1, node_2.child_r);
1116 } else if (leaf_2) {
1117 stack_emplace_back(node_1.child_l, id_2);
1118 stack_emplace_back(node_1.child_r, id_2);
1119 } else {
1120 if (node_1.box_tot_num >
1121 node_2.box_tot_num) { // split first larger tree
1122 if (id_s1 >= 0) {
1123 stack_emplace_back(node_1.child_l, id_s2);
1124 stack_emplace_back(node_1.child_r, id_s2);
1125 if (node_1.box_num > 0)
1126 stack_emplace_back(negate(id_1), id_2);
1127 } else if (id_s2 >= 0) { // and id_s1 < 0
1128 stack_emplace_back(id_s1, node_2.child_l);
1129 stack_emplace_back(id_s1, node_2.child_r);
1130 }
1131 } else {
1132 if (id_s2 >= 0) {
1133 stack_emplace_back(id_s1, node_2.child_l);
1134 stack_emplace_back(id_s1, node_2.child_r);
1135 if (node_2.box_num > 0)
1136 stack_emplace_back(id_1, negate(id_2));
1137 } else if (id_s1 >= 0) { // and id_s2 < 2
1138 stack_emplace_back(node_1.child_l, id_s2);
1139 stack_emplace_back(node_1.child_r, id_s2);
1140 }
1141 }
1142 }
1143 }
1144
1145 // Return the distance between the trees
1146 return distance;
1147 }
1148
1162 template <typename Object, typename Function = std::function<
1163 Real(Object const &, Box const &)>>
1165 Object const &obj, Integer const n, IndexSet &candidates,
1166 Function distance_function = [](Object const &o, Box const &b) {
1167 return b.interior_distance(o);
1168 }) const {
1169 // Reset statistics
1173
1174 // Return if the tree is empty
1175 if (this->is_empty()) {
1176 return -1.0;
1177 }
1178
1179 // Collect the original object boxes
1180 BoxUniquePtrList const &boxes{*m_boxes_ptr};
1181
1182 // Setup the stack
1183 m_stack.clear();
1184 m_stack.reserve(2 * this->size() + 1);
1185 m_stack.emplace_back(0);
1186
1187 // Candidate vector distance and index
1188 using Pair = std::pair<Real, Integer>;
1189 auto cmp = [](const Pair &a, const Pair &b) { return a.first < b.first; };
1190 std::priority_queue<Pair, std::vector<Pair>, decltype(cmp)> queue(cmp);
1191
1192 // Main loop that checks the intersection iteratively
1193 Real distance{
1194 std::numeric_limits<Real>::infinity()}; // Maximum distance in the queue
1195 candidates.clear();
1196 while (!m_stack.empty()) {
1197 // Pop the node from stack
1198 Integer const id{m_stack.back()};
1199 m_stack.pop_back();
1200
1201 // Get the node
1202 Node const &node{m_tree_structure[id]};
1203
1204 // Compute the distance between the object and the bounding box
1206 Real tmp_distance{distance_function(obj, node.box)};
1207
1208 // If the distance is greater than the maximum distance, skip the node
1209 if (tmp_distance > distance) {
1210 continue;
1211 }
1212
1213 // Compute the distance between the object and the long boxes on the
1214 // node
1215 if (node.box_num > 0) {
1216 Integer const id_ini{node.box_ptr};
1217 Integer const id_end{node.box_ptr + node.box_num};
1218 for (Integer i{id_ini}; i < id_end; ++i) {
1219 Integer const pos{m_tree_boxes_map[i]};
1221 tmp_distance = distance_function(obj, *boxes[pos]);
1222 if (tmp_distance < distance) {
1223 if (static_cast<Integer>(queue.size()) < n) {
1224 queue.emplace(tmp_distance, pos);
1225 } else {
1226 queue.pop();
1227 queue.emplace(tmp_distance, pos);
1228 }
1229 distance = queue.top().first;
1230 }
1231 }
1232 }
1233
1234 // Push children on the stack if thay are not leafs
1235 if (node.child_l > 0) {
1236 m_stack.emplace_back(node.child_l);
1237 }
1238 if (node.child_r > 0) {
1239 m_stack.emplace_back(node.child_r);
1240 }
1241 }
1242
1243 // Extract indices into candidates
1244 Real min_distance{queue.empty() ? distance : queue.top().first};
1245 while (!queue.empty()) {
1246 candidates.insert(queue.top().second);
1247 queue.pop();
1248 }
1249 return min_distance;
1250 }
1251
1265 template <typename Object, typename Function = std::function<
1266 Real(Object const &, Box const &)>>
1268 Object const &obj, Real const max_distance, IndexSet &candidates,
1269 Function distance_function = [](Object const &o, Box const &b) {
1270 return b.interior_distance(o);
1271 }) const {
1272 // Reset statistics
1276
1277 // Return if the tree is empty
1278 if (this->is_empty()) {
1279 return false;
1280 }
1281
1282 // Collect the original object boxes
1283 BoxUniquePtrList const &boxes{*m_boxes_ptr};
1284
1285 // Setup the stack
1286 m_stack.clear();
1287 m_stack.reserve(2 * this->size() + 1);
1288 m_stack.emplace_back(0);
1289
1290 // Main loop that checks the intersection iteratively
1291 candidates.clear();
1292 while (!m_stack.empty()) {
1293 // Pop the node from stack
1294 Integer const id{m_stack.back()};
1295 m_stack.pop_back();
1296
1297 // Get the node
1298 Node const &node{m_tree_structure[id]};
1299
1300 // Compute the distance between the object and the bounding box
1302 Real distance{distance_function(obj, node.box)};
1303
1304 // If the distance is greater than the maximum distance, skip the node
1305 if (distance > max_distance) {
1306 continue;
1307 }
1308
1309 // Compute the distance between the object and the long boxes on the
1310 // node
1311 if (node.box_num > 0) {
1312 Integer const id_ini{node.box_ptr};
1313 Integer const id_end{node.box_ptr + node.box_num};
1314 for (Integer i{id_ini}; i < id_end; ++i) {
1315 Integer const pos{m_tree_boxes_map[i]};
1317 distance = distance_function(obj, *boxes[pos]);
1318 if (distance <= max_distance) {
1319 candidates.insert(pos);
1320 }
1321 }
1322 }
1323
1324 // Push children on the stack if thay are not leafs
1325 if (node.child_l > 0) {
1326 m_stack.emplace_back(node.child_l);
1327 }
1328 if (node.child_r > 0) {
1329 m_stack.emplace_back(node.child_r);
1330 }
1331 }
1332
1333 // Return true if at least one candidate is within the given distance
1334 return !candidates.empty();
1335 }
1336
1342 void depth(Integer const i, Integer &d) const {
1343 d = 0;
1344 if (i < 0) {
1345 return;
1346 }
1347 m_stack.clear();
1348 m_stack.reserve(2 * this->size() + 1);
1349 m_stack.emplace_back(i);
1350 std::vector<Integer> depth_stack;
1351 depth_stack.reserve(2 * this->size() + 1);
1352 depth_stack.emplace_back(0);
1353 Integer depth{0};
1354 while (!m_stack.empty()) {
1355 Integer const id{m_stack.back()};
1356 m_stack.pop_back();
1357 Node const &node{m_tree_structure[id]};
1358 depth = static_cast<Integer>(depth_stack.back());
1359 depth_stack.pop_back();
1360 if (node.child_l == -1) {
1361 d = std::max(d, depth);
1362 } else {
1363 m_stack.emplace_back(node.child_l);
1364 depth_stack.emplace_back(depth + 1);
1365 }
1366 if (node.child_r == -1) {
1367 d = std::max(d, depth);
1368 } else {
1369 m_stack.emplace_back(node.child_r);
1370 depth_stack.emplace_back(depth + 1);
1371 }
1372 }
1373 }
1374
1383 void nodes(Integer const i, Integer &l, Integer &n, Integer &b) const {
1384 l = n = b = 0;
1385 if (i < 0) {
1386 return;
1387 }
1388 m_stack.clear();
1389 m_stack.reserve(2 * this->size() + 1);
1390 m_stack.emplace_back(i);
1391 while (!m_stack.empty()) {
1392 Integer const id{m_stack.back()};
1393 m_stack.pop_back();
1394 Node const &node{m_tree_structure[id]};
1395 if (node.child_l == -1) {
1396 ++l;
1397 } else {
1398 b += node.box_num;
1399 m_stack.emplace_back(node.child_l);
1400 }
1401 if (node.child_r == -1) {
1402 ++l;
1403 } else {
1404 b += node.box_num;
1405 m_stack.emplace_back(node.child_r);
1406 }
1407 ++n;
1408 }
1409 }
1410
1415 void stats(Statistics &stats) const {
1416 // Reset statistics
1417 stats.reset();
1418
1419 // Compute/copy the build statistics
1420 stats.objects = static_cast<Integer>(m_boxes_ptr->size());
1421 this->nodes(0, stats.leafs, stats.nodes, stats.long_boxes);
1422 this->depth(0, stats.depth);
1423 this->nodes(m_tree_structure[0].child_l, stats.left_leafs, stats.left_nodes,
1424 stats.left_long_boxes);
1425 this->depth(m_tree_structure[0].child_l, stats.left_depth);
1426 this->nodes(m_tree_structure[0].child_r, stats.right_leafs,
1427 stats.right_nodes, stats.right_long_boxes);
1428 this->depth(m_tree_structure[0].child_r, stats.right_depth);
1429 stats.dump_counter = m_dump_counter;
1430 stats.balance_ratio = static_cast<Real>(stats.left_leafs) /
1431 static_cast<Real>(stats.right_leafs);
1432 stats.depth_ratio = static_cast<Real>(stats.left_depth) /
1433 static_cast<Real>(stats.right_depth);
1434
1435 // Copy the check counter
1436 stats.check_counter =
1438 stats.node_check_counter = m_node_check_counter;
1439 stats.long_check_counter = m_long_check_counter;
1440 stats.objs_check_counter = m_objs_check_counter;
1441 }
1442
1447 void print(OutStream &os) const {
1448 // Retrieve the statistics
1450 this->stats(stats);
1451 // Print the tree info
1452 stats.print(os);
1453 }
1454
1455}; // class Tree
1456
1457} // namespace AABBtree
1458
1459#endif // INCLUDE_AABBTREE_TREE_HXX
#define AABBTREE_ASSERT(COND, MSG)
Definition AABBtree.hh:48
#define AABBTREE_ASSERT_WARNING(COND, MSG)
Definition AABBtree.hh:64
A class representing an axis-aligned bounding box (AABB) in N-dimensional space.
Definition Box.hxx:50
Box & extend(Point const &p)
Definition Box.hxx:511
void set_empty()
Definition Box.hxx:333
Side which_side(Real const x, Real const tol, Integer const dim) const
Definition Box.hxx:447
Real interior_distance(Point const &p) const
Definition Box.hxx:576
Point const & min() const
Definition Box.hxx:176
Side
Definition Box.hxx:437
@ RIGHT
Definition Box.hxx:437
@ LEFT
Definition Box.hxx:437
bool intersects(Box const &b) const
Definition Box.hxx:343
Point const & max() const
Definition Box.hxx:188
Integer m_long_check_counter
Definition Tree.hxx:151
Integer max_nodal_objects() const
Definition Tree.hxx:217
PriorityQueue m_queue
Definition Tree.hxx:157
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:1164
bool intersect(Tree const &tree, IndexMap &candidates) const
Definition Tree.hxx:581
void build(std::unique_ptr< BoxUniquePtrList > boxes_ptr)
Definition Tree.hxx:276
Integer max_dumpings() const
Definition Tree.hxx:200
Integer size() const
Definition Tree.hxx:255
AABBtree::BoxUniquePtr< Real, N > BoxUniquePtr
Definition Tree.hxx:114
AABBtree::Point< Real, N > Point
Definition Tree.hxx:117
void max_dumpings(Integer const max_dumpings)
Definition Tree.hxx:188
std::unique_ptr< BoxUniquePtrList > m_boxes_ptr
Definition Tree.hxx:138
Integer m_objs_check_counter
Definition Tree.hxx:152
void build()
Definition Tree.hxx:286
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:1267
bool self_intersect(IndexSet &candidates) const
Definition Tree.hxx:887
void clear()
Definition Tree.hxx:266
std::tuple< Integer, Integer, Real > QueueItem
Definition Tree.hxx:118
Integer m_max_nodal_objects
Definition Tree.hxx:146
BoxUniquePtr const & box(Integer const i) const
Definition Tree.hxx:182
AABBtree::BoxUniquePtrList< Real, N > BoxUniquePtrList
Definition Tree.hxx:115
bool self_intersect(IndexMap &candidates) const
Definition Tree.hxx:739
void nodes(Integer const i, Integer &l, Integer &n, Integer &b) const
Definition Tree.hxx:1383
Real separation_ratio_tolerance() const
Definition Tree.hxx:234
AABBtree::Box< Real, N > Box
Definition Tree.hxx:113
void depth(Integer const i, Integer &d) const
Definition Tree.hxx:1342
Integer m_node_check_counter
Definition Tree.hxx:150
std::vector< Node > const & structure() const
Definition Tree.hxx:242
~Tree()=default
Real m_separation_ratio_tolerance
Definition Tree.hxx:147
Node const & node(Integer const i) const
Definition Tree.hxx:249
Integer m_dump_counter
Definition Tree.hxx:153
Tree()=default
void max_nodal_objects(Integer const max_nodal_objects)
Definition Tree.hxx:206
IndexList m_tree_boxes_map
Definition Tree.hxx:141
Real distance(Object const &obj, IndexSet &candidates) const
Definition Tree.hxx:910
Integer m_max_dumpings
Definition Tree.hxx:145
void print(OutStream &os) const
Definition Tree.hxx:1447
Real distance(Tree const &tree, IndexMap &candidates) const
Definition Tree.hxx:992
IndexList m_stack
Definition Tree.hxx:156
void stats(Statistics &stats) const
Definition Tree.hxx:1415
bool is_empty() const
Definition Tree.hxx:261
void separation_ratio_tolerance(Real const ratio)
Definition Tree.hxx:223
BoxUniquePtrList const & boxes() const
Definition Tree.hxx:175
std::priority_queue< QueueItem, std::vector< QueueItem >, std::function< bool(QueueItem, QueueItem)> > PriorityQueue
Definition Tree.hxx:119
AABBtree::Vector< Real, N > Vector
Definition Tree.hxx:116
bool intersect(Object const &obj, IndexSet &candidates) const
Definition Tree.hxx:513
std::vector< Node > m_tree_structure
Definition Tree.hxx:140
Namespace for the AABBtree library.
Definition AABBtree.hh:81
std::basic_ostream< char > OutStream
Definition AABBtree.hh:96
Eigen::Vector< Real, N > Point
Definition AABBtree.hh:105
std::map< Integer, IndexSet > IndexMap
Definition AABBtree.hh:94
std::set< Integer > IndexSet
Definition AABBtree.hh:93
std::unique_ptr< Box< Real, N > > BoxUniquePtr
Definition AABBtree.hh:101
std::vector< BoxUniquePtr< Real, N > > BoxUniquePtrList
Definition AABBtree.hh:103
std::vector< Integer > IndexList
Definition AABBtree.hh:95
Eigen::Vector< Real, N > Vector
Definition AABBtree.hh:104
AABBTREE_DEFAULT_INTEGER_TYPE Integer
The Integer type used in the AABBtree class.
Definition AABBtree.hh:89
Definition Tree.hxx:126
Integer parent
Definition Tree.hxx:132
Box box_long
Definition Tree.hxx:128
Box box
Definition Tree.hxx:127
Integer child_l
Definition Tree.hxx:133
Integer box_num
Definition Tree.hxx:130
Integer box_tot_num
Definition Tree.hxx:131
Integer child_r
Definition Tree.hxx:134
Integer box_ptr
Definition Tree.hxx:129
Definition Tree.hxx:46
Integer right_long_boxes
Definition Tree.hxx:59
void reset() noexcept
Definition Tree.hxx:80
Integer check_counter
Definition Tree.hxx:66
Integer right_leafs
Definition Tree.hxx:58
Integer depth
Definition Tree.hxx:52
Real depth_ratio
Definition Tree.hxx:63
Integer right_nodes
Definition Tree.hxx:57
Integer nodes
Definition Tree.hxx:49
Integer long_check_counter
Definition Tree.hxx:68
Integer long_boxes
Definition Tree.hxx:51
Integer node_check_counter
Definition Tree.hxx:67
Integer left_leafs
Definition Tree.hxx:54
Integer dump_counter
Definition Tree.hxx:61
Integer left_nodes
Definition Tree.hxx:53
Integer objs_check_counter
Definition Tree.hxx:69
Integer leafs
Definition Tree.hxx:50
Integer left_long_boxes
Definition Tree.hxx:55
void print(OutStream &os) const
Definition Tree.hxx:86
Integer right_depth
Definition Tree.hxx:60
Real balance_ratio
Definition Tree.hxx:62
Integer left_depth
Definition Tree.hxx:56
Integer objects
Definition Tree.hxx:48