1
$\begingroup$

I'm creating a script that converts a loop of faces to an edge ring.

This is super easy if the faces loop all the way around because I just subtract the border loop and I'm left with the inner ring, but doesn't work in this situation.

How can I do this for a partial loop of faces with python? (see image)

$\endgroup$
1
$\begingroup$

This might not be the most elegant way of doing it but it works.

Also, I added a lot of extra stuff to account for odd situations, for example: when you have a partial loop of faces that suddenly changes direction (like an "L").

The point of all this was to integrate it into a bigger context sensitive tool that splits/subdivides your selection with a single edge loop and selects it (regardless of what your initial selection was) and that I can call with a single hotkey.

Next step... Modal operator!

Here it is:

import bpy, bmesh

context = bpy.context
ob = context.edit_object
me = ob.data
bm = bmesh.from_edit_mesh(me)

sel_mode = bpy.context.tool_settings.mesh_select_mode[:]

vert_sel = [v for v in bm.verts if v.select]
edge_sel = set(e for e in bm.edges if e.select)
face_sel = set(f for f in bm.faces if f.select)


#FACE MODE
if sel_mode[2] and len(face_sel) > 1:
    #Store the selected loop of faces as an edge ring
    contained_edges = edge_sel - set(e for e in bm.edges 
                            if e.select and (e.is_boundary or not all(f.select for f in e.link_faces)))

    #Store the border loop of edges as well                      
    bpy.ops.mesh.region_to_loop()
    border_edges = set(e for e in bm.edges if e.select)

    #We're have to do some extra stuff just in case we only selected a partial loop of faces
    #or a partial loop that changes direction
    bpy.ops.mesh.select_all(action='DESELECT')     
    for e in contained_edges: e.select = True                  

    #Extend and store the full ring because (contained_edges) is missing the first and last
    #edges of the ring if you only selected a partial loop of faces
    bpy.ops.mesh.loop_multi_select(ring=True)
    ring_edges =  set(e for e in bm.edges if e.select)

    #Extending the full ring gives us some corner edges we dont need if we changed direction so lets get them!
    bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='FACE')
    bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='EDGE')
    corner_edges = set(e for e in bm.edges if e.select).intersection(border_edges)

    #Update our ring edges. Still just in case we had a partial loop of faces
    ring_edges -= corner_edges

    #Lets just create a new Set at this point and do everything we need in one line. It works though!
    final_ring_sel = (border_edges.intersection(ring_edges) - corner_edges).union(contained_edges)

    bpy.ops.mesh.select_all(action='DESELECT')

    #Subdivide the final ring
    new_edges = bmesh.ops.subdivide_edges(bm, edges=list(final_ring_sel), cuts=1)

    #Select the new edges we created and extend the loop because there's a missing edge
    #whenever we have a corner
    for e in new_edges['geom_inner']: e.select = True
    bpy.ops.mesh.loop_multi_select(ring=False)

bmesh.update_edit_mesh(me)
$\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.