I created an animation (see code) that works perfectly. But, cannot manage to add 1) a Play/Pause/Stop button or "onClick()": nothing happens and the animation keeps on running. 2) Same thing for the timer, that I cannot see. Is is related to the 3D scatter ?
# -*- coding: utf-8 -*-
"""
Create a random distribution of points (cluster),
change the color and size of points
move the cluster (r, theta, phi)
This is done iteratively until we close the figure.
===================================================
"""
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.animation as animation
pause = True
def onClick(event):
global pause
pause ^= True
# We reset the main parameters of the plot
def update_plot(i, color, size, elev, azim, dist, scat):
global pause
# Set colors...
scat.set_array(color[i])
# Set sizes...
scat.set_sizes(size[i])
# Set elevation annd azimuth...
ax.view_init(elev=elev[i], azim=azim[i])
# Set distance...
ax.dist=dist[i]
return scat,
# How many frame?
numframes = 500
# How many points?
numpoints = 200
# Initialization the position (x, y, z), the color (c) and the size (s) of the points
mu, sigma = 0, 0.25 # mean and standard deviation
x = np.random.normal(mu, sigma, numpoints)
y = np.random.normal(mu, sigma, numpoints)
z = np.random.normal(mu, sigma, numpoints)
c, s = np.random.random((2, numpoints))
# Definition of the data for the new values for each new plot
color_data = np.random.random((numframes, numpoints))
size_data = 200*np.random.random((numframes, numpoints))
elev_data = np.linspace(0, 360, numframes)
azim_data = np.linspace(0, 360, numframes)
dist_data = np.linspace(50, 1, numframes)
fig = plt.figure()
ax = Axes3D(fig, axisbg='black')
# We do not want the axis
ax.set_axis_off()
# This is where we plot the cluster
scat = ax.scatter(x, y, z, c=c, s=s, alpha=0.5)
xmin = np.min(x)
xmax = np.max(x)
ax.set_xlim(xmin,xmax)
ax.set_ylim(xmin,xmax)
ax.set_zlim(xmin,xmax)
# This is the animation. In fargs, we provide the data for each new plot.
fig.canvas.mpl_connect('button_press_event', onClick)
ani = animation.FuncAnimation(fig, update_plot, frames=range(numframes),
fargs=(color_data, size_data,
elev_data, azim_data, dist_data,
scat), blit=False, interval=10, repeat=True)
plt.show()
Related
I have an array x_trj that has shape (50,3), and I want to plot a 2-D trajectory using the 1st and the 2nd columns of this array (x & y coordinates respectively). This trajectory will be on top of a circle. Here is my code so far:
from matplotlib.animation import FuncAnimation
import matplotlib.pyplot as plt
fig = plt.figure()
ax = plt.axes(xlim=(-5, 5), ylim=(-5, 5))
line, = ax.plot([], [], lw=2)
# Plot circle
theta = np.linspace(0, 2*np.pi, 100)
plt.plot(r*np.cos(theta), r*np.sin(theta), linewidth=5)
ax = plt.gca()
def animate(n):
# Plot resulting trajecotry of car
for n in range(x_trj.shape[0]):
line.set_xdata(x_trj[n,0])
line.set_ydata(x_trj[n,1])
return line,
anim = FuncAnimation(fig, animate,frames=200, interval=20)
However, the animation turns out to be a stationary figure. I checked out the Matplotlib animation example on the documentation page, but I still can't figure out what my animate(n) function should look like in this case. Can someone give me some hints?
The code below makes the following changes:
added some test data
in animate:
remove the for loop
only copy the part of the trajectory until the given n
in the call to FuncAnimation:
`frames should be equal to the given number of points (200 frames and 50 points doesn't work well)
interval= set to a larger number, as 20 milliseconds make things too fast for only 50 frames
added plt.show() (depending on the environment where the code is run, plt.show() will trigger the animation to start)
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np
# create some random test data
x_trj = np.random.randn(50, 3).cumsum(axis=0)
x_trj -= x_trj.min(axis=0, keepdims=True)
x_trj /= x_trj.max(axis=0, keepdims=True)
x_trj = x_trj * 8 - 4
fig = plt.figure()
ax = plt.axes(xlim=(-5, 5), ylim=(-5, 5))
line, = ax.plot([], [], lw=2)
# Plot circle
theta = np.linspace(0, 2 * np.pi, 100)
r = 4
ax.plot(r * np.cos(theta), r * np.sin(theta), linewidth=5)
def animate(n):
line.set_xdata(x_trj[:n, 0])
line.set_ydata(x_trj[:n, 1])
return line,
anim = FuncAnimation(fig, animate, frames=x_trj.shape[0], interval=200)
# anim.save('test_trajectory_animation.gif')
plt.show()
I need to animate something rather simple but I do need as well a slider to let the user interactively changes the parameters of the animation. I'd like it to happen online; i.e. if the user changes a parameter while the animation is playing, the animation should transit smoothly from its old dynamics to its new one.
So far I've written a function that takes arguments and makes an animation. But it's not interactive in the sense I mentioned before. There are no sliders or anything really interactive in my code. Nevertheless, the animation part is running smoothly at least.
Here is a simplified version of my code: A point revolves around the center with a specified distance r from the center and an angular velocity w. User can give them as arguments to see the animation. (If you saw something in the code that is never used don't bother yourself with it, it is probably because I forgot to trim it from the original code with is much longer.)
import numpy as np
%matplotlib notebook
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
def simplified_code(w,r):
fps = 36
M = int(.75*fps*2*np.pi/w)
T_final = 2*np.pi/w*16
def positions(t):
x = r*np.cos(w*t)
y = r*np.sin(w*t)
return x,y
fig = plt.figure()
ax = fig.add_subplot(111, aspect='equal', autoscale_on=False, )
ax.grid()
# position
x_e, y_e = [], []
# trajectory and center
traj_e, = plt.plot([],[],'-g',lw=1)
e, = plt.plot([], [], 'ok')
# time
time_text = ax.text(0.02, 0.95, '', transform=ax.transAxes)
def init():
ax.set_xlim(-(r+0.5), (r+0.5))
ax.set_ylim(-(r+0.5), (r+0.5))
ax.plot([0], ms=7, c='k',marker='o')
return d,e,traj_e
def update(frame):
x,y = positions(frame)
x_e.append(x)
y_e.append(y)
traj_e.set_data(x_e[-M:], y_e[-M:])
e.set_data(x, y)
time_text.set_text('time = %.1f' % frame)
return traj_d,traj_e,d,e, orb
return FuncAnimation(fig, update, frames=np.linspace(0, T_final, T_final*fps),
init_func=init, blit=True, interval=1./36*1000)
Note that it's possible to stop the animation, change the parameters via a slider and rerun it. I want to avoid this pause in the animation. I'd appreciate any help.
Thanks to #ImportanceOfBeingErnest I managed to combine update function of the animation and the one with the sliders:
import numpy as np
%matplotlib notebook
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from matplotlib.widgets import Slider
fig = plt.figure(figsize=(6,7))
ax = fig.add_subplot(111, aspect='equal', autoscale_on=False, position=[.15,.15,.75,.75] )
ax.grid()
w = 2
r = 1
fps = 36
M= 1024#
T_final = 256
x_e, y_e = [], []
orb_x, orb_y = [], []
# trajectories
traj_e, = ax.plot(x_e,y_e,'-g',lw=1)
# center
e, = ax.plot([], [], 'ok')
# orbit
orb, = ax.plot([], [], '.r',ms=1)
# time
time_text = ax.text(0.02, 0.95, '', transform=ax.transAxes)
def positions(t):
x = r*np.cos(w*t) # epicycle
y = r*np.sin(w*t) # epicycle
return x,y
def orbit(r):
phi = np.linspace(0, 2*np.pi, 360)
orb_x = r*np.cos(phi)
orb_y = r*np.sin(phi)
return orb_x,orb_y
def init():
ax.plot([0], ms=7, c='k',marker='o')
return e,traj_e
def update(t):
global r, w
w = s_w.val
r = s_r.val
ax.set_xlim(-(r)*1.1, (r)*1.1)
ax.set_ylim(-(r)*1.1, (r)*1.1)
x,y = positions(t)
x_e.append(x)
y_e.append(y)
traj_e.set_data(x_e[-M:-1], y_e[-M:-1])
orb.set_data(orbit(r))
e.set_data(x, y)
time_text.set_text('time = %.1f' % t)
return traj_e,e, orb
ax_w = plt.axes([0.1, 0.05, 0.35, 0.03])#, facecolor=axcolor)
ax_r = plt.axes([0.55, 0.05, 0.35, 0.03])#, facecolor=axcolor)
s_w = Slider(ax_w, r'$\omega$', -20, 20, valinit=w, valstep=0.2)
s_r = Slider(ax_r, r'r', 0, 5, valinit=r, valstep=0.2)
s_w.on_changed(update)
s_r.on_changed(update)
def anim():
fig.canvas.draw_idle()
return FuncAnimation(fig, update, frames=np.linspace(0, T_final, T_final*fps),
init_func=init, blit=True, interval=30)
anim()
Using this piece of code I can change the values of r and w without pausing or restarting the animation from scratch. Another problem appears with this code though which is that the point jumps to some random position on the circle and then jumps back the expected trajectory. I'd address it in another question.
I'm not a beginner, but I'm also not advanced dev of python code.
I'm been trying to animate points movement in scatter plot and to put annotation on every point. All I have done is animation of one point with no annotation. I've searched similar solutions, but it's so confusing. Any help is welcome. This is what I've done.
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
import matplotlib.animation as animation
frame_count = 0
points = reading_file("some_data") # this method is not of intrest
def make_one_point(i):
global frame_count, points
ex = [1]
ey = [1]
ez = [1]
point = points[i]
frame = point[frame_count]
ex[0] = frame[0]
ey[0] = frame[1]
ez[0] = frame[2]
frame_count += 1
return ex, ey, ez
def update(i):
global frame_count, points
if frame_count < len(points[i]):
return make_one_point(i)
else:
frame_count = 0
return make_one_point(i)
fig = plt.figure()
ax1 = fig.add_subplot(111, projection='3d')
ax1.set_xlim3d(-500, 2000)
ax1.set_ylim3d(-500, 2000)
ax1.set_zlim3d(0, 2000)
x = [1]
y = [1]
z = [1]
scat = ax1.scatter(x,y,z)
def animate(i):
scat._offsets3d = update(0)
ani = animation.FuncAnimation(fig, animate,
frames=len(points[10]),
interval=100, repeat=True)
plt.show()
How to animate more points at the same time, and put annontation on every one of them? There are 50 points, and I'm not so consern about efficiency, just to make it work.
This code output is moving one point animation
It turns out animating Text in 3D was harder than I anticipated. Not surprisingly, I was able to find the solution to the problem in an answer from #ImportanceOfBeingErnest. I then simply adapted the code I had already written in a previous answer, and produced the following code:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D, proj3d
import matplotlib.animation as animation
N_points = 10
def update(num, my_ax):
# the following corresponds to whatever logic must append in your code
# to get the new coordinates of your points
# in this case, we're going to move each point by a quantity (dx,dy,dz)
dx, dy, dz = np.random.normal(size=(3,N_points), loc=0, scale=1)
debug_text.set_text("{:d}".format(num)) # for debugging
x,y,z = graph._offsets3d
new_x, new_y, new_z = (x+dx, y+dy, z+dz)
graph._offsets3d = (new_x, new_y, new_z)
for t, new_x_i, new_y_i, new_z_i in zip(annots, new_x, new_y, new_z):
# animating Text in 3D proved to be tricky. Tip of the hat to #ImportanceOfBeingErnest
# for this answer https://stackoverflow.com/a/51579878/1356000
x_, y_, _ = proj3d.proj_transform(new_x_i, new_y_i, new_z_i, my_ax.get_proj())
t.set_position((x_,y_))
return [graph,debug_text]+annots
# create N_points initial points
x,y,z = np.random.normal(size=(3,N_points), loc=0, scale=10)
fig = plt.figure(figsize=(5, 5))
ax = fig.add_subplot(111, projection="3d")
graph = ax.scatter(x, y, z, color='orange')
debug_text = fig.text(0, 1, "TEXT", va='top') # for debugging
annots = [ax.text2D(0,0,"POINT") for _ in range(N_points)]
# Creating the Animation object
ani = animation.FuncAnimation(fig, update, fargs=[ax], frames=100, interval=50, blit=True)
plt.show()
I am trying to update the colorbars in my plot. Unfortunately, only the colors update i.e. the tick values do not change only the colors of the bar change accorgingly with the current values in the contour plot. I would like to make the ticks change as well as the colors in the colorbar.
import matplotlib
import numpy as np
import pylab as py
import matplotlib.cm as cm
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
fig, axs = plt.subplots(1, 2)
# I define the variables below but do not give the exact values as they come from computations
x - one dimensional array
v - four dimensional array
v_mag - three dimensional array
T_string - three dimensional array
X, Y = np.meshgrid(x, x)
cs1 = axs[0].quiver(X, Y, v[0][0], v[0][1],v_mag[0], cmap=cm.seismic)
cs2 = axs[1].contourf(X, Y, T[0], 100)
cbar1=fig.colorbar(cs1, ax=axs[0], format=\"%.2f\")
cbar2=fig.colorbar(cs2, ax=axs[1], format=\"%.2f\")
axcolor = 'lightgoldenrodyellow'
time = py.axes([0.1, 0.01, 0.65, 0.03], axisbg = axcolor)
S_time = Slider(time, 'Time', 0, 50, valinit = 0);
def update(val) :
timeval = int(S_time.val)
cs1.set_UVC(v[timeval][0],v[timeval][1], v_mag[timeval])
cbar1.on_mappable_changed(cs1)
cs2 = axs[1].contourf(X, Y, T[timeval], 100)
cbar2.on_mappable_changed(cs2)
plt.show()
#second try
def update(val) :
timeval = int(S_time.val)
cs1.set_UVC(v[timeval][0],v[timeval][1], v_mag[timeval])
cbar1.on_mappable_changed(cs1)
cs2 = axs[1].contourf(X, Y, T[timeval], 100)
cbar2.set_clim( np.amin(np.array(T[timeval])) , np.amax(np.array(T[timeval])) )
cbar2.update_ticks()
cbar2.draw_all()
plt.draw()
S_time.on_changed(update)
plt.show()
The answer is to define the update function as:
def update(val) :
timeval = int(S_time.val)
cs1.set_UVC(v[timeval][0],v[timeval][1], v_mag[timeval])
cbar1.on_mappable_changed(cs1)
cs2 = axs[1].contourf(X, Y, T[timeval], 100, cmap=cm.jet)
cbar2.set_ticklabels(np.linspace(np.amin(np.array(T[timeval])),np.amax(np.array(T[timeval])), num = 12))
cbar2.update_ticks()
plt.show()
I'm trying to create an animation with two subplots--one 3D and another 2D. I can't seem to figure out if there is a way to get better font rendering from the 2D axes however. I tried playing around with various settings with font_manager, and even changing the frame_format to raw, but I've had no success. Does anyone have any ideas how to fix this? I get the same results with mpeg4.
The strange thing is that the 3D figure seems to render the font properly.
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from mpl_toolkits.mplot3d import Axes3D
w, h = matplotlib.figure.figaspect(.5)
fig = plt.figure(figsize=(w,h))
ax3d = fig.add_subplot(121, projection='3d')
ax2d = fig.add_subplot(122)
ax3d.set_xlim(-3, 3)
ax3d.set_ylim(-3, 3)
ax3d.azim = -90
ax3d.elev = 0
ax3d.set_title('Car on Parking Ramp')
ax2d.set_xlim(-20,20)
ax2d.set_ylim(-20,20)
ax2d.set_ylabel('y')
ax2d.set_xlabel('x')
ax2d.set_title('Intersection with z=0')
''' Helix '''
K = 3 ## Angular velocity
H = 2*np.pi ## Height
t = np.linspace(0, H, 100, endpoint=True)
x = np.cos(K*t)
y = np.sin(K*t)
z = H - t
ax3d.plot(x, y, z, color='k')
''' z = 0 Plane '''
xx, yy = np.meshgrid([-20,20], [-20,20])
ax3d.plot_surface(xx, yy, 0, alpha=0.3, facecolor='b', rstride=1, cstride=1, shade=True)
ax3d.set_axis_off()
''' Tangent Line Data '''
xdata = np.array([ np.cos(K*t), np.cos(K*t) - K*(H - t)*np.sin(K*t) ])
ydata = np.array([ np.sin(K*t), np.sin(K*t) + K*(H - t)*np.cos(K*t) ])
''' Graph Lines '''
proj, = ax2d.plot([],[])
tangent, = ax3d.plot([], [], [], color='b')
def update_graph(n, tangent, proj, xdata, ydata):
tangent.set_data(xdata[:,n],
ydata[:,n])
tangent.set_3d_properties([H - t[n], 0])
proj.set_xdata(xdata[1,:n])
proj.set_ydata(ydata[1,:n])
ani = animation.FuncAnimation(fig, update_graph, len(t),
fargs=(tangent, proj, xdata, ydata), interval=75, blit=True)
ani.save('im.gif', writer='imagemagick', fps=10)
#ani.save('im.mp4', extra_args=['-vcodec', 'libx264'])
For people who face the same issue, it's indeed related to matplotlib backend.
Using different backend might help. In my case, the
%matplotlib nbagg
solved it (thanks to the linked question: Pixelated fonts when plot is saved as jpeg) .