Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Animation Ideas #12

Open
SonHyegang opened this issue May 21, 2022 · 1 comment
Open

Animation Ideas #12

SonHyegang opened this issue May 21, 2022 · 1 comment
Labels
documentation Improvements or additions to documentation help wanted Extra attention is needed

Comments

@SonHyegang
Copy link
Collaborator

SonHyegang commented May 21, 2022

Animation Creation Ideas

1. Using time.sleep() of the time module
2. Using os module
3. FuncAnimation

The first two ideas are very well known methods. I also tried to implement animation through two ideas, but it was not suitable for colab environment and it was difficult to write code friendly to pyplot module. I was able to check the technical documentation about FuncAnimation of the matplotlib module and solved the animation creation problem using that method.

If you have other ideas, please leave a comment.

matplotlib.animation

Inheritance Diagrams

image

Animation#

The easiest way to make a live animation in Matplotlib is to use one of the Animation classes.

Animation

A base class for Animations.

FuncAnimation

Makes an animation by repeatedly calling a function func.

ArtistAnimation

Animation using a fixed set of Artist objects.

In both cases it is critical to keep a reference to the instance object. The animation is advanced by a timer (typically from the host GUI framework) which the Animation object holds the only reference to. If you do not hold a reference to the Animation object, it (and hence the timers) will be garbage collected which will stop the animation.

To save an animation use Animation.save, Animation.to_html5_video, or Animation.to_jshtml.

See Helper Classes below for details about what movie formats are supported.

FuncAnimation#

The inner workings of FuncAnimation is more-or-less:

for d in frames:
    artists = func(d, *fargs)
    fig.canvas.draw_idle()
    fig.canvas.start_event_loop(interval)

with details to handle 'blitting' (to dramatically improve the live performance), to be non-blocking, not repeatedly start/stop the GUI event loop, handle repeats, multiple animated axes, and easily save the animation to a movie file.

'Blitting' is a standard technique in computer graphics. The general gist is to take an existing bit map (in our case a mostly rasterized figure) and then 'blit' one more artist on top. Thus, by managing a saved 'clean' bitmap, we can only re-draw the few artists that are changing at each frame and possibly save significant amounts of time. When we use blitting (by passing blit=True), the core loop of FuncAnimation gets a bit more complicated:

ax = fig.gca()

def update_blit(artists):
    fig.canvas.restore_region(bg_cache)
    for a in artists:
        a.axes.draw_artist(a)

    ax.figure.canvas.blit(ax.bbox)

artists = init_func()

for a in artists:
   a.set_animated(True)

fig.canvas.draw()
bg_cache = fig.canvas.copy_from_bbox(ax.bbox)

for f in frames:
    artists = func(f, *fargs)
    update_blit(artists)
    fig.canvas.start_event_loop(interval)

This is of course leaving out many details (such as updating the background when the figure is resized or fully re-drawn). However, this hopefully minimalist example gives a sense of how init_func and func are used inside of FuncAnimation and the theory of how 'blitting' works.

The expected signature on func and init_func is very simple to keep FuncAnimation out of your book keeping and plotting logic, but this means that the callable objects you pass in must know what artists they should be working on. There are several approaches to handling this, of varying complexity and encapsulation. The simplest approach, which works quite well in the case of a script, is to define the artist at a global scope and let Python sort things out. For example

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

fig, ax = plt.subplots()
xdata, ydata = [], []
ln, = plt.plot([], [], 'ro')

def init():
    ax.set_xlim(0, 2*np.pi)
    ax.set_ylim(-1, 1)
    return ln,

def update(frame):
    xdata.append(frame)
    ydata.append(np.sin(frame))
    ln.set_data(xdata, ydata)
    return ln,

ani = FuncAnimation(fig, update, frames=np.linspace(0, 2*np.pi, 128),
                    init_func=init, blit=True)
plt.show()

The second method is to use functools.partial to 'bind' artists to function. A third method is to use closures to build up the required artists and functions. A fourth method is to create a class.

@SonHyegang
Copy link
Collaborator Author

matplotlib.animation.FuncAnimation

class matplotlib.animation.FuncAnimation(fig, func, frames=None, init_func=None, fargs=None, save_count=None, *, cache_frame_data=True, **kwargs)[source]

Makes an animation by repeatedly calling a function func.

Note

You must store the created Animation in a variable that lives as long as the animation should run. Otherwise, the Animation object will be garbage-collected and the animation stops.

Parameters
figFigure

The figure object used to get needed events, such as draw or resize.

funccallable

The function to call at each frame. The first argument will be the next value in frames. Any additional positional arguments can be supplied via the fargs parameter.

The required signature is:

def func(frame, *fargs) -> iterable_of_artists

If blit == True, func must return an iterable of all artists that were modified or created. This information is used by the blitting algorithm to determine which parts of the figure have to be updated. The return value is unused if blit == False and may be omitted in that case.

framesiterable, int, generator function, or None, optional

Source of data to pass func and each frame of the animation

  • If an iterable, then simply use the values provided. If the iterable has a length, it will override the save_count kwarg.

  • If an integer, then equivalent to passing range(frames)

  • If a generator function, then must have the signature:

    def gen_function() -> obj
    
  • If None, then equivalent to passing itertools.count.

In all of these cases, the values in frames is simply passed through to the user-supplied func and thus can be of any type.

init_funccallable, optional

A function used to draw a clear frame. If not given, the results of drawing from the first item in the frames sequence will be used. This function will be called once before the first frame.

The required signature is:

def init_func() -> iterable_of_artists

If blit == True, init_func must return an iterable of artists to be re-drawn. This information is used by the blitting algorithm to determine which parts of the figure have to be updated. The return value is unused if blit == False and may be omitted in that case.

fargstuple or None, optional

Additional arguments to pass to each call to func.

save_countint, default: 100

Fallback for the number of values from frames to cache. This is only used if the number of frames cannot be inferred from frames, i.e. when it's an iterator without length or a generator.

intervalint, default: 200

Delay between frames in milliseconds.

repeat_delayint, default: 0

The delay in milliseconds between consecutive animation runs, if repeat is True.

repeatbool, default: True

Whether the animation repeats when the sequence of frames is completed.

blitbool, default: False

Whether blitting is used to optimize drawing. Note: when using blitting, any animated artists will be drawn according to their zorder; however, they will be drawn on top of any previous artists, regardless of their zorder.

cache_frame_databool, default: True

Whether frame data is cached. Disabling cache might be helpful when frames contain large objects.

__init__(fig, func, frames=None, init_func=None, fargs=None, save_count=None, *, cache_frame_data=True, **kwargs)[source]

Methods

__init__(fig, func[, frames, init_func, ...])

new_frame_seq()

Return a new sequence of frame information.

new_saved_frame_seq()

Return a new sequence of saved/cached frame information.

pause()

Pause the animation.

resume()

Resume the animation.

save(filename[, writer, fps, dpi, codec, ...])

Save the animation as a movie file by drawing every frame.

to_html5_video([embed_limit])

Convert the animation to an HTML5 <video> tag.

to_jshtml([fps, embed_frames, default_mode])

Generate HTML representation of the animation.

new_frame_seq()[source]

Return a new sequence of frame information.

new_saved_frame_seq()[source]

Return a new sequence of saved/cached frame information.

@SonHyegang SonHyegang added documentation Improvements or additions to documentation help wanted Extra attention is needed labels May 21, 2022
@SonHyegang SonHyegang changed the title Animation Creation Ideas Animation Ideas May 21, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

1 participant