# python -- apply/run script for every array instance individually

I have the following blender scene setup: (notice the python script, will be mentioned soon in question)

As you can see, there are many boards being duplicated to form a wall.

The thing is: in real life, the middle support board is usually different for each board, like this:

So basically, I want to randomly set the Z position within the range of the top beam and bottom beam (of the boards in my scene) so that the middle support-beam is randomly positioned, reapplied for each array instance. So I went ahead and made a python script that randomly sets the beam'ss Z position from the top beam to bottom beam, and currently whenever I hit "Run Script", it re-randomly sets the position of the beam, exactly how I wanted.

The problem: As you can see, all of the array instances are the same, but I want it to randomly move the beam (a.k.a., somehow re-apply the script) for every array instance, but I have absolutely no idea how to do this.

Anyone?

Distribute copies.

Further to @moonboots answer can copy and distribute the object. Each copy shares the one mesh.

import bpy
from mathutils import Vector
from random import uniform

def array(context, count, rel_offset=Vector(), rnd_var_range=(0, 0), rnd_var_dir=Vector()):
scene = context.scene
for ob in context.selected_objects:
mw = ob.matrix_world
R = mw.to_quaternion().to_matrix()
offset =  R * rel_offset.dot(ob.dimensions) * rel_offset.normalized()
copies = count - 1

for i in range(copies):
copy = ob.copy()
t = copy.matrix_world.translation
t  += (i + 1) * offset
copy.delta_location = uniform(*rnd_var_range) * rnd_var_dir
copy.select = True

# test call

context = bpy.context

array(context,
count = 5,
rel_offset = Vector((1.1 , 0, 0)),
rnd_var_dir = Vector((0, 1, 0)),
rnd_var_range = (-1, 1))

array(context,
count = 5,
rel_offset = Vector((0, 0, 1.1)),
rnd_var_dir = Vector((0, 1, 0)),
rnd_var_range = (-1, 1))


Result of running script with Suzanne selected. The variation of both arrays in test call are in local Y direction.

The same script updated to Blender 2.8 API

import bpy
from mathutils import Vector
from random import uniform

def array(context, count, rel_offset=Vector(), rnd_var_range=(0, 0), rnd_var_dir=Vector()):
scene = context.scene
for ob in context.selected_objects:
mw = ob.matrix_world
R = mw.to_quaternion().to_matrix()
offset =  R @ (rel_offset.dot(ob.dimensions) * rel_offset.normalized())
copies = count - 1

for i in range(copies):
copy = ob.copy()
t = copy.matrix_world.translation
t  += (i + 1) * offset
copy.delta_location = uniform(*rnd_var_range) * rnd_var_dir
copy.select_set(state=True)
context.view_layer.objects.active = copy

# test call

context = bpy.context

array(context,
count = 5,
rel_offset = Vector((1.1 , 0, 0)),
rnd_var_dir = Vector((0, 1, 0)),
rnd_var_range = (-1, 1))

array(context,
count = 5,
rel_offset = Vector((0, 0, 1.1)),
rnd_var_dir = Vector((0, 1, 0)),
rnd_var_range = (-1, 1))


Main changes are:

• @ operator is now used for matrix/vector component-wise multiplication
• selection of an object in context is now done with copy.select_set(state=True)
• The link must now be done via collections and not view_layer
• Very nice solution, I was thinking of doing something like this although I didn;'t know where to start, but I would somehow like to copy the array modifier as best as possible, is there a way to make it lke a modifier in the sense that you can make something like a slider, where if you adjust the number, the instances go up or down instantly? I know its pretty much impossible (or is it?) to make all of the objects part of the same MESH and still editable, but how can we copy the array modifier(pun intended) in the best sense? Oct 28 '18 at 8:08
• Code above primarily set up to be an operator, with for example an int prop for count that can be adjusted via a slider in active operator UI in toolbar (and for other vector props) Adding a join operator as last line will join into one object. To make it editable post operator would require some dicking around like adding a dummy object named full vertex group Oct 28 '18 at 9:46
• I see so you would have to join it all at once, but I'm not sure about the last point of making a "dummy object named full vertex group"? Do you mean to make each object its own vertex group, then somehow access each vertex group as an individual copy? Oct 28 '18 at 23:08

set the object name to something meaningful like "bar" or whatever. apply the modifier ass it is with no Z variation. then press [TAB] to enter edit mode, press [A] to select all, press [P] and select separate by loose parts. press [tab] back to object mode. and run:

import bpy, random as r
objs = bpy.data.objects
for i in objs:
if "bar" in i.name:    #"bar" name of the array obj "Cube" to you \n
i.select=True
bpy.context.object.location[2] =1+r.randint(-20,20)/10 #"here your random funct"


...i think this should do the thing.

• Please use the code highlighting button. Oct 25 '18 at 16:41

I am not sure if you can change the data for individual instance of the array modifier, since, I guess, the instances share the same data block.

One option is to apply the modifier, so as to combine the data of all objects into the parent object. This will complicate the calculations, though. The other option would be creating separate object for each instance. The most obvious way to do this is to copy the mesh data mesh = bpy.data.objects[x].data.copy() and use it to create a new objectobj = bpy.data.objects.new('new_obj', mesh) followed by bpy.context.scene.objects.link(obj). The objects location can be changed after it's linked into the scene.