# How do I generate random cylinders, with random height on a predefined area?

I am using the following python script to generate an amount of random cylinders that do not overlap with each other:

     import bpy
from random import random
from mathutils import Vector

maxIterations = 500 # Max iterations to prevent while loop from running
forever

# min and max values for each axis for the random numbers
ranges = {
'x' : { 'min' : 7, 'max' : 10 },
'y' : { 'min' : 7, 'max' : 10 },
'z' : { 'min' : 0, 'max' : 0}
}

# Generates a random number within the axis minmax range
randLocInRange = lambda axis: ranges[axis]['min'] + random() * (
ranges[axis]['max'] - ranges[axis]['min'] )

size  = 50 # Number of cylinders
cylinders = []  # Cylinders coordinates list

loopIterations = 0
while len( cylinders ) < size and loopIterations < maxIterations:
loopIterations += 1

# Generate a random 3D coordinate
loc = Vector([ randLocInRange( axis ) for axis in ranges.keys() ])

if len( cylinders ) > 0:
overlappingPoints = [ p for p in cylinders if ( p - loc ).length < cylinderRadius * 2 ]

# if any found, skip this location
if overlappingPoints: continue

# Add coordinate to cylinder list
cylinders.append( loc )

# Add the first cylinder (others will be duplicated from it)
cylinders[0] )
cylinder = bpy.context.scene.objects['Cylinder']

for c in cylinders[1:]:
dupliCylinder = cylinder.copy()
dupliCylinder.location = c


However, I do not get the 50 cylinders I am asking for when compiling. Does anyone have an idea on how to actually avoid setting a number of cylinders to be generated, but to fill in a pre-defined area (e.g. a square) with randomly placed cylinders, that do not overlap with each other, on it ?

Thanks!

• Kind of a 2d version of this blender.stackexchange.com/a/42232/15543 Mar 31 '19 at 12:57
• Do you want it in a triangle form as in your image or is it ok if it was in square form with a small gap in the middle. If so, try doing a loop for the y axis which has a loop for the x axis that adds 2 times the radius to that axis. To get 50 you would input 5 and 10 on the x and y values, giving you 5x10=50. Just giving a simpler approach... Mar 31 '19 at 12:58

Test run, settings as posted Placed 66 of 68

Algorithm for random distribution of circles in a square

This simple script to place a number of num_circles circles with radius radius in a refined rectangle (aligned to global axes) speficied by xmin, xmax, ymin and ymax by trying random locations until it not colliding with an existing circle. This is certainly not the most efficient way of doing it, but it should be fine for smaller problems and if the relative area covered is not too high.

Test code after making a couple of edits to https://blender.stackexchange.com/a/89658/15543 to make it add cylinders of random height to a rectangle.

from mathutils import Vector, Matrix
from mathutils.noise import random, seed_set
import bpy

# Specify parameters
seed = 0
xmin = -10
xmax = 10
ymin = -10
ymax = 10
num_circles = 68
max_tries = 10000
max_height = 4
# Init
seed_set(seed)
sx = xmax - xmin - 2 * radius
sy = ymax - ymin - 2 * radius
existing_locations = []
sce = bpy.context.scene
location=(0, 0, 0),
depth=1,
ref_circle = bpy.context.object
ref_circle.data.transform(Matrix.Translation((0, 0, 0.5)))

# Loop
for i in range(num_circles):
j = 0
searchOn = True
while searchOn:
if j > max_tries:
print("Placed", i, "of", num_circles)
bpy.ops.object.select_all(action='DESELECT')
ref_circle.select_set(True)
bpy.ops.object.delete()
raise ValueError('Found no more room for another circle')
break
j += 1
new_location = (xminm + random() * sx,
yminm + random() * sy,
0)
for existing_location in existing_locations:
if (Vector(existing_location) - Vector(new_location)).length < 2 * radius:
break
else:
new_circle = ref_circle.copy()
new_circle.dimensions.z = max_height * random()
new_circle.location = new_location