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()
remove_doubles()
orMesh.validate(True)
beforehand? $\endgroup$difference()
does not seem to help so far (in fact if I callremove_doubles()
I getRuntimeError: Operator bpy.ops.mesh.remove_doubles.poll() failed, context is incorrect
). $\endgroup$difference()
does not help either. From the evidence available so far I also don't see why it would. The problem now seems to be related to the order of adding/applying boolean operators (such as DIFFERENCE) to a mesh when targets are overlapping. The quoted advise from the documentation seems to say: ignore the error and use/apply the respective modifiers at a later time ("for subsequent operations"): but what does that amount to in practice (esp. in the context of a script)? $\endgroup$