VCGLib中邻接关系的维护依赖于各种单形中存储的相关信息。在VCGLib中几乎所有的算法实现都假设存在vcg::face::VertexRef,该属性存储了三个指向顶点对象的指针,可以通过V()函数访问。
面-面邻接关系
面之间的邻接关系存储于vcg::face::FFAdj(正四面体为vcg::face::TTAdj),该属性通过边来记录面之间的邻接关系。下图显示了两个三角形面:
图中顶点编号从0到2,以逆时针顺序编号,边i(i=0..2)的两个端点分别为i和(i+1)%3,因此图中面f0和面f1的公共边对f0而言是0,对f1而言是1。
对于面f的每个边e,vcg::face::FFAdj存储以下信息:
例如在上图中,有:
VCGLib中对于非流形的情况也有考虑,因为只是想简单了解,没有看。
Pos
三角网格中,Pos为一三元组,pos = {v,e,f},e是f的边,v是e的端点。下图以小三角形的形式显示了三角网格中的一些pos,在每个面中,每个小三角形指向一个顶点,倚靠一条边。这样就能保证任意给定一个pos c,若只改变c的三元组的一个分量,能够唯一确定一个邻居pos。
从一个pos移动到该pos的一个邻居的操作称为Flip,把改变顶点,边和面的Flip操作分别记做FlipV,FlipE和FlipF。
例如,对于上图中的c0而言,其三元组中除了顶点,其他分量都相同的pos只有一个,即c2。记c2 = FlipV(c1)。
类似地,有:
环绕v逆时针遍历:
在border上的情况:
环绕v顺时针遍历:
border:
可以发现,当两个flip嵌套操作的时候,根据pos与面的关系,可以实现顺时针或逆时针的pos转换。并且由于面-面邻接关系的定义方式,当pos在border上的时候,FlipF操作会返回到pos本身所在的面。
下面的例子展示了如何使用pos在顶点周围迭代
/* vcglib/apps/sample/trimesh_pos_demo/trimesh_pos_demo.cpp */
#include <vcg/simplex/face/pos.h> // include the definition of pos
//...includes to define your mesh type
//class MyVertex: ...
class MyFace: public vcg::FaceSimp2<MyVertex,MyEdge,MyFace, vcg::face::VertexRef, vcg::face::FFAdj>{};
void OneRingNeighborhood( MyFace * f)
{
MyVertex * v = f->V(0);
MyFace* start = f;
vcg::face::Pos<MyFace> p(f,0,v);// constructor that takes face, edge and vertex
do
{
p.FlipF();
p.FlipE();
}while(p.f!=start);
}
Jumping Pos
Jumping Pos类似Pos,但在遇到border的时候不会反弹回去,而是跨到下一个border-face上去。下面这个例子中,上图中的p会从f0跨到f2。
/* vcglib/apps/sample/trimesh_pos_demo/trimesh_pos_demo.cpp */
#include <vcg/simplex/face/jumping_pos.h> // include the definition of jumping pos
//...includes to define your mesh type
//class MyVertex: ...
class MyFace: public vcg::FaceSimp2<MyVertex,MyEdge,MyFace, vcg::face::VertexRef,vcg::face::FFAdj>{};
void OneRingNeighborhoodJP( MyFace * f)
{
MyVertex * v = f->V(0);
MyFace* start = f;
vcg::face::JumpingPos<MyFace> p(f,0,v);// constructor that takes face, edge and vertex
do
{
p.NextFE();
}while(p.f!=start);
}
顶点-面(VF)的邻接关系
VCG lib中,顶点与面之间也实现了邻接关系,即给定一个顶点v,可以找出所有与其关联的面。
设v_star = (f0,f1,f2,…,fk)为一个列表,该列表中的元素都是与顶点v关联的面。可以通过以下属性索引v_star:
这两个属性不仅是指针,还包含顶点在所指向的面中的索引。例如上图中,有:
v.VFp() == f2
v.VFi() == 0
f2->VFp(0) == f3
f2->VFi(0) == 1 //f2中索引为0的顶点(即v)指向了面f3,而v在f3中的索引为1
f3->VFp(1) == f1
f3->VFi(1) == 2
f1->VFp(2) == f0
f1->VFi(2) == 2
f0->VFp(2) == NULL
f0->VFi(2) == –1