首先,我要向你介绍一个优秀的C++半边数据结构实现:
OpenMesh。如果您想使用它,请确保您按照教程进行操作。只有这样,使用OpenMesh才相当简单。它还包含一些不错的方法,可以实现细分或减少算法。
现在来回答你的问题:
然而,这会创建一个没有关于相邻面的任何信息的面(带有半边)列表!这也感觉有点不对,因为似乎面才是第一等对象,而边提供附加信息。
我认为这有点误解了半边数据结构的重点。在半边数据结构中,最多的信息都是由半边携带的!
抄袭自
OpenMesh文档(请参见其中的图):
- 每个顶点引用一个出射半边,即从该顶点开始的半边。
- 每个面引用其边界之一的半边。
- 每个半边提供一个句柄,指向
- 它指向的顶点,
- 它所属的面,
- 面内的下一条半边(逆时针排序),
- 相对应的另一个半边,
- (可选:面内的前一条半边)。
正如你看到的,
大部分信息都存储在半边中 - 这些是主要的对象。在这个数据结构中迭代网格就是聪明地跟随指针。
然而,这会创建一个没有关于相邻面的任何信息的面(带有半边)列表!
这完全没问题!正如你在上面看到的那样,一个面仅引用一个边界半边。假设是三角网格,获取给定面 F
的3个相邻三角形所遵循的指针链如下:
F -> halfEdge -> oppositeHalfEdge -> face
F -> halfEdge -> nextHalfEdge -> oppositeHalfEdge -> face
F -> halfEdge -> previousHalfEdge -> oppositeHalfEdge -> face
如果您不使用 'previous' 指针,则可以选择使用nextHalfEdge -> nextHalfEdge
。当然,这很容易推广到四边形或更高阶多边形。
如果在构建网格时正确设置了上述指针,则可以像这样迭代网格中的所有类型的邻接关系。如果使用 OpenMesh,则可以使用一些特殊的迭代器为您执行指针跟踪。
当从“三角形 soup”构建半边结构时,设置“对向半边”指针当然是棘手的部分。我建议使用某种映射数据结构来跟踪已经创建的半边。
更具体地说,以下是一些非常概念性的伪代码,用于从面创建半边网格。我省略了顶点部分,其更简单,并且可以采用同样的方法实现。我假设对于一个面的边的迭代是有序的(例如按顺时针方向)。
我假设半边被实现为类型为HalfEdge
的结构体,其中包含上述指针作为成员。
struct HalfEdge
{
HalfEdge * oppositeHalfEdge;
HalfEdge * nextHalfEdge;
Vertex * vertex;
Face * face;
}
让 Edges
成为从顶点标识符对到指向实际半边实例的指针的映射,例如:
map< pair<unsigned int, unsigned int>, HalfEdge* > Edges;
C++中的构造伪代码(不包括顶点和面部分):
map< pair<unsigned int, unsigned int>, HalfEdge* > Edges;
for each face F
{
for each edge (u,v) of F
{
Edges[ pair(u,v) ] = new HalfEdge();
Edges[ pair(u,v) ]->face = F;
}
for each edge (u,v) of F
{
set Edges[ pair(u,v) ]->nextHalfEdge to next half-edge in F
if ( Edges.find( pair(v,u) ) != Edges.end() )
{
Edges[ pair(u,v) ]->oppositeHalfEdge = Edges[ pair(v,u) ];
Edges[ pair(v,u) ]->oppositeHalfEdge = Edges[ pair(u,v) ];
}
}
}
编辑:将代码稍微改动一下,让Edges
映射和指针更加明确。