Simple implementation of octree C++

Octree is a space segmentation data structure used for efficient storage and query of objects in three-dimensional space.
Divide the three-dimensional space into eight equal-sized subcubes (eight child nodes), each of which can be further subdivided into eight child nodes, and so on.

The construction and operation of the octree are as follows:
    Construction of the octree: At the beginning, the entire three-dimensional space is divided into a large cube as the root node. Then, recursively split the root node into eight sub-nodes, each sub-node representing a sub-cube. This process continues recursively until some termination condition is met (such as the maximum depth being reached or the number of objects in the subcube reaching a threshold).
    Inserting an object: To insert an object into the octree, you need to find the subcube where the object is located. Starting from the root node, the child nodes are traversed according to the position of the object until the appropriate child node is found. If the child node does not exist, create a new child node. Insert the object into this child node. If the child node has reached the termination condition, it is processed according to the specific situation (such as storing the object in the node, or further subdividing the child node).
    Query objects: To query objects in the octree, you can start from the root node and determine whether you need to traverse the child nodes according to the query conditions. If the query condition overlaps with the range of a child node, the child node is traversed recursively. If the query condition does not overlap with a child node, the child node can be skipped.

Each node of the octree represents a volume element of a cube, and each node has eight child nodes. The volume elements represented by these eight child nodes add up to the volume of the parent node.

The nodes of the octree can be divided into three categories:
    gray node: its corresponding cube is occupied by search elements;
    white node: its corresponding cube has no search element content;
    black node: its corresponding cube is entirely occupied by search elements. occupied.

Octree algorithm idea
(1). Set the maximum recursion depth.
(2). Find the maximum size of the scene and build the first cube with this size.
(3). Sequentially drop the identity elements into a cube that can be contained and has no child nodes.
(4). If the maximum recursion depth is not reached, subdivide it into eight equal parts, and then allocate all the unit elements contained in the cube to the eight sub-cubes.
(5). If it is found that the number of unit elements allocated to the sub-cube is not zero and is the same as the parent cube, the sub-cube will stop subdividing, because according to the space division theory, the subdivided space is allocated It must be less. If it is the same number, no matter how you cut it, the number will still be the same, which will lead to infinite cutting.
(6). Repeat 3 until the maximum recursion depth is reached.

How to determine the maximum recursion depth.
The maximum recursion depth can be calculated by setting the resolution. The resolution describes the size of the smallest voxel of the lowest level octree. Therefore, the depth of the octree is a function of the resolution and point cloud data.

Voxel nearest neighbor search based on octree
    Voxel
    nearest neighbor search returns the index of other points in the voxel where the query point is located. Therefore, the distance between the search point and the search result depends on the resolution of the octree parameter

Take a look at the simple implementation in C++;

#include <iostream>
using namespace std;//定义八叉树节点类
template<class T>
struct OctreeNode
{
    T data; //节点数据
    T xmin,xmax; //节点坐标,即六面体个顶点的坐标
    T ymin,ymax;
    T zmin,zmax;
    OctreeNode <T> *top_left_front,*top_left_back; //该节点的个子结点
    OctreeNode <T> *top_right_front,*top_right_back;
    OctreeNode <T> *bottom_left_front,*bottom_left_back;
    OctreeNode <T> *bottom_right_front,*bottom_right_back;
    OctreeNode //节点类
        (T nodeValue = T(),
        T xminValue = T(),T xmaxValue = T(),
        T yminValue = T(),T ymaxValue = T(),
        T zminValue = T(),T zmaxValue = T(),
        OctreeNode<T>* top_left_front_Node = NULL,
        OctreeNode<T>* top_left_back_Node = NULL,
        OctreeNode<T>* top_right_front_Node = NULL,
        OctreeNode<T>* top_right_back_Node = NULL,
        OctreeNode<T>* bottom_left_front_Node = NULL,
        OctreeNode<T>* bottom_left_back_Node = NULL,
        OctreeNode<T>* bottom_right_front_Node = NULL,
        OctreeNode<T>* bottom_right_back_Node = NULL )
        :data(nodeValue),
        xmin(xminValue),xmax(xmaxValue),
        ymin(yminValue),ymax(ymaxValue),
        zmin(zminValue),zmax(zmaxValue),
        top_left_front(top_left_front_Node),
        top_left_back(top_left_back_Node),
        top_right_front(top_right_front_Node),
        top_right_back(top_right_back_Node),
        bottom_left_front(bottom_left_front_Node),
        bottom_left_back(bottom_left_back_Node),
        bottom_right_front(bottom_right_front_Node),
        bottom_right_back(bottom_right_back_Node){}
};
//创建八叉树
template <class T>
void createOctree(OctreeNode<T> * &root,int maxdepth,double xmin,double xmax,double ymin,double ymax,double zmin,double zmax)
{
    //cout<<"处理中,请稍候……"<<endl;
    maxdepth=maxdepth-1; //每递归一次就将最大递归深度-1
    if(maxdepth>=0)
    {
        root=new OctreeNode<T>();
        root->data = 9; //为节点赋值,可以存储节点信息,如物体可见性。由于是简单实现八叉树功能,简单赋值为。
        root->xmin=xmin; //为节点坐标赋值
        root->xmax=xmax;
        root->ymin=ymin;
        root->ymax=ymax;
        root->zmin=zmin;
        root->zmax=zmax;
        double xm=(xmax-xmin)/2;//计算节点个维度上的半边长
        double ym=(ymax-ymin)/2;
        double zm=(zmax-zmin)/2;
        //递归创建子树,根据每一个节点所处(是几号节点)的位置决定其子结点的坐标。
        createOctree(root->top_left_front,maxdepth,xmin,xmax-xm,ymax-ym,ymax,zmax-zm,zmax);
        createOctree(root->top_left_back,maxdepth,xmin,xmax-xm,ymin,ymax-ym,zmax-zm,zmax);
        createOctree(root->top_right_front,maxdepth,xmax-xm,xmax,ymax-ym,ymax,zmax-zm,zmax);
        createOctree(root->top_right_back,maxdepth,xmax-xm,xmax,ymin,ymax-ym,zmax-zm,zmax);
        createOctree(root->bottom_left_front,maxdepth,xmin,xmax-xm,ymax-ym,ymax,zmin,zmax-zm);
        createOctree(root->bottom_left_back,maxdepth,xmin,xmax-xm,ymin,ymax-ym,zmin,zmax-zm);
        createOctree(root->bottom_right_front,maxdepth,xmax-xm,xmax,ymax-ym,ymax,zmin,zmax-zm);
        createOctree(root->bottom_right_back,maxdepth,xmax-xm,xmax,ymin,ymax-ym,zmin,zmax-zm);
    }
}
int i=1;
//先序遍历八叉树
template <class T>
void preOrder( OctreeNode<T> * & p)
{
    if(p)
    {
        cout<<i<<".当前节点的值为:"<<p->data<<"\n坐标为:";
        cout<<" xmin: "<<p->xmin<<" xmax: "<<p->xmax;
        cout<<" ymin: "<<p->ymin<<" ymax: "<<p->ymax;
        cout<<" zmin: "<<p->zmin<<" zmax: "<<p->zmax;
        i+=1;
        cout<<endl;
        preOrder(p->top_left_front);
        preOrder(p->top_left_back);
        preOrder(p->top_right_front);
        preOrder(p->top_right_back);
        preOrder(p->bottom_left_front);
        preOrder(p->bottom_left_back);
        preOrder(p->bottom_right_front);
        preOrder(p->bottom_right_back);
        cout<<endl;
    }
}
//求八叉树的深度
template<class T>
int depth(OctreeNode<T> *& p)
{
    if(p == NULL)
        return -1;
    int h = depth(p->top_left_front);
    return h+1;
}
//计算单位长度,为查找点做准备
int cal(int num)
{
    int result=1;
    if(1==num)
        result=1;
    else
    {
        for(int i=1;i<num;i++)
            result=2*result;
    }
    return result;
}
//查找点
int maxdepth=0;
int times=0;
static double xmin=0,xmax=0,ymin=0,ymax=0,zmin=0,zmax=0;
int tmaxdepth=0;
double txm=1,tym=1,tzm=1;
template<class T>
void find(OctreeNode<T> *& p,double x,double y,double z)
{
    double xm=(p->xmax-p->xmin)/2;
    double ym=(p->ymax-p->ymin)/2;
    double zm=(p->zmax-p->zmin)/2;
    times++;
    if(x>xmax || x<xmin || y>ymax || y<ymin || z>zmax || z<zmin)
    {
        cout<<"该点不在场景中!"<<endl;
        return;
    }
    if(x<=p->xmin+txm && x>=p->xmax-txm && y<=p->ymin+tym && y>=p->ymax-tym && z<=p->zmin+tzm && z>=p->zmax-tzm )
    {
        cout<<endl<<"找到该点!"<<"该点位于"<<endl;
        cout<<" xmin: "<<p->xmin<<" xmax: "<<p->xmax;
        cout<<" ymin: "<<p->ymin<<" ymax: "<<p->ymax;
        cout<<" zmin: "<<p->zmin<<" zmax: "<<p->zmax;
        cout<<"节点内!"<<endl;
        cout<<"共经过"<<times<<"次递归!"<<endl;
    }
    else if(x<(p->xmax-xm) && y<(p->ymax-ym) && z<(p->zmax-zm))
    {
        cout<<"当前经过节点坐标:"<<endl;
        cout<<" xmin: "<<p->xmin<<" xmax: "<<p->xmax;
        cout<<" ymin: "<<p->ymin<<" ymax: "<<p->ymax;
        cout<<" zmin: "<<p->zmin<<" zmax: "<<p->zmax;
        cout<<endl;
        find(p->bottom_left_back,x,y,z);
    }
    else if(x<(p->xmax-xm) && y<(p->ymax-ym) && z>(p->zmax-zm))
    {
        cout<<"当前经过节点坐标:"<<endl;
        cout<<" xmin: "<<p->xmin<<" xmax: "<<p->xmax;
        cout<<" ymin: "<<p->ymin<<" ymax: "<<p->ymax;
        cout<<" zmin: "<<p->zmin<<" zmax: "<<p->zmax;
        cout<<endl;
        find(p->top_left_back,x,y,z);
    }
    else if(x>(p->xmax-xm) && y<(p->ymax-ym) && z<(p->zmax-zm))
    {
        cout<<"当前经过节点坐标:"<<endl;
        cout<<" xmin: "<<p->xmin<<" xmax: "<<p->xmax;
        cout<<" ymin: "<<p->ymin<<" ymax: "<<p->ymax;
        cout<<" zmin: "<<p->zmin<<" zmax: "<<p->zmax;
        cout<<endl;
        find(p->bottom_right_back,x,y,z);
    }
    else if(x>(p->xmax-xm) && y<(p->ymax-ym) && z>(p->zmax-zm))
    {
        cout<<"当前经过节点坐标:"<<endl;
        cout<<" xmin: "<<p->xmin<<" xmax: "<<p->xmax;
        cout<<" ymin: "<<p->ymin<<" ymax: "<<p->ymax;
        cout<<" zmin: "<<p->zmin<<" zmax: "<<p->zmax;
        cout<<endl;
        find(p->top_right_back,x,y,z);
    }
    else if(x<(p->xmax-xm) && y>(p->ymax-ym) && z<(p->zmax-zm))
    {
        cout<<"当前经过节点坐标:"<<endl;
        cout<<" xmin: "<<p->xmin<<" xmax: "<<p->xmax;
        cout<<" ymin: "<<p->ymin<<" ymax: "<<p->ymax;
        cout<<" zmin: "<<p->zmin<<" zmax: "<<p->zmax;
        cout<<endl;
        find(p->bottom_left_front,x,y,z);
    }
    else if(x<(p->xmax-xm) && y>(p->ymax-ym) && z>(p->zmax-zm))
    {
        cout<<"当前经过节点坐标:"<<endl;
        cout<<" xmin: "<<p->xmin<<" xmax: "<<p->xmax;
        cout<<" ymin: "<<p->ymin<<" ymax: "<<p->ymax;
        cout<<" zmin: "<<p->zmin<<" zmax: "<<p->zmax;
        cout<<endl;
        find(p->top_left_front,x,y,z);
    }
    else if(x>(p->xmax-xm) && y>(p->ymax-ym) && z<(p->zmax-zm))
    {
        cout<<"当前经过节点坐标:"<<endl;
        cout<<" xmin: "<<p->xmin<<" xmax: "<<p->xmax;
        cout<<" ymin: "<<p->ymin<<" ymax: "<<p->ymax;
        cout<<" zmin: "<<p->zmin<<" zmax: "<<p->zmax;
        cout<<endl;
        find(p->bottom_right_front,x,y,z);
    }
    else if(x>(p->xmax-xm) && y>(p->ymax-ym) && z>(p->zmax-zm))
    {
        cout<<"当前经过节点坐标:"<<endl;
        cout<<" xmin: "<<p->xmin<<" xmax: "<<p->xmax;
        cout<<" ymin: "<<p->ymin<<" ymax: "<<p->ymax;
        cout<<" zmin: "<<p->zmin<<" zmax: "<<p->zmax;
        cout<<endl;
        find(p->top_right_front,x,y,z);
    }
}
//main函数
int main ()
{
    OctreeNode<double> * rootNode = NULL;
    int choiced = 0;
    while(true)
    {
        system("cls");
        cout<<"请选择操作:\n";
        cout<<"1.创建八叉树 2.先序遍历八叉树\n";
        cout<<"3.查看树深度 4.查找节点   \n";
        cout<<"0.退出\n\n";
        cin>>choiced;
        if(choiced == 0)
            return 0;
        else if(choiced == 1)
        {
            system("cls");
            cout<<"请输入最大递归深度:"<<endl;
            cin>>maxdepth;
            cout<<"请输入外包盒坐标,顺序如下:xmin,xmax,ymin,ymax,zmin,zmax"<<endl;
            cin>>xmin>>xmax>>ymin>>ymax>>zmin>>zmax;
            if(maxdepth>=0 || xmax>xmin || ymax>ymin || zmax>zmin || xmin>0 || ymin>0 ||zmin>0)
            {
                tmaxdepth=cal(maxdepth);
                txm=(xmax-xmin)/tmaxdepth;
                tym=(ymax-ymin)/tmaxdepth;
                tzm=(zmax-zmin)/tmaxdepth;
                createOctree(rootNode,maxdepth,xmin,xmax,ymin,ymax,zmin,zmax);
            }
            else
            {
                cout<<"输入错误!";
                return 0;
            }
        }
        else if(choiced == 2)
        {
            system("cls");
            cout<<"先序遍历八叉树结果:\n";
            i=1;
            preOrder(rootNode);
            cout<<endl;
            system("pause");
        }
        else if(choiced == 3)
        {
            system("cls");
            int dep = depth(rootNode);
            cout<<"此八叉树的深度为"<<dep+1<<endl;
            system("pause");
        }
        else if(choiced == 4)
        {
            system("cls");
            cout<<"请输入您希望查找的点的坐标,顺序如下:x,y,z\n";
            double x,y,z;
            cin>>x>>y>>z;
            times=0;
            cout<<endl<<"开始搜寻该点……"<<endl;
            find(rootNode,x,y,z);
            system("pause");
        }
        else
        {
            system("cls");
            cout<<"\n\n错误选择!\n";
            system("pause");
        }
    }
}

This code comes from the Internet; I don’t understand it very well; at first glance, it is obviously different from a binary tree. The node structure of a binary tree contains 2 node structure pointers, while this one contains 8 node structure pointers;

Run and take a look;

 

 

Enter an outer bounding box with length, width and height of 2.

 

 

 

Find (1, 1, 1) and exit directly.

 

Search a few more and the output is as follows:

 

 

 

Traversal, the output is very large;

 

I don’t quite understand it yet, I’ll try to understand it better by running the code, and I’ll continue next time;

Guess you like

Origin blog.csdn.net/bcbobo21cn/article/details/133308066