2024-08-29 15:18:15 +02:00
|
|
|
#!/usr/bin/python3
|
|
|
|
|
|
|
|
"""
|
|
|
|
SPVO = Sparse Voxel Octree
|
|
|
|
|
|
|
|
A Sparse Voxel Octree is a way of storing multidimensional data. (In this case it is 3D Data.)
|
|
|
|
It works similarly to a Sparse Voxel Matrix, but it is a bit compressed.
|
|
|
|
In a Sparse Voxel Octree, you have a root-node. The root node gets split up into 8 child nodes,
|
|
|
|
which can be all split up into 8 more child nodes which can be split up too and so on.
|
|
|
|
Thanks to this "compression", there are no zero values on places where there is just air unlike as in a normal matrix.
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
class Node:
|
2024-08-29 15:27:02 +02:00
|
|
|
def __init__(self, data=None):
|
2024-08-29 15:18:15 +02:00
|
|
|
self.children = [None] * 8
|
|
|
|
self.leaf = True
|
|
|
|
|
2024-08-29 15:27:02 +02:00
|
|
|
self.data = data
|
|
|
|
|
2024-08-29 15:18:15 +02:00
|
|
|
|
|
|
|
class SparseVoxelOctree:
|
2024-08-29 15:27:02 +02:00
|
|
|
def __init__(self):
|
2024-08-29 15:18:15 +02:00
|
|
|
self.root_node = Node()
|
|
|
|
self.origin = (0, 0, 0)
|
|
|
|
|
|
|
|
def _get_child_index(self, x, y, z, size):
|
2024-08-29 15:58:37 +02:00
|
|
|
"""
|
|
|
|
Get the index of a child.
|
|
|
|
Index: The position of the node in the node parent's children list.
|
|
|
|
"""
|
|
|
|
|
2024-08-29 15:18:15 +02:00
|
|
|
index = 0
|
|
|
|
|
2024-08-29 15:58:37 +02:00
|
|
|
if x >= size // 2: # x pos
|
|
|
|
# there are only 2 possible x coordinate values in a 2x2 cube, so we use the 1st bit in index for x
|
|
|
|
# xyz: the coordinates of the parent node
|
|
|
|
index |= 1 # set 1st bit to 1
|
|
|
|
x -= size // 2 # get parent x
|
|
|
|
|
|
|
|
# and the same for y and z
|
2024-08-29 15:18:15 +02:00
|
|
|
|
2024-08-29 15:58:37 +02:00
|
|
|
if y >= size // 2: # y pos
|
2024-08-29 15:18:15 +02:00
|
|
|
index |= 2
|
|
|
|
y -= size // 2
|
|
|
|
|
2024-08-29 15:58:37 +02:00
|
|
|
if z >= size // 2: # z pos
|
2024-08-29 15:18:15 +02:00
|
|
|
index |= 4
|
|
|
|
z -= size // 2
|
|
|
|
|
|
|
|
return index, x, y, z
|
|
|
|
|
|
|
|
def _extend_tree(self, x, y, z):
|
|
|
|
new_root = Node()
|
|
|
|
new_root.leaf = False
|
|
|
|
|
|
|
|
index, nx, ny, nz = self._get_child_index(x, y, z, 2)
|
|
|
|
new_root.children[index] = self.root_node
|
|
|
|
self.root_node = new_root
|
|
|
|
|
|
|
|
self.origin = (
|
|
|
|
self.origin[0] - (x >= 0) * 2 + 1,
|
|
|
|
self.origin[1] - (y >= 0) * 2 + 1,
|
|
|
|
self.origin[2] - (z >= 0) * 2 + 1
|
|
|
|
)
|
|
|
|
|
|
|
|
def _insert(self, node, x, y, z, size):
|
|
|
|
while not size == 1:
|
|
|
|
index, x, y, z = self._get_child_index(x, y, z, size)
|
|
|
|
|
|
|
|
if node.children[index] is None:
|
|
|
|
node.children[index] = Node()
|
|
|
|
|
|
|
|
size //= 2
|
|
|
|
|
|
|
|
node.leaf = True
|
|
|
|
|
|
|
|
def insert(self, x, y, z):
|
|
|
|
# move origin if necessary
|
|
|
|
while not (0 <= x < 2 ** 30 and 0 <= y < 2 ** 30 and 0 <= z < 2 ** 30):
|
|
|
|
self._extend_tree(x, y, z)
|
|
|
|
|
|
|
|
x, y, z = (
|
|
|
|
x - self.origin[0],
|
|
|
|
y - self.origin[1],
|
|
|
|
z - self.origin[2]
|
|
|
|
)
|
|
|
|
|
|
|
|
self._insert(self.root_node, x, y, z, 2 ** 30)
|