2
$\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$
11
  • $\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$
    – Jake Duth
    Commented Sep 8, 2016 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
    Commented Sep 8, 2016 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
    Commented Sep 8, 2016 at 12:58
  • $\begingroup$ try cyl.name instead of just cyl when you set the object to use. $\endgroup$
    – Jake Duth
    Commented Sep 8, 2016 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
    Commented Sep 8, 2016 at 14:49

1 Answer 1

2
$\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$
3
  • $\begingroup$ Yippee!! The SwissCheese modifier is born. i.sstatic.net/UDEZP.png Thank you very much for your time and persistence! Selected vs Active - I should have figured that one out. $\endgroup$
    – uhoh
    Commented Sep 9, 2016 at 1:35
  • $\begingroup$ Hasn't numpy come standard with Blender for a while now? $\endgroup$
    – uhoh
    Commented Sep 9, 2016 at 1:38
  • 1
    $\begingroup$ You're right about numpy...my bad. I just assumed it wasn't. Glad I could help! $\endgroup$
    – Jake Duth
    Commented Sep 9, 2016 at 1:50

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .