Box3D
Represents a single axis-aligned 3D bounding box: a minimum corner plus a width, height, and depth.
Parameters
| Field | Type | Description |
|---|---|---|
data | np.ndarray | list | tuple | Box coordinates [x, y, z, width, height, depth] (min corner + size), convertible to a (6,) float32 array via np.asarray. |
Raises
| Exception | Condition |
|---|---|
TypeError | data can't be converted into a uniform float32 array (e.g. ragged nested lists) |
ValueError | data is not rank-1 (e.g. a (N, 6) batch — use Boxes3D instead) |
ValueError | data does not have exactly 6 elements |
ValueError | Any element is non-finite (NaN/Inf) |
ValueError | width, 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
| Attribute | Type | Description |
|---|---|---|
data | np.ndarray | Defensive 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. |
shape | tuple[int, ...] | Always (6,). |
ndim | int | Always 1. |
dtype | np.dtype | Always float32. |
size | int | Always 6. |
shape_spec | tuple[int, ...] | Class-level shape spec (6,). |
width | np.ndarray (scalar) | data[3]. |
height | np.ndarray (scalar) | data[4]. |
depth | np.ndarray (scalar) | data[5]. |
volume | np.ndarray (scalar) | width * height * depth. |
center | np.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
| Method | Description |
|---|---|
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
| Operation | Behavior |
|---|---|
box == other | True 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 ValueError — Box3D has no single_cls, so it isn't indexable/iterable like a batch. Use .data[i] for raw coordinate access instead. |
for x in box | Raises 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()
