I'm trying to animate projectile motion with the help of matplotlib.animation but I've been facing a few errors. Please help me with this.
Thank you so much
I've tried searching through the internet and I did implement solutions of a few similar problems but the code still gives an error
import matplotlib as mat
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as mat_anim
u = 5
g = 9.8
theta_degree = np.rad2deg([0, np.pi/6, np.pi/4, np.pi/3, np.pi/2])
theta_rad = [0, np.pi/6, np.pi/4, np.pi/3, np.pi/2]
fr = 100
print(1)
def projectile_range():
# calculate projectile range
rng = ((u**2)*(np.sin(np.multiply(2.0, theta_rad))))/g
return rng
def max_height():
# calculate maximum height of projectile
max_ht = ((u*np.sin(theta_rad))**2)/(2*g)
return max_ht
def projectile():
# calculating projectile path
r = projectile_range()
for j in range(len(r)):
x = np.linspace(0, r[j], 100)
y.append(x*(np.tan(theta_rad[j])) - ((0.5*g*(x**2))/(u*np.cos(theta_rad[j]))**2))
return y
fig1, ax1 = plt.subplots(1,1)
fig1.suptitle("Projectile Motion Range", fontsize = 10)
ax1.set_xlim([0, round(max(projectile_range()))+1])
ax1.set_ylim([0, round(max(max_height()))+1])
# ax_range, = ax1.plot([], [])
dots, = ax1.plot([], [], 'o')
lines, = ax1.plot([], [], lw = 2)
plot_colour = ["black", "red", "green", "yellow", "blue"]
line_list = []
dot_list = []
print(2)
for index in range(len(theta_rad)):
line_obj = ax1.plot([], [], lw = 2, color = plot_colour[index])[0]
dot_obj = ax1.plot([], [], 'o', color = plot_colour[len(theta_rad)-index-1])[0]
line_list.append(line_obj)
dot_list.append(dot_obj)
print(3)
def initialize():
# initializing projectile range plot
print(4)
for line in line_list:
line.set_data([], [])
for dot in dot_list:
dot.set_data([], [])
print(5)
return dot_list, line_list,
print(6)
def proj_animation(i):
# animation function
print(7)
n = 100
# fr = n
y = np.empty([len(theta_rad), n], dtype = float)
x = np.empty([len(theta_rad), n], dtype = float)
r = projectile_range()
for j in range(len(r)):
x[j] = np.linspace(0, r[j], n)
y[j] = np.multiply(x[j], np.tan(theta_rad[j])) - ((0.5*g*(np.square(x[j])))/(u*np.cos(theta_rad[j]))**2)
for count, element in enumerate(line_list):
element.set_data(x[count][:i], y[count][:i])
for count, element in enumerate(dot_list):
element.set_data(x[count][i], y[count][i])
print(8)
return dot_list,line_list,
proj_anim = mat_anim.FuncAnimation(fig1, proj_animation, frames = fr,
interval = 20, blit = True)
proj_anim.save("projectile_range.mp4", fps = 20, extra_args = ['-vcodec', 'libx264'])
plt.show()
key=lambda x: x.get_zorder())
AttributeError: 'list' object has no attribute 'get_zorder'
I believe the issue is that in proj_animation() you are returning a tuple of two lists, but FuncAnimation() is looking for an iterable of drawn objects directly. The quickest fix for this is to concatenate dot_list with line_list and return the concatenated list. Nb This should also be done in your initialization function.
I was trying to plot sensor data using subplots and was getting the same error. The way I fixed it was to return just a variable or a list. In the animation function I was returning a list of lists, I just flattened this list of lists and the code works. The solution adapted to your code is the following:
import matplotlib as mat
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as mat_anim
u = 5
g = 9.8
theta_degree = np.rad2deg([0, np.pi/6, np.pi/4, np.pi/3, np.pi/2])
theta_rad = [0, np.pi/6, np.pi/4, np.pi/3, np.pi/2]
fr = 100
print(1)
def projectile_range():
# calculate projectile range
rng = ((u**2)*(np.sin(np.multiply(2.0, theta_rad))))/g
return rng
def max_height():
# calculate maximum height of projectile
max_ht = ((u*np.sin(theta_rad))**2)/(2*g)
return max_ht
def projectile():
# calculating projectile path
r = projectile_range()
for j in range(len(r)):
x = np.linspace(0, r[j], 100)
y.append(x*(np.tan(theta_rad[j])) - ((0.5*g*(x**2))/(u*np.cos(theta_rad[j]))**2))
return y
fig1, ax1 = plt.subplots(1,1)
fig1.suptitle("Projectile Motion Range", fontsize = 10)
ax1.set_xlim([0, round(max(projectile_range()))+1])
ax1.set_ylim([0, round(max(max_height()))+1])
# ax_range, = ax1.plot([], [])
dots, = ax1.plot([], [], 'o')
lines, = ax1.plot([], [], lw = 2)
plot_colour = ["black", "red", "green", "yellow", "blue"]
line_list = []
dot_list = []
print(2)
for index in range(len(theta_rad)):
line_obj = ax1.plot([], [], lw = 2, color = plot_colour[index])[0]
dot_obj = ax1.plot([], [], 'o', color = plot_colour[len(theta_rad)-index-1])[0]
line_list.append(line_obj)
dot_list.append(dot_obj)
print(3)
def initialize():
# initializing projectile range plot
print(4)
for line in line_list:
line.set_data([], [])
for dot in dot_list:
dot.set_data([], [])
print(5)
return dot_list, line_list,
print(6)
def proj_animation(i):
# animation function
print(7)
n = 100
# fr = n
y = np.empty([len(theta_rad), n], dtype = float)
x = np.empty([len(theta_rad), n], dtype = float)
r = projectile_range()
graph_list = []
for j in range(len(r)):
x[j] = np.linspace(0, r[j], n)
y[j] = np.multiply(x[j], np.tan(theta_rad[j])) - ((0.5*g*(np.square(x[j])))/(u*np.cos(theta_rad[j]))**2)
for count, element in enumerate(line_list):
element.set_data(x[count][:i], y[count][:i])
for count, element in enumerate(dot_list):
element.set_data(x[count][i], y[count][i])
graph_list.append(dot_list)
graph_list.append(line_list)
graph_list = [item for sublist in graph_list for item in sublist]
print(8)
return graph_list
proj_anim = mat_anim.FuncAnimation(fig1, proj_animation, frames = fr,
interval = 20, blit = True)
proj_anim.save("projectile_range.mp4", fps = 20, extra_args = ['-vcodec', 'libx264'])
plt.show()
I test the code and it works.
Related
I have the following code, which runs well under Visual Studio Code with python 3.9.10, opencv 4.5.5 and numpy 1.22.1.
I would like to migrate this code into the Spyder IDE (Version 5, another notebook), python 3.8, opencv 4.5.1 and numpy 1.22.2.
In spyder, I get the error message TypeError: only integer scalar arrays can be converted a scalar index in line: output_layers = [layer_names[i-1]...] (marked line down in the code section)
I have already checked other answers on this site such as
TypeError when indexing a list with a NumPy array: only integer scalar arrays can be converted to a scalar index
which suggests list comprehension, but in my understanding I am already implemented this.
What is the reason for running currectly in on environment but not in the other?
import cv2
import numpy as np
def get_output_layers(net):
layer_names = net.getLayerNames()
output_layers = [layer_names[i - 1] for i in net.getUnconnectedOutLayers()]
return output_layers
def draw_prediction(img, class_id, confidence, x, y, x_plus_w, y_plus_h):
label = str(classes[class_id])
color = COLORS[class_id]
cv2.rectangle(img, (x,y), (x_plus_w,y_plus_h), color, 2)
cv2.putText(img, label, (x-10,y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2)
image = cv2.imread('horses.jpg')
Width = image.shape[1]
Height = image.shape[0]
scale = 0.00392
classes = None
with open(r'yolov3.txt', 'r') as f:
classes = [line.strip() for line in f.readlines()]
COLORS = np.random.uniform(0, 255, size=(len(classes), 3))
net = cv2.dnn.readNet('yolov3.weights','yolov3.cfg')
blob = cv2.dnn.blobFromImage(image, scale, (416,416), (0,0,0), True, crop=False)
net.setInput(blob)
outs = net.forward(get_output_layers(net))
class_ids = []
confidences = []
boxes = []
conf_threshold = 0.5
nms_threshold = 0.4
for out in outs:
for detection in out:
scores = detection[5:]
class_id = np.argmax(scores)
confidence = scores[class_id]
if confidence > 0.5:
center_x = int(detection[0] * Width)
center_y = int(detection[1] * Height)
w = int(detection[2] * Width)
h = int(detection[3] * Height)
x = center_x - w / 2
y = center_y - h / 2
class_ids.append(class_id)
confidences.append(float(confidence))
boxes.append([x, y, w, h])
indices = cv2.dnn.NMSBoxes(boxes, confidences, conf_threshold, nms_threshold)
for i in indices:
box = boxes[i]
x = box[0]
y = box[1]
w = box[2]
h = box[3]
draw_prediction(image, class_ids[i], confidences[i], round(x), round(y),
round(x+w), round(y+h))
cv2.imshow("object detection", image)
cv2.waitKey()
cv2.imwrite("object-detection.jpg", image)
cv2.destroyAllWindows()
there were subtle, recent api changes wrt handling std::vector in python
(4.5.1 still expects a 2d array, but it's 1d in 4.5.5)
to avoid the whole trouble, please simply use:
output_layers = net.getUnconnectedOutLayersNames()
(like it is done in the sample)
I have coded the laplacien function for a non-regular mesh (created with the scipy.spatial.Delaunay function).
I have not errors but the results are not correct : the eigenvectors are correct but the eigenvalues are too high (in absolute value).
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import scipy.spatial
def rect_drum(L,H,U):
vals = []
val = 0
k = 1
l = 1
while val >= -U:
while val >= -U:
val = -np.pi**2*((k/L)**2+(l/H)**2)
if val >= -U:
vals.append(val)
l += 1
l = 1
k += 1
val = -np.pi**2*((k/L)**2+(l/H)**2)
return np.array(vals)
def count_vp(tab,U):
#count the n eigenvalues greater than equal to -U in the array tab
return tab[tab>=-U]
def in_curve(f,fargs,shape,a):
points = [] # the points inside the curve
for j in range(shape[0]):
for i in range(shape[1]):
if f(i*a,j*a,*fargs) < 0:
points.append([i*a,j*a])
return np.array(points)
def triang(points,a,f,fargs,bord):
tri_points = points.copy()
tri_points[:,1] *= np.sqrt(3)
tri_points2 = np.vstack((points,bord))
tri_points2[:,1] *= np.sqrt(3)
tri_points2[:,0] += a/2
tri_points2[:,1] += np.sqrt(3)/2*a
fin = np.vstack((tri_points,tri_points2))
i = 0
eps = 0.01
while i < len(fin):
if f(fin[i,0]+eps,fin[i,1]+eps,*fargs) > 0:
fin = np.delete(fin,i,0)
i -= 1
i += 1
return np.vstack((fin,bord)),len(fin),len(bord)
def tri_ang(points,ind,p0):
# sort the points in trigonometric order
vec=np.arctan2((points-p0)[:,1],(points-p0)[:,0])
values = []
dtype = [('val',float),('n',int)]
for i in range(len(vec)):
values.append((vec[i],i))
values = np.sort(np.array(values,dtype),order='val')
new_points = []
new_ind = []
for tup in values:
new_points.append(points[tup[1]])
new_ind.append(ind[tup[1]])
return np.array(new_points),np.array(new_ind)
def M(points,tri,Nint):
indptr,ind = tri.vertex_neighbor_vertices
W = np.zeros((Nint,Nint)) # cotangents matrix
A = np.zeros((Nint,1)) # surfaces vertex array for each point i (A[i])
for i in range(Nint):
tot = 0
nhb_ind = ind[indptr[i]:indptr[i+1]] # indices of the points close to the point of index k
nhb = points[nhb_ind] # their coordinates
nhb,nhb_ind = tri_ang(nhb,nhb_ind,points[i]) #the coordinates (nhb) and (nhb_ind) of each neighbor of i
for j in range(len(nhb_ind)):
vec = nhb[j]-points[i] # a vector connecting the point to his neighbor of index 0
vec_av = nhb[j-1]-points[i] # another vector but with the Vosin from before
if j+1 >= len(nhb_ind):
k = 0
else:
k = j+1
vec_ap = nhb[k]-points[i] # another vector but with the next neighbor
# another vector but with the next neighbor
A[i] += 0.5/3*np.linalg.norm(np.cross(vec,vec_av))
if nhb_ind[j] < Nint:
# we use the vector and scalar product to calculate the cotangents: A.B/||AxB||
cotan_alpha = np.dot(vec_av,vec_av-vec)/np.linalg.norm(np.cross(vec_av,vec_av-vec))
cotan_beta = np.dot(vec_ap,vec_ap-vec)/np.linalg.norm(np.cross(vec_ap,vec_ap-vec))
# Wij value :
W[i,nhb_ind[j]] = -0.5*(cotan_alpha+cotan_beta)
tot += cotan_alpha+cotan_beta
W[i,i] = -0.5*tot # diagonal values
return (1/A)*W
def rect(x,y,L,H,x0=0,y0=0):
if 0<x-x0<L and 0<y-y0<H:
return -1
else:
return 1
def rect_rim(L,H,a,x0=0,y0=0):
tab1 = np.arange(x0,L+x0,a)[:,np.newaxis]
h = np.hstack((tab1,H*np.ones((len(tab1),1))+y0))
b = np.hstack((tab1,np.zeros((len(tab1),1))+y0))
tab2 = np.arange(y0+a,H+y0,a)[:,np.newaxis]
g = np.hstack((np.zeros((len(tab2),1))+x0,tab2))
d = np.hstack((L*np.ones((len(tab2),1))+x0,tab2))
hp = np.array([[L+x0,H+y0]])
bp = np.array([[L+x0,0]])
return np.vstack((h,b,g,d,hp,bp))
# sample with a square 1*1
L = 1
H = 1
dl = 0.05
sol = in_curve(rect,[L,H],(100,100),dl)
sol_tri,Nint,Nbord = triang(sol,dl,rect,[L,H],rect_rim(L,H,dl))
# plt.plot(sol_tri[:,0],sol_tri[:,1],linestyle="",marker="+",label="tri")
# plt.plot(sol[:,0],sol[:,1],linestyle="",marker="x")
# plt.legend()
# plt.show()
# triangulation
tri = scipy.spatial.Delaunay(sol_tri)
# plt.triplot(sol_tri[:,0],sol_tri[:,1],tri.simplices)
# plt.show()
M = M(sol_tri,tri,Nint)
valp,vecp = np.linalg.eig(M) # eigenvalues and eigenvectors
vecp = np.real(vecp)
# comparison with the exact solution:
T = 1000
U = np.arange(0,T,1)
NUsim = np.array([len(count_vp(valp,u)) for u in U])
NU = np.array([len(rect_drum(L,H,u)) for u in U])
plt.plot(U,NUsim,label='simulation')
plt.plot(U,NU,label='exacts')
plt.legend()
plt.show()
# 3D plot of an eigenvector
vecp_tot = np.vstack((vecp,np.zeros((Nbord,Nint))))
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot_trisurf(sol_tri[:,0],sol_tri[:,1],vecp_tot[:,0],triangles=tri.simplices)
plt.show()
The laplacian is the function named "M".
The "in_curve function" return the points inside a curve defined by f(x,y,*fargs) < 0 (a square in the sample).
The "triang" function return points with added points (triangle meshs). The fonction uses an another function for the rim of the curve (for most precision), in the sample it is the "rect_rim" function.
I used the formula given at https://en.wikipedia.org/wiki/Discrete_Laplace_operator ("mesh laplacians").
I have solve my problem : it's a sign and a rim problems.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import scipy.spatial
def rect_drum(L,H,U):
vals = []
val = 0
k = 1
l = 1
while val >= -U:
while val >= -U:
val = -np.pi**2*((k/L)**2+(l/H)**2)
if val >= -U:
vals.append(val)
l += 1
l = 1
k += 1
val = -np.pi**2*((k/L)**2+(l/H)**2)
return np.array(vals)
def count_vp(tab,U):
#count the n eigenvalues greater than equal to -U in the array tab
return tab[tab>=-U]
def in_curve(f,fargs,shape,a):
points = [] # the points inside the curve
for j in range(shape[0]):
for i in range(shape[1]):
if f(i*a,j*a,*fargs) < 0:
points.append([i*a,j*a])
return np.array(points)
def triang(points,a,f,fargs,bord):
tri_points = points.copy()
tri_points[:,1] *= np.sqrt(3)
tri_points2 = np.vstack((points,bord))
tri_points2[:,1] *= np.sqrt(3)
tri_points2[:,0] += a/2
tri_points2[:,1] += np.sqrt(3)/2*a
fin = np.vstack((tri_points,tri_points2))
i = 0
eps = 0.01
while i < len(fin):
if f(fin[i,0]+eps,fin[i,1]+eps,*fargs) > 0:
fin = np.delete(fin,i,0)
i -= 1
i += 1
return np.vstack((fin,bord)),len(fin),len(bord)
def tri_ang(points,ind,p0):
# sort the points in trigonometric order
vec=np.arctan2((points-p0)[:,1],(points-p0)[:,0])
values = []
dtype = [('val',float),('n',int)]
for i in range(len(vec)):
values.append((vec[i],i))
values = np.sort(np.array(values,dtype),order='val')
new_points = []
new_ind = []
for tup in values:
new_points.append(points[tup[1]])
new_ind.append(ind[tup[1]])
return np.array(new_points),np.array(new_ind)
def Laplacian(points,tri,Nint):
indptr,ind = tri.vertex_neighbor_vertices
W = np.zeros((Nint,Nint)) # cotangents matrix
A = np.zeros((Nint,1)) # surfacesvertex aray of point i (A[i])
for i in range(Nint):
tot = 0
nhb_ind = ind[indptr[i]:indptr[i+1]] # indices of the points close to the point of index k
nhb = points[nhb_ind] # their coordinates
nhb,nhb_ind = tri_ang(nhb,nhb_ind,points[i]) #the coordinates (nhb) and (nhb_ind) of each neighbor of i
for j in range(len(nhb_ind)):
vec = nhb[j]-points[i] # a vector connecting the point to his neighbor of index 0
vec_av = nhb[j-1]-points[i] # another vector but with the Vosin from before
if j+1 >= len(nhb_ind):
k = 0
else:
k = j+1
vec_ap = nhb[k]-points[i] # another vector but with the next neighbor
# we use the cross product to calculate the areas of the triangles: ||AxB||/2:
A[i] += 0.5/3*np.linalg.norm(np.cross(vec,vec_av))
# we use the cross product and scalar product to calculate the cotangents: A.B/||AxB||
cotan_alpha = np.dot(vec_av,vec_av-vec)/np.linalg.norm(np.cross(vec_av,vec_av-vec))
cotan_beta = np.dot(vec_ap,vec_ap-vec)/np.linalg.norm(np.cross(vec_ap,vec_ap-vec))
tot += cotan_alpha+cotan_beta
if nhb_ind[j] < Nint:
W[i,nhb_ind[j]] = 0.5*(cotan_alpha+cotan_beta)
W[i,i] = -0.5*tot # diagonal values
return (1/A)*W
def rect(x,y,L,H,x0=0,y0=0):
if 0<x-x0<L and 0<y-y0<H:
return -1
else:
return 1
def rect_rim(L,H,a,x0=0,y0=0):
tab1 = np.arange(x0,L+x0,a)[:,np.newaxis]
h = np.hstack((tab1,H*np.ones((len(tab1),1))+y0))
b = np.hstack((tab1,np.zeros((len(tab1),1))+y0))
tab2 = np.arange(y0+a,H+y0,a)[:,np.newaxis]
g = np.hstack((np.zeros((len(tab2),1))+x0,tab2))
d = np.hstack((L*np.ones((len(tab2),1))+x0,tab2))
hp = np.array([[L+x0,H+y0]])
bp = np.array([[L+x0,0]])
return np.vstack((h,b,g,d,hp,bp))
# sample with a square 1*1
L = 1
H = 1
dl = 0.04
sol = in_curve(rect,[L,H],(100,100),dl)
sol_tri,Nint,Nbord = triang(sol,dl,rect,[L,H],rect_rim(L,H,dl))
# triangulation
tri = scipy.spatial.Delaunay(sol_tri)
M = Laplacian(sol_tri,tri,Nint)
valp,vecp = np.linalg.eig(M) # eigenvalues and eigenvectors
vecp = np.real(vecp)
# comparison with the exact solution:
T = 1000
U = np.arange(0,T,1)
NUsim = np.array([len(count_vp(valp,u)) for u in U])
NU = np.array([len(rect_drum(L,H,u)) for u in U])
plt.plot(U,NUsim,label='simulation')
plt.plot(U,NU,label='exacts')
plt.legend()
plt.show()
# 3D plot of an eigenvector
mode = 0 # change this for an another mode
vecp_tot = np.vstack((vecp,np.zeros((Nbord,Nint))))
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot_trisurf(sol_tri[:,0],sol_tri[:,1],vecp_tot[:,mode],triangles=tri.simplices)
plt.show()
Notes :
1- The hight eigenvalues are false : it's an effect of discretisation.
2- If dl is too small, we have false eigenvectors and eigenvalues (at the top of valp and firsts vectors of vecp), it's probably due to the quality of the meshing.
How to edit the for cycles under #ax5 and #ax6 to plot graphs in the same fashion? Now, the lower figure has no colour transit, as opposed to the upper one. The colour transit appears in the lower figure after increasing of dpi, however, some unwanted stuff also appears. Is there a scalling problem? Thank you
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.gridspec import GridSpec
import math
fig, ax = plt.subplots()
plt.rcParams["figure.figsize"] = [8, 8]
# Function for plotting parallels to curves
def get_parallels(length=.1):
px, py = [], []
for idx in range(len(x)-1):
x0, y0, xa, ya = x[idx], y[idx], x[idx+1], y[idx+1]
dx, dy = xa-x0, ya-y0
norm = math.hypot(dx, dy) * 1/length
dx /= norm
dy /= norm
px.append(x0-dy)
py.append(y0+dx)
return px, py
def offset(x,y, o):
""" Offset coordinates given by array x,y by o """
X = np.c_[x,y].T
m = np.array([[0,-1],[1,0]])
R = np.zeros_like(X)
S = X[:,2:]-X[:,:-2]
R[:,1:-1] = np.dot(m, S)
R[:,0] = np.dot(m, X[:,1]-X[:,0])
R[:,-1] = np.dot(m, X[:,-1]-X[:,-2])
On = R/np.sqrt(R[0,:]**2+R[1,:]**2)*o
Out = On+X
return Out[0,:], Out[1,:]
dpi = 20
def offset_curve(ax, x,y, o):
""" Offset array x,y in data coordinates
by o in points """
trans = ax.transData.transform
inv = ax.transData.inverted().transform
X = np.c_[x,y]
Xt = trans(X)
xto, yto = offset(Xt[:,0],Xt[:,1],o*dpi/72. )
Xto = np.c_[xto, yto]
Xo = inv(Xto)
return Xo[:,0], Xo[:,1]
fig = plt.figure(constrained_layout=True)
gs = GridSpec(3, 6, figure=fig)
ax5 = fig.add_subplot(gs[1, 3:6])
ax6 = fig.add_subplot(gs[2, :3])
ax7 = fig.add_subplot(gs[2, 3:6])
cmap = plt.get_cmap('Greys_r')
# ax5
x = np.linspace(-1, 1, 100)
y = -x**2
ax5.set_ylim(-1.02, 0.3)
width_l = ax5.get_ylim()[1] - ax5.get_ylim()[0]
for t in np.linspace(0, 1, 40):
length = -0.1*width_l*t
ax5.plot(*get_parallels(length=length), color=cmap(t/2 + 0.25))
# ax6
x = np.linspace(-3, 3, 100)
y = -(1/4*x**4 - 1.6*x**2)
ax6.plot(x, y)
ax6.set_xlim(ax6.get_xlim()[0]-0.5, ax6.get_xlim()[1]+0.5)
ax6.scatter(1/2*(ax6.get_xlim()[0] + ax6.get_xlim()[1]), 1.2, marker = 'o', s=900, facecolors='none')
lines = []
width_l = ax6.get_ylim()[1] - ax6.get_ylim()[0]
for t in np.linspace(0, 1, 40):
l, = ax6.plot(x, y - t * 0.1 * width_l, color=cmap(t/2 + 0.25))
lines.append(l)
def plot_rainbow(event=None):
x0 = x
y0 = y
for i in range(len(lines)):
xx, yy = offset_curve(ax, x0, y0, -width_l)
lines[i].set_data(xx, yy)
lines[i].set_linewidth(1.1*width_l)
x0 = xx
y0 = yy
plot_rainbow()
fig.canvas.mpl_connect("resize_event", plot_rainbow)
fig.canvas.mpl_connect("button_release_event", plot_rainbow)
plt.savefig('fig.pdf')
How to edit this code to have the same width and colour map as in the following figure? The script is based on this question.
import numpy as np
import matplotlib.pyplot as plt
dpi = 100
def offset(x,y, o):
""" Offset coordinates given by array x,y by o """
X = np.c_[x,y].T
m = np.array([[0,-1],[1,0]])
R = np.zeros_like(X)
S = X[:,2:]-X[:,:-2]
R[:,1:-1] = np.dot(m, S)
R[:,0] = np.dot(m, X[:,1]-X[:,0])
R[:,-1] = np.dot(m, X[:,-1]-X[:,-2])
On = R/np.sqrt(R[0,:]**2+R[1,:]**2)*o
Out = On+X
return Out[0,:], Out[1,:]
def offset_curve(ax, x,y, o):
""" Offset array x,y in data coordinates
by o in points """
trans = ax.transData.transform
inv = ax.transData.inverted().transform
X = np.c_[x,y]
Xt = trans(X)
xto, yto = offset(Xt[:,0],Xt[:,1],o*dpi/72. )
Xto = np.c_[xto, yto]
Xo = inv(Xto)
return Xo[:,0], Xo[:,1]
x = np.linspace(-3, 3, 100)
y = -(1/4*x**4 - 1.6*x**2)
fig, ax=plt.subplots(figsize=(4,2.5), dpi=dpi)
cmap = plt.get_cmap('Greys_r')
lw = 2.
lines = []
width_l = ax.get_ylim()[1] - ax.get_ylim()[0]
for t in np.linspace(0, 1, 40):
l, = ax.plot(x, y - t * 0.1 * width_l, color=cmap(t/2 + 0.25))
lines.append(l)
def plot_rainbow(event=None):
# initialization of lists
xr, yr = 6*[None], 6*[None]
xr[0],yr[0] = offset_curve(ax, x,y, lw/2.)
xr[1],yr[1] = offset_curve(ax, x,y, -lw/2.)
xr[2],yr[2] = offset_curve(ax, xr[0],yr[0], lw)
xr[3],yr[3] = offset_curve(ax, xr[1],yr[1], -lw)
xr[4],yr[4] = offset_curve(ax, xr[2],yr[2], lw)
xr[5],yr[5] = offset_curve(ax, xr[3],yr[3], -lw)
for i in range(6):
lines[i].set_data(xr[i], yr[i])
plot_rainbow()
fig.canvas.mpl_connect("resize_event", plot_rainbow)
fig.canvas.mpl_connect("button_release_event", plot_rainbow)
plt.show()
The figure above was created by the following script:
import numpy as np
import matplotlib.pyplot as plt
import math
dpi = 100
# Function for plotting parallels to curves
def get_parallels(length=.1):
px, py = [], []
for idx in range(len(x)-1):
x0, y0, xa, ya = x[idx], y[idx], x[idx+1], y[idx+1]
dx, dy = xa-x0, ya-y0
norm = math.hypot(dx, dy) * 1/length
dx /= norm
dy /= norm
px.append(x0-dy)
py.append(y0+dx)
return px, py
fig, ax=plt.subplots(figsize=(4,2.5), dpi=dpi)
cmap = plt.get_cmap('Greys_r')
x = np.linspace(-1, 1, 100)
y = -x**2
ax.set_ylim(-1.02, 0.3)
ax.scatter(1/2*(ax.get_xlim()[0] + ax.get_xlim()[1]), 0.145, marker = 'o', s=900, facecolors='none')
width_l = ax.get_ylim()[1] - ax.get_ylim()[0]
for t in np.linspace(0, 1, 40):
length = -0.1*width_l*t
ax.plot(*get_parallels(length=length), color=cmap(t/2 + 0.25))
plt.tight_layout()
plt.show()
Several curves are plotted in camp and the length is set.
I would like to have the same "shadow" for the curve in the first scrip. How to do that, please?
I want to create an interactive scatter plot so the user can select points with the cursor, so the chosen points are highlighted and the rest are faded.
Right now it only works if the color is changed, how can i change the opacity and keep the original colors?
import numpy as np
from numpy.random import rand
from matplotlib.widgets import LassoSelector
from matplotlib.path import Path
import matplotlib.pyplot as plt
class SelectFromCollection(object):
def __init__(self, ax, collection,c, alpha_other=0.3):
self.canvas = ax.figure.canvas
self.collection = collection
self.alpha_other = alpha_other
self.xys = collection.get_offsets()
self.Npts = len(self.xys)
self.c = c
# Ensure that we have separate colors for each object
self.fc = collection.get_facecolors()
if len(self.fc) == 0:
raise ValueError('Collection must have a facecolor')
elif len(self.fc) == 1:
self.fc = np.tile(self.fc, (self.Npts, 1))
self.lasso = LassoSelector(ax, onselect=self.onselect)
self.ind = []
def onselect(self, verts):
path = Path(verts)
self.ind = np.nonzero(path.contains_points(self.xys))[0]
self.fc[:, -1] = self.alpha_other
self.fc[self.ind, -1] = 1
self.collection.set_facecolors(self.fc)
self.canvas.draw_idle()
def disconnect(self):
self.lasso.disconnect_events()
self.fc[:, -1] = 1
self.collection.set_facecolors(self.fc)
self.canvas.draw_idle()
np.random.seed(1)
x, y, c = rand(3, 100)
subplot_kw = dict(xlim=(0, 1), ylim=(0, 1), autoscale_on=False)
fig, ax = plt.subplots(subplot_kw=subplot_kw)
pts = ax.scatter(x, y,c=c, s=100)
selector = SelectFromCollection(ax, pts, c)
plt.show()
Solved, I used the method self.collection.get_facecolors(), to get the format and values, then I just changed the value of the 3rd column for the chosen indices like this:
fc = self.collection.get_facecolors()
fc[self.ind, 3] = 1
fc[others, 3] = self.alpha_other
self.collection.set_facecolors(fc)
cheers