2

I'm reading output data from some simulations in fortran to make a movie of orbits, after generating a couple graphs. At first, I didn't use blitting for the animation, so while it worked, it was very, very slow.

I originally thought that the animation I wanted lent itself to scatter, since I'd have five series of data with decreasing alphas to create a trailing effect. Here's my original (non-blit) update function:

def animate(frame):
    jptx, jpty = jx[frame-3:frame], jy[frame-3:frame]
    cptx, cpty = cx[frame-3:frame], cy[frame-3:frame]
    eptx, epty = ex[frame-3:frame], ey[frame-3:frame]
    gptx, gpty = gx[frame-3:frame], gy[frame-3:frame]
    iptx, ipty = ix[frame-3:frame], iy[frame-3:frame]
    ax2.clear()
    ax2.scatter(jptx, jpty, s=32, c=ablue, marker="s", label='Jupiter')
    ax2.scatter(cptx, cpty, s=8, c=ared, marker="o", label='Callisto')
    ax2.scatter(eptx, epty, s=8, c=agreen, marker="o", label='Europa')
    ax2.scatter(gptx, gpty, s=8, c=ablack, marker="o", label='Ganymede')
    ax2.scatter(iptx, ipty, s=8, c=ayellow, marker="o", label='Io')
    ax2.set_xlim(-3, 7)
    ax2.set_ylim(-3, 4)
animation = animation.FuncAnimation(fig2, animate, interval=0.5, frames=jt.size)
print('Begin saving animation')
animation.save('Tabbys Star.mp4', writer='ffmpeg', fps=60)
print('Animation saved')
plt.show()

Now, when I run the script, a window appears for a fraction of a second, and there is very clearly a yellow circle on the screen, indicating the background is being drawn. However, the window closes immediately after. This is the relevant code for the second attempt. The yellow circle was added in this attempt.

import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np

# j_file = location + 'JUPITER.aei'
# jt, jx, jy, jz = read_data(j_file)
jt, jx, jy, jz = np.random.random([100,4]), np.random.random([100,4]), np.random.random([100,4]), np.random.random([100,4])

# c_file = location + 'CALLISTO.aei'
# ct, cx, cy, cz = read_data(c_file)
ct, cx, cy, cz = np.random.random([100,4]), np.random.random([100,4]), np.random.random([100,4]), np.random.random([100,4])

alphas = [0.25, 0.5, 0.75, 1]

ablue = np.zeros((4, 4))
ablue[:, 2] = 1.0
ablue[:, 3] = alphas

ared = np.zeros((4, 4))
ared[:, 0] = 1.0
ared[:, 3] = alphas

fig2 = plt.figure()
ax2 = fig2.add_subplot(111, aspect='equal')
xdata, ydata = np.zeros((4,)), np.zeros((4,))
jpt, = plt.plot(xdata, ydata, marker='.', ms=32, c=ablue, label='Jupiter')
cpt, = plt.plot(xdata, ydata, marker='.', ms=8, c=ared, label='Callisto')


def init():
    ax2.set_xlim(-3, 7)
    ax2.set_ylim(-3, 4)
    circle = plt.Circle((0, 0), 0.1, color='y')
    ax2.add_patch(circle)
    for pt in [jpt, cpt]:
        pt.set_data(np.zeros((4,)), np.zeros((4,)))
    return jpt, cpt


def animate(frame, j, c):
    jptx, jpty = jx[frame-3:frame], jy[frame-3:frame]
    cptx, cpty = cx[frame-3:frame], cy[frame-3:frame]
    j.set_data(jptx, jpty)
    c.set_data(cptx, cpty)
    return j, c


animation = animation.FuncAnimation(fig2, animate, fargs=(jpt, cpt), interval=0.5, frames=jt.size, init_func=init, blit=True)
print('Begin saving animation')
# animation.save('Tabbys Star.mp4', writer='ffmpeg', fps=60)
print('Animation saved')
plt.show()

I'd also eventually like to add a legend and some axis labels, but I believe that can be done normally.

So what's the problem with animate in the second code snippet?

Thanks

Edited for clarity (again)

  • You need to provide a [mcve] of the issue, with all relevant variables defined and all irrelevant ones left out, such that the code is runnable. Once you have [edit]ed your question to include such a [mcve], people will be able to help. Necessary information also include how you run the script (environment, versions etc.). – ImportanceOfBeingErnest Sep 05 '17 at 16:43
  • Edited for clarity. In lieu of astronomical data, the data (eg jt, jx, jy, jz above) can be simulated as np 1-D arrays of equal length (ie jt.size=ix.size etc) – Miguel Martinez Sep 05 '17 at 20:48
  • The idea of a [mcve] is that someone can copy paste the code from the question, run it and observe the problem in question. If instead I need to spend 20 minutes tinkering together the example from bits and pieces from the question, I will soon give up without being able to help. – ImportanceOfBeingErnest Sep 06 '17 at 06:56
  • I fixed it again. The whole code block will reproduce the problem from 2 datasets, which I suppose is conceptually similar to five datasets. – Miguel Martinez Sep 06 '17 at 12:04

2 Answers2

0

Please make sure, that you render it for more than 1 frame, by setting frames to a high value. In the code you posted, the number of frames is not clearly defined, which may cause this problem.

zimmerrol
  • 4,872
  • 3
  • 22
  • 41
0

You are confusing plt.plot and plt.scatter here. The error you get would even be produced without any animation.

While plt.plot has arguments color and ms to set the color and markersize respectively, they do not allow to use different values for different points. This is why there exists a scatter plot. plt.scatter has arguments c and s to set the color and markersize respectively.

So you need to use scatter to obtain differently colored points.

jpt = plt.scatter(xdata, ydata, marker='.', s=32, c=ablue, label='Jupiter')

Then for the animation you would need to adjust your code for the use with scatter since it does not have a .set_data method, but a .set_offsets method, which expects a 2 column array input.

j.set_offsets(np.c_[jptx, jpty])

In total the script would look like

import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np


jt, jx, jy, jz = [np.random.random([100,4]) for _ in range(4)]
ct, cx, cy, cz = [np.random.random([100,4]) for _ in range(4)]

alphas = [0.25, 0.5, 0.75, 1]

ablue = np.zeros((4, 4))
ablue[:, 2] = 1.0
ablue[:, 3] = alphas

ared = np.zeros((4, 4))
ared[:, 0] = 1.0
ared[:, 3] = alphas

fig2 = plt.figure()
ax2 = fig2.add_subplot(111, aspect='equal')
xdata, ydata = np.zeros((4,)), np.zeros((4,))

jpt = plt.scatter(xdata, ydata, marker='.', s=32, c=ablue, label='Jupiter')
cpt = plt.scatter(xdata, ydata, marker='.', s=8, c=ared, label='Callisto')


def init():
    ax2.axis([0,1,0,1])
    circle = plt.Circle((0, 0), 0.1, color='y')
    ax2.add_patch(circle)
    for pt in [jpt, cpt]:
        pt.set_offsets(np.c_[np.zeros((4,)), np.zeros((4,))])
    return jpt, cpt


def animate(frame, j, c):
    jptx, jpty = jx[frame-3:frame], jy[frame-3:frame]
    cptx, cpty = cx[frame-3:frame], cy[frame-3:frame]
    j.set_offsets(np.c_[jptx, jpty])
    c.set_offsets(np.c_[cptx, cpty])
    return j, c


animation = animation.FuncAnimation(fig2, animate, fargs=(jpt, cpt), 
                                    interval=50, frames=jt.size, init_func=init, blit=True)

plt.show()
ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • Thanks! Sorry for all the trouble. As you can probably tell I'm new here – Miguel Martinez Sep 06 '17 at 15:59
  • If I may ask a followup, why is it that the background was still being drawn? Why wouldn't I get an explicit error message from this oversight? – Miguel Martinez Sep 06 '17 at 16:16
  • You should get an explicit error (but I cannot tell, because I don't know how you run the code). Since the error is triggered at draw time (at the moment, when the renderer finds a color which it cannot interprete), the rest of the plot is drawn more or less correctly. – ImportanceOfBeingErnest Sep 06 '17 at 16:33