2

Do existing OpenMesh iterators change, when I add elements?

Example code:

auto vh1 = mesh.vertex_handle(0);
auto vh2 = mesh.vertex_handle(1);
auto vh3 = mesh.vertex_handle(2);
for(auto fh: mesh.faces()) {
    mesh.add_face(vh1, vh2, vh3);
}

I did not find something about this in the documentation.

Example seem to work, but I want to know if it's undefined behavior or if OpenMesh promises to make sure that the iterator does not change during the loop.

allo
  • 3,955
  • 8
  • 40
  • 71

2 Answers2

3

OpenMesh does not change the iterators when you add elements, but I don't think OpenMesh gives you a promise on that.

OpenMesh iterators are basically just ints. (They hold a SmartHandle and some information about which elements should be skipped. A SmartHandle holds a Handle and a reference to the mesh. A Handle is just a strongly typed integer.) Incrementing the iterator will just increment the integer (until an element is reached which should not be skipped). Since you always access elements via the mesh and a handle the relocation of the actual memory that stores the elements is not a problem.

Note that depending on how you code your loop the new element may or may not be iterated over.

for (auto it = mesh_.vertices_begin(); it != mesh_.vertices_end(); ++it)
{
  mesh_.add_vertex(point);
}

The loop above will include the newly added vertex as mesh_.vertices_end() is reevaluated for each comparison and will thus include the newly added elements. This leads to an infinite loop in that case.

auto end = mesh_.vertices.end();
for (auto it = mesh_.vertices_begin(); it != end; ++it)
{
  mesh_.add_vertex(point);
}

In this case, the newly added elements will not be contained in the loop. That is because end is evaluated only once in the beginning and basically just holds the number of vertices the mesh had at that point.

for (auto vh : mesh_.vertices())
{
  mesh_.add_vertex(point);
}

This will behave as the second version as here, too, vertices_end() is only evaluated once in the beginning.

Deletion

Since it was brought up in the other answer I want to quickly talk about deletion. Deleting an element will only mark it as deleted. Thus, deleting elements while iterating over the elements is fine.

When you delete elements which have not been visited yet, they may or may not be iterated over later. If you use skipping iterators the deleted elements will be skipped, otherwise they won't be skipped.

For OpenMesh 7.0 or newer for (auto fh : mesh_.faces()) {...} will not include deleted elements.

Instead for (auto fh : mesh_.all_faces()) {...} will include deleted elements.

Garbage Collection

You should probably not call garbage collection inside your loop. If you have deleted elements, garbage collection will cause two problems. First, it reduces the size of the container storing the elements. Thus, versions of the loop that evaluate the end iterator once will likely run too far and crash.

If you use the other version of the loop or manage to create more new elements than you remove, you still have the problem that garbage collection will move elements from the back into the spots of the elements that were marked as deleted. Thus, you will miss those elements if they are moved to spots that you already passed.

Max
  • 101
  • 4
  • Thank you. I knew that handles are storing integers, but had no idea about how the iterators work. – allo Jan 29 '21 at 10:20
0

One can search typedef std::vector< in openmesh,then you can find it. But add_face won't reallocation this iterators, because the new vertex handle or face handle will push_back to the end of this vector. Meanwhile , in order to have a high-efficient search speed, Openmesh builds at least three layers of iterators, and the vector we discuss is only the bottom of them. The middle or top iterators, I use them by assemble functions,so I'm not sure it will be reallocated/invalidated or not, and you can find them in PolyConnectivity.hh and TriConnectivity.hh.

allo
  • 3,955
  • 8
  • 40
  • 71
  • I easily see the reason why this is true for deleted elements, but I do not see why it is guaranteed when adding elements. It may be, but I don't see why and your answer does not address this question. – allo Jan 15 '21 at 10:12
  • When you use "add_face()" function, the new face handle will be add in face_handle queue by "push_back()". So these previous face handles will be stable. The "add_vertex()" function are alse the same. – 虹风暴 Jan 22 '21 at 06:14
  • I think the `ArrayKernel` uses `std::vector`, doesn't it? This would mean `add_face` can cause reallocations and invalidate iterators. – allo Jan 22 '21 at 18:02
  • Yes , you can search `typedef std::vector<` in openmesh,then you can find it. But `add_face` won't reallocation **this** iterators, because the new vertex handle or face handle will push_back to the end of **this** vector. Meanwhile , in order to have a high-efficient search speed, Openmesh builds at least three layers of iterators, and the vector we discuss is only the bottom of them. The middle or top iterators, I use them by assemble functions,so I'm not sure it will be reallocated/invalidated or not, and you can find them in `PolyConnectivity.hh` and `TriConnectivity.hh`. – 虹风暴 Jan 27 '21 at 02:56
  • @ 虹风暴 I edited your answer with your comment, which is much better than your initial answer. Feel free to edit it again if you like to improve on it or I did something wrong. – allo Jan 28 '21 at 08:28