resonance
resonance copied to clipboard
Matplotlib 2.1 includes a JS Widget for animations
See: http://matplotlib.org/users/whats_new.html#interactive-js-widgets-for-animation
This would likely work better than what we are doing.
It is a pretty slick interface and will play back with more reliable timing. I think it would also make the animations interactive in the served notebooks. It would also remove the need for the notebook backend.
But as far as I can tell it's pre-rendering the HTML5 video, so on my laptop it takes almost 3 minutes to render ~1000 frames of a pretty sparse animation.
Ah, the time to render. That's not that fun. What code did you type to get it to work?
I tried this out with the example I have in the nonlinear systems branch. Open notebook and do:
from resonance.tests.test_nonlinear_systems import sys, sample_rate
ani = sys.animate_configuration(interval=1.0 / sample_rate * 1000) # interval should be in milliseconds
from IPython.display import HTML
HTML(ani.to_jshtml())
It works but it doesn't play the full 5 seconds. It stops around 3.5 seconds or so and plays really fast. It isn't clear to me how the number of frames, the interval kwarg, and fps kwarg interact, as it seems to affect how many frames are played. I generate 151 frames that are spaced in simulation time 1/30 seconds apart. I set the interval kwarg to 1/30*1000 milliseconds and the animation doesn't seem to play all of the frames.
I'm not quite sure how to fix it. But it didn't take too awful long to build the HTML, and the controls make it quite nice.
Here's an example:
import numpy as np
from matplotlib import animation
from matplotlib.patches import Rectangle
import matplotlib.pyplot as plt
from IPython.display import HTML
def animate(frames, fps):
dt = 1 / fps
theta = np.linspace(0, 2*np.pi, frames)
fig, ax = plt.subplots(1, 1)
rect = Rectangle((0, 0), 1, 0.1)
ax.add_patch(rect)
ax.set_xlim(-1, 1)
ax.set_ylim(-1, 1)
ax.set_aspect('equal')
def update(i):
rect.angle = np.rad2deg(theta[i])
return rect,
return animation.FuncAnimation(fig, update, frames=frames, interval=dt)
frames = 300
fps = 30
HTML(animate(frames, fps).to_jshtml(fps=fps))
This plays with correct timing for me. Took about 30 seconds to render on my (somewhat old) i7 with 2 cores/4 threads.
Also note that matplotlib.patches.Rectangle
now has a proper angle
attribute. Careful using hidden attributes ;)
So is my fps / 1000
incorrect above? The docs says interval
is supposed to be milliseconds between frames. If fps is "frames per second" don't you need a factor of 1000 somewhere? The source for to_jshtml()
shows a factor of 1000 that is used if you don't pass in fps. In your example you set the interval to a number in seconds, not in milliseconds, right?
If I remove the factor of 1000 in my example I'm still not able to get it to animate the full 5 seconds.
Figured out the 5 second issue. If the frames
arg to FuncAnimation
is a generator or iterable that has no __len__
attribute, you need to give FuncAnimation
a save_count
, otherwise it defaults to 100.
I'll submit a PR to matplotlib noting this in the docs.
Still not sure about the interval/fps stuff. I'll investigate more later.
Looks like interval
is indeed supposed to be in milliseconds. I guess I just fixed the off-by-one(thousand) error by specifying fps
correctly in the to_jshtml
call :)
So it appears that generating the video will respect your interval by default, but you can override it by specifying fps
.
I just got this error when I increased save_count to 151:
IOPub data rate exceeded.
The notebook server will temporarily stop sending output
to the client in order to avoid crashing it.
To change this limit, set the config variable
`--NotebookApp.iopub_data_rate_limit`.
Looks like the above was probably fixed in notebook > 5.1.
https://github.com/jupyter/notebook/issues/2287
This is also cool because the animations show up in the rendered notebooks.
Indeed. I thought I mentioned that somewhere but I can't seem to find it.
Anyway, it may be worth adding a convenience function to resonance that does the conversion and display for you, like
from resonance.functions import display_animation
ani = sys.animate_configuration()
display_animation(ani)
because I personally can never remember the imports and function calls needed. Or alternatively just add a kwarg to animate_configuration
to do this. Changing the return type based on an argument might be a bad design choice though.
One thing I didn't quite get fully figured out in homework 3 (animating the bike wheel on a pivot) was getting the HTML5 video to display without also showing the animation from the sys.animate_configuration()
call. I've solved this before by adding plt.ioff()
/plt.ion()
calls to the cell generating the Figure
and then generating and displaying the video in a separate cell. There's probably a better way to do it than that though.