Skip to content

atomlib.bbox

BBox3D

3D Bounding Box

Source code in atomlib/bbox.py
class BBox3D:
    """3D Bounding Box"""

    def __init__(self, min: VecLike, max: VecLike):
        # shape: (3, 2)
        # self._inner[1, 0]: min of y
        min, max = to_vec3(min), to_vec3(max)
        self.inner: numpy.ndarray = numpy.stack((min, max), axis=-1)

    def transform_from_unit(self) -> AffineTransform3D:
        """Return the transform which transforms a unit bbox to `self`."""
        from .transform import AffineTransform3D
        return AffineTransform3D.translate(self.min).scale(self.max - self.min)

    def transform_to_unit(self) -> AffineTransform3D:
        """Return the transform which transforms `self` to a unit bbox."""
        return self.transform_from_unit().inverse()

    @classmethod
    def unit(cls) -> BBox3D:
        """Return a unit bbox (cube from [0,0,0] to [1,1,1])."""
        return cls([0., 0., 0.], [1., 1., 1.])

    @property
    def min(self) -> Vec3:
        return self.inner[:, 0]

    @property
    def max(self) -> Vec3:
        return self.inner[:, 1]

    @property
    def x(self) -> numpy.ndarray:
        return self.inner[0]

    @property
    def y(self) -> numpy.ndarray:
        return self.inner[1]

    @property
    def z(self) -> numpy.ndarray:
        return self.inner[2]

    @property
    def size(self) -> Vec3:
        return self.max - self.min

    def volume(self) -> float:
        return float(numpy.prod(self.size))

    def corners(self) -> numpy.ndarray:
        """Return a (8, 3) ndarray containing the corners of self."""
        return numpy.stack(list(map(numpy.ravel, numpy.meshgrid(*self.inner))), axis=-1)

    def pad(self, amount: t.Union[float, VecLike]) -> BBox3D:
        """
        Pad the given BBox by `amount`. If a vector [x, y, z] is given,
        pad each axis by the given amount.
        """
        amount_v = numpy.broadcast_to(amount, 3)

        return type(self)(
            self.min - amount_v,
            self.max + amount_v
        )

    @classmethod
    def from_pts(cls, pts: t.Union[numpy.ndarray, t.Sequence[Vec3]]) -> BBox3D:
        """Construct a BBox containing 'pts'."""
        pts = numpy.atleast_2d(pts).reshape(-1, 3)
        return cls(numpy.nanmin(pts, axis=0), numpy.nanmax(pts, axis=0))

    def __or__(self, other: t.Union[Vec3, BBox3D]) -> BBox3D:
        if isinstance(other, numpy.ndarray):
            return self.from_pts((self.min, self.max, other))

        return type(self)(
            numpy.nanmin(((self.min, other.min)), axis=0),
            numpy.nanmax(((self.max, other.max)), axis=0),
        )

    __ror__ = __or__

    def __and__(self, other: BBox3D) -> BBox3D:
        return type(self)(
            numpy.nanmax(((self.min, other.min)), axis=0),
            numpy.nanmin(((self.max, other.max)), axis=0),
        )

    def __repr__(self) -> str:
        return f"BBox({self.min}, {self.max})"

inner instance-attribute

inner: ndarray = stack((min, max), axis=-1)

min property

min: Vec3

max property

max: Vec3

x property

y property

z property

size property

size: Vec3

transform_from_unit

transform_from_unit() -> AffineTransform3D

Return the transform which transforms a unit bbox to self.

Source code in atomlib/bbox.py
def transform_from_unit(self) -> AffineTransform3D:
    """Return the transform which transforms a unit bbox to `self`."""
    from .transform import AffineTransform3D
    return AffineTransform3D.translate(self.min).scale(self.max - self.min)

transform_to_unit

transform_to_unit() -> AffineTransform3D

Return the transform which transforms self to a unit bbox.

Source code in atomlib/bbox.py
def transform_to_unit(self) -> AffineTransform3D:
    """Return the transform which transforms `self` to a unit bbox."""
    return self.transform_from_unit().inverse()

unit classmethod

unit() -> BBox3D

Return a unit bbox (cube from [0,0,0] to [1,1,1]).

Source code in atomlib/bbox.py
@classmethod
def unit(cls) -> BBox3D:
    """Return a unit bbox (cube from [0,0,0] to [1,1,1])."""
    return cls([0., 0., 0.], [1., 1., 1.])

volume

volume() -> float
Source code in atomlib/bbox.py
def volume(self) -> float:
    return float(numpy.prod(self.size))

corners

corners() -> ndarray

Return a (8, 3) ndarray containing the corners of self.

Source code in atomlib/bbox.py
def corners(self) -> numpy.ndarray:
    """Return a (8, 3) ndarray containing the corners of self."""
    return numpy.stack(list(map(numpy.ravel, numpy.meshgrid(*self.inner))), axis=-1)

pad

pad(amount: Union[float, VecLike]) -> BBox3D

Pad the given BBox by amount. If a vector [x, y, z] is given, pad each axis by the given amount.

Source code in atomlib/bbox.py
def pad(self, amount: t.Union[float, VecLike]) -> BBox3D:
    """
    Pad the given BBox by `amount`. If a vector [x, y, z] is given,
    pad each axis by the given amount.
    """
    amount_v = numpy.broadcast_to(amount, 3)

    return type(self)(
        self.min - amount_v,
        self.max + amount_v
    )

from_pts classmethod

from_pts(pts: Union[ndarray, Sequence[Vec3]]) -> BBox3D

Construct a BBox containing 'pts'.

Source code in atomlib/bbox.py
@classmethod
def from_pts(cls, pts: t.Union[numpy.ndarray, t.Sequence[Vec3]]) -> BBox3D:
    """Construct a BBox containing 'pts'."""
    pts = numpy.atleast_2d(pts).reshape(-1, 3)
    return cls(numpy.nanmin(pts, axis=0), numpy.nanmax(pts, axis=0))