3D transformations for Python with comprehensive rotation representations, coordinate conversions, and visualization tools
—
3D plotting and visualization tools using matplotlib and Open3D backends for transformations, trajectories, and geometric objects with comprehensive rendering capabilities.
2D and 3D plotting using matplotlib with support for transformations and geometric objects.
# Layout functions
def make_3d_axis(ax_s=1, pos=111, unit=None, n_ticks=5, **kwargs):
"""
Create 3D matplotlib axis with proper scaling.
Parameters:
- ax_s: float - Axis scaling factor
- pos: int - Subplot position
- unit: str, optional - Axis unit label
- n_ticks: int - Number of axis ticks
Returns:
- ax: Axes3D - Configured 3D axis
"""
def remove_frame(ax):
"""Remove frame decorations from 3D axis."""
# Transformation plotting
def plot_transform(ax=None, A2B=None, s=1.0, ax_s=1, **kwargs):
"""
Plot transformation as coordinate frame.
Parameters:
- ax: Axes3D, optional - 3D axis
- A2B: array, shape (4, 4), optional - Transformation matrix
- s: float - Frame size scaling
- ax_s: float - Axis scaling
"""
def plot_screw(ax=None, q=None, s_axis=None, h=None, theta=None, s=1.0, ax_s=1, **kwargs):
"""Plot screw motion visualization."""
def plot_trajectory(P=None, show_direction=True, n_frames=10, s=1.0, ax=None, **kwargs):
"""
Plot 3D trajectory with direction indicators.
Parameters:
- P: array, shape (n_steps, 3 or 4, 4) - Trajectory as transforms or positions
- show_direction: bool - Show trajectory direction arrows
- n_frames: int - Number of coordinate frames to show
- s: float - Frame size scaling
- ax: Axes3D, optional - 3D axis
"""
# Geometry plotting
def plot_box(ax=None, size=[1, 1, 1], A2B=np.eye(4), **kwargs):
"""Plot 3D box."""
def plot_sphere(ax=None, radius=1.0, p=np.zeros(3), **kwargs):
"""Plot sphere."""
def plot_cylinder(ax=None, length=1.0, radius=1.0, A2B=np.eye(4), **kwargs):
"""Plot cylinder."""
def plot_ellipsoid(ax=None, radii=[1, 1, 1], A2B=np.eye(4), **kwargs):
"""Plot ellipsoid."""
def plot_capsule(ax=None, height=1.0, radius=1.0, A2B=np.eye(4), **kwargs):
"""Plot capsule (cylinder with hemispherical ends)."""
def plot_cone(ax=None, length=1.0, radius=1.0, A2B=np.eye(4), **kwargs):
"""Plot cone."""
def plot_mesh(ax, filename, A2B=np.eye(4), **kwargs):
"""Plot mesh from file."""
# Vector utilities
def plot_vector(ax=None, start=np.zeros(3), direction=np.array([1, 0, 0]), **kwargs):
"""Plot 3D vector."""
def plot_length_variable(ax, start_point, end_point, label, **kwargs):
"""Plot length measurement with label."""Reusable artist objects for complex 3D visualizations.
class Arrow3D:
"""3D arrow visualization artist."""
class Frame:
"""Coordinate frame visualization artist."""
class LabeledFrame:
"""Labeled coordinate frame artist."""
class Trajectory:
"""3D trajectory visualization artist."""
class Camera:
"""Camera visualization artist."""High-performance 3D rendering using Open3D backend (requires open3d dependency).
def figure(**kwargs):
"""
Create Open3D-based 3D figure.
Returns:
- fig: Figure - Open3D figure instance
"""
class Figure:
"""Open3D-based 3D figure for high-performance rendering."""
def add(self, artist):
"""
Add artist to figure.
Parameters:
- artist: Artist - 3D artist object
"""
def show(self):
"""Display interactive 3D visualization."""
def plot(self, **kwargs):
"""Configure plot parameters."""
# Open3D Artist classes
class Artist:
"""Base artist class for Open3D rendering."""
class Line3D(Artist):
"""3D line visualization."""
class PointCollection3D(Artist):
"""Point cloud visualization."""
class Vector3D(Artist):
"""3D vector artist."""
class Frame(Artist):
"""Coordinate frame artist."""
class Trajectory(Artist):
"""3D trajectory artist."""
class Camera(Artist):
"""Camera visualization artist."""
class Box(Artist):
"""3D box artist."""
class Sphere(Artist):
"""Sphere artist."""
class Cylinder(Artist):
"""Cylinder artist."""
class Mesh(Artist):
"""Mesh visualization artist."""
class Ellipsoid(Artist):
"""Ellipsoid artist."""
class Capsule(Artist):
"""Capsule artist."""
class Cone(Artist):
"""Cone artist."""
class Plane(Artist):
"""Plane artist."""
class Graph(Artist):
"""Graph structure artist."""import numpy as np
import matplotlib.pyplot as plt
import pytransform3d.transformations as pt
import pytransform3d.rotations as pr
from pytransform3d.plot_utils import make_3d_axis, plot_transform
# Create transformations
T1 = pt.transform_from(p=[1, 0, 0])
R2 = pr.matrix_from_euler([0, 0, np.pi/4], "xyz", extrinsic=True)
T2 = pt.transform_from(R=R2, p=[0, 1, 0])
T3 = pt.transform_from(p=[0, 0, 1])
# Plot transformations
ax = make_3d_axis(ax_s=2)
plot_transform(ax=ax, A2B=np.eye(4), s=0.3, label='Origin')
plot_transform(ax=ax, A2B=T1, s=0.3, label='T1')
plot_transform(ax=ax, A2B=T2, s=0.3, label='T2')
plot_transform(ax=ax, A2B=T3, s=0.3, label='T3')
ax.legend()
plt.show()import numpy as np
import matplotlib.pyplot as plt
import pytransform3d.trajectories as ptr
from pytransform3d.plot_utils import make_3d_axis, plot_trajectory
# Generate circular trajectory
n_steps = 50
t = np.linspace(0, 2*np.pi, n_steps)
trajectory = []
for angle in t:
# Circular motion in x-y plane
x = np.cos(angle)
y = np.sin(angle)
z = 0.1 * angle # slight upward spiral
# Rotation to keep "forward" direction tangent to circle
R = pr.matrix_from_euler([0, 0, angle + np.pi/2], "xyz", extrinsic=True)
T = pt.transform_from(R=R, p=[x, y, z])
trajectory.append(T)
trajectory = np.array(trajectory)
# Plot trajectory
ax = make_3d_axis(ax_s=2)
plot_trajectory(trajectory, show_direction=True, n_frames=8, s=0.1, ax=ax)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
plt.show()import numpy as np
import matplotlib.pyplot as plt
from pytransform3d.plot_utils import (
make_3d_axis, plot_box, plot_sphere, plot_cylinder, plot_cone
)
import pytransform3d.transformations as pt
# Create scene with various objects
ax = make_3d_axis(ax_s=3)
# Plot different geometric objects
plot_box(ax=ax, size=[0.5, 0.3, 0.2],
A2B=pt.transform_from(p=[0, 0, 0]),
alpha=0.7, color='red')
plot_sphere(ax=ax, radius=0.3, p=[1, 0, 0],
alpha=0.7, color='green')
plot_cylinder(ax=ax, length=0.8, radius=0.2,
A2B=pt.transform_from(p=[0, 1, 0]),
alpha=0.7, color='blue')
plot_cone(ax=ax, length=0.6, radius=0.25,
A2B=pt.transform_from(p=[1, 1, 0]),
alpha=0.7, color='yellow')
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
plt.show()import numpy as np
import matplotlib.pyplot as plt
from pytransform3d.transform_manager import TransformManager
from pytransform3d.plot_utils import make_3d_axis, plot_cylinder, plot_sphere
import pytransform3d.transformations as pt
import pytransform3d.rotations as pr
# Create simple robot arm
tm = TransformManager()
# Base
tm.add_transform("world", "base", pt.transform_from(p=[0, 0, 0.1]))
# First joint (shoulder)
R1 = pr.matrix_from_euler([0, 0, 0.3], "xyz", extrinsic=True)
tm.add_transform("base", "link1", pt.transform_from(R=R1, p=[0, 0, 0.2]))
# Second joint (elbow)
R2 = pr.matrix_from_euler([0, -0.5, 0], "xyz", extrinsic=True)
tm.add_transform("link1", "link2", pt.transform_from(R=R2, p=[0.4, 0, 0]))
# End effector
tm.add_transform("link2", "end_effector", pt.transform_from(p=[0.3, 0, 0]))
# Visualize robot
ax = make_3d_axis(ax_s=1)
# Plot coordinate frames
tm.plot_frames_in("world", ax=ax, s=0.1)
tm.plot_connections_in_frame("world", ax=ax)
# Add geometric representation
# Base cylinder
plot_cylinder(ax=ax, length=0.1, radius=0.05,
A2B=tm.get_transform("world", "base"),
color='gray', alpha=0.8)
# Link 1
T_link1 = tm.get_transform("world", "link1")
link1_T = pt.transform_from(p=[0.2, 0, 0]) # offset for link geometry
plot_cylinder(ax=ax, length=0.4, radius=0.03,
A2B=pt.concat(T_link1, link1_T),
color='blue', alpha=0.8)
# Link 2
T_link2 = tm.get_transform("world", "link2")
link2_T = pt.transform_from(p=[0.15, 0, 0])
plot_cylinder(ax=ax, length=0.3, radius=0.025,
A2B=pt.concat(T_link2, link2_T),
color='red', alpha=0.8)
# End effector
T_end = tm.get_transform("world", "end_effector")
plot_sphere(ax=ax, radius=0.03, p=T_end[:3, 3], color='green')
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
plt.show()# Note: Requires 'pip install open3d'
try:
import pytransform3d.visualizer as pv
import pytransform3d.transformations as pt
import numpy as np
# Create Open3D figure
fig = pv.figure()
# Add coordinate frame
frame = pv.Frame(A2B=np.eye(4), s=0.3)
fig.add(frame)
# Add trajectory
trajectory = ptr.random_trajectories(50)
traj_artist = pv.Trajectory(trajectory)
fig.add(traj_artist)
# Add geometric objects
box = pv.Box(size=[0.2, 0.2, 0.2], A2B=pt.transform_from(p=[1, 0, 0]))
fig.add(box)
sphere = pv.Sphere(radius=0.1, center=[0, 1, 0])
fig.add(sphere)
# Show interactive visualization
fig.show()
except ImportError:
print("Open3D not available - install with 'pip install open3d'")import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from pytransform3d.plot_utils import make_3d_axis, plot_transform
import pytransform3d.transformations as pt
import pytransform3d.rotations as pr
# Set up figure
fig = plt.figure()
ax = make_3d_axis(ax_s=2)
# Animation parameters
n_frames = 100
t_values = np.linspace(0, 4*np.pi, n_frames)
def animate(frame):
ax.clear()
ax = make_3d_axis(ax_s=2)
# Create rotating transformation
angle = t_values[frame]
R = pr.matrix_from_euler([0, 0, angle], "xyz", extrinsic=True)
p = [np.cos(angle), np.sin(angle), 0.1*angle]
T = pt.transform_from(R=R, p=p)
# Plot reference frame and animated frame
plot_transform(ax=ax, A2B=np.eye(4), s=0.2, alpha=0.5)
plot_transform(ax=ax, A2B=T, s=0.3)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
ax.set_title(f'Frame: {frame}, Angle: {angle:.2f}')
# Create animation
anim = animation.FuncAnimation(fig, animate, frames=n_frames, interval=50)
plt.show()
# Uncomment to save animation
# anim.save('rotating_frame.gif', writer='pillow', fps=20)Install with Tessl CLI
npx tessl i tessl/pypi-pytransform3d