Maya Python – Select By Volume

posted in: Uncategorized | 1

I wrote up this quick script in response to a tech-artists.org forum post requested help on this selection-helper method. The below script should properly select all objects in a scene that are at least partially contained within the specified object’s bounding box volume.  This script is currently limited by the fact that the bounding box calculation performed by the xform command returns an axis-aligned box. To improve this, calculate the boundingbox with respect to the object’s local transformation – see http://tech-artists.org/forum/showthread.php?2860-maya-Object-Aligned-Bounding-Box. This will allow you to rotate your bounding volume object and still return visually expected results.

import maya.cmds as cmds

class BoundingBox():
''' Helper class for bounding box-related calculations. '''

    @classmethod
    def FromShape(cls, shapeObj):
    ''' Constructor method to create a bounding box from a shape. '''

        boundingBox = BoundingBox()
        bb = cmds.xform(shapeObj, q=True, bb=True)
        boundingBox.minX = bb[0]
        boundingBox.minY = bb[1]
        boundingBox.minZ = bb[2]
        boundingBox.maxX = bb[3]
        boundingBox.maxY = bb[4]
        boundingBox.maxZ = bb[5]
        return boundingBox

    def ContainsPoint(self, point):
    ''' Returns whether or not a point is contained in this bounding box. '''
        return (point[0] > self.minX and point[0] < self.maxX and point[1] > self.minY and point[1] < self.maxY and point[2] > self.minZ and point[2] < self.maxZ)

    def ContainsShape(self, shape):
    ''' Returns whether or not a shape is intersecting with this bounding box. '''
        shapeBB = BoundingBox.FromShape(shape)
        return (shapeBB.minX < self.maxX and shapeBB.maxX > self.minX) and (shapeBB.minY < self.maxY and shapeBB.maxY > self.minY) and(shapeBB.maxZ < self.maxZ and shapeBB.maxZ > self.minZ)

def SelectByVolume(volumeObj):
 ''' Selects all transforms in the scene that are within the specified argument's bounding box volume. '''

    # Create bounding box class from object
    boundingBox = BoundingBox.FromShape(volumeObj)

    # Get all scene objects asides from bounding box
    cmds.select(allDagObjects=True)
    sceneObjs = [obj for obj in cmds.ls(sl=True) if obj != volumeObj]

    # Compare against every object in our scene to determine what is in our volume
    newSelection = list()
    for obj in sceneObjs:
        objPos = cmds.xform(obj, q=True, rotatePivot=True, ws=True)

        # If the shape's bounding box intersects with the volume's bounding box
        if boundingBox.ContainsShape(obj):
            print "%s is contained."%obj
            newSelection.append(obj)

        # NOTE - Optional alternative means to determine objects within volume.
        # If the center of the object is contained
        #if boundingBox.ContainsPoint(objPos):
            # print "%s is contained."%obj
            # newSelection.append(obj)

    # Silly but compact list comprehension way of getting all our objects
    #newSelection = [obj for obj in [obj for obj in cmds.ls(sl=True) if obj != volumeObj] if boundingBox.ContainsPoint( cmds.xform(obj, q=True, rotatePivot=True, ws=True))]

    # Update selection
    if newSelection:
        cmds.select(newSelection)
    else:
        cmds.select(clear=True)

# Usage
yourVolumeObject = 'pCube1'
SelectByVolume(yourVolumeObject)