How to draw sphere with cylinder by formula with matplotlib - python

I have two formulas:
x^2 + y^2+z^2 = 6
(x-3)^2+y^2=4
So I have to draw this shapes by matplotlib on python.
My solution:
`
import math
import matplotlib.pyplot as plt
from pylab import *
import numpy as np
import matplotlib.patches as patches
from mpl_toolkits.mplot3d import Axes3D
from itertools import product, combinations
import mpl_toolkits.mplot3d.art3d as art3d
def task3():
fig = plt.figure()
ax = plt.axes(projection='3d')
coefs = (1, 1, 1) # Coefficients in a0/c x**2 + a1/c y**2 + a2/c z**2 = 1
# Radii corresponding to the coefficients:
rx, ry, rz = 1 / np.sqrt(coefs)
# Set of all spherical angles:
u = np.linspace(0, 2 * np.pi, 100)
v = np.linspace(0, np.pi, 100)
# Cartesian coordinates that correspond to the spherical angles:
# (this is the equation of an ellipsoid):
x = rx * np.outer(np.cos(u), np.sin(v))
y = ry * np.outer(np.sin(u), np.sin(v))
z = rz * np.outer(np.ones_like(u), np.cos(v))
ax.plot_surface(x, y, z, rstride=6, cstride=6,
cmap='viridis', edgecolor='none')
# ax.scatter(X, Y, Z, c=Z, cmap='viridis', label="xy")
radius = 0.3
height = 2.4
elevation = -1
resolution = 720
color = 'r'
x_center = 0.7
y_center = -0
a = np.linspace(x_center - radius, x_center + radius, resolution)
b = np.linspace(elevation, elevation + height, resolution)
X, Z = np.meshgrid(a, b)
Y = np.sqrt(radius ** 2 - (X - x_center) ** 2) + y_center # Pythagorean theorem
ax.plot_surface(X, Y, Z, rstride=6, cstride=6,
cmap='viridis', edgecolor='none')
ax.plot_surface(X, (2 * y_center - Y), Z, rstride=6, cstride=6,
cmap='viridis', edgecolor='none')
plt.show()
[![Result. I cant post images, but you can compile result and then you will see that my solution too approximate)
I want to do it correctly

Related

Strange edge behaviour of surface plot in matplotlib

I would like to make surface plot of a function which is discontinuous at certain values in parameter space. It is near these discontinuities that the plot's coloring becomes incorrect, as shown in the picture below. How can I fix this?
My code is given below:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np
def phase(mu_a, mu_b, t, gamma):
theta = 0.5*np.arctan2(2*gamma, mu_b-mu_a)
epsilon = 2*gamma**2/np.sqrt((mu_a-mu_b)**2+4*gamma**2)
y1 = np.arccos(0.5/t*(-mu_a*np.sin(theta)**2 -mu_b*np.cos(theta)**2 - epsilon))
y2 = np.arccos(0.5/t*(-mu_a*np.cos(theta)**2 -mu_b*np.sin(theta)**2 + epsilon))
return y1+y2
fig = plt.figure()
ax = fig.gca(projection='3d')
# Make data.
X = np.arange(-2.5, 2.5, 0.01)
Y = np.arange(-2.5, 2.5, 0.01)
X, Y = np.meshgrid(X, Y)
Z = phase(X, Y, 1, 0.6)
# Plot the surface.
surf = ax.plot_surface(X, Y, Z, cmap=cm.coolwarm, linewidth=0, antialiased=False)
surf.set_clim(1, 5)
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.show()
An idea is to make all the arrays 1D, filter out the NaN values and then call ax.plot_trisurf:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np
def phase(mu_a, mu_b, t, gamma):
theta = 0.5 * np.arctan2(2 * gamma, mu_b - mu_a)
epsilon = 2 * gamma ** 2 / np.sqrt((mu_a - mu_b) ** 2 + 4 * gamma ** 2)
with np.errstate(divide='ignore', invalid='ignore'):
y1 = np.arccos(0.5 / t * (-mu_a * np.sin(theta) ** 2 - mu_b * np.cos(theta) ** 2 - epsilon))
y2 = np.arccos(0.5 / t * (-mu_a * np.cos(theta) ** 2 - mu_b * np.sin(theta) ** 2 + epsilon))
return y1 + y2
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
# Make data.
X = np.linspace(-2.5, 2.5, 200)
Y = np.linspace(-2.5, 2.5, 200)
X, Y = np.meshgrid(X, Y)
X = X.ravel() # make the array 1D
Y = Y.ravel()
Z = phase(X, Y, 1, 0.6)
mask = ~np.isnan(Z) # select the indices of the valid values
# Plot the surface.
surf = ax.plot_trisurf(X[mask], Y[mask], Z[mask], cmap=cm.coolwarm, linewidth=0, antialiased=False)
surf.set_clim(1, 5)
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.show()
Some remarks:
plot_trisurf will join the XY-values via triangles; this only works well if the domain is convex
to make things draw quicker, less points could be used (the original used 500x500 points, the code here reduces that to 200x200
calling fig.gca(projection='3d') has been deprecated; instead, you could call fig.add_subplot(projection='3d')
the warnings for dividing by zero or using arccos out of range can be temporarily suppressed; that way the warning will still be visible for situations when such isn't expected behavior

Clip a contourf() plot in 3D plot

I'm making multiple density plots in 3D with a circle shape, using contourf.
What I want to do is similar to a clip_path for a pcolormesh plot.
How can I clip the plot in 3D in particular for a contourf plot?
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.path import Path
import matplotlib.patches as patches
fig = plt.figure()
ax = fig.gca(projection='3d')
x = np.linspace(0, 1, 100)
X, Y = np.meshgrid(x, x)
levels = np.linspace(-0.1, 0.4, 100) #(z_min,z_max,number of contour),
a=0
b=1
c=2
Z1 = a+.1*np.sin(2*X)*np.sin(4*Y)
Z2 = b+.1*np.sin(3*X)*np.sin(4*Y)
Z3 = c+.1*np.sin(4*X)*np.sin(5*Y)
plt.contourf(X, Y,Z1, levels=a+levels,cmap=plt.get_cmap('rainbow'))
plt.contourf(X, Y,Z2, levels=b+levels,cmap=plt.get_cmap('rainbow'))
plt.contourf(X, Y,Z3, levels=c+levels,cmap=plt.get_cmap('rainbow'))
ax.set_xlim3d(0, 1)
ax.set_ylim3d(0, 1)
ax.set_zlim3d(0, 2)
plt.show()
Are you sure your code generated that output image with the given parameters? I only got some blue squares. Tweaking a bit, it starts looking like yours.
To clip the contours, consider masking the Z arrays. As in:
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt
import numpy.ma as ma
fig = plt.figure()
ax = fig.gca(projection='3d')
x = np.linspace(0, 1, 100)
X, Y = np.meshgrid(x, x)
levels = np.linspace(-0.1, 0.4, 100) #(z_min,z_max,number of contour),
a = 0
b = 1
c = 2
Z1 = a + .3 * np.sin(2 * X) * np.sin(4 * Y)
Z2 = b + .3 * np.sin(3 * X) * np.sin(4 * Y)
Z3 = c + .3 * np.sin(4 * X) * np.sin(5 * Y)
mask_cond = (X - .5) ** 2 + (Y - .5) ** 2 > .25
Z1 = ma.masked_where(mask_cond, Z1)
Z2 = ma.masked_where(mask_cond, Z2)
Z3 = ma.masked_where(mask_cond, Z3)
plt.contourf(X, Y, Z1, levels=a + levels, cmap='rainbow')
plt.contourf(X, Y, Z2, levels=b + levels, cmap='rainbow')
plt.contourf(X, Y, Z3, levels=c + levels, cmap='rainbow')
ax.set_xlim3d(0, 1)
ax.set_ylim3d(0, 1)
ax.set_zlim3d(0, 3)
plt.show()

How to plot Saturn and its rings using matplotlib 3D?

I have managed to create a code that outputs a sphere and a ring around it but the ring doesn't look like its rounding the sphere...
Can anyone save me here?
Code:
import numpy as np
from matplotlib import pyplot as plt
import mpl_toolkits.mplot3d.axes3d
theta = np.linspace(0, 2.*np.pi, 100)
phi = np.linspace(0, 2*np.pi, 100)
theta, phi = np.meshgrid(theta, phi)
c, a = 10, 0.2
x = (c + a*np.cos(theta)) * np.cos(phi)
y = (c + a*np.cos(theta)) * np.sin(phi)
z = a * np.sin(theta)
fig = plt.figure()
ax1 = fig.add_subplot(111, projection='3d')
ax1.set_zlim(-10,10)
ax1.plot_surface(x, y, z, rstride=5, cstride=5, color='b', edgecolors='b')
ax1.view_init(36, 26)
u = np.linspace(0, 2 * np.pi, 100)
v = np.linspace(0, np.pi, 100)
x2 = 4 * np.outer(np.cos(u), np.sin(v))
y2 = 4 * np.outer(np.sin(u), np.sin(v))
z2 = 10 * np.outer(np.ones(np.size(u)), np.cos(v))
ax1.plot_surface(x2, y2, z2, rstride=5, cstride=5, color='r')
plt.show()

python matplotlib: drawing 3D sphere with circumferences

I'm trying to draw a sphere like this one using matplotlib:
but I can't find a way of having a dashed lines on the back and the vertical circumference looks a bit strange
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure(figsize=(12,12), dpi=300)
ax = fig.add_subplot(111, projection='3d')
ax.set_aspect('equal')
u = np.linspace(0, 2 * np.pi, 100)
v = np.linspace(0, np.pi, 100)
x = 1 * np.outer(np.cos(u), np.sin(v))
y = 1 * np.outer(np.sin(u), np.sin(v))
z = 1 * np.outer(np.ones(np.size(u)), np.cos(v))
#for i in range(2):
# ax.plot_surface(x+random.randint(-5,5), y+random.randint(-5,5), z+random.randint(-5,5), rstride=4, cstride=4, color='b', linewidth=0, alpha=0.5)
ax.plot_surface(x, y, z, rstride=4, cstride=4, color='b', linewidth=0, alpha=0.5)
ax.plot(np.sin(theta),np.cos(u),0,color='k')
ax.plot([0]*100,np.sin(theta),np.cos(u),color='k')
In the example you show, I don't think that the circles can be perpendicular to one another (i.e. one is the equator and one runs through the north pole and south pole). If the horizontal circle is the equator, then the north pole must be somewhere on a vertical line drawn through the center of the yellow circle that represents the sphere. Otherwise, the right side of the equator would look higher or lower than the left. However, the ellipse that represents the polar circle only crosses through that center line at the top and bottom of the yellow circle. Therefore, the north pole is at the top of the sphere, which means that we must be looking straight on the equator, which means it should look like a line, not an ellipse.
Here's some code to reproduce something similar to the figure you posted:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.set_aspect('equal')
u = np.linspace(0, 2 * np.pi, 100)
v = np.linspace(0, np.pi, 100)
x = 1 * np.outer(np.cos(u), np.sin(v))
y = 1 * np.outer(np.sin(u), np.sin(v))
z = 1 * np.outer(np.ones(np.size(u)), np.cos(v))
#for i in range(2):
# ax.plot_surface(x+random.randint(-5,5), y+random.randint(-5,5), z+random.randint(-5,5), rstride=4, cstride=4, color='b', linewidth=0, alpha=0.5)
elev = 10.0
rot = 80.0 / 180 * np.pi
ax.plot_surface(x, y, z, rstride=4, cstride=4, color='b', linewidth=0, alpha=0.5)
#calculate vectors for "vertical" circle
a = np.array([-np.sin(elev / 180 * np.pi), 0, np.cos(elev / 180 * np.pi)])
b = np.array([0, 1, 0])
b = b * np.cos(rot) + np.cross(a, b) * np.sin(rot) + a * np.dot(a, b) * (1 - np.cos(rot))
ax.plot(np.sin(u),np.cos(u),0,color='k', linestyle = 'dashed')
horiz_front = np.linspace(0, np.pi, 100)
ax.plot(np.sin(horiz_front),np.cos(horiz_front),0,color='k')
vert_front = np.linspace(np.pi / 2, 3 * np.pi / 2, 100)
ax.plot(a[0] * np.sin(u) + b[0] * np.cos(u), b[1] * np.cos(u), a[2] * np.sin(u) + b[2] * np.cos(u),color='k', linestyle = 'dashed')
ax.plot(a[0] * np.sin(vert_front) + b[0] * np.cos(vert_front), b[1] * np.cos(vert_front), a[2] * np.sin(vert_front) + b[2] * np.cos(vert_front),color='k')
ax.view_init(elev = elev, azim = 0)
plt.show()

Matplotlib boolean coloring

I would like to graph a 3d plot of a sphere, which colors sections differently according to some function of the theta, phi coordinates. I could graph two separate graphs, one of each color, but I'm not sure exactly how the gridding/meshing works when plotting. Viz. I want to grid/mesh over all thetas/phis in a sphere, but then throw out certain pairs given a boolean function. Is this possible? Attached is a picture of a scatterplot that does what I'd like to do with surfaces.
Based on scatter3d_demo.py found in the matplotlib tutorial:
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, projection='3d')
THETA, PHI = np.ogrid[0:2*np.pi:40j, 0:np.pi:30j]
X = 10 * np.cos(THETA) * np.sin(PHI)
Y = 10 * np.sin(THETA) * np.sin(PHI)
Z = 10 * np.ones_like(THETA) * np.cos(PHI)
def func(THETA, PHI):
mask = (THETA < np.pi/2) & (np.pi/3 < PHI) & (PHI < 2 * np.pi/3)
return np.where(mask, 1, 0.5)
C = func(THETA, PHI)
x = X.ravel()
y = Y.ravel()
z = Z.ravel()
c = C.ravel()
ax.scatter(x, y, z, c=c, s=30, vmin=0, vmax=1)
ax.set_aspect('equal')
plt.show()
yields
Note you could also color patches on a sphere using plot_surface:
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
THETA, PHI = np.ogrid[0:2*np.pi:40j, 0:np.pi:30j]
X = 10 * np.cos(THETA) * np.sin(PHI)
Y = 10 * np.sin(THETA) * np.sin(PHI)
Z = 10 * np.ones_like(THETA) * np.cos(PHI)
def func(THETA, PHI):
mask = (THETA < np.pi/2) & (np.pi/3 < PHI) & (PHI < 2 * np.pi/3)
return np.where(mask, 1, 0.5)
C = func(THETA, PHI)
jet = plt.cm.jet
ax.plot_surface(X, Y, Z, rstride=2, cstride=2, facecolors=jet(C))
ax.set_aspect('equal')
plt.show()

Categories

Resources