Инициализация структуры данных Half-edge из вершин

Я работаю над реализацией различных алгоритмов разбиения (таких как catmull-clark); для эффективного использования требуется хороший способ хранения информации о сетке тесселированных полигонов. Я реализовал структуру данных с половинным фронтом как обозначенный флипкодом, но теперь я не уверен, как заполнить структуру данных из вершин!

Моя первоначальная попытка заключалась в

  • создать вершины
  • групповые вершины в грани
  • сортировать вершины внутри граней (используя их угол относительно центра тяжести)
  • для каждого лица, возьмите первую вершину и пройдите через отсортированный список вершин, чтобы создать список с половинным краем.

Однако это создает список лиц (с полуребрами), которые не имеют никакой информации о смежных грани! Это также кажется немного неправильным, потому что кажется, что лица действительно являются первоклассным объектом, а края предоставляют вспомогательную информацию; Мне действительно кажется, что я должен создавать ребра из вершин, а затем сортировать лица оттуда. Но опять же, я не совсем уверен, как это сделать - я не могу придумать способ создания списка полуребер, не создавая сначала лица.

Любые предложения о том, какой лучший способ перевернуть данные о вершинах (и гранях) в полуребрах?

Ответ 1

Во-первых, я хотел бы указать вам на отличную реализацию С++ для структуры данных с половинным краем: OpenMesh. Если вы хотите использовать его, убедитесь, что вы работаете над учебником. Если (и только если) вы это делаете, работа с OpenMesh довольно проста. Он также содержит несколько полезных методов, поверх которых вы можете реализовать алгоритмы разбиения или сокращения.

Теперь на ваш вопрос:

Однако это создает список лиц (с полуребрами), которые не имеют никакой информации о смежных грани! Это также кажется немного неправильным, потому что кажется, что лица действительно являются первоклассным объектом, а ребра предоставляют вспомогательную информацию

Я думаю, что это несколько не соответствует точке структуры данных с половинным краем. В структуре с половинным краем это половина ребер несет самую большую информацию!

Бесстыдно ссылаться на документацию OpenMesh (см. также рисунок):

  • Каждая вершина ссылается на один исходящий промежуточный элемент, т.е. полуэлемент, начинающийся с этой вершины.
  • Каждое лицо ссылается на одно из ограничивающих его элементов.
  • Каждый полузадачник предоставляет дескриптор
    • указанную вершину,
    • лицо, которому оно принадлежит
    • следующий полузадачник внутри лица (упорядоченный против часовой стрелки),
    • противоположный полуэлемент,
    • (необязательно: предыдущее полузадача в лицо).

Как вы видите, большая часть информации хранится в полуребрах - это основные объекты. Итерация по сеткам в этой структуре данных - это все из-за хитрости следующих указателей.

Однако это создает список граней (с полуребрами), которые не имеют никакой информации о смежных гранях!

Это нормально! Как вы видите выше, лицо ссылается на только на один граничный край. Предполагая треугольную сетку, цепочка указателей, которой вы следуете, чтобы получить 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;

в С++. Вот строковый псевдокод (без вершины и граничной части):

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 и указателях.