creating two groups of joined objects using python

I am trying to create multiple joined groups using this answer to an already existing question.

I am reading some coordinates from a file, creating spheres at those coordinates, and assigning a material of a certain color depending on the index. I would like to join together the spheres that have the same color.

Below is a minimal reproducible code:

import bpy
import numpy as np

# 0 (A)
matBlue = bpy.data.materials.new(name="blue")
matBlue.diffuse_color = (0,0,1)
# 1 (B)
matRed = bpy.data.materials.new(name="red")
matRed.diffuse_color = (1,0,0)

materialColors = {0:matBlue, 1:matRed}

spheres = [[],[]]
count = 0
for coord in coordinates:
name = 'sphere' + str(count)
bpy.context.active_object.name = name
currentSphere = bpy.data.objects[name]
currentSphere.data.materials.append(materialColors[count%2])
spheres[count%2].append(currentSphere)
count += 1

# join spheres
scene = bpy.context.scene

for i in range(2):
ctx = bpy.context.copy()
ctx['selected_objects'] = spheres[i]
ctx['active_object'] = spheres[i][0]
ctx['selected_editable_bases'] = [scene.object_bases[ob.name] for ob in spheres[i]]
bpy.ops.object.join(ctx)


However, when I do this, I end up with red spheres joined, and the blue spheres are not joined. What am I doing wrong?

Note: In reality, I have 8 groups instead of 2. Hence the for loop at the end.

Beware the naming system

There is an issue with this part of your code, I've added a print statement boolean if the object you have chosen as currentSphere is also the newly created sphere. bpy.data.objects["sphere0"] will always be the first object created with this name. Running again with any objects with the same name existing, will give somewhat unpredictable results.

for coord in coords:
name = 'sphere' + str(count)
bpy.context.active_object.name = name

currentSphere = bpy.data.objects[name]
print(currentSphere is bpy.context.active_object)


could use something like below, where the currentSphere will be the newly created object, but may not end up with the name you choose. (To make sure it has unique name would need to check and rename/remove others)

for coord in coords:
name = 'sphere' + str(count)
currentSphere = bpy.context.active_object
currentSphere.name = name
currentSphere.data.materials.append(materialColors[count%2])
spheres[count%2].append(currentSphere)
count += 1


I also contend in this case can set context, rather than overriding.

for i in range(2):
for o in scene.objects:
o.select = o in spheres[i]
scene.objects.active = spheres[i][0]
bpy.ops.object.join()


Bmesh approach

Another suggestion would be scrapping the primitive add and join operators altogether and use bmesh to create spheres at coordinates. Test script, creates two bmeshes for odd and even coordinates, and writes the first to active object's mesh.

import bpy
import bmesh
from mathutils import Matrix

coords = [(0, 0, 1), (1, 0, 0), (0, 1, 0), (1, 1, 0)]
bmeshes = [bmesh.new(), bmesh.new()]
for i, coord in enumerate(coords):
bmesh.ops.create_uvsphere(bmeshes[i % 2],
u_segments = 16,
v_segments = 8,
matrix=Matrix.Translation(coord),
diameter=0.5)

# for example sake to mesh onto active_ob
me = bpy.context.object.data
bmeshes[0].to_mesh(me)
me.update()
for bm in bmeshes:
bm.free()

• Thanks for the detailed explanation and pointing out the possibility to use bmesh. – sodiumnitrate Apr 20 '18 at 21:21