From 94cb475ecc3152ac2e979e5a3c534d71ef79c589 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Wed, 29 Jun 2022 12:13:10 +0300 Subject: [PATCH] Add generic ndtree implementation --- libs/cg/include/psemek/cg/ndtree.hpp | 227 +++++++++++++++++++++++++++ 1 file changed, 227 insertions(+) create mode 100644 libs/cg/include/psemek/cg/ndtree.hpp diff --git a/libs/cg/include/psemek/cg/ndtree.hpp b/libs/cg/include/psemek/cg/ndtree.hpp new file mode 100644 index 00000000..30dc8f77 --- /dev/null +++ b/libs/cg/include/psemek/cg/ndtree.hpp @@ -0,0 +1,227 @@ +#pragma once + +#include +#include +#include + +#include +#include + +namespace psemek::cg +{ + + template + struct ndtree; + + template + using quadtree = ndtree; + + template + using octree = ndtree; + + template + struct ndtree + { + public: + using point_type = geom::point; + using box_type = geom::box; + + struct value_type + { + point_type point; + Data data; + + template + value_type(A && point, B && data) + : point(std::forward(point)) + , data(std::forward(data)) + {} + }; + + struct node; + + using node_ptr = std::unique_ptr; + using node_ref = node *; + + struct node + { + node_ptr children[1 << N]; + + std::optional value; + + node() = default; + + template + node(A && point, B && data) + : value(std::in_place, std::forward(point), std::forward(data)) + {} + + bool leaf() const + { + return std::all_of(std::begin(children), std::end(children), [](node_ptr const & p){ return !static_cast(p); }); + } + + bool full() const + { + return std::all_of(std::begin(children), std::end(children), [](node_ptr const & p){ return static_cast(p); }); + } + }; + + template + std::pair insert(A && point, B && data) + { + if (!root) + { + root = std::make_unique(std::forward(point), std::forward(data)); + for (std::size_t d = 0; d < N; ++d) + { + root_bbox[d].min = root->value->point[d]; + root_bbox[d].max = root->value->point[d] + T{1}; + } + + return {root.get(), true}; + } + + if (!geom::half_open_contains(root_bbox, point)) + { + while (!geom::half_open_contains(root_bbox, point)) + extend_root(point); + } + + node * current_node = root.get(); + box_type current_bbox = root_bbox; + + while (true) + { + if (current_node->leaf()) + { + if (current_node->value) + { + if (current_node->value->point == point) + { + return {current_node, false}; + } + else + { + split(current_node, current_bbox); + assert(!current_node->value); + } + } + else + { + current_node->value.emplace(std::forward(point), std::forward(data)); + return {current_node, true}; + } + } + + std::size_t index = 0; + for (std::size_t d = 0; d < N; ++d) + { + auto const c = current_bbox[d].center(); + if (point[d] < c) + { + current_bbox[d].max = c; + } + else + { + index |= (1 << d); + current_bbox[d].min = c; + } + } + + auto & child = current_node->children[index]; + + if (!child) + { + child = std::make_unique(std::forward(point), std::forward(data)); + return {child.get(), true}; + } + + current_node = child.get(); + } + } + + template + void traverse(Visitor && visitor) const + { + if (!root) return; + traverse_impl(std::forward(visitor), root.get(), root_bbox); + } + + template + void traverse(Visitor && visitor, node_ref current_node, box_type const & bbox) const + { + if (!current_node) return; + traverse_impl(std::forward(visitor), current_node, bbox); + } + + private: + + node_ptr root; + geom::box root_bbox; + + void extend_root(point_type const & point) + { + std::size_t index_in_new_root = 0; + for (std::size_t d = 0; d < N; ++d) + { + if (point[d] >= root_bbox[d].min) + root_bbox[d].max += root_bbox[d].length(); + else + { + root_bbox[d].min -= root_bbox[d].length(); + index_in_new_root |= (1 << d); + } + } + + auto new_root = std::make_unique(); + new_root->children[index_in_new_root] = std::move(root); + root = std::move(new_root); + } + + static void split(node * n, box_type const & bbox) + { + assert(n->value); + + std::size_t index_in_parent = 0; + for (std::size_t d = 0; d < N; ++d) + { + if (n->value->point[d] >= bbox[d].center()) + index_in_parent |= (1 << d); + } + + auto & child = n->children[index_in_parent]; + child = std::make_unique(n->value->point, std::move(n->value->data)); + n->value = std::nullopt; + } + + template + static void traverse_impl(Visitor && visitor, node * current_node, box_type const & box) + { + std::size_t children_count = 0; + for (std::size_t i = 0; i < (1 << N); ++i) + if (current_node->children[i]) + ++children_count; + + if (visitor(*current_node, box)) + { + for (std::size_t i = 0; i < (1 << N); ++i) + { + if (!current_node->children[i]) continue; + + box_type child_box; + for (std::size_t d = 0; d < N; ++d) + { + auto const c = box[d].center(); + if (i & (1 << d)) + child_box[d] = {c, box[d].max}; + else + child_box[d] = {box[d].min, c}; + } + traverse_impl(visitor, current_node->children[i].get(), child_box); + } + } + } + }; + +}