Чаще всего для быстрых упрощенных расчетов с геометрией объектов используются их Bounding Box – минимальный параллелепипед, в который вписан данный объект. Однако. иногда большую точность и простоту в расчетах может дать Bounding Sphere – минимальная сфера, в которую можно вписать объект. И если размеры и расположение Bounding Box доступно в Blender для любого объекта сразу, Bounding Sphere необходимо рассчитывать самостоятельно.
Напишем функцию, которая на основании переданного объекта вернет нам координаты центра и радиус описывающей его Bounding Sphere.
1 |
def bounding_sphere(objects, mode='BBOX'): |
Определим функцию, в которую будем передавать два параметра:
objects – объект или список объектов, вокруг которых нужно рассчитать Bounding Sphere
mode – способ расчета Bounding Sphere: GEOMETRY – точный метод, на основе координат каждой точки объекта, или BBOX – упрощенный расчет, на основании только 8 точек от уже известного для каждого объекта Bounding Box. Упрощенный расчет даст меньшую точность (сфера получится больше в диаметре), однако он не вызовет больших нагрузок при расчете для высокополигональных объектов.
Предусмотрим, что в objects мы можем передавать как отдельный объект, так и список объектов.
1 2 |
if not isinstance(objects, list): objects = [objects] |
Определим список координат точек, по которым будем вести расчет (в глобальной системе координат):
1 |
points_co_global = [] |
Для точного подсчета занесем в него координаты всех вертексов объекта.
1 2 3 4 |
if mode == 'GEOMETRY': # GEOMETRY - by all vertices/points - more precis, more slow for obj in objects: points_co_global.extend([obj.matrix_world @ vertex.co for vertex in obj.data.vertices]) |
Для расчета по Bounding Box в список достаточно занести восемь координат точек параллелепипеда.
1 2 3 4 |
elif mode == 'BBOX': # BBOX - by object bounding boxes - less precis, quick for obj in objects: points_co_global.extend([obj.matrix_world @ Vector(bbox) for bbox in obj.bound_box]) |
Имея список координат всех нужных точек, мы можем найти центральную для них точку – это будет центр искомой Bounding Sphere.
Разобьем координаты точек на три списка – отдельно координаты по оси X, отдельно по оси Y и отдельно по оси Z. Считать координаты в проекциях всегда проще и быстрее, чем в 3D пространстве.
1 |
x, y, z = [[point_co[i] for point_co in points_co_global] for i in range(3)] |
Определим функцию, которая будет возвращать центральную точку для двух точек из списка – с максимальной и минимальной координатами.
1 2 |
def get_center(l): return (max(l) + min(l)) / 2 if l else 0.0 |
Теперь мы можем получить координаты центральной точки во всех трех проекциях. Это и будут координаты центра Bounding Sphere.
1 |
b_sphere_center = Vector([get_center(axis) for axis in [x, y, z]]) if (x and y and z) else None |
Имея координаты центра и список всех точек, найдем координаты точки, максимально удаленной от центра. Расстояние между центром и найденной точкой – будет радиусом для Bounding Sphere.
1 |
b_sphere_radius = max(((point - b_sphere_center) for point in points_co_global)) if b_sphere_center else None |
Вернем параметры рассчитанной Bounding Sphere.
1 |
return b_sphere_center, b_sphere_radius.length |
Полный код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
import bpy from mathutils import Vector def bounding_sphere(objects, mode='BBOX'): # return the bounding sphere center and radius for objects (in global coordinates) if not isinstance(objects, list): objects = [objects] points_co_global = [] if mode == 'GEOMETRY': # GEOMETRY - by all vertices/points - more precis, more slow for obj in objects: points_co_global.extend([obj.matrix_world @ vertex.co for vertex in obj.data.vertices]) elif mode == 'BBOX': # BBOX - by object bounding boxes - less precis, quick for obj in objects: points_co_global.extend([obj.matrix_world @ Vector(bbox) for bbox in obj.bound_box]) def get_center(l): return (max(l) + min(l)) / 2 if l else 0.0 x, y, z = [[point_co[i] for point_co in points_co_global] for i in range(3)] b_sphere_center = Vector([get_center(axis) for axis in [x, y, z]]) if (x and y and z) else None b_sphere_radius = max(((point - b_sphere_center) for point in points_co_global)) if b_sphere_center else None return b_sphere_center, b_sphere_radius.length b_sphere_co, b_sphere_radius = bounding_sphere( objects=bpy.context.selected_objects, mode='GEOMETRY' ) print(b_sphere_co, b_sphere_radius) # <Vector (0.0000, 0.0000, 1.5710)> 2.0898222449791612 |