1

I am trying to calculate the cooordinates of the center of a group of objects using this code:

obj = bpy.context.active_object                                             
vcos = [ obj.matrix_world * v.co for v in obj.data.vertices ]                     
findCenter = lambda l: ( max(l) + min(l) ) / 2      
x,y,z  = [ [ v[i] for v in vcos ] for i in range(3) ]      
center = [ findCenter(axis) for axis in [x,y,z] ]

The problem is that "bpy.context.active_object" returns only the (bpy_struct, Object) of one OBJECT only (the first to render) and does not consider the other objects. As a result the obtained X,Y,Z coordinates are only correct for one object and not all of them.

Any solution?

Amir

Gorgious
  • 30,723
  • 2
  • 44
  • 101
Amir Aly
  • 11
  • 4
  • 1
    try bpy.context.selected_objects – Gorgious Sep 09 '20 at 13:48
  • @Gorgious: This gives ('list' object has no attribute 'data') this is attribute of vertices used later in the code. I use this code after rendering, so I have a loop and I render hide some objects and render others and in each time I render an individual object I wanna get it X,Y,Z coordinates. For some rason, when I print the coordinates of (center) of each object rendered separately in the loop, i find the same coordinates and I find "obj" is only referring to the first rendered object and does not change when I render the other objects. Any advise???? – Amir Aly Sep 09 '20 at 14:47
  • What is not clear for me: do you want the center considering all objects at once (so 1 center for all) or the center for each object? (@Gorgious) – lemon Sep 09 '20 at 15:04

2 Answers2

2

Take this image : enter image description here

bpy.context.active_object only gives a reference to the active object in the given context. This is Cube.002, with the light orange outline.

Cube is selected, but it is not the active object. (dark orange outline).

When you type bpy.context.selected_objects in the blender console you get an unordered list of all the selected objects.

>>> bpy.context.selected_objects
[bpy.data.objects['Cube'], bpy.data.objects['Cube.002']]

In python you can iterate through a list of objects with a for loop. (Documentation) Your code, which is valid for one object, can be translated for a list to :

import bpy

for obj in bpy.context.selected_objects: if obj.type != 'MESH': continue # Sidenote : Use @ instead of * for element-wise multiplication in matrices
vcos = [ obj.matrix_world @ v.co for v in obj.data.vertices ]
findCenter = lambda l: ( max(l) + min(l) ) / 2
x,y,z = [ [ v[i] for v in vcos ] for i in range(3) ]
center = [ findCenter(axis) for axis in [x,y,z] ]

print(center)

Result :

enter image description here

Gorgious
  • 30,723
  • 2
  • 44
  • 101
  • Given the OP's new question might pay to put the type checking in loop, eg if obj.type != 'MESH' : continue to avoid trying to process vertices of non mesh objects. https://blender.stackexchange.com/questions/196663/error-when-processing-a-blender-file-in-python – batFINGER Oct 07 '20 at 13:54
  • @batFINGER Good point ! Edited answer – Gorgious Oct 07 '20 at 14:07
1

Bounds center is the middle of the bounding box.

The local positions of the axis oriented bounding box of a mesh are generated for us and available in the object's bounding box property in local coordinates.

The average of the bounding boxes 8 corner coordinates give the local center, which can be converted to global by multiplying by matrix world of owner.

Pros, only summing 8 coordinates per object and matrix multiplying once Using the object's vertices is unnecessary overhead.

import bpy
from mathutils import Vector
# mesh objects in scene
scene = bpy.context.scene
mesh_obs = [o for o in scene.objects if o.type == 'MESH']

for ob in mesh_obs: bbox_local_center = sum((Vector(b) for b in ob.bound_box), Vector()) / 8 center = ob.matrix_world @ bbox_local_center print(f"{ob.name} {center}")

Thought this rang a bell, possible duplicate and source of question code. Get center of geometry of an object If you source code from elsewhere, providing a link gives context.

Note.

If instead you wish to get the "global bounding box" of all recommend using numpy.

import bpy
import numpy as np

mesh objects in scene

scene = bpy.context.scene mesh_obs = [o for o in scene.objects if o.type == 'MESH']

stack global coords of bboxes of all objects in scene

coords = np.vstack( (np.dot( np.hstack( (np.array(ob.bound_box), np.ones(8).reshape(8, 1)) ), ob.matrix_world.transposed() ) for ob in mesh_obs) )

#bottom front left corner, top, right back corner
print(coords.min(), coords.max())

batFINGER
  • 84,216
  • 10
  • 108
  • 233