Предоставление нескольких итерируемых интерфейсов - PullRequest
1 голос
/ 04 ноября 2011

A Scene содержит список Shape.

Каждый Shape содержит:

  • Его std::vector из Vertex (с нормалью поверхности, texcoord, 3-х позиционное положение)
  • Его std::vector из Triangle (связанные вершины, для пересечения сетки)

Я хотел бы сделать объект Scene повторяемым и как коллекцию Vertex, и коллекцию Triangle, в не так грязно way.

В настоящее время требуется пройтись по треугольникам: (синтаксис C # здесь):

foreach( Shape shape in Scene )
{
    foreach( Mesh mesh in shape.meshGroup.meshes )
    {
        foreach( Triangle tri in mesh.tris )
        {
            // work with tri
        }
    }
}

Тройное вложенное for не очень приятно видеть, и, конечно, синтаксис C ++ хуже, с использованием либо счетчиков i, j и k, либо с использованием ::iterator s ..

Для доступа к каждому Vertex:

foreach( Shape shape in Scene )
{
    foreach( Mesh mesh in shape.meshGroup.meshes )
    {
        foreach( Vertex v in mesh.verts )
        {
            // work with v
        }
    }
}

Поскольку обход всех треугольников / вершин стоит начинать с , каков наилучший способ сделать это? (Предположим, что необходимо нажать каждые Triangle / Vertex, поэтому нет необходимости в алгоритмах пространственного разделения и т. Д.)

Вы можете использовать любые функции C ++ 0x (включена VS-2010), включая лямбду.

Ответы [ 3 ]

2 голосов
/ 04 ноября 2011

Вы можете создавать функции, которые перебирают все элементы и вызывают объект функции:

template <typename F>
inline void for_each_vertex(Scene& scene,F f)
{
  for (Shape& shape : scene) {
    for (Mesh& mesh : shape.meshGroup.meshes) {
      for (Vertex& vertex : mesh.verts) {
        f(vertex);
      }
    }
  }
}

template <typename F>
inline void for_each_triangle(Scene& scene,F f)
{
  for (Shape& shape : scene) {
    for (Mesh& mesh : shape.meshGroup.meshes) {
      for (Triangle& triangle : mesh.tris) {
        f(triangle);
      }
    }
  }
}

Теперь вы можете сделать

for_each_vertex(scene,[](Vertex& vertex)(/* work with vertex */});
for_each_triangle(scene,[](Triangle& triangle)(/* work with triangle */});

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

0 голосов
/ 04 ноября 2011

Что я обычно делаю, чтобы выставлять отдельные «логические контейнеры» в большой (мульти) контейнерный класс:

Я определяю метод 'getMeshRange () , getShapeRange () `и т. Д.

Если вы используете библиотеку увеличенных диапазонов, вы можете вернуть boost::iterator_range<const Shape*>, boost::sub_range<std::vector<Vertex> > и т. Д.

На практике IIRC, чтобы удовлетворить концепцию диапазона, вы можете просто вернуть std::pair<const_iterator, const_iterator>.

Диапазон можно использовать следующим образом:

for (It it = std::begin(X.getMeshRange()); it!= std::end(X.getMeshRange()); ++it)
{ 
    // use *it
}

или в C ++ 0x

for (auto& mesh : X.getMeshRange())
{
    // use mesh
}

С помощью алгоритма повышения диапазона вы можете делать все, что можете с«обычный» контейнер:

const Shape& shape = *std::min_element(X.getShapeRange());
0 голосов
/ 04 ноября 2011

Если вся обработка выполняется во внутреннем цикле, вы можете создать пользовательский итератор, который выполняет итерацию по всем трем уровням и производит (Shape*,Mesh*,Vertex*) тройки, обрабатывая логику для перехода, например, к следующему Mesh один раз для всех Vertex es были перебраны для текущего Mesh.

Если вы хотите выполнить некоторую обработку в среднем или внешнем цикле, вы можете добавить несколько хуков или, например, return(Shape*,NULL,NULL) тройной впервые через новый Shape.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...