0
$\begingroup$

I would like to make an opaque screen with many circular holes of different diameters and locations by an automated by a python script.

The locations and sizes will come from an algorithm and will appear random and some holes will overlap.

The script doesn't quite run - it gets hung up on the line indicated where I am trying to say "use this cylinder to make the hole!

enter image description here

The error in the console:

Traceback (most recent call last):
  File "/Users/david/Documents/fishing/static concentrator/static concentrator v01.blend/Text", line 31, in <module>
TypeError: bpy_struct: item.attr = val: BooleanModifier.object ID type does not support assignment to its self
Error: Python script fail, look in the console for now...

The script:

import numpy as np
import bpy

# make a screen
bpy.ops.mesh.primitive_cube_add(location=(0, 0, 2))
screen = bpy.context.active_object
screen.scale = 0.05, 2, 2
screen.location = 0, 0, 2

bpy.ops.mesh.primitive_cylinder_add(radius=1, depth=1,  location=(0, 0, 2))
cyl = bpy.context.active_object
cyl.rotation_euler = 0, 0.5*np.pi, 0
bpy.ops.object.select_all(action='DESELECT')


scales, offsets = np.array([4, 4, 0.1]), np.array([-0.5, 0, 1])

holes = (scales * (np.random.random(300).reshape(-1, 3) + offsets))

for y, z, r in holes:

    cyl.location = 0, y, z
    cyl.scale = r, r, 1

    bpy.ops.object.select_all(action='DESELECT')
    screen.select = True

    # bpy.context.space_data.context = 'MODIFIER'
    bpy.ops.object.modifier_add(type='BOOLEAN')
    bpy.context.object.modifiers["Boolean"].operation = 'DIFFERENCE'
    bpy.context.object.modifiers["Boolean"].object = cyl  # Hey! Use this to cut the hole please!!
    bpy.ops.object.modifier_apply(apply_as='DATA', modifier="Boolean")
    bpy.ops.object.select_all(action='DESELECT')

The alternate script with a tiny loop and without numpy:

import bpy
import math

# make a screen
bpy.ops.mesh.primitive_cube_add(location=(0, 0, 2))
screen = bpy.context.active_object
screen.scale = 0.05, 2, 2
screen.location = 0, 0, 2

# make a cylinder
bpy.ops.mesh.primitive_cylinder_add(radius=1, depth=1,  location=(0, 0, 2))
cyl = bpy.context.active_object
cyl.rotation_euler = 0, 0.5*math.pi, 0
bpy.ops.object.select_all(action='DESELECT')

holes = [(1, 3, 0.25), (0, 2, 0.15)]

for y, z, r in holes:

    cyl.location = 0, y, z
    cyl.scale = r, r, 1

    bpy.ops.object.select_all(action='DESELECT')
    screen.select = True

    # bpy.context.space_data.context = 'MODIFIER'
    bpy.ops.object.modifier_add(type='BOOLEAN')
    bpy.context.object.modifiers["Boolean"].operation = 'DIFFERENCE'
    bpy.context.object.modifiers["Boolean"].object = cyl  # Hey! Use this to cut the hole please!!
    bpy.ops.object.modifier_apply(apply_as='DATA', modifier="Boolean")
    bpy.ops.object.select_all(action='DESELECT')

enter image description here

$\endgroup$
  • $\begingroup$ Cutting holes is one of the harder things to do in 3d modeling programs...I would say to use a boolean modifier, but I don't know why you'd have to subdivide the plane so much... $\endgroup$ – JakeD Sep 8 '16 at 11:19
  • $\begingroup$ @pycoder thanks - actually a thin slab seems to work well. I was speculating about the subdivision based on other tutorials. Actually I am making progress with my automated boolean modifier script - I am going to rewrite this question in a few minutes based on my results. Stay tuned. $\endgroup$ – uhoh Sep 8 '16 at 11:26
  • $\begingroup$ @pycoder OK the question is updated - almost working script to perforate a 3D slab. No progress perforating a plane, but that's OK I can just make this thin. $\endgroup$ – uhoh Sep 8 '16 at 12:58
  • $\begingroup$ try cyl.name instead of just cyl when you set the object to use. $\endgroup$ – JakeD Sep 8 '16 at 13:00
  • 1
    $\begingroup$ note: This answer and it's question may contain the solution - I'll give it a try again tomorrow - if anyone wants to try first, that's great! $\endgroup$ – uhoh Sep 8 '16 at 14:49
1
$\begingroup$

This should work for you:

import bpy
import math

# make a screen
bpy.ops.mesh.primitive_cube_add(location=(0, 0, 2))
screen = bpy.context.active_object
screen.scale = 0.05, 2, 2
screen.location = 0, 0, 2

# make a cylinder
bpy.ops.mesh.primitive_cylinder_add(radius=1, depth=1,  location=(0, 0, 2))
cyl = bpy.context.active_object
cyl.rotation_euler = 0, 0.5*math.pi, 0
bpy.ops.object.select_all(action='DESELECT')

holes = [(1, 3, 0.25), (0, 2, 0.15)]

for y, z, r in holes:

    cyl.location = 0, y, z
    cyl.scale = r, r, 1

    bpy.ops.object.select_all(action='DESELECT')
    screen.select = True  # this doesn't matter, the active object does
    bpy.context.scene.objects.active = screen

    # bpy.context.space_data.context = 'MODIFIER'
    bpy.ops.object.modifier_add(type='BOOLEAN')
    bpy.context.object.modifiers["Boolean"].operation = 'DIFFERENCE'
    bpy.context.object.modifiers["Boolean"].object = cyl  # Hey! Use this to cut the hole please!!
    bpy.ops.object.modifier_apply(apply_as='DATA', modifier="Boolean")
    bpy.ops.object.select_all(action='DESELECT')

The key is that it doesn't matter what is selected, but what is active.

$\endgroup$
  • $\begingroup$ Yippee!! The SwissCheese modifier is born. i.stack.imgur.com/UDEZP.png Thank you very much for your time and persistence! Selected vs Active - I should have figured that one out. $\endgroup$ – uhoh Sep 9 '16 at 1:35
  • $\begingroup$ Hasn't numpy come standard with Blender for a while now? $\endgroup$ – uhoh Sep 9 '16 at 1:38
  • 1
    $\begingroup$ You're right about numpy...my bad. I just assumed it wasn't. Glad I could help! $\endgroup$ – JakeD Sep 9 '16 at 1:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.