Comment utiliser l'opération loopcut_slide sans aucune interface utilisateur?

Oct 01 2020

J'ai donc besoin d'exécuter des opérateurs de coupe et de glissement de boucle de base sur un maillage. L'index de bord est connu et le montant de la diapositive est également fixe.

en utilisant la méthode de remplacement de Comment remplacer le contexte pour bpy.ops.mesh.loopcut? je suis capable d'effectuer la coupe avec succès dans l'éditeur de script de blender. mais lorsqu'il est exécuté sans l'interface utilisateur, le script donne une erreur.

Je voudrais également savoir s'il existe une autre méthode non basée sur l'interface utilisateur pour effectuer des coupes en boucle avec des mises à jour UV appropriées

voici une version d'exemple qui ajoute des coupes de boucle et un fonctionnement en douceur. testé sur l'éditeur de script Blender 2.83

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()

```

Réponses

1 batFINGER Oct 11 2020 at 22:15

Subdiviser les boucles de bord est un bon ajustement pour la diapositive coupée en boucle.

La boucle couper et glisser est un opérateur avancé qui fait un peu derrière la scène. Peut sélectionner un seul bord et la coupe en boucle sélectionnera et coupera l'anneau de bord défini par le bord.

Opérateurs d'arête de subdivision Bmesh

Si une boucle d'arête est introduite dans l'opérateur bmesh subdivide arêtes avec l'utilisation de remplissage de grille, elle émule une boucle de base couper et glisser.

De plus, il y a l'opérateur de subdivision edge ring bmesh, qui générera une erreur si les arêtes soumises ne sont pas des anneaux de bord valides.

Anneaux de bord sub'd. Profil carré inversé, facteur 0,5

Il existe un certain nombre d'options disponibles pour les deux opérateurs, notamment la définition des espacements des coupes et le type de réduction lisse.

Parce que la coupe en boucle et le glissement subdivisent les bords, vous remarquerez un certain nombre d'options familières.

Consultez la documentation.

Voir Ajout d'un maillage cuboïde pour voir cela en fonctionnement pour créer un$i \times j \times k$ cuboïde.

Sélection des anneaux de bord.

Ont utilisé une méthode récursive similaire à celle illustrée ici.

Comment trouver des boucles et des anneaux en maille

N'ajoutez aucune vérification pour ne pas trouver un anneau de bord valide. (par exemple, les dernières arêtes trouvées voudraient être identiques ou une limite)

Tester le script, sélectionner l'arête en mode édition et exécuter le script. Utilise l'arête active pour sélectionner l'anneau d'arête.

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 ont utilisé un mode d'édition bmesh à titre d'exemple. Pour le script en mode objet, chargez le bmesh comme indiqué dans votre réponse . La bissectrice IMO ne donnera pas un bon résultat si les bords de la boucle de bord ne sont pas bien alignés.

Veuillez vous abstenir de publier le code de tous les objets dans les scripts de question lorsque cela n'est pas nécessaire.

1 Prime007 Oct 11 2020 at 14:26

bmesh bisect fait exactement cela ... mais ce n'est pas automatisé comme bpy loopcut, alors assurez-vous de sélectionner toutes les faces que vous devez couper et vous devrez fournir vous-même l'emplacement et la direction de la coupe.

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()

```