3
$\begingroup$

I'm generating cubes with this code:

for a in range(10):
    x = random.randint(-5, 4)
    y = random.randint(-2, 7)
    z = random.randint(3, 10)
    bpy.ops.mesh.primitive_cube_add(location=(x,y,z), radius = 1)`

What do I add to make sure no one of these 10 cubes are created with some part of it inside of one of the earlier created cubes?

$\endgroup$
3
  • $\begingroup$ I would look into Poisson sampling $\endgroup$ Commented Dec 1, 2017 at 15:03
  • $\begingroup$ @SebastiánMestre would be interested in seeing an answer using Poisson Sampling $\endgroup$
    – batFINGER
    Commented Dec 3, 2017 at 14:36
  • $\begingroup$ my mistake, i meant poisson disk sampling. and i am sorry, i won't be leaving a reply $\endgroup$ Commented Dec 5, 2017 at 19:13

3 Answers 3

4
$\begingroup$

enter image description here

A similar answer that utilizes Blender's Mathutils' Vector length property (prevents you from needing to calculate distances in all 3 axes individually):

import bpy
from random import random
from mathutils import Vector

cubeRadius    = 0.25
maxIterations = 1000 # Max iterations to prevent while loop from running forever

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

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

size  = 250 # Number of cubes
cubes = []  # Cube coordinates list

loopIterations = 0
while len( cubes ) < size and loopIterations < maxIterations:
    loopIterations += 1
        
    # Generate a random 3D coordinate
    loc = Vector([ randLocInRange( axis ) for axis in ranges.keys() ])

    if len( cubes ) > 0:
        # Search for overlapping points (within the cube radius radius)
        overlappingPoints = [ p for p in cubes if ( p - loc ).length < cubeRadius * 2 ]

        # if any found, skip this location
        if overlappingPoints: continue
    
    # Add coordinate to cube list
    cubes.append( loc )

# Add the first cube (others will be duplicated from it)
# Note: in blender 2.8 size arg is used instead of radius
bpy.ops.mesh.primitive_cube_add( radius = cubeRadius, location = cubes[0] )

cube = bpy.context.scene.objects['Cube']

# Add all other cubes
for c in cubes[1:]:
    dupliCube = cube.copy()
    dupliCube.location = c
    bpy.context.scene.objects.link( dupliCube )
    # in blender 2.8 an api change requires to use the collection instead of the scene
    # bpy.context.collection.objects.link(dupliCube)
$\endgroup$
2
  • $\begingroup$ Much more efficient. The only thought i would have for a change is instead of a max iteration check for consecutive fails vs a max fails in the event someone wanted to push the limit of size > iterations. (easily incremented & reset at the "overlappingPoints" check. And i guess regarding the specific question adjust for using randint(). but thank you for the improved version. $\endgroup$
    – Ratt
    Commented Dec 3, 2017 at 13:33
  • 1
    $\begingroup$ Good candidate for using any if any((p - loc).length < D for p in cubes): continue $\endgroup$
    – batFINGER
    Commented Dec 4, 2017 at 13:26
1
$\begingroup$

For small sample sets testing collision can be done like this:

import random
import bpy

obj_ctr = []
obj_radius = 1
# one object must be created outside the loop for data structure to be available for testing
x = random.randint(-5, 4)
y = random.randint(-2, 7)
z = random.randint(3, 10)
obj_ctr.append((x,y,z))

#while len(obj_ctr) < 10:
for a in range(10):
    test_x = False
    test_y = False
    test_z = False
    x = random.randint(-5, 4)
    y = random.randint(-2, 7)
    z = random.randint(3, 10)
    for test in obj_ctr: #verify the new randoms will not allow collision
        if abs(test[0] - x) < obj_radius * 2:
            test_x = False
        else:
            test_x = True
        if abs(test[1] - y) < obj_radius * 2:
            test_y = False
        else:
            test_y = True
        if abs(test[2] - y) < obj_radius * 2:
            test_z = False
        else:
            test_z = True
    if (test_x and test_y and test_z):
        obj_ctr.append((x,y,z))

for obj in obj_ctr:
    bpy.ops.mesh.primitive_cube_add(radius = obj_radius, location = obj)

As you attempt to create more items iterating through all the previously created items to test for intersection will become increasingly time consuming.If you use the while loop (to ensure 10 objects) be careful that the random ranges are large enough or radius is set small enough to avoid infinite loops.

$\endgroup$
1
$\begingroup$

enter image description here

I am not very good with Python codes, but there is one node in Sverchok Add-On that basically scatter bunch of vector points and "remove within distance" so you don't get overlapping. Check the node Python code and you can see the algorithm.

$\endgroup$

You must log in to answer this question.

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