Skip to content

Commit e41a23c

Browse files
C. Andy Martinc-andy-martin
C. Andy Martin
authored andcommitted
deleteAABB: delete an axis-aligned bounding box
Add methods to delete cells either in or out of an axis-aligned bounding box. This is a common operation for users of the octomap library, and accelerating it by not visiting nodes to be deleted is very useful in certain applications, like bounds-limited rolling costmaps. Also adds the ability to register a delection callback during node deletion. This callback is called for every leaf node that is deleted.
1 parent 45d7581 commit e41a23c

File tree

4 files changed

+373
-0
lines changed

4 files changed

+373
-0
lines changed

octomap/include/octomap/OcTreeBaseImpl.h

+55
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include <iterator>
4141
#include <stack>
4242
#include <bitset>
43+
#include <functional>
4344

4445
#include "octomap_types.h"
4546
#include "OcTreeKey.h"
@@ -223,6 +224,44 @@ namespace octomap {
223224
*/
224225
bool deleteNode(const OcTreeKey& key, unsigned int depth = 0);
225226

227+
using DeletionCallback = std::function<void(const OcTreeBaseImpl<NODE, INTERFACE>* tree, const NODE* node, const OcTreeKey& key, unsigned int depth)>;
228+
229+
/**
230+
* Delete all nodes in (or out) of the given AABB.
231+
*
232+
* Delete all the nodes in the given axis-aligned bounding box (AABB).
233+
* This method efficiently descends the tree only visiting the nodes in
234+
* (or out) of the box. If invert is set, nodes outside the box are
235+
* deleted (exclusive), otherwise nodes inside or on the box are deleted
236+
* (inclusive).
237+
*
238+
* @param min Bottom-left corner of box (inclusive).
239+
* @param max Top-right corner of box (inclusive).
240+
* @param invert If true, delete everything out of the box, otherwise delete in the box.
241+
* @param deletion_notifier If set, a callback to call when a leaf node is
242+
* about to be deleted.
243+
*/
244+
void deleteAABB(const point3d& min, const point3d& max, bool invert = false,
245+
DeletionCallback deletion_notifier = DeletionCallback());
246+
247+
/**
248+
* Delete all nodes in (or out) of the given AABB.
249+
*
250+
* Delete all the nodes in the given axis-aligned bounding box (AABB).
251+
* This method efficiently descends the tree only visiting the nodes in
252+
* (or out) of the box. If invert is set, nodes outside the box are
253+
* deleted (exclusive), otherwise nodes inside or on the box are deleted
254+
* (inclusive).
255+
*
256+
* @param min Bottom-left corner of box (inclusive).
257+
* @param max Top-right corner of box (inclusive).
258+
* @param invert If true, delete everything out of the box, otherwise delete in the box.
259+
* @param deletion_notifier If set, a callback to call when a leaf node is
260+
* about to be deleted.
261+
*/
262+
void deleteAABB(const OcTreeKey& min, const OcTreeKey& max, bool invert = false,
263+
DeletionCallback deletion_notifier = DeletionCallback());
264+
226265
/// Deletes the complete tree structure
227266
void clear();
228267

@@ -588,6 +627,18 @@ namespace octomap {
588627
/// recursive call of deleteNode()
589628
bool deleteNodeRecurs(NODE* node, unsigned int depth, unsigned int max_depth, const OcTreeKey& key);
590629

630+
/// Recursively delete a node and all children, calling the deletion_notifier for every leaf node deleted.
631+
void deleteNodeRecurs(NODE* node,
632+
const OcTreeKey& key,
633+
unsigned int depth,
634+
const DeletionCallback& deletion_notifier);
635+
636+
/// recursive call of deleteAABB(). Returns true if node has been deleted.
637+
bool deleteAABBRecurs(const OcTreeKey& min, const OcTreeKey& max,
638+
NODE* node, const OcTreeKey& key, unsigned int depth,
639+
unsigned int max_depth, bool invert,
640+
const DeletionCallback& deletion_notifier);
641+
591642
/// recursive call of prune()
592643
void pruneRecurs(NODE* node, unsigned int depth, unsigned int max_depth, unsigned int& num_pruned);
593644

@@ -604,6 +655,10 @@ namespace octomap {
604655
protected:
605656
void allocNodeChildren(NODE* node);
606657
void deleteNodeChildren(NODE* node);
658+
void deleteNodeChildren(NODE* node,
659+
const OcTreeKey& key,
660+
unsigned int depth,
661+
const DeletionCallback& deletion_notifier);
607662

608663
NODE* root; ///< Pointer to the root NODE, NULL for empty tree
609664

octomap/include/octomap/OcTreeBaseImpl.hxx

+156
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,23 @@ namespace octomap {
306306
}
307307
}
308308

309+
template <class NODE,class I>
310+
void OcTreeBaseImpl<NODE,I>::deleteNodeChildren(NODE* node, const OcTreeKey& key, unsigned int depth,
311+
const DeletionCallback& deletion_notifier){
312+
if (node->children != NULL) {
313+
key_type center_offset_key = tree_max_val >> (depth + 1);
314+
for (unsigned int i=0; i<8; i++) {
315+
if (node->children[i] != NULL){
316+
OcTreeKey child_key;
317+
computeChildKey(i, center_offset_key, key, child_key);
318+
this->deleteNodeRecurs(static_cast<NODE*>(node->children[i]), child_key, depth + 1, deletion_notifier);
319+
}
320+
}
321+
delete[] node->children;
322+
node->children = NULL;
323+
}
324+
}
325+
309326
template <class NODE,class I>
310327
inline key_type OcTreeBaseImpl<NODE,I>::coordToKey(double coordinate, unsigned depth) const{
311328
assert (depth <= tree_depth);
@@ -575,6 +592,24 @@ namespace octomap {
575592
return deleteNodeRecurs(root, 0, depth, key);
576593
}
577594

595+
template <class NODE,class I>
596+
void OcTreeBaseImpl<NODE,I>::deleteAABB(const point3d& min, const point3d& max, bool invert,
597+
DeletionCallback deletion_notifier) {
598+
OcTreeKey min_key, max_key;
599+
coordToKeyClamped(min, min_key);
600+
coordToKeyClamped(max, max_key);
601+
deleteAABB(min_key, max_key, invert, deletion_notifier);
602+
}
603+
604+
template <class NODE,class I>
605+
void OcTreeBaseImpl<NODE,I>::deleteAABB(const OcTreeKey& min, const OcTreeKey& max, bool invert,
606+
DeletionCallback deletion_notifier) {
607+
octomap::OcTreeKey root_key(tree_max_val, tree_max_val, tree_max_val);
608+
if (deleteAABBRecurs(min, max, root, root_key, 0, tree_depth, invert, deletion_notifier)) {
609+
root = NULL;
610+
}
611+
}
612+
578613
template <class NODE,class I>
579614
void OcTreeBaseImpl<NODE,I>::clear() {
580615
if (this->root){
@@ -775,6 +810,127 @@ namespace octomap {
775810
return false;
776811
}
777812

813+
template <class NODE,class I>
814+
void OcTreeBaseImpl<NODE,I>::deleteNodeRecurs(NODE* node, const OcTreeKey& key, unsigned int depth,
815+
const DeletionCallback& deletion_notifier){
816+
assert(node);
817+
assert(deletion_notifier);
818+
819+
if (!nodeHasChildren(node)) {
820+
// Only notify for leaf nodes
821+
deletion_notifier(this, node, key, depth);
822+
}
823+
824+
// Always call delete node children in the rare case there is a children
825+
// pointer area in an inner node but all children pointers are NULL
826+
deleteNodeChildren(node, key, depth, deletion_notifier);
827+
828+
delete node;
829+
tree_size--;
830+
size_changed = true;
831+
}
832+
833+
template <class NODE,class I>
834+
bool OcTreeBaseImpl<NODE,I>::deleteAABBRecurs(const OcTreeKey& min,
835+
const OcTreeKey& max,
836+
NODE* node,
837+
const OcTreeKey& key,
838+
unsigned int depth,
839+
unsigned int max_depth,
840+
bool invert,
841+
const DeletionCallback& deletion_notifier) {
842+
bool delete_node = false;
843+
bool skip_delete_notification = false;
844+
if (node != NULL && depth <= tree_depth && depth <= max_depth) {
845+
#ifndef NDEBUG
846+
if (depth < tree_depth)
847+
{
848+
unsigned int mask = (1 << (tree_depth - depth - 1)) - 1;
849+
assert((key[0] & mask) == 0);
850+
assert((key[1] & mask) == 0);
851+
assert((key[2] & mask) == 0);
852+
}
853+
#endif
854+
if (min[0] > max[0] || min[1] > max[1] || min[2] > max[2]) {
855+
// The box has no volume.
856+
if (invert) {
857+
// A non-positive volume box and we are to delete everything outside
858+
// of it. Delete the node.
859+
delete_node = true;
860+
} else {
861+
// A non-positive volume box and we are to delete everything inside
862+
// of it. Do nothing.
863+
}
864+
} else {
865+
key_type key_size_down = tree_max_val >> depth;
866+
key_type key_size_up = (tree_max_val-1) >> depth;
867+
868+
assert(key[0] - key_size_down <= key[0] + key_size_up);
869+
assert(key[1] - key_size_down <= key[1] + key_size_up);
870+
assert(key[2] - key_size_down <= key[2] + key_size_up);
871+
bool inside = ((min[0] <= (key[0] - key_size_down)) && (max[0] >= (key[0] + key_size_up)) &&
872+
(min[1] <= (key[1] - key_size_down)) && (max[1] >= (key[1] + key_size_up)) &&
873+
(min[2] <= (key[2] - key_size_down)) && (max[2] >= (key[2] + key_size_up)));
874+
bool outside = !((min[0] <= (key[0] + key_size_up)) && (max[0] >= (key[0] - key_size_down)) &&
875+
(min[1] <= (key[1] + key_size_up)) && (max[1] >= (key[1] - key_size_down)) &&
876+
(min[2] <= (key[2] + key_size_up)) && (max[2] >= (key[2] - key_size_down)));
877+
assert(!(inside && outside));
878+
if ((inside && invert) || (outside && !invert)) {
879+
// Nothing to do, we are entirely out of the deletion target
880+
} else if ((inside && !invert) || (outside && invert)) {
881+
// The entire area is inside the deletion area.
882+
delete_node = true;
883+
} else if (depth < max_depth) {
884+
// At this point the current, inner node is both inside and oustide
885+
// the bounds (it crosses the boundary).
886+
// It is not possible to be at the tree_depth unless somehow at the
887+
// tree_depth we were neither inside or outside the bounding keys
888+
assert(depth < tree_depth);
889+
// Expand if pruned leaf.
890+
if (!nodeHasChildren(node)) {
891+
expandNode(node);
892+
}
893+
894+
key_type center_offset_key = tree_max_val >> (depth + 1);
895+
for (unsigned int i=0; i<8; i++) {
896+
if (nodeChildExists(node, i)) {
897+
OcTreeKey child_key;
898+
computeChildKey(i, center_offset_key, key, child_key);
899+
900+
if (deleteAABBRecurs(min, max,
901+
getNodeChild(node, i), child_key, depth + 1,
902+
max_depth, invert, deletion_notifier)) {
903+
setNodeChild(node, i, NULL);
904+
}
905+
}
906+
}
907+
908+
// If we have no more children left, this inner node can be removed
909+
if (!nodeHasChildren(node)) {
910+
delete_node = true;
911+
// prevent notification, as this is not a leaf
912+
skip_delete_notification = true;
913+
} else {
914+
// It should not be possible to prune this node unless there is a
915+
// logic bug above, because we should have deleted at least one of
916+
// our children.
917+
assert(pruneNode(node) == false);
918+
// Update the inner node's expiry to track the min of all children
919+
node->updateOccupancyChildren();
920+
}
921+
}
922+
}
923+
}
924+
if (delete_node) {
925+
if (deletion_notifier && !skip_delete_notification) {
926+
deleteNodeRecurs(node, key, depth, deletion_notifier);
927+
} else {
928+
deleteNodeRecurs(node);
929+
}
930+
}
931+
return delete_node;
932+
}
933+
778934
template <class NODE,class I>
779935
void OcTreeBaseImpl<NODE,I>::pruneRecurs(NODE* node, unsigned int depth,
780936
unsigned int max_depth, unsigned int& num_pruned) {

octomap/src/testing/CMakeLists.txt

+4
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ if(BUILD_TESTING)
2929
ADD_EXECUTABLE(test_bbx test_bbx.cpp)
3030
TARGET_LINK_LIBRARIES(test_bbx octomap)
3131

32+
ADD_EXECUTABLE(test_delete_aabb test_delete_aabb.cpp)
33+
TARGET_LINK_LIBRARIES(test_delete_aabb octomap octomath)
34+
3235
ADD_EXECUTABLE(test_set_tree_values test_set_tree_values.cpp)
3336
TARGET_LINK_LIBRARIES(test_set_tree_values octomap octomath)
3437

@@ -48,6 +51,7 @@ if(BUILD_TESTING)
4851
ADD_TEST (NAME test_raycasting COMMAND test_raycasting)
4952
ADD_TEST (NAME test_io COMMAND test_io ${PROJECT_SOURCE_DIR}/share/data/geb079.bt)
5053
ADD_TEST (NAME test_pruning COMMAND test_pruning )
54+
ADD_TEST (NAME test_delete_aabb COMMAND test_delete_aabb )
5155
ADD_TEST (NAME test_set_tree_values COMMAND test_set_tree_values )
5256
ADD_TEST (NAME test_iterators COMMAND test_iterators ${PROJECT_SOURCE_DIR}/share/data/geb079.bt)
5357
ADD_TEST (NAME test_mapcollection COMMAND test_mapcollection ${PROJECT_SOURCE_DIR}/share/data/mapcoll.txt)

0 commit comments

Comments
 (0)