forcing two matplotlib 3d plots to be in one figure - python

How can I make these to be overlapping or be together in one figure (not side by side):
fig = plt.figure()
ax = Axes3D(fig)
ax.set_xlim(-6, 6)
ax.set_ylim(-6, 6)
ax.set_zlim(-6, 6)
xx_floor, yy_floor, zz_floor = plot_plane(plane1['a'],
plane1['b'],
plane1['c'],
plane1['d'],
xwin=[-2, 2],
ywin=[-2, 2])
ax.plot_surface(xx_floor, yy_floor, zz_floor, color=(0, 1, 1, 0.3))
plane2_points = plane2[:3]
p0, p1, p2 = plane2_points
x0, y0, z0 = p0
x1, y1, z1 = p1
x2, y2, z2 = p2
ux, uy, uz = u = [x1 - x0, y1 - y0, z1 - z0]
vx, vy, vz = v = [x2 - x0, y2 - y0, z2 - z0]
u_cross_v = [uy * vz - uz * vy, uz * vx - ux * vz, ux * vy - uy * vx]
plane2_point = np.array(p0)
plane2_normal = np.array(u_cross_v)
pointdot = -plane2_point.dot(plane2_normal)
xx, yy = np.meshgrid(range(6), range(6))
z = (-plane2_normal[0] * xx - plane2_normal[1] * yy - pointdot) * 1. / plane2_normal[2]
plt3d = plt.figure().gca(projection='3d')
plt3d.plot_surface(xx, yy, z)
plt.show()
plt.close(fig=fig)
Currently, I see two figures that I should close the first one to see the second one.

fig = plt.figure()
ax = Axes3D(fig)
ax.set_xlim(-6, 6)
ax.set_ylim(-6, 6)
ax.set_zlim(-6, 6)
xx_floor, yy_floor, zz_floor = plot_plane(plane1['a'],
plane1['b'],
plane1['c'],
plane1['d'],
xwin=[-2, 2],
ywin=[-2, 2])
ax.plot_surface(xx_floor, yy_floor, zz_floor, color=(0, 1, 1, 0.3))
plane2_points = plane2[:3]
p0, p1, p2 = plane2_points
x0, y0, z0 = p0
x1, y1, z1 = p1
x2, y2, z2 = p2
ux, uy, uz = u = [x1 - x0, y1 - y0, z1 - z0]
vx, vy, vz = v = [x2 - x0, y2 - y0, z2 - z0]
u_cross_v = [uy * vz - uz * vy, uz * vx - ux * vz, ux * vy - uy * vx]
plane2_point = np.array(p0)
plane2_normal = np.array(u_cross_v)
pointdot = -plane2_point.dot(plane2_normal)
xx, yy = np.meshgrid(range(6), range(6))
z = (-plane2_normal[0] * xx - plane2_normal[1] * yy - pointdot) * 1. / plane2_normal[2]
#plt3d = plt.figure().gca(projection='3d')
#plt3d.plot_surface(xx, yy, z)
ax.plot_surface(xx, yy, z)
plt.show()
plt.close(fig=fig)
Followed the instructions by swatchai

Related

How to fill the logarithmic spiral with color

I have written a snippet to draw a logarithmic spiral, now the curves drawing is done, but for the color filling part, I'm not familiar with how to fill it (part between the two curves, and part between the curve and outer circle border). How to finish the color filling part? Is fill_between more suitable here?
import matplotlib.pyplot as plt
b = 0.2
a = 2
theta = np.linspace(0, np.pi * 3.0, 1000, endpoint=True)
r = exp(b * theta) * a
theta0 = np.linspace(0, np.pi * 4.0, 1000, endpoint=True)
r0 = [r[-1]] * len(theta0)
theta2 = np.linspace(np.pi, np.pi * 4.0, 1000, endpoint=True)
r2 = exp(b * theta) * a
ax = plt.subplot(111, projection='polar')
ax.set_rmax(r[-1])
ax.fill(theta0, r0, c='blue')
ax.plot(theta, r)
ax.plot(theta2, r2)
ax.fill(theta2, r2, 'red')
theta3 = np.linspace(0, np.pi * 2.0, 1000, endpoint=True)
r3 = [a] * len(theta3)
plt.box(on=None)
ax.fill(theta3, r3, c='black')
ax.grid(False)
plt.show()
What I have got now:
One way is to create a close line for the fill command, putting together the different pieces:
import matplotlib.pyplot as plt
import numpy as np
b = 0.2
a = 2
theta1 = np.linspace(0, np.pi * 3.0, 1000, endpoint=True)
r1 = np.exp(b * theta1) * a
theta2 = np.linspace(np.pi, np.pi * 4.0, 1000, endpoint=True)
r2 = np.exp(b * theta1) * a
theta3 = np.linspace(np.pi, 0, 1000)
r3 = r1[-1] * np.ones_like(theta3)
theta4 = np.linspace(np.pi, 2 * np.pi, 1000)
r4 = a * np.ones_like(theta4)
theta5 = np.linspace(np.pi, 2*np.pi, 1000)
r5 = r1[-1] * np.ones_like(theta5)
theta6 = np.linspace(0, np.pi, 1000)
r6 = a * np.ones_like(theta6)
# put together the different pieces
theta_final_red = np.concatenate([theta1, theta3, np.flip(theta2), theta4])
radius_red = np.concatenate([r1, r3, np.flip(r2), r4])
theta_final_blue = np.concatenate([theta1, theta5, np.flip(theta2), theta6])
radius_blue = np.concatenate([r1, r5, np.flip(r2), r6])
fig = plt.figure()
ax = fig.add_subplot(111, projection='polar')
ax.set_rmax(r1[-1])
ax.fill(theta_final_red, radius_red, "r")
ax.fill(theta_final_blue, radius_blue, "b")
ax.plot(theta1, r1)
ax.plot(theta2, r2)
# black inner circle
theta3 = np.linspace(0, np.pi * 2.0, 1000, endpoint=True)
r3 = [a] * len(theta3)
ax.fill(theta3, r3, c='black')
ax.axis(False)
ax.grid(False)
plt.show()

Is there a way to graph 3d points on a cylinder graph in python?

I'm in the process of graphing different color values based on their hue, lightness, and saturation as the xyz axis. It works pretty well with matplotlib, here's a pic.
But recently I saw an image of a graph on the internet that's cylinder based
I couldn't find any info on the internet about it, and it seems that matplotlib doesn't support making this kind of graph. Does anyone know of a way to plot scatter points on a cylinder graph in python?
In order to place points on the surface of a cylinder, you would have to parameterize the surface. This is one way:
import numpy as np
import matplotlib.pyplot as plt
def cylinder(R, z_min, z_max, theta_max, N):
z = np.random.uniform(z_min, z_max, N)
theta = np.random.uniform(0, theta_max, N)
x = R * np.cos(theta)
y = R * np.sin(theta)
return x, y, z
def dics(z, R_max, theta_max, N):
r = np.random.uniform(0, R_max, N)
theta = np.random.uniform(0, theta_max, N)
x = r * np.cos(theta)
y = r * np.sin(theta)
return x, y, z * np.ones_like(x)
def plane(theta, R_max, z_min, z_max, N):
r = np.random.uniform(0, R_max, N)
x = r * np.cos(theta)
y = r * np.sin(theta)
z = np.random.uniform(z_min, z_max, N)
return x, y, z
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, projection="3d")
N = 1000
z_min = -1
z_max = 1
R = 3
theta_max = 1.5 * np.pi
# points on the cylindrical surface
x_cyl, y_cyl, z_cyl = cylinder(R, z_min, z_max, theta_max, N)
ax.scatter(x_cyl, y_cyl, z_cyl)
# points on the top cap
x_top, y_top, z_top = dics(z_max, R, theta_max, N)
ax.scatter(x_top, y_top, z_top)
# points on the bottom cap
x_bottom, y_bottom, z_bottom = dics(z_min, R, theta_max, N)
ax.scatter(x_bottom, y_bottom, z_bottom)
# points on the first slice-wall
x1, y1, z1 = plane(0, R, z_min, z_max, N)
ax.scatter(x1, y1, z1)
# points on the second slice-wall
x2, y2, z2 = plane(theta_max, R, z_min, z_max, N)
ax.scatter(x2, y2, z2)
plt.show()
Then, you would have to modify the above functions to return the angle theta and the radius r, so that you can apply colors based on H, S, V, but that's left for you as an exercise :)
Edit: if you just want to plot scatter points into the volume of a sliced cylinder, you could modify the cylinder function in this way:
def cylinder(R, z_min, z_max, theta_max, N):
z = np.random.uniform(z_min, z_max, N)
theta = np.random.uniform(0, theta_max, N)
r = np.random.uniform(0, R, N)
x = r * np.cos(theta)
y = r * np.sin(theta)
return x, y, z

Correctly interpolate 3D vector field with python scipy

GOAL
My goal is to interpolate a 3D vector field using python.
CODE
Original Vector field
import numpy as np
import matplotlib.pyplot as plt
# For interpolation
from scipy.interpolate import RegularGridInterpolator
#%% VECTOR FIELD
xx, yy, zz = np.meshgrid(np.arange(-0.8, 1, 0.2),
np.arange(-0.8, 1, 0.2),
np.arange(-0.8, 1, 0.8))
uu = np.sin(np.pi * xx) * np.cos(np.pi * yy) * np.cos(np.pi * zz)
vv = -np.cos(np.pi * xx) * np.sin(np.pi * yy) * np.cos(np.pi * zz)
ww = (np.sqrt(2.0 / 3.0) * np.cos(np.pi * xx) * np.cos(np.pi * yy) *
np.sin(np.pi * zz))
# Ravel -> make 1D lists
x = np.ravel(xx)
y = np.ravel(yy)
z = np.ravel(zz)
u = np.ravel(uu)
v = np.ravel(vv)
w = np.ravel(ww)
Interpolation Function (something is wrong here)
#%% INTERPOLATION FUNCTION
def interpolate_field(x,y,z,u,v,w,new_points):
x = np.unique(x)
y = np.unique(y)
z = np.unique(z)
u = np.reshape(u, (len(x), len(y), len(z)))
v = np.reshape(u, (len(x), len(y), len(z)))
w = np.reshape(u, (len(x), len(y), len(z)))
u_int_f = RegularGridInterpolator((x, y, z), u)
v_int_f = RegularGridInterpolator((x, y, z), v)
w_int_f = RegularGridInterpolator((x, y, z), w)
u_int = u_int_f(new_points)
v_int = v_int_f(new_points)
w_int = w_int_f(new_points)
return u_int, v_int, w_int
Evaluate interpolation at new points
#%% EVALUATE INTERPOLATION FUNCTION
new_grid = np.meshgrid(
np.linspace(np.min(x), np.max(x), 20),
np.linspace(np.min(y), np.max(y), 20),
np.linspace(np.min(z), np.max(z), 3)
, indexing="xy")
# create list of new_points
new_points = np.vstack(list(map(np.ravel, new_grid))).T
# get vector field values at new points
uint, vint, wint = interpolate_field(x,y,z,u,v,w,new_points)
new_points = np.array(new_points)
xn = new_points[:,0]
yn = new_points[:,1]
zn = new_points[:,2]
# PLOT
fig = plt.figure(dpi=300)
ax = fig.gca(projection='3d')
ax.quiver(xn, yn, zn, uint, vint, wint, length=0.1)
plt.show()
THE ISSUE
As you can see something is wrong with the interpolation function since the resulting vector plot does not show the same behavior as the original at all.
Here is a way to do it using RegularGridInterpolator:
import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import RegularGridInterpolator
def interp_field(field, coords, shape):
interpolator = RegularGridInterpolator(
(gridZ[:, 0, 0], gridY[0, :, 0], gridX[0, 0, :]), field)
return interpolator(coords).reshape(shape)
gridZ, gridY, gridX = np.mgrid[-0.8:1:3j, -0.8:1:10j, -0.8:1:10j]
u = np.sin(np.pi * gridX) * np.cos(np.pi * gridY) * np.cos(np.pi * gridZ)
v = -np.cos(np.pi * gridX) * np.sin(np.pi * gridY) * np.cos(np.pi * gridZ)
w = (np.sqrt(2.0 / 3.0) * np.cos(np.pi * gridX) * np.cos(np.pi * gridY) *
np.sin(np.pi * gridZ))
interp_gridZ, interp_gridY, interp_gridX = np.mgrid[
-0.8:1:3j, -0.8:1:20j, -0.8:1:20j]
interp_coords = np.column_stack(
(interp_gridZ.flatten(), interp_gridY.flatten(), interp_gridX.flatten()))
interp_u = interp_field(u, interp_coords, interp_gridX.shape)
interp_v = interp_field(v, interp_coords, interp_gridX.shape)
interp_w = interp_field(w, interp_coords, interp_gridX.shape)
fig, (ax, bx) = plt.subplots(ncols=2, subplot_kw=dict(projection='3d'),
constrained_layout=True)
ax.quiver(gridX, gridY, gridZ, u, v, w, length=0.3)
bx.quiver(interp_gridX, interp_gridY, interp_gridZ,
interp_u, interp_v, interp_w, length=0.3)
plt.show()
The resulting plot looks like that:

ValueError: need more than 1 value to unpack with matplotlib

I have a 3D array and would like to make a 2D plot using plt.pcolormesh but I keep getting the error
ValueError: need more than 1 value to unpack.
My x and y arrays are both of length 59 and the z array is length 59*59=3481 since x and y is now a matrix.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
points = [(54.5, 17.041667, 31.993), (54.5, 17.083333, 31.911), (54.458333, 17.041667, 31.945), (54.458333, 17.083333, 31.866)]
points = sorted(points) # order points by x, then by y
(x1, y1, q11), (_x1, y2, q12), (x2, _y1, q21), (_x2, _y2, q22) = points
interp2d=[]
nums = np.linspace(x1, x2, num = 60, endpoint=True)
numms = np.linspace(y1, y2, num = 60, endpoint=True)
X, Y = np.meshgrid(nums[:-1], numms[:-1], indexing='xy')
for i in range(len(nums)-1):
for j in range(len(numms)-1):
x=nums[i]
y=numms[j]
interp = (q11 * (x2 - x) * (y2 - y) +
q21 * (x - x1) * (y2 - y) +
q12 * (x2 - x) * (y - y1) +
q22 * (x - x1) * (y - y1)
) / ((x2 - x1) * (y2 - y1) + 0.0)
interp2d.append(interp)
interp2d = np.asarray(interp2d)
fig, (ax1, ax2) = plt.subplots(1,2, sharex=True)
ax2.pcolormesh(X,Y,interp2d, cmap=plt.cm.BuPu_r)
cbar = plt.colorbar()
plt.show()
The values of interp2d are expected to be 2D array. So, you might wanna look into that. Rest of the code is irrelevant to the question you asked. So, good luck!

Python: point on a line closest to third point

I have a line/vector between two XY points (p1 and p2) and a third XY point (p3) that is outside the line. According to this post I know how to get the distance of that point to the line. But what I'm actually looking for is a point (p4) on that line that is in a minimum distance (d) to the third point (p3). I found this post, but I feel it's not the correct solution. Maybe there's something included in Numpy or Python?
According to #allo I tried the following. You can download my code as Python file or Jupyter Notebook (both Python3).
points = [[1, 1], [3, 1], [2.5, 2], [2.5, 1]]
import matplotlib.pyplot as plt
%matplotlib inline
fig, ax = plt.subplots()
fig.set_size_inches(6,6)
x, y = zip(*points[:2])
l1, = ax.plot(x,y, color='blue')
scatter1 = ax.scatter(x=x,y=y, color='blue', marker='x', s=80, alpha=1.0)
x, y = zip(*points[2:])
l2, = ax.plot(x,y, color='red')
scatter2 = ax.scatter(x=x,y=y, color='red', marker='x', s=80, alpha=1.0)
p1 = Vector2D(*points[0])
p2 = Vector2D(*points[1])
p3 = Vector2D(*points[2])
p1p2 = p2.sub_vector(p1)
p1p3 = p3.sub_vector(p1)
angle_p1p2_p1p3 = p1p2.get_angle_radians(p1p3)
length_p1p3 = p1p3.get_length()
length_p1p2 = p1p2.get_length()
p4 = p1.add_vector(p1p2.multiply(p1p3.get_length()/p1p2.get_length()).multiply(math.cos(p1p2.get_angle_radians(p1p3))))
#p4 = p1 + p1p2 * length(p1p3)/length(p1p2)*cos(angle(p1p2, p1p3))
p4 = p1.add_vector(p1p2.multiply(length_p1p3/length_p1p2*math.cos(angle_p1p2_p1p3)))
p4
Which results in p4 = (1.8062257748298551, 1.0) but should obviously be (2.5, 1.0).
Analytical Geometry
Let's start with the assigned line, we define the line in terms of two points on it (x1, y1) and (x2, y2).
With dx = x2-x1 and dy = y2-y1 we can formally write every point on the line as (x12, y12) = (x1, y1) + a*(dx, dy) where a is a real number.
Using an analogous notation a point on the line passing in (x3, y3) and perpendicular to the assigned one is (x34, y34) = (x3, y3) + b*(-dy, +dx).
To find the intersection we have to impose (x12, y12) = (x34, y34) or
(x1, y1) + a*(dx, dy) = (x3, y3) + b*(-dy, +dx).
Writing separately the equations for x and y
y1 + a dy - y3 - b dx = 0
x1 + a dx + b dy - x3 = 0
it is a linear system in a and b whose solution is
a = (dy y3 - dy y1 + dx x3 - dx x1) / (dy^2 + dx^2)
b = (dy x3 - dy x1 - dx y3 + dx y1) / (dy^2 + dx^2)
The coordinates of the closest point to (x3, y3) lying on the line
are (x1+a*dx, y1+a*dy) — you need to compute only the coefficient a.
Numerically speaking, the determinant of the linear system is dx**2+dy**2 so you have problems only when the two initial points are extremely close to each other with respect to their distance w/r to the third point.
Python Implementation
We use a 2-uple of floats to represent a 2D point and we define a function whose arguments are 3 2-uples representing the points that define the line (p1, p2) and the point (p3) that determines the position of p4 on said line.
In [16]: def p4(p1, p2, p3):
...: x1, y1 = p1
...: x2, y2 = p2
...: x3, y3 = p3
...: dx, dy = x2-x1, y2-y1
...: det = dx*dx + dy*dy
...: a = (dy*(y3-y1)+dx*(x3-x1))/det
...: return x1+a*dx, y1+a*dy
To test the implementation I am using the three points used by the OP
to demonstrate their issues with this problem:
In [17]: p4((1.0, 1.0), (3.0, 1.0), (2.5, 2))
Out[17]: (2.5, 1.0)
It seems that the result of p4(...) coincides with the OP expectation.
A Matplotlib Example
import matplotlib.pyplot as plt
def p(p1, p2, p3):
(x1, y1), (x2, y2), (x3, y3) = p1, p2, p3
dx, dy = x2-x1, y2-y1
det = dx*dx + dy*dy
a = (dy*(y3-y1)+dx*(x3-x1))/det
return x1+a*dx, y1+a*dy
p1, p2, p3 = (2, 4), (7, 3), (1, 1)
p4 = p(p1, p2, p3)
fig, ax = plt.subplots()
# if we are after right angles, anything else would be wrong
ax.set_aspect(1)
plt.plot(*zip(p1, p2, p4, p3), marker='*')
Shapely's distance() function returns the minimum distance:
>>> from shapely.geometry import LineString as shLs
>>> from shapely.geometry import Point as shPt
>>> l = shLs([ (1,1), (3,1)])
>>> p = shPt(2,2)
>>> dist = p.distance(l)
1.0
>>> l.interpolate(dist).wkt
'POINT (2 1)'
What you want to do is a vector projection.
The edge p1p3 is rotated onto the edge p1p2 and you need to find the correct length of the segment p1p4. Then you can just use p1+FACTOR*p1p2 / length(p1p2). The needed factor is given by the cosine of the angle between p1p2 and p1p3. Then you get
p4 = p1 + p1p2 * length(p1p3)/length(p1p2)*cos(angle(p1p2, p1p3))
Here the two edge cases as example:
The cosinus is 0 if p1p3 is orthogonal to p1p2, so p4 lies on p1.
The cosinus is 1 when p1p3 lies on p1p2, so p1p2 is just scaled by length(p1p3)/length(p1p2) to get p1p4.
You further can replace the cosinus by the dot product dot(p1p2 / length(p1p2), p1p3 / length(p1p3).
You can find more details and nice illustrations in the wikibook about linear algebra.
Here an full example derived from your python code. I used numpy instead of Vector2D here.
points = [[1, 1], [3, 1], [2.5, 2], [2.5, 1]]
import matplotlib.pyplot as plt
%matplotlib inline
import numpy as np
fig, ax = plt.subplots()
fig.set_size_inches(6,6)
x, y = zip(*points[:2])
l1, = ax.plot(x,y, color='blue')
scatter1 = ax.scatter(x=x,y=y, color='blue', marker='x', s=80, alpha=1.0)
x, y = zip(*points[2:])
l2, = ax.plot(x,y, color='red')
scatter2 = ax.scatter(x=x,y=y, color='red', marker='x', s=80, alpha=1.0)
p1 = np.array(points[0])
p2 = np.array(points[1])
p3 = np.array(points[2])
p1p2 = p2 - p1
p1p3 = p3 - p1
p4 = p1 + p1p2 / np.linalg.norm(p1p2) * np.linalg.norm(p1p3) * ((p1p2/np.linalg.norm(p1p2)).T * (p1p3/np.linalg.norm(p1p3)))
p1, p2, p3, p4, p1p2, p1p3
We can shorten the p4 line a bit like this, using the linearity of the scalar product:
p4 = p1 + p1p2 / np.linalg.norm(p1p2) * ((p1p2/np.linalg.norm(p1p2)).T * (p1p3))
p4 = p1 + p1p2 / np.linalg.norm(p1p2)**2 * (p1p2.T * (p1p3))

Categories