How to form a plane surfaces using 8 points? - python

I have plotted my 8 corner points with center of a cuboid as in figure .
I have tried with the scatter but now i want the surface plot connecting these 8 points.
when i have tried with the surface plot i am unable to attend to that. Can you please suggest any solution for that
l = 0.3
w = 0.4
h = 0.1
center =
[2.10737, -0.100085, 0.716869]
F=
[[array([[1.]]) array([[-0.001]]) array([[-0.017]])]
[array([[0.]]) array([[-0.999]]) array([[0.037]])]
[array([[0.017]]) array([[0.037]]) array([[0.999]])]]
def cuboid(center, size):
ox, oy, oz = center
l, w, h = size
ax = fig.gca(projection='3d') ##plot the project cuboid
X=[ox-l/2,ox-l/2,ox-l/2,ox-l/2,ox+l/2,ox+l/2,ox+l/2,ox+l/2]
Y=[oy+w/2,oy-w/2,oy-w/2,oy+w/2,oy+w/2,oy-w/2,oy-w/2,oy+w/2]
Z=[oz-h/2,oz-h/2,oz+h/2,oz+h/2,oz+h/2,oz+h/2,oz-h/2,oz-h/2]
X_new = ([])
Y_new = ([])
Z_new = ([])
for i in range(0,8):
c=np.matrix([[X[i]],
[Y[i]],
[Z[i]]])
u=F*c
X_new = np.append(X_new, u.item(0))
Y_new = np.append(Y_new, u.item(1))
Z_new = np.append(Z_new, u.item(2))
ax.scatter(X_new,Y_new,Z_new,c='darkred',marker='o') #the plot of points after rotated
ax.scatter(ox,oy,oz,c='crimson',marker='o') #the previous plot of points before rotated
## Add title
plt.title('Plot_for_PSM', fontsize=20)
plt.gca().invert_yaxis()
##labelling the axes
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')

Here a solution.
###Added import
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Poly3DCollection, Line3DCollection
###Addded size and center in this format in order to manipulate them easily
size = [0.3, 0.4, 0.1]
center = [2.10737, -0.100085, 0.716869]
###This numpy vector will be used to store the position of the sides
side = np.zeros((8,3))
###Just re-ordered your matrix in some np.arrays
F = [[np.array([1., -0.001, -0.017])],
[np.array([0., -0.999, 0.037])],
[np.array([0.017, 0.037, 0.999])]]
def cuboid(center, size):
ox, oy, oz = center
l, w, h = size
###Added the fig in order to be able to plot it later
fig = plt.figure()
ax = fig.gca(projection='3d') ##plot the project cuboid
X=[ox-l/2,ox-l/2,ox-l/2,ox-l/2,ox+l/2,ox+l/2,ox+l/2,ox+l/2]
Y=[oy+w/2,oy-w/2,oy-w/2,oy+w/2,oy+w/2,oy-w/2,oy-w/2,oy+w/2]
Z=[oz-h/2,oz-h/2,oz+h/2,oz+h/2,oz+h/2,oz+h/2,oz-h/2,oz-h/2]
X_new = ([])
Y_new = ([])
Z_new = ([])
for i in range(0,8):
c=np.matrix([[X[i]],
[Y[i]],
[Z[i]]])
u=F*c
X_new = np.append(X_new, u.item(0))
Y_new = np.append(Y_new, u.item(1))
Z_new = np.append(Z_new, u.item(2))
###Doing a dot product between F and c like you did earlier but using np.dot as we're now working with Numpy format
side[i,:] = np.dot(F, c)
###Storing the position of every points
sides = [[side[0],side[1],side[2],side[3]],
[side[4],side[5],side[6],side[7]],
[side[0],side[1],side[4],side[5]],
[side[2],side[3],side[4],side[5]],
[side[1],side[2],side[5],side[6]],
[side[4],side[7],side[0],side[3]]]
###Scatter plot
ax.scatter(X_new,Y_new,Z_new,c='darkred',marker='o') #the plot of points after rotated
ax.scatter(ox,oy,oz,c='crimson',marker='o') #the previous plot of points before rotated
### Add title
plt.title('Plot_for_PSM', fontsize=20)
plt.gca().invert_yaxis()
##labelling the axes
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
###This draw the plane sides as you wanted
ax.add_collection3d(Poly3DCollection(sides, facecolors='blue', linewidths=1, edgecolors='r', alpha=.25))
cuboid(center, size)
###Mandatory to plot the cube
plt.show()
It uses Poly3DCollection, Line3DCollection from mpl_toolkits to draw 6 plane square, representing the sides of the cube.
The first step is to find the 4 coords of every side. Then you need to use Poly3DCollection to plot it.

Related

Two equals ellipses contact rotation about centers

Let the distance between the centers of two identical ellipses with semi-axes $a, b$ be $a+b$.
The first ellipse rotates around its center with a constant angular velocity $w$.
The second ellipse rotates so that it is in contact with the first ellipse (i.e. there is a one point of contact between the two ellipses).
Calculate the speed of the second ellipse depending on the rotation angle of the first ellipse. Find the locus of the points of contact of two ellipses.
To find the contact line, to solve the system of three equations
$$x_1(t,p)=x_2(s,q)$$
$$y_1(t,p)=y_2(s,q)$$
$$\frac{x_1(t,p)_t}{x_2(s,q)_s}=\frac{y_1(t,p)_t}{y_2(s,q)_s}$$
Here $$p$$ and $$q$$ are the rotation angles of the ellipses. The system of equations is solved using standard Python tools.
The locus contact points are two lines - one in the form of an infinity
symbol (1 line) and the second in the form of a line close to an ellipse (2 line), passes through points $(a,0)$, $(b,0)$.
For each value $p$ of the rotation angle of the first ellipse, the corresponding value of the rotation angle $q$ for the second ellipse is sought.
I find set of $p,q$ - but this set mixture values for 1 line and values for 2 line. How to sort this set in two sets for values for 1 line and values for 2 line ? This sets give correct animation this problem by Python.
# two equals ellipses (a*cos(t),b*sin(t)), (a*cos(s)+a+b,b*sin(s))
# contact rotation about centers (0,0) and (a+b,0)
# front and rear contact - two variants
# the first ellipse has a constant angular velocity of rotation
import scipy.optimize
import numpy as np
import math as m
import matplotlib.pyplot as plt
import time
fig = plt.figure()
plt.xlabel('X')
plt.ylabel('Y')
ax = fig.gca()
plt.axis('equal')
P=[]
def xx(p,t,sd):
return a*np.cos(t)*np.cos(p)+b*np.sin(t)*np.sin(p)+sd
def yy(p,t,sd):
return -a*np.cos(t)*np.sin(p)+b*np.sin(t)*np.cos(p)+sd
def f(y):
t,q,s = y[:3]
eq1=yy(p,t,0)-yy(q,s,0)
eq2=xx(p,t,0)-xx(q,s,a+b)
eq3=(a*m.sin(p)*m.sin(t) + b*m.cos(p)*m.cos(t))*(-a*m.sin(s)*m.cos(q) + b*m.sin(q)*m.cos(s))-(
(a*m.sin(q)*m.sin(s) + b*m.cos(q)*m.cos(s))*(-a*m.sin(t)*m.cos(p) + b*m.sin(p)*m.cos(t)))
return(eq1,eq2,eq3)
w, a, b, h, N =0, 3, 2, 2*m.pi, 36
for k in range (N):
p=h/N*k
x0 = np.array([3.3,0.3,-p+m.pi/6])
sol = scipy.optimize.root(f, x0, method='lm')
t,q,s=sol.x
e1,e2,e3=f(sol.x)
if abs(e1)+abs(e2)+abs(e3)<10**(-7):
w=w+1
q=m.fmod(q,2*m.pi)
#print (p,q)
P.append([p,q])
x, y =xx(p,t,0), yy(p,t,0)
plt.plot(x,y,'ro',markersize=1.)
ksi = np.arange(0, 2*np.pi, 0.01)
#plt.plot(np.sin(ksi)/2+(a+b)/2, 1*np.cos(ksi), lw=1)
for pq in P:
p, q =pq[0],pq[1]
plt.plot(xx(q,ksi,a+b), yy(q,ksi,0), lw=1)
plt.plot(xx(p,ksi,0), yy(p,ksi,0), lw=1)
plt.plot(0, 0, 'ro',markersize=2.)
plt.plot(a+b, 0, 'ro',markersize=2.)
print(w)
plt.grid()
plt.show()
#time.sleep(1)
#plt.plot(xx(q,ksi,a+b), yy(q,ksi,0), lw=0)
#plt.plot(xx(p,ksi,0), yy(p,ksi,0), lw=0)
Example animation
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
N=20
N1=120
a=3
b=2
t = np.arange(0, 2*np.pi+4*np.pi/N1, 2*np.pi/N1)
x = a*np.cos(t)
y = b*np.sin(t)
fig, ax = plt.subplots()
plt.axis('equal')
line1, = ax.plot(x, y, color = "r")
line2, = ax.plot(y+2*a+b, x, color = "g")
def update(t, x, y, line1, line2):
line2.set_data(x*np.cos(t/N)+y*np.sin(t/N), -x*np.sin(t/N)+y*np.cos(t/N))
line1.set_data(y*np.cos(-t/N)+(x)*np.sin(-t/N)+a+b , -y*np.sin(-t/N)+x*np.cos(-t/N))
return [line1,line2]
ani = animation.FuncAnimation(fig, update, int(2*np.pi*N), fargs=[ x, y, line1, line2],
interval=295, blit=True)
ax.set_xlabel('x')
ax.set_ylabel('y')
plt.show()

How does the Matplotlib trisurf plot work?

I am unable to understand from the matplotlib documentation(https://matplotlib.org/mpl_toolkits/mplot3d/tutorial.html), the working of a trisurf plot. Can someone please explain how the X,Y and Z arguments result in a 3-D plot?
Let me talk you through this example taken from the docs
'''
======================
Triangular 3D surfaces
======================
Plot a 3D surface with a triangular mesh.
'''
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
n_radii = 8
n_angles = 36
# Make radii and angles spaces (radius r=0 omitted to eliminate duplication).
radii = np.linspace(0.125, 1.0, n_radii)
angles = np.linspace(0, 2*np.pi, n_angles, endpoint=False)
# Repeat all angles for each radius.
angles = np.repeat(angles[..., np.newaxis], n_radii, axis=1)
# Convert polar (radii, angles) coords to cartesian (x, y) coords.
# (0, 0) is manually added at this stage, so there will be no duplicate
# points in the (x, y) plane.
x = np.append(0, (radii*np.cos(angles)).flatten())
y = np.append(0, (radii*np.sin(angles)).flatten())
# Compute z to make the pringle surface.
z = np.sin(-x*y)
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot_trisurf(x, y, z, linewidth=0.2, antialiased=True)
plt.show()
The x, y values are a range of values over which we calculate the surface. For each (x, y) pair of coordinates, we have a single value of z, which represents the height of the surface at that point.

scatterplot and combined polar histogram in matplotlib

I am attempting to produce a plot like this which combines a cartesian scatter plot and a polar histogram. (Radial lines optional)
A similar solution (by Nicolas Legrand) exists for looking at differences in x and y (code here), but we need to look at ratios (i.e. x/y).
More specifically, this is useful when we want to look at the relative risk measure which is the ratio of two probabilities.
The scatter plot on it's own is obviously not a problem, but the polar histogram is more advanced.
The most promising lead I have found is this central example from the matplotlib gallery here
I have attempted to do this, but have run up against the limits of my matplotlib skills. Any efforts moving towards this goal would be great.
I'm sure that others will have better suggestions, but one method that gets something like you want (without the need for extra axes artists) is to use a polar projection with a scatter and bar chart together. Something like
import matplotlib.pyplot as plt
import numpy as np
x = np.random.uniform(size=100)
y = np.random.uniform(size=100)
r = np.sqrt(x**2 + y**2)
phi = np.arctan2(y, x)
h, b = np.histogram(phi, bins=np.linspace(0, np.pi/2, 21), density=True)
colors = plt.cm.Spectral(h / h.max())
ax = plt.subplot(111, projection='polar')
ax.scatter(phi, r, marker='.')
ax.bar(b[:-1], h, width=b[1:] - b[:-1],
align='edge', bottom=np.max(r) + 0.2, color=colors)
# Cut off at 90 degrees
ax.set_thetamax(90)
# Set the r grid to cover the scatter plot
ax.set_rgrids([0, 0.5, 1])
# Let's put a line at 1 assuming we want a ratio of some sort
ax.set_thetagrids([45], [1])
which will give
It is missing axes labels and some beautification, but it might be a place to start. I hope it is helpful.
You can use two axes on top of each other:
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(6,6))
ax1 = fig.add_axes([0.1,0.1,.8,.8], label="cartesian")
ax2 = fig.add_axes([0.1,0.1,.8,.8], projection="polar", label="polar")
ax2.set_rorigin(-1)
ax2.set_thetamax(90)
plt.show()
Ok. Thanks to the answer from Nicolas, and the answer from tomjn I have a working solution :)
import numpy as np
import matplotlib.pyplot as plt
# Scatter data
n = 50
x = 0.3 + np.random.randn(n)*0.1
y = 0.4 + np.random.randn(n)*0.02
def radial_corner_plot(x, y, n_hist_bins=51):
"""Scatter plot with radial histogram of x/y ratios"""
# Axis setup
fig = plt.figure(figsize=(6,6))
ax1 = fig.add_axes([0.1,0.1,.6,.6], label="cartesian")
ax2 = fig.add_axes([0.1,0.1,.8,.8], projection="polar", label="polar")
ax2.set_rorigin(-20)
ax2.set_thetamax(90)
# define useful constant
offset_in_radians = np.pi/4
def rotate_hist_axis(ax):
"""rotate so that 0 degrees is pointing up and right"""
ax.set_theta_offset(offset_in_radians)
ax.set_thetamin(-45)
ax.set_thetamax(45)
return ax
# Convert scatter data to histogram data
r = np.sqrt(x**2 + y**2)
phi = np.arctan2(y, x)
h, b = np.histogram(phi,
bins=np.linspace(0, np.pi/2, n_hist_bins),
density=True)
# SCATTER PLOT -------------------------------------------------------
ax1.scatter(x,y)
ax1.set(xlim=[0, 1], ylim=[0, 1], xlabel="x", ylabel="y")
ax1.spines['right'].set_visible(False)
ax1.spines['top'].set_visible(False)
# HISTOGRAM ----------------------------------------------------------
ax2 = rotate_hist_axis(ax2)
# rotation of axis requires rotation in bin positions
b = b - offset_in_radians
# plot the histogram
bars = ax2.bar(b[:-1], h, width=b[1:] - b[:-1], align='edge')
def update_hist_ticks(ax, desired_ratios):
"""Update tick positions and corresponding tick labels"""
x = np.ones(len(desired_ratios))
y = 1/desired_ratios
phi = np.arctan2(y,x) - offset_in_radians
# define ticklabels
xticklabels = [str(round(float(label), 2)) for label in desired_ratios]
# apply updates
ax2.set(xticks=phi, xticklabels=xticklabels)
return ax
ax2 = update_hist_ticks(ax2, np.array([1/8, 1/4, 1/2, 1, 2, 4, 8]))
# just have radial grid lines
ax2.grid(which="major", axis="y")
# remove bin count labels
ax2.set_yticks([])
return (fig, [ax1, ax2])
fig, ax = radial_corner_plot(x, y)
Thanks for the pointers!

Python, Matplotlib: Drawing vertical lines in 3d plot, when data is independent

I have a random walker in the (x,y) plane and a -log(bivariate gaussian) in the (x,y,z) plane. These two datasets are essentially independent.
I want to sample, say 5 (x,y) pairs of the random walker and draw vertical lines up the z-axis and terminate the vertical line when it "meets" the bivariate gaussian.
This is my code so far:
import matplotlib as mpl
import matplotlib.pyplot as plt
import random
import numpy as np
import seaborn as sns
import scipy
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.mlab import bivariate_normal
%matplotlib inline
# Data for random walk
def randomwalk():
mpl.rcParams['legend.fontsize'] = 10
xyz = []
cur = [0, 0]
for _ in range(40):
axis = random.randrange(0, 2)
cur[axis] += random.choice([-1, 1])
xyz.append(cur[:])
# Get density
x, y = zip(*xyz)
data = np.vstack([x,y])
kde = scipy.stats.gaussian_kde(data)
density = kde(data)
# Data for bivariate gaussian
a = np.linspace(-7.5, 7.5, 40)
b = a
X,Y = np.meshgrid(a, b)
Z = bivariate_normal(X, Y)
surprise_Z = -np.log(Z)
# Get random points from walker and plot up z-axis to the gaussian
M = data[:,np.random.choice(20,5)].T
# Plot figure
fig = plt.figure(figsize=(10, 7))
ax = fig.gca(projection='3d')
ax.plot(x, y, 'grey', label='Random walk') # Walker
ax.scatter(x[-1], y[-1], c='k', marker='o') # End point
ax.legend()
surf = ax.plot_surface(X, Y, surprise_Z, rstride=1, cstride=1,
cmap = plt.cm.gist_heat_r, alpha=0.1, linewidth=0.1)
#fig.colorbar(surf, shrink=0.5, aspect=7, cmap=plt.cm.gray_r)
for i in range(5):
ax.plot([M[i,0], M[i,0]],[M[i,1], M[i,1]], [0,10],'k--',alpha=0.8, linewidth=0.5)
ax.set_zlim(0, 50)
ax.set_xlim(-10, 10)
ax.set_ylim(-10, 10)
Which produces
As you can see the only thing I'm struggling with is how to terminate the vertical lines when they meet the appropriate Z-value. Any ideas are welcome!
You're currently only letting those lines get to a height of 10 by using [0,10] as the z coordinates. You can change your loop to the following:
for i in range(5):
x = [M[i,0], M[i,0]]
y = [M[i,1], M[i,1]]
z = [0,-np.log(bivariate_normal(M[i,0],M[i,1]))]
ax.plot(x,y,z,'k--',alpha=0.8, linewidth=0.5)
This takes the x and y coordinates for each point you loop over and calculates the height of overlying Gaussian for that point and plots to there. Here is a plot with the linestyle changed to emphasize the lines relevant to the question:

how to rotate a 3D surface in matplotlib

I have written code to plot a 3D surface of a parabaloid in matplotlib.
How would I rotate the figure so that the figure remains in place (i.e. no vertical or horizontal shifts) however it rotates around the line y = 0 and z = 0 through an angle of theta ( I have highlighted the line about which the figure should rotate in green). Here is an illustration to help visualize what I am describing:
For example, If the figure were rotated about the line through an angle of 180 degrees then this would result in the figure being flipped 'upside down' so that the point at the origin would be now be the maximum point.
I would also like to rotate the axis so that the colormap is maintained.
Here is the code for drawing the figure:
#parabaloid
import numpy as np
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
#creating grid
y = np.linspace(-1,1,1000)
x = np.linspace(-1,1,1000)
x,y = np.meshgrid(x,y)
#set z values
z = x**2+y**2
#label axes
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
#plot figure
ax.plot_surface(x,y,z,linewidth=0, antialiased=False, shade = True, alpha = 0.5)
plt.show()
Something like this?
ax.view_init(-140, 30)
Insert it just before your plt.show() command.
Following my comment:
import mayavi.mlab as mlab
import numpy as np
x,y = np.mgrid[-1:1:0.001, -1:1:0.001]
z = x**2+y**2
s = mlab.mesh(x, y, z)
alpha = 30 # degrees
mlab.view(azimuth=0, elevation=90, roll=-90+alpha)
mlab.show()
or following #Tamas answer:
#parabaloid
import numpy as np
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from math import sin, cos, pi
import matplotlib.cm as cm
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
#creating grid
y = np.linspace(-1,1,200)
x = np.linspace(-1,1,200)
x,y = np.meshgrid(x,y)
#set z values
z0 = x**2+y**2
# rotate the samples by pi / 4 radians around y
a = pi / 4
t = np.transpose(np.array([x,y,z0]), (1,2,0))
m = [[cos(a), 0, sin(a)],[0,1,0],[-sin(a), 0, cos(a)]]
x,y,z = np.transpose(np.dot(t, m), (2,0,1))
# or `np.dot(t, m)` instead `t # m`
#label axes
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
#plot figure
ax.plot_surface(x,y,z,linewidth=0, antialiased=False, shade = True, alpha = 0.5, facecolors=cm.viridis(z0))
plt.show()
The best I could come up with is to rotate the data itself.
#parabaloid
import numpy as np
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from math import sin, cos, pi
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
#creating grid
y = np.linspace(-1,1,200)
x = np.linspace(-1,1,200)
x,y = np.meshgrid(x,y)
#set z values
z = x**2+y**2
# rotate the samples by pi / 4 radians around y
a = pi / 4
t = np.transpose(np.array([x,y,z]), (1,2,0))
m = [[cos(a), 0, sin(a)],[0,1,0],[-sin(a), 0, cos(a)]]
x,y,z = np.transpose(t # m, (2,0,1))
# or `np.dot(t, m)` instead `t # m`
#label axes
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
#plot figure
ax.plot_surface(x,y,z,linewidth=0, antialiased=False, shade = True, alpha = 0.5)
plt.show()
I can't seem to add a comment just yet but I wanted to make an amendment to Tamas' implementation. There is an issue where the surface is not rotated counter-clockwise to the axis (the y-axis in this case) where the y-axis is coming out of the page. Rather, it's rotated clockwise.
In order to rectify this, and to make it more straightforward, I construct the x, y and z grids and reshape them into straightforward lists on which we perform the rotation. Then I reshape them into grids in order to use the plot_surface() function:
import numpy as np
from matplotlib import pyplot as plt
from math import sin, cos, pi
import matplotlib.cm as cm
num_steps = 50
# Creating grid
y = np.linspace(-1,1,num_steps)
x = np.linspace(-1,1,num_steps)
x,y = np.meshgrid(x,y)
# Set z values
z = x**2+y**2
# Work with lists
x = x.reshape((-1))
y = y.reshape((-1))
z = z.reshape((-1))
# Rotate the samples by pi / 4 radians around y
a = pi / 4
t = np.array([x, y, z])
m = [[cos(a), 0, sin(a)],[0,1,0],[-sin(a), 0, cos(a)]]
x, y, z = np.dot(m, t)
ax = plt.axes(projection='3d')
# Label axes
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
# Plot the surface view it with y-axis coming out of the page.
ax.view_init(30, 90)
# Plot the surface.
ax.plot_surface(x.reshape(num_steps,num_steps), y.reshape(num_steps,num_steps), z.reshape(num_steps,num_steps));
here is the best solution:
- First, you have to perform your python script in the Spyder environment which is easy to get by downloading Anaconda. Once you perform your script in Spyder, all you have to do is to follow the next instructions:
Click on “Tools”.
Click on “Preferences”.
Click on “IPython console”.
Click on “Graphics”.
Here you’ll find an option called “Backend”, you have to change it from “Inline” to “Automaticlly”.
Finally, apply the performed changes, then Click on “OK”, and reset spyder!!!!.
Once you perform the prior steps, in theory, if you run your script, then the graphics created will appear in a different windows and you could interact with them through zooming and panning. In the case of 3d plots (3d surface) you will be able to orbit it.

Categories