1#ifndef LIBSBX_CONTAINERS_OCTREE_HPP_
2#define LIBSBX_CONTAINERS_OCTREE_HPP_
4#include <range/v3/all.hpp>
6#include <libsbx/utility/enum.hpp>
8#include <libsbx/math/vector2.hpp>
9#include <libsbx/math/vector3.hpp>
10#include <libsbx/math/uuid.hpp>
11#include <libsbx/math/volume.hpp>
12#include <libsbx/math/box.hpp>
14#include <libsbx/containers/static_vector.hpp>
16namespace sbx::containers {
18template<
typename Type, std::
size_t Threshold = 16u, std::
size_t Depth = 8u>
21 inline static constexpr auto threshold = Threshold;
22 inline static constexpr auto depth = Depth;
26 using id = std::uint32_t;
33 inline constexpr static auto null =
static_cast<id>(-1);
36 : children{null, null, null, null, null, null, null, null} { }
38 auto is_leaf() const noexcept ->
bool {
39 return children[0u] == null;
42 auto child_at(
const std::size_t index)
const noexcept ->
id {
43 return children[index];
46 auto push_back(
const value_type& value)
noexcept ->
void {
47 values.push_back(value);
50 auto push_back(value_type&& value)
noexcept ->
void {
51 values.push_back(std::move(value));
54 static auto find_volume(
const math::volume& outer,
const math::volume& inner)
noexcept -> std::optional<std::uint32_t> {
55 auto center = outer.center();
57 if (!outer.contains(inner)) {
61 if (inner.max().x() <= center.x() && inner.max().y() <= center.y() && inner.max().z() <= center.z()) {
65 if (inner.min().x() >= center.x() && inner.max().y() <= center.y() && inner.max().z() <= center.z()) {
69 if (inner.max().x() <= center.x() && inner.min().y() >= center.y() && inner.max().z() <= center.z()) {
73 if (inner.min().x() >= center.x() && inner.min().y() >= center.y() && inner.max().z() <= center.z()) {
77 if (inner.max().x() <= center.x() && inner.max().y() <= center.y() && inner.min().z() >= center.z()) {
81 if (inner.min().x() >= center.x() && inner.max().y() <= center.y() && inner.min().z() >= center.z()) {
85 if (inner.max().x() <= center.x() && inner.min().y() >= center.y() && inner.min().z() >= center.z()) {
89 if (inner.min().x() >= center.x() && inner.min().y() >= center.y() && inner.min().z() >= center.z()) {
96 static auto child_bounds(
const math::volume& outer,
const std::uint32_t index) -> math::volume {
97 const auto min = outer.min();
98 const auto max = outer.max();
99 const auto center = (min + max) * 0.5f;
102 case 0:
return math::volume{min, center};
103 case 1:
return math::volume{math::vector3{center.x(), min.y(), min.z()}, math::vector3{max.x(), center.y(), center.z()}};
104 case 2:
return math::volume{math::vector3{min.x(), center.y(), min.z()}, math::vector3{center.x(), max.y(), center.z()}};
105 case 3:
return math::volume{math::vector3{center.x(), center.y(), min.z()}, math::vector3{max.x(), max.y(), center.z()}};
106 case 4:
return math::volume{math::vector3{min.x(), min.y(), center.z()}, math::vector3{center.x(), center.y(), max.z()}};
107 case 5:
return math::volume{math::vector3{center.x(), min.y(), center.z()}, math::vector3{max.x(), center.y(), max.z()}};
108 case 6:
return math::volume{math::vector3{min.x(), center.y(), center.z()}, math::vector3{center.x(), max.y(), max.z()}};
109 case 7:
return math::volume{center, max};
112 throw std::runtime_error(
"Invalid index");
115 std::vector<value_type> values;
116 std::array<id, 8u> children;
132 using value_type = Type;
133 using reference = value_type&;
134 using const_reference =
const value_type&;
139 _nodes.push_back(
node{});
142 auto insert(
const value_type& value,
const math::volume& bounds)
noexcept ->
void {
143 _insert(_root, _bounds, value, bounds, 0u);
146 auto intersections() -> std::vector<intersection> {
147 auto intersections = std::vector<intersection>{};
149 _intersections(_root, _bounds, intersections);
151 return intersections;
154 auto inside(
const math::box& box) -> std::vector<inside_result> {
155 auto inside = std::vector<inside_result>{};
157 _inside(_root, _bounds, box, inside);
162 auto clear() ->
void {
166 template<
typename Fn>
167 requires (std::is_invocable_v<Fn, const math::volume&>)
168 auto for_each_volume(Fn&& fn) ->
void {
169 _for_each_volume(_root, _bounds, std::forward<Fn>(fn));
174 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 {
175 if (!bounds.contains(value_bounds)) {
179 if (_nodes[node_id].is_leaf()) {
180 if (_nodes[node_id].values.size() < threshold || current_depth >= depth) {
181 _nodes[node_id].push_back({value, value_bounds});
183 _split(node_id, bounds);
184 _insert(node_id, bounds, value, value_bounds, current_depth);
187 const auto quadrant = node::find_volume(bounds, value_bounds);
190 _insert(_nodes[node_id].child_at(*quadrant), node::child_bounds(bounds, *quadrant), value, value_bounds, current_depth + 1u);
192 _nodes[node_id].values.push_back({value, value_bounds});
197 auto _split(node::id node_id,
const math::volume& bounds) ->
void {
198 const auto current_size = _nodes.size();
200 for (
auto&& [i, child] : ranges::views::enumerate(_nodes[node_id].children)) {
201 child =
static_cast<node::id
>(current_size + i);
204 _nodes.insert(_nodes.end(), _nodes[node_id].children.size(),
node{});
206 auto new_values = std::vector<typename node::value_type>{};
208 for (
const auto& [value, child_bounds] : _nodes[node_id].values) {
209 const auto quadrant = node::find_volume(bounds, child_bounds);
212 const auto child_id = _nodes[node_id].child_at(*quadrant);
213 _nodes[child_id].values.push_back({value, child_bounds});
215 new_values.push_back({value, child_bounds});
219 _nodes[node_id].values = std::move(new_values);
222 auto _intersections(node::id node_id,
const math::volume& bounds, std::vector<intersection>& intersections) ->
void {
223 for (
auto i : std::views::iota(0u, _nodes[node_id].values.size())) {
224 for (
auto j : std::views::iota(0u, i)) {
225 if (_nodes[node_id].values[i].bounds.intersects(_nodes[node_id].values[j].bounds)) {
226 intersections.push_back({_nodes[node_id].values[i].value, _nodes[node_id].values[j].value});
231 if (!_nodes[node_id].is_leaf()) {
232 for (
const auto& child_id : _nodes[node_id].children) {
233 for (
const auto& [value, bounds] : _nodes[node_id].values) {
234 _intersections_with_descendants(child_id, value, bounds, intersections);
238 for (
auto i : std::views::iota(0u, _nodes[node_id].children.size())) {
239 auto child_bounds = node::child_bounds(bounds, i);
241 _intersections(_nodes[node_id].child_at(i), child_bounds, intersections);
246 auto _intersections_with_descendants(node::id node_id,
const value_type& value,
const math::volume& value_bounds, std::vector<intersection>& intersections) ->
void {
247 for (
const auto& [node_value, bound] : _nodes[node_id].values) {
248 if (bound.intersects(value_bounds)) {
249 intersections.push_back({value, node_value});
253 if (!_nodes[node_id].is_leaf()) {
254 for (
const auto& child : _nodes[node_id].children) {
255 _intersections_with_descendants(child, value, value_bounds, intersections);
260 template<
typename Fn>
261 auto _for_each_volume(node::id node_id,
const math::volume& bounds, Fn&& fn) ->
void {
262 std::invoke(fn, bounds);
264 if (!_nodes[node_id].is_leaf()) {
265 for (
auto i = 0u; i < _nodes[node_id].children.size(); ++i) {
266 const auto child_id = _nodes[node_id].child_at(i);
267 const auto child_volume = node::child_bounds(bounds, i);
269 _for_each_volume(child_id, child_volume, std::forward<Fn>(fn));
274 auto _inside(node::id node_id,
const math::volume& bounds,
const math::box& box, std::vector<inside_result>& inside) ->
void {
276 if (!box.intersects(bounds)) {
281 for (
const auto& [value, value_bounds] : _nodes[node_id].values) {
282 if (box.intersects(value_bounds)) {
283 inside.push_back({value, value_bounds});
288 if (!_nodes[node_id].is_leaf()) {
289 for (
auto i : std::views::iota(0u, _nodes[node_id].children.size())) {
290 auto child_id = _nodes[node_id].child_at(i);
291 auto child_bounds = node::child_bounds(bounds, i);
293 _inside(child_id, child_bounds, box, inside);
298 math::volume _bounds;
300 std::vector<node> _nodes;
Definition: octree.hpp:19
Definition: volume.hpp:11
Definition: octree.hpp:127
Definition: octree.hpp:122
Definition: octree.hpp:28