How to create a bounding box for the whole scene

Typically: "How do I... ", "How can I... " questions
Post Reply
Andres
Posts: 18
Joined: 06 Oct 2023, 09:06

How to create a bounding box for the whole scene

Post by Andres »

Hi, I am trying to create a bounding box around all the elements in my scene.

I tried to start with a grouping, to get the bounds of all the objects and then calculate the bounding box. That is ok, but it still has issues.

First, the bounding box is not precise. It is not positioned properly. For example, for the object at the bottom the bounding box is not touching it, it is still under. For the object the top, the bounding box is cutting it because it is not up enough. The bounding box is lower than what it should. Sometimes it is half the object of error, or a little bit, or almost the whole object is out. This happens for the 3 axis.

I already positioned the bounding box at 0, 0, 0. Nothing changes, the bounding box was already at the center. But it is still not matching accurately with the objects of the scene.

My main problem, many problems with grouping. To restore everything, I ungroup. That ungroup is not working as expected. After ungrouping, the icons in the scene hierarchy change, I guess the properties of the objects have also changed. In addition, it is not only ungrouping my group, but everything, including robots. Finally, when the simulation ends, it destroys everything, including the floor.

I also thought about getting all the objects and their coordinates with a for loop. But I am reluctant to this solution because it is very time consuming. I am just looking for a reliable way to generate a bounding boy around all the scene, so you do not have to stick to the code I provided, I am open to other solutions. '

Thank you.

# python
from coppeliasim_zmqremoteapi_client import RemoteAPIClient
import time

'''
@dev: This function gets the x, y, and z lenghts of the bounding box of a given object
@param: the handle of the object to calculate its bounding box
@returns: 3 values. The x length, the y length and the z length
@author: Andres Masis
'''
def getObjectBoundingBoxSize(handle):
# Gets the x lenght
r, m = sim.getObjectFloatParameter(handle, sim.objfloatparam_objbbox_max_x)
r, n = sim.getObjectFloatParameter(handle, sim.objfloatparam_objbbox_min_x)
x = m - n

# Gets the y lenght
r, m = sim.getObjectFloatParameter(handle, sim.objfloatparam_objbbox_max_y)
r, n = sim.getObjectFloatParameter(handle, sim.objfloatparam_objbbox_min_y)
y = m - n

# Gets the z lenght
r, m = sim.getObjectFloatParameter(handle, sim.objfloatparam_objbbox_max_z)
r, n = sim.getObjectFloatParameter(handle, sim.objfloatparam_objbbox_min_z)
z = m - n

# Returns all the values
return x, y, z

# Access to the CoppeliaSim client
client = RemoteAPIClient()
sim = client.getObject('sim')

# Makes sure that the idle loop runs at full speed for this program:
defaultIdleFps = sim.getInt32Param(sim.intparam_idle_fps)
sim.setInt32Param(sim.intparam_idle_fps, 0)

# Run a simulation in stepping mode:
client.setStepping(True)
sim.startSimulation()

# Get all of the elements of the scene.
scene_objects = sim.getObjectsInTree(sim.handle_scene, sim.handle_all, 0)
client.step() # triggers next simulation step

# Create group of objects
groupHandle = sim.groupShapes(scene_objects, False)
client.step() # triggers next simulation step

x, y, z = getObjectBoundingBoxSize(groupHandle)

sim.ungroupShape(groupHandle)
client.step() # triggers next simulation step

boxHandle = sim.createPrimitiveShape(sim.primitiveshape_cuboid,[x, y, z], 0)
client.step() # triggers next simulation step

# Color
sim.setShapeColor(boxHandle, None, sim.colorcomponent_emission, [0, 255, 0])
client.step() # triggers next simulation step

# Transparency
sim.setShapeColor(boxHandle, None, sim.colorcomponent_transparency, [0.5])
client.step() # triggers next simulation step

# Position
sim.setObjectPosition(boxHandle, sim.handle_parent, [0.255, 0.225, 0.225])
client.step() # triggers next simulation step

time.sleep(10)

sim.stopSimulation()

# Restore the original idle loop frequency:
sim.setInt32Param(sim.intparam_idle_fps, defaultIdleFps)

print('Program ended')

coppelia
Site Admin
Posts: 10381
Joined: 14 Dec 2012, 00:25

Re: How to create a bounding box for the whole scene

Post by coppelia »

Hello,

using the individual bounding box extents is a bit tricky, since each shape has a different orientation. You'd have to take into account the 8 corners of each bounding box to compute the global bounding box.

With shapes, you can fetch all meshes and parse all vertices to find out about the exact global bounding box, something like (from within a Python child script):

Code: Select all

#python

def sysCall_init():
    sim = require('sim')
    scene_objects = sim.getObjectsInTree(sim.handle_scene, sim.object_shape_type, 0)
    mmin = [999, 999, 999]
    mmax = [-999, -999, -999]
    for i in range(len(scene_objects)):
        vertices, *_ = sim.getShapeMesh(scene_objects[i])
        pose = sim.getObjectPose(scene_objects[i])
        for j in range(len(vertices)//3):
            vert = [vertices[3 * j + 0], vertices[3 * j + 1], vertices[3 * j + 2]]
            vert = sim.multiplyVector(pose, vert)
            for k in range(3):
                if mmin[k] > vert[k]:
                    mmin[k] = vert[k]
                if mmax[k] < vert[k]:
                    mmax[k] = vert[k]
    print(mmin)
    print(mmax)
This is quite slow, but should be fine if done once. Otherwise, consider doing the calculations in Lua and calling that Lua function from Python.

Cheers

coppelia
Site Admin
Posts: 10381
Joined: 14 Dec 2012, 00:25

Re: How to create a bounding box for the whole scene

Post by coppelia »

This Python version is faster and using numpy instead:

Code: Select all

#python

import numpy as np

def sysCall_init():
    sim = require('sim')
    scene_objects = sim.getObjectsInTree(sim.handle_scene, sim.object_shape_type, 0)
    mmin = [999, 999, 999]
    mmax = [-999, -999, -999]
    for i in range(len(scene_objects)):
        v, *_ = sim.getShapeMesh(scene_objects[i])
        vertices = [(v[i], v[i+1], v[i+2], 1) for i in range(0, len(v), 3)]
        vertices = np.array(vertices).T
        m = sim.getObjectMatrix(scene_objects[i])
        matrix = np.array([m[0:4], m[4:8], m[8:12], [0, 0, 0, 1]])
        vertices = np.dot(matrix, vertices)
        for j in range(len(vertices[0])):
            for k in range(3):
                if mmin[k] > vertices[k][j]:
                    mmin[k] = vertices[k][j]
                if mmax[k] < vertices[k][j]:
                    mmax[k] = vertices[k][j]
    print(mmin)
    print(mmax)

Post Reply