1
$\begingroup$

I would like to know how Blender automatically selects the edges and faces implied by the verts selected in a manual multi-vert selection in edit mode.

For example, I take the default cube, jump into edit mode, deselect all, hold shift, and manually multi-select each vert on the top. As I click from the first to the second, the edge in between becomes also selected. As I click the fourth vert the face that is made by those verts also automatically becomes selected.

manual multi vert selection

When making the same kind of selection, but instead using the Python API, Blender does not automatically select the edges and faces for me implied by my vert selection.

import bpy, bmesh

bmesh.select_mode = {'VERT', 'EDGE', 'FACE'}
mesh = bmesh.from_edit_mesh(bpy.context.object.data)
for v in mesh.verts:
    if v.co[2] > 0.0:
        v.select = True

Even if I set the bmesh.select_mode to include verts, edges, and faces.

Python API vert selection via bmesh

Which I suppose has its uses as well. I ran into issues with this earlier though and posted this question Python API script, why does extruding verts behave in this way? which gives a bit more context on the subject. I thought this particular point deserved its own question though.

What I want to understand is how the selection flush, in the case of toggling modes, or the monitor in the case of manually working in edit mode implies correctly and selects the edges and faces based on my vert selection. Right now that seems like magic to me and I want to know if there is a programmatic routine for essentially doing the same thing (maybe with bmesh) that is more explicit than toggling modes via bpy.ops to force a selection flush.

One option I considered is iterating through all of the faces and edges that make up the mesh, and if all the the verts that make up that face/edge happen to also be selected, then also select that face/edge. That option seems very brute force and expensive in the case of a large mesh though. Are there any easier ways to do that? Is there an API function that already does this?

$\endgroup$
3
$\begingroup$

Flushing select mode.

Recommend naming a bmesh representation of a mesh bm and reserve mesh or me for a mesh.

bpy.ops.mesh operators manipulate the mesh. A bound bmesh, ie from_edit_mesh is still not the mesh. An edit mode mesh is what we don't have a handle on via python. Can set me.vertices properties in edit mode, but will have no effect on the "special" edit mode instance of the mesh.

Flushing selection, or updating the bmesh will ensure the mesh used by the operator has the changes. Put it in a fit context to be operated on

If the intention is using bound bmesh to select the geometry required by bpy.ops.mesh operator, flush the selection before the operator call.

As mentioned in prior answer, the results of your script will change depending on selection mode. It is part of the context bpy.ops.mesh operators use. This sets it to all.

>>> C.tool_settings.mesh_select_mode = (True, True, True)

enter image description here Result of running the script in prior question in only vert, only edge, and only face selection modes. No prior selections

Will also get the desired result if we select face in face selection mode, then switch to vert select mode and run script. The simplest fix of all to use bpy.ops.mesh with face selection from verts selection is to set one or both of faces and edges to True.

In the bmesh docs

Selection / Flushing

As mentioned above, it is possible to create an invalid selection state (by selecting a state and then de-selecting one of its vertices’s for example), mostly the best way to solve this is to flush the selection after performing a series of edits. this validates the selection state.

In Keeping a Correct State it states that vertices belonging to edge are selected when edge selected. Similarly for edges, (and hence verts) of a face.

It does not mention the converse is true. Selecting verts selects their edges and / or faces. (Speculatively This works from the UI since it has a handle on "special edit mode mesh" ie select 4 verts in UI, vert select only, then on running script to check, face is selected)

To set a face to selected, makes sense to select the sub-elements. Work like an update method. Face sets edges, edges set verts. To conversely select a vert then check all its linked edges and faces, and set to selected would fire off our update reelecting the selected. Or similarly as a property getter checking all the selection of sub-elements, could prove expensive.

Instead select_flush is used to sanitize state.

Akin to setting the context tool select mode calling bm.select_flush_mode() will flush the states according to those set in bmesh.select_mode.

Test scripts. both designed to select face and extrude even in select only vertex mode. context.tool_settings.mesh_select_mode = (True, False, False),

Selects verts, flushes mode, extrudes face.

import bpy 
import bmesh
context = bpy.context
me = context.object.data
bmesh.select_mode = {'VERT', 'EDGE', 'FACE'}
bm = bmesh.from_edit_mesh(me)
for v in bm.verts:
    v.select = v.co.z > 0
# flush the selection
bm.select_flush_mode()    

bpy.ops.mesh.extrude_region_move(
        TRANSFORM_OT_translate={"value" : (0, 0, 2)}
        )

If we use faces can skip the flush call and select the faces based on the vert selection

    for f in bm.faces:
        f.select = all(v.select for v in f.verts) 

but, IMO if you are going to extrude regions, generally only want faces, not edges or verts, so rather than diddle around with vertex selection, select faces instead (and vertex edge selection is done for us) The so called brute force approach

    for f in bm.faces:
        f.select = all(v.co.z > 0 for v in f.verts) 

Test script.

import bpy 
import bmesh


context = bpy.context
me = context.object.data
bm = bmesh.from_edit_mesh(me)  

for f in bm.faces:
    f.select = all(v.co.z > 0 for v in f.verts) 

bpy.ops.mesh.extrude_region_move(
        TRANSFORM_OT_translate={"value" : (0, 0, 2)})

As mentioned in prior answer, recommend using bmesh.ops operators over bpy.ops.mesh... The geometry is passed to the operator, rather than manipulating context and selection.

Leave selection to the user, or a selection operator.

Can use the tag property

for v i bm.verts:
    v.tag = some_long_winded_test_bool_func

for f in bm.faces:
    f.select = all(v.tag for v in f.verts)

to avoid calculating per vert per face, and verts not part of a face.

| improve this answer | |
$\endgroup$

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.