plot rotation of a parametrised algebraic surface - python

I'd like to plot a rotated parabolic cylinder. Like holding one point of the plane of symmetry with two fingers and let this plane rotate about this point.
There is the rotation matrix (for the y-axis).
And the parametrised surface is:
(u,v,u**2)
I've already plotted this surface for rotations about the x-axis:
Below is the modified(!) code to plot the rotations about the y-axis.
from math import cos,sin,pi
import matplotlib.pyplot as plt
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.auto_scale_xyz([0, 500], [0, 500], [0, 0.15])
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
Us = np.arange(-1, 1, 0.005)
Vs = np.arange(-1, 1, 0.005)
for ch in [0.2*i*pi for i in range(3)]:
Xs = Us*cos(ch) + sin(ch)*(Us**2)
Ys = Vs
Xs, Ys = np.meshgrid(Xs, Ys)
Zs = -Us*sin(ch) + cos(ch)*(Us**2)
Axes3D.plot3D(ax,Xs,Ys,Zs, alpha=0.05)
And it doesn't work. It produces nonsense. This is the output i get:
The operations are identical, so i really can't see why it doesn't work. Can anyone tell me what i'm doing wrong?
I need to make this work in order to just look at the way algebraic surfaces transform when subjected to certain rotations. The code above is a crudely simplified snippet of what i am currently debugging. So the answer i am hoping for regards this particular code.

Here is another version which uses your explicit rotation equations:
import matplotlib as mpl
mpl.use('TkAgg')
from math import cos,sin,pi
import numpy as np
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.auto_scale_xyz([0, 500], [0, 500], [0, 0.15])
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
U = np.arange(-1, 1, 0.005)
V = np.arange(-1, 1, 0.005)
def animate():
for ch in [0.2*i*pi for i in range(3)]:
Us, Vs = np.meshgrid(U, V)
Xs = Us*cos(ch) + sin(ch)*(Us**2)
Ys = Vs
Zs = -Us*sin(ch) + cos(ch)*(Us**2)
tmp = ax.plot_surface(Xs,Ys,Zs, alpha=0.05)
fig.canvas.draw()
tmp.remove()
win = fig.canvas.manager.window
fig.canvas.manager.window.after(100, animate)
plt.show()

The rotate_axes3d_demo does it with ax.view_init this way:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d
plt.ion()
sin = np.sin
cos = np.cos
pi = np.pi
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.auto_scale_xyz([0, 500], [0, 500], [0, 0.15])
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
Us, Vs = np.meshgrid(np.linspace(-1, 1, 200), np.linspace(-1, 1, 200))
ax.plot_surface(Us,Vs,Us**2)
for angle in range(0, 360):
ax.view_init(angle, 30)
plt.draw()

Related

Python matplotlib adjust colormap

This is what I want to create.
This is what I get.
This is the code I have written.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
x = np.linspace(-90, 90, 181)
y = np.linspace(-90, 90, 181)
x_grid, y_grid = np.meshgrid(x, y)
z = np.e**x_grid
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, projection="3d")
ax.plot_surface(x_grid, y_grid, z, cmap=cm.rainbow)
I also tried to normalize z and the colormap.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
import matplotlib as mpl
x = np.linspace(-90, 90, 181)
y = np.linspace(-90, 90, 181)
x_grid, y_grid = np.meshgrid(x, y)
z = np.e**x_grid
cmap = mpl.cm.rainbow
norm = mpl.colors.Normalize(vmin=0, vmax=1)
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, projection="3d")
ax.plot_surface(x_grid, y_grid, z/np.max(z), norm=norm, cmap=cm.rainbow)
Question: How can I adjust the colormap to make it less discrete and more continuous for these simultaneously tiny and large values in z?
Welcome to Stackoverflow!!
Your problem is related to the fact that you are working with exponential numbers, but you're using a linear colormap. For x=90 you have z=1.2e+39, reaaaally large.
You were very close with your second attempt! I just changed 1 line in there, instead of
norm = mpl.colors.Normalize(vmin=0, vmax=1)
I used
norm = mpl.colors.LogNorm()
And the result I got was the following:
Now, you can tweak this as much as you like in order to get the colors you want :) Just don't forget that your colormap should be normalized in a logarithmic fashion, so that it counters the exponential behaviour of your function in this case.
Hope this helps!

How to draw a flat 3D rectangle in matplotlib?

How do I draw a rectangle in 3D ? The code below works but it draws a triangle
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
import matplotlib.pyplot as plt
fig = plt.figure()
ax = Axes3D(fig)
x = [0,1,1,0]
y = [0,0,1,1]
z = [0,1,0,1]
verts = [list(zip(x,y,z))]
ax.add_collection3d(Poly3DCollection(verts))
plt.show()
I tried adding a 4th corner but I get an error
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
import matplotlib.pyplot as plt
fig = plt.figure()
ax = Axes3D(fig)
x = [0,1,1,0]
y = [0,0,1,1]
z = [0,1,0,1]
a = [0,1,0,1]
verts = [list(zip(x,y,z,a))]
ax.add_collection3d(Poly3DCollection(verts))
plt.show()
Is there a function that accepts 4 3D-coordinates then draws a flat rectangle? e.g.
ax.draw_rectangle3d(tl=[0,0,0],tr=[0,0,1],bl=[0,1,0],br=[0,1,1])
This function Adding a Rectangle Patch and Text Patch to 3D Collection in Matplotlib draws a flat rectangle but it does not allow me to set my own x,y,z coordinate and the rectangle is projected onto the axes.
The Z axis is wrong should be [0,0,0,0], since each column is a point and the rectangle is on the 0 on Z axis.
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
import matplotlib.pyplot as plt
fig = plt.figure()
ax = Axes3D(fig)
x = [0,1,1,0]
y = [0,0,1,1]
z = [0,0,0,0]
verts = [list(zip(x,y,z))]
ax.add_collection3d(Poly3DCollection(verts))
plt.show()
You can use this to plot multiple surfaces.
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
import matplotlib.pyplot as plt
fig = plt.figure()
ax = Axes3D(fig)
x = [0,1,1,0],[0,1,1,0]
y = [0,0,1,1],[0,0,1,1]
z = [0,0,0,0],[1,1,1,1]
surfaces = []
for i in range(len(x)):
surfaces.append( [list(zip(x[i],y[i],z[i]))] )
for surface in surfaces:
ax.add_collection3d(Poly3DCollection(surface))
plt.show()
import numpy as np
from mpl_toolkits.mplot3d.art3d import Poly3DCollection, Line3DCollection
import matplotlib.pyplot as plt
points = np.array([[-1, -1, -1],
[1, -1, -1 ],
[1, 1, -1],
[-1, 1, -1],
[-1, -1, 1],
[1, -1, 1 ],
[1, 1, 1],
[-1, 1, 1]])
Z = points
Z = 10.0*Z
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
r = [-1,1]
X, Y = np.meshgrid(r, r)
ax.scatter3D(Z[:, 0], Z[:, 1], Z[:, 2])
verts = [[Z[0],Z[1],Z[2],Z[3]],
[Z[4],Z[5],Z[6],Z[7]],
[Z[0],Z[1],Z[5],Z[4]],
[Z[2],Z[3],Z[7],Z[6]],
[Z[1],Z[2],Z[6],Z[5]],
[Z[4],Z[7],Z[3],Z[0]]]
ax.add_collection3d(Poly3DCollection(verts, facecolors='cyan', linewidths=1, edgecolors='r', alpha=.20))
plt.show()

python - how do I fix interpolate grid origin issues in matplotlib?

I have a data set with a small sample size of data. For example:
My code looks something like this:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.interpolate import Rbf
df=pd.read_csv('test.csv')
df.head()
extent = x_extent = x_min, x_max, y_min, y_max = [df["X"].min()-1000, df["X"].max()+1000, df["Y"].min()-1000, df["Y"].min()+1000]
grid_x, grid_y = np.mgrid[x_min:x_max:100, y_min:y_max:100]
rbfi=Rbf(df["X"], df["Y"], df["Total"])
di=rbfi(grid_x, grid_y)
plt.scatter(grid_x, grid_y, s=10)
plt.figure(figsize=(15,15))
plt.imshow(di.T, origin="lower", extent=extent)
c2 = plt.scatter(df["X"], df["Y"], s=60, c=df["Total"], edgecolor='#ffffff66')
plt.colorbar(c2, shrink=0.6)
plt.show()
the result:
The result is a scatter plot of my points that appear to be in the correct place, but the interpolated grid is not covering the scatter points. So I think this has something to do with my origin not being correct, but I don't know how to fix this.
Two approaches here, one with a Delaunay triangulation, the other using the Radial Basis Function. Snippet and figure below.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.tri import Triangulation
from scipy.interpolate import Rbf
rng = np.random.default_rng()
X = rng.random(size=(15))
Y = rng.random(size=(15))
Total = rng.random(size=(15))
fig, (ax, bx) = plt.subplots(nrows=1, ncols=2, num=0, figsize=(16, 8))
tri = Triangulation(X, Y)
tctrf = ax.tricontourf(tri, Total)
gridY, gridX = np.mgrid[np.amin(Y):np.amax(Y):100 * 1j,
np.amin(X):np.amax(X):100 * 1j]
rbfi = Rbf(X, Y, Total, function='linear')
iTotal = rbfi(gridX, gridY)
bx.contourf(gridX, gridY, iTotal)
scat = ax.scatter(X, Y, s=60, c=Total, edgecolor='black')
fig.colorbar(scat, ax=ax)
scat = bx.scatter(X, Y, s=60, c=Total, edgecolor='black')
fig.colorbar(scat, ax=bx)
ax.set_aspect('equal')
bx.set_aspect('equal')
fig.tight_layout()
fig.savefig('so.png')
plt.show()

How to get the slice of a plot3d object?

I have some points and I plot the surface of them using the code below:
import matplotlib.pyplot as plt
from matplotlib import cm, colors
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
# Create a sphere
r = 1
pi = np.pi
cos = np.cos
sin = np.sin
phi, theta = np.mgrid[0.0:pi:20j, 0.0:2.0*pi:20j]
radis=np.random.normal(1,0.2,(20,20))
x = radis*sin(phi)*cos(theta)
y = radis*sin(phi)*sin(theta)
z = radis*cos(phi)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(
x, y, z, rstride=1, cstride=1, color='c', alpha=0.3, linewidth=0)
ax.scatter3D(x,y,z, c='r')
ax.set_xlim([-1,1])
ax.set_ylim([-1,1])
ax.set_zlim([-1,1])
# ax.set_aspect("equal")
plt.tight_layout()
plt.show()
Then I get the 3d plot result:
The thing I want to do is that get the image of any plane, like z=0.
Is there any method or library can cover this problem?

Way of plotting data into boxes instead of pyramids using mplot3d

So I have an array of values that I am trying to plot using the plot_surface command. Specifically I have a 30x30 array with one in the middle and zeros elsewhere. When I plot it this is what I obtain:
I would like however for the value to be represented as a cuboid. Is that possible?
Thank you
edit: Code that shows the figure
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
N=30
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x = y = np.arange(0, N)
z = np.zeros((N,N))
z[15,15] = 1
X, Y = np.meshgrid(x, y)
ax.plot_surface(X, Y, z, cmap='YlOrBr')
plt.show(block=False)
I think a 3D bar plot will give what you're looking for.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
N=30
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x = y = np.arange(0, N)
z_bottom = np.zeros((N,N))
z_top = z_bottom.copy()
z_top[15,15] = 1
X, Y = np.meshgrid(x, y)
ax.bar3d(X.ravel(), Y.ravel(), z_bottom.ravel(), 1, 1, z_top.ravel())
plt.show(block=False)
The full documentation is here if you want to play with the colors and so forth.

Categories

Resources