Send partial updates over time

Rerun allows you to log only the data that has changed in-between frames (or whatever atomic unit your timeline is using), i.e. you can think of this as a sort of diffs or delta encodings.

This is a natural consequence of how Rerun ingests, stores and finally queries data: Rerun always operates that way, whether you're aware of it or not. Consider this simple snippet:

"""Log some very simple points."""

import rerun as rr

rr.init("rerun_example_points3d", spawn=True)

rr.log("points", rr.Points3D([[0, 0, 0], [1, 1, 1]]))

Here, only the positions of the points have been specified but, looking at the complete definition for Points3D, we can see that it has quite a few more components available:

Required: Position3D

Recommended & Optional: Radius, Color, Text, ShowLabels, ClassId, KeypointId

All three languages for which we provide logging SDKs (Python, Rust, C++) expose APIs that allow fine-grained control over which components of an archetypes, when, and how.

The best way to learn about these APIs is to see them in action: check out the examples below.

Examples examples

Update specific properties of a point cloud over time update-specific-properties-of-a-point-cloud-over-time

"""Update specific properties of a point cloud over time."""

import rerun as rr

rr.init("rerun_example_points3d_partial_updates", spawn=True)

positions = [[i, 0, 0] for i in range(0, 10)]

rr.set_time_sequence("frame", 0)
rr.log("points", rr.Points3D(positions))

for i in range(0, 10):
    colors = [[20, 200, 20] if n < i else [200, 20, 20] for n in range(0, 10)]
    radii = [0.6 if n < i else 0.2 for n in range(0, 10)]

    # Update only the colors and radii, leaving everything else as-is.
    rr.set_time_sequence("frame", i)
    rr.log("points", rr.Points3D.from_fields(radii=radii, colors=colors))

# Update the positions and radii, and clear everything else in the process.
rr.set_time_sequence("frame", 20)
rr.log("points", rr.Points3D.from_fields(clear_unset=True, positions=positions, radii=0.3))

Update specific properties of a transform over time update-specific-properties-of-a-transform-over-time

"""Update specific properties of a transform over time."""

import math

import rerun as rr


def truncated_radians(deg: float) -> float:
    return float(int(math.radians(deg) * 1000.0)) / 1000.0


rr.init("rerun_example_transform3d_partial_updates", spawn=True)

# Set up a 3D box.
rr.log(
    "box",
    rr.Boxes3D(half_sizes=[4.0, 2.0, 1.0], fill_mode=rr.components.FillMode.Solid),
    rr.Transform3D(clear=False, axis_length=10),
)

# Update only the rotation of the box.
for deg in range(46):
    rad = truncated_radians(deg * 4)
    rr.log(
        "box",
        rr.Transform3D.from_fields(
            rotation_axis_angle=rr.RotationAxisAngle(axis=[0.0, 1.0, 0.0], radians=rad),
        ),
    )

# Update only the position of the box.
for t in range(51):
    rr.log(
        "box",
        rr.Transform3D.from_fields(translation=[0, 0, t / 10.0]),
    )

# Update only the rotation of the box.
for deg in range(46):
    rad = truncated_radians((deg + 45) * 4)
    rr.log(
        "box",
        rr.Transform3D.from_fields(
            rotation_axis_angle=rr.RotationAxisAngle(axis=[0.0, 1.0, 0.0], radians=rad),
        ),
    )

# Clear all of the box's attributes, and reset its axis length.
rr.log(
    "box",
    rr.Transform3D.from_fields(clear_unset=True, axis_length=15),
)

Update specific parts of a 3D mesh over time update-specific-parts-of-a-3d-mesh-over-time

"""Log a simple colored triangle, then update its vertices' positions each frame."""

import numpy as np
import rerun as rr

rr.init("rerun_example_mesh3d_partial_updates", spawn=True)

vertex_positions = np.array([[-1.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]], dtype=np.float32)

# Log the initial state of our triangle
rr.set_time_sequence("frame", 0)
rr.log(
    "triangle",
    rr.Mesh3D(
        vertex_positions=vertex_positions,
        vertex_normals=[0.0, 0.0, 1.0],
        vertex_colors=[[255, 0, 0], [0, 255, 0], [0, 0, 255]],
    ),
)

# Only update its vertices' positions each frame
factors = np.abs(np.sin(np.arange(1, 300, dtype=np.float32) * 0.04))
for i, factor in enumerate(factors):
    rr.set_time_sequence("frame", i)
    rr.log("triangle", rr.Mesh3D.from_fields(vertex_positions=vertex_positions * factor))