I have a python script to generate a table out of simple cubes. To create the legs I've saved the coordinates of the bottom corners of the tabletop, and I'm then creating a cube at each location to begin the legs.
The issue I've run into is that setting the location of the newly created leg cube objects while still in edit mode works, but gives the wrong values when setting the location in object mode. Further, the values in object mode change intermittently if you run the script a few times.
Here's the key part of the code, while staying fully in edit mode:
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)
which logs
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)>
as expected. Uncommenting the #set_mode('OBJECT')
so it goes into object mode first gives:
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)>
The coordinate values haven't changed but the locations of the objects are no longer set to them.
Running it a few more times I was even able to get the following with no changes:
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)>
The create_cube() function is just a simple mesh creation 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
What's happening differently in object mode, and how can I fix this?
Update: I've narrowed it down to: changing to Object mode changes the value of co, but only in the loop.
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)
prints
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)>
as expected. But changing to object mode first (set_mode('OBJECT')
) gives
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:
Running log(self.tabletop_bottom_corners_co)
again afterwards shows that the array contents has been updated:
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))]
Why does iterating over the array of co change them, but only in object mode? Saving them was done as
self.tabletop_bottom_corners_co = []
for vert in bm.faces[4].verts:
self.tabletop_bottom_corners_co.append(vert.co)
bmesh
docs on usingbmesh.from_edit_mesh
when in edit mode. $\endgroup$