I am trying to animate some fractals in matplotlib using FuncAnimation.
When I have the blit set to False I get no errors: the code runs fine and generates a nice animation for me. However, when I set the blit to True, it gives me TypeError: 'Line2D' object is not iterable
. Does anyone know why this happens and how I can fix it?
I would like to take advantage of blitting as I am planning to animate a large family of fractals and just taking a small slice of them (64 different fractals) already takes noticeable computation time. I have a fast way to generate a matrix with columns containing the different fractals so I know computation time is spent trying to animate a bunch of plots without blitting.
In my example I am just animating the iteration of generating a fractal. This is a short and fast way of illustrating the error I am getting, not what I am actually trying to animate because otherwise I wouldn't care about the blitting.
Here is a minimal example that should run in a jupyter notebook if you have ffmpeg installed:
import numpy as np
import scipy as sp
import scipy.linalg as la
from matplotlib import pyplot as plt
from matplotlib.animation import FuncAnimation
from matplotlib import animation, rc
from IPython.display import HTML
%matplotlib inline
plt.rcParams['figure.figsize'] = [8,8]
plt.rcParams['animation.ffmpeg_path'] = "C:\\Program Files\\ffmpeg\\bin\\ffmpeg.exe" #<- CHANGE TO YOUR PATH TO FFMPEG or delete this line if the notebook knows where to find ffmpeg.
class IFS:
"""Represent an iterated function system used to generate a fractal."""
def __init__(self,start,f1,f2,reversef2=False):
self.points = start
self.f1 = f1
self.f2 = f2
self.reversef2 = reversef2
def iterate(self,iterations=1):
"""Perform iterations using the functions"""
for i in range(0,iterations):
if self.reversef2: #This is needed for the dragon curve
self.points = np.append(self.f1(self.points),self.f2(self.points)[::-1])
else: #However, you do not want to append in reverse when constructing the levyC curve
self.points = np.append(self.f1(self.points),self.f2(self.points))
def get_points(self):
return self.points
def dragon_ifs():
"""Return a dragon fractal generating IFS"""
def f1(z):
return (0.5+0.5j)*z
def f2(z):
return 1 - (0.5-0.5j)*z
return IFS(np.array([0,1]),f1,f2,reversef2=True)
#Animation
class UpdateFractal:
"""A class for animating an IFS by slowly iterating it"""
def __init__(self,ax,ifs):
self.ifs = ifs
self.line, = ax.plot([], [], 'k-',lw=2)
self.ax = ax
#set parameters
self.ax.axis([-1,2,-1,2])
def get_plot_points(self):
"""Get plottable X and Y values from the IFS points"""
points = self.ifs.get_points()
X = points.real
Y = points.imag
return X,Y
def init(self):
X,Y = self.get_plot_points()
self.line.set_data(X,Y)
return self.line
def __call__(self,i):
self.ifs.iterate()
X,Y = self.get_plot_points()
self.line.set_data(X,Y)
return self.line
fig, ax = plt.subplots()
dragon = dragon_ifs()
uf = UpdateFractal(ax,dragon)
anim = FuncAnimation(fig, uf, frames=np.arange(15), init_func=uf.init,
interval=200, blit=True)
#Show animation
HTML(anim.to_html5_video())
Side Note: I saw this also unanswered question: FuncAnimation not iterable and I think they may be facing the a similar problem. I noticed they have blit set to True and if I had enough reputation I would comment and ask them if setting blit to False "fixes" their problem.
As the error suggests, the return of the animating function needs to be an iterable.
Replace the line return self.line
by
return self.line,
(note the comma)