I'm trying to make 2D collision simulation in Python.
I made a circle on the figure to use as an object, and I wanted to move it in real-time without the figure window closed.
I found this code:
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 10, 100)
y = np.cos(x)
fig = plt.figure()
for p in range(50):
p=3
updated_x=x+p
updated_y=np.cos(x)
plt.plot(updated_x,updated_y)
plt.draw()
x=updated_x
y=updated_y
plt.pause(0.2)
fig.clear()
and I tried this:
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
board = plt.axes(xlim = (0, 200), ylim = (0, 100))
x = 10
y = 10
r = 10
for p in range(10):
circle = plt.Circle((x, y), body.r, fc='w', ec='b')
board.add_patch(circle)
plt.annotate("", xytext=(x, y), xy=(x+10, y+10), arrowprops=dict(facecolor='r', edgecolor='r', headwidth=6, headlength=6, width=0.1))
plt.draw()
plt.pause(0.2)
fig.clear()
x += 10
y += 10
I wanted to draw only the present circle and delete the previous one.
But it didn't worked, and I can't understand why fig.clear() in the first code didn't really clear it.
Please help me...
Your problem was with fig.clear(). If you just remove the circle and the annotation from the plot you get an animation.
from matplotlib import pyplot as plt, animation as an
import numpy as np
fig = plt.figure()
board = plt.axes(xlim=(0, 200), ylim=(0, 100))
x = 10
y = 10
r = 10
for p in range(10):
circle = plt.Circle((x, y), r, fc='w', ec='b')
board.add_patch(circle)
annotation = plt.annotate("", xytext=(x, y), xy=(x+10, y+10), arrowprops=dict(facecolor='r', edgecolor='r', headwidth=6, headlength=6, width=0.1))
plt.draw()
plt.pause(0.2)
circle.remove()
annotation.remove()
x += 10
y += 10
plt.show()
Related
I want to animate the trajectory of a circle (ball) defined by y = -t^2 + 11t - 18. Basically it would just be bouncing up and down (i.e. no change in x). Its intercepts are (2,0) and (9,0) so the animation should start at time t = 2 as it leaves the ground and end at time t = 9 as it returns to the ground. I am also hoping that a running display of the time could also be included in the animation. So basically between times t=0 and t=2, the ball would just be on the ground. This is the code I have so far but it doesn't seem to make sense. I'm not sure whether the animation is just going too fast.
%matplotlib notebook
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
fig = plt.figure()
fig.set_dpi(100)
fig.set_size_inches(3, 3)
ax = plt.axes(xlim=(0, 10), ylim=(0, 15))
patch = plt.Circle((5, 0), 0.2, fc='r')
def init():
patch.center = (5, 0)
ax.add_patch(patch)
return patch,
def animate(i):
x, y = patch.center
x = 0 * i+5
y = - i**2 + 11 * i - 18
patch.center = (x, y)
return patch,
anim = animation.FuncAnimation(fig, animate,
init_func=init,
frames=3600,
interval=1,
blit=True)
plt.show()
I have the problem that circle is not an iterable, how do I solve it? I'd like the parabolic shot to work with the circle.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import animation, rc
from IPython.display import HTML
plt.style.use('dark_background')
ax = plt.axes(xlim=(-5, 25), ylim=(-5, 25))
patch = plt.Circle((1, -1), 0.5, fc='b') #figura
X = 30
Y = 30
gravity=9.81
angle=70
velocity=80
vx=velocity * np.cos(np.radians(angle))
vy=velocity * np.sin(np.radians(angle))
t=0
def setup():
patch.center = (10, 10)
ax.add_patch(patch)
return patch
def throwBall():
global X, Y, gravity, t,vx,vy
t +=0.02
X = vx*t
Y = 400 -(vy*t - (gravity/2)*t*t)
patch.center = (X, Y)
return patch
animen = animation.FuncAnimation(fig, throwBall,init_func=setup,frames=360,interval=15,blit=True)
HTML(animen.to_html5_video())
Pretty much what #JohanC said. According to the documentation, if blit=True "func must return an iterable of all artists that were modified or created." Therefore, you must return a list or a tuple of Artists, even if there only one artist modified.
I've also made some cosmetic changes. If you are incrementing t in your update function, why not pass directly the value of t as an argument to your update function using frames = np.arange(0,7.2,0.02) or something equivalent.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import animation
from matplotlib.patches import Circle
fig = plt.figure()
ax = fig.add_subplot(111, xlim=(0, 500), ylim=(0, 500), aspect='equal')
patch = Circle((1, -1), 10, fc='b')
ax.add_patch(patch)
X = 30
Y = 30
gravity=9.81
angle=70
velocity=80
vx=velocity * np.cos(np.radians(angle))
vy=velocity * np.sin(np.radians(angle))
def setup():
patch.set_center((X, Y))
return (patch,)
def throwBall(t):
global X, Y
X = vx*t
Y = 400 -(vy*t - (gravity/2)*t*t)
patch.set_center((X, Y))
return (patch)
ani = animation.FuncAnimation(fig, throwBall, init_func=setup, frames=np.arange(0,7.2,0.02), interval=20, blit=True)
I am using an artist animation method with 5 subplots. There is one static plot on the left, with 3 smaller animated imshow plots to the right (the colorbar is the 5th). I have successfully used ConnectionPatch to connect subplots to show where the data is coming from, but only on static plots. No matter what I try, I can't seem to get the patches to show up on the animation. I've tried to include the patch in the image artist list, tried to update the figure with the artist instead of the axis (which I guess doesn't make much sense), among other things. It will be very difficult to extract a working example due to the complexity of the plot, but maybe someone has a tip.
Could setting the facecolor to 'white' with the animation savefig_kwargs be covering up the connector lines? If so, how do I change the z order of the patch/facecolor?
Without a minimal working example, I can only tell you that it is possible to use a ConnectionPatch in an animation. However, as seen below, one has to recreate it for every frame.
import matplotlib.pyplot as plt
import numpy as np; np.random.seed(0)
import matplotlib.gridspec as gridspec
from matplotlib.patches import ConnectionPatch
import matplotlib.animation
plt.rcParams["figure.figsize"] = np.array([6,3.6])*0.7
x = np.linspace(-3,3)
X,Y = np.meshgrid(x,x)
f = lambda x,y: (1 - x / 2. + x ** 5 + y ** 3) * np.exp(-x ** 2 - y ** 2)+1.5
Z = f(X,Y)
bins=np.linspace(Z.min(), Z.max(), 16)
cols = plt.cm.PuOr((bins[:-1]-Z.min())/(Z.max()-Z.min()))
gs = gridspec.GridSpec(2, 2, height_ratios=[34,53], width_ratios=[102,53])
fig = plt.figure()
ax=fig.add_subplot(gs[:,0])
ax2=fig.add_subplot(gs[0,1])
ax3=fig.add_subplot(gs[1,1])
ax.imshow(Z, cmap="PuOr")
rec = plt.Rectangle([-.5,-.5], width=9, height=9, edgecolor="crimson", fill=False, lw=2)
conp = ConnectionPatch(xyA=[-0.5,0.5], xyB=[9.5,4], coordsA="data", coordsB="data",
axesA=ax3, axesB=ax, arrowstyle="-|>", zorder=25, shrinkA=0, shrinkB=1,
mutation_scale=20, fc="w", ec="crimson", lw=2)
ax3.add_artist(conp)
ax.add_artist(rec)
im = ax3.imshow(Z[:9,:9], cmap="PuOr", vmin=Z.min(), vmax=Z.max())
ticks = np.array([0,4,8])
ax3.set_yticks(ticks); ax3.set_xticks(ticks)
ax2.hist(Z[:9,:9].flatten(), bins=bins)
def ins(px,py):
global rec, conp, histpatches
ll = [px-.5,py-.5]
rec.set_xy(ll)
conp.remove()
conp = ConnectionPatch(xyA=[-0.5,0.5], xyB=[px+9.5,py+4], coordsA="data", coordsB="data",
axesA=ax3, axesB=ax, arrowstyle="-|>", zorder=25, shrinkA=0, shrinkB=1,
mutation_scale=20, fc="w", ec="crimson", lw=2)
ax3.add_patch(conp)
data = Z[px:px+9,py:py+9]
im.set_data(data)
ax3.set_xticklabels(ticks+px)
ax3.set_yticklabels(ticks+py)
ax2.clear()
ax2.set_ylim(0,60)
h, b_, patches = ax2.hist(data.flatten(), bins=bins, ec="k", fc="#f1a142")
[pat.set_color(cols[i]) for i, pat in enumerate(patches)]
def func(p):
px,py = p
ins(px, py)
phi = np.linspace(0.,2*np.pi)
r = np.sin(2*phi)*20+np.pi/2
xr = (r*np.cos(phi)).astype(np.int8)
yr = (r*np.sin(phi)).astype(np.int8)
plt.subplots_adjust(top=0.93,bottom=0.11,left=0.04,right=0.96,hspace=0.26,wspace=0.15)
frames = np.c_[xr+20, yr+20]
ani = matplotlib.animation.FuncAnimation(fig, func, frames=frames, interval=300, repeat=True)
plt.show()
I have a random walker in the (x,y) plane and a -log(bivariate gaussian) in the (x,y,z) plane. These two datasets are essentially independent.
I want to sample, say 5 (x,y) pairs of the random walker and draw vertical lines up the z-axis and terminate the vertical line when it "meets" the bivariate gaussian.
This is my code so far:
import matplotlib as mpl
import matplotlib.pyplot as plt
import random
import numpy as np
import seaborn as sns
import scipy
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.mlab import bivariate_normal
%matplotlib inline
# Data for random walk
def randomwalk():
mpl.rcParams['legend.fontsize'] = 10
xyz = []
cur = [0, 0]
for _ in range(40):
axis = random.randrange(0, 2)
cur[axis] += random.choice([-1, 1])
xyz.append(cur[:])
# Get density
x, y = zip(*xyz)
data = np.vstack([x,y])
kde = scipy.stats.gaussian_kde(data)
density = kde(data)
# Data for bivariate gaussian
a = np.linspace(-7.5, 7.5, 40)
b = a
X,Y = np.meshgrid(a, b)
Z = bivariate_normal(X, Y)
surprise_Z = -np.log(Z)
# Get random points from walker and plot up z-axis to the gaussian
M = data[:,np.random.choice(20,5)].T
# Plot figure
fig = plt.figure(figsize=(10, 7))
ax = fig.gca(projection='3d')
ax.plot(x, y, 'grey', label='Random walk') # Walker
ax.scatter(x[-1], y[-1], c='k', marker='o') # End point
ax.legend()
surf = ax.plot_surface(X, Y, surprise_Z, rstride=1, cstride=1,
cmap = plt.cm.gist_heat_r, alpha=0.1, linewidth=0.1)
#fig.colorbar(surf, shrink=0.5, aspect=7, cmap=plt.cm.gray_r)
for i in range(5):
ax.plot([M[i,0], M[i,0]],[M[i,1], M[i,1]], [0,10],'k--',alpha=0.8, linewidth=0.5)
ax.set_zlim(0, 50)
ax.set_xlim(-10, 10)
ax.set_ylim(-10, 10)
Which produces
As you can see the only thing I'm struggling with is how to terminate the vertical lines when they meet the appropriate Z-value. Any ideas are welcome!
You're currently only letting those lines get to a height of 10 by using [0,10] as the z coordinates. You can change your loop to the following:
for i in range(5):
x = [M[i,0], M[i,0]]
y = [M[i,1], M[i,1]]
z = [0,-np.log(bivariate_normal(M[i,0],M[i,1]))]
ax.plot(x,y,z,'k--',alpha=0.8, linewidth=0.5)
This takes the x and y coordinates for each point you loop over and calculates the height of overlying Gaussian for that point and plots to there. Here is a plot with the linestyle changed to emphasize the lines relevant to the question:
I have a three-variable function myfunc that is generated inside three for loops. I want to draw a contour plot of y vs x and animate this for different times t. However, I've looked at the various matplotlib examples on the webpage, and am still unsure of how to do this.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import animation
def myfunc(x,y,t):
w = 0.5*x + y + 4*np.sin(1.8*t)
return w
xlist = np.linspace(0,10,10)
ylist = np.linspace(-1,1,10)
tlist = np.linspace(0,50,50)
plt.figure()
for t in tlist:
for x in xlist:
for y in ylist:
w = myfunc(x,y,t)
w_vec = np.array(w)
w_contour = w_vec.reshape((xlist.size, ylist.size))
w_plot = plt.contourf(ylist,xlist,w_contour)
plt.xlabel('x', fontsize=16)
plt.ylabel('y', fontsize=16)
plt.show()
Edit: I quite like the look of dynamic_image2.py in this tutorial. This seems to get things moving, but the axes are wrong:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig = plt.figure()
def f(x,y,t):
return 0.5*x + np.sin(y) + 4*np.sin(1.8*t)
x = np.linspace(0, 10, 10)
y = np.linspace(-1, 1, 10).reshape(-1, 1)
tlist = np.linspace(0,50,50)
ims = []
for t in tlist:
x += np.pi / 15.0
y += np.pi / 20.0
im = plt.imshow(f(x,y,t))
ims.append([im])
ani = animation.ArtistAnimation(fig, ims, interval=20, blit=True,
repeat_delay=1000)
plt.show()