I am trying to create a 3D model using Python scripting with Blender 2.69. I start from a cube and subtract several spheres from it to make indentations. I then form a ring out of four cylinders and four spheres, which I then also subtract from the sphere. However, the result of this step (as marked in the script) is unexpected (the ring isn't subtracted, only one cylinder is deleted/unlinked from the scene). When I inspect the cube in the Blender UI, it only says:
Cannot execute boolean operation.
There is also something else that seems odd and that could be related: If I uncomment the two lines at the end of
union(), it will lead to this error message:
RuntimeError: Operator bpy.ops.object.modifier_apply.poll() failed, context is incorrect
How can I dig deeper and find out more about the root cause, and how does the script have to be patched?
I've seen this previous answer and a mentioning of the error message in the Blender documentation, however neither seemed to provide sufficient information. I get the sense that the problem may be that several of the objects which I want to subtract are overlapping and it may not be possible to subtract them all at once, but I don't (yet) know what specific changes to the script are needed to obey this piece of advice from the documentation:
In this case, you can apply the first Boolean modifier of the stack for the target and then use the other Boolean modifier(s) in the stack for subsequent operations.
Now here is the current script:
import bpy import math def deleteAll(): bpy.ops.object.select_all(action = 'SELECT') bpy.ops.object.delete() def createBoard(): # prepare some constants BOARD_WIDTH = 22.7 BOARD_HEIGHT = 22.7 BOARD_DEPTH = 1.5 SPHERE_DIAMETER = 1.9 SPHERE_PADDING = 0.5 DITCH_DIAMETER = 2.5 DITCH_PADDING = 0.8 # create platform origin = (0,0,0) bpy.ops.mesh.primitive_cube_add(location=origin) platform = bpy.context.active_object platform.scale = (BOARD_WIDTH/2,BOARD_HEIGHT/2,BOARD_DEPTH/2) # create spheres for i in [-2,-1,1,2]: for j in [-2,-1,1,2]: x = (SPHERE_DIAMETER + SPHERE_PADDING)*(i - copysign(.5,i)); y = (SPHERE_DIAMETER + SPHERE_PADDING)*(j - copysign(.5,j)); z = BOARD_DEPTH/2 difference(platform,createSphere(x,y,z,SPHERE_DIAMETER/2)) # create ditch xMax = BOARD_WIDTH/2 - DITCH_PADDING - DITCH_DIAMETER/2 xMin = -xMax yMax = BOARD_HEIGHT/2 - DITCH_PADDING - DITCH_DIAMETER/2 yMin = -yMax z = BOARD_DEPTH/2 r = DITCH_DIAMETER/2 ring = createCylinder(xMin,0,z,r,xMax,[math.pi/2,0,0]) union(ring,createSphere(xMin,yMin,z,r)) union(ring,createCylinder(0,yMin,z,r,xMax,[0,math.pi/2,0])) union(ring,createSphere(xMax,yMin,z,r)) union(ring,createCylinder(xMax,0,z,r,yMax,[math.pi/2,0,0])) union(ring,createSphere(xMax,yMax,z,r)) union(ring,createCylinder(0,yMax,z,r,yMax,[0,math.pi/2,0])) union(ring,createSphere(xMin,yMax,z,r)) difference(platform,ring) # this leads to unexpected result def createSphere(x, y, z, r): origin = (x,y,z) bpy.ops.mesh.primitive_uv_sphere_add(location=origin) sphere = bpy.context.active_object sphere.scale = (r,r,r) return sphere def createCylinder(x, y, z, r, height, rotation): origin = (x,y,z) bpy.ops.mesh.primitive_cylinder_add(location=origin) cylinder = bpy.context.active_object cylinder.scale = (r,r,height) cylinder.rotation_mode = 'XYZ' cylinder.rotation_euler = rotation return cylinder def difference(first,second): modifier = first.modifiers.new('Modifier', 'BOOLEAN') modifier.object = second modifier.operation = 'DIFFERENCE' bpy.ops.object.modifier_apply(apply_as='DATA', modifier=modifier.name) scene = bpy.context.scene scene.objects.unlink(second) def union(first,second): modifier = first.modifiers.new('Modifier', 'BOOLEAN') modifier.object = second modifier.operation = 'UNION' bpy.ops.object.modifier_apply(apply_as='DATA', modifier=modifier.name) #scene = bpy.context.scene #scene.objects.unlink(second) if __name__ == "__main__": deleteAll() createBoard()