2
$\begingroup$

So i need to perform some basic loop cut and slide operators on a mesh. The edge index is known and the slide amount is also fixed.

using override method from How do I override context for bpy.ops.mesh.loopcut? i am able to perform the cut successfully inside blender script editor. but when run without the UI the the script gives an error.

I would also like to know if there is an alternate non-UI based methods to perform loop cuts with proper UV updates

here's an example version which adds loop cuts and smooth operation. tested on Blender 2.83 script editor

import bpy

bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete(use_global=False)

bpy.ops.mesh.primitive_cube_add(enter_editmode=False, align='WORLD', location=(0, 0, 0))
bpy.ops.object.editmode_toggle()


win      = bpy.context.window
scr      = win.screen
areas3d  = [area for area in scr.areas if area.type == 'VIEW_3D']
region   = [region for region in areas3d[0].regions if region.type == 'WINDOW']

override = {'window':win,
            'screen':scr,
            'area'  :areas3d[0],
            'region':region[0],
            'scene' :bpy.context.scene,
            }


bpy.ops.mesh.loopcut_slide(override, 
MESH_OT_loopcut={
"number_cuts":5, 
"smoothness":1, 
"falloff":'INVERSE_SQUARE', 
"object_index":0, 
"edge_index":4, 
"mesh_select_mode_init":(False, True, False)
}, 
TRANSFORM_OT_edge_slide={
"value":0,
"single_side":False,
"use_even":False,
"flipped":False,
"use_clamp":True,
"mirror":True,
"snap":False,
"snap_target":'CLOSEST',
"snap_point":(0, 0, 0),
"snap_align":False,
"snap_normal":(0, 0, 0),
"correct_uv":False,
"release_confirm":False,
"use_accurate":False})

bpy.ops.object.editmode_toggle()

```
$\endgroup$
1
$\begingroup$

Subdivide edge loops is a good fit for loop cut slide.

The loop cut and slide is an advanced operator that is doing a bit behind the scene. Can select a single edge and loop cut will select and cut the edge ring defined by the edge.

Bmesh subdivide edge operators

If an edge loop is fed into the bmesh subdivide edges operator with use grid fill it emulates a basic loop cut and slide.

enter image description here

As well there is the subdivide edge ring bmesh operator, which will throw an error if the edges submitted are not valid edge rings.

enter image description here Sub'd edge rings. Inverse square profile, factor 0.5

There are a number of options available for both operators, including setting the spacings of the cuts, and smooth falloff type.

Because loop cut and slide is subdividing the edges will notice a number of familiar options.

Consult the docs..

See Adding a Cuboid Mesh to see this in operation to create an $i \times j \times k$ cuboid.

Selecting the edge rings.

Have used a recursive method similar to that shown here.

How to find a mesh loops and rings

Have not added any checks re not finding a valid edge ring. (eg last edges found would want to be same or boundary)

Test script, select edge in edit mode and run script. Uses the active edge to select edge ring.

import bpy
import bmesh

cuts = 4

context = bpy.context
ob = context.object
me = ob.data

bm = bmesh.from_edit_mesh(me)
edge = bm.select_history.active
def edge_loops(edge):
    def walk(edge):
        yield edge
        edge.tag = True
        for l in edge.link_loops:
            loop = l.link_loop_radial_next.link_loop_next.link_loop_next
            if not (len(loop.face.verts) != 4 or loop.edge.tag):
                yield from walk(loop.edge)
    for e in bm.edges:
        e.tag = False
    return list(walk(edge))
        
if isinstance(edge, bmesh.types.BMEdge): 
    ''' 
    bmesh.ops.subdivide_edges(
        bm,
        edges=edge_loops(edge),
        cuts=cuts,
        smooth_falloff='INVERSE_SQUARE',
        use_grid_fill=True,
        )
    '''
    bmesh.ops.subdivide_edgering(
        bm,
        edges=edge_loops(edge),
        cuts=cuts,
        profile_shape='INVERSE_SQUARE',
        profile_shape_factor=0.0,
        )    
    bmesh.update_edit_mesh(me)

Note have used an edit mode bmesh by way of example. For object mode script load the bmesh as shown in your answer. IMO bisect is not going to give a good result if edge loop edges are not nicely aligned.

Please refrain from posting delete all objects code in question scripts when it is not necessary.

$\endgroup$
1
$\begingroup$

bmesh bisect does exactly this...but its not automated like bpy loopcut so make sure to select all the faces you need to cut and you'll need to provide the cutting location & direction yourself.

import bpy
import bmesh
import math
import mathutils

bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete(use_global=False)
bpy.ops.mesh.primitive_plane_add(enter_editmode=False, align='WORLD', location=(0, 0, 0))

def bmesh_loopcut(bm,face_list,direction_axis,center='auto'):
    for f in bm.faces:
        f.select = False
    bm.faces.ensure_lookup_table()  
    for f in face_list:
        bm.faces[f].select = True

    edges = [e for e in bm.edges if e.select == True]
    faces = [f for f in bm.faces if f.select == True]
    
    if center=='auto':    
        weights = [f.calc_area() for f in faces]
        weighted_centres = [f.calc_area() * f.calc_center_median() for f in faces]
        cutting_point = sum(weighted_centres, mathutils.Vector()) / sum(weights)
    else:
        cutting_point = bpy.context.scene.cursor.location
    geom = []
    geom.extend(edges)
    geom.extend(faces) 

    result = bmesh.ops.bisect_plane(bm,dist=0.01,geom=geom,plane_co=cutting_point,plane_no=direction_axis)



# Make a new BMesh
bm = bmesh.new()

obj = bpy.context.object

ob = obj
me = ob.data 
bm = bmesh.new() 
bm.from_mesh(me)


face_cut=([0])
bmesh_loopcut(bm,face_cut,[1,0,0])
    
bm.to_mesh(me)
bm.free()
me.update()

```
$\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.