Live plot

This demo combines two synchronized canvas types in the same anim.window. On the left, an anim.plane.canva displays a red disk moving around a circular orbit. On the right, an anim.plot.canva displays the disk coordinates \(x(t)=\cos(t)\) and \(y(t)=\sin(t)\) as live Matplotlib traces. Both panels receive the same anim.time object, so the geometric motion and the plotted data always represent the same animation step.

The plot constructor configures its exposed anim.plot.canva.axes object directly. The two calls to plot return Matplotlib artists that are kept in x_trace and y_trace; only their data need to change during the animation. The legend automatically follows the dark canvas style.

self.axes.set_xlabel('Time')
self.axes.set_ylabel('Coordinate')
self.axes.set_xlim(0, 2*np.pi)
self.axes.set_ylim(-1.1, 1.1)
self.axes.grid(True, alpha=0.3)
self.x_trace, = self.axes.plot([0], [1], color='c', label='x')
self.y_trace, = self.axes.plot([0], [0], color='m', label='y')
self.axes.legend()

In anim.plot.canva.update(), the displayed arrays are reconstructed from the current t.step rather than extended blindly. This makes the plot idempotent and reversible: revisiting a step produces exactly the same data, and stepping backward removes samples beyond the current time. The final call to super().update(t) redraws the Matplotlib canvas and completes the frame.

times = np.arange(t.step + 1)*self.window.dt
self.x_trace.set_data(times, np.cos(times))
self.y_trace.set_data(times, np.sin(times))
super().update(t)

The two panels are placed explicitly in adjacent grid columns. Their default relative width is identical, while the window’s aspect ratio provides enough horizontal room for both. Playback is limited to one revolution and backward stepping is enabled to demonstrate that both canvas types remain synchronized. See the Matplotlib plot canvas guide for sizing, multiple axes, styling and other update patterns.

A disk orbiting beside live cosine and sine traces

Full Code

 1'''
 2Live plot demo
 3'''
 4
 5import numpy as np
 6import anim
 7
 8# ═══ Orbital animation canvas ═════════════════════════════════════════════
 9
10class OrbitCanva(anim.plane.canva):
11
12  # ────────────────────────────────────────────────────────────────────────
13  def __init__(self, window):
14
15    super().__init__(window,
16                     boundaries=[[-1.25, 1.25], [-1.25, 1.25]],
17                     display_boundaries=False)
18
19    theta = np.linspace(0, 2*np.pi, 200)
20    self.item['orbit'] = anim.plane.path(
21      points=np.column_stack((np.cos(theta), np.sin(theta))),
22      stroke='grey',
23      thickness=0
24    )
25    self.item['disk'] = anim.plane.circle(
26      position=[1, 0],
27      radius=0.03,
28      color='red'
29    )
30
31  # ────────────────────────────────────────────────────────────────────────
32  def update(self, t):
33
34    self.item['disk'].position = [np.cos(t.time), np.sin(t.time)]
35    super().update(t)
36
37# ═══ Live plot canvas ══════════════════════════════════════════════════════
38
39class PlotCanva(anim.plot.canva):
40
41  # ────────────────────────────────────────────────────────────────────────
42  def __init__(self, window):
43
44    super().__init__(window)
45
46    self.axes.set_title('Disk coordinates')
47    self.axes.set_xlabel('Time')
48    self.axes.set_ylabel('Coordinate')
49    self.axes.set_xlim(0, 2*np.pi)
50    self.axes.set_ylim(-1.1, 1.1)
51    self.axes.grid(True, alpha=0.3)
52
53    self.x_trace, = self.axes.plot([0], [1], color='c', label='x')
54    self.y_trace, = self.axes.plot([0], [0], color='m', label='y')
55    self.axes.legend()
56
57  # ────────────────────────────────────────────────────────────────────────
58  def update(self, t):
59
60    # Rebuild the traces from the current step so backward motion remains
61    # deterministic and never appends duplicate or out-of-order samples.
62
63    times = np.arange(t.step + 1)*self.window.dt
64    self.x_trace.set_data(times, np.cos(times))
65    self.y_trace.set_data(times, np.sin(times))
66
67    super().update(t)
68
69# ═══ Main ═════════════════════════════════════════════════════════════════
70
71W = anim.window('Live plot', aspect_ratio=2)
72
73W.add(OrbitCanva, row=0, col=0)
74W.add(PlotCanva, row=0, col=1)
75
76W.step_max = round(2*np.pi/W.dt)
77W.allow_backward = True
78W.allow_negative_time = False
79
80W.show()