fix graphic isometric figure - python

I Have the following code taken from Here, in this code a figure of height two is generated by two cubes (upper and bottom), I want generate the figure of height two by a only one figure, likewise the figure of height 3,4,5,...
import matplotlib as mpl
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt
def cuboid_data(center, N, size=(1,1,1)):
# code taken from
# https://stackoverflow.com/questions/30715083/python-plotting-a-wireframe-3d-cuboid?noredirect=1&lq=1
# suppose axis direction: x: to left; y: to inside; z: to upper
# get the (left, outside, bottom) point
o = [a - b / 2 for a, b in zip(center, size)]
l, w, h = size
x = [[o[0], o[0] + l, o[0] + l, o[0], o[0]], # x coordinate of points in bottom surface
[o[0], o[0] + l, o[0] + l, o[0], o[0]], # x coordinate of points in upper surface
[o[0], o[0] + l, o[0] + l, o[0], o[0]], # x coordinate of points in outside surface
[o[0], o[0] + l, o[0] + l, o[0], o[0]]] # x coordinate of points in inside surface
y = [[o[1], o[1], o[1] + w, o[1] + w, o[1]], # y coordinate of points in bottom surface
[o[1], o[1], o[1] + w, o[1] + w, o[1]], # y coordinate of points in upper surface
[o[1], o[1], o[1], o[1], o[1]], # y coordinate of points in outside surface
[o[1] + w, o[1] + w, o[1] + w, o[1] + w, o[1] + w]] # y coordinate of points in inside surface
z = [[0,0,0,0,0],
[N,N,N,N,N],
[0, 0, N,N, 0],
[0, 0, N, N, 0]]
return x, y, z
def plotCubeAt(pos=(0,0), N=0, ax=None):
# Plotting N cube elements at position pos
if ax !=None:
if N > 0:
#for n in range(N):
X, Y, Z = cuboid_data( (pos[0],pos[1],N),N )
ax.plot_surface(X, Y, Z, color='y', rstride=1, cstride=1)#,linewidth=0)
def plotIsoMatrix(ax, matrix):
# plot a Matrix
# where matrix[i,j] cubes are added at position (i,j)
for i in range(matrix.shape[0]):
for j in range(matrix.shape[1]):
plotCubeAt(pos=(i,j), N=matrix[i,j], ax=ax)
l = max(matrix.shape[0], matrix.shape[1], matrix.max())
#bb = np.array([(0,0,0), (0,l,0), (l,0,0), (l,l,0),(0,0,l), (0,l,l), (l,0,l), (l,l,l)])
#ax.plot(bb[:,0], bb[:,1], bb[:,2], "w", alpha=0.0)
if __name__ == '__main__':
fig = plt.figure()
ax = fig.gca(projection='3d')
#ax.set_aspect('equal')
matrix = np.array([[2,2],[1,2]])
plotIsoMatrix(ax, matrix)
#ax.set_axis_off()
plt.ion()
plt.show()
This generate the following figure:
I want to generate the following figure:
How fix this ?
Thanks

Solution usiing matplotlib
First of all, if you do not want to have single cubes, but cuboids, there is a much simpler solution - using matplotlib bar3d.
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.set_aspect('equal')
matrix = np.array([[2,2],[1,2]])
xpos, ypos = np.meshgrid(np.arange(matrix.shape[0]),np.arange(matrix.shape[1]) )
xpos = xpos.flatten('F')
ypos = ypos.flatten('F')
zpos = np.zeros_like(xpos)
dx = np.ones_like(zpos)
dy = dx.copy()
dz = matrix.flatten()
ax.bar3d(xpos, ypos, zpos, dx, dy, dz, color='y', zsort='average', linewidth=0)
l = max(matrix.shape[0], matrix.shape[1], matrix.max())
bb = np.array([(0,0,0), (0,l,0), (l,0,0), (l,l,0),(0,0,l), (0,l,l), (l,0,l), (l,l,l)])
ax.plot(bb[:,0], bb[:,1], bb[:,2], "w", alpha=0.0)
ax.set_axis_off()
plt.show()
Concerning the overlapping faces of the cuboids, there is no solution in matplotlib. I think this behaviour is commonly considered to be an unresolvable bug, as is also described in the Matplotlib 3D FAQ. Also, manually setting zorder will not work. The good news is, however, that this overlapping is angle dependent. So you will always find a viewing angle (rotate the plot with the mouse) where it looks good.
Solution using mayavi
Using Mayavi, one does not have the problem of overlapping faces at all. Also the barchart in mayavi is much more convenient, making this only 5 lines of code:
import numpy as np
import mayavi.mlab as mlab
mlab.figure(bgcolor=(1,1,1))
matrix = np.array([[2,2],[1,2]])
mlab.barchart(matrix, color=(1.,0.86, 0.12), lateral_scale=1.0)
mlab.show()

Related

Find the closest point to a line from an array of points [duplicate]

This question already has answers here:
Plot a point on a line closest to a point
(1 answer)
Python: point on a line closest to third point
(3 answers)
Python: Closest Point to a line
(1 answer)
Closed 3 months ago.
This post was edited and submitted for review 3 months ago and failed to reopen the post:
Original close reason(s) were not resolved
I have the problem of finding the point which is closest to a line from an array of x- and y-data.
The line is semi-infinite originating from the origin at (0,0) and running into the direction of a given angle.
The x,y data of the points are given in relation to the origin.
How do I find the closest point (and its distance) to the line in line direction (not opposite)?
This is an example of the data I have:
import numpy as np
import matplotlib.pyplot as plt
def main():
depth = np.random.random((100))*20+50
angle = np.linspace(0, 2*np.pi, 100)
x,y = depth2xy(depth, angle)
line = np.random.random_sample()*2*np.pi
# fig, ax = plt.subplots(subplot_kw={'projection': 'polar'})
plt.scatter(x, y)
plt.plot([0,100*np.cos(line)], [0, 100*np.sin(line)], markersize=10, color = "r")
plt.show()
def depth2xy(depth, angle):
x, y = np.zeros(len(depth)), np.zeros(len(depth))
for i in range(len(depth)):
x[i] = depth[i]*np.cos(angle[i])
y[i] = depth[i]*np.sin(angle[i])
return x,y
if __name__ == "__main__": main()
I could try a brute force approach, iterating over different distances along the line to find the ultimate smallest distance.
But as time efficiency is critical my case and the algorithm would not perform as well as I think it could, I would rather try an analytical approach.
I also thought about scipy.spatial.distance, but I am not sure how this would work for a line.
Your assigned line passes through the origin, its parametric equation is
x = u cos(a)
y = u sin(a)
and you can see the parameter u is simply the (oriented) distance beteween the origin and a point on the assigned line.
Now, consider a point of coordinates X and Y, a line perpendicular to the assigned one has the parametric equation
x = X - v sin(a)
y = Y + v cos(a)
and again, the parameter v is simply the (oriented) distance between (X, Y) and a point on a line passing per (X, Y) and perpendicular to the assigned one.
The intersection is given by the equation
X = u cos(a) + v sin(a)
Y = u sin(a) - v cos(a)
you can check by inspection that the solution of the system is
u = X cos(a) + Y sin(a)
v = X sin(a) - Y cos(a)
The distance of the point (X, Y) from the assigned line is hence
d = | X sin(a) - Y cos(a) |
A Python Implementation
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(20221126)
X = 2*np.random.random(32)-1
Y = 2*np.random.random(32)-1
fig, ax = plt.subplots()
ax.set_xlim((-1.2, 1.2))
ax.set_ylim((-1.2, 1.2))
ax.grid(1)
ax.set_aspect(1)
ax.scatter(X, Y, s=80, ec='k', color='y')
a = 2*np.random.random()*np.pi
s, c = np.sin(a), np.cos(a)
plt.plot((0, c), (0, s), color='k')
plt.plot((-s, s), (c, -c), color='r')
# strike out "bad" points
bad = X*c+Y*s<0
plt.scatter(X[bad], Y[bad], marker='x', color='k')
# consider only good (i.e., not bad) points
Xg, Yg = X[~bad], Y[~bad]
# compute all distances (but for good points only)
d = np.abs(Xg*s-Yg*c)
# find the nearest point and hilight it
imin = np.argmin(d)
plt.scatter(Xg[imin], Yg[imin], ec='k', color='r')
plt.show()
An OVERDONE Example
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(20221126)
X = 2*np.random.random(32)-1
Y = 2*np.random.random(32)-1
fig, axs = plt.subplots(2, 4, figsize=(10,5), layout='constrained')
for ax, a in zip(axs.flat,
(2.8, 1.8, 1.4, 0.2,
3.4, 4.5, 4.9, 6.0)):
ax.set_xlim((-1.2, 1.2))
ax.set_xticks((-1, -0.5, 0, 0.5, 1.0))
ax.set_ylim((-1.2, 1.2))
ax.grid(1)
ax.set_aspect(1)
ax.set_title('$\\alpha \\approx %d^o$'%round(np.rad2deg(a)))
ax.scatter(X, Y, s=80, ec='k', color='yellow')
s, c = np.sin(a), np.cos(a)
ax.arrow(0, 0, 1.2*c, 1.2*s, fc='k',
length_includes_head=True,
head_width=0.08, head_length=0.1)
# divide the drawing surface in two semiplanes
if abs(c)>abs(s):
if c>0:
ax.plot((1.2*s, -1.2*s), (-1.2, 1.2))
else:
ax.plot((-1.2*s, 1.2*s), (-1.2, 1.2))
elif abs(s)>=abs(c):
if s>0:
ax.plot((-1.2, 1.2), (1.2*c, -1.2*c))
else:
ax.plot((-1.2, 1.2), (-1.2*c, 1.2*c))
# strike out "bad" points
bad = X*c+Y*s<0
ax.scatter(X[bad], Y[bad], marker='x', color='k')
# consider only good (i.e., not bad) points
Xg, Yg = X[~bad], Y[~bad]
# compute all distances (but for good points only)
d = np.abs(Xg*s-Yg*c)
# find the nearest point and hilight it
imin = np.argmin(d)
ax.scatter(Xg[imin], Yg[imin], s=80, ec='k', color='yellow')
ax.scatter(Xg[imin], Yg[imin], s= 10, color='k', alpha=1.0)
plt.show()
Let P be a point from your know data set. Let Q be the projection of this point on the line. You can use an analytic approach to determine the exact location of Q:
OQ is the segment from the origin to the Q point. It is aligned to the line.
PQ is the distance of the point P to the line.
from geometry, the dot product between QP and OQ is zero (the two segments are orthogonal to each other). From this equation we can compute the point Q.
After that, you simply compute all distances and find the shortest one.
I'm going to use SymPy for the analytical part, Numpy for the numerical part and Matplotlib for plotting:
from sympy import *
import numpy as np
import matplotlib.pyplot as plt
xq, xp, yq, yp, m = symbols("x_Q, x_P, y_Q, y_P, m")
A = Matrix([xq - xp, yq - yp])
B = Matrix([xq, yq])
# this equations contains two unkowns: xq, yq
eq = A.dot(B)
# but we know the line equation: yq = m * xq, so we substitute it into
# eq and solve for xq
xq_expr = solve(eq.subs(yq, m * xq), xq)[1]
print(xq_expr)
# (m*y_P + x_P)/(m**2 + 1)
# generate data
mv = -0.5
xp_vals = np.random.uniform(2, 10, 30)
yp_vals = np.random.uniform(2, 10, 30)
# convert the symbolic expression to a numerical function
f = lambdify([m, xp, yp], xq_expr)
# compute the projections on the line
xq_vals = f(mv, xp_vals, yp_vals)
yq_vals = mv * xq_vals
# compute the distance
d = np.sqrt((xp_vals - xq_vals)**2 + (yp_vals - yq_vals)**2)
# find the index of the shortest distance
idx = d.argmin()
fig, ax = plt.subplots()
xline = np.linspace(0, 10)
yline = mv * xline
ax.plot(xline, yline, "k:", label="line")
ax.scatter(xq_vals, yq_vals, label="Q", marker=".")
ax.scatter(xp_vals, yp_vals, label="P", marker="*")
ax.plot([xp_vals[idx], xq_vals[idx]], [yp_vals[idx], yq_vals[idx]], "r", label="min distance")
ax.set_aspect("equal")
ax.legend()
plt.show()

Matplotlib - Arrows to and from points along cardinal directions

Problem:
I have an array terrain of shape (N+2,N+2,4). In the last dimension, we have either 1's or -1's, -1 if the arrow in that direction is pointing TO the point in question and 1 if the arrow points FROM the point in that direction. The order is north, south, east, west. So, 1,-1,1,1 would correspond to:
The code I'm trying to work with is the following. But that doesn't work since they are all plotted FROM the point. I couldn't find a way to easily plot from or to the point in the desired direction based on the value of the position in terrain[i,j] that corresponds to the direction in question.
import numpy as np
import matplotlib.pyplot as plt
N=3
terrain = np.zeros((N+2,N+2,4))
for i in range(1, N+1):
for j in range(1, N+1):
terrain[i,j] = np.random.choice([-1,1],4)
for i in range(N+2):
for j in range(N+2):
iterr.append([i,j])
iterr = np.array(iterr)
fig = plt.figure()
ax = fig.add_subplot(111)
plt.quiver(iterr[:,0], iterr[:,1], terrain[:,:,0].reshape(((N+2)**2,1)), np.zeros(((N+2)**2,1)), color='k')
plt.quiver(iterr[:,0], iterr[:,1], terrain[:,:,1].reshape(((N+2)**2,1)), np.zeros(((N+2)**2,1)), color='yellow')
plt.quiver(iterr[:,0], iterr[:,1], np.zeros(((N+2)**2,1)), terrain[:,:,2].reshape(((N+2)**2,1)), color='blue')
plt.quiver(iterr[:,0], iterr[:,1], np.zeros(((N+2)**2,1)), terrain[:,:,3].reshape(((N+2)**2,1)), color='red')
Probably plt.quiver(X, Y, U, V, C, **kw) is not the easiest method to use in your case. The quiver method will always plot an arrow vector (u,v) from (x,y).
It is probably easier to iterate over the data and use plt.arrow(x, y, dx, dy, **kwargs) to draw the arrows.
Using your data structures and plt.arrow I wrote this, where I skipped all the 0 values in terrain:
import numpy as np
import matplotlib.pyplot as plt
N=3
terrain = np.zeros((N+2,N+2,4))
for i in range(1, N+1):
for j in range(1, N+1):
terrain[i,j] = np.random.choice([-1,1],4)
iterr = []
for i in range(N+2):
for j in range(N+2):
iterr.append([i,j])
iterr = np.array(iterr)
#-------------------------------------------------
fig = plt.figure()
ax = fig.add_subplot(111, aspect='equal')
X = iterr[:,0]
Y = iterr[:,1]
cardinal_vectors = [(0,1), (0,-1), (1,0), (-1,0)] # north, south, east, west
cardinal_colors = ['black', 'green', 'blue', 'red']
# plot all points
plt.plot(X,Y,'k.')
for x, y, arrow_directions in zip(X, Y, terrain.reshape(((N+2)**2,4))):
for direction, vector, color in zip(arrow_directions,
cardinal_vectors,
cardinal_colors):
arrow_size = 0.5
head_size = 0.2
arrow_args = {'length_includes_head' : True,
'head_width' : head_size,
'head_length' : head_size,
'width' : 0.02,
'fc' : color,
'ec' : color}
dx = vector[0] * arrow_size
dy = vector[1] * arrow_size
if direction == 1:
# arrow FROM (x,y)
plt.arrow(x, y, dx, dy, **arrow_args)
if direction == -1:
# arrow TO (x,y)
plt.arrow(x + dx, y + dy, -dx, -dy, **arrow_args)
plt.show()

How to add a point inside a matplotlib contour plot?

I would like to draw a phase portrait of a system of ordinary differential equations in a 2D phase space, and used the streamplot function of matplotlib to achieve it. And I would like to mark some points (the fixed points) in the figure, but I don't know how to do it on the same diagram, for using scatter only plots points on another figure. How can I do it? The following is what I have for now:
import numpy as np
import matplotlib.pyplot as plt
"""
The system of ODE is given by:
dx/dt = alpha*(1-x)*x - c*x*y
dy/dt = -b*y + d*x*y
"""
# Set the parameters
alpha, b, c, d = 4, 3, 2, 1
# The fixed points
fp = [(0, 0), (1,0), (b/d, alpha/c*(1-b/d))]
# The phase portrait lies in {(x,y)|-l<x<l, -l<y<l} with n arrows in each dimension
l, n = 2, 41
# The 'velocity' of a point in phase space = (dx/dt, dy/dt)
def velocity(x, y):
return alpha*(1-x)*x - c*x*y, -b*y + d*x*y
# Mesh the grids in x-y space
x, y = np.linspace(-l, l, n), np.linspace(-1, 1, n)
X, Y = np.meshgrid(x, y)
U, V = velocity(X, Y)
plt.figure()
plt.streamplot(X, Y, U, V)
plt.xlabel('x')
plt.ylabel('y')
plt.grid()
plt.show()

python : plotting a wireframe 3D cuboid

I want to plot 3d cuboid in python.
Input :
center (3 points for the center)
radius (3 radius values, one for each dimension)
Ideally it should be a wireframe plot(I need to see whats inside).I am not exactly sure how to go about this. Using python matplotlib or Mayavi is fine.
Thanks!
So far I have tried the following code ..but that only draws a cube
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
from itertools import product, combinations
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.set_aspect("equal")
#draw cube
r = [-1, 1]
for s, e in combinations(np.array(list(product(r,r,r))), 2):
if np.sum(np.abs(s-e)) == r[1]-r[0]:
ax.plot3D(*zip(s,e), color="b")
plt.show()
Whats missing in this code is that its only a cube(not a cuboid) and it's only centered around 0 (I actually want to provide the center)
After thinking a little bit I came up with this.Which seems right. Let me know if you think its not correct...this is the simplest possible way without installing myavi,pygame, povray (I had a hard time installing these on ipython, conda,my windows laptop)
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
from itertools import product, combinations
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.set_aspect("equal")
#draw cube
r1 = [-1, 1]
r2 = [-2, 2]
r3 = [-3, 3]
center =[5,5,5]
for s, e in combinations(np.array(list(product(r1,r2,r3))), 2):
s=np.array(center)+np.array(s)
e=np.array(center)+np.array(e)
ax.scatter3D(*center, color="r")
if np.linalg.norm(s-e) == 2*r1[1] or np.linalg.norm(s-e) == 2*r2[1] or np.linalg.norm(s-e) == 2*r3[1]:
print zip(s,e)
ax.plot3D(*zip(s,e), color="b")
plt.show()
I have encountered the same question, and tried to give a answer as follows.
def cuboid_data(center, size):
"""
Create a data array for cuboid plotting.
============= ================================================
Argument Description
============= ================================================
center center of the cuboid, triple
size size of the cuboid, triple, (x_length,y_width,z_height)
:type size: tuple, numpy.array, list
:param size: size of the cuboid, triple, (x_length,y_width,z_height)
:type center: tuple, numpy.array, list
:param center: center of the cuboid, triple, (x,y,z)
"""
# suppose axis direction: x: to left; y: to inside; z: to upper
# get the (left, outside, bottom) point
o = [a - b / 2 for a, b in zip(center, size)]
# get the length, width, and height
l, w, h = size
x = [[o[0], o[0] + l, o[0] + l, o[0], o[0]], # x coordinate of points in bottom surface
[o[0], o[0] + l, o[0] + l, o[0], o[0]], # x coordinate of points in upper surface
[o[0], o[0] + l, o[0] + l, o[0], o[0]], # x coordinate of points in outside surface
[o[0], o[0] + l, o[0] + l, o[0], o[0]]] # x coordinate of points in inside surface
y = [[o[1], o[1], o[1] + w, o[1] + w, o[1]], # y coordinate of points in bottom surface
[o[1], o[1], o[1] + w, o[1] + w, o[1]], # y coordinate of points in upper surface
[o[1], o[1], o[1], o[1], o[1]], # y coordinate of points in outside surface
[o[1] + w, o[1] + w, o[1] + w, o[1] + w, o[1] + w]] # y coordinate of points in inside surface
z = [[o[2], o[2], o[2], o[2], o[2]], # z coordinate of points in bottom surface
[o[2] + h, o[2] + h, o[2] + h, o[2] + h, o[2] + h], # z coordinate of points in upper surface
[o[2], o[2], o[2] + h, o[2] + h, o[2]], # z coordinate of points in outside surface
[o[2], o[2], o[2] + h, o[2] + h, o[2]]] # z coordinate of points in inside surface
return x, y, z
def test():
import matplotlib as mpl
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
center = [0, 0, 0]
length = 32 * 2
width = 50 * 2
height = 100 * 2
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.gca(projection='3d')
X, Y, Z = cuboid_data(center, (length, width, height))
ax.plot_surface(X, Y, Z, color='b', rstride=1, cstride=1, alpha=0.1)
ax.set_xlabel('X')
ax.set_xlim(-100, 100)
ax.set_ylabel('Y')
ax.set_ylim(-100, 100)
ax.set_zlabel('Z')
ax.set_zlim(-100, 100)
plt.show()
if __name__ == '__main__':
test()
This is the result:
Here is a wireframe plot for a cuboid.
def plot_cuboid(center, size):
"""
Create a data array for cuboid plotting.
============= ================================================
Argument Description
============= ================================================
center center of the cuboid, triple
size size of the cuboid, triple, (x_length,y_width,z_height)
:type size: tuple, numpy.array, list
:param size: size of the cuboid, triple, (x_length,y_width,z_height)
:type center: tuple, numpy.array, list
:param center: center of the cuboid, triple, (x,y,z)
"""
# suppose axis direction: x: to left; y: to inside; z: to upper
# get the (left, outside, bottom) point
import numpy as np
ox, oy, oz = center
l, w, h = size
x = np.linspace(ox-l/2,ox+l/2,num=10)
y = np.linspace(oy-w/2,oy+w/2,num=10)
z = np.linspace(oz-h/2,oz+h/2,num=10)
x1, z1 = np.meshgrid(x, z)
y11 = np.ones_like(x1)*(oy-w/2)
y12 = np.ones_like(x1)*(oy+w/2)
x2, y2 = np.meshgrid(x, y)
z21 = np.ones_like(x2)*(oz-h/2)
z22 = np.ones_like(x2)*(oz+h/2)
y3, z3 = np.meshgrid(y, z)
x31 = np.ones_like(y3)*(ox-l/2)
x32 = np.ones_like(y3)*(ox+l/2)
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.gca(projection='3d')
# outside surface
ax.plot_wireframe(x1, y11, z1, color='b', rstride=1, cstride=1, alpha=0.6)
# inside surface
ax.plot_wireframe(x1, y12, z1, color='b', rstride=1, cstride=1, alpha=0.6)
# bottom surface
ax.plot_wireframe(x2, y2, z21, color='b', rstride=1, cstride=1, alpha=0.6)
# upper surface
ax.plot_wireframe(x2, y2, z22, color='b', rstride=1, cstride=1, alpha=0.6)
# left surface
ax.plot_wireframe(x31, y3, z3, color='b', rstride=1, cstride=1, alpha=0.6)
# right surface
ax.plot_wireframe(x32, y3, z3, color='b', rstride=1, cstride=1, alpha=0.6)
ax.set_xlabel('X')
ax.set_xlim(-100, 100)
ax.set_ylabel('Y')
ax.set_ylim(-100, 100)
ax.set_zlabel('Z')
ax.set_zlim(-100, 100)
plt.show()
def test():
center = [0, 0, 0]
length = 32 * 2
width = 50 * 2
height = 100 * 2
plot_cuboid(center, (length, width, height))
if __name__ == '__main__':
test()
Here is the result.
Everybody forgets about POVray that handles 3D very well. It doesn't render wireframe, though, but you can use a half-transparent texture to see what is inside of the box.
#!/usr/bin/python
# -*- coding: utf-8 -*-
import os
center='-1, -1, -1'
radius='1, 1, 1'
pov='camera { location <0, 2, -3> look_at <0, 1, 2> }\n\
light_source { <2, 4, -3> color rgb 1*1.5}\n\
background {color rgb <0.00, 0.00, 0.00>}\n\
box {<'+center+'>, < '+radius+'>\n\
pigment { color rgbt <0.67, 1.00, 0.39, 0.80> }\n\
rotate <52, 6, 0>\n\
scale 0.9\n\
translate <0, 1.2, 1>}\n\
'
f=open('scene.pov', 'w')
f.write(pov)
f.close()
os.system('povray +W400 +H300 +A +FN scene.pov')
Output "scene.png"
You need to read povray's documentation.

How do you create a 3D surface plot with missing values matplotlib?

I am trying to create a 3D surface energy diagram where an x,y position on a grid contains an associated z level. The issue is that the grid is not uniform (ie, there is not a z component for every x,y position). Is there a way to refrain from plotting those values by calling them NaN in the corresponding position in the array?
Here is what I have tried so far:
import numpy as np
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import pylab
from matplotlib import cm
#Z levels
energ = np.array([0,3.5,1,-0.3,-1.5,-2,-3.4,-4.8])
#function for getting x,y associated z values?
def fun(x,y,array):
return array[x]
#arrays for grid
x = np.arange(0,7,0.5)
y = np.arange(0,7,0.5)
#create grid
X, Y = np.meshgrid(x,y)
zs = np.array([fun(x,y,energ) for x in zip(np.ravel(X))])
Z = zs.reshape(X.shape)
plt3d = plt.figure().gca(projection='3d')
#gradients now with respect to x and y, but ideally with respect to z only
Gx, Gz = np.gradient(X * Y)
G = (Gx ** 2 + Gz ** 2) ** .5 # gradient magnitude
N = G / G.max() # normalize 0..1
plt3d.plot_surface(X, Y, Z, rstride=1, cstride=1,
facecolors=cm.jet(N), edgecolor='k', linewidth=0, antialiased=False, shade=False)
plt.show()
I cannot post image here of this plot but if you run the code you will see it
But I would like to not plot certain x,y pairs, so the figure should triangle downward to the minimum. Can this be accomplished by using nan values? Also would like spacing between each level, to be connected by lines.
n = np.NAN
#energ represents the z levels, so the overall figure should look like a triangle.
energ = np.array([[0,0,0,0,0,0,0,0,0,0,0,0,0],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,2.6,n,2.97,n,2.6,n,2.97,n,2.6,n,3.58,n],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,n,1.09,n,1.23,n,1.09,n,1.23,n,1.7,n,n],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,n,n,-0.65,n,-0.28,n,-0.65,n,0.33,n,n,n],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,n,n,n,-2.16,n,-2.02,n,-1.55,n,n,n,n],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,n,n,n,n,-3.9,n,-2.92,n,n,n,n,n,],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,n,n,n,n,n,-4.8,n,n,n,n,n,n,]])
plt3d = plt.figure().gca(projection='3d')
Gx, Gz = np.gradient(X * energ) # gradients with respect to x and z
G = (Gx ** 2 + Gz ** 2) ** .5 # gradient magnitude
N = G / G.max() # normalize 0..1
x = np.arange(0,13,1)
y = np.arange(0,13,1)
X, Y = np.meshgrid(x,y)
#but the shapes don't seem to match up
plt3d.plot_surface(X, Y, energ, rstride=1, cstride=1,
facecolors=cm.jet(N), edgecolor='k',
linewidth=0, antialiased=False, shade=False
)
Using masked arrays generates the following error: local Python[7155] : void CGPathCloseSubpath(CGMutablePathRef): no current point.
n = np.NAN
energ = np.array([[0,0,0,0,0,0,0,0,0,0,0,0,0],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,2.6,n,2.97,n,2.6,n,2.97,n,2.6,n,3.58,n],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,n,1.09,n,1.23,n,1.09,n,1.23,n,1.7,n,n],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,n,n,-0.65,n,-0.28,n,-0.65,n,0.33,n,n,n],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,n,n,n,-2.16,n,-2.02,n,-1.55,n,n,n,n],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,n,n,n,n,-3.9,n,-2.92,n,n,n,n,n,],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,n,n,n,n,n,-4.8,n,n,n,n,n,n,]])
x = np.arange(0,13,1)
y = np.arange(0,13,1)
X, Y = np.meshgrid(x,y)
#create masked arrays
mX = ma.masked_array(X, mask=[[0,0,0,0,0,0,0,0,0,0,0,0,0],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,0,1,0,1,0,1,0,1,0,1,0,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,0,1,0,1,0,1,0,1,0,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,0,1,0,1,0,1,0,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,0,1,0,1,0,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,0,1,0,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,0,1,1,1,1,1,1]])
mY = ma.masked_array(Y, mask=[[0,0,0,0,0,0,0,0,0,0,0,0,0],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,0,1,0,1,0,1,0,1,0,1,0,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,0,1,0,1,0,1,0,1,0,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,0,1,0,1,0,1,0,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,0,1,0,1,0,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,0,1,0,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,0,1,1,1,1,1,1]])
m_energ = ma.masked_array(energ, mask=[[0,0,0,0,0,0,0,0,0,0,0,0,0],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,0,1,0,1,0,1,0,1,0,1,0,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,0,1,0,1,0,1,0,1,0,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,0,1,0,1,0,1,0,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,0,1,0,1,0,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,0,1,0,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,0,1,1,1,1,1,1]])
plt3d = plt.figure().gca(projection='3d')
plt3d.plot_surface(mX, mY, m_energ, rstride=1, cstride=1, edgecolor='k', linewidth=0, antialiased=False, shade=False)
plt.show()
I was playing around with the code from this forum post, and I was able to make the graph have missing values. You can try the code yourself! I got it to work using float("nan") for the missing values.
import plotly.graph_objects as go
import numpy as np
x = np.arange(0.1,1.1,0.1)
y = np.linspace(-np.pi,np.pi,10)
#print(x)
#print(y)
X,Y = np.meshgrid(x,y)
#print(X)
#print(Y)
result = []
for i,j in zip(X,Y):
result.append(np.log(i)+np.sin(j))
result[0][0] = float("nan")
upper_bound = np.array(result)+1
lower_bound = np.array(result)-1
fig = go.Figure(data=[
go.Surface(z=result),
go.Surface(z=upper_bound, showscale=False, opacity=0.3,colorscale='purp'),
go.Surface(z=lower_bound, showscale=False, opacity=0.3,colorscale='purp')])
fig.show()

Categories

Resources