Reworked RD-Tree to behave more consistently. The construction of the tree did not allow specifying what index to insert a child at under a parent.

Traverser orders were also broken, which is now fixed.
This commit is contained in:
2025-08-14 17:07:48 -04:00
parent f173c3e7cd
commit 2535e1ac4b
3 changed files with 171 additions and 51 deletions

View File

@@ -44,28 +44,26 @@ public:
static constexpr size_t root = 0;
static constexpr size_t npos = -1;
enum control_ : uint8_t {
control_continue = 0,
control_jump_over,
control_break
};
protected:
struct node {
optional<TypeT> value;
size_t parent, child, prev, next;
size_t depth, num_children;
template<typename...ArgsT>
constexpr node(size_t p, size_t c, size_t v, size_t n, ArgsT&&...args)
constexpr node(size_t p, size_t c, size_t v, size_t n, size_t d, ArgsT&&...args)
: value(fennec::forward<ArgsT>(args)...)
, parent(p), child(c), prev(v), next(n) {
, parent(p), child(c), prev(v), next(n)
, depth(d), num_children(0) {
}
constexpr ~node() {
parent = npos;
child = npos;
prev = npos;
next = npos;
parent = npos;
child = npos;
prev = npos;
next = npos;
depth = npos;
num_children = 0;
}
};
@@ -77,7 +75,7 @@ public:
explicit constexpr rdtree(ArgsT&&...args)
: _table(), _freed(), _size(1) {
_table.callocate(8);
fennec::construct(&_table[0], npos, npos, npos, npos, fennec::forward<ArgsT>(args)...);
fennec::construct(&_table[0], npos, npos, npos, npos, 0, fennec::forward<ArgsT>(args)...);
}
constexpr rdtree(const rdtree& tree)
@@ -133,34 +131,74 @@ public:
/// \param i The id of the node to check
/// \returns The id of the parent node
constexpr size_t parent(size_t i) const {
if (i >= _table.capacity()) return npos;
return i == npos ? npos : _table[i].parent;
}
///
/// \param i The id of the node to check
/// \returns The id of the child node
constexpr size_t child(size_t i) const {
return i == npos ? npos : _table[i].child;
constexpr size_t child(size_t i, size_t n = 0) const {
if (i >= _table.capacity()) return npos;
size_t c = i == npos ? npos : _table[i].child;
if (n != 0)
return next(c, n == npos ? npos : n - 1);
return c;
}
///
/// \param i The id of the node to check
/// \returns The id of the next node
constexpr size_t next(size_t i) const {
return i == npos ? npos : _table[i].next;
constexpr size_t next(size_t i, size_t n = 0) const {
if (i >= _table.capacity()) return npos;
if (i == npos) {
return npos;
}
size_t org = i;
size_t nxt = _table[i].next;
while (nxt != npos) {
i = nxt;
nxt = _table[i].next;
if (n != npos) {
if (n-- == 0) {
break;
}
}
}
return i == org && n != npos ? npos : i;
}
///
/// \param i The id of the node to check
/// \returns The id of the previous node
constexpr size_t prev(size_t i) const {
return i == npos ? npos : _table[i].prev;
constexpr size_t prev(size_t i, size_t n = 0) const {
if (i >= _table.capacity()) return npos;
if (i == npos) {
return npos;
}
size_t org = i;
size_t prv = _table[i].prev;
while (prv != npos) {
i = prv;
prv = _table[i].prev;
if (n != npos) {
if (n-- == 0) {
break;
}
}
}
return i == org && n != npos ? npos : i;
}
///
/// \param i the node to start at
/// \returns the left-most child of node `i`
constexpr size_t left_most(size_t i) const {
if (i >= _table.capacity()) return npos;
size_t n = i;
if ((n = child(n)) == npos) {
return i;
@@ -177,6 +215,7 @@ public:
/// \param i the node to start at
/// \returns the right-most child of node `i`
constexpr size_t right_most(size_t i) const {
if (i >= _table.capacity()) return npos;
if ((i = child(i)) == npos) {
return npos;
}
@@ -192,18 +231,54 @@ public:
}
}
///
/// \param i The id of the node to check
/// \returns The depth of the node
constexpr size_t depth(size_t i) const {
if (i >= _table.capacity()) return npos;
return i == npos ? npos : _table[i].depth;
}
///
/// \param i The id of the node to check
/// \returns The number of children the node has
constexpr size_t num_children(size_t i) const {
if (i >= _table.capacity()) return 0;
return i == npos ? 0 : _table[i].num_children;
}
///
/// \returns The next node id were `insert` or `emplace` to be called
constexpr size_t next_free() const {
size_t i = _size;
if (not _freed.empty()) {
i = _freed.front();
}
return i;
}
///
/// \param i The id of the node to access
/// \returns A reference to the value of the node wrapped in an optional
constexpr optional<value_t>& operator[](size_t i) {
return _table[i].value;
constexpr value_t* operator[](size_t i) {
auto& it = _table[i].value;
if (it) {
return &*_table[i].value;
} else {
return nullptr;
}
}
///
/// \param i The id of the node to access
/// \returns A const-qualified reference to the value of the node wrapped in an optional
constexpr const optional<value_t>& operator[](size_t i) const {
return _table[i].value;
constexpr const value_t* operator[](size_t i) const {
const auto& it = _table[i].value;
if (it) {
return &*_table[i].value;
} else {
return nullptr;
}
}
@@ -212,21 +287,49 @@ public:
///
/// \brief Insertion, creates a node in the tree with parent `parent`
/// \param parent the parent node, if `npos` sets the value of the root node
/// \param next the next node, as an index relative to the parent
/// \param val the value to insert
/// \returns the index of the created node
constexpr size_t insert(size_t parent, const value_t& val) {
return this->_insert(parent, val);
constexpr size_t insert(size_t parent, size_t next, const value_t& val) {
return this->_insert(parent, next, val);
}
///
/// \brief Insertion, creates a node in the tree with parent `parent`
/// \param parent the parent node, if `npos` sets the value of the root node
/// \param next the next node, as an index relative to the parent
/// \param val the value to insert
/// \returns the index of the created node
constexpr size_t insert(size_t parent, value_t&& val) {
return this->_insert(parent, fennec::forward<value_t>(val));
constexpr size_t insert(size_t parent, size_t next, value_t&& val) {
return this->_insert(parent, next, fennec::forward<value_t>(val));
}
///
/// \brief Insertion, creates a node in the tree with parent `parent`
/// \param parent the parent node, if `npos` sets the value of the root node
/// \param next the next node, as an index relative to the parent
/// \param args the args to construct the value to insert
/// \returns the index of the created node
template<typename...ArgsT>
constexpr size_t emplace(size_t parent, size_t next, ArgsT&&...args) {
return this->_insert(parent, next, fennec::forward<ArgsT>(args)...);
}
///
/// \brief Swap two nodes
/// \param i0 The id of the first node
/// \param i1 The id of the second node
constexpr void swap(size_t i0, size_t i1) {
size_t p0 = parent(i0);
size_t p1 = parent(i1);
fennec::swap(_table[i0], _table[i1]);
if (child(p0) == i0) _table[p0].child = i1;
if (child(p1) == i1) _table[p1].child = i0;
}
///
/// \brief Erase a node in the tree and all of it's children
/// \param i the index of the node
@@ -332,7 +435,7 @@ public:
OrderT order;
i = order(*this, i);
while (i != npos) {
uint8_t mode = visit(*_table[i].value);
uint8_t mode = visit(*_table[i].value, i);
if (mode == traversal_control_break) {
break;
}
@@ -353,8 +456,8 @@ protected:
size_t _next_free() {
size_t next = _size;
if (not _freed.empty()) {
next = _freed.back();
_freed.pop_back();
next = _freed.front();
_freed.pop_front();
}
if (_size >= capacity()) {
_expand();
@@ -364,9 +467,9 @@ protected:
}
template<typename...ArgsT>
constexpr size_t _insert(size_t p, ArgsT&&...args) {
constexpr size_t _insert(size_t p, size_t n, ArgsT&&...args) {
if (_size == 0) {
fennec::construct(&_table[root], npos, npos, npos, npos, fennec::forward<ArgsT>(args)...);
fennec::construct(&_table[root], npos, npos, npos, npos, 0, fennec::forward<ArgsT>(args)...);
_size = 1;
return root;
}
@@ -377,17 +480,34 @@ protected:
return root;
}
size_t i = _next_free();
size_t n = child(p);
_table[p].child = i;
if (n != npos) _table[n].prev = i;
fennec::construct(&_table[i], p, npos, npos, n, fennec::forward<ArgsT>(args)...);
return i;
size_t idx = _next_free();
size_t nxt = child(p, n);
size_t prv = n == npos ? npos : prev(n);
++_table[p].num_children;
if ((nxt == child(p) && n != npos) || nxt == npos) {
_table[p].child = idx;
}
if (n == npos) {
if (nxt != npos) {
_table[nxt].next = idx;
}
fennec::construct(&_table[idx], p, npos, nxt, npos, depth(p) + 1, fennec::forward<ArgsT>(args)...);
} else {
if (nxt != npos) {
_table[nxt].prev = idx;
}
if (prv != npos) {
_table[prv].next = idx;
}
fennec::construct(&_table[idx], p, npos, prv, nxt, depth(p) + 1, fennec::forward<ArgsT>(args)...);
}
return idx;
}
constexpr void _erase(size_t i) {
list<size_t> queue;
size_t j = 0;
queue.push_back(child(i));
while (queue.empty() == false) {
size_t n = queue.front(); queue.pop_front();
@@ -397,7 +517,6 @@ protected:
fennec::destruct(&_table[n]);
_freed.push_back(n);
--_size;
++j;
}
fennec::destruct(&_table[i]);