Transform3D
A transform between two 3D spaces, i.e. a pose.
From the point of view of the entity's coordinate system, all components are applied in the inverse order they are listed here. E.g. if both a translation and a max3x3 transform are present, the 3x3 matrix is applied first, followed by the translation.
Whenever you log this archetype, it will write all components, even if you do not explicitly set them. This means that if you first log a transform with only a translation, and then log one with only a rotation, it will be resolved to a transform with only a rotation.
For transforms that affect only a single entity and do not propagate along the entity tree refer to archetypes.InstancePoses3D
.
Fields fields
Optional optional
translation
:Translation3D
rotation_axis_angle
:RotationAxisAngle
quaternion
:RotationQuat
scale
:Scale3D
mat3x3
:TransformMat3x3
relation
:TransformRelation
axis_length
:AxisLength
Can be shown in can-be-shown-in
- Spatial3DView
- Spatial2DView (if logged above active projection)
- DataframeView
API reference links api-reference-links
Examples examples
Variety of 3D transforms variety-of-3d-transforms
"""Log different transforms between three arrows."""
from math import pi
import rerun as rr
from rerun.datatypes import Angle, RotationAxisAngle
rr.init("rerun_example_transform3d", spawn=True)
arrow = rr.Arrows3D(origins=[0, 0, 0], vectors=[0, 1, 0])
rr.log("base", arrow)
rr.log("base/translated", rr.Transform3D(translation=[1, 0, 0]))
rr.log("base/translated", arrow)
rr.log(
"base/rotated_scaled",
rr.Transform3D(
rotation=RotationAxisAngle(axis=[0, 0, 1], angle=Angle(rad=pi / 4)),
scale=2,
),
)
rr.log("base/rotated_scaled", arrow)
Transform hierarchy transform-hierarchy
"""Logs a transform hierarchy."""
import numpy as np
import rerun as rr
import rerun.blueprint as rrb
rr.init("rerun_example_transform3d_hierarchy", spawn=True)
if False:
# One space with the sun in the center, and another one with the planet.
# TODO(#5521): enable this once we have it in Rust too, so that the snippets compare equally
rr.send_blueprint(
rrb.Horizontal(rrb.Spatial3DView(origin="sun"), rrb.Spatial3DView(origin="sun/planet", contents="sun/**")),
)
rr.set_time("sim_time", duration=0)
# Planetary motion is typically in the XY plane.
rr.log("/", rr.ViewCoordinates.RIGHT_HAND_Z_UP, static=True)
# Setup points, all are in the center of their own space:
# TODO(#1361): Should use spheres instead of points.
rr.log("sun", rr.Points3D([0.0, 0.0, 0.0], radii=1.0, colors=[255, 200, 10]))
rr.log("sun/planet", rr.Points3D([0.0, 0.0, 0.0], radii=0.4, colors=[40, 80, 200]))
rr.log("sun/planet/moon", rr.Points3D([0.0, 0.0, 0.0], radii=0.15, colors=[180, 180, 180]))
# Draw fixed paths where the planet & moon move.
d_planet = 6.0
d_moon = 3.0
angles = np.arange(0.0, 1.01, 0.01) * np.pi * 2
circle = np.array([np.sin(angles), np.cos(angles), angles * 0.0], dtype=np.float32).transpose()
rr.log("sun/planet_path", rr.LineStrips3D(circle * d_planet))
rr.log("sun/planet/moon_path", rr.LineStrips3D(circle * d_moon))
# Movement via transforms.
for i in range(6 * 120):
time = i / 120.0
rr.set_time("sim_time", duration=time)
r_moon = time * 5.0
r_planet = time * 2.0
rr.log(
"sun/planet",
rr.Transform3D(
translation=[np.sin(r_planet) * d_planet, np.cos(r_planet) * d_planet, 0.0],
rotation=rr.RotationAxisAngle(axis=(1, 0, 0), degrees=20),
),
)
rr.log(
"sun/planet/moon",
rr.Transform3D(
translation=[np.cos(r_moon) * d_moon, np.sin(r_moon) * d_moon, 0.0],
relation=rr.TransformRelation.ChildFromParent,
),
)
Update a transform over time update-a-transform-over-time
"""
Update a transform over time.
See also the `transform3d_column_updates` example, which achieves the same thing in a single operation.
"""
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_row_updates", spawn=True)
rr.set_time("tick", sequence=0)
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),
)
for t in range(100):
rr.set_time("tick", sequence=t + 1)
rr.log(
"box",
rr.Transform3D(
clear=False,
translation=[0, 0, t / 10.0],
rotation_axis_angle=rr.RotationAxisAngle(axis=[0.0, 1.0, 0.0], radians=truncated_radians(t * 4)),
),
)
Update a transform over time, in a single operation update-a-transform-over-time-in-a-single-operation
"""
Update a transform over time, in a single operation.
This is semantically equivalent to the `transform3d_row_updates` example, albeit much faster.
"""
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_column_updates", spawn=True)
rr.set_time("tick", sequence=0)
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),
)
rr.send_columns(
"box",
indexes=[rr.TimeColumn("tick", sequence=range(1, 101))],
columns=rr.Transform3D.columns(
translation=[[0, 0, t / 10.0] for t in range(100)],
rotation_axis_angle=[
rr.RotationAxisAngle(axis=[0.0, 1.0, 0.0], radians=truncated_radians(t * 4)) for t in range(100)
],
),
)
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),
)