You will see that the images of previous calls are still present by printing the number of images inside the plot
function,
print ( len(plt.gca().images) )
In your case this number will increase steadily, even though you delete the image, because it is still part of the axes and hence get redrawn every iteration.
It would be much better to draw the image once, and then only update its data.
class GoL():
# ...
def __init__(self, width, height, p=0.3):
self.width = width
self.height = height
self.matrix = np.random.choice(a=[1, 0], size=(width, height), p=[p, 1 - p])
self.im = plt.imshow(self.matrix)
# ....
def plot(self):
self.im.set_data(self.matrix)
plt.pause(0.05)
print len(plt.gca().images)
This will result in an animation at constant speed.
Concerning the question on where to improve the code, there are two things:
- use
matplotlib.animation.FuncAnimation
, as this is much more stable and lets you savely terminate the animation.
- use conditions on numpy arrays instead of looping over all pixels of the matrix.
Full code:
import matplotlib.pyplot as plt
import numpy as np
from scipy.ndimage import convolve
import matplotlib.animation as animation
class GoL():
KERNEL = np.array([[1, 1, 1],
[1, 0, 1],
[1, 1, 1]])
def __init__(self, width, height, p=0.3):
self.width = width
self.height = height
self.matrix = np.random.choice(a=[1, 0], size=(width, height), p=[p, 1 - p])
self.im = plt.imshow(self.matrix)
def play(self):
self.plot()
self.ani= animation.FuncAnimation(plt.gcf(), self.cycle, repeat=True, interval=50 )
plt.show()
def cycle(self, n):
c = self.eval()
new = np.zeros_like(self.matrix)
new[c == 3] = 1
new[c < 2] = 0
new[c > 3] = 0
new[(self.matrix == 1) & (c == 2)] = 1
self.matrix = new
self.plot()
def eval(self):
return convolve(self.matrix, self.KERNEL, mode='constant')
def plot(self):
self.im.set_data(self.matrix)
gol = GoL(width=100,height=100)
gol.play()