Animated plots

Animations are generated from a list (or other iterable) of graphics objects. Images are produced by calling the save_image method on each input object, creating a sequence of PNG files. These are then assembled to various target formats using different tools. In particular, the convert program from ImageMagick can be used to generate an animated GIF file. FFmpeg (with the command line program ffmpeg) provides support for various video formats, but also an alternative method of generating animated GIFs. For browsers which support it, APNG can be used as another alternative which works without any extra dependencies.

Warning

Note that ImageMagick and FFmpeg are not included with Sage, and must be installed by the user. On unix systems, type which convert at a command prompt to see if convert (part of the ImageMagick suite) is installed. If it is, you will be given its location. Similarly, you can check for ffmpeg with which ffmpeg. See the websites of ImageMagick or FFmpeg for installation instructions.

EXAMPLES:

The sine function:

sage: sines = [plot(c*sin(x), (-2*pi,2*pi), color=Color(c,0,0), ymin=-1, ymax=1) for c in sxrange(0,1,.2)]
sage: a = animate(sines)
sage: a
Animation with 5 frames
sage: a.show()  # optional -- ImageMagick

Animate using FFmpeg instead of ImageMagick:

sage: f = tmp_filename(ext='.gif')
sage: a.save(filename=f, use_ffmpeg=True) # optional -- ffmpeg

Animate as an APNG:

sage: a.apng()  # long time

An animated sage.plot.graphics.GraphicsArray of rotating ellipses:

sage: E = animate((graphics_array([[ellipse((0,0),a,b,angle=t,xmin=-3,xmax=3)+circle((0,0),3,color='blue') for a in range(1,3)] for b in range(2,4)]) for t in sxrange(0,pi/4,.15)))
sage: E         # animations produced from a generator do not have a known length
Animation with unknown number of frames
sage: E.show()  # optional -- ImageMagick

A simple animation of a circle shooting up to the right:

sage: c = animate([circle((i,i), 1-1/(i+1), hue=i/10) for i in srange(0,2,0.2)],
....:               xmin=0,ymin=0,xmax=2,ymax=2,figsize=[2,2])
sage: c.show() # optional -- ImageMagick

Animations of 3d objects:

sage: var('s,t')
(s, t)
sage: def sphere_and_plane(x):
....:     return sphere((0,0,0),1,color='red',opacity=.5)+parametric_plot3d([t,x,s],(s,-1,1),(t,-1,1),color='green',opacity=.7)
sage: sp = animate([sphere_and_plane(x) for x in sxrange(-1,1,.3)])
sage: sp[0]      # first frame
Graphics3d Object
sage: sp[-1]     # last frame
Graphics3d Object
sage: sp.show()  # optional -- ImageMagick

sage: (x,y,z) = var('x,y,z')
sage: def frame(t):
....:     return implicit_plot3d((x^2 + y^2 + z^2), (x, -2, 2), (y, -2, 2), (z, -2, 2), plot_points=60, contour=[1,3,5], region=lambda x,y,z: x<=t or y>=t or z<=t)
sage: a = animate([frame(t) for t in srange(.01,1.5,.2)])
sage: a[0]       # long time
Graphics3d Object
sage: a.show()   # optional -- ImageMagick

If the input objects do not have a save_image method, then the animation object attempts to make an image by calling its internal method sage.plot.animate.Animation.make_image(). This is illustrated by the following example:

sage: t = var('t')
sage: a = animate((sin(c*pi*t) for c in sxrange(1,2,.2)))
sage: a.show()  # optional -- ImageMagick

AUTHORS:

  • William Stein
  • John Palmieri
  • Niles Johnson (2013-12): Expand to animate more graphics objects
  • Martin von Gagern (2014-12): Added APNG support
class sage.plot.animate.APngAssembler(out, num_frames, num_plays=0, delay=200, delay_denominator=100)

Bases: object

Builds an APNG (Animated PNG) from a sequence of PNG files. This is used by the sage.plot.animate.Animation.apng() method.

This code is quite simple; it does little more than copying chunks from input PNG files to the output file. There is no optimization involved. This does not depend on external programs or libraries.

INPUT:

  • out – a file opened for binary writing to which the data will be written
  • num_frames – the number of frames in the animation
  • num_plays – how often to iterate, 0 means infinitely
  • delay – numerator of the delay fraction in seconds
  • delay_denominator – denominator of the delay in seconds

EXAMPLES:

sage: from sage.plot.animate import APngAssembler
sage: def assembleAPNG():
....:     a = animate([sin(x + float(k)) for k in srange(0,2*pi,0.7)],
....:                 xmin=0, xmax=2*pi, figsize=[2,1])
....:     pngdir = a.png()
....:     outfile = sage.misc.temporary_file.tmp_filename(ext='.png')
....:     with open(outfile, "wb") as f:
....:         apng = APngAssembler(f, len(a))
....:         for i in range(len(a)):
....:             png = os.path.join(pngdir, "{:08d}.png".format(i))
....:             apng.add_frame(png, delay=10*i + 10)
....:     return outfile
....:
sage: assembleAPNG()  # long time
'...png'
add_frame(pngfile, delay=None, delay_denominator=None)

Adds a single frame to the APNG file.

INPUT:

  • pngfile – file name of the PNG file with data for this frame
  • delay – numerator of the delay fraction in seconds
  • delay_denominator – denominator of the delay in seconds

If the delay is not specified, the default from the constructor applies.

TESTS:

sage: from sage.plot.animate import APngAssembler
sage: from StringIO import StringIO
sage: buf = StringIO()
sage: apng = APngAssembler(buf, 2)
sage: fn = APngAssembler._testData("input1", True)
sage: apng.add_frame(fn, delay=0x567, delay_denominator=0x1234)
sage: fn = APngAssembler._testData("input2", True)
sage: apng.add_frame(fn)
sage: len(buf.getvalue())
217
sage: buf.getvalue() == APngAssembler._testData("anim12", False)
True
sage: apng.add_frame(fn)
Traceback (most recent call last):
...
RuntimeError: Already reached the declared number of frames
set_default(pngfile)

Adds a default image for the APNG file.

This image is used as a fallback in case some application does not understand the APNG format. This method must be called prior to any calls to the add_frame method, if it is called at all. If it is not called, then the first frame of the animation will be the default.

INPUT:

  • pngfile – file name of the PNG file with data for the default image

TESTS:

sage: from sage.plot.animate import APngAssembler
sage: from StringIO import StringIO
sage: buf = StringIO()
sage: apng = APngAssembler(buf, 1)
sage: fn = APngAssembler._testData("input1", True)
sage: apng.set_default(fn)
sage: fn = APngAssembler._testData("input2", True)
sage: apng.add_frame(fn, delay=0x567, delay_denominator=0x1234)
sage: len(buf.getvalue())
179
sage: buf.getvalue() == APngAssembler._testData("still1anim2", False)
True
sage: apng.add_frame(fn)
Traceback (most recent call last):
...
RuntimeError: Already reached the declared number of frames
class sage.plot.animate.Animation(v=None, **kwds)

Bases: sage.structure.sage_object.SageObject

Return an animation of a sequence of plots of objects.

INPUT:

  • v - iterable of Sage objects. These should preferably be graphics objects, but if they aren’t then make_image() is called on them.
  • xmin, xmax, ymin, ymax - the ranges of the x and y axes.
  • **kwds - all additional inputs are passed onto the rendering command. E.g., use figsize to adjust the resolution and aspect ratio.

EXAMPLES:

sage: a = animate([sin(x + float(k)) for k in srange(0,2*pi,0.3)],
....:                xmin=0, xmax=2*pi, figsize=[2,1])
sage: a
Animation with 21 frames
sage: a[:5]
Animation with 5 frames
sage: a.show()          # optional -- ImageMagick
sage: a[:5].show()      # optional -- ImageMagick

The show() method takes arguments to specify the delay between frames (measured in hundredths of a second, default value 20) and the number of iterations (default value 0, which means to iterate forever). To iterate 4 times with half a second between each frame:

sage: a.show(delay=50, iterations=4) # optional -- ImageMagick

An animation of drawing a parabola:

sage: step = 0.1
sage: L = Graphics()
sage: v = []
sage: for i in srange(0,1,step):
....:       L += line([(i,i^2),(i+step,(i+step)^2)], rgbcolor=(1,0,0), thickness=2)
....:       v.append(L)
sage: a = animate(v, xmin=0, ymin=0)
sage: a.show() # optional -- ImageMagick
sage: show(L)

TESTS:

This illustrates that trac ticket #2066 is fixed (setting axes ranges when an endpoint is 0):

sage: animate([plot(sin, -1,1)], xmin=0, ymin=0)._kwds['xmin']
0

We check that trac ticket #7981 is fixed:

sage: a = animate([plot(sin(x + float(k)), (0, 2*pi), ymin=-5, ymax=5)
....:              for k in srange(0,2*pi,0.3)])
sage: a.show() # optional -- ImageMagick

Do not convert input iterator to a list, but ensure that the frame count is known after rendering the frames:

sage: a = animate((plot(x^p, (x,0,2)) for p in sxrange(1,2,.1))); a
Animation with unknown number of frames
sage: a.png()    # long time
'.../'
sage: len(a)     # long time
10
sage: a._frames
<generator object ...
apng(savefile=None, show_path=False, delay=20, iterations=0)

Creates an animated PNG composed from rendering the graphics objects in self. Return the absolute path to that file.

Notice that not all web browsers are capable of displaying APNG files, though they should still present the first frame of the animation as a fallback.

The generated file is not optimized, so it may be quite large.

Input:

  • delay - (default: 20) delay in hundredths of a second between frames
  • savefile - file that the animated gif gets saved to
  • iterations - integer (default: 0); number of iterations of animation. If 0, loop forever.
  • show_path - boolean (default: False); if True, print the path to the saved file

EXAMPLES:

sage: a = animate([sin(x + float(k)) for k in srange(0,2*pi,0.7)],
....:                xmin=0, xmax=2*pi, figsize=[2,1])
sage: dir = tmp_dir()
sage: a.apng()  # long time
sage: a.apng(savefile=dir + 'my_animation.png', delay=35, iterations=3)  # long time
sage: a.apng(savefile=dir + 'my_animation.png', show_path=True)  # long time
Animation saved to .../my_animation.png.

If the individual frames have different sizes, an error will be raised:

sage: a = animate([plot(sin(x), (x, 0, k)) for k in range(1,4)],
....:             ymin=-1, ymax=1, aspect_ratio=1, figsize=[2,1])
sage: a.apng()  # long time
Traceback (most recent call last):
...
ValueError: Chunk IHDR mismatch
ffmpeg(savefile=None, show_path=False, output_format=None, ffmpeg_options='', delay=None, iterations=0, pix_fmt='rgb24')

Returns a movie showing an animation composed from rendering the frames in self.

This method will only work if ffmpeg is installed. See http://www.ffmpeg.org for information about ffmpeg.

INPUT:

  • savefile - file that the mpeg gets saved to.
  • show_path - boolean (default: False); if True, print the path to the saved file
  • output_format - string (default: None); format and suffix to use for the video. This may be ‘mpg’, ‘mpeg’, ‘avi’, ‘gif’, or any other format that ffmpeg can handle. If this is None and the user specifies savefile with a suffix, say savefile='animation.avi', try to determine the format (‘avi’ in this case) from that file name. If no file is specified or if the suffix cannot be determined, ‘mpg’ is used.
  • ffmpeg_options - string (default: ‘’); this string is passed directly to ffmpeg.
  • delay - integer (default: None); delay in hundredths of a second between frames. The framerate is 100/delay. This is not supported for mpeg files: for mpegs, the frame rate is always 25 fps.
  • iterations - integer (default: 0); number of iterations of animation. If 0, loop forever. This is only supported for animated gif output and requires ffmpeg version 0.9 or later. For older versions, set iterations=None.
  • pix_fmt - string (default: ‘rgb24’); used only for gif output. Different values such as ‘rgb8’ or ‘pal8’ may be necessary depending on how ffmpeg was installed. Set pix_fmt=None to disable this option.

If savefile is not specified: in notebook mode, display the animation; otherwise, save it to a default file name. Use sage.misc.misc.set_verbose() with level=1 to see additional output.

EXAMPLES:

sage: a = animate([sin(x + float(k)) for k in srange(0,2*pi,0.7)],
....:             xmin=0, xmax=2*pi, ymin=-1, ymax=1, figsize=[2,1])
sage: td = tmp_dir()
sage: a.ffmpeg(savefile=td + 'new.mpg')       # optional -- ffmpeg
sage: a.ffmpeg(savefile=td + 'new.avi')       # optional -- ffmpeg
sage: a.ffmpeg(savefile=td + 'new.gif')       # optional -- ffmpeg
sage: a.ffmpeg(savefile=td + 'new.mpg', show_path=True) # optional -- ffmpeg
Animation saved to .../new.mpg.

Note

If ffmpeg is not installed, you will get an error message like this:

Error: ffmpeg does not appear to be installed. Saving an animation to
a movie file in any format other than GIF requires this software, so
please install it and try again.

See www.ffmpeg.org for more information.

TESTS:

sage: a.ffmpeg(output_format='gif',delay=30,iterations=5)     # optional -- ffmpeg
gif(delay=20, savefile=None, iterations=0, show_path=False, use_ffmpeg=False)

Returns an animated gif composed from rendering the graphics objects in self.

This method will only work if either (a) the ImageMagick software suite is installed, i.e., you have the convert command or (b) ffmpeg is installed. See the web sites of ImageMagick and FFmpeg for more details. By default, this produces the gif using convert if it is present. If this can’t find convert or if use_ffmpeg is True, then it uses ffmpeg instead.

INPUT:

  • delay - (default: 20) delay in hundredths of a second between frames
  • savefile - file that the animated gif gets saved to
  • iterations - integer (default: 0); number of iterations of animation. If 0, loop forever.
  • show_path - boolean (default: False); if True, print the path to the saved file
  • use_ffmpeg - boolean (default: False); if True, use ‘ffmpeg’ by default instead of ‘convert’.

If savefile is not specified: in notebook mode, display the animation; otherwise, save it to a default file name.

EXAMPLES:

sage: a = animate([sin(x + float(k)) for k in srange(0,2*pi,0.7)],
....:             xmin=0, xmax=2*pi, ymin=-1, ymax=1, figsize=[2,1])
sage: td = tmp_dir()
sage: a.gif()              # not tested
sage: a.gif(savefile=td + 'my_animation.gif', delay=35, iterations=3)  # optional -- ImageMagick
sage: a.gif(savefile=td + 'my_animation.gif', show_path=True) # optional -- ImageMagick
Animation saved to .../my_animation.gif.
sage: a.gif(savefile=td + 'my_animation_2.gif', show_path=True, use_ffmpeg=True) # optional -- ffmpeg
Animation saved to .../my_animation_2.gif.

Note

If neither ffmpeg nor ImageMagick is installed, you will get an error message like this:

Error: Neither ImageMagick nor ffmpeg appears to be installed. Saving an
animation to a GIF file or displaying an animation requires one of these
packages, so please install one of them and try again.

See www.imagemagick.org and www.ffmpeg.org for more information.
graphics_array(ncols=3)

Return a sage.plot.graphics.GraphicsArray with plots of the frames of this animation, using the given number of columns. The frames must be acceptable inputs for sage.plot.graphics.GraphicsArray.

EXAMPLES:

sage: E = EllipticCurve('37a')
sage: v = [E.change_ring(GF(p)).plot(pointsize=30) for p in [97, 101, 103, 107]]
sage: a = animate(v, xmin=0, ymin=0, axes=False)
sage: a
Animation with 4 frames
sage: a.show() # optional -- ImageMagick

Modify the default arrangement of array:

sage: g = a.graphics_array(); print g
Graphics Array of size 2 x 3
sage: g.show(figsize=[6,3]) # optional

Specify different arrangement of array and save with different file name:

sage: g = a.graphics_array(ncols=2); print g
Graphics Array of size 2 x 2
sage: f = tmp_filename(ext='.png')
sage: g.show(f) # optional

Frames can be specified as a generator too; it is internally converted to a list:

sage: t = var('t')
sage: b = animate((plot(sin(c*pi*t)) for c in sxrange(1,2,.2)))
sage: g = b.graphics_array(); print g
Graphics Array of size 2 x 3
sage: g.show() # optional
make_image(frame, filename, **kwds)

Given a frame which has no save_image() method, make a graphics object and save it as an image with the given filename. By default, this is sage.plot.plot.plot(). To make animations of other objects, override this method in a subclass.

EXAMPLES:

sage: from sage.plot.animate import Animation
sage: class MyAnimation(Animation):
....:    def make_image(self, frame, filename, **kwds):
....:        P = parametric_plot(frame[0], frame[1], **frame[2])
....:        P.save_image(filename,**kwds)

sage: t = var('t')
sage: x = lambda t: cos(t)
sage: y = lambda n,t: sin(t)/n
sage: B = MyAnimation([([x(t), y(i+1,t)],(t,0,1), {'color':Color((1,0,i/4)), 'aspect_ratio':1, 'ymax':1}) for i in range(4)])

sage: d = B.png(); v = os.listdir(d); v.sort(); v  # long time
['00000000.png', '00000001.png', '00000002.png', '00000003.png']
sage: B.show()  # not tested

sage: class MyAnimation(Animation):
....:    def make_image(self, frame, filename, **kwds):
....:        G = frame.plot()
....:        G.set_axes_range(floor(G.xmin()),ceil(G.xmax()),floor(G.ymin()),ceil(G.ymax()))
....:        G.save_image(filename, **kwds)

sage: B = MyAnimation([graphs.CompleteGraph(n) for n in range(7,11)], figsize=5)
sage: d = B.png()
sage: v = os.listdir(d); v.sort(); v
['00000000.png', '00000001.png', '00000002.png', '00000003.png']
sage: B.show()  # not tested
png(dir=None)

Render PNG images of the frames in this animation, saving them in dir. Return the absolute path to that directory. If the frames have been previously rendered and dir is None, just return the directory in which they are stored.

When dir is other than None, force re-rendering of frames.

INPUT:

  • dir – Directory in which to store frames. Default None; in this case, a temporary directory will be created for storing the frames.

EXAMPLES:

sage: a = animate([plot(x^2 + n) for n in range(4)], ymin=0, ymax=4)
sage: d = a.png(); v = os.listdir(d); v.sort(); v  # long time
['00000000.png', '00000001.png', '00000002.png', '00000003.png']
save(filename=None, show_path=False, use_ffmpeg=False)

Save this animation.

INPUT:

  • filename - (default: None) name of save file
  • show_path - boolean (default: False); if True, print the path to the saved file
  • use_ffmpeg - boolean (default: False); if True, use ‘ffmpeg’ by default instead of ‘convert’ when creating GIF files.

If filename is None, then in notebook mode, display the animation; otherwise, save the animation to a GIF file. If filename ends in ‘.sobj’, save to an sobj file. Otherwise, try to determine the format from the filename extension (‘.mpg’, ‘.gif’, ‘.avi’, etc.). If the format cannot be determined, default to GIF.

For GIF files, either ffmpeg or the ImageMagick suite must be installed. For other movie formats, ffmpeg must be installed. An sobj file can be saved with no extra software installed.

EXAMPLES:

sage: a = animate([sin(x + float(k)) for k in srange(0,2*pi,0.7)],
....:             xmin=0, xmax=2*pi, ymin=-1, ymax=1, figsize=[2,1])
sage: td = tmp_dir()
sage: a.save()         # not tested
sage: a.save(td + 'wave.gif')   # optional -- ImageMagick
sage: a.save(td + 'wave.gif', show_path=True)   # optional -- ImageMagick
Animation saved to file .../wave.gif.
sage: a.save(td + 'wave.avi', show_path=True)   # optional -- ffmpeg
Animation saved to file .../wave.avi.
sage: a.save(td + 'wave0.sobj')
sage: a.save(td + 'wave1.sobj', show_path=True)
Animation saved to file .../wave1.sobj.
show(delay=20, iterations=0)

Show this animation.

INPUT:

  • delay - (default: 20) delay in hundredths of a second between frames
  • iterations - integer (default: 0); number of iterations of animation. If 0, loop forever.

Note

Currently this is done using an animated gif, though this could change in the future. This requires that either ffmpeg or the ImageMagick suite (in particular, the convert command) is installed.

See also the ffmpeg() method.

EXAMPLES:

sage: a = animate([sin(x + float(k)) for k in srange(0,2*pi,0.7)],
....:                xmin=0, xmax=2*pi, figsize=[2,1])
sage: a.show()       # optional -- ImageMagick

The preceding will loop the animation forever. If you want to show only three iterations instead:

sage: a.show(iterations=3)    # optional -- ImageMagick

To put a half-second delay between frames:

sage: a.show(delay=50)        # optional -- ImageMagick

Note

If you don’t have ffmpeg or ImageMagick installed, you will get an error message like this:

Error: Neither ImageMagick nor ffmpeg appears to be installed. Saving an
animation to a GIF file or displaying an animation requires one of these
packages, so please install one of them and try again.

See www.imagemagick.org and www.ffmpeg.org for more information.
sage.plot.animate.animate(frames, **kwds)

Animate a list of frames by creating a sage.plot.animate.Animation object.

EXAMPLES:

sage: t = var('t')
sage: a = animate((cos(c*pi*t) for c in sxrange(1,2,.2)))
sage: a.show()  # optional -- ImageMagick

See also sage.plot.animate for more examples.

Previous topic

Graphics objects

Next topic

Arcs of circles and ellipses

This Page