Make a sphere from a circle of dots - python

I have a circle a circle from dots
Now I need make a sphere.
Could someone help me with it.
I think i have to use x2 + y2 + z2 <= R2 and use Axes3D module.
import numpy
import matplotlib.pyplot as plt
X = list(range(1, 101))
Y = list(range(1, 101))
x = numpy.array(X)
y = numpy.array(Y)
xgrid, ygrid = numpy.meshgrid(x, y)
plt.style.use('seaborn')
fig, ax = plt.subplots()
filter = (xgrid-50)**2 + (ygrid-50)**2 <= 25**2
ax.scatter(xgrid[filter], ygrid[filter], s= 1, color='green')
ax.set_title('сетка из точек 100х100',
fontfamily = 'monospace',
fontstyle = 'normal',
fontweight = 'bold',
fontsize = 10)
ax.set_xlabel("Value", fontsize=14)
ax.set_ylabel("Square of Value", fontsize=14)
ax.tick_params(axis='both', which='major', labelsize=14)
ax.axis([0, 101, 0, 101])
plt.show()

Is this what you're looking for? I don't recommend plotting that much points though, it is quite heavy, try plotting less points (like in the code comments)...
Also try not using 'filter', it is a reserved keyword in Python
import numpy as np
import matplotlib.pyplot as plt
# define sphere parameters
cX, cY, cZ = 50, 50, 50
radius = 25
x = np.array(range(0, 101))
y = np.array(range(0, 101))
z = np.array(range(0, 101))
# try that instead, it is less heavy
# x = np.array(range(0, 101, 5))
# y = np.array(range(0, 101, 5))
# z = np.array(range(0, 101, 5))
xgrid, ygrid, zgrid = np.meshgrid(x, y, z)
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
check = (xgrid - cX)**2 + (ygrid - cY)**2 + (zgrid - cZ)**2 <= radius**2
ax.scatter(xgrid[check], ygrid[check], zgrid[check], color='green')
ax.set_title('сетка из точек 100х100',
fontfamily = 'monospace',
fontstyle = 'normal',
fontweight = 'bold',
fontsize = 10)
ax.set_xlabel("Value", fontsize=14)
ax.set_ylabel("Square of Value", fontsize=14)
ax.set_xlim3d(0, 101)
ax.set_ylim3d(0, 101)
ax.set_zlim3d(0, 101)
plt.show()

Related

How to animate a 3D plot, defined with three functions x=(), y=(), z=()?

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import mpl_toolkits.mplot3d as Axes3D
r = 20
h = 1.7
phi = np.linspace(0, 4*np.pi, 1000)
theta = np.linspace(-np.pi/4, np.pi/4, 1000)
#theta = np.arcsin(0.524)
x = r * np.cos(phi)
y = r * np.sin(phi) * np.cos(theta) - h * np.sin(theta)
z = r * np.sin(phi) * np.sin(theta) + h * np.cos(theta)
fig = plt.figure('Parametric pancake')
ax = fig.add_subplot(111, projection='3d')
ax.plot(x, y, z, '-r', linewidth = 3)
ax.set_xlabel('X', fontweight = 'bold', fontsize = 14)
ax.set_ylabel('Y', fontweight = 'bold', fontsize = 14)
ax.set_zlabel('Z', fontweight = 'bold', fontsize = 14)
plt.title('Parametric pancake', fontweight = 'bold', fontsize = 16)
plt.show()
This code draws the plot, defined by the x, y and z equations. That is great, but I need to draw it, i.e. to have a point that draws the plot as it moves and not have a plot, thats already drawn. How do I make that happen? Furthermore, I need the plot to move periodically - the angle phi to continue to rotate and angle theta to move harmonically from -90 degrees to +90 degrees kind of like a pendulum. And the last thing is how do I make the point move in a loop so it does not stop when the plot is drawn, but continues to move along that plot?
You need to create a animate callback to update the data.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import mpl_toolkits.mplot3d as Axes3D
import matplotlib.animation as animation
r = 20
h = 1.7
N=1000
phi = np.linspace(0, 4*np.pi, N)
theta = np.linspace(-np.pi/4, np.pi/4, N)
#theta = np.arcsin(0.524)
x = r * np.cos(phi)
y = r * np.sin(phi) * np.cos(theta) - h * np.sin(theta)
z = r * np.sin(phi) * np.sin(theta) + h * np.cos(theta)
fig = plt.figure('Parametric pancake')
ax = fig.add_subplot(111, projection='3d')
ax.set_xlim(x.min(),x.max())
ax.set_ylim(y.min(),y.max())
ax.set_zlim(z.min(),z.max())
pltdata, = ax.plot(x[:1], y[:1], z[:1], '-r', linewidth = 3)
lastPoint, = ax.plot(x[0], y[0], z[0], 'b', marker='o')
txt = ax.text(x[0], y[0], z[0]+0.5, 'i=0')
ax.set_xlabel('X', fontweight = 'bold', fontsize = 14)
ax.set_ylabel('Y', fontweight = 'bold', fontsize = 14)
ax.set_zlabel('Z', fontweight = 'bold', fontsize = 14)
plt.title('Parametric pancake', fontweight = 'bold', fontsize = 16)
def animate(i):
pltdata.set_data(x[:i+1], y[:i+1])
pltdata.set_3d_properties(z[:i+1])
lastPoint.set_data(x[i:i+1], y[i:i+1])
lastPoint.set_3d_properties(z[i:i+1])
txt.set_text(f"{i=}")
txt.set_x(x[i])
txt.set_y(y[i])
txt.set_z(z[i]+0.5)
return [pltdata, lastPoint, txt]
theAnim = animation.FuncAnimation(fig, animate, frames=N, interval=100, blit=True, repeat=False)
plt.show()
#theAnim.save('out.gif')

Plotting a line between 2-D point to its corresponding value in 3-D

I am trying to plot a dashed line in a 3-D Matplotlib plot. I would like to get a dashed line between each (x_pt, y_pt) to its corresponding z_pt.
from mpl_toolkits import mplot3d
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
import matplotlib
matplotlib.rcParams['mathtext.fontset'] = 'cm'
matplotlib.rcParams['axes.labelsize'] = 13
def z_function(x, y):
a = 1
b = 5.1/(4*np.pi**2)
c = 5/np.pi
r = 6
s = 10
t = 1/(8*np.pi)
return a*(y - b*x**2 + c*x - r)**2 + s*(1 - t)*np.cos(x) + s
x = np.linspace(-5, 10, 100)
y = np.linspace(0, 15, 100)
indexes = np.random.randint(0, 100, 5)
x_pt = x[indexes]
y_pt = y[indexes]
z_pt = z_function(x_pt, y_pt)
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.scatter(x_pt, y_pt, color='k', marker='x', depthshade=False)
ax.scatter(x_pt, y_pt, z_pt, color='k', marker='^', depthshade=False)
ax.set_xticks([-5, 0, 5, 10])
ax.set_yticks([0, 5, 10, 15])
ax.set_zticks([100, 200, 300])
ax.view_init(30, -120)
ax.set_xlabel(r'$x_1$')
ax.set_ylabel(r'$x_2$')
ax.zaxis.set_rotate_label(False)
ax.set_zlabel(r'$f(x)$', rotation=0)
ax.w_xaxis.pane.fill = False
ax.w_yaxis.pane.fill = False
ax.w_zaxis.pane.fill = False
plt.show()
Can anyone help me with this?
If I understand your problem correctly, you need to connect the point (x,y,0) to (x,y,z) like so:
for x_,y_,z_ in zip(x_pt, y_pt, z_pt):
ax.plot([x_,x_],[y_,y_],[0,z_], '--', c='grey')
It should be as simple as:
ax.plot(x_pt, y_pt, zs=z_pt, color='blue', marker='--', depthshade=False)
alternatively using:
ax.plot3D(x_pt, y_pt, z_pt, marker='--')
UPDATE:
You will need to create extra dummy coordinates for each point on the x-y axis, like so:
import numpy as np
n = 10 # number of points in the line
for i in len(x_pt):
x_range = np.linspace(0, x_pt[i], n)
y_range = np.linspace(0, y_pt[i], n)
ax.plot3D(x_range, y_range, [z_pt[i]]*n, marker='--')
NOTE: Untested

Trying to calculate then show the gradient vector of a function

I was able to make a program that shows both a 3d graph of a 2 variable function then a vector field of the gradient of the function, but then I wanted to have it calculate the gradient itself, but I keep getting isinfinite errors from plt.quiver(). I feel like part of the reason is because I'm going back and forth from numpy and sympy notation for x and y, but i have no idea what to do in that case.
def z_func(x,y):
return (x**2+y**2)
def show_graph():
x,y = np.meshgrid(np.linspace(-15,15,20),np.linspace(-15,15,20))
z = z_func(x,y)
fig = plt.figure(2)
ax = fig.gca( projection='3d')
surf = ax.plot_surface(x,y,z,rstride=1,cstride=1)
ax.set_xlabel('X', fontweight = 'bold', fontsize = 14)
ax.set_ylabel('Y', fontweight = 'bold', fontsize = 14)
ax.set_zlabel('Z', fontweight = 'bold', fontsize = 14)
plt.title('Ahem', fontweight = 'bold', fontsize = 16)
def get_grad():
x = sy.Symbol('x')
y= sy.Symbol('y')
f = z_func(x,y)
gradi = sy.diff(f,x)
gradj = sy.diff(f,y)
show_vector(gradi,gradj)
def show_vector(gradi,gradj):
a = sy.Symbol('x')
b = sy.Symbol('y')
u = gradi
v = gradj
print('[{0},{1}]'.format(u,v))
a,b = np.meshgrid(np.linspace(-10,10,10),np.linspace(-10,10,10))
print('[{0},{1}]'.format(u,v))
figv = plt.figure(1)
plt.xlabel('X')
plt.ylabel('Y')
plt.quiver(a,b,u,v)
def lazy():
get_grad()
show_graph()
plt.show()
lazy()
When you want to use sympy expressions outside sympy, you need lambdify.
Is the following code doing what you expected?
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import sympy as sy
def z_func(x, y):
return (x ** 2 + y ** 2)
x = sy.Symbol('x')
y = sy.Symbol('y')
f = z_func(x, y)
gradi = sy.diff(f, x)
gradj = sy.diff(f, y)
np_gradi = sy.lambdify(x, gradi, 'numpy')
np_gradj = sy.lambdify(y, gradj, 'numpy')
a, b = np.meshgrid(np.linspace(-10, 10, 10), np.linspace(-10, 10, 10))
u = np_gradi(a)
v = np_gradj(b)
x, y = np.meshgrid(np.linspace(-15, 15, 20), np.linspace(-15, 15, 20))
z = z_func(x, y)
fig = plt.figure(2)
ax = fig.gca(projection='3d')
surf = ax.plot_surface(x, y, z, rstride=1, cstride=1)
ax.set_xlabel('X', fontweight='bold', fontsize=14)
ax.set_ylabel('Y', fontweight='bold', fontsize=14)
ax.set_zlabel('Z', fontweight='bold', fontsize=14)
figv = plt.figure(1)
plt.xlabel('X')
plt.ylabel('Y')
plt.quiver(a, b, u, v)
plt.show()

aligning axes of different plots in matplotlib

I am trying to align these plots so the x axis of the top plot perfectly aligns with the x axis values of the imshow. I'm able to do this by setting the aspect to auto, but then my image is warped. is there a way to do this?
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-10, 10, 1200)
y = np.linspace(-20, 20, 1600)
xv, yv = np.meshgrid(x, y)
w = 3
xpos = 0
ypos = 5
z = np.exp(-((xv - xpos)**2 + (yv - ypos)**2) / w**2)
xh = np.linspace(0, 2)
yh = np.sin(xh)
sumvertical = np.sum(z, 0)
xvert = range(np.shape(z)[1])
sumhoriz = np.sum(z, 1)
yhoriz = range(np.shape(z)[0])
# definitions for the axes
left, width = 0.1, 0.65
bottom, height = 0.1, 0.65
bottom_h = left_h = left + width + 0.02
rect_scatter = [left, bottom, width, height]
rect_x = [left, bottom_h, width, 0.2]
rect_y = [left_h, bottom, 0.2, height]
plt.figure(1, figsize=(8, 8))
axCenter = plt.axes(rect_scatter)
axhoriz = plt.axes(rect_x)
axvert = plt.axes(rect_y)
axCenter.imshow(z, origin='lower', cmap='jet') #aspect='auto')
axhoriz.plot(xvert, sumvertical)
axvert.plot(sumhoriz, yhoriz)
plt.show()
I would recommend using the tools from mpl_toolkits.axes_grid1, namely make_axes_locatable to divide the center axes to leave room for the marginal axes.
You should then also set the margins to 0 along the shared direction to have the ranges match up.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
x = np.linspace(-10, 10, 1200)
y = np.linspace(-20, 20, 1600)
xv, yv = np.meshgrid(x, y)
w = 3
xpos = 0
ypos = 5
z = np.exp(-((xv - xpos)**2 + (yv - ypos)**2) / w**2)
xh = np.linspace(0, 2)
yh = np.sin(xh)
sumvertical = np.sum(z, 0)
xvert = range(np.shape(z)[1])
sumhoriz = np.sum(z, 1)
yhoriz = range(np.shape(z)[0])
fig, axCenter = plt.subplots(figsize=(8, 8))
fig.subplots_adjust(.05,.1,.95,.95)
divider = make_axes_locatable(axCenter)
axvert = divider.append_axes('right', size='30%', pad=0.5)
axhoriz = divider.append_axes('top', size='20%', pad=0.25)
axCenter.imshow(z, origin='lower', cmap='jet')
axhoriz.plot(xvert, sumvertical)
axvert.plot(sumhoriz, yhoriz)
axhoriz.margins(x=0)
axvert.margins(y=0)
plt.show()

Matplotlib circles on the Basemap don't transit to the other half of sphere

I got another problem with my Matplotlib Basemap code, circle on the edge of the map (coord: 180, 0) behave like this is the end, but this is just the end of projection. Anyone have the idea how to fix this? Here the image of issue
import numpy as np
import matplotlib.pyplot as plt
def plot_mwd(RA,Dec,org=0,title='Mollweide projection', projection='aitoff'):
x = np.remainder(RA+360-org,360) # shift RA values
ind = x>180
x[ind] -=360 # scale conversion to [-180, 180]
x=-x # reverse the scale: East to the left
tick_labels = np.array([150, 120, 90, 60, 30, 0, 330, 300, 270, 240, 210])
tick_labels = np.remainder(tick_labels+360+org,360)
fig = plt.figure(figsize=(10, 5))
ax = fig.add_subplot(111, projection=projection, axisbg ='LightCyan')
ax.scatter(np.radians(x),np.radians(Dec), s = 15, c = 'k', marker = '.') # convert degrees to radians
ax.set_xticklabels(tick_labels) # we add the scale on the x axis
ax.set_title(title)
ax.title.set_fontsize(15)
ax.xaxis.label.set_fontsize(12)
ax.yaxis.label.set_fontsize(12)
ax.grid(color='tab:gray', linestyle='-', linewidth=0.2)
phi = np.linspace(0, 2.*np.pi, 72) #72 points
r = np.radians(30)
x = np.radians(180) + r*np.cos(phi)
y = np.radians(0) + r*np.sin(phi)
ax.plot(x, y, color="r", linewidth = '0.7')
fig = plt.gcf()
ax = fig.gca()
coord = np.array([(180, 0)])
plot_mwd(coord[:,0],coord[:,1], org=0, title ='Galactic Coordinate System', projection = 'aitoff')
plt.show()
The problem is that wrapping does not work as you expect, and you need to put it by hand. In other words, you need to make sure your x and y are explicitly between -pi and pi, and -pi/2 and pi/2 respectively. Also you can not plot lines because they will be connected directly and not in the "wrapped around the projection" way.
To be clear, I've modified your function in a simple way. You can probably do it somewhat better but here you'll understand more clearly.
def plot_mwd(RA,Dec,org=0,title='Mollweide projection', projection='aitoff'):
x = np.remainder(RA+360-org,360) # shift RA values
ind = x>180
x[ind] -=360 # scale conversion to [-180, 180]
x=-x # reverse the scale: East to the left
tick_labels = np.array([150, 120, 90, 60, 30, 0, 330, 300, 270, 240, 210])
tick_labels = np.remainder(tick_labels+360+org,360)
fig = plt.figure(figsize=(10, 5))
ax = fig.add_subplot(111, projection=projection, axisbg ='LightCyan')
ax.scatter(np.radians(x),np.radians(Dec), s = 15, c = 'k', marker = '.') # convert degrees to radians
ax.set_xticklabels(tick_labels) # we add the scale on the x axis
ax.set_title(title)
ax.title.set_fontsize(15)
ax.xaxis.label.set_fontsize(12)
ax.yaxis.label.set_fontsize(12)
ax.grid(color='tab:gray', linestyle='-', linewidth=0.2)
phi = np.linspace(0, 2.*np.pi, 72) #72 points
r = np.radians(30)
x = np.radians(180) + r*np.cos(phi)
y = np.radians(70) + r*np.sin(phi)
# Correct wrapping
x[x>np.radians(180)] = x[x>np.radians(180)] - np.radians(360)
x[x<np.radians(180)] = x[x<np.radians(180)] + np.radians(360)
y[y>np.radians(90)] = y[y>np.radians(90)] - np.radians(180)
y[y<np.radians(90)] = y[y<np.radians(90)] + np.radians(180)
# Plot only points. No lines
ax.plot(x, y, color="r", linewidth = '0.0', marker='.')
fig = plt.gcf()
ax = fig.gca()

Categories

Resources