2#ifndef LIBSBX_CONTAINERS_OCTREE_HPP_
3#define LIBSBX_CONTAINERS_OCTREE_HPP_
5#include <range/v3/all.hpp>
7#include <libsbx/utility/enum.hpp>
8#include <libsbx/utility/iterator.hpp>
10#include <libsbx/memory/concepts.hpp>
12#include <libsbx/math/vector2.hpp>
13#include <libsbx/math/vector3.hpp>
14#include <libsbx/math/uuid.hpp>
15#include <libsbx/math/volume.hpp>
18#include <libsbx/containers/static_vector.hpp>
20namespace sbx::containers {
22template<
typename Type, std::
size_t Threshold = 16u, std::
size_t Depth = 8u>
25 inline static constexpr auto threshold = Threshold;
26 inline static constexpr auto depth = Depth;
30 using id = std::uint32_t;
32 inline static constexpr auto children_count = std::size_t{8u};
39 inline constexpr static auto null =
static_cast<id>(-1);
42 : values{utility::make_reserved_vector<
value_type>(threshold)},
43 children{null, null, null, null, null, null, null, null} { }
45 auto is_leaf() const noexcept ->
bool {
46 return children[0u] == null;
49 auto child_at(
const std::size_t index)
const noexcept ->
id {
50 return children[index];
53 auto push_back(
const value_type& value)
noexcept ->
void {
54 values.push_back(value);
57 auto push_back(value_type&& value)
noexcept ->
void {
58 values.push_back(std::move(value));
61 static auto find_volume(
const math::volume& outer,
const math::volume& inner)
noexcept -> std::optional<std::uint32_t> {
62 auto center = outer.center();
64 if (!outer.contains(inner)) {
68 if (inner.max().x() <= center.x() && inner.max().y() <= center.y() && inner.max().z() <= center.z()) {
72 if (inner.min().x() >= center.x() && inner.max().y() <= center.y() && inner.max().z() <= center.z()) {
76 if (inner.max().x() <= center.x() && inner.min().y() >= center.y() && inner.max().z() <= center.z()) {
80 if (inner.min().x() >= center.x() && inner.min().y() >= center.y() && inner.max().z() <= center.z()) {
84 if (inner.max().x() <= center.x() && inner.max().y() <= center.y() && inner.min().z() >= center.z()) {
88 if (inner.min().x() >= center.x() && inner.max().y() <= center.y() && inner.min().z() >= center.z()) {
92 if (inner.max().x() <= center.x() && inner.min().y() >= center.y() && inner.min().z() >= center.z()) {
96 if (inner.min().x() >= center.x() && inner.min().y() >= center.y() && inner.min().z() >= center.z()) {
103 static auto child_bounds(
const math::volume& outer,
const std::uint32_t index) -> math::volume {
104 const auto min = outer.min();
105 const auto max = outer.max();
106 const auto center = (min + max) * 0.5f;
109 case 0:
return math::volume{min, center};
110 case 1:
return math::volume{math::vector3{center.x(), min.y(), min.z()}, math::vector3{max.x(), center.y(), center.z()}};
111 case 2:
return math::volume{math::vector3{min.x(), center.y(), min.z()}, math::vector3{center.x(), max.y(), center.z()}};
112 case 3:
return math::volume{math::vector3{center.x(), center.y(), min.z()}, math::vector3{max.x(), max.y(), center.z()}};
113 case 4:
return math::volume{math::vector3{min.x(), min.y(), center.z()}, math::vector3{center.x(), center.y(), max.z()}};
114 case 5:
return math::volume{math::vector3{center.x(), min.y(), center.z()}, math::vector3{max.x(), center.y(), max.z()}};
115 case 6:
return math::volume{math::vector3{min.x(), center.y(), center.z()}, math::vector3{center.x(), max.y(), max.z()}};
116 case 7:
return math::volume{center, max};
119 throw std::runtime_error(
"Invalid index");
122 std::vector<value_type> values;
123 std::array<id, children_count> children;
129 using value_type = Type;
130 using reference = value_type&;
131 using const_reference =
const value_type&;
146 _nodes.reserve(estimated_elements / 2);
147 _nodes.push_back(
node{});
154 auto insert(
const value_type& value,
const math::volume& bounds)
noexcept ->
void {
155 _insert(_root, _bounds, value, bounds, 0u);
158 auto intersections() -> std::vector<intersection> {
159 auto intersections = utility::make_reserved_vector<intersection>(_nodes.size());
161 _intersections(_root, _bounds, intersections);
163 intersections.shrink_to_fit();
165 return intersections;
168 auto inside(
const math::box& box) -> std::vector<inside_result> {
169 auto inside = std::vector<inside_result>{};
171 _inside(_root, _bounds, box, inside);
176 auto clear() ->
void {
179 _nodes[0].values.clear();
180 _nodes[0].children.fill(node::null);
183 template<
typename Fn>
184 requires (std::is_invocable_v<Fn, const math::volume&>)
185 auto for_each_volume(Fn&& fn) ->
void {
186 _for_each_volume(_root, _bounds, std::forward<Fn>(fn));
191 auto _insert(
const node::id node_id,
const math::volume& bounds,
const value_type& value,
const math::volume& value_bounds,
const std::size_t current_depth)
noexcept ->
void {
192 if (!bounds.contains(value_bounds)) {
196 if (_nodes[node_id].is_leaf()) {
197 if (_nodes[node_id].values.size() < threshold || current_depth >= depth) {
198 _nodes[node_id].push_back({value, value_bounds});
200 _split(node_id, bounds);
201 _insert(node_id, bounds, value, value_bounds, current_depth);
204 const auto quadrant = node::find_volume(bounds, value_bounds);
207 _insert(_nodes[node_id].child_at(*quadrant), node::child_bounds(bounds, *quadrant), value, value_bounds, current_depth + 1u);
209 _nodes[node_id].values.push_back({value, value_bounds});
214 auto _split(node::id node_id,
const math::volume& bounds) ->
void {
215 const auto first_child_id =
static_cast<node::id
>(_nodes.size());
217 _nodes.resize(_nodes.size() + node::children_count);
219 for (
auto i = 0u; i < node::children_count; ++i) {
220 _nodes[node_id].children[i] = first_child_id + i;
223 auto current_node_values = std::move(_nodes[node_id].values);
225 _nodes[node_id].values.clear();
227 for (
auto&& value : current_node_values) {
228 const auto quadrant = node::find_volume(bounds, value.bounds);
231 _nodes[first_child_id + *quadrant].values.push_back(std::move(value));
233 _nodes[node_id].values.push_back(std::move(value));
238 auto _intersections(node::id node_id,
const math::volume& bounds, std::vector<intersection>& intersections) ->
void {
239 for (
auto i : std::views::iota(0u, _nodes[node_id].values.size())) {
240 for (
auto j : std::views::iota(0u, i)) {
241 if (_nodes[node_id].values[i].bounds.intersects(_nodes[node_id].values[j].bounds)) {
242 intersections.push_back({_nodes[node_id].values[i].value, _nodes[node_id].values[j].value});
247 if (!_nodes[node_id].is_leaf()) {
248 for (
const auto& child_id : _nodes[node_id].children) {
249 for (
const auto& [value, bounds] : _nodes[node_id].values) {
250 _intersections_with_descendants(child_id, value, bounds, intersections);
254 for (
auto i : std::views::iota(0u, _nodes[node_id].children.size())) {
255 auto child_bounds = node::child_bounds(bounds, i);
257 _intersections(_nodes[node_id].child_at(i), child_bounds, intersections);
262 auto _intersections_with_descendants(node::id node_id,
const value_type& value,
const math::volume& value_bounds, std::vector<intersection>& intersections) ->
void {
263 for (
const auto& [node_value, bound] : _nodes[node_id].values) {
264 if (bound.intersects(value_bounds)) {
265 intersections.push_back({value, node_value});
269 if (!_nodes[node_id].is_leaf()) {
270 for (
const auto& child : _nodes[node_id].children) {
271 _intersections_with_descendants(child, value, value_bounds, intersections);
276 template<
typename Fn>
277 auto _for_each_volume(node::id node_id,
const math::volume& bounds, Fn&& fn) ->
void {
278 std::invoke(fn, bounds);
280 if (!_nodes[node_id].is_leaf()) {
281 for (
auto i = 0u; i < _nodes[node_id].children.size(); ++i) {
282 const auto child_id = _nodes[node_id].child_at(i);
283 const auto child_volume = node::child_bounds(bounds, i);
285 _for_each_volume(child_id, child_volume, std::forward<Fn>(fn));
290 auto _inside(node::id node_id,
const math::volume& bounds,
const math::box& box, std::vector<inside_result>& inside) ->
void {
297 for (
const auto& [value, value_bounds] : _nodes[node_id].values) {
299 inside.push_back({value, value_bounds});
304 if (!_nodes[node_id].is_leaf()) {
305 for (
auto i : std::views::iota(0u, _nodes[node_id].children.size())) {
306 auto child_id = _nodes[node_id].child_at(i);
307 auto child_bounds = node::child_bounds(bounds, i);
309 _inside(child_id, child_bounds, box, inside);
314 math::volume _bounds;
316 std::vector<node> _nodes;
Box / frustum-style volume intersection helpers.
boxf box
Default box alias.
Definition: box.hpp:131
Definition: octree.hpp:23
auto intersects(const volume_type &volume) const -> bool
Tests whether this box intersects a volume.
Definition: box.ipp:15
Definition: volume.hpp:14
Definition: octree.hpp:138
Definition: octree.hpp:133
Definition: octree.hpp:34