首先,我想向您指出半边数据结构的一个出色的 C++ 实现:OpenMesh。如果您想使用它,请确保您按照教程进行操作。如果(且仅当)您这样做,使用 OpenMesh 将非常简单。它还包含一些不错的方法,您可以在这些方法之上实现细分或归约算法。
现在回答你的问题:
但是,这会创建一个没有关于相邻面的任何信息的面列表(带有半边)!这也感觉有点不对劲,因为看起来人脸真的是第一类对象,边缘提供了辅助信息
我认为这有点忽略了半边数据结构的要点。在半边结构中,承载信息最多的是半边!
从OpenMesh 文档中无耻地引用(另见那里的图):
- 每个顶点引用一个输出半边,即从该顶点开始的半边。
- 每个面都引用围绕它的半边之一。
- 每个半边提供一个句柄
- 它指向的顶点,
- 它所属的脸
- 面内的下一个半边(逆时针顺序),
- 对面的半边,
- (可选:面中的前一个半边)。
如您所见,大多数信息都存储在半边中- 这些是主要对象。在这个数据结构中迭代网格就是巧妙地遵循指针。
但是,这会创建一个没有关于相邻面的任何信息的面列表(带有半边)!
这完全没问题!正如您在上面看到的,一个面仅引用一个边界半边。假设一个三角形网格,您遵循的指针链将 3 个相邻三角形指向给定面F
如下:
F -> halfEdge -> oppositeHalfEdge -> face
F -> halfEdge -> nextHalfEdge -> oppositeHalfEdge -> face
F -> halfEdge -> previousHalfEdge -> oppositeHalfEdge -> face
或者,nextHalfEdge -> nextHalfEdge
如果您不使用“以前的”指针,则可以使用。当然,这很容易推广到四边形或更高阶的多边形。
如果您在构建网格时正确设置了上面列出的指针,那么您可以像这样遍历网格中的各种邻接。如果你使用 OpenMesh,你可以使用一堆特殊的迭代器来为你追逐指针。
当从“三角形汤”构建半边结构时,设置“对边半边”指针当然是棘手的部分。我建议使用某种地图数据结构来跟踪已经创建的半边。
更具体地说,这里有一些非常概念性的伪代码,用于从面创建半边网格。顶点部分我省略了,比较简单,可以本着同样的精神来实现。我假设对面边缘的迭代是有序的(例如顺时针方向)。
我假设半边被实现为 type 的结构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
地图和指针。