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 magick/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
magick
at a command prompt to see if magick
(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: x = SR.var("x")
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: print(a)
Animation with 5 frames
sage: a.show() # long time # optional -- ImageMagick
>>> from sage.all import *
>>> x = SR.var("x")
>>> sines = [plot(c*sin(x), (-Integer(2)*pi,Integer(2)*pi), color=Color(c,Integer(0),Integer(0)), ymin=-Integer(1), ymax=Integer(1))
... for c in sxrange(Integer(0),Integer(1),RealNumber('.2'))]
>>> a = animate(sines)
>>> print(a)
Animation with 5 frames
>>> a.show() # long time # optional -- ImageMagick
x = SR.var("x") 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)] a = animate(sines) print(a) a.show() # long time # optional -- ImageMagick
Animate using FFmpeg instead of ImageMagick:
sage: a.show(use_ffmpeg=True) # long time # optional -- FFmpeg
>>> from sage.all import *
>>> a.show(use_ffmpeg=True) # long time # optional -- FFmpeg
a.show(use_ffmpeg=True) # long time # optional -- FFmpeg
Animate as an APNG:
sage: a.apng(show_path=True) # long time
Animation saved to ....png.
>>> from sage.all import *
>>> a.apng(show_path=True) # long time
Animation saved to ....png.
a.apng(show_path=True) # long time
An animated sage.plot.multigraphics.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: str(E) # animations produced from a generator do not have a known length
'Animation with unknown number of frames'
sage: E.show() # long time # optional -- ImageMagick
>>> from sage.all import *
>>> E = animate((graphics_array([[ellipse((Integer(0),Integer(0)), a, b, angle=t, xmin=-Integer(3), xmax=Integer(3))
... + circle((Integer(0),Integer(0)), Integer(3), color='blue')
... for a in range(Integer(1),Integer(3))]
... for b in range(Integer(2),Integer(4))])
... for t in sxrange(Integer(0), pi/Integer(4), RealNumber('.15'))))
>>> str(E) # animations produced from a generator do not have a known length
'Animation with unknown number of frames'
>>> E.show() # long time # optional -- ImageMagick
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))) str(E) # animations produced from a generator do not have a known length E.show() # long time # 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() # long time # optional -- ImageMagick
>>> from sage.all import *
>>> c = animate([circle((i,i), Integer(1) - Integer(1)/(i+Integer(1)), hue=i/Integer(10))
... for i in srange(Integer(0), Integer(2), RealNumber('0.2'))],
... xmin=Integer(0), ymin=Integer(0), xmax=Integer(2), ymax=Integer(2), figsize=[Integer(2),Integer(2)])
>>> c.show() # long time # optional -- ImageMagick
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]) c.show() # long time # optional -- ImageMagick
Animations of 3d objects:
sage: s,t = SR.var("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() # long time # optional -- ImageMagick
sage: (x,y,z) = SR.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() # long time # optional -- ImageMagick
>>> from sage.all import *
>>> s,t = SR.var("s,t")
>>> def sphere_and_plane(x):
... return (sphere((Integer(0),Integer(0),Integer(0)), Integer(1), color='red', opacity=RealNumber('.5'))
... + parametric_plot3d([t,x,s], (s,-Integer(1),Integer(1)), (t,-Integer(1),Integer(1)),
... color='green', opacity=RealNumber('.7')))
>>> sp = animate([sphere_and_plane(x)
... for x in sxrange(-Integer(1), Integer(1), RealNumber('.3'))])
>>> sp[Integer(0)] # first frame
Graphics3d Object
>>> sp[-Integer(1)] # last frame
Graphics3d Object
>>> sp.show() # long time # optional -- ImageMagick
>>> (x,y,z) = SR.var("x,y,z")
>>> def frame(t):
... return implicit_plot3d((x**Integer(2) + y**Integer(2) + z**Integer(2)),
... (x, -Integer(2), Integer(2)), (y, -Integer(2), Integer(2)), (z, -Integer(2), Integer(2)),
... plot_points=Integer(60), contour=[Integer(1),Integer(3),Integer(5)],
... region=lambda x,y,z: x<=t or y>=t or z<=t)
>>> a = animate([frame(t) for t in srange(RealNumber('.01'), RealNumber('1.5'), RealNumber('.2'))])
>>> a[Integer(0)] # long time
Graphics3d Object
>>> a.show() # long time # optional -- ImageMagick
s,t = SR.var("s,t") 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)) sp = animate([sphere_and_plane(x) for x in sxrange(-1, 1, .3)]) sp[0] # first frame sp[-1] # last frame sp.show() # long time # optional -- ImageMagick (x,y,z) = SR.var("x,y,z") 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) a = animate([frame(t) for t in srange(.01, 1.5, .2)]) a[0] # long time a.show() # long time # 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 = SR.var("t")
sage: a = animate((sin(c*pi*t) for c in sxrange(1, 2, .2)))
sage: a.show() # long time # optional -- ImageMagick
>>> from sage.all import *
>>> t = SR.var("t")
>>> a = animate((sin(c*pi*t) for c in sxrange(Integer(1), Integer(2), RealNumber('.2'))))
>>> a.show() # long time # optional -- ImageMagick
t = SR.var("t") a = animate((sin(c*pi*t) for c in sxrange(1, 2, .2))) a.show() # long time # 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
Joshua Campbell (2020): interactive animation via Three.js viewer
REFERENCES:
- class sage.plot.animate.APngAssembler(out, num_frames, num_plays=0, delay=200, delay_denominator=100)[source]¶
Bases:
object
Build 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 writtennum_frames
– the number of frames in the animationnum_plays
– how often to iterate, 0 means infinitelydelay
– numerator of the delay fraction in secondsdelay_denominator
– denominator of the delay in seconds
EXAMPLES:
sage: from sage.plot.animate import APngAssembler sage: x = SR.var("x") 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'
>>> from sage.all import * >>> from sage.plot.animate import APngAssembler >>> x = SR.var("x") >>> def assembleAPNG(): ... a = animate([sin(x + float(k)) for k in srange(Integer(0),Integer(2)*pi,RealNumber('0.7'))], ... xmin=Integer(0), xmax=Integer(2)*pi, figsize=[Integer(2),Integer(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=Integer(10)*i + Integer(10)) ... return outfile >>> assembleAPNG() # long time '...png'
from sage.plot.animate import APngAssembler x = SR.var("x") 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 assembleAPNG() # long time
- add_frame(pngfile, delay=None, delay_denominator=None)[source]¶
Add a single frame to the APNG file.
INPUT:
pngfile
– file name of the PNG file with data for this framedelay
– numerator of the delay fraction in secondsdelay_denominator
– denominator of the delay in seconds
If the delay is not specified, the default from the constructor applies.
- magic = b'\x89PNG\r\n\x1a\n'¶
- mustmatch = frozenset({b'IHDR', b'PLTE', b'bKGD', b'cHRM', b'gAMA', b'pHYs', b'sBIT', b'tRNS'})¶
- set_default(pngfile)[source]¶
Add 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
- class sage.plot.animate.Animation(v=None, **kwds)[source]¶
Bases:
WithEqualityById
,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, thenmake_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., usefigsize
to adjust the resolution and aspect ratio.
EXAMPLES:
sage: x = SR.var("x") 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: print(a) Animation with 21 frames sage: print(a[:5]) Animation with 5 frames sage: a.show() # long time # optional -- ImageMagick sage: a[:5].show() # long time # optional -- ImageMagick
>>> from sage.all import * >>> x = SR.var("x") >>> a = animate([sin(x + float(k)) for k in srange(Integer(0), Integer(2)*pi, RealNumber('0.3'))], ... xmin=Integer(0), xmax=Integer(2)*pi, figsize=[Integer(2),Integer(1)]) >>> print(a) Animation with 21 frames >>> print(a[:Integer(5)]) Animation with 5 frames >>> a.show() # long time # optional -- ImageMagick >>> a[:Integer(5)].show() # long time # optional -- ImageMagick
x = SR.var("x") a = animate([sin(x + float(k)) for k in srange(0, 2*pi, 0.3)], xmin=0, xmax=2*pi, figsize=[2,1]) print(a) print(a[:5]) a.show() # long time # optional -- ImageMagick a[:5].show() # long time # 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: 0, which means to iterate forever). To iterate 4 times with half a second between each frame:sage: a.show(delay=50, iterations=4) # long time # optional -- ImageMagick
>>> from sage.all import * >>> a.show(delay=Integer(50), iterations=Integer(4)) # long time # optional -- ImageMagick
a.show(delay=50, iterations=4) # long time # 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() # long time # optional -- ImageMagick sage: show(L)
>>> from sage.all import * >>> step = RealNumber('0.1') >>> L = Graphics() >>> v = [] >>> for i in srange(Integer(0), Integer(1), step): ... L += line([(i,i**Integer(2)),(i+step,(i+step)**Integer(2))], rgbcolor=(Integer(1),Integer(0),Integer(0)), thickness=Integer(2)) ... v.append(L) >>> a = animate(v, xmin=Integer(0), ymin=Integer(0)) >>> a.show() # long time # optional -- ImageMagick >>> show(L)
step = 0.1 L = Graphics() v = [] 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) a = animate(v, xmin=0, ymin=0) a.show() # long time # optional -- ImageMagick show(L)
- apng(savefile=None, show_path=False, delay=20, iterations=0)[source]¶
Create 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 framessavefile
– file that the animated gif gets saved toiterations
– 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: x = SR.var("x") 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(show_path=True) # long time Animation saved to ....png. 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.
>>> from sage.all import * >>> x = SR.var("x") >>> a = animate([sin(x + float(k)) ... for k in srange(Integer(0),Integer(2)*pi,RealNumber('0.7'))], ... xmin=Integer(0), xmax=Integer(2)*pi, figsize=[Integer(2),Integer(1)]) >>> dir = tmp_dir() >>> a.apng(show_path=True) # long time Animation saved to ....png. >>> a.apng(savefile=dir + 'my_animation.png', delay=Integer(35), iterations=Integer(3)) # long time >>> a.apng(savefile=dir + 'my_animation.png', show_path=True) # long time Animation saved to .../my_animation.png.
x = SR.var("x") a = animate([sin(x + float(k)) for k in srange(0,2*pi,0.7)], xmin=0, xmax=2*pi, figsize=[2,1]) dir = tmp_dir() a.apng(show_path=True) # long time a.apng(savefile=dir + 'my_animation.png', delay=35, iterations=3) # long time a.apng(savefile=dir + 'my_animation.png', show_path=True) # long time
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
>>> from sage.all import * >>> a = animate([plot(sin(x), (x, Integer(0), k)) ... for k in range(Integer(1),Integer(4))], ... ymin=-Integer(1), ymax=Integer(1), aspect_ratio=Integer(1), figsize=[Integer(2),Integer(1)]) >>> a.apng() # long time Traceback (most recent call last): ... ValueError: Chunk IHDR mismatch
a = animate([plot(sin(x), (x, 0, k)) for k in range(1,4)], ymin=-1, ymax=1, aspect_ratio=1, figsize=[2,1]) a.apng() # long time
- ffmpeg(savefile=None, show_path=False, output_format=None, ffmpeg_options='', delay=None, iterations=0, pix_fmt='rgb24')[source]¶
Return a movie showing an animation composed from rendering the frames in
self
.This method will only work if
ffmpeg
is installed. See https://www.ffmpeg.org for information aboutffmpeg
.INPUT:
savefile
– file that the mpeg gets saved to
Warning
This will overwrite
savefile
if it already exists.show_path
– boolean (default:False
); ifTrue
, print the path to the saved fileoutput_format
– string (default:None
); format and suffix to use for the video. This may be'mpg'
,'mpeg'
,'avi'
,'gif'
, or any other format thatffmpeg
can handle. If this isNone
and the user specifiessavefile
with a suffix, saysavefile='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 ffmpegdelay
– 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 requiresffmpeg
version 0.9 or later. For older versions, setiterations=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. Setpix_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. Usesage.misc.verbose.set_verbose()
withlevel=1
to see additional output.EXAMPLES:
sage: x = SR.var("x") 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') # long time # optional -- FFmpeg sage: a.ffmpeg(savefile=td + 'new.avi') # long time # optional -- FFmpeg sage: a.ffmpeg(savefile=td + 'new.gif') # long time # optional -- FFmpeg sage: a.ffmpeg(savefile=td + 'new.mpg', show_path=True) # long time # optional -- FFmpeg Animation saved to .../new.mpg.
>>> from sage.all import * >>> x = SR.var("x") >>> a = animate([sin(x + float(k)) ... for k in srange(Integer(0), Integer(2)*pi, RealNumber('0.7'))], ... xmin=Integer(0), xmax=Integer(2)*pi, ymin=-Integer(1), ymax=Integer(1), figsize=[Integer(2),Integer(1)]) >>> td = tmp_dir() >>> a.ffmpeg(savefile=td + 'new.mpg') # long time # optional -- FFmpeg >>> a.ffmpeg(savefile=td + 'new.avi') # long time # optional -- FFmpeg >>> a.ffmpeg(savefile=td + 'new.gif') # long time # optional -- FFmpeg >>> a.ffmpeg(savefile=td + 'new.mpg', show_path=True) # long time # optional -- FFmpeg Animation saved to .../new.mpg.
x = SR.var("x") 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]) td = tmp_dir() a.ffmpeg(savefile=td + 'new.mpg') # long time # optional -- FFmpeg a.ffmpeg(savefile=td + 'new.avi') # long time # optional -- FFmpeg a.ffmpeg(savefile=td + 'new.gif') # long time # optional -- FFmpeg a.ffmpeg(savefile=td + 'new.mpg', show_path=True) # long time # optional -- FFmpeg
Note
If ffmpeg is not installed, you will get an error message like this:
FeatureNotPresentError: ffmpeg is not available. Executable 'ffmpeg' not found on PATH. Further installation instructions might be available at https://www.ffmpeg.org/.
- gif(delay=20, savefile=None, iterations=0, show_path=False, use_ffmpeg=False)[source]¶
Return 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
magick/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 Imagemagick if it is present. If this can’t find ImageMagick or ifuse_ffmpeg
is True, then it usesffmpeg
instead.INPUT:
delay
– (default: 20) delay in hundredths of a second between framessavefile
– file that the animated gif gets saved toiterations
– 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 fileuse_ffmpeg
– boolean (default:False
); ifTrue
, use ‘ffmpeg’ by default instead of ImageMagick
If
savefile
is not specified: in notebook mode, display the animation; otherwise, save it to a default file name.EXAMPLES:
sage: x = SR.var("x") 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', # long time # optional -- ImageMagick ....: delay=35, iterations=3) sage: with open(td + 'my_animation.gif', 'rb') as f: # long time # optional -- ImageMagick ....: print(b'GIF8' in f.read()) True sage: a.gif(savefile=td + 'my_animation.gif', # long time # optional -- ImageMagick ....: show_path=True) Animation saved to .../my_animation.gif. sage: a.gif(savefile=td + 'my_animation_2.gif', # long time # optional -- FFmpeg ....: show_path=True, use_ffmpeg=True) Animation saved to .../my_animation_2.gif.
>>> from sage.all import * >>> x = SR.var("x") >>> a = animate([sin(x + float(k)) ... for k in srange(Integer(0),Integer(2)*pi,RealNumber('0.7'))], ... xmin=Integer(0), xmax=Integer(2)*pi, ymin=-Integer(1), ymax=Integer(1), figsize=[Integer(2),Integer(1)]) >>> td = tmp_dir() >>> a.gif() # not tested >>> a.gif(savefile=td + 'my_animation.gif', # long time # optional -- ImageMagick ... delay=Integer(35), iterations=Integer(3)) >>> with open(td + 'my_animation.gif', 'rb') as f: # long time # optional -- ImageMagick ... print(b'GIF8' in f.read()) True >>> a.gif(savefile=td + 'my_animation.gif', # long time # optional -- ImageMagick ... show_path=True) Animation saved to .../my_animation.gif. >>> a.gif(savefile=td + 'my_animation_2.gif', # long time # optional -- FFmpeg ... show_path=True, use_ffmpeg=True) Animation saved to .../my_animation_2.gif.
x = SR.var("x") 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]) td = tmp_dir() a.gif() # not tested a.gif(savefile=td + 'my_animation.gif', # long time # optional -- ImageMagick delay=35, iterations=3) with open(td + 'my_animation.gif', 'rb') as f: # long time # optional -- ImageMagick print(b'GIF8' in f.read()) a.gif(savefile=td + 'my_animation.gif', # long time # optional -- ImageMagick show_path=True) a.gif(savefile=td + 'my_animation_2.gif', # long time # optional -- FFmpeg show_path=True, use_ffmpeg=True)
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)[source]¶
Return a
sage.plot.multigraphics.GraphicsArray
with plots of the frames of this animation, using the given number of columns. The frames must be acceptable inputs forsage.plot.multigraphics.GraphicsArray
.EXAMPLES:
sage: # needs sage.schemes sage: E = EllipticCurve('37a') sage: v = [E.change_ring(GF(p)).plot(pointsize=30) ....: for p in [97, 101, 103]] sage: a = animate(v, xmin=0, ymin=0, axes=False) sage: print(a) Animation with 3 frames sage: a.show() # optional -- ImageMagick
>>> from sage.all import * >>> # needs sage.schemes >>> E = EllipticCurve('37a') >>> v = [E.change_ring(GF(p)).plot(pointsize=Integer(30)) ... for p in [Integer(97), Integer(101), Integer(103)]] >>> a = animate(v, xmin=Integer(0), ymin=Integer(0), axes=False) >>> print(a) Animation with 3 frames >>> a.show() # optional -- ImageMagick
# needs sage.schemes E = EllipticCurve('37a') v = [E.change_ring(GF(p)).plot(pointsize=30) for p in [97, 101, 103]] a = animate(v, xmin=0, ymin=0, axes=False) print(a) a.show() # optional -- ImageMagick
Modify the default arrangement of array:
sage: g = a.graphics_array(); print(g) # needs sage.schemes Graphics Array of size 1 x 3 sage: g.show(figsize=[6,3]) # needs sage.schemes
>>> from sage.all import * >>> g = a.graphics_array(); print(g) # needs sage.schemes Graphics Array of size 1 x 3 >>> g.show(figsize=[Integer(6),Integer(3)]) # needs sage.schemes
g = a.graphics_array(); print(g) # needs sage.schemes g.show(figsize=[6,3]) # needs sage.schemes
Specify different arrangement of array and save it with a given file name:
sage: g = a.graphics_array(ncols=2); print(g) # needs sage.schemes Graphics Array of size 2 x 2 sage: f = tmp_filename(ext='.png'); print(f) # needs sage.schemes ...png sage: g.save(f) # needs sage.schemes
>>> from sage.all import * >>> g = a.graphics_array(ncols=Integer(2)); print(g) # needs sage.schemes Graphics Array of size 2 x 2 >>> f = tmp_filename(ext='.png'); print(f) # needs sage.schemes ...png >>> g.save(f) # needs sage.schemes
g = a.graphics_array(ncols=2); print(g) # needs sage.schemes f = tmp_filename(ext='.png'); print(f) # needs sage.schemes g.save(f) # needs sage.schemes
Frames can be specified as a generator too; it is internally converted to a list:
sage: t = SR.var("t") sage: b = animate((plot(sin(c*pi*t)) for c in sxrange(1,2,.2))) sage: g = b.graphics_array() sage: print(g) Graphics Array of size 2 x 3
>>> from sage.all import * >>> t = SR.var("t") >>> b = animate((plot(sin(c*pi*t)) for c in sxrange(Integer(1),Integer(2),RealNumber('.2')))) >>> g = b.graphics_array() >>> print(g) Graphics Array of size 2 x 3
t = SR.var("t") b = animate((plot(sin(c*pi*t)) for c in sxrange(1,2,.2))) g = b.graphics_array() print(g)
- interactive(**kwds)[source]¶
Create an interactive depiction of the animation.
INPUT:
**kwds
– any of the viewing options accepted by show() are valid as keyword arguments to this function and they will behave in the same way. Those that are animation-related and recognized by the Three.js viewer are:animate
,animation_controls
,auto_play
,delay
, andloop
.
OUTPUT: a 3D graphics object which, by default, will use the Three.js viewer
EXAMPLES:
sage: x = SR.var("x") sage: frames = [point3d((sin(x), cos(x), x)) ....: for x in (0, pi/16, .., 2*pi)] sage: animate(frames).interactive(online=True) Graphics3d Object
>>> from sage.all import * >>> x = SR.var("x") >>> frames = [point3d((sin(x), cos(x), x)) ... for x in (ellipsis_iter(Integer(0), pi/Integer(16),Ellipsis, Integer(2)*pi))] >>> animate(frames).interactive(online=True) Graphics3d Object
x = SR.var("x") frames = [point3d((sin(x), cos(x), x)) for x in (0, pi/16, .., 2*pi)] animate(frames).interactive(online=True)
Works with frames that are 2D or 3D graphics objects or convertible to 2D or 3D graphics objects via a
plot
orplot3d
method:sage: frames = [dodecahedron(), circle(center=(0, 0), radius=1), x^2] sage: animate(frames).interactive(online=True, delay=100) Graphics3d Object
>>> from sage.all import * >>> frames = [dodecahedron(), circle(center=(Integer(0), Integer(0)), radius=Integer(1)), x**Integer(2)] >>> animate(frames).interactive(online=True, delay=Integer(100)) Graphics3d Object
frames = [dodecahedron(), circle(center=(0, 0), radius=1), x^2] animate(frames).interactive(online=True, delay=100)
See also
- make_image(frame, filename, **kwds)[source]¶
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 issage.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 = SR.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
>>> from sage.all import * >>> from sage.plot.animate import Animation >>> class MyAnimation(Animation): ... def make_image(self, frame, filename, **kwds): ... P = parametric_plot(frame[Integer(0)], frame[Integer(1)], **frame[Integer(2)]) ... P.save_image(filename, **kwds) >>> t = SR.var("t") >>> x = lambda t: cos(t) >>> y = lambda n,t: sin(t)/n >>> B = MyAnimation([([x(t), y(i+Integer(1),t)], (t,Integer(0),Integer(1)), ... {'color':Color((Integer(1),Integer(0),i/Integer(4))), 'aspect_ratio':Integer(1), 'ymax':Integer(1)}) ... for i in range(Integer(4))]) >>> d = B.png(); v = os.listdir(d); v.sort(); v # long time ['00000000.png', '00000001.png', '00000002.png', '00000003.png'] >>> B.show() # not tested >>> 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) >>> B = MyAnimation([graphs.CompleteGraph(n) ... for n in range(Integer(7),Integer(11))], figsize=Integer(5)) >>> d = B.png() >>> v = os.listdir(d); v.sort(); v ['00000000.png', '00000001.png', '00000002.png', '00000003.png'] >>> B.show() # not tested
from sage.plot.animate import Animation class MyAnimation(Animation): def make_image(self, frame, filename, **kwds): P = parametric_plot(frame[0], frame[1], **frame[2]) P.save_image(filename, **kwds) t = SR.var("t") x = lambda t: cos(t) y = lambda n,t: sin(t)/n 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)]) d = B.png(); v = os.listdir(d); v.sort(); v # long time B.show() # not tested 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) B = MyAnimation([graphs.CompleteGraph(n) for n in range(7,11)], figsize=5) d = B.png() v = os.listdir(d); v.sort(); v B.show() # not tested
- png(dir=None)[source]¶
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 anddir
isNone
, just return the directory in which they are stored.When
dir
is other thanNone
, force re-rendering of frames.INPUT:
dir
– (default:None
) directory in which to store frames; in this case, a temporary directory will be created for storing the frames
OUTPUT: absolute path to the directory containing the PNG images
EXAMPLES:
sage: x = SR.var("x") 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']
>>> from sage.all import * >>> x = SR.var("x") >>> a = animate([plot(x**Integer(2) + n) for n in range(Integer(4))], ymin=Integer(0), ymax=Integer(4)) >>> d = a.png(); v = os.listdir(d); v.sort(); v # long time ['00000000.png', '00000001.png', '00000002.png', '00000003.png']
x = SR.var("x") a = animate([plot(x^2 + n) for n in range(4)], ymin=0, ymax=4) d = a.png(); v = os.listdir(d); v.sort(); v # long time
- save(filename=None, show_path=False, use_ffmpeg=False, **kwds)[source]¶
Save this animation.
INPUT:
filename
– (default:None
) name of save fileshow_path
– boolean (default:False
); if True, print the path to the saved fileuse_ffmpeg
– boolean (default:False
); ifTrue
, use ‘ffmpeg’ by default instead of ImageMagick 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 ‘.html’, save aninteractive()
version of the animation to an HTML file that uses the Three.js viewer. 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. sobj and HTML files can be saved with no extra software installed.
EXAMPLES:
sage: x = SR.var("x") 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') # long time # optional -- ImageMagick sage: a.save(td + 'wave.gif', show_path=True) # long time # optional -- ImageMagick Animation saved to file .../wave.gif. sage: a.save(td + 'wave.avi', show_path=True) # long time # 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. sage: a.save(td + 'wave0.html', online=True) sage: a.save(td + 'wave1.html', show_path=True, online=True) Animation saved to file .../wave1.html.
>>> from sage.all import * >>> x = SR.var("x") >>> a = animate([sin(x + float(k)) ... for k in srange(Integer(0), Integer(2)*pi, RealNumber('0.7'))], ... xmin=Integer(0), xmax=Integer(2)*pi, ymin=-Integer(1), ymax=Integer(1), figsize=[Integer(2),Integer(1)]) >>> td = tmp_dir() >>> a.save() # not tested >>> a.save(td + 'wave.gif') # long time # optional -- ImageMagick >>> a.save(td + 'wave.gif', show_path=True) # long time # optional -- ImageMagick Animation saved to file .../wave.gif. >>> a.save(td + 'wave.avi', show_path=True) # long time # optional -- FFmpeg Animation saved to file .../wave.avi. >>> a.save(td + 'wave0.sobj') >>> a.save(td + 'wave1.sobj', show_path=True) Animation saved to file .../wave1.sobj. >>> a.save(td + 'wave0.html', online=True) >>> a.save(td + 'wave1.html', show_path=True, online=True) Animation saved to file .../wave1.html.
x = SR.var("x") 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]) td = tmp_dir() a.save() # not tested a.save(td + 'wave.gif') # long time # optional -- ImageMagick a.save(td + 'wave.gif', show_path=True) # long time # optional -- ImageMagick a.save(td + 'wave.avi', show_path=True) # long time # optional -- FFmpeg a.save(td + 'wave0.sobj') a.save(td + 'wave1.sobj', show_path=True) a.save(td + 'wave0.html', online=True) a.save(td + 'wave1.html', show_path=True, online=True)
- show(delay=None, iterations=None, **kwds)[source]¶
Show this animation immediately.
This method attempts to display the graphics immediately, without waiting for the currently running code (if any) to return to the command line. Be careful, calling it from within a loop will potentially launch a large number of external viewer programs.
INPUT:
delay
– (default: 20) delay in hundredths of a second between framesiterations
– integer (default: 0); number of iterations of animation. If 0, loop forever.format
– (default: gif) format to use for output. Currently supported formats are: gif, ogg, webm, mp4, flash, matroska, avi, wmv, quicktime.
OUTPUT:
This method does not return anything. Use
save()
if you want to save the figure as an image.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
magick/convert
command) is installed.See also the
ffmpeg()
method.EXAMPLES:
sage: x = SR.var("x") 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() # long time # optional -- ImageMagick
>>> from sage.all import * >>> x = SR.var("x") >>> a = animate([sin(x + float(k)) ... for k in srange(Integer(0),Integer(2)*pi,RealNumber('0.7'))], ... xmin=Integer(0), xmax=Integer(2)*pi, figsize=[Integer(2),Integer(1)]) >>> a.show() # long time # optional -- ImageMagick
x = SR.var("x") a = animate([sin(x + float(k)) for k in srange(0,2*pi,0.7)], xmin=0, xmax=2*pi, figsize=[2,1]) a.show() # long time # optional -- ImageMagick
The preceding will loop the animation forever. If you want to show only three iterations instead:
sage: a.show(iterations=3) # long time # optional -- ImageMagick
>>> from sage.all import * >>> a.show(iterations=Integer(3)) # long time # optional -- ImageMagick
a.show(iterations=3) # long time # optional -- ImageMagick
To put a half-second delay between frames:
sage: a.show(delay=50) # long time # optional -- ImageMagick
>>> from sage.all import * >>> a.show(delay=Integer(50)) # long time # optional -- ImageMagick
a.show(delay=50) # long time # optional -- ImageMagick
You can also make use of the HTML5 video element in the Sage Notebook:
sage: # long time, optional -- FFmpeg sage: a.show(format='ogg') sage: a.show(format='webm') sage: a.show(format='mp4') sage: a.show(format='webm', iterations=1)
>>> from sage.all import * >>> # long time, optional -- FFmpeg >>> a.show(format='ogg') >>> a.show(format='webm') >>> a.show(format='mp4') >>> a.show(format='webm', iterations=Integer(1))
# long time, optional -- FFmpeg a.show(format='ogg') a.show(format='webm') a.show(format='mp4') a.show(format='webm', iterations=1)
Other backends may support other file formats as well:
sage: # long time, optional -- FFmpeg sage: a.show(format='flash') sage: a.show(format='matroska') sage: a.show(format='avi') sage: a.show(format='wmv') sage: a.show(format='quicktime')
>>> from sage.all import * >>> # long time, optional -- FFmpeg >>> a.show(format='flash') >>> a.show(format='matroska') >>> a.show(format='avi') >>> a.show(format='wmv') >>> a.show(format='quicktime')
# long time, optional -- FFmpeg a.show(format='flash') a.show(format='matroska') a.show(format='avi') a.show(format='wmv') a.show(format='quicktime')
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)[source]¶
Animate a list of frames by creating a
sage.plot.animate.Animation
object.EXAMPLES:
sage: t = SR.var("t") sage: a = animate((cos(c*pi*t) for c in sxrange(1, 2, .2))) sage: a.show() # long time # optional -- ImageMagick
>>> from sage.all import * >>> t = SR.var("t") >>> a = animate((cos(c*pi*t) for c in sxrange(Integer(1), Integer(2), RealNumber('.2')))) >>> a.show() # long time # optional -- ImageMagick
t = SR.var("t") a = animate((cos(c*pi*t) for c in sxrange(1, 2, .2))) a.show() # long time # optional -- ImageMagick
See also
sage.plot.animate
for more examples.