OpenMesh-Python face definition bug?

Hi, I am new to OpenMesha and currently working with the python bindings to build a quick prototype of my code before moving to c++. attached a simple icosahedron structure. I defined 12 vertices and 20 faces. When using the vf circulator I experience that not all neighbouring face of a vertex are found. I also found that the number of faces is sensitive to the order of the vertex handles when defining a face. when looking at the indices some are set to -1. Is this expected behavior or am I missing something fundamental? Please find the minimal example below. Any help or comments would be greatly appreciated! Best regards, Marc Siggel import OpenMesh as om import numpy as np mesh = om.TriMesh() mesh_notebook = {} face_notebook = {} radius = 3.0/4.0/np.pi phi = 0.5*(1+np.sqrt(5)) a = radius / np.sqrt(phi*np.sqrt(5)) mesh_notebook[0,"handle"] = mesh.add_vertex([a*phi,0,a]) mesh_notebook[1,"handle"] = mesh.add_vertex([a*phi,0,-a]) mesh_notebook[2,"handle"] = mesh.add_vertex([-a*phi,0,-a]) mesh_notebook[3,"handle"] = mesh.add_vertex([-a*phi,0,a]) mesh_notebook[4,"handle"] = mesh.add_vertex([a,a*phi,0]) mesh_notebook[5,"handle"] = mesh.add_vertex([-a,a*phi,0]) mesh_notebook[6,"handle"] = mesh.add_vertex([-a,-a*phi,0]) mesh_notebook[7,"handle"] = mesh.add_vertex([a,-a*phi,0]) mesh_notebook[8,"handle"] = mesh.add_vertex([0,a,a*phi]) mesh_notebook[9,"handle"] = mesh.add_vertex([0,-a,a*phi]) mesh_notebook[10,"handle"] = mesh.add_vertex([0,-a,-a*phi]) mesh_notebook[11,"handle"] = mesh.add_vertex([0,a,-a*phi]) face_notebook[0,"handle"] = mesh.add_face(mesh_notebook[7,"handle"], mesh_notebook[0,"handle"], mesh_notebook[9,"handle"]) face_notebook[1,"handle"] = mesh.add_face(mesh_notebook[9,"handle"], mesh_notebook[7,"handle"], mesh_notebook[6,"handle"]) face_notebook[2,"handle"] = mesh.add_face(mesh_notebook[9,"handle"], mesh_notebook[6,"handle"], mesh_notebook[3,"handle"]) face_notebook[3,"handle"] = mesh.add_face(mesh_notebook[7,"handle"], mesh_notebook[1,"handle"], mesh_notebook[10,"handle"]) face_notebook[4,"handle"] = mesh.add_face(mesh_notebook[7,"handle"], mesh_notebook[10,"handle"], mesh_notebook[6,"handle"]) face_notebook[5,"handle"] = mesh.add_face(mesh_notebook[10,"handle"], mesh_notebook[6,"handle"], mesh_notebook[2,"handle"]) face_notebook[6,"handle"] = mesh.add_face(mesh_notebook[0,"handle"], mesh_notebook[1,"handle"], mesh_notebook[7,"handle"]) face_notebook[7,"handle"] = mesh.add_face(mesh_notebook[6,"handle"], mesh_notebook[3,"handle"], mesh_notebook[2,"handle"]) face_notebook[8,"handle"] = mesh.add_face(mesh_notebook[0,"handle"], mesh_notebook[4,"handle"], mesh_notebook[8,"handle"]) face_notebook[9,"handle"] = mesh.add_face(mesh_notebook[8,"handle"], mesh_notebook[4,"handle"], mesh_notebook[5,"handle"]) face_notebook[10,"handle"] = mesh.add_face(mesh_notebook[8,"handle"], mesh_notebook[3,"handle"], mesh_notebook[5,"handle"]) face_notebook[11,"handle"] = mesh.add_face(mesh_notebook[1,"handle"], mesh_notebook[4,"handle"], mesh_notebook[11,"handle"]) face_notebook[12,"handle"] = mesh.add_face(mesh_notebook[4,"handle"], mesh_notebook[5,"handle"], mesh_notebook[11,"handle"]) face_notebook[13,"handle"] = mesh.add_face(mesh_notebook[5,"handle"], mesh_notebook[11,"handle"], mesh_notebook[2,"handle"]) face_notebook[14,"handle"] = mesh.add_face(mesh_notebook[0,"handle"], mesh_notebook[1,"handle"], mesh_notebook[4,"handle"]) face_notebook[15,"handle"] = mesh.add_face(mesh_notebook[3,"handle"], mesh_notebook[2,"handle"], mesh_notebook[5,"handle"]) face_notebook[16,"handle"] = mesh.add_face(mesh_notebook[0,"handle"], mesh_notebook[8,"handle"], mesh_notebook[9,"handle"]) face_notebook[17,"handle"] = mesh.add_face(mesh_notebook[9,"handle"], mesh_notebook[8,"handle"], mesh_notebook[3,"handle"]) face_notebook[18,"handle"] = mesh.add_face(mesh_notebook[1,"handle"], mesh_notebook[10,"handle"], mesh_notebook[11,"handle"]) face_notebook[19,"handle"] = mesh.add_face(mesh_notebook[2,"handle"], mesh_notebook[10,"handle"], mesh_notebook[11,"handle"]) print(len(mesh.faces())) # should be 20 for edge in mesh.vf(mesh_notebook[0,"handle"]): print(edge.idx()) # should be 5 for j,i in face_notebook: print(face_notebook[j,i].idx()) # some are defined as -1 - default behavior?

Hi, this is indeed the expected behavior, as OpenMesh requires the vertices of incident faces to be ordered in a consistent manner. In OpenMesh an edge connecting the vertices v1 and v2 is represented by two halfedges pointing in opposite directions, i.e. one halfedge pointing from v1 to v2 and one pointing from v2 to v1. Each halfedge is associated with one face and the order of the vertices of that face has to be consistent with the halfedge direction. You can think of it in terms of consistent normal orientation: For example, the first two triangles added to the mesh in your snippet are (7, 0, 9) and (9, 7, 6). These triangles share the edge connecting vertices 9 and 7, but their normals (defined by the order of their vertices) point in opposite directions. This is why you should see an error message “PolyMeshT::add_face: complex edge”. You can learn more about OpenMesh’s halfedge data structure here: https://www.openmesh.org/media/Documentations/OpenMesh-6.1-Documentation/a00... Also, this stackoverflow post has some nice images: https://stackoverflow.com/questions/24205196/addfacecomplex-edge-error-in-op... Best regards, Alexander Dielen On 10. Apr 2019, at 10:25, Marc Siggel <marc.siggel@biophys.mpg.de<mailto:marc.siggel@biophys.mpg.de>> wrote: Hi, I am new to OpenMesha and currently working with the python bindings to build a quick prototype of my code before moving to c++. attached a simple icosahedron structure. I defined 12 vertices and 20 faces. When using the vf circulator I experience that not all neighbouring face of a vertex are found. I also found that the number of faces is sensitive to the order of the vertex handles when defining a face. when looking at the indices some are set to -1. Is this expected behavior or am I missing something fundamental? Please find the minimal example below. Any help or comments would be greatly appreciated! Best regards, Marc Siggel import OpenMesh as om import numpy as np mesh = om.TriMesh() mesh_notebook = {} face_notebook = {} radius = 3.0/4.0/np.pi phi = 0.5*(1+np.sqrt(5)) a = radius / np.sqrt(phi*np.sqrt(5)) mesh_notebook[0,"handle"] = mesh.add_vertex([a*phi,0,a]) mesh_notebook[1,"handle"] = mesh.add_vertex([a*phi,0,-a]) mesh_notebook[2,"handle"] = mesh.add_vertex([-a*phi,0,-a]) mesh_notebook[3,"handle"] = mesh.add_vertex([-a*phi,0,a]) mesh_notebook[4,"handle"] = mesh.add_vertex([a,a*phi,0]) mesh_notebook[5,"handle"] = mesh.add_vertex([-a,a*phi,0]) mesh_notebook[6,"handle"] = mesh.add_vertex([-a,-a*phi,0]) mesh_notebook[7,"handle"] = mesh.add_vertex([a,-a*phi,0]) mesh_notebook[8,"handle"] = mesh.add_vertex([0,a,a*phi]) mesh_notebook[9,"handle"] = mesh.add_vertex([0,-a,a*phi]) mesh_notebook[10,"handle"] = mesh.add_vertex([0,-a,-a*phi]) mesh_notebook[11,"handle"] = mesh.add_vertex([0,a,-a*phi]) face_notebook[0,"handle"] = mesh.add_face(mesh_notebook[7,"handle"], mesh_notebook[0,"handle"], mesh_notebook[9,"handle"]) face_notebook[1,"handle"] = mesh.add_face(mesh_notebook[9,"handle"], mesh_notebook[7,"handle"], mesh_notebook[6,"handle"]) face_notebook[2,"handle"] = mesh.add_face(mesh_notebook[9,"handle"], mesh_notebook[6,"handle"], mesh_notebook[3,"handle"]) face_notebook[3,"handle"] = mesh.add_face(mesh_notebook[7,"handle"], mesh_notebook[1,"handle"], mesh_notebook[10,"handle"]) face_notebook[4,"handle"] = mesh.add_face(mesh_notebook[7,"handle"], mesh_notebook[10,"handle"], mesh_notebook[6,"handle"]) face_notebook[5,"handle"] = mesh.add_face(mesh_notebook[10,"handle"], mesh_notebook[6,"handle"], mesh_notebook[2,"handle"]) face_notebook[6,"handle"] = mesh.add_face(mesh_notebook[0,"handle"], mesh_notebook[1,"handle"], mesh_notebook[7,"handle"]) face_notebook[7,"handle"] = mesh.add_face(mesh_notebook[6,"handle"], mesh_notebook[3,"handle"], mesh_notebook[2,"handle"]) face_notebook[8,"handle"] = mesh.add_face(mesh_notebook[0,"handle"], mesh_notebook[4,"handle"], mesh_notebook[8,"handle"]) face_notebook[9,"handle"] = mesh.add_face(mesh_notebook[8,"handle"], mesh_notebook[4,"handle"], mesh_notebook[5,"handle"]) face_notebook[10,"handle"] = mesh.add_face(mesh_notebook[8,"handle"], mesh_notebook[3,"handle"], mesh_notebook[5,"handle"]) face_notebook[11,"handle"] = mesh.add_face(mesh_notebook[1,"handle"], mesh_notebook[4,"handle"], mesh_notebook[11,"handle"]) face_notebook[12,"handle"] = mesh.add_face(mesh_notebook[4,"handle"], mesh_notebook[5,"handle"], mesh_notebook[11,"handle"]) face_notebook[13,"handle"] = mesh.add_face(mesh_notebook[5,"handle"], mesh_notebook[11,"handle"], mesh_notebook[2,"handle"]) face_notebook[14,"handle"] = mesh.add_face(mesh_notebook[0,"handle"], mesh_notebook[1,"handle"], mesh_notebook[4,"handle"]) face_notebook[15,"handle"] = mesh.add_face(mesh_notebook[3,"handle"], mesh_notebook[2,"handle"], mesh_notebook[5,"handle"]) face_notebook[16,"handle"] = mesh.add_face(mesh_notebook[0,"handle"], mesh_notebook[8,"handle"], mesh_notebook[9,"handle"]) face_notebook[17,"handle"] = mesh.add_face(mesh_notebook[9,"handle"], mesh_notebook[8,"handle"], mesh_notebook[3,"handle"]) face_notebook[18,"handle"] = mesh.add_face(mesh_notebook[1,"handle"], mesh_notebook[10,"handle"], mesh_notebook[11,"handle"]) face_notebook[19,"handle"] = mesh.add_face(mesh_notebook[2,"handle"], mesh_notebook[10,"handle"], mesh_notebook[11,"handle"]) print(len(mesh.faces())) # should be 20 for edge in mesh.vf(mesh_notebook[0,"handle"]): print(edge.idx()) # should be 5 for j,i in face_notebook: print(face_notebook[j,i].idx()) # some are defined as -1 - default behavior? _______________________________________________ OpenMesh mailing list -- openmesh@lists.rwth-aachen.de<mailto:openmesh@lists.rwth-aachen.de> To unsubscribe send an email to openmesh-leave@lists.rwth-aachen.de<mailto:openmesh-leave@lists.rwth-aachen.de> https://lists.rwth-aachen.de/postorius/lists/openmesh.lists.rwth-aachen.de

Hi, Following up on this issue, is there sample code for creating a consistent triangle ordering from a connected triangle 'soup'. Or how can one catch this error and reverse the triangle before adding it. Thanks On Thu, Apr 11, 2019 at 2:01 AM Dielen, Alexander Michael < alexander.dielen@rwth-aachen.de> wrote:
Hi,
this is indeed the expected behavior, as OpenMesh requires the vertices of incident faces to be ordered in a consistent manner.
In OpenMesh an edge connecting the vertices v1 and v2 is represented by two halfedges pointing in opposite directions, i.e. one halfedge pointing from v1 to v2 and one pointing from v2 to v1. Each halfedge is associated with one face and the order of the vertices of that face has to be consistent with the halfedge direction.
You can think of it in terms of consistent normal orientation: For example, the first two triangles added to the mesh in your snippet are (7, 0, 9) and (9, 7, 6). These triangles share the edge connecting vertices 9 and 7, but their normals (defined by the order of their vertices) point in opposite directions.
This is why you should see an error message “PolyMeshT::add_face: complex edge”.
You can learn more about OpenMesh’s halfedge data structure here: https://www.openmesh.org/media/Documentations/OpenMesh-6.1-Documentation/a00...
Also, this stackoverflow post has some nice images: https://stackoverflow.com/questions/24205196/addfacecomplex-edge-error-in-op...
Best regards, Alexander Dielen
On 10. Apr 2019, at 10:25, Marc Siggel <marc.siggel@biophys.mpg.de> wrote:
Hi,
I am new to OpenMesha and currently working with the python bindings to build a quick prototype of my code before moving to c++. attached a simple icosahedron structure. I defined 12 vertices and 20 faces. When using the vf circulator I experience that not all neighbouring face of a vertex are found. I also found that the number of faces is sensitive to the order of the vertex handles when defining a face. when looking at the indices some are set to -1. Is this expected behavior or am I missing something fundamental?
Please find the minimal example below. Any help or comments would be greatly appreciated!
Best regards, Marc Siggel
import OpenMesh as om import numpy as np mesh = om.TriMesh() mesh_notebook = {} face_notebook = {} radius = 3.0/4.0/np.pi phi = 0.5*(1+np.sqrt(5)) a = radius / np.sqrt(phi*np.sqrt(5))
mesh_notebook[0,"handle"] = mesh.add_vertex([a*phi,0,a]) mesh_notebook[1,"handle"] = mesh.add_vertex([a*phi,0,-a]) mesh_notebook[2,"handle"] = mesh.add_vertex([-a*phi,0,-a]) mesh_notebook[3,"handle"] = mesh.add_vertex([-a*phi,0,a]) mesh_notebook[4,"handle"] = mesh.add_vertex([a,a*phi,0]) mesh_notebook[5,"handle"] = mesh.add_vertex([-a,a*phi,0]) mesh_notebook[6,"handle"] = mesh.add_vertex([-a,-a*phi,0]) mesh_notebook[7,"handle"] = mesh.add_vertex([a,-a*phi,0]) mesh_notebook[8,"handle"] = mesh.add_vertex([0,a,a*phi]) mesh_notebook[9,"handle"] = mesh.add_vertex([0,-a,a*phi]) mesh_notebook[10,"handle"] = mesh.add_vertex([0,-a,-a*phi]) mesh_notebook[11,"handle"] = mesh.add_vertex([0,a,-a*phi])
face_notebook[0,"handle"] = mesh.add_face(mesh_notebook[7,"handle"], mesh_notebook[0,"handle"], mesh_notebook[9,"handle"]) face_notebook[1,"handle"] = mesh.add_face(mesh_notebook[9,"handle"], mesh_notebook[7,"handle"], mesh_notebook[6,"handle"]) face_notebook[2,"handle"] = mesh.add_face(mesh_notebook[9,"handle"], mesh_notebook[6,"handle"], mesh_notebook[3,"handle"]) face_notebook[3,"handle"] = mesh.add_face(mesh_notebook[7,"handle"], mesh_notebook[1,"handle"], mesh_notebook[10,"handle"]) face_notebook[4,"handle"] = mesh.add_face(mesh_notebook[7,"handle"], mesh_notebook[10,"handle"], mesh_notebook[6,"handle"]) face_notebook[5,"handle"] = mesh.add_face(mesh_notebook[10,"handle"], mesh_notebook[6,"handle"], mesh_notebook[2,"handle"]) face_notebook[6,"handle"] = mesh.add_face(mesh_notebook[0,"handle"], mesh_notebook[1,"handle"], mesh_notebook[7,"handle"]) face_notebook[7,"handle"] = mesh.add_face(mesh_notebook[6,"handle"], mesh_notebook[3,"handle"], mesh_notebook[2,"handle"]) face_notebook[8,"handle"] = mesh.add_face(mesh_notebook[0,"handle"], mesh_notebook[4,"handle"], mesh_notebook[8,"handle"]) face_notebook[9,"handle"] = mesh.add_face(mesh_notebook[8,"handle"], mesh_notebook[4,"handle"], mesh_notebook[5,"handle"]) face_notebook[10,"handle"] = mesh.add_face(mesh_notebook[8,"handle"], mesh_notebook[3,"handle"], mesh_notebook[5,"handle"]) face_notebook[11,"handle"] = mesh.add_face(mesh_notebook[1,"handle"], mesh_notebook[4,"handle"], mesh_notebook[11,"handle"]) face_notebook[12,"handle"] = mesh.add_face(mesh_notebook[4,"handle"], mesh_notebook[5,"handle"], mesh_notebook[11,"handle"]) face_notebook[13,"handle"] = mesh.add_face(mesh_notebook[5,"handle"], mesh_notebook[11,"handle"], mesh_notebook[2,"handle"]) face_notebook[14,"handle"] = mesh.add_face(mesh_notebook[0,"handle"], mesh_notebook[1,"handle"], mesh_notebook[4,"handle"]) face_notebook[15,"handle"] = mesh.add_face(mesh_notebook[3,"handle"], mesh_notebook[2,"handle"], mesh_notebook[5,"handle"]) face_notebook[16,"handle"] = mesh.add_face(mesh_notebook[0,"handle"], mesh_notebook[8,"handle"], mesh_notebook[9,"handle"]) face_notebook[17,"handle"] = mesh.add_face(mesh_notebook[9,"handle"], mesh_notebook[8,"handle"], mesh_notebook[3,"handle"]) face_notebook[18,"handle"] = mesh.add_face(mesh_notebook[1,"handle"], mesh_notebook[10,"handle"], mesh_notebook[11,"handle"]) face_notebook[19,"handle"] = mesh.add_face(mesh_notebook[2,"handle"], mesh_notebook[10,"handle"], mesh_notebook[11,"handle"])
print(len(mesh.faces())) # should be 20 for edge in mesh.vf(mesh_notebook[0,"handle"]): print(edge.idx()) # should be 5 for j,i in face_notebook: print(face_notebook[j,i].idx()) # some are defined as -1 - default behavior? _______________________________________________ OpenMesh mailing list -- openmesh@lists.rwth-aachen.de To unsubscribe send an email to openmesh-leave@lists.rwth-aachen.de https://lists.rwth-aachen.de/postorius/lists/openmesh.lists.rwth-aachen.de
_______________________________________________ OpenMesh mailing list -- openmesh@lists.rwth-aachen.de To unsubscribe send an email to openmesh-leave@lists.rwth-aachen.de https://lists.rwth-aachen.de/postorius/lists/openmesh.lists.rwth-aachen.de

Thanks Alexander! The stackoverflow thread and your explanation were very helpful and everything works now. The python version does not throw any error. It's a silent error and only when looking at the number of faces I realized this issue exists. Maybe one could add a warning or exception in case a face was not correctly defined? I agree with Andrew that having a tool to convert an unordered list of triangles containing vertex handles to an ordered list for face definitions would be very useful. Thanks again and best regards, Marc
participants (3)
-
Andrew Cunningham
-
Dielen, Alexander Michael
-
Marc Siggel