Skip to content

Box3D

Represents a single axis-aligned 3D bounding box: a minimum corner plus a width, height, and depth.

Parameters

FieldTypeDescription
datanp.ndarray | list | tupleBox coordinates [x, y, z, width, height, depth] (min corner + size), convertible to a (6,) float32 array via np.asarray.

Raises

ExceptionCondition
TypeErrordata can't be converted into a uniform float32 array (e.g. ragged nested lists)
ValueErrordata is not rank-1 (e.g. a (N, 6) batch — use Boxes3D instead)
ValueErrordata does not have exactly 6 elements
ValueErrorAny element is non-finite (NaN/Inf)
ValueErrorwidth, height, or depth (data[3:6]) is negative

A zero-sized width/height/depth is allowed but logs a warning (volume will be 0).

Attributes

AttributeTypeDescription
datanp.ndarrayDefensive copy of the underlying (6,) array [x, y, z, width, height, depth]. Reading it returns a copy; writing re-validates the same way as construction.
shapetuple[int, ...]Always (6,).
ndimintAlways 1.
dtypenp.dtypeAlways float32.
sizeintAlways 6.
shape_spectuple[int, ...]Class-level shape spec (6,).
widthnp.ndarray (scalar)data[3].
heightnp.ndarray (scalar)data[4].
depthnp.ndarray (scalar)data[5].
volumenp.ndarray (scalar)width * height * depth.
centernp.ndarray[cx, cy, cz], computed as the min corner (data[:3]) plus half the size — data[:3] itself is the min corner, not the center.

Methods

MethodDescription
to_numpy(copy=True)Returns the (6,) array. Pass copy=False for a zero-copy view — mutating it mutates the Box3D.
copy()Returns a new Box3D with an independent data buffer.
translate(offset)Returns a new Box3D with [x, y, z] shifted by offset ([dx, dy, dz]). Size is unchanged.
scale(factor)Returns a new Box3D scaled around its center. factor is a scalar (uniform) or a length-3 array-like ([fw, fh, fd], per-axis). The min corner is recomputed so the center stays fixed.
convert_box_format(target_format)Returns this box re-expressed as a (6,) array in target_format: "xyzwhd" (native), "xyzxyz" (min corner, max corner), or "cxcyczwhd" (center + size).
Box3D.from_format(data, source_format)Classmethod. Inverse of convert_box_format — builds a Box3D from data already given in source_format ("xyzwhd"/"xyzxyz"/"cxcyczwhd").
Box3D.coerce(value)Classmethod. Returns value unchanged if it's already a Box3D; otherwise wraps an array-like into one (same validation as the constructor). Raises TypeError for any other input.

Operators

OperationBehavior
box == otherTrue only if other is also a Box3D with element-equal data. False for anything else.
len(box)Returns 6 — the length of the coordinate vector, not a box count (a single box is conceptually one item).
box[i]Always raises ValueErrorBox3D has no single_cls, so it isn't indexable/iterable like a batch. Use .data[i] for raw coordinate access instead.
for x in boxRaises ValueError immediately (falls back to box[0], which raises).
np.asarray(box)Returns a copy of data as an np.ndarray; NumPy functions accept a Box3D directly.
hash(box)Not supported — a Box3D can't be used as a dict key or set member.

Visualization

datatypes.visualize(box3d, entity_path=...) logs it as a single rr.Boxes3D(mins=[[x, y, z]], sizes=[[w, h, d]]), alongside a small 3D reference frame and an "origin" text label logged to {entity_path}/origin. Passing label="..." attaches a floating text label anchored at the box's mins vertex ([x, y, z]).

Example

python
"""Demonstrates the Telekinesis Box3D datatype."""

import time

import numpy as np
from loguru import logger
import rerun as rr

from telekinesis import datatypes

def box3d_example():
    """Demonstrate creation, access, update, translation, scaling, NumPy interop, format conversion, and serialization."""

    # ======================= Create ============================================
    coords = [1, 2, 2.5, 5, 3, 5]
    box3d = datatypes.Box3D(coords)
    logger.info(f"Original Box3D: {box3d}")

    # ======================= Inspect ===========================================
    logger.info(f"Box3D data: {box3d.data}")
    logger.info(
        f"shape={box3d.shape}, "
        f"width={box3d.width}, "
        f"height={box3d.height}, "
        f"depth={box3d.depth}, "
        f"volume={box3d.volume}, "
        f"center={box3d.center}"
    )

    # ======================= Visualize =========================================
    rr.init("box3d_example", spawn=True)
    datatypes.visualize(box3d, entity_path="/Box3D/my_box3d", label="Original Box3D")

    # ======================= Update ============================================
    updated_coords = [1, 4, 2.5, 5, 2, 7]
    box3d.data = updated_coords
    logger.info(f"Updated Box3D: {box3d}")
    datatypes.visualize(box3d, entity_path="/Box3D/my_updated_box3d", label="Updated Box3D")

    # ======================= Translate =========================================
    translation = [3, 3, 1]
    translated_box3d = box3d.translate(translation)
    logger.info(f"Translated Box3D: {translated_box3d}")
    datatypes.visualize(
        translated_box3d, entity_path="/Box3D/my_translated_box3d", label="Translated Box3D"
    )

    # ======================= Scale =============================================
    scale_factors = [2, 0.5, 1.5]
    scaled_box3d = box3d.scale(scale_factors)
    logger.info(f"Scaled Box3D: {scaled_box3d}")
    datatypes.visualize(scaled_box3d, entity_path="/Box3D/my_scaled_box3d", label="Scaled Box3D")

    # ======================= NumPy Interop =====================================
    multiply_factor = 1
    scaled_dimensions = np.multiply(box3d, multiply_factor)
    logger.info(
        f"Box3D dimensions multiplied by {multiply_factor} using numpy: {scaled_dimensions}"
    )

    # ======================= Convert ===========================================
    xyzxyz_coords = [1, 2, 2.5, 5, 3, 5]
    box3d_from_xyzxyz = datatypes.Box3D.from_format(xyzxyz_coords, source_format="xyzxyz")
    logger.info(f"Box3D created from xyzxyz format: {box3d_from_xyzxyz}")

    # ======================= Serialize / Deserialize ===========================
    start = time.perf_counter()
    serialized = datatypes.serialize(box3d)
    serialization_ms = (time.perf_counter() - start) * 1000

    start = time.perf_counter()
    deserialized = datatypes.deserialize(serialized)["param_0"]
    deserialization_ms = (time.perf_counter() - start) * 1000

    logger.info(f"Deserialized Box3D: {deserialized}")
    logger.info(f"Round-trip successful: {box3d == deserialized}")
    logger.info(f"Serialization time: {serialization_ms:.3f} ms")
    logger.info(f"Deserialization time: {deserialization_ms:.3f} ms")


if __name__ == "__main__":
    box3d_example()