Note this is a follow-up question of How to make an animation of a Lissajous curve;
My first idea was to edit my original question and ask for the animation, but I understand and respect SO way of operating. So the best is making another question.
I want to make an animation of the curve (where you incrementally draw it) with parametrization: x(t) = sin(3t) and y(y) = sin(4t) where t[0, 2pi].
For doing so I would add the code:
from matplotlib.animation import FuncAnimation
fig, ax = plt.subplots()
ln, = plt.plot([], [], 'b')
def init():
ax.set_xlim(-1, 1)
ax.set_ylim(-1, 1)
return ln,
def update(frame):
x.append(np.sin(4*frame))
y.append(np.sin(3*frame))
ln.set_data(x, y)
return ln,
ani = FuncAnimation(fig, update, frames=np.linspace(0, 2*np.pi, 128),
init_func=init, blit=True)
The problem is that with this code it doesn't draw the whole curve from scratch. What does is overdrawing it, getting overlapping.
How can I draw it from scratch (i.e. starting with white background)? I've been thinking about an if else but got nothing.
Thanks
EDIT
Let me show you the whole code:
%matplotlib notebook
import matplotlib.pyplot as plt
import math
import numpy as np
from matplotlib.animation import FuncAnimation
# set the minimum potential
rm = math.pow(2, 1 / 6)
t = np.linspace(-10, 10, 1000, endpoint = False)
x = []
y = []
for i in t: #TypeError 'int' object is not iterable
x_i = np.sin( 3 * i )
y_i = np.sin( 4 * i )
x.append(x_i)
y.append(y_i)
# set the title
plt.title('Plot sin(4t) Vs sin(3t)')
# set the labels of the graph
plt.xlabel('sin(3t)')
plt.ylabel('sin(4t)')
fig, ax = plt.subplots()
ln, = plt.plot([], [], 'b')
def init():
ax.set_xlim(-1, 1)
ax.set_ylim(-1, 1)
return ln,
def update(frame):
x.append(np.sin(4*frame))
y.append(np.sin(3*frame))
ln.set_data(x, y)
return ln,
ani = FuncAnimation(fig, update, frames=np.linspace(0, 2*np.pi, 128),
init_func=init, blit=True)
# display the graph
plt.show()
This is the image I get at the beginning (screenshot taken after approximately 1s after started running; that's why you see that funny line): https://imgur.com/a/bNoViDA. As you can see it doesn't start from scratch (i.e not from white background)
This is the plot I get at the end: https://imgur.com/a/WQHHUk9
I am seeking getting that ending point but drawing everything from scratch, without starting with the shown plot.
Related
I am trying to animate a patch.Rectangle object using matplotlib. I want the said object to move along a path.Arc.
A roundabout way to do this would be (approximately) :
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
import matplotlib.patches as mpat
fig, ax = plt.subplots()
ax.set(xlim=(0, 10), ylim=(0, 10))
# generate the patch
patch = mpat.Rectangle((5, 5), 1, 4)
patch.rotation_point = 'center'
# generate the path to follow
path_to_follow = mpat.Arc((5, 5), 2, 2)
ax.add_patch(path_to_follow)
def init():
patch.set(x=5, y=5)
ax.add_patch(patch)
return patch,
def animate(i, ax):
new_x = 5 + np.sin(np.radians(i)) - 0.5 # parametric form for the circle
new_y = 5 + np.cos(np.radians(i)) - 2
patch.set(x=new_x, y=new_y, angle=90-i)
return patch,
anim = animation.FuncAnimation(fig, animate,
init_func=init,
fargs=[ax],
frames=360,
interval=10,
blit=True)
plt.show()
The rectangle follows a circle, but a parametric one. Would it be possible to make it follow any path?
In other words, I would like to know if there are other simpler methods to do this (make my patch follow my path, here a circle), and if that could be generalized to other path.
Thanks in advance !
I searched into the matplotlib doc for a methods which gives the parametric form for a given path (but apparently there is not), or for a methods which directly move a patch along a path (obviously, there was not).
Here is one way to use matplotlib.path.Path to generate a path, whose vertices can be obtained using the method cleaned, to move a patch along it.
I have tried to showcase how blue and red colored Rectangles can be moved along a (blue) linear path and a (red) circular path, respectively:
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation, path
import matplotlib.patches as mpat
fig, ax = plt.subplots()
ax.set(xlim=(0, 10), ylim=(0, 10))
# generate a linear path
path1 = np.column_stack((np.arange(500)/50, np.arange(500)/50))
# generate a circular path
circle = path.Path.circle(center=(5, 5), radius=1)
path2 = circle.cleaned().vertices[:-3]
# create patches
patch1 = mpat.Rectangle((0, 0), 1, 3)
patch2 = mpat.Rectangle((0, 0), 1, 3, color='red', fill=None)
# plot path vertices
plt.scatter(x=path1[:, 0], y=path1[:, 1], s=2)
plt.scatter(x=path2[:, 0], y=path2[:, 1], color='red', s=2)
def init():
patch1.set(x=0, y=0)
patch2.set(x=5, y=6)
ax.add_patch(patch1)
ax.add_patch(patch2)
return [patch1, patch2]
def animate(i, ax):
j = i % 500 # path1 has shape (500, 2)
k = (i % 16) # path2 has shape (16, 2)
patch1.set(x=path1[j][0], y=path1[j][1], angle=-j)
patch2.set(x=path2[k][0], y=path2[k][1], angle=-k)
return [patch1, patch2]
anim = animation.FuncAnimation(fig, animate,
init_func=init,
fargs=[ax],
frames=360,
interval=100,
blit=True)
plt.show()
If your path is some collection of coordinates, you can not only translate the rectangle, but also compute the vector from one point to the next and update the rectangle angle accordingly. In the next example (mix of your code with mine), we generate from the beginning the path, but it could be instead live read from some external source.
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
import matplotlib.patches as mpat
# create some path with pairs of X and Y coordinates
t = np.linspace(0, 360, 361)
X = 5. * np.sin(np.radians(t))
Y = t * (t-360*2) / 8000 + 7.5
# create x and y lists to store the position pairs as they are plotted
x = [X[0],]
y = [Y[0],]
# plotting
fig, ax = plt.subplots()
ax.set(xlim=(-10, 10), ylim=(-10, 10))
patch = mpat.Rectangle((x[0], y[0]), 1, 3)
def init():
patch.set(x=x[0], y=y[0])
ax.plot(X, Y)
ax.add_patch(patch)
return patch,
def animate(i, ax):
new_x = X[i] # we are getting from pre-generated data,
new_y = Y[i] # but it could be some function, or even live external source
vx = new_x - x[-1] # calculate the vectors, which are used for angle
vy = new_y - y[-1]
x.append(new_x) # store for next iteration, so that we can calculate the vectors
y.append(new_y)
new_alfa = np.degrees(np.arctan2(vy, vx))
patch.set(x=new_x, y=new_y, angle = new_alfa)
return patch,
anim = animation.FuncAnimation(fig, animate,
init_func=init,
fargs=[ax],
frames=360,
interval=20,
blit=True)
plt.show()
Thanks a lot for your answers, here is the code I made (mixing the two answers) and which does exactly what I wanted, if it helps anyone :
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
import matplotlib.patches as mpat
fig, ax = plt.subplots()
ax.set(xlim=(-6, 6), ylim=(-6, 6))
# generate a circular path
circle = mpat.Arc((0, 0), 10, 10, theta1=20, theta2=220, color='green')
path = circle.get_transform().transform_path(circle.get_path()).cleaned().vertices[:-3] # get_path is not enough because of the transformation, so we apply to the path the same transformation as the circle has got from the identity circle
ax.add_patch(circle)
# create patch
patch = mpat.Rectangle((0, 0), 1, 4, color='red', fill=None)
patch.rotation_point = 'center'
# plot path vertices
plt.scatter(x=path[:, 0], y=path[:, 1], color='red', s=2)
shape = len(path)
def init():
patch.set(x=5-0.5, y=6-2) # we substract to get to the center
ax.add_patch(patch)
return [patch]
def animate(i, ax):
k = i % shape
new_x = path[k][0]
new_y = path[k][1]
vx = new_x - path[k-1][0]
vy = new_y - path[k-1][1]
patch.set(x=new_x-0.5, y=new_y-2, angle=np.degrees(np.arctan2(vy, vx) + 90))
return [patch]
anim = animation.FuncAnimation(fig, animate,
init_func=init,
fargs=[ax],
frames=360,
interval=200,
blit=True,
repeat=False)
plt.show()
To improve this, is anyone know how to increase the number of points given? In other words, increase the len of path to be more precise in moving the rectangle.
Thanks in advance !
I am trying to just have a simple life by getting a real-time plot functionality with blit=True but what I get is a plot with wrong x-axis limits and the plot changes when I maximize the plot window.
I want to have a plot of 50000 (say) points made in one go and then use funcanimation to call animate() to update the existing plot with set_data(x,y). Everything works fine if blit=False but I want to have blitting in my GUI. Please help with your thoughts. Attaching a short video for your reference along with the code.
I am pasting my code below:
Thanks in advance!
import time
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from random import randrange
fig = plt.figure(figsize=(6, 3))
varLen = 1000
x=[r for r in range(varLen-300)]
y=[randrange(0, 10) for r in range(varLen-300)]
y2=[10+randrange(0, 10) for r in range(varLen-300)]
# y=[]
# y2=[]
print(type(x))
ln, = plt.plot(x, y, '-')
ln2, = plt.plot(x, y2, '-')
def update(frame):
start=time.time()
global x, y, y2
x.append(x[-1]+1)
val=randrange(0, 10)
y.append(val)
y2.append(10+val)
# print(len(x), len(y), len(y2))
x=x[-varLen:]
y = y[-varLen:]
y2 = y2[-varLen:]
ln.set_data(x, y)
ln2.set_data(x,y2)
# ln.set_data(frame, randrange(0, 10))
# ln2.set_data(frame, 10+randrange(0, 10))
fig.gca().relim()
fig.gca().autoscale_view()
print(f'Time (ms): {round((time.time() - start)*1000,2)}')
return ln,ln2,
animation = FuncAnimation(fig, update, interval=1, blit=True)
plt.show()
Context: I am trying to create a teaching demo tool to show how the iteration guesses through a set of points to ultimately arrive at the root of an equation
Problem: I want to animate using matplotlib to show the iterations viusally. Specifically, given a curve (see along side) and an initial guess (say 1.5 in this particular case), I want to compose an animation of 3 scenes:
draw a vertical line at x = guess (=1.5), to meet the curve at y= 9 (aka value).
Draw a line through (guess, value) with a slope 'm' (generated by my code). Delete the vertical line at this stage and keep the second line
Delete the second line after a pause
For illustration, here is the image
Additionally here is my code:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
fig, ax = plt.subplots()
xdata, ydata = [], []
xdata2, ydata2 = [], []
ln, = plt.plot([], [])
def init():
ax.set_xlim(-3, 3)
ax.set_ylim(-10, 10)
return [ln]
def update(frame):
xdata.append(frame)
ydata.append(frame ** 3 + 4 * frame ** 2 + frame - 6)
ln.set_data(xdata, ydata)
return [ln]
def update2(frame):
xdata2.append(1.5)
ydata2.append(frame)
ln.set_data(xdata2,ydata2)
return[ln]
ani = FuncAnimation(fig, update, frames=np.linspace(-3, 3, 100),
init_func=init, blit=True)
ani2 = FuncAnimation(fig, update2, frames=np.linspace(0, 3, 100),blit=True)
plt.show()
This is a simplified version of the problem that I am trying to solve and is not part of the code that involves the calculation of the iterations etc. For now I am just trying to draw the curve in Update and post that, draw a vertical line at x=1.5.
Results: At my end, the entire animation is a set of flickering where it is apparent that matplotlib switches "thread context" very rapidly between the two FuncAnimation calls
The desired animation in your next question can be achieved in the form of drawing a curve as a base graph, adding line graphs frame by frame, and deleting that graph object when necessary.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import time
fig, ax = plt.subplots()
x = np.linspace(-3, 3, 100)
y = x ** 3 + 4 * x **2 + x -6
xx = x[74]
yy = y[74]
#print(xx,yy)
xx2 = x[65]
yy2 = y[65]
#print(xx2,yy2)
ln, = ax.plot(x, y)
ln2, = ax.plot([], [])
ln3, = ax.plot([],[])
ax.set_xlim(-3, 3)
ax.set_ylim(-10, 10)
# Move axes center and spines off
ax.spines[['top', 'right']].set_visible(False)
ax.spines[['left', 'bottom']].set_position('center')
# Show ticks axes only
ax.xaxis.set_ticks_position('bottom')
ax.yaxis.set_ticks_position('left')
def update(i):
ln2.set_data((xx, xx), (0, yy))
ln2.set_color('k')
if i == 2:
ln3.set_data((xx2, xx), (yy2, yy))
ln3.set_color('red')
ln3.set_width=3
if i >=3:
ln2.set_data([],[])
ln3.set_data([],[])
return ln2,ln3
ani = FuncAnimation(fig, update, frames=[0,1,2,3], interval=500, blit=True, repeat=True)
plt.show()
I'm trying to animate a 2d path, and I would like it to have a sort of "Disappearing Tail", where at any given time, it shows only the last 5 (for example) particles.
What I currently have is quite far from this:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import animation, rc
from IPython.display import HTML
sample_path = np.random.uniform(size=(100,2))
fig, ax = plt.subplots()
x = np.arange(-1, 1, 0.01) # x-array
line, = ax.plot(sample_path[0,0], sample_path[0,1])
def connect(i):
(x0,y0) = sample_path[i-1,:]
(x1,y1) = sample_path[i,:]
plt.plot([x0,x1],[y0,y1],'ro-')
return line,
def init():
line.set_ydata(np.ma.array(x, mask=True))
return line,
ani = animation.FuncAnimation(fig, connect, np.arange(1, 100), init_func=init,
interval=200, blit=True)
HTML(ani.to_html5_video())
This retains a 'full tail', i.e. after k steps, it shows all of the first k locations.
Is there a way to adapt what I've got so that the animation only shows the most recent history of the particle?
You would probably want to update the line instead of adding a lot of new points to the plot. Selecting the 5 most recent points can be done via indexing, e.g.
sample_path[i-5:i, 0]
Complete example, where we take care not to have a negative index and also don't use blit (which does not make sense if saving the animation).
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import animation, rc
r = np.sin(np.linspace(0,3.14,100))
t = np.linspace(0, 10, 100)
sample_path = np.c_[r*(np.sin(t)+np.cos(t)), r*(np.cos(t)-np.sin(t))]/1.5
fig, ax = plt.subplots()
line, = ax.plot(sample_path[0,0], sample_path[0,1], "ro-")
def connect(i):
start=max((i-5,0))
line.set_data(sample_path[start:i,0],sample_path[start:i,1])
return line,
ax.set_xlim(-1,1)
ax.set_ylim(-1,1)
ani = animation.FuncAnimation(fig, connect, np.arange(1, 100), interval=200)
plt.show()
Not as good as ImportanceOfBeingErnest's answer technically, but it still does the job and looks pretty cool, just plot the latest points and clear the old ones. I added a few more and sped it up because I thought it looked better with a longer trail.
def connect(i):
#clear current points
plt.clf()
#prevent axis auto-resizing
plt.plot(0,0)
plt.plot(1,1)
#generate points to plot
(x0,y0) = sample_path[i-8,:]
(x1,y1) = sample_path[i-7,:]
(x2,y2) = sample_path[i-6,:]
(x3,y3) = sample_path[i-5,:]
(x4,y4) = sample_path[i-4,:]
(x5,y5) = sample_path[i-3,:]
(x6,y6) = sample_path[i-2,:]
(x7,y7) = sample_path[i-1,:]
(x8,y8) = sample_path[i,:]
#plot old points
plt.plot([x0,x1,x2,x3,x4,x5,x6,x7],[y0,y1,y2,y3,y4,y5,y6,y7],'ro-')
#plot new point in blue
plt.plot([x7,x8],[y7,y8],'bo-')
return line,
def init():
line.set_ydata(np.ma.array(x, mask=True))
return line,
ani = animation.FuncAnimation(fig, connect, frames=np.arange(1, 100),
init_func=init,
interval=50, blit=True)
HTML(ani.to_html5_video())
I would like to fill an array gradually in a loop and display the result by every iteration, so that I don’t like to declare it as ones or zeros. Is there any technique to achieve that?
Here is an example in which I want to fill xp and yp gradually. Defining xp and yp as empty falsifies the plots! Any help please?
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
a=.1
dt=.05
nx=33
ny=33
px=1
py=1
qx=1.0*px/(nx-1)
qy=1.0*py/(ny-1)
x = np.linspace(0,px,nx)
y = np.linspace(0,py,ny)
fig = plt.figure()
ax = plt.axes(xlim=(0, px), ylim=(0, py))
line, = ax.plot([], [], lw=2)
def init():
line.set_data([], [])
return line,
ax.set_xlim(0,px)
ax.set_ylim(0,py)
X,Y = np.meshgrid(x,y)
U=-a*Y
V=a*X
x1=.5
y1=.5
xp=np.empty(nx)
yp=np.empty(ny)
xp[0]=x1
yp[0]=y1
def animate(i):
xp[i+1]=xp[i]+dt*U[yp[i]/qy,xp[i]/qx]
yp[i+1]=yp[i]+dt*V[yp[i]/qy,xp[i]/qx]
line.set_data(xp,yp)
return line,
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=int(10), interval=5, blit=True)
plt.show()
Thank you!
In order to declare an array without filling it with ones or zeros/empty you may use for your 2 vectors the following:
xp = [None] * 80
yp = [None] * 80
And all now is going well!