Bridge Edge Loops By Height

I have a stack of ellipses with all normals aligned to the origin z (ie. pointing up) I want to programatically bridge the edges between each ellipse in order of height, but the underlying interpolation causes the following transformation. Note how the wider ellipse 3/4 of the way up the stack connects to the bottom one…

I’ve tried playing with the bridge edge loop settings in the scene inspector to see if there’s some tweaking that could resolve, but sadly not.
This is a toy example. The proper script will instantiate thousands of similar structures, so resolving by hand is not an option. Any advice would be greatly appreciated!!

First you should try with rings having no fill (0 faces, just vertices connected by edges)

Yeah, I started with no fill - same outcome sadly

If you do it programmatically simply do it incrementally.

Thanks for the suggestion, but I have tried an (inefficient) incremental implementation where I select the top face of the bottom most ellipse (they are sorted by name acc. to z_height), join with the next highest ellipse and then bridge: repeat.

    for i in range(len(bpy.data.collections['InProgress'].all_objects)):
        # Sort objects by Z height
        coll = [x for x in bpy.data.collections['InProgress'].all_objects]
        coll.sort(key=lambda x: x.name)
        coll[0].select_set(True)
        bpy.context.view_layer.objects.active = coll[0]
        
        # Select topmost face of bottom most object
        bpy.ops.object.editmode_toggle()
        mesh = bpy.context.active_object.data
        for p in mesh.polygons:
            if p.normal.z == 1:
                p.select=True
        bpy.ops.object.editmode_toggle()  
        
        # Select next highest ellipse, join and bridge
        coll[1].select_set(True)
        bpy.context.view_layer.objects.active = coll[1]
        bpy.ops.object.join()
        bpy.ops.object.editmode_toggle()
        bpy.ops.mesh.bridge_edge_loops()
        bpy.ops.object.editmode_toggle()
        bpy.ops.object.select_all(action='DESELECT')

This process has the same outcome as the original issue if the ellipses are filled or if they are just rings, results in another problematic outcome:

Ok, I dont have the time to write it for you, but I’d recommend to rewrite it. To continue from from what you have here you could do the following. First join all those objects manually into one object and just operate on that single object. Stay in edit mode in your script for the bridging. Find the two lowest faces ( eg by comparing the centers z value) of those whose normals are pointing up, select just those and call the bridge command. Then find the face with the last upper height and the one above and so on.

Thanks a million @Debuk, that worked a treat. For anyone who is looking for a solution to a similar problem (Note that this solution will only hold for connecting objects perpendicular to the z-axis):

 # Deselect all objects
    bpy.ops.object.select_all(action='DESELECT')

    # Join all objects
    for o in bpy.data.collections['InProgress'].all_objects:
        o.select_set(True)
        bpy.context.view_layer.objects.active = o
    bpy.ops.object.join()
    
    # Enter edit mode
    bpy.ops.object.editmode_toggle()
    
    # Get a BMesh representation of the active mesh
    obj = bpy.context.edit_object
    me = obj.data
    bm = bmesh.from_edit_mesh(me)
    
    # Build a list of faces with ZNorm == 1, sorted by height
    bm_faces = []
    for face in bm.faces:
        if face.normal.z == 1:
            bm_faces.append([face, round(face.verts[0].co.z, 2)])
    bm_faces.sort(key=lambda x: x[1], reverse=True)
    
    # Iterate through the list connecting each face sequentially
    for i in range(len(bm_faces)-1):
        bpy.ops.mesh.select_all(action = 'DESELECT')
        bm_faces[i][0].select=True
        bm_faces[i+1][0].select=True
        bpy.ops.mesh.bridge_edge_loops()
    bpy.ops.object.editmode_toggle()
1 Like