4
$\begingroup$

I run into this issue when writing Python scripts. When I am selecting several individual verts and I make an extrusion, I get some odd behavior I cannot explain. It doesn't happen if I am working with edges or faces. For example, I open a fresh instance of Blender, select the default cube, go into edit more, deselect all, and run

import bpy, bmesh
# have cube as active object, in edit mode, no verts selected
mesh = bmesh.from_edit_mesh(bpy.context.object.data)
for v in mesh.verts:
    if v.co[2] > 0.0:
        v.select = True
bpy.ops.mesh.extrude_region_move(
    MESH_OT_extrude_region={"use_normal_flip":False, "mirror":False},
    TRANSFORM_OT_translate={"value":(0.0, 0.0, 2.0),
                            "orient_type":'GLOBAL',
                            "orient_matrix":((1, 0, 0), (0, 1, 0), (0, 0, 1)),
                            "orient_matrix_type":'GLOBAL',
                            "constraint_axis":(False, False, False),
                            "mirror":False,
                            "use_proportional_edit":False,
                            "proportional_edit_falloff":'SMOOTH',
                            "proportional_size":1,
                            "use_proportional_connected":False,
                            "use_proportional_projected":False,
                            "snap":False,
                            "snap_target":'CLOSEST',
                            "snap_point":(0, 0, 0),
                            "snap_align":False,
                            "snap_normal":(0, 0, 0),
                            "gpencil_strokes":False,
                            "cursor_transform":False,
                            "texture_space":False,
                            "remove_on_cancel":False,
                            "release_confirm":False,
                            "use_accurate":False})

I get this disjointed vert extrusion where the verts are extruded but they are just lines, not connected to each other, as if I had went around and extruded each vert individually.

I don't generally want that though. I found this odd little "hack" to get around the issue. I get the verts selected as desired, and then toggle out and back into edit mode before doing the extrusion, like this

import bpy, bmesh

mesh = bmesh.from_edit_mesh(bpy.context.object.data)
for v in mesh.verts:
    if v.co[2] > 0.0:
        v.select = True
bpy.ops.object.editmode_toggle()
bpy.ops.object.editmode_toggle()
bpy.ops.mesh.extrude_region_move(...)

and then the extrude behaves like this enter image description here

So I have a couple questions. Why does it do this? And is there a better way? What should I be doing instead?

$\endgroup$
4
$\begingroup$

Use bmesh operators.

Here is an edit mode script that finds any face in the mesh that has an upward facing normal (locally) and extrudes.

Notice there is no element selection (e.select = True) required, it's done purely on face normal. When using bpy.ops operators in edit mode need to keep in mind, and set, select mode (the issue above suggests vertex. Toggling modes flushes the selection, selecting the edges and face implied by your vert selection. ie Even though you have all verts of a face selected, the face isn't yet.)

>>> C.tool_settings.mesh_select_mode[:]
(False, False, True) #  vertex, edge, face

Recommend ditching edit mode bpy.ops whenever possible.

import bpy
import bmesh


ext = (0, 0, 2)
TOL = 1e-7

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

# select the top face

faces = [f for f in bm.faces if f.normal.angle((0, 0, 1)) < TOL]

if faces:  # 
    ret = bmesh.ops.extrude_discrete_faces(bm,
            faces=faces,
            )
    verts = list(set(v for f in ret['faces']
            for v in f.verts))

    bmesh.ops.translate(bm,
            verts = verts,
            vec = ext,
        )

    bmesh.update_edit_mesh(me)

I've used bmesh.ops.extrude_faces_discrete(...) however could also use

if faces:
    ret = bmesh.ops.extrude_face_region(bm,
            geom=faces,
            )
    verts = list(set(e for e in ret['geom']
            if isinstance(e, bmesh.types.BMVert)))

    bmesh.ops.translate(bm,
            verts = verts,
            vec = ext,
        )
    bmesh.ops.delete(bm,
            geom=faces,
            context='FACES')

    bmesh.update_edit_mesh(me)

when using edit mode bmesh, can often use the users selection

[f for f in bm.faces if f.select]

or

f = bm.select_history.active 

and check that is of type bmesh.types.BMFace.

$\endgroup$
  • $\begingroup$ That is a helpful response. Thank you. What I still do not understand, but want to understand, is how the selection flush implies correctly the edges and faces to select 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 $\endgroup$ – aaron Jan 4 at 17:30

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.