八叉树模型(体素)分解简易程序


八叉树

 

维基释义:八叉树(Octree)是一种用于描述三维空间的数据结构。八叉树的每个节点表示一个正方体的体积元素,每个节点有八个子节点,

      这八个子节点所表示的体积元素加在一起就等于父节点的体积。一般中心点作为节点的分叉中心。


百度百科释义八叉树(Octree)的定义是:若不为空树的话,树中任一节点的子节点恰好只会有八个,或零个,也就是子节点不会有0与8以外的数目。

那么,这要用来做什么?想象一个立方体, 我们最少可以切成多少个相同等分的小立方体?答案就是8个。再想象我们有一个房间,房间里某个角落藏着一

枚金币,我们想很快的把金币找出来,聪明的你会怎 么做?我们可以把房间当成一个立方体,先切成八个小立方体,然后排除掉没有放任何东西的小立方体,再把有可能藏金币的小立方体继续切八等份….如此下去, 平均在Log8(房间内的所有物品数)的时间内就可找到金币。因此,八叉树就是用在3D空间中的场景管理,可以很快地知道物体在3D场景中的位置,或侦测 与其它物体是否有碰撞以及是否在可视范围内。

  八叉树是一种用于描述三维空间的树状数据结构。八叉树的每个节点表示一个正方体的体积元素,每个节点有八个子节点,将八个子节点所表示的体积元素加在一起就等于父节点的体积。

 


 实现八叉树的原理

  (1). 设定最大递归深度。
  (2). 找出场景的最大尺寸,并以此尺寸建立第一个立方体。
  (3). 依序将单位元元素丢入能被包含且没有子节点的立方体。
  (4). 若没达到最大递归深度,就进行细分八等份,再将该立方体所装的单位元元素全部分担给八个子立方体。
  (5). 若发现子立方体所分配到的单位元元素数量不为零且跟父立方体是一样的,则该子立方体停止细分,因为跟据空间分割理论,

细分的空间所得到的分配必定较少,若是一样数目,则再怎么切数目还是一样,会造成无穷切割的情形。
  (6). 重复3,直到达到最大递归深度。

 


八叉树三维数据结构 

(一)   基本原理     

用八叉树来表示三维形体,并研究在这种表示下的各种操作及应用是在进入80年代后才比较全面地开展起来的。这种方法,既可以看成是四叉树方法在三维空间的推

广,也可以认为是用三维体素阵列表示形体方法的一种改进。    

八叉树的逻辑结构如下:

假设要表示的形体V可以放在一个充分大的正方体C内,C的边长为2n,形体V=C,它的八叉树可以用以下的递归方法来定义:

八 叉树的每个节点与C的一个子立方体对应,树根与C本身相对应,如果V=C,那么V的八叉树仅有树根,如果V≠C,则将C等分为八个子立方体,每个子立方体

与树根的一个子节点相对应。只要某个子立方体不是完全空白或完全为V所占据,就要被八等分(图2-5-1),从而对应的节点也就有了八个子节点。这样的递

归判断、分割一直要进行到节点所对应的立方体或是完全空白,或是完全为V占据,或是其大小已是预先定义的体素大小,并且对它与V之交作一定的“舍入”,使

体素或认为是空白的,或认为是V占据的。

 

 

如此所生成的八叉树上的节点可分为三类:

灰节点,它对应的立方体部分地为V所占据;  

白节点,它所对应的立方体中无V的内容;

黑节点,它所对应的立方体全为V所占据。

后两类又称为叶结点。形体V关于C的八叉树的逻辑结构是这样的:它是一颗树,其上的节点要么是叶节点,要么就是有八个子节点的灰节点。根节点与C相对应,

其它节点与C的某个子立方体相对应。     

 

因为八叉树的结构与四叉树的结构是如此的相似,所以八叉树的存贮结构方式可以完全沿用四叉树的有关方法。因而,根据不同的存贮方式,八叉树也可以分别称为

常规的、线性的、一对八的八叉树等等。

 

      另外,由于这种方法充分利用了形体在空上的相关性,因此,一般来说,它所占用的存贮空间要比三维体素阵列的少。但是实际上它还是使用了相当多的存贮,这并 不是八叉树的主要优点。这一方法的主要优点在于可以非常方便地实现有广泛用途的集合运算(例如可以求两个物体的并、交、差等运算),而这些恰是其它表示方 法比较难以处理或者需要耗费许多计算资源的地方。不仅如此,由于这种方法的有序性及分层性,因而对显示精度和速度的平衡、隐线和隐面的消除等,带来了很大 的方便,特别有用。

 

(二)八叉树的存贮结构     

 

八叉树有三种不同的存贮结构,分别是规则方式、线性方式以及一对八方式。相应的八叉树也分别称为规则八叉树、线性八叉树以及一对八式八叉树。不同的存贮结构

的空间利用率及运算操作的方便性是不同的。分析表明,一对八式八叉树优点更多一些。 

 

1、规则八叉树

 

      规则八叉树的存贮结构用一个有九个字段的记录来表示树中的每个结点。其中一个字段用来描述该结点的特性(在目前假定下,只要描述它是灰、白、黑三类结点中 哪一类即可),其余的八个字段用来作为存放指向其八个子结点的指针。这是最普遍使用的表示树形数据的存贮结构方式。

 

      规则八叉树缺陷较多,最大的问题是指针占用了大量的空间。假定每个指针要用两个字节表示,而结点的描述用一个字节,那么存放指针要占总的存贮量的94%。

因此,这种方法虽然十分自然,容易掌握,但在存贮空间的使用率方面不很理想。

 

2、线性八叉树

 

      线性八叉树注重考虑如何提高空间利用率。用某一预先确定的次序遍历八叉树(例如以深度第一的方式),将八叉树转换成一个线性表(图2-5-2),表的每个 元素与一个结点相对应。对于结点的描述可以丰富一点,例如用适当的方式来说明它是否为叶结点,如果不是叶结点时还可用其八个子结点值的平均值作为非叶结点 的值等等。这样,可以在内存中以紧凑的方式来表示线性表,可以不用指针或者仅用一个指针表示即可。

 

 

3、一对八式的八叉树

 

      一个非叶结点有八个子结点,为了确定起见,将它们分别标记为0,1,2,3,4,5,6,7。从上面的介绍可以看到,如果一个记录与一个结点相对应,那么 在这个记录中描述的是这个结点的八个子结点的特性值。而指针给出的则是该八个子结点所对应记录的存放处,而且还隐含地假定了这些子结点记录存放的次序。也 就是说,即使某个记录是不必要的(例如,该结点已是叶结点),那么相应的存贮位置也必须空闲在那里(图2-5-3),以保证不会错误地存取到其它同辈结点 的记录。这样当然会有一定的浪费,除非它是完全的八叉树,即所有的叶结点均在同一层次出现,而在该层次之上的所有层中的结点均为非叶结点。

 

 

为了克服这种缺陷,有两条途径可以采纳。一是增加计算量,在记录中增加一定的信息,使计算工作适当减少或者更方便。

(转自:http://blog.csdn.net/augusdi/article/details/36001543)

 


 

程序如下(稍作修改)

 

  主要是把八叉树单独提出来,作为一个文件otctree.h,然后方便引入主程序处理

  1 #pragma once;
  2 #ifndef __OCTRESSNODE__
  3 #define __OCTRESSNODE__
  4 
  5 #include <iostream>
  6 
  7 template<class T>
  8 struct OctreeNode
  9 {
 10     T data; //节点数据
 11     T xmin,xmax; //节点坐标,即六面体个顶点的坐标    
 12     T ymin,ymax;    
 13     T zmin,zmax;    
 14     OctreeNode <T>*top_left_front,*top_left_back; //该节点的个子结点    
 15     OctreeNode <T>*top_right_front,*top_right_back;    
 16     OctreeNode <T>*bottom_left_front,*bottom_left_back;    
 17     OctreeNode <T>*bottom_right_front,*bottom_right_back;    
 18     //节点类    
 19     OctreeNode (T nodeValue = T(),    
 20         T xminValue = T(),T xmaxValue = T(),    
 21         T yminValue = T(),T ymaxValue = T(),    
 22         T zminValue = T(),T zmaxValue = T(),    
 23         OctreeNode<T>*top_left_front_Node = NULL,    
 24         OctreeNode<T>*top_left_back_Node = NULL,    
 25         OctreeNode<T>*top_right_front_Node = NULL,    
 26         OctreeNode<T>*top_right_back_Node = NULL,    
 27         OctreeNode<T>*bottom_left_front_Node = NULL,    
 28         OctreeNode<T>*bottom_left_back_Node = NULL,    
 29         OctreeNode<T>*bottom_right_front_Node = NULL,    
 30         OctreeNode<T>*bottom_right_back_Node = NULL )
 31         :data(nodeValue),    
 32         xmin(xminValue),xmax(xmaxValue),    
 33         ymin(yminValue),ymax(ymaxValue),    
 34         zmin(zminValue),zmax(zmaxValue),    
 35         top_left_front(top_left_front_Node),    
 36         top_left_back(top_left_back_Node),    
 37         top_right_front(top_right_front_Node),    
 38         top_right_back(top_right_back_Node),    
 39         bottom_left_front(bottom_left_front_Node),    
 40         bottom_left_back(bottom_left_back_Node),    
 41         bottom_right_front(bottom_right_front_Node),    
 42         bottom_right_back(bottom_right_back_Node){ }    
 43 };    
 44 //创建八叉树    
 45 template <class T>    
 46 inline void createOctree(OctreeNode<T> * &root,int maxdepth,double xmin,double xmax,double ymin,double ymax,double zmin,double zmax)    
 47 {
 48     cout<<"处理中,请稍候……"<<endl;    
 49     maxdepth=maxdepth-1; //每递归一次就将最大递归深度-1    
 50     if(maxdepth>=0)//
 51     {
 52         root=new OctreeNode<T>();    
 53         //cout<<"请输入节点值:";    
 54         root->data =9;//为节点赋值,可以存储节点信息,如物体可见性。由于是简单实现八叉树功能,简单赋值为9。    
 55         //cin>>root->data;  //为节点赋值    
 56         root->xmin=xmin; //为节点坐标赋值    
 57         root->xmax=xmax;    
 58         root->ymin=ymin;    
 59         root->ymax=ymax;    
 60         root->zmin=zmin;    
 61         root->zmax=zmax;    
 62         double xm=(xmax-xmin)/2;//计算节点每个维度上的半边长    
 63         double ym=(ymax-ymin)/2;    
 64         double zm=(ymax-ymin)/2;    
 65         //递归创建子树,根据每一个节点所处(是几号节点)的位置决定其子结点的坐标。    
 66         createOctree(root->top_left_front,maxdepth,xmin,xmax-xm,ymax-ym,ymax,zmax-zm,zmax);    
 67         createOctree(root->top_left_back,maxdepth,xmin,xmax-xm,ymin,ymax-ym,zmax-zm,zmax);    
 68         createOctree(root->top_right_front,maxdepth,xmax-xm,xmax,ymax-ym,ymax,zmax-zm,zmax);    
 69         createOctree(root->top_right_back,maxdepth,xmax-xm,xmax,ymin,ymax-ym,zmax-zm,zmax);    
 70         createOctree(root->bottom_left_front,maxdepth,xmin,xmax-xm,ymax-ym,ymax,zmin,zmax-zm);    
 71         createOctree(root->bottom_left_back,maxdepth,xmin,xmax-xm,ymin,ymax-ym,zmin,zmax-zm);    
 72         createOctree(root->bottom_right_front,maxdepth,xmax-xm,xmax,ymax-ym,ymax,zmin,zmax-zm);    
 73         createOctree(root->bottom_right_back,maxdepth,xmax-xm,xmax,ymin,ymax-ym,zmin,zmax-zm);    
 74     }  else
 75         cout << "深度< 0" << endl;
 76 }    
 77 int i=1;
 78 //先序遍历八叉树    
 79 template <class T>    
 80 inline void preOrder( OctreeNode<T> * & p)    
 81 {    
 82     if(p)
 83     {
 84         cout<<i<<".当前节点的值为:"<<p->data<<"\n坐标为:";    
 85         cout<<"xmin: "<<p->xmin<<" xmax: "<<p->xmax;    
 86         cout<<"ymin: "<<p->ymin<<" ymax: "<<p->ymax;    
 87         cout<<"zmin: "<<p->zmin<<" zmax: "<<p->zmax;    
 88         i+=1;
 89         cout<<endl;    
 90         preOrder(p->top_left_front);    
 91         preOrder(p->top_left_back);    
 92         preOrder(p->top_right_front);    
 93         preOrder(p->top_right_back);    
 94         preOrder(p->bottom_left_front);    
 95         preOrder(p->bottom_left_back);    
 96         preOrder(p->bottom_right_front);    
 97         preOrder(p->bottom_right_back);    
 98         cout<<endl;    
 99     }    
100 }
101 //求八叉树的深度    
102 template<class T>    
103 inline int depth(OctreeNode<T> *& p)    
104 {    
105     if(p == NULL)    
106         return -1;    
107     int h =depth(p->top_left_front);    
108     return h+1;
109 }    
110 //计算单位长度,为查找点做准备
111 //计算输入(n-1)个2相乘
112 inline int cal(int num)    
113 {    
114     int result=1;    
115     if(1==num)    
116         result=1;    
117     else    
118     {    
119         for(int i=1;i<num;i++)    
120             result=2*result;    
121     }    
122     return result;    
123 }    
124 //查找点
125 int maxdepth=0;
126 int times=0;
127 static double xmin=0,xmax=0,ymin=0,ymax=0,zmin=0,zmax=0;    
128 int tmaxdepth=0;
129 double txm=1,tym=1,tzm=1;//计算 detX/(递归深度的步长)
130 template<class T>
131 inline void find(OctreeNode<T> *& p,double x,double y,double z)    
132 {    
133     double xm=(p->xmax-p->xmin)/2;    
134     double ym=(p->ymax-p->ymin)/2;    
135     double zm=(p->ymax-p->ymin)/2;    
136     times++;
137     if(x>xmax || x<xmin|| y>ymax || y<ymin || z>zmax || z<zmin)    
138     {    
139         cout<<"该点不在场景中!"<<endl;    
140         return;    
141     }    
142     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 )    
143     {    
144         cout<<endl<<"找到该点!"<<"该点位于"<<endl;    
145         cout<<"xmin: "<<p->xmin<<" xmax: "<<p->xmax;    
146         cout<<"ymin: "<<p->ymin<<" ymax: "<<p->ymax;    
147         cout<<"zmin: "<<p->zmin<<" zmax: "<<p->zmax;    
148         cout<<"节点内!"<<endl;    
149         cout<<"共经过"<<times<<"次递归!"<<endl;    
150     }    
151     else if(x<(p->xmax-xm) && y<(p->ymax-ym) &&z<(p->zmax-zm))    
152     {    
153         cout<<"当前经过节点坐标:"<<endl;    
154         cout<<"xmin: "<<p->xmin<<" xmax: "<<p->xmax;    
155         cout<<"ymin: "<<p->ymin<<" ymax: "<<p->ymax;    
156         cout<<"zmin: "<<p->zmin<<" zmax: "<<p->zmax;    
157         cout<<endl;    
158         find(p->bottom_left_back,x,y,z);    
159     }    
160     else if(x<(p->xmax-xm) && y<(p->ymax-ym) &&z>(p->zmax-zm))    
161     {    
162         cout<<"当前经过节点坐标:"<<endl;    
163         cout<<"xmin: "<<p->xmin<<" xmax: "<<p->xmax;    
164         cout<<"ymin: "<<p->ymin<<" ymax: "<<p->ymax;    
165         cout<<"zmin: "<<p->zmin<<" zmax: "<<p->zmax;    
166         cout<<endl;    
167         find(p->top_left_back,x,y,z);    
168     }    
169     else if(x>(p->xmax-xm) && y<(p->ymax-ym) &&z<(p->zmax-zm))    
170     {    
171         cout<<"当前经过节点坐标:"<<endl;    
172         cout<<"xmin: "<<p->xmin<<" xmax: "<<p->xmax;    
173         cout<<"ymin: "<<p->ymin<<" ymax: "<<p->ymax;    
174         cout<<"zmin: "<<p->zmin<<" zmax: "<<p->zmax;    
175         cout<<endl;    
176         find(p->bottom_right_back,x,y,z);    
177     }    
178     else if(x>(p->xmax-xm) && y<(p->ymax-ym) &&z>(p->zmax-zm))    
179     {    
180         cout<<"当前经过节点坐标:"<<endl;    
181         cout<<"xmin: "<<p->xmin<<" xmax: "<<p->xmax;    
182         cout<<"ymin: "<<p->ymin<<" ymax: "<<p->ymax;    
183         cout<<"zmin: "<<p->zmin<<" zmax: "<<p->zmax;    
184         cout<<endl;    
185         find(p->top_right_back,x,y,z);    
186     }    
187     else if(x<(p->xmax-xm) && y>(p->ymax-ym) &&z<(p->zmax-zm))    
188     {    
189         cout<<"当前经过节点坐标:"<<endl;    
190         cout<<"xmin: "<<p->xmin<<" xmax: "<<p->xmax;    
191         cout<<"ymin: "<<p->ymin<<" ymax: "<<p->ymax;    
192         cout<<"zmin: "<<p->zmin<<" zmax: "<<p->zmax;    
193         cout<<endl;    
194         find(p->bottom_left_front,x,y,z);    
195     }    
196     else if(x<(p->xmax-xm) && y>(p->ymax-ym) &&z>(p->zmax-zm))    
197     {    
198         cout<<"当前经过节点坐标:"<<endl;    
199         cout<<"xmin: "<<p->xmin<<" xmax: "<<p->xmax;    
200         cout<<"ymin: "<<p->ymin<<" ymax: "<<p->ymax;    
201         cout<<"zmin: "<<p->zmin<<" zmax: "<<p->zmax;    
202         cout<<endl;    
203         find(p->top_left_front,x,y,z);    
204     }    
205     else if(x>(p->xmax-xm) && y>(p->ymax-ym) &&z<(p->zmax-zm))    
206     {    
207         cout<<"当前经过节点坐标:"<<endl;    
208         cout<<"xmin: "<<p->xmin<<" xmax: "<<p->xmax;    
209         cout<<"ymin: "<<p->ymin<<" ymax: "<<p->ymax;    
210         cout<<"zmin: "<<p->zmin<<" zmax: "<<p->zmax;    
211         cout<<endl;    
212         find(p->bottom_right_front,x,y,z);    
213     }    
214     else if(x>(p->xmax-xm) && y>(p->ymax-ym) &&z>(p->zmax-zm))    
215     {    
216         cout<<"当前经过节点坐标:"<<endl;    
217         cout<<"xmin: "<<p->xmin<<" xmax: "<<p->xmax;    
218         cout<<"ymin: "<<p->ymin<<" ymax: "<<p->ymax;    
219         cout<<"zmin: "<<p->zmin<<" zmax: "<<p->zmax;    
220         cout<<endl;    
221         find(p->top_right_front,x,y,z);    
222     }    
223 }    
224 //main函数    
225 /**
226 int main ()    
227 {    
228     OctreeNode<double> *rootNode = NULL;    
229     int choiced = 0;    
230     while(true)    
231     {    
232         system("cls");    
233         cout<<"请选择操作:\n";    
234         cout<<"1.创建八叉树 2.先序遍历八叉树\n";    
235         cout<<"3.查看树深度 4.查找节点   \n";    
236         cout<<"0.退出\n\n";    
237         cin>>choiced;    
238         if(choiced == 0)    
239             return 0;    
240         else if(choiced == 1)    
241         {    
242             system("cls");    
243             cout<<"请输入最大递归深度:"<<endl;    
244             cin>>maxdepth;    
245             cout<<"请输入外包盒坐标,顺序如下:xmin,xmax,ymin,ymax,zmin,zmax"<<endl;    
246             cin>>xmin>>xmax>>ymin>>ymax>>zmin>>zmax;    
247             if(maxdepth>=0|| xmax>xmin || ymax>ymin || zmax>zmin || xmin>0 || ymin>0||zmin>0)    
248             {    
249                 tmaxdepth=cal(maxdepth);    
250                 txm=(xmax-xmin)/tmaxdepth;    
251                 tym=(ymax-ymin)/tmaxdepth;    
252                 tzm=(zmax-zmin)/tmaxdepth;    
253                 createOctree(rootNode,maxdepth,xmin,xmax,ymin,ymax,zmin,zmax);    
254             }    
255             else    
256             {    
257                 cout<<"输入错误!";    
258                 return 0;    
259             }    
260         }    
261         else if(choiced == 2)    
262         {    
263             system("cls");    
264             cout<<"先序遍历八叉树结果:/n";    
265             i=1;    
266             preOrder(rootNode);    
267             cout<<endl;    
268             system("pause");    
269         }    
270         else if(choiced == 3)    
271         {    
272             system("cls");    
273             int dep =depth(rootNode);    
274             cout<<"此八叉树的深度为"<<dep+1<<endl;    
275             system("pause");    
276         }    
277         else if(choiced == 4)    
278         {    
279             system("cls");    
280             cout<<"请输入您希望查找的点的坐标,顺序如下:x,y,z\n";    
281             double x,y,z;    
282             cin>>x>>y>>z;    
283             times=0;    
284             cout<<endl<<"开始搜寻该点……"<<endl;    
285             find(rootNode,x,y,z);    
286             system("pause");    
287         }    
288         else    
289         {    
290             system("cls");    
291             cout<<"\n\n错误选择!\n";    
292             system("pause");    
293         }    
294     }    
295 }
296 */
297 #endif

主程序调用如下

 1 #include "stdafx.h"
 2 #include "octortrees.h"
 3 #include <string>
 4 using namespace std;
 5 int _tmain(int argc, _TCHAR* argv[])
 6 {
 7     OctreeNode<double> *rootNode = NULL;    
 8     int choiced = 0;    
 9     while(true)    
10     {    
11         system("cls");    
12         cout<<"请选择操作:\n";    
13         cout<<"1.创建八叉树 2.先序遍历八叉树\n";    
14         cout<<"3.查看树深度 4.查找节点   \n";    
15         cout<<"0.退出\n\n";    
16         cin>>choiced;    
17         if(choiced == 0)    
18             return 0;    
19         else if(choiced == 1)    
20         {    
21             system("cls");    
22             cout<<"请输入最大递归深度:"<<endl;    
23             cin>>maxdepth;//递归深度
24             cout<<"请输入外包盒坐标,顺序如下:xmin,xmax,ymin,ymax,zmin,zmax"<<endl;    
25             cin>>xmin>>xmax>>ymin>>ymax>>zmin>>zmax;    
26             if(maxdepth>=0|| xmax>xmin || ymax>ymin || zmax>zmin || xmin>0 || ymin>0||zmin>0)    
27             {    
28                 tmaxdepth=cal(maxdepth);    
29                 txm=(xmax-xmin)/tmaxdepth;    
30                 tym=(ymax-ymin)/tmaxdepth;    
31                 tzm=(zmax-zmin)/tmaxdepth;    
32                 createOctree(rootNode,maxdepth,xmin,xmax,ymin,ymax,zmin,zmax); 
33                 
34                 cout << "8叉树创建完毕, input the 'continue' to  \n" << endl;41             }    
42             else    
43             {    
44                 cout<<"输入错误!";    
45                 return 0;    
46             }    
47         }    
48         else if(choiced == 2)    
49         {    
50             system("cls");    
51             cout<<"先序遍历八叉树结果:/n";    
52             i=1;    
53             preOrder(rootNode);    
54             cout<<endl;    
55             system("pause");    
56         }    
57         else if(choiced == 3)    
58         {    
59             system("cls");    
60             int dep =depth(rootNode);    
61             cout<<"此八叉树的深度为"<<dep+1<<endl;    
62             system("pause");    
63         }    
64         else if(choiced == 4)    
65         {    
66             system("cls");    
67             cout<<"请输入您希望查找的点的坐标,顺序如下:x,y,z\n";    
68             double x,y,z;    
69             cin>>x>>y>>z;    
70             times=0;    
71             cout<<endl<<"开始搜寻该点……"<<endl;    
72             find(rootNode,x,y,z);    
73             system("pause");    
74         }    
75         else    
76         {    
77             system("cls");    
78             cout<<"\n\n错误选择!\n";    
79             system("pause");    
80         }    
81     }    
82 
83     cin.get();
84     return 0;
85 }

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM