2
$\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 Answer 1

2
$\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$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .