1
$\begingroup$

I encounter the following problem. If, somewhere in my script, I switch from Object mode to Edit Mode and back, the indices of the elements in my object become messed up.

To recreate the problem: Create a cube (or any mesh object for that matter) and run the following Python snippet:

import bpy

object = bpy.context.active_object

for v in object.data.vertices:
    print('1',v.index)
    bpy.ops.object.mode_set(mode = 'EDIT')
    bpy.ops.object.mode_set(mode = 'OBJECT')
    print('2',v.index)

I expect the following output:

1 0
2 0
1 1
2 1
1 2
2 2
1 3
2 3
1 4
2 4
1 5
2 5
1 6
2 6
1 7
2 7
1 8
2 8

However, I end up with the following output (the numbers change seemingly randomly everytime I run the script):

1 0
2 123
1 124
2 -166
1 -165
2 116
1 117
2 -164
1 -163
2 153
1 154
2 -162
1 -161
2 182
1 183
2 -160

Does anyone know what causes this problem and how to solve/workaround this? I prefer NOT to use the BMesh module, since the bpy operators suit my needs much better.

Thanks in advance,

Niels

$\endgroup$
4
  • $\begingroup$ Can only manipulate mesh data (eg me.vertices) in object mode. eg to set a vert to selected in object mode v.select = True then flip into edit mode it's selected. Setting it in edit mode does nada. Blender makes a (lets call it a) copy and places in edit mode and writes it back when toggling back to object mode. Hence a reference to a vertex before and after will not work as expected. It would be the same case with bmesh. An object mode bmesh wont pick up any changes if done (after loading) to the mesh in edit mode and an edit mode one will be dead after the toggle. This may be a $\endgroup$ – batFINGER Mar 31 '20 at 12:56
  • $\begingroup$ far more answerable question if you describe what you are intending to do that requires having a vert reference in object mode to use after a toggle. If a vert is deleted in edit mode above, the code would quite likely crash blender. $\endgroup$ – batFINGER Mar 31 '20 at 12:59
  • $\begingroup$ Thanks for your answer batFINGER. What I intend to do is calculate a gradient for each vertex. So in pseudocode, it would look like this: $\endgroup$ – Niels Mar 31 '20 at 13:09
  • $\begingroup$ 1) For every vertex in object 1a) Select this vertex (v.select = True) 1b) Select neighbor vertices (bpy.ops.mesh.select_more()) 1c) Store normal angles 2) Compare normal angles within selection... etc. For being able to perform a bpy operator (select_more() in this case) I need to toggle to Edit Mode. While (as you said) for manipulating mesh I need to be in Object Mode. So I constantly need to toggle, right? $\endgroup$ – Niels Mar 31 '20 at 13:25
1
$\begingroup$

No reason an edit mode bmesh cannot be used with edit ops.

I prefer NOT to use the BMesh module, since the bpy operators suit my needs much better.

They are not mutually exclusive.

In answer to

1) For every vertex in object 1a) Select this vertex (v.select = True) 1b) Select neighbor vertices (bpy.ops.mesh.select_more()) 1c) Store normal angles 2) Compare normal angles within selection... etc. For being able to perform a bpy operator (select_more() in this case) I need to toggle to Edit Mode. While (as you said) for manipulating mesh I need to be in Object Mode. So I constantly need to toggle, right?

As a simple example, cube in edit mode each vert selected separately and default select more (one face away) called. For the cube this will result in only one vert being unselected. Note: Select more can also be called with bpy.ops.mesh.select_more(use_face_step=False) to only select via edge connection.

import bpy
import bmesh

context = bpy.context

ob = context.edit_object
me = ob.data

bm = bmesh.from_edit_mesh(me)
print("-" * 33)
for v in bm.verts:
    bpy.ops.mesh.select_all(action='DESELECT')
    v.select_set(True)
    print("Before", v.index, [v.select for v in bm.verts])
    bpy.ops.mesh.select_more()
    print("After", v.index, [v.select for v in bm.verts])

bmesh.update_edit_mesh(me)

Produces output.

---------------------------------
Before 0 [True, False, False, False, False, False, False, False]
After 0 [True, True, True, True, True, True, False, True]
Before 1 [False, True, False, False, False, False, False, False]
After 1 [True, True, True, True, True, True, True, False]
Before 2 [False, False, True, False, False, False, False, False]
After 2 [True, True, True, True, False, True, True, True]
Before 3 [False, False, False, True, False, False, False, False]
After 3 [True, True, True, True, True, False, True, True]
Before 4 [False, False, False, False, True, False, False, False]
After 4 [True, True, False, True, True, True, True, True]
Before 5 [False, False, False, False, False, True, False, False]
After 5 [True, True, True, False, True, True, True, True]
Before 6 [False, False, False, False, False, False, True, False]
After 6 [False, True, True, True, True, True, True, True]
Before 7 [False, False, False, False, False, False, False, True]
After 7 [True, False, True, True, True, True, True, True]

Strongly recommend simply using bmesh for this kind of thing. The linked geometry of an element is readily available.

import bpy
import bmesh

context = bpy.context

ob = context.edit_object
me = ob.data

bm = bmesh.from_edit_mesh(me)
print("-" * 33)
for v in bm.verts:
    # set of all verts surounding based on linked faces
    face_neighbours = {v for f in v.link_faces 
            for v in f.verts} - {v}  
    # list of edge neighbours        
    edge_neighbours = [e.other_vert(v) for e in v.link_edges]
    print(v.index, [v.index for v in face_neighbours])
bmesh.update_edit_mesh(me)

Producing output

---------------------------------
0 [1, 2, 3, 4, 5, 7]
1 [0, 2, 3, 4, 5, 6]
2 [0, 1, 3, 5, 6, 7]
3 [0, 1, 2, 4, 6, 7]
4 [0, 1, 3, 5, 6, 7]
5 [0, 1, 2, 4, 6, 7]
6 [1, 2, 3, 4, 5, 7]
7 [0, 2, 3, 4, 5, 6]

An example to get the angle between vert and edge connected verts' normals

[v.normal.angle(n.normal) for n in edge_neighbours]

Hopefully the examples above might move you a little towards bmesh. Consider the first example above used on a million vertex mesh, would require two million operator calls, as would the question code.

Python performance with Blender operators

Second example requires no operators, nor does it change the selection state. Actually There is no need to update back to the mesh, or use edit mode to obtain all the info needed for this example

Note worth mentioning for this example could be done in object mode directly from mesh data. Quick python console code example, look for faces that contain the vert index.

>>> me = C.object.data
>>> v = me.vertices[0]
>>> v.index
0

>>> set(sum([list(p.vertices) for p in me.polygons if v.index in p.vertices], [])) - {v.index}
{1, 2, 3, 4, 5, 7}

as in both examples above the only vert not in any face containing vert 0 is vert 6.

$\endgroup$
1
  • $\begingroup$ Yes, amazing! This is exactly what I meant. Apparently, the BMesh module has more advantages than I thought. $\endgroup$ – Niels Apr 1 '20 at 14:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.