옥트리 C++의 간단한 구현

Octree는 3차원 공간에서 객체를 효율적으로 저장하고 쿼리하기 위해 사용되는 공간 분할 데이터 구조입니다.
3차원 공간을 8개의 동일한 크기의 하위 큐브(8개의 하위 노드)로 나눕니다. 각 하위 큐브는 8개의 하위 노드로 더 세분화될 수 있습니다.

옥트리의 구성과 동작은 다음과 같다.
    옥트리의 구성: 처음에는 전체 3차원 공간이 루트 노드인 큰 큐브로 나누어진다. 그런 다음 루트 노드를 8개의 하위 노드로 재귀적으로 분할합니다. 각 하위 노드는 하위 큐브를 나타냅니다. 이 프로세스는 일부 종료 조건(예: 최대 깊이 도달 또는 하위 큐브의 개체 수가 임계값 도달)이 충족될 때까지 반복적으로 계속됩니다.
    개체 삽입: 옥트리에 개체를 삽입하려면 해당 개체가 있는 하위 큐브를 찾아야 합니다. 루트 노드부터 시작하여 적절한 자식 노드를 찾을 때까지 객체의 위치에 따라 자식 노드를 순회합니다. 하위 노드가 없으면 새 하위 노드를 만듭니다. 이 하위 노드에 개체를 삽입합니다. 자식 노드가 종료 조건에 도달한 경우 특정 상황(예: 노드에 개체를 저장하거나 자식 노드를 추가로 세분화하는 등)에 따라 처리됩니다.
    쿼리 개체: 옥트리의 개체를 쿼리하려면 루트 노드에서 시작하여 쿼리 조건에 따라 하위 노드를 순회해야 하는지 여부를 결정할 수 있습니다. 쿼리 조건이 자식 노드의 범위와 겹칠 경우 해당 자식 노드를 재귀적으로 순회합니다. 쿼리 조건이 하위 노드와 겹치지 않는 경우 해당 하위 노드를 건너뛸 수 있습니다.

옥트리의 각 노드는 큐브의 볼륨 요소를 나타내며 각 노드에는 8개의 하위 노드가 있습니다. 이 8개의 하위 노드가 나타내는 볼륨 요소는 상위 노드의 볼륨에 추가됩니다.

옥트리의 노드는 세 가지 범주로 나눌 수 있습니다:
    회색 노드: 해당 큐브가 검색 요소로 채워져 있습니다.
    흰색 노드: 해당 큐브에 검색 요소 내용이 없습니다.
    검정색 노드: 해당 큐브가 검색 요소로 완전히 채워져 있습니다. .

Octree 알고리즘 아이디어
(1) 최대 재귀 깊이를 설정합니다.
(2) 장면의 최대 크기를 찾고 이 크기로 첫 번째 큐브를 만듭니다.
(3) 아이덴티티 요소를 포함할 수 있고 하위 노드가 없는 큐브에 순차적으로 놓습니다.
(4) 최대 재귀 깊이에 도달하지 못한 경우 이를 8개의 동일한 부분으로 세분화한 후 큐브에 포함된 모든 단위 요소를 8개의 하위 큐브에 할당합니다.
(5) 하위 큐브에 할당된 단위 요소의 수가 0이 아니고 상위 큐브와 동일한 것으로 확인되면 하위 큐브는 공간 분할 이론에 따라 세분화되기 때문에 하위 큐브는 세분화를 중지합니다. 할당된 공간은 적어야 하며, 같은 숫자라면 어떻게 잘라도 그 숫자는 그대로이므로 무한 자르기가 발생하게 됩니다.
(6) 최대 재귀 깊이에 도달할 때까지 3을 반복합니다.

최대 재귀 깊이를 결정하는 방법
최대 재귀 깊이는 해상도를 설정하여 계산할 수 있습니다. 해상도는 최하위 옥트리의 가장 작은 복셀 크기를 나타냅니다. 따라서 옥트리의 깊이는 해상도와 포인트의 함수입니다. 클라우드 데이터.

옥트리 기반 복셀 최근접 이웃 검색 복셀
    최근
    접 이웃 검색은 쿼리 포인트가 위치한 복셀 내 다른 포인트의 인덱스를 반환하므로 검색 포인트와 검색 결과 사이의 거리는 옥트리 매개변수의 분해능에 따라 달라집니다.

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");
        }
    }
}

이 코드는 인터넷에서 가져온 것입니다. 잘 이해가 되지 않습니다. 얼핏 보면 이진 트리와 확실히 다릅니다. 이진 트리의 노드 구조에는 2개의 노드 구조 포인터가 포함되어 있는 반면 이 트리에는 8개의 노드 구조 포인터가 포함되어 있습니다. ;

달려가서 살펴보세요.

 

 

길이, 너비, 높이가 2인 외부 경계 상자를 입력합니다.

 

 

 

(1, 1, 1)을 찾아 바로 종료하세요.

 

몇 가지를 더 검색하면 결과는 다음과 같습니다.

 

 

 

순회, 출력이 매우 큽니다.

 

아직은 잘 이해가 되지 않습니다. 코드를 실행해 보면서 더 잘 이해하도록 노력하겠습니다. 다음 번에 계속하겠습니다.

Supongo que te gusta

Origin blog.csdn.net/bcbobo21cn/article/details/133308066
Recomendado
Clasificación