Why does attached Python script produce error “Cannot execute boolean operation”?
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()
It reminds me of an error I had during a manual difference, like this one: http://blenderartists.org/forum/archive/index.php/t-263716.html
It seemed to me that blender 2.63 cannot make the difference of two objects sharing the same plane frontier. I just moved one for a very short distance and got rid of the message “Cannot execute boolean operation”
To help find the source of the error, starting blender from a console window provides more information on error messages, in this case the output was
SG failed, exception degenerate edge Unknown internal error in boolean
So, as mentioned in ebt’s answer, the error probably has something to do with vertices or faces sharing the same position in the different objects.
To fix this, I modified line 57 of your script (in the function createCylinder)
cylinder.scale = (r,r,height+0.01)
cylinder.scale = (r,r,height)
This allows the ring to be applied as a boolean to the cube, however inspecting the ring shows that it too has several problems with booleans. It may be possible to resolve it with a similar approach, however I wouldn’t recommend it. In my experience blender’s booleans utterly fail when faces or vertices of the objects share the same positions.
Unless doing this as a python script is essential, it would be easiest to model the ring separately, then subtract that from the platform.
- Database Administration Tutorials
- Programming Tutorials & IT News
- Linux & DevOps World
- Entertainment & General News
- Games & eSport