python: emplacement final différent après avoir défini l'emplacement en mode édition vs mode objet

Aug 29 2020

J'ai un script python pour générer une table à partir de cubes simples. Pour créer les jambes, j'ai enregistré les coordonnées des coins inférieurs de la table, puis je crée un cube à chaque emplacement pour commencer les jambes.

Le problème que j'ai rencontré est que la définition de l'emplacement des objets de cube de jambe nouvellement créés tout en étant toujours en mode d'édition fonctionne, mais donne des valeurs erronées lors de la définition de l'emplacement en mode objet. En outre, les valeurs en mode objet changent par intermittence si vous exécutez le script plusieurs fois.

Voici la partie clé du code, tout en restant entièrement en mode édition:

    def create_legs(self):
        
        log(bpy.context.mode)
        log(self.tabletop_bottom_corners_co)
        #set_mode('OBJECT')
        log(bpy.context.mode)
        log(self.tabletop_bottom_corners_co)
        
        self.legs = []
        for co in self.tabletop_bottom_corners_co:
            leg = self.create_cube()
            self.legs.append(leg)
            leg.location = co
        
        for leg in self.legs:
            log(leg.location)

quels journaux

create_legs: EDIT_MESH
create_legs: [Vector((-1.0, 0.5, 0.0)), Vector((1.0, 0.5, 0.0)), Vector((1.0, -0.5, 0.0)), Vector((-1.0, -0.5, 0.0))]
create_legs: EDIT_MESH
create_legs: [Vector((-1.0, 0.5, 0.0)), Vector((1.0, 0.5, 0.0)), Vector((1.0, -0.5, 0.0)), Vector((-1.0, -0.5, 0.0))]
create_legs: <Vector (-1.0000, 0.5000, 0.0000)>
create_legs: <Vector (1.0000, 0.5000, 0.0000)>
create_legs: <Vector (1.0000, -0.5000, 0.0000)>
create_legs: <Vector (-1.0000, -0.5000, 0.0000)>

comme prévu. Décommenter le #set_mode('OBJECT')pour qu'il passe en mode objet donne d'abord:

create_legs: EDIT_MESH
create_legs: [Vector((-1.0, 0.5, 0.0)), Vector((1.0, 0.5, 0.0)), Vector((1.0, -0.5, 0.0)), Vector((-1.0, -0.5, 0.0))]
create_legs: OBJECT
create_legs: [Vector((-1.0, 0.5, 0.0)), Vector((1.0, 0.5, 0.0)), Vector((1.0, -0.5, 0.0)), Vector((-1.0, -0.5, 0.0))]
create_legs: <Vector (-0.5000, 0.5000, -0.5000)>
create_legs: <Vector (0.5000, 0.5000, -0.5000)>
create_legs: <Vector (0.5000, -0.5000, -0.5000)>
create_legs: <Vector (-0.5000, -0.5000, -0.5000)>

Les valeurs des coordonnées n'ont pas changé mais les emplacements des objets ne leur sont plus définis.

En l'exécutant plusieurs fois, j'ai même pu obtenir les éléments suivants sans changement:

create_legs: [Vector((-1.0, 0.5, 0.0)), Vector((1.0, 0.5, 0.0)), Vector((1.0, -0.5, 0.0)), Vector((-1.0, -0.5, 0.0))]
create_legs: OBJECT
create_legs: [Vector((-1.0, 0.5, 0.0)), Vector((1.0, 0.5, 0.0)), Vector((1.0, -0.5, 0.0)), Vector((-1.0, -0.5, 0.0))]
create_legs: <Vector (158682362545562451968.0000, 0.0000, 0.0000)>
create_legs: <Vector (0.5000, 0.5000, -0.5000)>
create_legs: <Vector (158692495644724035584.0000, 0.0000, 0.0000)>
create_legs: <Vector (-0.5000, -0.5000, -0.5000)>

La fonction create_cube () est juste une simple création de maillage via bmesh:

    def create_cube(self):
        '''
        creates a cube object using bmesh and returns it.
        '''
        
        # unselect all others
        for obj in bpy.context.selected_objects:
            obj.select_set(False)
        
        # old bpy.ops method
        # bpy.ops.mesh.primitive_cube_add(size=initial_size)
        
        # use the bmesh method: https://blender.stackexchange.com/questions/63546/create-a-cube-in-blender-from-python
        scene = bpy.context.scene
        # create a new mesh
        cube_mesh = bpy.data.meshes.new('Cube')
        cube_object = bpy.data.objects.new('Cube', cube_mesh)
        # add the object into the scene. to do this we now link to a collection
        scene.collection.objects.link(cube_object)

        # make it the only active and selected

        bpy.context.view_layer.objects.active = cube_object
        cube_object.select_set(True)

        # create the cube in the mesh via bmesh
        cube_bm = bmesh.new()
        bmesh.ops.create_cube(cube_bm, size=1)
        cube_bm.to_mesh(cube_mesh)
        # the bmesh is freed at the end of the script anyway
        #cube_bm.free()

        return cube_object

Que se passe-t-il différemment en mode objet et comment puis-je résoudre ce problème?

Mise à jour : je l'ai réduit à: le passage en mode objet modifie la valeur de co, mais uniquement dans la boucle.

        log(bpy.context.mode)
        log(self.tabletop_bottom_corners_co)
        #set_mode('OBJECT')
        log(bpy.context.mode)
        log(self.tabletop_bottom_corners_co)
        
        log()
        
        self.legs = []
        for co in self.tabletop_bottom_corners_co:
            leg = self.create_cube()
            self.legs.append(leg)
            leg.location = 1,1,1
            #leg.scale = self.leg_size,self.leg_size,self.leg_length
            log('co:')
            log(co)

impressions

create_legs: EDIT_MESH
create_legs: [Vector((-1.0, 0.5, 0.0)), Vector((1.0, 0.5, 0.0)), Vector((1.0, -0.5, 0.0)), Vector((-1.0, -0.5, 0.0))]
create_legs: EDIT_MESH
create_legs: [Vector((-1.0, 0.5, 0.0)), Vector((1.0, 0.5, 0.0)), Vector((1.0, -0.5, 0.0)), Vector((-1.0, -0.5, 0.0))]
create_legs:
create_legs: co:
create_legs: <Vector (-1.0000, 0.5000, 0.0000)>
create_legs: co:
create_legs: <Vector (1.0000, 0.5000, 0.0000)>
create_legs: co:
create_legs: <Vector (1.0000, -0.5000, 0.0000)>
create_legs: co:
create_legs: <Vector (-1.0000, -0.5000, 0.0000)>

comme prévu. Mais passer d'abord en mode objet ( set_mode('OBJECT')) donne

create_legs: EDIT_MESH
create_legs: [Vector((-1.0, 0.5, 0.0)), Vector((1.0, 0.5, 0.0)), Vector((1.0, -0.5, 0.0)), Vector((-1.0, -0.5, 0.0))]
create_legs: OBJECT
create_legs: [Vector((-1.0, 0.5, 0.0)), Vector((1.0, 0.5, 0.0)), Vector((1.0, -0.5, 0.0)), Vector((0.0, 0.0, -2.3268253990238463e-10))]
create_legs:
create_legs: co:
create_legs: <Vector (-0.5000, 0.5000, -0.5000)>
create_legs: co:
create_legs: <Vector (0.5000, 0.5000, -0.5000)>
create_legs: co:
create_legs: <Vector (0.5000, -0.5000, -0.5000)>
create_legs: co:
create_legs: <Vector (-0.5000, -0.5000, -0.5000)>
create_legs:

L'exécution à log(self.tabletop_bottom_corners_co)nouveau après montre que le contenu du tableau a été mis à jour:

create_legs: [Vector((-0.5, 0.5, -0.5)), Vector((0.5, 0.5, -0.5)), Vector((0.5, -0.5, -0.5)), Vector((-0.5, -0.5, -0.5))]

Pourquoi itérer sur le tableau de co les change-t-il, mais uniquement en mode objet? Les sauver a été fait comme

        self.tabletop_bottom_corners_co = []
        for vert in bm.faces[4].verts:
            self.tabletop_bottom_corners_co.append(vert.co)

Réponses

GSGBen Aug 29 2020 at 09:32

Corrigé! Vector (), qui est ce qu'est vert.co, est un objet, donc les variables lui font référence. Ma liste de coordonnées sauvegardées était une liste de références aux coordonnées réelles des quatre sommets inférieurs sur le bmesh, un bmesh qui a été supprimé lorsque j'ai quitté le mode d'édition. Le comportement intermittent était la nature de l'accès à la mémoire libérée.

j'ai changé

        # save the bottom face corner locations too
        self.tabletop_bottom_corners_co = []
        for vert in bm.faces[4].verts:
            self.tabletop_bottom_corners_co.append(vert.co)

à

        # save the bottom face corner locations too
        self.tabletop_bottom_corners_co = []
        for vert in bm.faces[4].verts:
            # Vector variables are references, so we need to copy them, otherwise they can change/disappear,
            # *especially* when changing modes
            co_copy = vert.co.copy()
            self.tabletop_bottom_corners_co.append(co_copy)