Most often, for quick simplified calculations with the object’s geometry, their Bounding Boxes are used – the minimum parallelepiped into which this object is inscribed. But sometimes the Bounding Sphere – the minimum sphere into which an object can be inscribed – can provide greater accuracy and simplify the calculations. While the location and size of the object’s Bounding Box is available in Blender at once, the Bounding Sphere we need to calculate manually
Let’s write a function that, based on the object list, returns the coordinates of the center of their Bounding Sphere and its radius.
1 |
def bounding_sphere(objects, mode='BBOX'): |
Let’s define a function with two parameters:
objects – an object or an objects list, around which the Bounding Sphere needs to be calculated
mode – how the Bounding Sphere will be calculated: “GEOMETRY” – the exact method, based on the coordinates of each object vertex, or “BBOX” – simplified calculation, which used only 8 points from the already known for each object Bounding Box. This will give less accuracy (the sphere will be larger in diameter), but it will not lose performance when calculating for high poly objects.
Assume that through the “objects” parameter we can pass both a separate object and a list of objects.
1 2 |
if not isinstance(objects, list): objects = [objects] |
Define a list of points coordinates which we will use in calculations (in the global coordinate system):
1 |
points_co_global = [] |
For a precise calculation, we will append the coordinates of all vertices of the object to it.
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]) |
To calculate using the Bounding Box, it is enough to append the only eight coordinates of the Bounding Box parallelepiped to this list.
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]) |
Having a list of coordinates of all the necessary points, we can find the central point for all of them – this will be the center of the required Bounding Sphere.
Let’s split the coordinates into three lists – separately coordinates projection to the X-axis, to the Y-axis, and to the Z-axis. It is always easier and faster to calculate coordinates in their projections than in 3D space.
1 |
x, y, z = [[point_co[i] for point_co in points_co_global] for i in range(3)] |
Let’s define a function that will return the center point for two points from the list – with the maximum and minimum coordinates.
1 2 |
def get_center(l): return (max(l) + min(l)) / 2 if l else 0.0 |
Now we can get the coordinates of the center point in all three projections. These will be the coordinates of the center of our Bounding Sphere.
1 |
b_sphere_center = Vector([get_center(axis) for axis in [x, y, z]]) if (x and y and z) else None |
Having the coordinates of the center and a list of all points, we can find the coordinates of the point that is most distant from the center. The distance between the center and the found point will be the radius for the Bounding Sphere.
1 |
b_sphere_radius = max(((point - b_sphere_center) for point in points_co_global)) if b_sphere_center else None |
Now we can return the parameters of the calculated Bounding Sphere.
1 |
return b_sphere_center, b_sphere_radius.length |
The final code:
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 |
Hi, could you please clarify how exactly can a bounding sphere ‘provide greater accuracy and simplify the calculations’?
Hi!
Bounding spheres calculates more simply, for example, to process the collision of two bounding spheres you need to compare only two vectors – their radius.
The accuracy depends on the object’s shape. If an object’s shape is more close to spherical, for example – Suzanne, it would be more accurate to calculate physics with bounding spheres than bounding boxes.