Visualization

The moro.visualization package provides tools for plotting and animating serial robot manipulators.

Visualization tools for serial robots.

class moro.visualization.DHSegmentData(row_index: int, kind: Literal['d', 'a'], start: ndarray, end: ndarray)[source]

Bases: object

One numerical DH translation contribution, in base coordinates.

row_index is 1-based. kind is “d” or “a”. The endpoints are independent, finite real arrays with shape (3,) when produced by evaluate_robot(). These segments describe DH geometry, not the physical shape of a robot link.

end: ndarray
kind: Literal['d', 'a']
row_index: int
start: ndarray
class moro.visualization.FrameData(T: ndarray)[source]

Bases: object

Numerical homogeneous transformation of one robot frame.

T: ndarray
property position
property rotation
property x
property y
property z
class moro.visualization.JointData(index: int, kind: Literal['r', 'p'], origin: ndarray, axis: ndarray)[source]

Bases: object

Kinematic metadata for a single classic-DH joint.

The index is the 1-based DH row/joint index; kind is “r” or “p”. Origin is the previous frame origin p_(i-1), and axis is its positive z direction z_(i-1), both expressed in world/base coordinates. Together they define the motion axis, not physical actuator geometry.

Origin and axis are independent finite float arrays of shape (3,). Any nonzero finite axis is normalized to unit length on construction.

axis: ndarray
index: int
kind: Literal['r', 'p']
origin: ndarray
class moro.visualization.MatplotlibBackend[source]

Bases: object

Render robot kinematic diagrams using Matplotlib 3D.

static animate(scene_data_list: list[SceneData], interval=100, figsize=(10, 8), style: VisualizationStyle | None = None, layout: VisualizationLayout | None = None, framing: SceneFraming | None = None, **kwargs)[source]

Create a Matplotlib animation.

Notes

Keep a reference to the returned FuncAnimation until it has been displayed or saved; otherwise it may be garbage-collected prematurely.

static render(scene_data: SceneData, ax=None, figsize=(10, 8), view_init=(30, 30), style: VisualizationStyle | None = None, layout: VisualizationLayout | None = None, framing: SceneFraming | None = None)[source]

Render one robot configuration on a Matplotlib 3D axis.

class moro.visualization.RobotVisualizer(robot: Robot)[source]

Bases: object

Render a moro.core.Robot using the available backends.

animate(num_vals_list, backend='threejs', **kwargs)[source]

Animate joint configurations; return HTML by default.

backend=’matplotlib’ explicitly returns FuncAnimation instead.

plot(num_vals, backend='threejs', **kwargs)[source]

Render one configuration; Three.js is the default backend.

Returns IPython.display.HTML for Three.js, or (Figure, Axes3D) when backend=’matplotlib’ is requested explicitly.

class moro.visualization.SceneBounds(minimum: tuple[float, float, float], maximum: tuple[float, float, float])[source]

Bases: object

Finite axis-aligned bounds in the robot’s world-coordinate units.

property center: tuple[float, float, float]

Center of the box, avoiding overflow from direct endpoint addition.

maximum: tuple[float, float, float]
minimum: tuple[float, float, float]
property size: tuple[float, float, float]

Nonnegative extents along world X, Y and Z.

class moro.visualization.SceneData(joints: list[~numpy.ndarray], frames: list[~moro.visualization.data.FrameData], dimension: float, dh_segments: list[~moro.visualization.data.DHSegmentData] = <factory>, joint_data: list[~moro.visualization.data.JointData] = <factory>)[source]

Bases: object

Evaluated numerical data for one robot configuration.

dh_segments lists two contributions per DH row in d, a order. joint_data describes each motion axis in joint index order. Both fields are optional so legacy positional scene construction remains valid. The historic joints positions still denote frame origins.

property characteristic_length: float | None

Sum of the evaluated absolute DH translation lengths.

Returns None when geometric segments are unavailable (e.g. a legacy manually constructed scene). Returns 0.0 for explicitly evaluated but degenerate DH geometry. No absolute unit-dependent minimum is imposed, and the result follows mutations to segments.

dh_segments: list[DHSegmentData]
dimension: float
frames: list[FrameData]
joint_data: list[JointData]
joints: list[ndarray]
class moro.visualization.SceneFraming(bounds: SceneBounds, center: tuple[float, float, float], half_extent: float, fit_radius: float)[source]

Bases: object

World-space framing instructions, independent of projection and viewport.

bounds: SceneBounds
center: tuple[float, float, float]
fit_radius: float
half_extent: float
class moro.visualization.ThreeJSBackend[source]

Bases: object

Render robot kinematic diagrams using Three.js.

static animate(scene_data_list: list[SceneData], width: int = 800, height: int = 600, style: VisualizationStyle | None = None, layout: VisualizationLayout | None = None, framing: SceneFraming | None = None, interval: float = 100)[source]

Return an interactive Three.js animation as IPython.display.HTML.

static render(scene_data: SceneData, width: int = 800, height: int = 600, style: VisualizationStyle | None = None, layout: VisualizationLayout | None = None, framing: SceneFraming | None = None)[source]

Return an interactive Three.js view as IPython.display.HTML.

class moro.visualization.VisualizationLayout(reference_length: float, frame_length: float, joint_radius: float, base_radius: float, link_radius: float)[source]

Bases: object

Resolved world-space sizes shared by rendering backends.

All five lengths use the robot’s input linear units. The nominal link radius is not yet modified by VisualizationStyle.link_linewidth.

base_radius: float
frame_length: float
joint_radius: float
reference_length: float
class moro.visualization.VisualizationStyle(show_frames: bool = True, show_links: bool = True, show_joints: bool = True, show_base: bool = True, show_grid: bool = True, link_color: str = '#778877', joint_color: str = '#ff1493', base_color: str = '#ff00ff', frame_scale: float | None = None, joint_size: float | None = None, base_size: float | None = None, link_linewidth: float = 3, show_trajectory: bool = False, trajectory_color: str = '#1565c0', trajectory_linewidth: float = 2, trajectory_mode: str = 'full', joint_representation: Literal['auto', 'simple', 'kinematic'] = 'auto', joint_scale: float = 1.0, joint_appearance: Literal['schematic', 'solid'] = 'solid', revolute_color: str = '#0072B2', prismatic_color: str = '#D55E00')[source]

Bases: object

Style configuration for robot visualization.

Parameters:
show_framesbool

Draw coordinate frames at each joint.

show_linksbool

Draw links connecting consecutive joints.

show_jointsbool

Draw joint markers.

show_basebool

Highlight the base joint when joints are visible.

show_gridbool

Draw the reference grid.

joint_representationstr

‘auto’ (default) selects backend-appropriate joint markers: R/P solids in Three.js if joint metadata exists, otherwise spheres; Matplotlib uses spheres. ‘simple’ forces spheres; ‘kinematic’ requests R/P geometry explicitly.

joint_scalefloat

Finite positive dimensionless factor for kinematic joint symbols.

joint_appearancestr

‘solid’ (default within kinematic mode, Three.js) or ‘schematic’ (also available in Matplotlib). In ‘auto’ mode, Three.js uses ‘solid’ when full joint metadata is available.

revolute_colorstr

Color of kinematic revolute joint symbols or solid cylinders.

prismatic_colorstr

Color of kinematic prismatic joint symbols or solid prisms.

link_colorstr

Color used for links.

joint_colorstr

Color used for joints.

base_colorstr

Color used for the base joint.

frame_scalefloat or None

Length of frame axes in robot units. If None, use the shared layout.

joint_sizefloat or None

Explicit size: Matplotlib marker area factor (s=size*100 in pt²), or Three.js world-space radius. If None, use the backend adapter.

base_sizefloat or None

Base marker size with the same backend-specific semantics as joint_size. If None, use the backend adapter.

link_linewidthfloat

Link thickness in Matplotlib points; Three.js uses it as a multiplicative factor relative to its nominal link radius (value/3).

show_trajectorybool

Draw end-effector Cartesian trajectory in animations.

trajectory_colorstr

Color used for the trajectory line.

trajectory_linewidthfloat

Trajectory line thickness.

trajectory_modestr

Trajectory display mode: "full" or "trace".

base_color: str
base_size: float | None
frame_scale: float | None
joint_appearance: Literal['schematic', 'solid']
joint_color: str
joint_representation: Literal['auto', 'simple', 'kinematic']
joint_scale: float
joint_size: float | None
prismatic_color: str
revolute_color: str
show_base: bool
show_frames: bool
show_grid: bool
show_joints: bool
show_trajectory: bool
trajectory_color: str
trajectory_linewidth: float
trajectory_mode: str
moro.visualization.compute_scene_bounds(scene: SceneData, *, layout: VisualizationLayout | None = None, style: VisualizationStyle | None = None) → SceneBounds[source]

Bound visible robot primitives in one evaluated configuration.

Includes DH translation segments, or legacy joint-to-joint links when no DH data is present; visible joints and their nominal radii; visible local frame origins and axis tips; and the EE position if a trajectory is shown.

Link radii are conservative world-space estimates using the Three.js linewidth ratio. Explicit Matplotlib marker sizes are in points-squared, so they cannot be interpreted as physical radii here. Global axes and grids do not contribute. A fully hidden or empty scene has a zero-size box at the world origin, expanded visually by resolve_framing.

moro.visualization.evaluate_robot(robot: Robot, num_vals: dict) → SceneData[source]

Evaluate a robot at a numerical configuration.

Parameters:
robotRobot

Serial-link robot instance.

num_valsdict

Mapping from symbolic variables to numerical values.

Returns:
SceneData

Numerical frame origins, frame transformations, ordered DH translation segments and kinematic joint axes in the base frame.

moro.visualization.plot_workspace(workspace, *, projection='auto', ax=None, figsize=(8, 6), marker_size=8, alpha=0.5)[source]

Plot a sampled workspace as a Matplotlib point cloud.

moro.visualization.resolve_framing(scenes: Sequence[SceneData], *, layout: VisualizationLayout | None = None, style: VisualizationStyle | None = None, margin: float = 0.1, min_extent_factor: float = 0.1) → SceneFraming[source]

Resolve one stable view volume around all sampled scenes.

Bounds cover only the supplied configurations, not continuous robot motion between sampled frames. Both half_extent (cube) and fit_radius (enclosing sphere) include a relative margin and a visual-only minimum proportional to the reference length, with no absolute physical floor.

moro.visualization.resolve_layout(scenes: Sequence[SceneData], *, reference_length: float | None = None) → VisualizationLayout[source]

Resolve one stable geometric layout for a scene or animation.

Scale precedence: 1. Explicit finite positive reference_length. 2. Maximum positive DH characteristic length across scenes. 3. Maximum finite positive legacy SceneData.dimension. 4. Visual-only fallback length of 1.0.

SceneData.characteristic_length remains unchanged, including when zero or unavailable. No camera, plot bounds, or backend-specific sizes are calculated here. Resolve once per precomputed animation, not per frame.

moro.visualization.resolve_reference_length(scenes: Iterable[SceneData]) → float | None[source]

Return the largest available characteristic length across scenes.

Only evaluated DH geometry contributes. Legacy scenes without segments are ignored. None means no evaluated geometry was available; 0.0 means that all evaluated geometry is degenerate. The caller must handle the visual fallback separately when the result is zero or unavailable.

The result is suitable for keeping graphical dimensions constant through a precomputed animation sequence.

moro.visualization.union_bounds(bounds: Sequence[SceneBounds]) → SceneBounds[source]

Compute the AABB union of a nonempty sequence of boxes.