Draw cycloid animation using matplotlib.animation.ArtistAnimation


The result is:
Draw cycloid animation using matplotlib.animation.ArtistAnimation

In this page, an animation of cycloid is generated using ArtistAnimation function of matplotlib.animation.
The animation of cycloid is already generated in "Draw cycloid animation using matplotlib.animation.FuncAnimation".
Here, an altanative way for drawing animations that is matplotlib.animation.ArtistAnimation is used.
I think ArtistAnimation is easier to understand and write the code than FucAnimation.
In [1]:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
In [2]:
# Config to put the animation on the jupyter notebook
plt.rcParams['animation.html'] = 'html5'
The documents of the matplotlib.animation.ArtistAnimation
In [3]:
animation.ArtistAnimation?
Init signature: animation.ArtistAnimation(fig, artists, *args, **kwargs)
Docstring:     
Animation using a fixed set of `Artist` objects.

Before creating an instance, all plotting should have taken place
and the relevant artists saved.


Parameters
----------
fig : matplotlib.figure.Figure
   The figure object that is used to get draw, resize, and any
   other needed events.

artists : list
    Each list entry a collection of artists that represent what
    needs to be enabled on each frame. These will be disabled for
    other frames.

interval : number, optional
   Delay between frames in milliseconds.  Defaults to 200.

repeat_delay : number, optional
    If the animation in repeated, adds a delay in milliseconds
    before repeating the animation.  Defaults to `None`.

repeat : bool, optional
    Controls whether the animation should repeat when the sequence
    of frames is completed. Defaults to `True`.

blit : bool, optional
    Controls whether blitting is used to optimize drawing.  Defaults
    to `False`.
File:           ****/lib/site-packages/matplotlib/animation.py
Type:           type
In [4]:
animation.ArtistAnimation??
Init signature: animation.ArtistAnimation(fig, artists, *args, **kwargs)
Source:        
class ArtistAnimation(TimedAnimation):
    '''Animation using a fixed set of `Artist` objects.

    Before creating an instance, all plotting should have taken place
    and the relevant artists saved.


    Parameters
    ----------
    fig : matplotlib.figure.Figure
       The figure object that is used to get draw, resize, and any
       other needed events.

    artists : list
        Each list entry a collection of artists that represent what
        needs to be enabled on each frame. These will be disabled for
        other frames.

    interval : number, optional
       Delay between frames in milliseconds.  Defaults to 200.

    repeat_delay : number, optional
        If the animation in repeated, adds a delay in milliseconds
        before repeating the animation.  Defaults to `None`.

    repeat : bool, optional
        Controls whether the animation should repeat when the sequence
        of frames is completed. Defaults to `True`.

    blit : bool, optional
        Controls whether blitting is used to optimize drawing.  Defaults
        to `False`.

    '''
    def __init__(self, fig, artists, *args, **kwargs):
        # Internal list of artists drawn in the most recent frame.
        self._drawn_artists = []

        # Use the list of artists as the framedata, which will be iterated
        # over by the machinery.
        self._framedata = artists
        TimedAnimation.__init__(self, fig, *args, **kwargs)

    def _init_draw(self):
        # Make all the artists involved in *any* frame invisible
        figs = set()
        for f in self.new_frame_seq():
            for artist in f:
                artist.set_visible(False)
                artist.set_animated(self._blit)
                # Assemble a list of unique figures that need flushing
                if artist.get_figure() not in figs:
                    figs.add(artist.get_figure())

        # Flush the needed figures
        for fig in figs:
            fig.canvas.draw_idle()

    def _pre_draw(self, framedata, blit):
        '''
        Clears artists from the last frame.
        '''
        if blit:
            # Let blit handle clearing
            self._blit_clear(self._drawn_artists, self._blit_cache)
        else:
            # Otherwise, make all the artists from the previous frame invisible
            for artist in self._drawn_artists:
                artist.set_visible(False)

    def _draw_frame(self, artists):
        # Save the artists that were passed in as framedata for the other
        # steps (esp. blitting) to use.
        self._drawn_artists = artists

        # Make all the artists from the current frame visible
        for artist in artists:
            artist.set_visible(True)
File:           ****/lib/site-packages/matplotlib/animation.py
Type:           type
In [5]:
# radius of the circle
R = 1
In [6]:
def circle(a, b, r):
    # (a,b): the center of the circle
    # r: the radius of the circle
    # T: The number of the segments
    T = 100
    x, y = [0]*T, [0]*T
    for i,theta in enumerate(np.linspace(0,2*np.pi,T)):
        x[i] = a + r*np.cos(theta)
        y[i] = b + r*np.sin(theta)
    return x, y

# Calculate the cycloid line
thetas = np.linspace(0,4*np.pi,100)
cycloid_x = R*(thetas-np.sin(thetas))
cycloid_y = R*(1-np.cos(thetas))
cycloid_c = R*thetas
In [7]:
fig = plt.figure()

lns = []
trans = plt.axes().transAxes
for i in range(len(thetas)):
    x,y = circle(cycloid_c[i], R, R)
    ln1, = plt.plot(x, y, 'g-', lw=2)
    ln2, = plt.plot(cycloid_x[:i+1] ,cycloid_y[:i+1], 'r-', lw=2)
    ln3, = plt.plot(cycloid_x[i], cycloid_y[i], 'bo', markersize=4)
    ln4, = plt.plot([cycloid_c[i], cycloid_x[i]], [R,cycloid_y[i]], 'y-', lw=2)
    tx1  = plt.text(0.05, 0.8, r'$\theta$ = %.2f $\pi$' % (thetas[i]/np.pi), transform=trans)
    lns.append([ln1,ln2,ln3,ln4,tx1])
plt.xlim(0,15)
plt.ylim(0,3)
plt.xlabel('x')
plt.ylabel('y')
plt.grid()
plt.axes().set_aspect('equal')
The data structure is like this.
In [8]:
lns[:5]
Out[8]:
[[<matplotlib.lines.Line2D at 0x26a7debe0b8>,
  <matplotlib.lines.Line2D at 0x26a79695198>,
  <matplotlib.lines.Line2D at 0x26a7debec18>,
  <matplotlib.lines.Line2D at 0x26a7dc3c080>,
  <matplotlib.text.Text at 0x26a7dc330b8>],
 [<matplotlib.lines.Line2D at 0x26a7da69978>,
  <matplotlib.lines.Line2D at 0x26a7da69e10>,
  <matplotlib.lines.Line2D at 0x26a7ded1f98>,
  <matplotlib.lines.Line2D at 0x26a7ded9780>,
  <matplotlib.text.Text at 0x26a7ded9fd0>],
 [<matplotlib.lines.Line2D at 0x26a7dee1ac8>,
  <matplotlib.lines.Line2D at 0x26a7dee1cf8>,
  <matplotlib.lines.Line2D at 0x26a7dee75c0>,
  <matplotlib.lines.Line2D at 0x26a7dee7d68>,
  <matplotlib.text.Text at 0x26a7deec5f8>],
 [<matplotlib.lines.Line2D at 0x26a7def50f0>,
  <matplotlib.lines.Line2D at 0x26a7def5320>,
  <matplotlib.lines.Line2D at 0x26a7def5ba8>,
  <matplotlib.lines.Line2D at 0x26a7defa390>,
  <matplotlib.text.Text at 0x26a7defabe0>],
 [<matplotlib.lines.Line2D at 0x26a7df036d8>,
  <matplotlib.lines.Line2D at 0x26a7df03908>,
  <matplotlib.lines.Line2D at 0x26a7df0a1d0>,
  <matplotlib.lines.Line2D at 0x26a7df0a978>,
  <matplotlib.text.Text at 0x26a7df10208>]]
Then create animation
In [9]:
ani = animation.ArtistAnimation(fig, lns, interval=50)
In [10]:
ani.save('cycloid_ArtistAnimation.mp4',writer='ffmpeg')
ani.save('cycloid_ArtistAnimation.gif',writer='imagemagick')
In [11]:
ani
Out[11]: