我正在努力实现各种细分算法(例如catmull-clark);要有效地执行此操作,需要一种很好的方法来存储有关细分多边形网格的信息。我将半边数据结构实现为outlined by flipcode,但现在我不确定如何从顶点填充数据结构!
我最初的尝试是
然而,这会创建一个没有任何关于相邻面信息的面部列表(带有半边)!这也感觉有点不对,因为看起来好像是真正的第一类物体,边缘提供辅助信息;我真的觉得我应该从顶点创建边缘,然后从那里整理出面。但同样,我不确定如何去做 - 我想不出一种方法来创建一个半边列表而不先创建面。
有关将顶点(和面)的数据转换为半边的最佳方法的建议吗?
答案 0 :(得分:22)
首先,我想指出一个优秀的半边数据结构的C ++实现:OpenMesh。如果您想使用它,请确保您完成整个教程。如果(并且仅当)您这样做,使用OpenMesh非常简单。它还包含一些很好的方法,您可以在其上实现细分或缩减算法。
现在回答你的问题:
然而,这会创建一个没有任何关于相邻面信息的面部列表(带有半边)!这也感觉有点不对,因为看起来好像是真正的第一类物体,边缘提供了辅助信息
我认为这有点忽略了半边数据结构的要点。在半边结构中,半边缘带有最多的信息!
从OpenMesh documentation无耻地引用(参见那里的图):
如您所见,大部分信息存储在半边 - 这些是主要对象。在这个数据结构中迭代网格就是巧妙地遵循指针。
然而,这会创建一个面部列表(带有半边),但没有任何关于相邻面的信息!
这是完全可以的!如上所示,面部仅引用 一个边界半边。假设有一个三角形网格,您遵循的指针链将3个相邻的三角形到达给定的面F
,如下所示:
F -> halfEdge -> oppositeHalfEdge -> face
F -> halfEdge -> nextHalfEdge -> oppositeHalfEdge -> face
F -> halfEdge -> previousHalfEdge -> oppositeHalfEdge -> face
如果您不使用“之前的”指针,可以选择使用nextHalfEdge -> nextHalfEdge
。当然,这很容易推广到四边形或更高阶的多边形。
如果在构建网格时正确设置上面列出的指针,那么您可以像这样迭代网格中的各种邻接。如果你使用OpenMesh,你可以使用一堆特殊的迭代器来指向你的指针。
当从“三角汤”构建半边结构时,设置“相对的半边”指针当然是棘手的部分。我建议使用某种地图数据结构来跟踪已经创建的半边缘。
更具体地说,这里有一些非常概念化的伪代码,用于从面创建半边网格。我省略了顶点部分,这更简单,并且可以以相同的精神实现。我假设在面边缘上的迭代是有序的(例如,顺时针方向)。
我假设半边是作为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
地图和指针。