1
$\begingroup$

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

 cylinderRadius    = 1
 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:
    # Search for overlapping points (within the cylinder radius radius)
    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)
bpy.ops.mesh.primitive_cylinder_add( radius = cylinderRadius, location = 
cylinders[0] )
cylinder = bpy.context.scene.objects['Cylinder']

# Add all other cylinders
for c in cylinders[1:]:
    dupliCylinder = cylinder.copy()
    dupliCylinder.location = c
    bpy.context.scene.objects.link( dupliCylinder )

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 ? enter image description here

Thanks!

$\endgroup$
2
  • $\begingroup$ Kind of a 2d version of this blender.stackexchange.com/a/42232/15543 $\endgroup$
    – batFINGER
    Mar 31 '19 at 12:57
  • $\begingroup$ 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... $\endgroup$ Mar 31 '19 at 12:58
2
$\begingroup$

enter image description here 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
radius = 1
num_circles = 68
max_tries = 10000
max_height = 4
# Init
seed_set(seed)
sx = xmax - xmin - 2 * radius
sy = ymax - ymin - 2 * radius
xminm = xmin + radius
yminm = ymin + radius
existing_locations = []
sce = bpy.context.scene
bpy.ops.mesh.primitive_cylinder_add(
    location=(0, 0, 0),
    depth=1,
    radius=radius)
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
            sce.collection.objects.link(new_circle)
            existing_locations.append(new_location)
            searchOn = False

ref_circle.select_set(True)
bpy.ops.object.delete()
context.view_layer.update()
$\endgroup$
1
  • $\begingroup$ Hi batFINGER, Thank you for this script of random generation of meshes. I have some questions about my script, can I send with a private message regarding the distribution of random cylinders and cubes. $\endgroup$
    – saded
    Apr 21 '19 at 18:02

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.