4
$\begingroup$

I have this python script which perfectly distributes my cylinders on a circle.

def create_circular_array(number_of_objects, radius, name):
    
    # angle = radian / numbers_of_ob
    angle = 2 * math.pi / number_of_objects

    halfPi = math.pi / 2

    # loop number_of_ob
    for i in range(number_of_objects):
        
        new_Name = name + '_' + str(i)
        bpy.ops.mesh.primitive_cylinder_add(radius=1, depth=2, enter_editmode=False, align='WORLD', location=(0, 0, 0), scale=(0.1, 0.1, 1))
   #     bpy.context.active_object.name = new_Name

        bpy.ops.transform.rotate(value = halfPi, orient_axis='Y', orient_type='GLOBAL', orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)), orient_matrix_type='GLOBAL', constraint_axis=(False, True, False), mirror=True, use_proportional_edit=False, proportional_edit_falloff='SMOOTH', proportional_size=1, use_proportional_connected=False, use_proportional_projected=False)
        bpy.ops.transform.rotate(value = math.pi * 2 - angle * i + math.pi / 2, orient_axis='Z', orient_type='GLOBAL', orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)), orient_matrix_type='GLOBAL', constraint_axis=(False, False, True), mirror=True, use_proportional_edit=False, proportional_edit_falloff='SMOOTH', proportional_size=1, use_proportional_connected=False, use_proportional_projected=False)
        bpy.data.objects[i].location[0] = math.sin(angle*i)*radius       # x = sine(angle*i)*radius 
        bpy.data.objects[i].location[1] = math.cos(angle*i)*radius       # y = cosine(angle*i)*radius
        bpy.data.objects[i].location[2] = 0
        
        

create_circular_array(16, 2, 'inner')

But...

If I uncomment this line

#     bpy.context.active_object.name = new_Name

Everything is messed up. The coordinates and angles are wrong.

What am I doing wrong here!?

and yes, i want to rename my newly created objects. So it is not a solution for me to delete or comment out that line.

$\endgroup$

2 Answers 2

6
$\begingroup$

Python is very dependent on the text indentation to structure the code. Adding the comment character (#) where you have is not aligned with the indentation and so it will be effectively ending the loop at that point.

Instead, comment the line immediately before the text (#bpy.context....) so as to maintain the indentation.

The other problem you have is that you are indexing the objects directly using 'bpy.data.objects[i]'. This results in the 'location' not being set on the correct object - since it's the position in the list of all objects in the scene - not just the ones you have created. Furthermore, since the objects are effectively stored in name sequence, changing the name is changing the position in the list and so you are changing the wrong object each time.

The solution is to stick to using the active_object - since that's the one you have just created on this iteration of the loop.

Here's the amended code :

import bpy
import math

def create_circular_array(number_of_objects, radius, name):
    
    # angle = radian / numbers_of_ob
    angle = 2 * math.pi / number_of_objects

    halfPi = math.pi / 2

    # loop number_of_ob
    for i in range(number_of_objects):
        
        new_Name = name + '_' + str(i)
        bpy.ops.mesh.primitive_cylinder_add(radius=1, depth=2, enter_editmode=False, align='WORLD', location=(0, 0, 0), scale=(0.1, 0.1, 1))
        bpy.context.active_object.name = new_Name

        bpy.ops.transform.rotate(value = halfPi, orient_axis='Y', orient_type='GLOBAL', orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)), orient_matrix_type='GLOBAL', constraint_axis=(False, True, False), mirror=True, use_proportional_edit=False, proportional_edit_falloff='SMOOTH', proportional_size=1, use_proportional_connected=False, use_proportional_projected=False)
        bpy.ops.transform.rotate(value = math.pi * 2 - angle * i + math.pi / 2, orient_axis='Z', orient_type='GLOBAL', orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)), orient_matrix_type='GLOBAL', constraint_axis=(False, False, True), mirror=True, use_proportional_edit=False, proportional_edit_falloff='SMOOTH', proportional_size=1, use_proportional_connected=False, use_proportional_projected=False)

        #This bit changed to use 'active_object' instead of indexed in the objects list
        bpy.context.active_object.location[0] = math.sin(angle*i)*radius       # x = sine(angle*i)*radius 
        bpy.context.active_object.location[1] = math.cos(angle*i)*radius       # y = cosine(angle*i)*radius
        bpy.context.active_object.location[2] = 0
        
        

create_circular_array(16, 2, 'inner')

And here's the result :

enter image description here

$\endgroup$
1
  • 1
    $\begingroup$ Thank you very much! i learnt a lot!!! $\endgroup$
    – Chris
    Mar 9, 2021 at 9:04
8
$\begingroup$

Vectors Matrices and Shared Mesh. enter image description here

FWIW have added an approach that will be a lot quicker, use one operator call, and create only a single mesh

A single operator call, creates the cylinder at the scale and applies the 90 degree rotation about Y to its mesh.

This object is then passed to our create array method with a center, radius, how many to create, and optionally the collection to link them to. This way can create Suzanne's or any object to "array".

As default it creates 40 about the point (1, 2, 0) with a radius of 1. To make same with question code would use 40 x 3 = 120 operator calls. See

Python performance with Blender operators

which can get horribly slow for a lot of copies.

The initial object is moved to the center point plus the radius in x. A matrix is calculated that will rotate it an nth' of 360 degrees around z axis.

Matrix math to translate, rotate, scale with respect to a pivot point in Object mode

A copy is made, it shares the mesh of the original, and the alignment. The matrix is applied (multiplied) to its matrix world rotating it 360 / n degrees. It is then copied and repeated until there is no more and we are back to first.

Duplicating a mesh object

import bpy
from mathutils import Matrix, Vector
from math import pi

context = bpy.context
scene = context.scene

def circular_array(
        ob, 
        n, 
        center, 
        radius, 
        revs=1, 
        matrix=Matrix(), 
        coll=bpy.context.scene.collection): 
    
    center = Vector(center)
    
    
    ob.matrix_world.translation = center
    ob.matrix_world.translation.x += radius
    angle = 2 * pi / n
    name = ob.name
    ob.name = f"{name}_0"

    M = matrix @ (
         
        Matrix.Translation(center) @        
        Matrix.Rotation(angle, 4, 'Z') @ 
        Matrix.Translation(-center)
        )
    for f in range(1, revs * n):
        ob = ob.copy()
        ob.name = f"{name}_{f}"
        ob.matrix_world = M @ ob.matrix_world
        coll.objects.link(ob)

# Test Call

# create our object to array       
bpy.ops.mesh.primitive_cylinder_add(
        location=(0, 0, 0),
        scale=(0.1, 0.1, 1),
        rotation=(0, pi / 2, 0),
        )

ob = context.object
ob.name = "Tick"
# array ob 40 times about (1, 2, 0) with radius 2)

circular_array(
        ob,  
        40,
        (1, 2, 0),
        1,
        )

Matrices

To further prattle on about matrices have put a matrix into the transform mix.

S = Matrix.Diagonal((1.01, 1, 1, 0.9))
R = Matrix.Rotation(28.6 * pi, 4, 'Y')

enter image description here

circular_array(
        ob,
        50,
        (1, 2, 0),
        1,
        matrix = R,
        revs = 10,        
        )

enter image description here

Bringing a scale matrix into the equation would have a spriral effect.

$\endgroup$
1
  • $\begingroup$ Thank you very much for your answer batFinger. Unfortunately i do not understand it. But that is my fault because i have just a few days experience with Blender python and i am pretty proud i made it that far (from my - i have no idea what i am doing - point of view ;). Hopefully i do understand your answer in a few weeks when i am diving deeper into that very amazing topic. $\endgroup$
    – Chris
    Mar 9, 2021 at 20:21

You must log in to answer this question.

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