#c #loops
#c #циклы
Вопрос:
Scene
Содержит список Shape
.
Каждый Shape
содержит:
- Его
std::vector
ofVertex
(с нормалью поверхности, texcoord, позицией с 3 пробелами) - Его
std::vector
ofTriangle
(связанные вершины, для пересечения сетки)
Я хотел бы сделать Scene
объект итеративным как в виде коллекции Vertex
, так и в виде коллекции Triangle
, не таким беспорядочным способом.
В настоящее время требуется обойти треугольники: (синтаксис 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), включая lambda.
Комментарии:
1. Это проблема, потому что вам нужно повторять одни и те же циклы в нескольких местах вашего кода?
2. Вы бы сделали
for (Vertex amp; v : shape.vertices()) /*...*/
, гдеShape::vertices
возвращает диапазон. Аналогично для треугольников. Как вы реализуетеShape::vertices
— это совершенно другой вопрос.3. В качестве дополнения, я уже некоторое время скучаю
yield
по C .4. Не стали бы вы массово пересчитывать вершины, поскольку каждая вершина может принадлежать произвольно многим треугольникам?
5. @KerrekSB Краткий ответ: Нет :). Подробности:
verts
Массив — это в основном массив вершин, используемый для рисования. Он может быть проиндексирован, что означает, что вершины не будут повторяться (существует отдельный массив с именемindices
, который указывает порядок рисования.)tris
Список является вспомогательным, используется только для таких вещей, как пересечение сетки.
Ответ №1:
Вы могли бы создавать функции, которые выполняют итерацию по всем элементам и вызывают объект функции:
template <typename F>
inline void for_each_vertex(Sceneamp; scene,F f)
{
for (Shapeamp; shape : scene) {
for (Meshamp; mesh : shape.meshGroup.meshes) {
for (Vertexamp; vertex : mesh.verts) {
f(vertex);
}
}
}
}
template <typename F>
inline void for_each_triangle(Sceneamp; scene,F f)
{
for (Shapeamp; shape : scene) {
for (Meshamp; mesh : shape.meshGroup.meshes) {
for (Triangleamp; triangle : mesh.tris) {
f(triangle);
}
}
}
}
Теперь вы можете сделать
for_each_vertex(scene,[](Vertexamp; vertex)(/* work with vertex */});
for_each_triangle(scene,[](Triangleamp; triangle)(/* work with triangle */});
Это должно иметь ту же производительность, что и ваш исходный код, но вам не придется писать одну и ту же структуру цикла несколько раз в вашей программе.
Комментарии:
1. Однако обратите внимание, что эта конструкция инвертирует логику итерации и делает ее немного менее полезной (например, это не позволяет вам использовать какой-либо из стандартных алгоритмов диапазона).
2. Я думал об этом, но не приведет ли лямбда к огромным издержкам производительности (более 1 000 000 с лишним вершин)?
3. Ах да, вы встроили это, хм, позвольте мне протестировать это
Ответ №2:
Что я обычно делаю, чтобы выделить отдельные «логические контейнеры» в большой (мульти-) контейнерный класс:
Я определяю метод ‘getMeshRange() ,
getShapeRange()` и т.д.
Если вы используете библиотеку boost range, вы могли бы вернуть 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 (autoamp; mesh : X.getMeshRange())
{
// use mesh
}
С помощью алгоритма Boost Range вы можете делать все, что могли бы с «обычным» контейнером:
const Shapeamp; shape = *std::min_element(X.getShapeRange());
Комментарии:
1. Я думаю, что вопрос больше о том, как связать все контейнеры вершин, чтобы получить один повторяющийся диапазон.
2. Ну, на самом деле было бы полезно разделить повторяющиеся диапазоны. Я не хочу использовать Boost.
3. @bobobobo Помните: вам не обязательно использовать boost. Пара итераторов отлично моделирует диапазон (
for (const Tamp; i: make_pair(it1, it2))
будет хорошо работать на c 0x)
Ответ №3:
Если вся обработка выполняется во внутреннем цикле, вы можете создать пользовательский итератор, который выполняет итерации по всем трем уровням и создает (Shape*,Mesh*,Vertex*)
тройки, обрабатывая логику для перехода, например, к следующему, Mesh
как только все Vertex
элементы будут повторены для текущего Mesh
.
Если вы хотите выполнить некоторую обработку в среднем или внешних циклах, вы могли бы добавить несколько перехватов или, например, вернуть (Shape*,NULL,NULL)
тройку в первый раз через новый Shape
.