私はさまざまな細分化アルゴリズム(catmull-clarkなど)の実装に取り組んでいます。これを効率的に行うには、テッセレートされたポリゴンのグリッドに関する情報を格納するための優れた方法が必要です。ハーフエッジデータ構造を フリップコードで概説 として実装しましたが、頂点からデータ構造を設定する方法がわかりません!
私の最初の試みは
ただし、これにより、隣接する面に関する情報がない面(ハーフエッジ付き)のリストが作成されます。面が実際には一流のオブジェクトであり、エッジが補助情報を提供しているように見えるため、これも少し間違っているように感じます。頂点からエッジを作成し、そこから面を並べ替える必要があると本当に感じています。しかし、繰り返しになりますが、その方法がよくわかりません。最初に面を作成せずに、ハーフエッジのリストを作成する方法を考えることはできません。
頂点(および面)に関するデータをハーフエッジに変換するための最良の方法について何か提案はありますか?
まず、ハーフエッジデータ構造の優れたC++実装を紹介します: OpenMesh 。それを使用したい場合は、チュートリアルを順調に進めてください。それを行う場合(そしてその場合のみ)、OpenMeshの操作は非常に簡単です。また、細分割または縮小アルゴリズムを実装できるいくつかの優れたメソッドも含まれています。
今あなたの質問に:
ただし、これにより、隣接する面に関する情報がない面(ハーフエッジ付き)のリストが作成されます。面が実際には一流のオブジェクトであり、エッジが補助情報を提供しているように見えるため、これも少し間違っているように感じます
これは、ハーフエッジデータ構造のポイントをいくらか見逃していると思います。ハーフエッジ構造では、最も多くの情報を伝達するのはハーフエッジです。
OpenMeshドキュメント から恥知らずに引用(そこの図も参照):
ご覧のとおり、ほとんどの情報はハーフエッジに格納されています-これらは主要なオブジェクトです。このデータ構造のメッシュを反復処理することは、ポインターを巧みに追跡することです。
ただし、これにより、隣接する面に関する情報がない面(ハーフエッジ付き)のリストが作成されます。
これは完全に大丈夫です!上で見たように、面はonly1つの境界の半分のエッジを参照します。三角形メッシュを想定すると、特定の面F
に隣接する3つの三角形を取得するためにたどるポインタのチェーンは次のとおりです。
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) ];
}
}
}
EDIT:Edges
マップとポインターについてより明確にするために、コードの疑似を少し少なくしました。