How to avoid plotting a line through a given point in Matplotlib? - python

I made an animation of an orbit that plots an orbit using an initial altitude and velocity.
Here is the code:
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import ode
import matplotlib.animation as animation
plt.style.use('dark_background')
mu_earth = 398600
# Planet radius, km
R = 6371
fig = plt.figure()
ax1 = fig.add_subplot(111)
orbit_alt = 500
v_mag = 7.7 # km/s
# time step (delta-time)
dt = 10
satellite_size = 100
r_mag = R + orbit_alt
v_esc = np.sqrt(2*mu_earth*1e9/(R*1000 + orbit_alt*1000))
earth = plt.Circle((0, 0), 6371, color='blue')
ax1.add_artist(earth)
def diff_eq(t, y):
rx, ry, vx, vy = y # state vectors
r = np.array([rx, ry])
radial_vector = np.linalg.norm(r)
ax, ay = -r * mu_earth / radial_vector ** 3
return [vx, vy, ax, ay]
# Initial states
r0 = [r_mag, 0]
v0 = [0, v_mag]
specific_energy = (v_mag * 1000 * v_mag * 1000) / 2 - ((mu_earth * 1e9) / ((R + orbit_alt) * 1000))
semi_major = -mu_earth * 1e9 / (2 * specific_energy)
orbit_period = 2 * np.pi * np.sqrt(semi_major ** 3 / (mu_earth * 1e9))
if v_mag * 1000 < v_esc:
tspan = 5 * orbit_period
else:
tspan = 1500000
eccen = ((v_mag * 1000) ** 2 * (r_mag)*1000)/(mu_earth*1e9) - 1
perigee = (semi_major * (1 - np.abs(eccen)))/1000
apogee = (semi_major * (1 + np.abs(eccen)))/1000
n_steps = int(np.ceil(tspan/dt))
ts = np.zeros((n_steps, 1))
ys = np.zeros((n_steps, 4))
ys0 = r0 + v0
ts[0] = 0
ys[0] = ys0
solver = ode(diff_eq)
solver.set_integrator('lsoda')
solver.set_initial_value(ys0, 0)
rs = ys[:, :2]
def animate(i):
solver.integrate(solver.t + dt)
ts[i] = solver.t
ys[i] = solver.y
point = plt.Circle((ys[i][0], ys[i][1]), satellite_size, facecolor=(1, 1, 1))
ax1.add_artist(point)
satellite, = ax1.plot(rs[:, 0], rs[:, 1], color='r', alpha = 1)
return satellite, point
ax1.set_xlim([-apogee, apogee])
ax1.set_ylim([-apogee, apogee])
ax1.set_xlabel('X (km)')
ax1.set_ylabel('Y (km)')
ax1.set_title("ORBIT SIMULATION")
plt.gca().set_aspect('equal', adjustable='box')
ani = animation.FuncAnimation(fig, animate, interval=0.01, blit=True)
plt.show()
And here is a screen shot of the output:
I want to omit the line that goes to the origin (0, 0). That line is a part of the satellite line object. What I believe is happening is that the origin is considered the first point, and the satellite is the second point, and matplotlib is connecting them. So how could that line leading to the middle be omitted?

The main problem is that your data is a full vector of zeros and your code plots the full dataset. Change from rs[:, 0] to rs[:, 0][:i] to only plot the points that have been simulated so far.
Second consideration is that FuncAnimation is usually used to update data already plotted. See small change below in animate(i).
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import ode
import matplotlib.animation as animation
plt.style.use('dark_background')
mu_earth = 398600
# Planet radius, km
R = 6371
fig = plt.figure()
ax1 = fig.add_subplot(111)
orbit_alt = 500
v_mag = 7.7 # km/s
# time step (delta-time)
dt = 10
satellite_size = 100
r_mag = R + orbit_alt
v_esc = np.sqrt(2*mu_earth*1e9/(R*1000 + orbit_alt*1000))
earth = plt.Circle((0, 0), 6371, color='blue')
ax1.add_artist(earth)
def diff_eq(t, y):
rx, ry, vx, vy = y # state vectors
r = np.array([rx, ry])
radial_vector = np.linalg.norm(r)
ax, ay = -r * mu_earth / radial_vector ** 3
return [vx, vy, ax, ay]
# Initial states
r0 = [r_mag, 0]
v0 = [0, v_mag]
specific_energy = (v_mag * 1000 * v_mag * 1000) / 2 - ((mu_earth * 1e9) / ((R + orbit_alt) * 1000))
semi_major = -mu_earth * 1e9 / (2 * specific_energy)
orbit_period = 2 * np.pi * np.sqrt(semi_major ** 3 / (mu_earth * 1e9))
if v_mag * 1000 < v_esc:
tspan = 5 * orbit_period
else:
tspan = 1500000
eccen = ((v_mag * 1000) ** 2 * (r_mag)*1000)/(mu_earth*1e9) - 1
perigee = (semi_major * (1 - np.abs(eccen)))/1000
apogee = (semi_major * (1 + np.abs(eccen)))/1000
n_steps = int(np.ceil(tspan/dt))
ts = np.zeros((n_steps, 1))
ys = np.zeros((n_steps, 4))
ys0 = r0 + v0
ts[0] = 0
ys[0] = ys0
solver = ode(diff_eq)
solver.set_integrator('lsoda')
solver.set_initial_value(ys0, 0)
rs = ys[:, :2]
orbit, = ax1.plot(rs[:, 0], rs[:, 1], color='r', alpha=1)
point = plt.Circle((ys[0][0], ys[0][1]), satellite_size, facecolor=(1, 1, 1))
ax1.add_artist(point)
def animate(i):
solver.integrate(solver.t + dt)
ts[i] = solver.t
ys[i] = solver.y
orbit.set_data(rs[:, 0][:i], rs[:, 1][:i])
point.set_center((ys[i][0], ys[i][1]))
return orbit, point
ax1.set_xlim([-apogee, apogee])
ax1.set_ylim([-apogee, apogee])
ax1.set_xlabel('X (km)')
ax1.set_ylabel('Y (km)')
ax1.set_title("ORBIT SIMULATION")
plt.gca().set_aspect('equal', adjustable='box')
ani = animation.FuncAnimation(fig, animate, interval=10, blit=True)
plt.show()

Related

Animating two figures with different data units using matplotlib

Trying to plot two separate animations, i.e. in different windows as separate figures. Running this code for me rightly creates two windows, but animates the data on the second figure at the same time. Closing figure 1 results in only the intended data for figure 2 being animated, removing the overlap from the data intended for figure 1. Closing figure 2 results in only the intended data for figure 1 being animated, removing the overlap from the data intended for figure 2.
Minimum code below:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
dx, dv, N, Nb, decp = 2, 1.5, 100, 12, int(1)
Pd = np.zeros([N + 1, 2 * Nb])
Vd = np.zeros([N + 1, 2 * Nb])
Pd[:, 1] = 4
Vd[:, 3] = 2
t = np.zeros(N + 1)
t[0] = 0
for i in range(0, N):
t[i + 1] = (i + 1) * 0.1
Px = []
for i in range(0, (2 * Nb)):
PX = dx * (-Nb + i) / 4
Px.append(PX)
lblx = []
for i in range(0, int((Nb / 2) + 1)):
if i == (Nb / 4):
LBL = r"$\mu_x$"
lblx.append(LBL)
else:
LBL = r"${0}\sigma_x$".format(-(Nb / 4) + i)
lblx.append(LBL)
Pv = []
for i in range(0, (2 * Nb)):
PV = dv * (-Nb + i) / 4
Pv.append(PV)
lblv = []
for i in range(0, int((Nb / 2) + 1)):
if i == (Nb / 4):
LBL = r"$\mu_v$"
lblv.append(LBL)
else:
LBL = r"${0}\sigma_v$".format(-(Nb / 4) + i)
lblv.append(LBL)
fig1 = plt.figure(figsize=(8,6))
def animatex(i):
fig1.clear()
plt.bar(Px, Pd[i, :], width = dx / 4, align = 'edge', color = 'b', \
label = 't = {} seconds'.format(round(t[i], decp)))
s_ticks = np.arange(-3 * dx, (3 + 1) * dx, dx)
plt.xticks(s_ticks, lblx)
plt.ylim(0, np.max(Pd))
plt.xlim(-3 * dx, 3 * dx)
plt.legend()
plt.draw()
anix = FuncAnimation(fig1, animatex, repeat = True, interval = 200, frames = N + 1)
fig2 = plt.figure(figsize=(8,6))
def animatev(i):
fig2.clear()
plt.bar(Pv, Vd[i, :], width = dv / 4, align = 'edge', color = 'b', \
label = 't = {} seconds'.format(round(t[i], decp)))
s_ticks = np.arange(-3 * dv, (3 + 1) * dv, dv)
plt.xticks(s_ticks, lblv)
plt.ylim(0, np.max(Vd))
plt.xlim(-3 * dv, 3 * dv)
plt.legend()
plt.draw()
aniv = FuncAnimation(fig2, animatev, repeat = True, interval = 200, frames = N + 1)
plt.show()
As is probably clear, they are two bar plots, with different vertical and horizontal dimensions. I've seen some solutions for these kinds of problems where the data shares an axis through a shared variable, but here they are not (as can be seen).
For this minimum code, the solution involves having the two bars, one in Pd and the other in Vd, being on their respective intended figures, not both on the second figure.
Let me know if there are any issues with the information here i.e. minimal code requirements not met, more information etc. and I will update.
Ignore any wayward writing style, it is not relevant.
Simplifying your code:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
dx, dv, N, Nb, decp = 2, 1.5, 10, 12, int(1)
Px = np.arange(Nb)
Pd = np.random.randn(N, Nb)
Vd = np.random.randn(N, Nb)
fig1, ax1 = plt.subplots(figsize=(8, 6))
def animatex(i):
ax1.clear()
ax1.bar(Px, Pd[i, :], width=dx / 4, align='edge', color='b')
anix = FuncAnimation(fig1, animatex, repeat=True, interval=200, frames=N)
fig2, ax2 = plt.subplots(figsize=(8, 6))
def animatev(i):
ax2.clear()
ax2.bar(Px, Vd[i, :], width = dv / 4, align='edge', color='b')
aniv = FuncAnimation(fig2, animatev, repeat=True, interval=200, frames=N)
plt.show()
works fine for me. You can add the esthetic/data details back in...

How to add common Tittle, label and legend in Matplotlib.pyplot?

My following code can't add a common title, common legend also a common x and y label. When I add title, legend in between the for loop it works but its not working for the whole figure?
I want to add a common title, common legend, and shared x and y label in this plot. What is the best way to do that?
My code
#position vs Time
h = 0.05
nrow, ncol, count = 2,4,1
plt.figure(figsize=(20,10))
plt.title("Common Title") #does not working
with plt.style.context('ggplot'):
for inv in range(8):
plt.subplot(nrow, ncol, count)
x = [1]
v = [0]
rk4_x = [1] #position
rk4_v = [0] #velocity
t = np.arange(0, 30, 0.1)
for i in range(len(t)-1):
k1 = h*v[i]
l1 = h*-(x[i])
k2 = h*(v[i]+l1)
l2 = h*-(x[i]+k1)
x_value = x[i]+0.5*(k1+k2)
x.append(x_value)
v_value = v[i]+0.5*(l1+l2)
v.append(v_value)
k1 = h*rk4_v[i]
l1 = h*-(rk4_x[i])
k2 = h*(rk4_v[i]+0.5*l1)
l2 = h*-(rk4_x[i]+0.5*k1)
k3 = h*(rk4_v[i]+0.5*l2)
l3 = h*-(rk4_x[i]+0.5*k2)
k4 = h*(rk4_v[i]+l3)
l4 = h*-(rk4_x[i]+k3)
x_value = rk4_x[i]+(1/6)*(k1+2*k2+2*k3+k4)
rk4_x.append(x_value)
v_value = rk4_v[i]+(1/6)*(l1+2*l2+2*l3+l4)
rk4_v.append(v_value)
# with plt.style.context('ggplot'):
# plt.figure()
plt.plot(t, x,'r', label= "Position vs Time RK2")
plt.plot(t, rk4_x, 'k--', label = "Position vs Time RK4")
plt.ylim(-1.5, 1.5)
h = round(h,3)
plt.title("h="+str(h), fontsize= '11')
plt.grid('on')
plt.tick_params(labelcolor='none', which='both', top=False, bottom=False, left=False, right=False)
plt.xlabel('Time')
plt.ylabel('Position')
plt.legend()
h = h + 0.05
count = count+1
Use subplots with fig, axs. See the document on matplotlib.pyplot.subplots
And you should use Numpy array not the python list to speed up the numerical calculation. (Not this example, but generally).
Also, If the amount of calculation increases, it seems necessary to make the formula simple. Like x[i+1] = (1 - 0.5 * h**2) * x[i]+ h * v[i]
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
matplotlib.use('Agg')
plt.style.use('ggplot')
def get_x_v(h, t):
x = np.ones_like(t)
v = np.zeros_like(t)
rk4_x = np.ones_like(t)
rk4_v = np.zeros_like(t)
for i, _ in enumerate(t[:-1]):
k1 = h * (v[i])
l1 = h * (x[i]) * -1
k2 = h * (v[i] + l1)
l2 = h * (x[i] + k1) * -1
x[i+1] = x[i] + 0.5 * (k1 + k2)
v[i+1] = v[i] + 0.5 * (l1 + l2)
rk1 = h * (rk4_v[i])
rl1 = h * (rk4_x[i]) * -1
rk2 = h * (rk4_v[i] + 0.5 * rl1)
rl2 = h * (rk4_x[i] + 0.5 * rk1) * -1
rk3 = h * (rk4_v[i] + 0.5 * rl2)
rl3 = h * (rk4_x[i] + 0.5 * rk2) * -1
rk4 = h * (rk4_v[i] + rl3)
rl4 = h * (rk4_x[i] + rk3) * -1
rk4_x[i+1] = rk4_x[i]+(1/6)*(rk1+2*rk2+2*rk3+rk4)
rk4_v[i+1] = rk4_v[i]+(1/6)*(rl1+2*rl2+2*rl3+rl4)
return x, v, rk4_x, rk4_v
h = 0.05
ncol, nrow = 4, 2
fig, axs = plt.subplots(nrows=nrow, ncols=ncol, figsize=(20, 10))
fig.suptitle('Common Title', y=0.94, fontsize=20)
fig.supxlabel('Common X Label', y=0.05, fontsize=14)
t = np.arange(0, 30, 0.1)
for ax in axs.flat:
x, v, rk4_x, rk_v = get_x_v(h, t)
ax.plot(t, x, 'r', label="Position vs Time RK2")
ax.plot(t, rk4_x, 'k--', label="Position vs Time RK4")
ax.set_ylim(-1.5, 1.5)
ax.set_title('h={0}'.format(str(round(h,3))), fontsize='11')
ax.grid('on')
ax.tick_params(labelcolor='none', which='both',
top=False, bottom=False, left=False, right=False)
ax.set_xlabel('Time')
ax.set_ylabel('Position')
ax.legend(loc=1)
ax.set_facecolor('gray')
h += 0.05
plt.savefig('test.png')

Why these two ODE solving program don't match?

I don't understand why this code (reference):
from numpy import zeros, linspace
import matplotlib.pyplot as plt
# Time unit: 1 h
beta = 10./(40*8*24)
gamma = 3./(15*24)
dt = 0.1 # 6 min
D = 30 # Simulate for D days
N_t = int(D*24/dt) # Corresponding no of hours
t = linspace(0, N_t*dt, N_t+1)
S = zeros(N_t+1)
I = zeros(N_t+1)
R = zeros(N_t+1)
# Initial condition
S[0] = 50
I[0] = 1
R[0] = 0
# Step equations forward in time
for n in range(N_t):
S[n+1] = S[n] - dt*beta*S[n]*I[n]
I[n+1] = I[n] + dt*beta*S[n]*I[n] - dt*gamma*I[n]
R[n+1] = R[n] + dt*gamma*I[n]
fig = plt.figure()
l1, l2, l3 = plt.plot(t, S, t, I, t, R)
fig.legend((l1, l2, l3), ('S', 'I', 'R'), 'upper left')
plt.xlabel('hours')
plt.show()
doesn't produce the same results as this code I made:
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
beta = 10. / (40 * 8 * 24)
gamma = 3. / (15 * 24)
def flu(y, t):
S = y[0]
P = y[1]
R = y[2]
S = - beta * S * P
P = beta * S * P - gamma * P
R = gamma * P
return [S, P, R]
C_I = [50, 1, 0]
t = np.linspace(0, 1000, 1000)
y = odeint(flu, C_I, t)
S = y[:, 0]
P = y[:, 1]
R = y[:, 2]
fig, ax = plt.subplots()
ax.plot(t, S, 'b--', label='S')
ax.plot(t, P, 'r--', label='I')
ax.plot(t, R, 'g--', label='R')
legend = ax.legend(loc='upper right', shadow=True, fontsize='x-large')
legend.get_frame().set_facecolor('#FFFCCC')
plt.show()
I used P instead of I to avoid confusion.
The equations solved with odeint should be the same as the ones provided in the reference link above. And if the equations I use are correct, which I am convinced they are, I don't understand where the mistake(s) lie(s).
Thank you for your help
You set S=y[0] then you set S=- beta * S * P. This overwrites y[0]!!! Similar problems for P and R
Try this:
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
beta = 10. / (40 * 8 * 24)
gamma = 3. / (15 * 24)
def flu(y, t):
S = y[0]
P = y[1]
R = y[2]
dS = - beta * S * P
dP = beta * S * P - gamma * P
dR = gamma * P
return [dS, dP, dR]
C_I = [50, 1, 0]
t = np.linspace(0, 1000, 1000)
y = odeint(flu, C_I, t)
S = y[:, 0]
P = y[:, 1]
R = y[:, 2]
fig, ax = plt.subplots()
ax.plot(t, S, 'b--', label='S')
ax.plot(t, P, 'r--', label='I')
ax.plot(t, R, 'g--', label='R')
legend = ax.legend(loc='upper right', shadow=True, fontsize='x-large')
legend.get_frame().set_facecolor('#FFFCCC')
plt.show()

What is the best solution to move the 6 different blue balls with pyGTK?

what would be the best recommendation in order to move the 6 blue balls around the 3D field
a) with a pygtk and threading matplot for the plot window? (menue in pygtk where the new position is given manually and the balls would move after a "Click Go")
b) or something else?
It seems that matplot is slow: 4s after starting the attached python script, the plot windows open and everything appear.
Moving later manually the window (rotation) is very slow too (it is mandatory, I have access to that manual rotation function of the plot window in order to observe the position of the balls in the 3D field).
Perhaps another library or programm should be used?
For the reason 6 additional balls (light grey) must be added, it will become probably slow. The 3D rendering dont has to be good (just a bit non transparent 3D).
Any recommendation is welcome before I go too far in that pygtk and matplotlib direction.
The programm is for hobby sport and will help beginners to know where to have their position in a field and how to move (3D.. NOT 2D)
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
import matplotlib.axes as axs
import numpy as np
import math
from pylab import figure
# parameter in m
#swimminpool_width
s_w = 10
#swimmingpool_length
s_l = 18
#swimmingpool_depth
s_d = 4
#exchange lane width
el_w = 3
# ball radius
b_r = 0.53 / (2 * math.pi)
if __name__ == '__main__':
# basket at the swimmingpool bottom in the middle
x_basket1 = s_w / 2
y_basket1 = 0.24
# basket at the swimmingpool bottom in the middle
x_basket2 = s_w / 2
y_basket2 = s_l - 0.24
#
fig = plt.figure()
ax1 = fig.add_subplot (111,projection='3d')
# field
xG = [0,10,10,0,0, 0,10,10,10,10,10, 0, 0,0, 0,10]
yG = [0, 0, 0,0,0,18,18, 0, 0,18,18,18,18,0,18,18]
zG = [0, 0, 4,4,0, 0, 0, 0, 4, 4, 0, 0, 4,4, 4, 4]
ax1.plot_wireframe (xG,yG,zG,colors= (0,0,1,1)) # blue line game area
# exchange area
xW = [10,13,13,10,10,10,13,13,13,13,13,10,10,10,10,13]
yW = [0, 0, 0, 0, 0,18,18, 0, 0,18,18,18,18, 0,18,18]
zW = [0, 0, 4, 4, 0, 0, 0, 0, 4, 4, 0, 0, 4, 4, 4, 4]
ax1.plot_wireframe (xW,yW,zW,colors= (0,1,1,1)) # light blue line exchange area
#
ax1.set_xlabel('Wide')
ax1.set_ylabel('Length')
ax1.set_zlabel('Depth')
#
# Make data for sphere 80cm radius = player1
# pos Player 1
Pos_xP1 = 1
Pos_yP1 = 1
Pos_zP1 = 4
u = np.linspace(0, 2 * np.pi, 100)
v = np.linspace(0, np.pi, 100)
xP1 = Pos_xP1+ 0.4 * np.outer(np.cos(u), np.sin(v))
yP1 = Pos_yP1+ 0.4 * np.outer(np.sin(u), np.sin(v))
zP1 = Pos_zP1+ 0.4 * np.outer(np.ones(np.size(u)), np.cos(v))
# Plot the surface
ax1.plot_surface(xP1, yP1, zP1,color= (0,0,1,1))
#mark it
i=1
ax1.text(Pos_xP1, Pos_yP1, Pos_zP1, '%s' % (str(i)), size=20,color='k')
#
# Make data for sphere 80cm radius = player2
# pos Player 2 (use later lists)?
Pos_xP2 = 2.5
Pos_yP2 = 1
Pos_zP2 = 4
u = np.linspace(0, 2 * np.pi, 100)
v = np.linspace(0, np.pi, 100)
xP2 = Pos_xP2+ 0.4 * np.outer(np.cos(u), np.sin(v))
yP2 = Pos_yP2+ 0.4 * np.outer(np.sin(u), np.sin(v))
zP2 = Pos_zP2+ 0.4 * np.outer(np.ones(np.size(u)), np.cos(v))
# Plot the surface
ax1.plot_surface(xP2, yP2, zP2,color= (0,0,1,1))
#mark it
i=2
ax1.text(Pos_xP2, Pos_yP2, Pos_zP2, '%s' % (str(i)), size=20,color='k')
#
# Make data for sphere 80cm radius = player3
# pos Player 3
Pos_xP3 = 4
Pos_yP3 = 1
Pos_zP3 = 4
u = np.linspace(0, 2 * np.pi, 100)
v = np.linspace(0, np.pi, 100)
xP3 = Pos_xP3+ 0.4 * np.outer(np.cos(u), np.sin(v))
yP3 = Pos_yP3+ 0.4 * np.outer(np.sin(u), np.sin(v))
zP3 = Pos_zP3+ 0.4 * np.outer(np.ones(np.size(u)), np.cos(v))
# Plot the surface
ax1.plot_surface(xP3, yP3, zP3,color= (0,0,1,1))
#mark it
i=3
ax1.text(Pos_xP3, Pos_yP3, Pos_zP3, '%s' % (str(i)), size=20,color='k')
#
# Make data for sphere 80cm radius = player4
# pos Player 4
Pos_xP4 = 5.5
Pos_yP4 = 1
Pos_zP4 = 4
u = np.linspace(0, 2 * np.pi, 100)
v = np.linspace(0, np.pi, 100)
xP4 = Pos_xP4+ 0.4 * np.outer(np.cos(u), np.sin(v))
yP4 = Pos_yP4+ 0.4 * np.outer(np.sin(u), np.sin(v))
zP4 = Pos_zP4+ 0.4 * np.outer(np.ones(np.size(u)), np.cos(v))
# Plot the surface
ax1.plot_surface(xP4, yP4, zP4,color= (0,0,1,1))
#mark it
i=4
ax1.text(Pos_xP4, Pos_yP4, Pos_zP4, '%s' % (str(i)), size=20,color='k')
#
# Make data for sphere 80cm radius = player5
# pos Player 5
Pos_xP5 = 7
Pos_yP5 = 1
Pos_zP5 = 4
u = np.linspace(0, 2 * np.pi, 100)
v = np.linspace(0, np.pi, 100)
xP5 = Pos_xP5+ 0.4 * np.outer(np.cos(u), np.sin(v))
yP5 = Pos_yP5+ 0.4 * np.outer(np.sin(u), np.sin(v))
zP5 = Pos_zP5+ 0.4 * np.outer(np.ones(np.size(u)), np.cos(v))
# Plot the surface
ax1.plot_surface(xP5, yP5, zP5,color= (0,0,1,1))
#mark it
i=5
ax1.text(Pos_xP5, Pos_yP5, Pos_zP5, '%s' % (str(i)), size=20,color='k')
#
# Make data for sphere 80cm radius = player6
# pos Player 6
Pos_xP6 = 8.5
Pos_yP6 = 1
Pos_zP6 = 4
u = np.linspace(0, 2 * np.pi, 100)
v = np.linspace(0, np.pi, 100)
xP6 = Pos_xP6+ 0.4 * np.outer(np.cos(u), np.sin(v))
yP6 = Pos_yP6+ 0.4 * np.outer(np.sin(u), np.sin(v))
zP6 = Pos_zP6+ 0.4 * np.outer(np.ones(np.size(u)), np.cos(v))
# Plot the surface
ax1.plot_surface(xP6, yP6, zP6,color= (0,0,1,1))
#mark it
i=6
ax1.text(Pos_xP6, Pos_yP6, Pos_zP6, '%s' % (str(i)), size=20,color='k')
#
#
# Make data for sphere ball
posx_ball = 5
posy_ball = 9
posz_ball = b_r
u = np.linspace(0, 2 * np.pi, 100)
v = np.linspace(0, np.pi, 100)
x_ball = posx_ball + b_r * np.outer(np.cos(u), np.sin(v))
y_ball = posy_ball + b_r * np.outer(np.sin(u), np.sin(v))
z_ball = posz_ball + b_r * np.outer(np.ones(np.size(u)), np.cos(v))
# Plot the surface
ax1.plot_surface(x_ball, y_ball, z_ball, color=(1, 0, 0, 1))
#
#use a factor for having y = x in factor
ax1.set_aspect(aspect=0.222)
#
# define the basket1
t = np.linspace(0, np.pi * 2, 16)
#bottom
ax1.plot(x_basket1+0.24*np.cos(t), y_basket1+0.24*np.sin(t), 0, linewidth=1, color='black')
ax1.plot(x_basket1+0.16*np.cos(t), y_basket1+0.16*np.sin(t), 0, linewidth=1, color='black')
#top
ax1.plot(x_basket1+0.24*np.cos(t), y_basket1+0.24*np.sin(t), 0.45, linewidth=1, color='black')
# side bars
A=0
while A < 16:
xBar = [x_basket1+ 0.16 * math.sin(A*22.5*np.pi/180),x_basket1+ 0.24 * math.sin(A*22.5*np.pi/180)]
yBar = [y_basket1+ 0.16 * math.cos(A*22.5*np.pi/180),y_basket1+ 0.24 * math.cos(A*22.5*np.pi/180)]
zBar = [0,0.45]
ax1.plot(xBar,yBar,zBar,color='black')
A = A+1
# define the basket2
t = np.linspace(0, np.pi * 2, 16)
# bottom
ax1.plot(x_basket2 + 0.24 * np.cos(t), y_basket2 + 0.24 * np.sin(t), 0, linewidth=1, color='black')
ax1.plot(x_basket2 + 0.16 * np.cos(t), y_basket2 + 0.16 * np.sin(t), 0, linewidth=1, color='black')
# top
ax1.plot(x_basket2 + 0.24 * np.cos(t), y_basket2 + 0.24 * np.sin(t), 0.45, linewidth=1, color='black')
# side bars
A = 0
while A < 16:
xBar = [x_basket2 + 0.16 * math.sin(A * 22.5 * np.pi / 180),x_basket2 + 0.24 * math.sin(A * 22.5 * np.pi / 180)]
yBar = [y_basket2 + 0.16 * math.cos(A * 22.5 * np.pi / 180),y_basket2 + 0.24 * math.cos(A * 22.5 * np.pi / 180)]
zBar = [0, 0.45]
ax1.plot(xBar, yBar, zBar, color='black')
A = A + 1
#
plt.show()
I'm not sure why this has been downvoted, seems a reasonable question. However, matplotlib is not intended to be a solution for fast plotting (see this answer) and has limited support for 3D, so certainly consider another library for this.
That said, if you are only using 6 balls then I think you can make this work. I'd suggest cleaning up your code using functions for repeated code, for example defining,
def draw_ball(x, y, z, label="", color=(0,0,1,1)):
u = np.linspace(0, 2 * np.pi, 100)
v = np.linspace(0, np.pi, 100)
xP1 = x+ 0.4 * np.outer(np.cos(u), np.sin(v))
yP1 = y+ 0.4 * np.outer(np.sin(u), np.sin(v))
zP1 = z+ 0.4 * np.outer(np.ones(np.size(u)), np.cos(v))
# Plot the surface
b = ax1.plot_surface(xP1, yP1, zP1, color= color)
#mark it
t = ax1.text(x, y, z, '%s' % label, size=20, color='k')
return b, t
would allow you to set everything up with just,
players = []
for i in range(6):
players.append(draw_ball(1+i*1.5, 1, 4, label=str(i+1)))
That said, you'll find that drawing spheres with plot surface/text is too slow, instead I'd recommend using a scatter plot, with some transparency, and changing the data each time as follows,
#Instead, get all positions and plot as a single scatter collection
pos = []
for i in range(6):
pos.append([1+i*1.5, 1, 4])
#Define numpy array which is faster to work with
pos = np.array(pos)
s = ax1.scatter(pos[:,0], pos[:,1], pos[:,2], s=100, alpha = 0.5)
where pos is a 6 by 3 array for all players ball locations.
This can then be updated as follows, from this answer, when pos changes,
s._offsets3d = juggle_axes(pos[:,0], pos[:,1], pos[:,2], 'z')
It should be more efficient to update the whole collection (6 players by 3) all in one go. Adding annotation can be done following the excellent example by #Luchko.
To give you an idea how this all works together, try running the following code,
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
import matplotlib.axes as axs
import numpy as np
import math
from mpl_toolkits.mplot3d.proj3d import proj_transform
from matplotlib.text import Annotation
#based on https://stackoverflow.com/questions/10374930/matplotlib-annotating-a-3d-scatter-plot#34139293
class Annotation3D(Annotation):
'''Annotate the point xyz with text s'''
def __init__(self, s, xyz, *args, **kwargs):
Annotation.__init__(self,s, xy=(0,0), *args, **kwargs)
self._verts3d = xyz
def draw(self, renderer):
xs3d, ys3d, zs3d = self._verts3d
xs, ys, zs = proj_transform(xs3d, ys3d, zs3d, renderer.M)
self.xy=(xs,ys)
Annotation.draw(self, renderer)
def annotate3D(ax, s, *args, **kwargs):
'''add anotation text s to to Axes3d ax'''
tag = Annotation3D(s, *args, **kwargs)
ax.add_artist(tag)
def draw_ball(x, y, z, label="", color=(0,0,1,1)):
u = np.linspace(0, 2 * np.pi, 100)
v = np.linspace(0, np.pi, 100)
xP1 = x+ 0.4 * np.outer(np.cos(u), np.sin(v))
yP1 = y+ 0.4 * np.outer(np.sin(u), np.sin(v))
zP1 = z+ 0.4 * np.outer(np.ones(np.size(u)), np.cos(v))
# Plot the surface
b = ax1.plot_surface(xP1, yP1, zP1, color= color)
#mark it
t = ax1.text(x, y, z, '%s' % label, size=20, color='k')
return b, t
def draw_basket(x, y, z, h, color='black'):
# define the basket1
t = np.linspace(0, np.pi * 2, 16)
#bottom
ax1.plot(x+0.24*np.cos(t), y+0.24*np.sin(t), z, linewidth=1, color=color)
ax1.plot(x+0.16*np.cos(t), y+0.16*np.sin(t), z, linewidth=1, color=color)
#top
ax1.plot(x+0.24*np.cos(t), y+0.24*np.sin(t), z+h, linewidth=1, color=color)
# side bars
A=0
while A < 16:
xBar = [x+ 0.16 * math.sin(A*22.5*np.pi/180),x+ 0.24 * math.sin(A*22.5*np.pi/180)]
yBar = [y+ 0.16 * math.cos(A*22.5*np.pi/180),y+ 0.24 * math.cos(A*22.5*np.pi/180)]
zBar = [0,0.45]
ax1.plot(xBar, yBar, zBar, color=color)
A = A+1
# parameter in m
#swimminpool_width
s_w = 10
#swimmingpool_length
s_l = 18
#swimmingpool_depth
s_d = 4
#exchange lane width
el_w = 3
# ball radius
b_r = 0.53 / (2 * math.pi)
if __name__ == '__main__':
# basket at the swimmingpool bottom in the middle
x_basket1 = s_w / 2
y_basket1 = 0.24
# basket at the swimmingpool bottom in the middle
x_basket2 = s_w / 2
y_basket2 = s_l - 0.24
fig = plt.figure()
ax1 = fig.add_subplot(111,projection='3d')
# field
xG = [0,10,10,0,0, 0,10,10,10,10,10, 0, 0,0, 0,10]
yG = [0, 0, 0,0,0,18,18, 0, 0,18,18,18,18,0,18,18]
zG = [0, 0, 4,4,0, 0, 0, 0, 4, 4, 0, 0, 4,4, 4, 4]
ax1.plot_wireframe (xG,yG,zG,colors= (0,0,1,1)) # blue line game area
# exchange area
xW = [10,13,13,10,10,10,13,13,13,13,13,10,10,10,10,13]
yW = [0, 0, 0, 0, 0,18,18, 0, 0,18,18,18,18, 0,18,18]
zW = [0, 0, 4, 4, 0, 0, 0, 0, 4, 4, 0, 0, 4, 4, 4, 4]
ax1.plot_wireframe (xW,yW,zW,colors= (0,1,1,1)) # light blue line exchange area
ax1.set_xlabel('Wide')
ax1.set_ylabel('Length')
ax1.set_zlabel('Depth')
#use a factor for having y = x in factor
ax1.set_aspect(aspect=0.222)
# Make data for sphere ball
draw_ball(8.5, 1, b_r, label="")
# define the basket1
draw_basket(x_basket1, y_basket1, 0., 0.45)
# define the basket2
draw_basket(x_basket2, y_basket2, 0., 0.45)
#Instead, get all positions and plot as a single scatter collection
pos = []
for i in range(6):
pos.append([1+i*1.5, 1, 4])
#Define numpy array which is faster to work with
pos = np.array(pos)
s = ax1.scatter(pos[:,0], pos[:,1], pos[:,2], s=100, alpha = 0.5)
#Add labels
for j, xyz_ in enumerate(pos):
annotate3D(ax1, s=str(j), xyz=xyz_, fontsize=10, xytext=(-3,3),
textcoords='offset points', ha='right',va='bottom')
#Use interactive mode for quick animation
plt.ion()
plt.show()
# Do 100 steps and add random change to positions
for step in range(100):
for i in range(pos.shape[0]):
pos[i,0] += 0.1*np.random.randn(1)
pos[i,1] += 0.1*np.random.randn(1)
pos[i,2] += 0.1*np.random.randn(1)
s._offsets3d = pos[:,0], pos[:,1], pos[:,2]
plt.pause(0.01)
this appears to allow quick rotation and plotting, at least for me,
#!/usr/bin/python3.5
"""
/* game uwr: field and basket design test module
* Copyright (C) Creative Commons Alike V4.0
* No warranty
*/
"""
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.art3d import juggle_axes
import matplotlib.axes as axs
import numpy as np
import math
from mpl_toolkits.mplot3d.proj3d import proj_transform
from matplotlib.text import Annotation
# parameter in m
#swimminpool_width
s_w = 10
#swimmingpool_length
s_l = 18
#swimmingpool_depth
s_d = 4
#exchange lane width
el_w = 3
# ball radius
b_r = 0.53 / (2 * math.pi)
# coordinate depth_pb1 player blue nb 1
# previous1 and previous2 is array pos_p1_b and array pos_p2_b
# current is array pos_b ..
# target is (from menue gtk) 1m length and 1m deeper
depth_pb1 = 2
length_pb1 = 2
side_pb1 = ((s_w/6)/2)+0*(s_w/6)
text_speed_pb1 = "low"
#based on https://stackoverflow.com/questions/10374930/matplotlib-annotating-a-3d-scatter-plot#34139293
class Annotation3D(Annotation):
'''Annotate the point xyz with text s'''
def __init__(self, s, xyz, *args, **kwargs):
Annotation.__init__(self,s, xy=(0,0), *args, **kwargs)
self._verts3d = xyz
def draw(self, renderer):
xs3d, ys3d, zs3d = self._verts3d
xs, ys, zs = proj_transform(xs3d, ys3d, zs3d, renderer.M)
self.xy=(xs,ys)
Annotation.draw(self, renderer)
def annotate3D(ax, s, *args, **kwargs):
'''add anotation text s to to Axes3d ax'''
tag = Annotation3D(s, *args, **kwargs)
ax.add_artist(tag)
def draw_basket(x, y, z, h, color='black'):
# define the basket1
t = np.linspace(0, np.pi * 2, 16)
#bottom
ax1.plot(x+0.24*np.cos(t), y+0.24*np.sin(t), z, linewidth=1, color=color)
ax1.plot(x+0.16*np.cos(t), y+0.16*np.sin(t), z, linewidth=1, color=color)
#top
ax1.plot(x+0.24*np.cos(t), y+0.24*np.sin(t), z+h, linewidth=1, color=color)
# side bars
A=0
while A < 16:
xBar = [x+ 0.16 * math.sin(A*22.5*np.pi/180),x+ 0.24 * math.sin(A*22.5*np.pi/180)]
yBar = [y+ 0.16 * math.cos(A*22.5*np.pi/180),y+ 0.24 * math.cos(A*22.5*np.pi/180)]
zBar = [0,0.45]
ax1.plot(xBar, yBar, zBar, color=color)
A = A+1
def draw_ball(x, y, z, label="", color=(1,0,0,1)):
global b_r
u = np.linspace(0, 2 * np.pi, 50)
v = np.linspace(0, np.pi, 50)
xP1 = x+ b_r * np.outer(np.cos(u), np.sin(v))
yP1 = y+ b_r * np.outer(np.sin(u), np.sin(v))
zP1 = z+ b_r * np.outer(np.ones(np.size(u)), np.cos(v))
# Plot the surface
b = ax1.plot_surface(xP1, yP1, zP1, color= color)
#mark it
t = ax1.text(x, y, z, '%s' % label, size=20, color='k')
# return b, t
if __name__ == '__main__':
# basket at the swimmingpool bottom in the middle
x_basket1 = s_w / 2
y_basket1 = 0.24
# basket at the swimmingpool bottom in the middle
x_basket2 = s_w / 2
y_basket2 = s_l - 0.24
fig = plt.figure()
ax1 = fig.add_subplot(111,projection='3d')
# field
xG = [0,10,10,0,0, 0,10,10,10,10,10, 0, 0,0, 0,10]
yG = [0, 0, 0,0,0,18,18, 0, 0,18,18,18,18,0,18,18]
zG = [0, 0, 4,4,0, 0, 0, 0, 4, 4, 0, 0, 4,4, 4, 4]
ax1.plot_wireframe (xG,yG,zG,colors= (0,0,1,1)) # blue line game area
# exchange area
xW = [10,13,13,10,10,10,13,13,13,13,13,10,10,10,10,13]
yW = [0, 0, 0, 0, 0,18,18, 0, 0,18,18,18,18, 0,18,18]
zW = [0, 0, 4, 4, 0, 0, 0, 0, 4, 4, 0, 0, 4, 4, 4, 4]
ax1.plot_wireframe (xW,yW,zW,colors= (0,1,1,1)) # light blue line exchange area
ax1.set_xlabel('Wide')
ax1.set_ylabel('Length')
ax1.set_zlabel('Water')
# draw the 2 lines which show the depth
xG1 = [0, 10]
yG1 = [4, 4]
zG1 = [0, 0]
ax1.plot_wireframe(xG1, yG1, zG1, colors=(0, 0, 1, 1),linestyle=':') # blue line
xG2 = [0, 10]
yG2 = [14, 14]
zG2 = [0, 0]
ax1.plot_wireframe(xG2, yG2, zG2, colors=(0, 0, 1, 1),linestyle=':') # blue line
#
# put the axis fix
ax1.set_xlim3d(0, 13)
ax1.set_ylim3d(0, 18)
ax1.set_zlim3d(0, 4)
#
# use a factor for having y = x in factor
ax1.set_aspect(aspect=0.222)
#
# sphere red ball for playing
draw_ball(5, 9, b_r, label="")
#
# define the basket1
draw_basket(x_basket1, y_basket1, 0., 0.45)
# define the basket2
draw_basket(x_basket2, y_basket2, 0., 0.45)
#get all positions and plot as a single scatter collection at all player position blue
pos_b = []
for i in range(6):
# distribute the players at the side with the same distance
# at game start
pos_b.append([((s_w/6)/2)+i*(s_w/6),1, s_d])
#Define numpy array which is faster to work with
pos_b = np.array(pos_b)
# s parameter below is the surface of the scatter point 100 is 80cm diam by non zooming
# c blue
p_b = ax1.scatter(pos_b[:,0], pos_b[:,1], pos_b[:,2], s=400, alpha = 0.5, c=(0, 0, 1, 1))
#Add labels
for j, xyz_ in enumerate(pos_b):
annotate3D(ax1, s=str(j+1), xyz=xyz_, fontsize=10, xytext=(-3,3),
textcoords='offset points', ha='right',va='bottom')
#
#get all positions and plot as a single scatter collection at all player position white
pos_w = []
for i in range(6):
# distribute the players at the side with the same distance
# at game start
pos_w.append([((s_w/6)/2)+i*(s_w/6), (s_l-1), s_d])
#Define numpy array which is faster to work with
pos_w = np.array(pos_w)
# s parameter below is the surface of the scatter point 100 is 80cm diam by non zooming
# c="lightgrey", alpha=0.5 half transparent
p_w = ax1.scatter(pos_w[:,0], pos_w[:,1], pos_w[:,2], s=400, alpha = 0.5, c="darkgrey")
#Add labels
for j, xyz_ in enumerate(pos_w):
annotate3D(ax1, s=str(j+1), xyz=xyz_, fontsize=10, xytext=(-3,3),
textcoords='offset points', ha='right',va='bottom')
#
#
#Use interactive mode for quick animation
plt.ion()
plt.show()
# Linear move in 10 steps
# Do 10 steps and add change to positions in homogenous speed along distance of each player
# delta_pos_pb1_x = side_pb1 - pos_b[0,0]
# delta_pos_pb1_y = length_pb1 - pos_b[0,1]
# delta_pos_pb1_z = depth_pb1 - pos_b[0,2]
delta_pos_pb1_x = 3
delta_pos_pb1_y = 3
delta_pos_pb1_z = -3
delta_pos_pb2_x = 3
delta_pos_pb2_y = 0.5
delta_pos_pb2_z = -3.5
for step in range(10):
pos_b[0,0] += 0.1 * delta_pos_pb1_x
pos_b[0,1] += 0.1 * delta_pos_pb1_y
pos_b[0,2] += 0.1 * delta_pos_pb1_z
pos_b[1, 0] += 0.1 * delta_pos_pb2_x
pos_b[1, 1] += 0.1 * delta_pos_pb2_y
pos_b[1, 2] += 0.1 * delta_pos_pb2_z
# for i in range(pos_b.shape[0]):
# pos_b[i,0]+= 0.01*delta_pos_pb1_x
# pos_b[i,1]+= 0.01*delta_pos_pb1_y
# pos_b[i,2]+= 0.01*delta_pos_pb1_z
# plt.pause(1.0)
# plt.draw()
# s._offsets3d = juggle_axes(pos_b[:,0], pos_b[:,1], pos_b[:,2], 'z')
p_b._offsets3d = pos_b[:, 0], pos_b[:, 1], pos_b[:, 2]
plt.pause(0.001)

Matplotlib plot pulse propagation in 3d

I'd like to plot pulse propagation in such a way at each step, it plots the pulse shape. In other words, I want a serie of x-z plots, for each values of y. Something like this (without color):
How can I do this using matplotlib (or Mayavi)? Here is what I did so far:
def drawPropagation(beta2, C, z):
""" beta2 in ps / km
C is chirp
z is an array of z positions """
T = numpy.linspace(-10, 10, 100)
sx = T.size
sy = z.size
T = numpy.tile(T, (sy, 1))
z = numpy.tile(z, (sx, 1)).T
U = 1 / numpy.sqrt(1 - 1j*beta2*z * (1 + 1j * C)) * numpy.exp(- 0.5 * (1 + 1j * C) * T * T / (1 - 1j*beta2*z*(1 + 1j*C)))
fig = pyplot.figure()
ax = fig.add_subplot(1,1,1, projection='3d')
surf = ax.plot_wireframe(T, z, abs(U))
Change to:
ax.plot_wireframe(T, z, abs(U), cstride=1000)
and call:
drawPropagation(1.0, 1.0, numpy.linspace(-2, 2, 10))
will create the following graph:
If you need the curve been filled with white color:
import numpy
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import pyplot
from matplotlib.collections import PolyCollection
def drawPropagation(beta2, C, z):
""" beta2 in ps / km
C is chirp
z is an array of z positions """
T = numpy.linspace(-10, 10, 100)
sx = T.size
sy = z.size
T = numpy.tile(T, (sy, 1))
z = numpy.tile(z, (sx, 1)).T
U = 1 / numpy.sqrt(1 - 1j*beta2*z * (1 + 1j * C)) * numpy.exp(- 0.5 * (1 + 1j * C) * T * T / (1 - 1j*beta2*z*(1 + 1j*C)))
fig = pyplot.figure()
ax = fig.add_subplot(1,1,1, projection='3d')
U = numpy.abs(U)
verts = []
for i in xrange(T.shape[0]):
verts.append(zip(T[i, :], U[i, :]))
poly = PolyCollection(verts, facecolors=(1,1,1,1), edgecolors=(0,0,1,1))
ax.add_collection3d(poly, zs=z[:, 0], zdir='y')
ax.set_xlim3d(numpy.min(T), numpy.max(T))
ax.set_ylim3d(numpy.min(z), numpy.max(z))
ax.set_zlim3d(numpy.min(U), numpy.max(U))
drawPropagation(1.0, 1.0, numpy.linspace(-2, 2, 10))
pyplot.show()

Categories