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
.
Components components
Optional: Translation3D
, RotationAxisAngle
, RotationQuat
, Scale3D
, TransformMat3x3
, TransformRelation
, AxisLength
Shown in 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 transforms transform hierarchy."""
import numpy as np
import rerun as rr
import rerun.blueprint as rrb
rr.init("rerun_example_transform3d_hierarchy", spawn=True)
# One space with the sun in the center, and another one with the planet.
rr.send_blueprint(
rrb.Horizontal(rrb.Spatial3DView(origin="sun"), rrb.Spatial3DView(origin="sun/planet", contents="sun/**"))
)
rr.set_time_seconds("sim_time", 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]).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(0, 6 * 120):
time = i / 120.0
rr.set_time_seconds("sim_time", 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],
from_parent=True,
),
)
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_sequence("tick", 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_sequence("tick", 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_sequence("tick", 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.TimeSequenceColumn("tick", 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),
)