isaacsmith0427
isaacsmith0427

Reputation: 65

Trying to make an animation with matplotlib

This is my attempt at the animation I am trying to make:

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

fig, ax = plt.subplots(figsize=(10,10))
ax.set_xlim([0,1])
ax.set_ylim([0,1])

def animate(t):
    circle1 = plt.Circle((0.5, 0.5), 0.1*t, color='k',fill=False)
    circle2 = plt.Circle((0.5, 0.5), 0.16*t, color='deepskyblue',fill=False)
    circle3 = plt.Circle((0.5, 0.5), 0.22*t, color='deepskyblue',fill=False)
    circle4 = plt.Circle((0.5, 0.5), 0.28*t, color='deepskyblue',fill=False)
    circle5 = plt.Circle((0.5, 0.5), 0.34*t, color='deepskyblue',fill=False)
    circle6 = plt.Circle((0.5, 0.5), 0.40*t, color='deepskyblue',fill=False)
    circle7 = plt.Circle((0.5, 0.5), 0.46*t, color='k',fill=False)
    ax.add_patch(circle1)
    ax.add_patch(circle2)
    ax.add_patch(circle3)
    ax.add_patch(circle4)
    ax.add_patch(circle5)
    ax.add_patch(circle6)
    ax.add_patch(circle7)
    plt.scatter(.5,.5,color='k')
    plt.annotate('',(.5,.5-.1*t),(.5,.5),arrowprops={'width':1,'color':'k'})
    plt.annotate('',(.5+.46*t,.5),(.5,.5),arrowprops={'width':1,'color':'k'})
    plt.annotate('',(.5-.2*t,.5+.15*t),(.5,.5),arrowprops={'width':.5,'color':'red'})
    return ax

FuncAnimation(fig, animate, frames=np.arange(0.0, 1, .01), interval=10, blit=True, repeat=True)

I would like to get the following figure (which is what I get when I call animate(1)), but expanding from just a point to that size: Click to see figure.

I know that I am not supposed to return ax, because I get the error message TypeError: 'AxesSubplot' object is not iterable. I don't know, however, what animate should be returning. Any help you could give would be amazing!

Upvotes: 1

Views: 95

Answers (1)

Reti43
Reti43

Reputation: 9796

You are supposed to return a sequence of Artists, in this case

return circle1, circle2, circle3, circle4, circle5, circle6, circle7

But it seems you also want the annotations to get updated, so you'd have to return those as well.

text1 = plt.annotate('',(.5,.5-.1*t),(.5,.5),arrowprops={'width':1,'color':'k'})
# etc...
return circle1, ..., circle7, text1, text2, text3

A better way

Instead of having multiple names, such as circle1, circle2, etc, put all the Circle objects in a list and then access them with circles[index]. That way you can update the radius of the original objects instead of creating new ones in each animate call.

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


def create_circles():
    radii = np.arange(0.1, .47, 0.06)
    colors = ['k'] + ['deepskyblue'] * 5 + ['k']
    circles = [plt.Circle((0.5, 0.5), color=c, fill=False) for c in colors]
    for circle in circles:
        ax.add_patch(circle)
    return circles, radii

def create_annotations():
    widths = [1, 1, .5]
    colors = ['k', 'k', 'red']
    annotations = [plt.annotate('', (.5, .5), (.5, .5), arrowprops={'width': w, 'color': c})
                   for w, c in zip(widths, colors)]
    lengths = [(0, -.1), (+.46, 0), (-.2, .15)]
    return annotations, lengths


fig, ax = plt.subplots(figsize=(10,10))
ax.set_xlim([0,1])
ax.set_ylim([0,1])

center = [plt.scatter(.5, .5, color='k')]
circles, radii = create_circles()
annotations, lengths = create_annotations()

def animate(t):
    for circle, radius in zip(circles, radii):
        circle.set_radius(radius * t)
    for ann, (x, y) in zip(annotations, lengths):
        ann.xy = (.5+x*t, .5+y*t)
    return center + circles + annotations

FuncAnimation(fig, animate, frames=np.arange(0.0, 1, .01), interval=10, blit=True, repeat=True)

Upvotes: 1

Related Questions