Skip to content

atomlib.bbox

Bounding boxes

BBox3D

3D axis-aligned bounding box, with corners min and max.

Source code in atomlib/bbox.py
class BBox3D:
    """
    3D axis-aligned bounding box, with corners `min` and `max`.
    """

    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)

    @classmethod
    def from_pts(cls, pts: t.Union[numpy.ndarray, t.Sequence[Vec3]]) -> BBox3D:
        """Construct the minimum bounding box containing the points `pts`."""
        pts = numpy.atleast_2d(pts).reshape(-1, 3)
        return cls(numpy.nanmin(pts, axis=0), numpy.nanmax(pts, axis=0))

    @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.])

    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()

    @property
    def min(self) -> Vec3:
        """Return the minimum corner `[xmin, ymin, zmin]`."""
        return self.inner[:, 0]

    @property
    def max(self) -> Vec3:
        """Return the minimum corner `[xmax, ymax, zmax]`."""
        return self.inner[:, 1]

    @property
    def x(self) -> numpy.ndarray:
        """Return the interval `[xmin, xmax]`."""
        return self.inner[0]

    @property
    def y(self) -> numpy.ndarray:
        """Return the interval `[ymin, ymax]`."""
        return self.inner[1]

    @property
    def z(self) -> numpy.ndarray:
        """Return the interval `[zmin, zmax]`."""
        return self.inner[2]

    @property
    def size(self) -> Vec3:
        """Return the size `[xsize, ysize, zsize]`."""
        return self.max - self.min

    def volume(self) -> float:
        """Return the volume of the bbox."""
        return float(numpy.prod(self.size))

    def corners(self) -> numpy.ndarray:
        """Return a (8, 3) ndarray containing the corners of the bbox."""
        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
        )

    def __or__(self, other: t.Union[Vec3, BBox3D]) -> BBox3D:
        """
        Union this bbox with another point or bbox.
        """
        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:
        """
        Intersect this bbox with another point or bbox.

        Undefined if there is no overlap between the two.
        """
        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

Return the minimum corner [xmin, ymin, zmin].

max property

max: Vec3

Return the minimum corner [xmax, ymax, zmax].

x property

Return the interval [xmin, xmax].

y property

Return the interval [ymin, ymax].

z property

Return the interval [zmin, zmax].

size property

size: Vec3

Return the size [xsize, ysize, zsize].

from_pts classmethod

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

Construct the minimum bounding box containing the points pts.

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

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.])

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()

volume

volume() -> float

Return the volume of the bbox.

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

corners

corners() -> ndarray

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

Source code in atomlib/bbox.py
def corners(self) -> numpy.ndarray:
    """Return a (8, 3) ndarray containing the corners of the bbox."""
    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
    )