Graphical

Time-signal plots: a trajectory’s signals, one row per component, on a chosen backend.

class minilink.graphical.signals.time_signals.SignalTrace(signal, component, label, unit, values, color, alpha=1.0, linewidth=1.5)[source]

One scalar time trace selected from a vector signal.

Parameters:
  • signal (str)

  • component (int)

  • label (str)

  • unit (str)

  • values (ndarray)

  • color (str)

  • alpha (float)

  • linewidth (float)

class minilink.graphical.signals.time_signals.SignalPlotSpec(title, t, traces, abscissa_label='Time [s]')[source]

Backend-neutral signal plot request.

Parameters:
  • title (str)

  • t (ndarray)

  • traces (tuple[SignalTrace, ...])

  • abscissa_label (str)

class minilink.graphical.signals.time_signals.LivePlotHandle[source]

Base class for live signal plot handles.

update(traj_or_spec, *, title=None)[source]

Update an existing plot from a new trajectory or plot spec.

Parameters:

title (str | None)

Return type:

None

close()[source]

Release backend resources held by the live plot.

Return type:

None

minilink.graphical.signals.time_signals.resolve_plot_signals(sys)[source]

Return default time-signal names for sys (leaf or diagram).

Leaf systems plot the state x, and the input u when they have one. Diagrams plot flattened states plus controller control outputs ({id}:u for static or dynamic controllers) and source outputs ({id}:y) without requiring explicit sys_id:port names from the user.

Return type:

tuple[str, …]

minilink.graphical.signals.time_signals.build_signal_plot_spec(sys, traj, *, signals=('x', 'u'), title=None, abscissa_label='Time [s]')[source]

Build a one-component-per-row plot specification.

Parameters:
  • signals (tuple[str, ...])

  • title (str | None)

  • abscissa_label (str)

Return type:

SignalPlotSpec

minilink.graphical.signals.time_signals.plot_time_signals(sys, traj, *, signals=('x', 'u'), abscissa_label='Time [s]', backend='matplotlib', show=True, **kwargs)[source]

Plot sampled time signals with the selected backend.

Parameters:
  • signals (tuple[str, ...])

  • abscissa_label (str)

  • show (bool)

Return type:

PlotResult

minilink.graphical.signals.time_signals.open_time_signal_plot(sys, traj, *, signals=('x', 'u'), abscissa_label='Time [s]', backend='matplotlib', show=True, **kwargs)[source]

Open a live time-signal plot with an update handle.

Parameters:
  • signals (tuple[str, ...])

  • abscissa_label (str)

  • show (bool)

Return type:

LivePlotHandle

Phase plane: a system’s vector field over two state coordinates, with an optional trajectory.

minilink.graphical.phase_plane.phase_plane.plot_phase_plane(sys, traj=None, *, x_axis=0, y_axis=None, u=None, x_ref=None, t=0.0, bounds=None, grid_shape=(21, 21), params=None, title=None, backend='matplotlib', streamplot=False, show=True, **kwargs)[source]

Plot a phase-plane vector field with an optional trajectory overlay.

Parameters:
  • sys (DynamicSystem) – Continuous-time model whose f defines the vector field (leaf subclass or wired diagram with stacked f).

  • x_axis (int)

  • y_axis (int | None)

  • t (float)

  • grid_shape (tuple[int, int])

  • title (str | None)

  • backend (str)

  • streamplot (bool)

  • show (bool)

Return type:

PlotResult

minilink.graphical.phase_plane.phase_plane.build_phase_plane_spec(sys, traj=None, *, x_axis=0, y_axis=None, u=None, x_ref=None, t=0.0, bounds=None, grid_shape=(21, 21), params=None, title=None)[source]

Build a phase-plane vector-field specification.

The selected state coordinates vary over the plot grid. All other state components are held at x_ref and the input is held at u.

Parameters:
  • x_axis (int)

  • y_axis (int | None)

  • t (float)

  • grid_shape (tuple[int, int])

  • title (str | None)

Return type:

PhasePlaneSpec

class minilink.graphical.phase_plane.phase_plane.PhasePlaneSpec(title, X, Y, V, W, x_axis, y_axis, x_label, y_label, x_unit, y_unit, x_bounds, y_bounds, trajectory=None)[source]

Backend-neutral phase-plane plot request.

Parameters:
  • title (str)

  • X (ndarray)

  • Y (ndarray)

  • V (ndarray)

  • W (ndarray)

  • x_axis (int)

  • y_axis (int)

  • x_label (str)

  • y_label (str)

  • x_unit (str)

  • y_unit (str)

  • x_bounds (tuple[float, float])

  • y_bounds (tuple[float, float])

  • trajectory (PhasePlaneTrajectoryOverlay | None)

class minilink.graphical.phase_plane.phase_plane.PhasePlaneTrajectoryOverlay(x, y)[source]

Trajectory data projected onto two phase-plane axes.

Parameters:
  • x (ndarray)

  • y (ndarray)

minilink.graphical.phase_plane.phase_plane.render_phase_plane_matplotlib(spec, *, streamplot=False, show=True, ax=None, figsize=None, dpi=None, vector_color='C0', trajectory_color='C1', vector_kwargs=None, trajectory_kwargs=None, start_kwargs=None, end_kwargs=None, block=None, pause=0.0)[source]

Render a phase-plane specification with matplotlib.

Parameters:
  • spec (PhasePlaneSpec)

  • streamplot (bool)

  • show (bool)

  • vector_color (str)

  • trajectory_color (str)

  • block (bool | None)

  • pause (float)

Return type:

PlotResult

minilink.graphical.phase_plane.phase_plane.render_phase_plane_plotly(spec, *, streamplot=False, show=True)[source]

Render a phase-plane specification with Plotly (quiver or streamlines).

Parameters:
Return type:

PlotResult

Diagram topology: the backend-neutral nodes, ports, edges and clusters a diagram is drawn from.

minilink.graphical.diagrams.topology.build_diagram_topology(sys_or_diagram, *, abstract_boundary=False, expand=False)[source]

Build a display/export topology snapshot from a system or diagram.

abstract_boundary=True removes external Inputs/Outputs routing nodes and records boundary_inputs / boundary_outputs anchors on wired ports. expand=True replaces every nested diagram by its own blocks, at every depth, and records one TopologyCluster per nested diagram.

Return type:

DiagramTopology

minilink.graphical.diagrams.topology.expand_nested_diagrams(topology, diagram)[source]

Replace every nested diagram block by its own blocks, grouped in a cluster.

The blocks of a nested diagram keep their ids behind the diagram’s id (controller__pi). An edge that reached a boundary port of a nested diagram goes straight to the block ports wired to that port inside: one edge per inner reader of a boundary input, none when nothing is wired behind the port. A boundary port left unconnected outside draws nothing.

Parameters:

topology (DiagramTopology)

Return type:

DiagramTopology

minilink.graphical.diagrams.topology.abstract_boundary_ports(topology)[source]

Remove external input/output routing nodes and record boundary port anchors.

Diagram boundary ports that wire straight through input / output nodes are mapped to the connected subsystem port. Internal edges between subsystems are unchanged. Unconnected boundary ports are omitted from the snapshot.

Parameters:

topology (DiagramTopology)

Return type:

DiagramTopology

class minilink.graphical.diagrams.topology.DiagramTopology(name, nodes, edges, boundary_inputs=(), boundary_outputs=(), clusters=())[source]

Read-only topology snapshot for display/export.

nodes and edges are always a complete flat diagram. clusters only groups the nodes that came out of an expanded nested diagram.

Parameters:
class minilink.graphical.diagrams.topology.TopologyNode(id, name, display_id, kind, inputs, outputs)[source]

One display node in a system or diagram topology.

Parameters:
class minilink.graphical.diagrams.topology.TopologyPort(id, dim, label=None)[source]

One input or output port on a topology node; label is the text drawn, the id by default.

Parameters:
  • id (str)

  • dim (int)

  • label (str | None)

property text: str

its label, else its id.

Type:

The text drawn beside the port

class minilink.graphical.diagrams.topology.TopologyEdge(source_node, source_port, target_node, target_port)[source]

Directed port-to-port connection.

Parameters:
  • source_node (str)

  • source_port (str)

  • target_node (str)

  • target_port (str)

class minilink.graphical.diagrams.topology.BoundaryPortRef(diagram_port, node_id, port_id)[source]

One diagram boundary port routed to an internal block port.

Parameters:
  • diagram_port (str)

  • node_id (str)

  • port_id (str)

class minilink.graphical.diagrams.topology.TopologyCluster(id, name, display_id, node_ids, clusters=())[source]

One nested diagram, drawn as a labelled box around its own blocks.

Parameters:
  • id (str)

  • name (str)

  • display_id (str)

  • node_ids (tuple[str, ...])

  • clusters (tuple[TopologyCluster, ...])

Diagram export: a system’s block topology in Graphviz or Mermaid form.

class minilink.graphical.diagrams.export.TopologyExporter[source]

Small topology exporter contract.

export(topology, **kwargs)[source]

Export a topology snapshot.

minilink.graphical.diagrams.export.export_diagram_topology(sys_or_diagram, *, backend='graphviz', expand=True, **kwargs)[source]

Export a system or diagram topology with the selected backend.

expand=True exports the blocks of every nested diagram, grouped in a labelled box; expand=False exports a nested diagram as one block.

Trajectory animation: frame-keyed drawable hooks to a flat draw list, then a renderer.

class minilink.graphical.animation.animator.Animator(sys)[source]

Playback coordinator: frame-keyed hooks → flat draw list → renderer.

Owns the simulated system and delegates drawing to an AnimationRenderer, one of the backends under minilink.graphical.animation.renderers, picked by name through make_renderer(). Each frame resolves the system’s named frame poses (tf()), the cached kinematic geometry (get_kinematic_geometry()) and the per-frame dynamic geometry (get_dynamic_geometry()) into the flat (primitive, world 4x4) draw list the renderers consume, and the camera from the system’s hints. Matplotlib figure size and resolution live in minilink.graphical.common.matplotlib_style.

Live sessions (interactive game() mode) are orchestrated by RealtimeSimulator, which drives open_live_scene() and update_live_frame() per frame; the animator itself stays playback and frame resolution only.

animate_simulation(traj, time_factor_video=1.0, is_3d=False, save=False, file_name='Animation', show=True, html=None, renderer='matplotlib', native=True, scene_title=None, camera=None, overlays=None)[source]

Plays back a full simulation trajectory.

The four orthogonal kwargs are:

  • renderer : graphics tech ("matplotlib", "meshcat", "plotly", "pygame").

  • is_3d : 2-D or 3-D axes for the flat renderers (matplotlib, plotly, pygame). Meshcat is always a 3-D viewer and ignores it.

  • html : output channel. None auto-resolves via minilink.graphical.common.environment.prefers_inline_animation(): True in Colab and in local Jupyter when the active matplotlib backend is non-interactive (inline / agg), False for bare script, IPython REPL, and Jupyter with an interactive backend (qt / widget / macosx / tk / nbagg). Explicit True/False is honored.

  • native : playback engine. True (default) drives the backend’s own animation engine: matplotlib.animation.FuncAnimation for matplotlib, meshcat.animation.Animation + set_animation for meshcat, and browser-side Plotly frames for plotly. False falls back to the per-frame Python loop (handy for debugging or when the native path’s limitations matter).

camera accepts the optional override: a constant 4x4 or a camera(frames, x, u, t) callable.

Notes

Meshcat native animation keyframes rigid poses (position+quaternion), and plays geometry that changes (Arrow length/direction, TorqueArrow sweep, CustomLine trails / horizons) as a flipbook of pages, each shown over its own frames. In 3-D, Meshcat draws line primitives as thin ribbons so matplotlib dash styles and widths remain visible.

Plotly does not support the per-frame Python loop (native=False with html=False): use native=True or html=True for inline/browser playback.

save=True writes a file before playback: matplotlib a GIF, meshcat a standalone HTML page of the native keyframed animation.

Parameters:
  • html (bool | None)

  • native (bool)

  • scene_title (str | None)

show(x, u, t=0.0, is_3d=False, renderer='matplotlib', camera=None, overlays=None)[source]

Render a single static frame at state x, u, t.

open_live_scene(backend, x, u, t, *, is_3d=False, kinematic=None, title=None)[source]

Open backend and draw the initial live frame; return the frame dict.

Pacing (frame-rate sleep) belongs to the live orchestrator, so the frame is presented non-blocking with no interval.

update_live_frame(backend, x, u, t, *, kinematic=None)[source]

Draw one live frame on an already-open backend; return backend events.

Dynamic geometry (force/torque arrows) is rebuilt from the frame-keyed hooks each call, so live sessions render the same visuals as playback.

class minilink.graphical.animation.animator.AnimationRenderer(animator)[source]

One backend (matplotlib, meshcat, pygame, …) for static views and trajectory playback. Subclasses own all library-specific setup and I/O.

abstract open_scene(*, is_3d, show, camera, title=None)[source]

Initialize backend resources for one render session.

camera is a 4x4 camera_matrix() used to set up initial framing. Renderers consume the slots they understand (target, projection-axis columns, view scale, view-out direction) and ignore the rest; see DESIGN.md §4.7.

Parameters:
  • is_3d (bool)

  • show (bool)

  • title (str | None)

Return type:

None

abstract draw_frame(primitives, transforms, t, camera)[source]

Draw one frame from precomputed transforms and camera.

Parameters:

t (float)

Return type:

None

abstract present(*, block, interval_s=None)[source]

Present the currently drawn frame and optionally block/sleep.

Parameters:
  • block (bool)

  • interval_s (float | None)

Return type:

None

poll_events()[source]

Return backend events/state for interactive loops.

Live external input and output live in minilink.simulation.realtime (RealtimeInput / RealtimeOutput); renderers stay draw/present-only and report only view events such as {"quit": True}.

abstract close_scene()[source]

Release/close backend resources for the current session.

Return type:

None

render_inline_animation(primitives, frames, schedule, *, is_3d)[source]

Optional notebook inline output (default: unsupported).

frames entries each carry "transforms" and "camera" (see minilink.graphical.animation.Animator._prepare_transforms()).

Parameters:

is_3d (bool)

export_animation(primitives, frames, schedule, file_name, *, is_3d=False)[source]

Optional animation export (default: unsupported).

Parameters:
  • file_name (str)

  • is_3d (bool)

Return type:

None

play_native(primitives, frames, schedule, *, is_3d, scene_title=None)[source]

Optional native playback using the backend’s own animation engine.

Subclasses that support a native animation API (matplotlib FuncAnimation, meshcat Animation + set_animation, …) override this hook. Default raises NotImplementedError so callers can fall back to the generic per-frame loop.

Parameters:
  • is_3d (bool)

  • scene_title (str | None)

class minilink.graphical.animation.animator.MatplotlibRenderer(animator)[source]

Static matplotlib figure and GIF / notebook HTML animation.

open_scene(*, is_3d, show, camera, title=None)[source]

Initialize backend resources for one render session.

camera is a 4x4 camera_matrix() used to set up initial framing. Renderers consume the slots they understand (target, projection-axis columns, view scale, view-out direction) and ignore the rest; see DESIGN.md §4.7.

Parameters:
  • is_3d (bool)

  • show (bool)

  • title (str | None)

Return type:

None

draw_frame(primitives, transforms, t, camera)[source]

Draw one frame from precomputed transforms and camera.

Parameters:

t (float)

Return type:

None

present(*, block, interval_s=None)[source]

Present the currently drawn frame and optionally block/sleep.

Parameters:
  • block (bool)

  • interval_s (float | None)

Return type:

None

poll_events()[source]

Return backend events/state for interactive loops.

Live external input and output live in minilink.simulation.realtime (RealtimeInput / RealtimeOutput); renderers stay draw/present-only and report only view events such as {"quit": True}.

Return type:

dict[str, bool]

close_scene()[source]

Release/close backend resources for the current session.

Return type:

None

render_inline_animation(primitives, frames, schedule, *, is_3d)[source]

Optional notebook inline output (default: unsupported).

frames entries each carry "transforms" and "camera" (see minilink.graphical.animation.Animator._prepare_transforms()).

Parameters:

is_3d (bool)

export_animation(primitives, frames, schedule, file_name, *, is_3d=False)[source]

Optional animation export (default: unsupported).

Parameters:
  • file_name (str)

  • is_3d (bool)

Return type:

None

play_native(primitives, frames, schedule, *, is_3d, scene_title=None)[source]

Drive playback through matplotlib.animation.FuncAnimation instead of a Python frame loop.

Only the window path lands here: Colab and Jupyter-with-non-interactive backend are routed to render_inline_animation upstream via minilink.graphical.common.environment.prefers_inline_animation().

  • Bare script (is_blocking_needed() is True): block until the user closes the window, then clean up.

  • IPython terminal REPL or Jupyter with an interactive backend (qt / widget / macosx / tk / nbagg): show the window non-blocking and intentionally retain strong references to the figure and FuncAnimation on self so the backend event loop can drive playback without FuncAnimation being garbage collected.

Parameters:
  • is_3d (bool)

  • scene_title (str | None)

class minilink.graphical.animation.animator.MeshcatRenderer(animator)[source]

Browser-based playback and static-HTML snapshots.

The viewer is always 3-D, so the is_3d argument of the renderer interface is accepted and ignored: lines are always drawn as ribbons with a visible width and dash pattern.

open_scene(*, is_3d, show, camera, title=None)[source]

Initialize backend resources for one render session.

camera is a 4x4 camera_matrix() used to set up initial framing. Renderers consume the slots they understand (target, projection-axis columns, view scale, view-out direction) and ignore the rest; see DESIGN.md §4.7.

Parameters:
  • is_3d (bool)

  • show (bool)

  • title (str | None)

Return type:

None

draw_frame(primitives, transforms, t, camera)[source]

Draw one frame from precomputed transforms and camera.

Parameters:

t (float)

Return type:

None

present(*, block, interval_s=None)[source]

Present the currently drawn frame and optionally block/sleep.

Parameters:
  • block (bool)

  • interval_s (float | None)

Return type:

None

close_scene()[source]

Release/close backend resources for the current session.

Return type:

None

export_animation(primitives, frames, schedule, file_name, *, is_3d=False)[source]

Write a standalone HTML page of the native Meshcat animation.

Parameters:
  • file_name (str)

  • is_3d (bool)

Return type:

None

play_native(primitives, frames, schedule, *, is_3d, scene_title=None)[source]

Drive playback through meshcat.animation.Animation + Visualizer.set_animation instead of a Python frame loop. The browser plays keyframes natively; no time.sleep in Python.

Parameters:
  • is_3d (bool)

  • scene_title (str | None)

render_inline_animation(primitives, frames, schedule, *, is_3d)[source]

Return an IPython.display.HTML iframe snapshot of the meshcat scene with the animation embedded. Uses Visualizer.render_static under the hood, which wraps static_html() in a self-contained srcdoc=. Ideal for Colab: no zmq client, no port forwarding.

Parameters:

is_3d (bool)

class minilink.graphical.animation.animator.PlotlyRenderer(animator)[source]

Notebook-friendly Plotly renderer.

This backend is intended for static figures and precomputed trajectory playback. It deliberately does not implement a high-frequency live update loop for game() or trajectory-optimization iteration callbacks.

open_scene(*, is_3d, show, camera, title=None)[source]

Initialize backend resources for one render session.

camera is a 4x4 camera_matrix() used to set up initial framing. Renderers consume the slots they understand (target, projection-axis columns, view scale, view-out direction) and ignore the rest; see DESIGN.md §4.7.

Parameters:
  • is_3d (bool)

  • show (bool)

  • title (str | None)

Return type:

None

draw_frame(primitives, transforms, t, camera)[source]

Draw one frame from precomputed transforms and camera.

Parameters:

t (float)

Return type:

None

present(*, block, interval_s=None)[source]

Present the currently drawn frame and optionally block/sleep.

Parameters:
  • block (bool)

  • interval_s (float | None)

poll_events()[source]

Return backend events/state for interactive loops.

Live external input and output live in minilink.simulation.realtime (RealtimeInput / RealtimeOutput); renderers stay draw/present-only and report only view events such as {"quit": True}.

close_scene()[source]

Release/close backend resources for the current session.

Return type:

None

render_inline_animation(primitives, frames, schedule, *, is_3d)[source]

Optional notebook inline output (default: unsupported).

frames entries each carry "transforms" and "camera" (see minilink.graphical.animation.Animator._prepare_transforms()).

Parameters:

is_3d (bool)

play_native(primitives, frames, schedule, *, is_3d, scene_title=None)[source]

Optional native playback using the backend’s own animation engine.

Subclasses that support a native animation API (matplotlib FuncAnimation, meshcat Animation + set_animation, …) override this hook. Default raises NotImplementedError so callers can fall back to the generic per-frame loop.

Parameters:
  • is_3d (bool)

  • scene_title (str | None)

class minilink.graphical.animation.animator.PygameRenderer(animator)[source]

800×600 window; ESC or close to quit.

open_scene(*, is_3d, show, camera, title=None)[source]

Initialize backend resources for one render session.

camera is a 4x4 camera_matrix() used to set up initial framing. Renderers consume the slots they understand (target, projection-axis columns, view scale, view-out direction) and ignore the rest; see DESIGN.md §4.7.

Parameters:
  • is_3d (bool)

  • show (bool)

  • title (str | None)

Return type:

None

draw_frame(primitives, transforms, t, camera)[source]

Draw one frame from precomputed transforms and camera.

Parameters:

t (float)

Return type:

None

present(*, block, interval_s=None)[source]

Present the currently drawn frame and optionally block/sleep.

Parameters:
  • block (bool)

  • interval_s (float | None)

Return type:

None

poll_events()[source]

Return backend events/state for interactive loops.

Live external input and output live in minilink.simulation.realtime (RealtimeInput / RealtimeOutput); renderers stay draw/present-only and report only view events such as {"quit": True}.

Return type:

dict[str, bool]

close_scene()[source]

Release/close backend resources for the current session.

Return type:

None

minilink.graphical.animation.animator.make_renderer(name, animator)[source]

Return a backend instance for name.

To add a backend, implement AnimationRenderer and extend this function.

Parameters:
Return type:

AnimationRenderer

Graphical primitives: shapes in their own local frame, and the 4x4 camera transforms.

class minilink.graphical.animation.primitives.GraphicPrimitive(color='blue', linewidth=1, style='-', local_transform=None)[source]

Base class for all geometric objects rendered by the animator engine.

A system’s visualization is a dict of named frames to primitives (shapes defined in their own local frame) plus, at every instant, one 4x4 homogeneous transform per frame placing it in the world (see minilink.core.system.System.get_kinematic_geometry(), get_dynamic_geometry(), and tf()). Renderers draw the primitives; they never know about states or inputs.

Primitives are honest: they bake their full point geometry at real world size in their own local frame. local_transform is a fixed 4x4 graphical offset baked into the primitive (a constant pose within its frame), so the animator poses a primitive at frames[key] @ local_transform; it defaults to identity. Placement helpers for spans and coils live in minilink.graphical.catalog.shapes; rigid pose math lives in minilink.core.kinematics.

class minilink.graphical.animation.primitives.CustomLine(pts, color='blue', linewidth=1, style='-', opacity=1.0)[source]

A generic sequence of connected line segments.

points_at(t)[source]

Nx3 polyline points at playback time t (static: time-independent).

class minilink.graphical.animation.primitives.Point(pt=(0, 0, 0), color='red', marker='o', size=5)[source]

An individual point marker.

class minilink.graphical.animation.primitives.Circle(radius=1.0, center=(0, 0, 0), color='blue', fill=False)[source]

A basic circle primitive. Lives in the XY plane by default.

class minilink.graphical.animation.primitives.Sphere(radius=1.0, center=(0, 0, 0), color='blue', opacity=1.0)[source]

A 3D sphere primitive centered at center in local frame.

class minilink.graphical.animation.primitives.Rod(length=1.0, radius=0.05, color='blue', opacity=1.0, linewidth=2, style='-')[source]

A slender rigid rod primitive aligned with local -Y axis.

class minilink.graphical.animation.primitives.Plane(normal=(0, 1, 0), offset=0.0, size=10.0, thickness=0.02, color='lightgray', opacity=0.65)[source]

A finite square patch representing a plane: n·x = offset.

The patch is centered at offset * normal and spans size x size.

class minilink.graphical.animation.primitives.Box(length_x=1.0, length_y=1.0, length_z=1.0, center=(0.0, 0.0, 0.0), color='gray', opacity=1.0)[source]

Axis-aligned rectangular solid in local frame (centered at center).

Dimensions are full lengths along local X, Y, Z. Used for simple vehicle bodies and blocks in 3D renderers (MeshCat box geometry).

Parameters:
  • length_x (float)

  • length_y (float)

  • length_z (float)

  • opacity (float)

class minilink.graphical.animation.primitives.ExtrudedPolygon(pts_xy, height=1.0, center=(0.0, 0.0, 0.0), color='gray', opacity=1.0)[source]

Convex polygon in local XY, extruded symmetrically along local Z.

This is useful for light-weight 3D body shells such as vehicle noses, cabins, side pods, and tapered covers without introducing a full mesh asset pipeline.

Parameters:
  • height (float)

  • opacity (float)

vertices_local()[source]

Return local vertices with shape (2*n, 3).

Return type:

ndarray

edges()[source]

Return wireframe edges as vertex-index pairs.

Return type:

tuple[tuple[int, int], …]

mesh_data()[source]

Return (vertices, faces) for triangle-mesh renderers.

Return type:

tuple[ndarray, ndarray]

class minilink.graphical.animation.primitives.Arrow(base=(0.0, 0.0), vector=(1.0, 0.0), scale=1.0, head_ratio=0.15, color='red', linewidth=2, style='-')[source]

A straight arrow at honest world size — shaft from base along vector.

Accepts planar (length-2) or spatial (length-3) base / vector. Geometry is baked at true length scale * |vector|; the renderer simply draws self.pts posed by the frame. Pass body-frame vector and key the primitive to that body frame so its orientation comes from tf (no manual cos/sin rotation). For world-aligned task forces, key to a translation-only frame and pass the force components directly as vector.

points_at(t)[source]

Nx3 arrow points at playback time t (geometry baked at construction).

class minilink.graphical.animation.primitives.TorqueArrow(sweep, radius=1.0, head_ratio=0.4, n_arc_pts=40, color='red', linewidth=2, style='-')[source]

A curved torque arc at honest geometry — sweep baked into the points.

The arc for the given sweep and radius is built at construction, centered at the local origin; the placing frame supplies world position and the start orientation. Rebuild it each frame in get_dynamic_geometry with the instantaneous torque.

Parameters:
  • sweep (float) – Arc sweep angle in radians (positive = CCW, negative = CW).

  • radius (float) – Radius of the arc in world units.

  • head_ratio (float) – Chevron barb length as a fraction of the radius.

  • n_arc_pts (int) – Number of sample points used for a full-circle arc (subsampled proportionally for smaller sweeps).

points_at(t)[source]

Nx3 arc points at playback time t (geometry baked at construction).

class minilink.graphical.animation.primitives.HorizonPolyline(plans, *, color='tab:orange', linewidth=2, style='--')[source]

World-frame polyline of the active receding-horizon plan at time t.

plans is a sequence of (t_solve, trajectory) pairs with world-frame trajectory.x and trajectory.t. At playback time t, the primitive draws the latest plan with t_solve <= t over samples with trajectory.t >= t.

Geometry is rebuilt each frame through points_at() (an overlay’s get_dynamic_geometry(t) bakes the world polyline into a CustomLine).

compute_pts(t_now)[source]

Return Nx3 world-frame polyline points for the active plan tail.

points_at(t)[source]

Nx3 world-frame points of the active plan tail at playback time t.

class minilink.graphical.animation.primitives.TrajectoryPolyline(trajectory, *, window='prefix', color='tab:blue', linewidth=2, style='-')[source]

World-frame XY polyline sampled from a Trajectory.

At playback time t:

window="prefix"

Samples with trajectory.t <= t — a growing executed trail.

window="suffix"

Samples with trajectory.t >= t.

window="all"

Full trajectory polyline (time-independent geometry).

Geometry is rebuilt each frame through compute_pts(), like HorizonPolyline.

compute_pts(t_now)[source]

Return Nx3 world-frame polyline points for the selected time window.

points_at(t)[source]

Nx3 world-frame points of the selected time window at playback time t.

minilink.graphical.animation.primitives.arrow_pts(base, vector, scale=1.0, head_ratio=0.15)[source]

Polyline (Nx3) of a straight arrow from base along vector.

Accepts matching length-2 or length-3 base / vector. Planar arrows stay in local XY (z = 0); spatial arrows build the chevron in a plane spanned by the shaft and a deterministic perpendicular. Drawn length is scale * |vector|. A near-zero vector collapses to a single point.

Parameters:
  • base (array-like, length 2 or 3) – Tail of the arrow in the local frame.

  • vector (array-like, length 2 or 3) – Direction (and, with scale, length) of the shaft.

  • scale (float) – Multiplies vector to set the drawn length (e.g. a force gain).

  • head_ratio (float) – Chevron barb length as a fraction of the shaft length.

minilink.graphical.animation.primitives.torque_arc_pts(sweep, radius=1.0, head_ratio=0.4, n_arc_pts=40)[source]

Polyline (Nx3) of a circular torque arc + chevron, centered at the origin.

The arc is baked at the given sweep (radians, + = CCW) and radius, so the sweep rides in the geometry rather than a transform side-channel.

minilink.graphical.animation.primitives.camera_matrix(target=(0.0, 0.0, 0.0), plot_axes=(0, 1), scale=10.0)[source]

Standard 4x4 camera transform.

The matrix doubles as the camera’s pose in the world frame and the renderer projection knob. The T[3, 3] slot carries the view scale (orthographic half-extent for matplotlib / pygame; camera distance for meshcat).

Slot meaning

  • T[:3, 3] — look-at target in world (point at the center of the view).

  • T[:3, 0] — world direction shown as plot horizontal axis.

  • T[:3, 1] — world direction shown as plot vertical axis.

  • T[:3, 2] — camera view-out direction (projected away in 2D / ortho; eye-out in 3D perspective).

  • T[3, 3] — view scale (orthographic half-extent in world units for matplotlib / pygame; perspective camera distance for meshcat).

param target:

Look-at point in world coordinates.

type target:

array-like of length 3, optional

param plot_axes:

World axis indices used as plot-X (i) and plot-Y (j). R is built so its columns are (e_i, e_j, e_i x e_j). Default (0, 1) is the canonical top-down view.

type plot_axes:

tuple of two ints in {0, 1, 2}, optional

param scale:

View half-extent (orthographic) or camera distance (perspective).

type scale:

float, optional

returns:

4x4 camera transform.

rtype:

np.ndarray

minilink.graphical.animation.primitives.world_to_camera(camera)[source]

Return the world-to-camera (view) 4x4 matrix.

Inverts the rigid part of camera (target translation + R); the amplitude channel T[3, 3] is reset to 1 so the result is a regular rigid transform suitable for pre-multiplying body transforms before orthographic 2D rendering.

minilink.graphical.animation.primitives.ground_line(length=20.0, y=0.0, color='black', style='--')[source]

Horizontal reference line of span length at height y (e.g. ground).

minilink.graphical.animation.primitives.spring_line(coils=6, amplitude=0.12, color='black', linewidth=1)[source]

Unit-length zig-zag spring along local +X (lead-in, coils coils, lead-out).

Drawn from x=0 to x=1; pair with a transform that spans the two endpoints.

minilink.graphical.animation.primitives.wheel_box(length=0.45, width=0.16)[source]

Small flat box used as a wheel/contact patch in vehicle diagrams.

minilink.graphical.animation.primitives.vehicle_body(length=1.0, width=0.5, color='blue', opacity=0.85)[source]

Planar car/robot shell (arrow-shaped outline pointing along +X).

minilink.graphical.animation.primitives.bounding_points(primitive)[source]

Local points that bound the primitive, plus a radial pad, or None when excluded.

Returns (pts, pad): an (N, 3) array of local-frame points and a scalar margin to add around them (a rod’s radius, a sphere’s radius). The auto-fit camera transforms the points into the world and pads the box. Backdrops and force glyphs (primitive.camera_fit is False) return None; unknown primitives count as their origin only.

minilink.graphical.animation.primitives.bounding_radius(primitive)[source]

Radius of a ball about the local origin containing the primitive (None when excluded).

Abstract animation backend.

class minilink.graphical.animation.renderers.renderer.AnimationRenderer(animator)[source]

One backend (matplotlib, meshcat, pygame, …) for static views and trajectory playback. Subclasses own all library-specific setup and I/O.

abstract open_scene(*, is_3d, show, camera, title=None)[source]

Initialize backend resources for one render session.

camera is a 4x4 camera_matrix() used to set up initial framing. Renderers consume the slots they understand (target, projection-axis columns, view scale, view-out direction) and ignore the rest; see DESIGN.md §4.7.

Parameters:
  • is_3d (bool)

  • show (bool)

  • title (str | None)

Return type:

None

abstract draw_frame(primitives, transforms, t, camera)[source]

Draw one frame from precomputed transforms and camera.

Parameters:

t (float)

Return type:

None

abstract present(*, block, interval_s=None)[source]

Present the currently drawn frame and optionally block/sleep.

Parameters:
  • block (bool)

  • interval_s (float | None)

Return type:

None

poll_events()[source]

Return backend events/state for interactive loops.

Live external input and output live in minilink.simulation.realtime (RealtimeInput / RealtimeOutput); renderers stay draw/present-only and report only view events such as {"quit": True}.

abstract close_scene()[source]

Release/close backend resources for the current session.

Return type:

None

render_inline_animation(primitives, frames, schedule, *, is_3d)[source]

Optional notebook inline output (default: unsupported).

frames entries each carry "transforms" and "camera" (see minilink.graphical.animation.Animator._prepare_transforms()).

Parameters:

is_3d (bool)

export_animation(primitives, frames, schedule, file_name, *, is_3d=False)[source]

Optional animation export (default: unsupported).

Parameters:
  • file_name (str)

  • is_3d (bool)

Return type:

None

play_native(primitives, frames, schedule, *, is_3d, scene_title=None)[source]

Optional native playback using the backend’s own animation engine.

Subclasses that support a native animation API (matplotlib FuncAnimation, meshcat Animation + set_animation, …) override this hook. Default raises NotImplementedError so callers can fall back to the generic per-frame loop.

Parameters:
  • is_3d (bool)

  • scene_title (str | None)

Port maps: a block’s output over swept input components, and a controller’s control law.

minilink.graphical.port_map.plot_input_output_map(block, *, in_port=None, out_port=None, x_axis=0, y_axis=None, out_axis=0, bounds=None, grid_shape=(101, 101), show_3d=False, params=None, t=0.0, title=None, cmap='bwr', vmin=None, vmax=None, ax=None, show=True)[source]

Plot one output component of block over swept input components.

Sweeps one component of an input port (a line) or two (a heatmap, or a 3-D surface with show_3d=True) through the output port’s ordinary compute: no special block API is required.

Parameters:
  • block (System) – Any block with input and output ports; evaluated through the output port’s compute at the block’s nominal state and pinned inputs.

  • in_port (str, optional) – Port ids; default first input port and first output port.

  • out_port (str, optional) – Port ids; default first input port and first output port.

  • x_axis (int, optional) – Input-port components to sweep. y_axis=None draws a 1-D line.

  • y_axis (int, optional) – Input-port components to sweep. y_axis=None draws a 1-D line.

  • out_axis (int, optional) – Output component to plot (vector outputs).

  • bounds (optional) – Sweep ranges: (lo, hi) for a line, ((xlo, xhi), (ylo, yhi)) for a surface. Defaults to the port bounds when finite, else ±10.

  • show_3d (bool, optional) – True draws a 3-D surface instead of a heatmap.

  • params (dict, optional) – Alternative parameter set passed through to the port compute.

  • ax (matplotlib axes, optional) – Draw into an existing subplot (side-by-side comparisons).

Return type:

PlotResult

minilink.graphical.port_map.plot_control_law(block, *, x_axis=None, y_axis=None, u_axis=0, r=None, space=None, bounds=None, grid_shape=(101, 101), show_3d=False, params=None, t=0.0, title=None, cmap='bwr', vmin=None, vmax=None, ax=None, show=True)[source]

Plot the textbook slice of a controller block’s control law.

The block’s port names say what it reads (minilink.core.feedback.block_roles()): the measurement y or x, or the error entry of a compensator. The zero-argument call picks the sweep space (minilink.core.feedback.plot_space_of(): error space for error-driven laws, absolute measurement/state otherwise), sweeps the first relevant coordinates, and pins the reference and every other coordinate at its nominal value. Blocks with internal state are evaluated at x0 (the instantaneous law). Like plot_input_output_map(), it evaluates the block through its output port’s compute.

Parameters:
  • block (System) – A block that reads a measurement (y, x or an error entry) and commands u.

  • x_axis (int, optional) – Workspace components to sweep, in z = [measurement-space; x_ctrl]. Components 0 .. p-1 are the measurement-space coordinates; p .. index the block’s internal state (dynamic controllers), for slices such as the error against the integral state. y_axis=None on a scalar measurement draws a line.

  • y_axis (int, optional) – Workspace components to sweep, in z = [measurement-space; x_ctrl]. Components 0 .. p-1 are the measurement-space coordinates; p .. index the block’s internal state (dynamic controllers), for slices such as the error against the integral state. y_axis=None on a scalar measurement draws a line.

  • u_axis (int, optional) – Control component to draw (MIMO commands).

  • r (array, optional) – Pin the reference at this value; default the ref-port nominal.

  • space (str, optional) – Override the sweep space: "error", "error_qdq", "measurement", or "state"; default from the ports or the block’s plot_space.

  • bounds (optional) – Sweep ranges, as in plot_input_output_map().

  • show_3d (bool, optional) – True draws a 3-D surface instead of a heatmap.

  • params (dict, optional) – Alternative parameter set (gain sweeps, trained vs. untrained).

Return type:

PlotResult

class minilink.graphical.port_map.SweepAxis(target, index, label, unit, bounds, base=0.0, sign=1.0)[source]

One swept coordinate mapped affinely into the block evaluation inputs.

The swept value v enters the flat input bundle (target="u") or the block state (target="x") at index as base + sign * v. Absolute coordinates use sign=+1; error coordinates use sign=-1 with the reference component as base (y = r - e).

Parameters:
  • target (str)

  • index (int)

  • label (str)

  • unit (str)

  • bounds (tuple[float, float])

  • base (float)

  • sign (float)

class minilink.graphical.port_map.PortMapSpec(title, x_values, y_values, U, x_label, x_unit, y_label, y_unit, out_label, out_unit, vmin, vmax)[source]

Backend-neutral input-output map plot request.

Parameters:
  • title (str)

  • x_values (ndarray)

  • y_values (ndarray | None)

  • U (ndarray)

  • x_label (str)

  • x_unit (str)

  • y_label (str | None)

  • y_unit (str | None)

  • out_label (str)

  • out_unit (str)

  • vmin (float | None)

  • vmax (float | None)

minilink.graphical.port_map.render_port_map_matplotlib(spec, *, show_3d=False, cmap='bwr', ax=None, show=True, figsize=None, dpi=None, block=None, pause=0.0)[source]

Render a port-map specification with matplotlib.

Parameters:
  • spec (PortMapSpec)

  • show_3d (bool)

  • cmap (str)

  • show (bool)

  • block (bool | None)

  • pause (float)

Return type:

PlotResult

Control-analysis figures: one spec, rendered with matplotlib or plotly.

class minilink.graphical.control.ControlFigure(title, panels, subtitle='', share_x=False, extra=<factory>)[source]

A titled stack of panels; subtitle names the channel (From / To).

One or two panels of line traces, markers, dashed reference lines and text notes: enough for Bode, pole-zero, root locus, Nyquist and step-response plots. matplotlib_backend and plotly_backend render the same spec.

Parameters:
  • title (str)

  • panels (tuple[Panel, ...])

  • subtitle (str)

  • share_x (bool)

  • extra (dict)

class minilink.graphical.control.Note(text, x=0.02, y=0.05)[source]

A text box anchored at a fraction of the panel (0..1).

Parameters:
  • text (str)

  • x (float)

  • y (float)

class minilink.graphical.control.Panel(traces, x_label, y_label, lines=(), notes=(), x_log=False, zero_lines=False, x_lim=None, y_lim=None, y_ticks=None)[source]

One axes: traces, references, notes, and how the axes are drawn.

Parameters:
  • traces (tuple[Trace, ...])

  • x_label (str)

  • y_label (str)

  • lines (tuple[RefLine, ...])

  • notes (tuple[Note, ...])

  • x_log (bool)

  • zero_lines (bool)

  • x_lim (tuple[float, float] | None)

  • y_lim (tuple[float, float] | None)

  • y_ticks (tuple[float, ...] | None)

class minilink.graphical.control.RefLine(axis, value, color='#7f7f7f', dash='dash')[source]

A dashed reference line across the panel: axis is "x" or "y".

Parameters:
  • axis (str)

  • value (float)

  • color (str)

  • dash (str)

class minilink.graphical.control.Trace(x, y, color, mode='lines', marker='x', size=8.0, width=1.5, dash='solid', label='', hover=(), arrows=False)[source]

A line or a marker set; hover holds one text per point for plotly.

Parameters:
  • x (ndarray)

  • y (ndarray)

  • color (str)

  • mode (str)

  • marker (str)

  • size (float)

  • width (float)

  • dash (str)

  • label (str)

  • hover (tuple[str, ...])

  • arrows (bool)

minilink.graphical.control.render_control_figure(spec, *, backend='matplotlib', show=True)[source]

Render spec with the selected backend and return a PlotResult.

Parameters:

spec (ControlFigure)

Graphical catalog

Shape primitives and the placement helpers that pose them in a frame.

class minilink.graphical.catalog.shapes.Arrow(base=(0.0, 0.0), vector=(1.0, 0.0), scale=1.0, head_ratio=0.15, color='red', linewidth=2, style='-')[source]

A straight arrow at honest world size — shaft from base along vector.

Accepts planar (length-2) or spatial (length-3) base / vector. Geometry is baked at true length scale * |vector|; the renderer simply draws self.pts posed by the frame. Pass body-frame vector and key the primitive to that body frame so its orientation comes from tf (no manual cos/sin rotation). For world-aligned task forces, key to a translation-only frame and pass the force components directly as vector.

points_at(t)[source]

Nx3 arrow points at playback time t (geometry baked at construction).

class minilink.graphical.catalog.shapes.Box(length_x=1.0, length_y=1.0, length_z=1.0, center=(0.0, 0.0, 0.0), color='gray', opacity=1.0)[source]

Axis-aligned rectangular solid in local frame (centered at center).

Dimensions are full lengths along local X, Y, Z. Used for simple vehicle bodies and blocks in 3D renderers (MeshCat box geometry).

Parameters:
  • length_x (float)

  • length_y (float)

  • length_z (float)

  • opacity (float)

class minilink.graphical.catalog.shapes.Circle(radius=1.0, center=(0, 0, 0), color='blue', fill=False)[source]

A basic circle primitive. Lives in the XY plane by default.

class minilink.graphical.catalog.shapes.ExtrudedPolygon(pts_xy, height=1.0, center=(0.0, 0.0, 0.0), color='gray', opacity=1.0)[source]

Convex polygon in local XY, extruded symmetrically along local Z.

This is useful for light-weight 3D body shells such as vehicle noses, cabins, side pods, and tapered covers without introducing a full mesh asset pipeline.

Parameters:
  • height (float)

  • opacity (float)

vertices_local()[source]

Return local vertices with shape (2*n, 3).

Return type:

ndarray

edges()[source]

Return wireframe edges as vertex-index pairs.

Return type:

tuple[tuple[int, int], …]

mesh_data()[source]

Return (vertices, faces) for triangle-mesh renderers.

Return type:

tuple[ndarray, ndarray]

class minilink.graphical.catalog.shapes.HorizonPolyline(plans, *, color='tab:orange', linewidth=2, style='--')[source]

World-frame polyline of the active receding-horizon plan at time t.

plans is a sequence of (t_solve, trajectory) pairs with world-frame trajectory.x and trajectory.t. At playback time t, the primitive draws the latest plan with t_solve <= t over samples with trajectory.t >= t.

Geometry is rebuilt each frame through points_at() (an overlay’s get_dynamic_geometry(t) bakes the world polyline into a CustomLine).

compute_pts(t_now)[source]

Return Nx3 world-frame polyline points for the active plan tail.

points_at(t)[source]

Nx3 world-frame points of the active plan tail at playback time t.

minilink.graphical.catalog.shapes.Line

alias of CustomLine

class minilink.graphical.catalog.shapes.Plane(normal=(0, 1, 0), offset=0.0, size=10.0, thickness=0.02, color='lightgray', opacity=0.65)[source]

A finite square patch representing a plane: n·x = offset.

The patch is centered at offset * normal and spans size x size.

class minilink.graphical.catalog.shapes.Point(pt=(0, 0, 0), color='red', marker='o', size=5)[source]

An individual point marker.

class minilink.graphical.catalog.shapes.Rod(length=1.0, radius=0.05, color='blue', opacity=1.0, linewidth=2, style='-')[source]

A slender rigid rod primitive aligned with local -Y axis.

class minilink.graphical.catalog.shapes.Sphere(radius=1.0, center=(0, 0, 0), color='blue', opacity=1.0)[source]

A 3D sphere primitive centered at center in local frame.

class minilink.graphical.catalog.shapes.TorqueArrow(sweep, radius=1.0, head_ratio=0.4, n_arc_pts=40, color='red', linewidth=2, style='-')[source]

A curved torque arc at honest geometry — sweep baked into the points.

The arc for the given sweep and radius is built at construction, centered at the local origin; the placing frame supplies world position and the start orientation. Rebuild it each frame in get_dynamic_geometry with the instantaneous torque.

Parameters:
  • sweep (float) – Arc sweep angle in radians (positive = CCW, negative = CW).

  • radius (float) – Radius of the arc in world units.

  • head_ratio (float) – Chevron barb length as a fraction of the radius.

  • n_arc_pts (int) – Number of sample points used for a full-circle arc (subsampled proportionally for smaller sweeps).

points_at(t)[source]

Nx3 arc points at playback time t (geometry baked at construction).

class minilink.graphical.catalog.shapes.TrajectoryPolyline(trajectory, *, window='prefix', color='tab:blue', linewidth=2, style='-')[source]

World-frame XY polyline sampled from a Trajectory.

At playback time t:

window="prefix"

Samples with trajectory.t <= t — a growing executed trail.

window="suffix"

Samples with trajectory.t >= t.

window="all"

Full trajectory polyline (time-independent geometry).

Geometry is rebuilt each frame through compute_pts(), like HorizonPolyline.

compute_pts(t_now)[source]

Return Nx3 world-frame polyline points for the selected time window.

points_at(t)[source]

Nx3 world-frame points of the selected time window at playback time t.

minilink.graphical.catalog.shapes.ground_line(length=20.0, y=0.0, color='black', style='--')[source]

Horizontal reference line of span length at height y (e.g. ground).

minilink.graphical.catalog.shapes.spring_line(coils=6, amplitude=0.12, color='black', linewidth=1)[source]

Unit-length zig-zag spring along local +X (lead-in, coils coils, lead-out).

Drawn from x=0 to x=1; pair with a transform that spans the two endpoints.

minilink.graphical.catalog.shapes.spring_between(p0, p1, *, coils=6, amplitude=0.12, color='black', linewidth=1)[source]

World-space spring coil from p0 to p1 (dynamic-tier geometry).

Coil count and lateral amplitude stay fixed in world units; only span along the axis changes with compression/extension.

minilink.graphical.catalog.shapes.line_segment(p0, p1, **style)[source]

Honest world-space line segment (dynamic-tier geometry).

minilink.graphical.catalog.shapes.segment_pose_2d(p0, p1)[source]

Pose a unit 2-D segment primitive to span p0 → p1 (static tf tier).

Used for fixed-length chord lines where endpoints are known each frame. Native-array: NumPy or JAX depending on the input backend.

Rigid pose for a unit Rod (length on primitive) from p0 toward p1.

Native-array: NumPy or JAX depending on the input backend. Near-zero span collapses to a pure translation at p0.

minilink.graphical.catalog.shapes.point_pose(p)[source]

Pure translation placing a primitive at p.

Native-array: NumPy or JAX depending on the input backend. Length-2 points are lifted to the z = 0 plane.

minilink.graphical.catalog.shapes.plane_airframe_3d(length=2.0, width=0.2, l_cg=None, span=2.0, chord_w=0.35, chord_t=0.2, span_t=0.7, fin_height=0.35, l_w=0.0, l_t=None, color='#2f6fed', wing_color='#4c8cff', tail_color='#3a7af0')[source]

Body-frame solid airframe for meshcat (c.g. at the origin).

Axes match a standard aircraft body frame: +x forward, +y right, +z down. The fuselage is an ExtrudedPolygon side silhouette (local XY = body x–up, extruded along body y); wing / horizontal tail / vertical fin are thin Box solids.

minilink.graphical.catalog.shapes.vehicle_body(length=1.0, width=0.5, color='blue', opacity=0.85)[source]

Planar car/robot shell (arrow-shaped outline pointing along +X).

minilink.graphical.catalog.shapes.wheel_box(length=0.45, width=0.16)[source]

Small flat box used as a wheel/contact patch in vehicle diagrams.

Skins: a plant’s swappable look, as primitives keyed to the frames of its tf.

minilink.graphical.catalog.skins.car_skin_2d(plant, color='#1a1a1a')[source]

2-D centerline car look: chassis line + two axle wheels.

A skin is a plant’s swappable look, (plant) -> dict[frame, list[primitive]]: point geometry keyed to the frames of the plant’s tf, with no state math (tf places every frame). A plant opts in with skin = car_skin_2d and swaps its look with one assignment (car.skin = car_skin_3d), keeping the same f and the same tf. Skins are plain functions, so a custom skin is just another (plant) -> dict.

Frame keys: body (chassis along the wheelbase plus the rear wheel outline; the rear axle offset is baked into the wheel local_transform), and axle_front (steered front wheel). The placing tf supplies world pose and the front steer angle.

minilink.graphical.catalog.skins.car_skin_3d(plant, color='#151922')[source]

3-D four-wheel car look: ground plane, body box, four wheel rods.

Frame keys: world (ground), body (chassis box plus the two rear wheel rods; fixed hub offsets are baked into each rod local_transform), and wheel_fl / wheel_fr (steered front rods placed by tf).

minilink.graphical.catalog.skins.plane_skin_3d(plant, color='#2f6fed')[source]

3-D aircraft look: ground plane plus solid fuselage / wing / tails.

Frame keys: world (ground) and body (airframe solids with the c.g. at the body-frame origin). Reads length / span / lever arms from plant graphic attrs and params when present.

minilink.graphical.catalog.skins.ur5_skin(plant)[source]

Schematic UR5 look made from link and joint cylinders.

Frame keys are base, link0 … link5, joint0 … joint6, and tool. The plant’s tf places every moving part.

minilink.graphical.catalog.skins.merge_skins(*skins)[source]

Compose skin factories into one (plant) -> dict that merges their output.

Primitive lists are concatenated per frame key, so several looks stack on a single plant (e.g. a base body plus a decal layer).

minilink.graphical.catalog.skins.debug_state_skin(plant)[source]

Opt-in schematic dashboard: one marker per state and input, in a column.

A quick look for non-spatial plants (chemical reactors, filters, …) that have no natural geometry. Markers are keyed to world and offset vertically via local_transform (blue dots for states, red crosses for inputs).

Animation cameras: the 4x4 view from a drawable’s hints, and ready-made factories.

minilink.graphical.animation.camera.resolve_camera_from_hints(source, frames, x=None, u=None, t=0.0, *, override=None)[source]

Resolve the 4x4 camera for one frame from a drawable’s hints.

The camera is a view hint, not a kinematic frame, so it lives in the graphical band (out of tf / core/kinematics.py) and keeps its view scale in the T[3, 3] slot of camera_matrix(). Of the four design layers, two meet here:

  • Layer 2, the default resolver: a drawable’s plain camera_* attributes plus the resolved tf frames give the 4x4 of each frame (following a frame when camera_follow_frame is set).

  • Layer 3, the override: an explicit animate(camera=…), a constant 4x4 ndarray or a callable camera(frames, x, u, t) -> 4x4; follow_frame_camera() and fixed_camera() build such callables.

Layer 1 (the camera_* attributes on System) and Layer 4 (multi-source selection) live elsewhere.

Parameters:
  • source (object) – The drawable supplying camera_* attributes (Layer 1).

  • frames (dict[str, 4x4]) – Resolved tf for this instant (used when following a frame).

  • x (optional) – Playback state/input/time, forwarded to a callable override.

  • u (optional) – Playback state/input/time, forwarded to a callable override.

  • t (optional) – Playback state/input/time, forwarded to a callable override.

  • override (ndarray or callable, optional) – Layer-3 animate(camera=…) override. A constant 4x4 is returned as-is; a callable is invoked as override(frames, x, u, t).

Returns:

4x4 camera transform (rigid pose + T[3, 3] view scale).

Return type:

np.ndarray

minilink.graphical.animation.camera.fit_camera_to_frames(frames, *, plot_axes=(0, 1), margin=1.15, min_scale=0.5)[source]

Camera that frames every primitive of frames: the auto-fit for hint-less drawables.

frames are the animator’s per-frame dicts (primitives plus world transforms). Backdrops (planes) are ignored. The view is centred on the bounding box of everything drawn, with a square half-width margin times the larger box half-extent along plot_axes.

minilink.graphical.animation.camera.follow_frame_camera(frame_key, *, scale=10.0, plot_axes=(0, 1), target_offset=(0.0, 0.0, 0.0))[source]

Return a callable camera that re-centers on frames[frame_key] each frame.

The callable is the whole Layer-3 contract — pass it to animate(camera=…). target_offset shifts the look-at relative to the followed frame’s origin.

minilink.graphical.animation.camera.fixed_camera(target, *, scale=10.0, plot_axes=(0, 1))[source]

Return a callable camera fixed at target (a constant look-at).

Like follow_frame_camera() but frame-independent; pass it to animate(camera=…).

Time-only overlay drawables for composition at animate(overlays=[...]).

class minilink.graphical.animation.drawables.Overlay[source]

Base overlay: time-only drawable hooks with empty defaults.

Overlays reuse the same three drawable hooks as System but are driven by playback time t only (no x / u). The animator resolves each overlay with tf(t), cached get_kinematic_geometry(), and get_dynamic_geometry(t), then concatenates the flat draw lists after the primary plant.

class minilink.graphical.animation.drawables.Replay(drawable, trajectory)[source]

Full-skin ghost: forward another drawable’s hooks at trajectory samples.

class minilink.graphical.animation.drawables.SceneHistory(**layers)[source]

Time-indexed plan/trail/reference polylines (graphics-only, not collision).

Static layers (e.g. a fixed reference CustomLine) live in kinematic geometry. Layers with points_at() / points_at() are rebuilt each frame in get_dynamic_geometry(t).

class minilink.graphical.animation.drawables.SceneVisualizer(scene, *, color='#c44e52', opacity=0.45)[source]

Static obstacle skin exported from a collision-first Scene.

minilink.graphical.animation.drawables.scene_as_visualizer(scene, **kwargs)[source]

Build a SceneVisualizer overlay for scene.

minilink.graphical.animation.drawables.require_overlay(overlay)[source]

Refuse a drawable that is not an overlay: a raw scene, a bare system, a missing hook.

Return type:

None