What I want to get:
What I've coded using numpy, ipywidget & ipyvolume:
##define coordinates [TopPointX, ...etc.]
p1 = np.array([TopPointX, TopPointY, TopPointZ])
p2 = np.array([BottomPointX, BottomPointY, BottomPointZ])
p3 = np.array([ThirdPointX, ThirdPointY, ThirdPointZ])
##calculate plane
# These two vectors are in the plane
v1 = p3 - p1
v2 = p2 - p1
# the cross product is a vector normal to the plane
cp = np.cross(v1, v2)
a, b, c = cp
# This evaluates a * x3 + b * y3 + c * z3 which equals d
d = np.dot(cp, p3)
print('The equation is {0}x + {1}y + {2}z = {3}'.format(a, b, c, d))
x = np.linspace(TopPointX, BottomPointX, Meshcells)
y = np.linspace(TopPointY, BottomPointY, Meshcells)
X, Y = np.meshgrid(x, y)
Z = (d - a * X - b * Y) / c
#ipv
ipv.plot_surface(X, Y, Z, color="orange")
ipv.plot_wireframe(X, Y, Z, color="red")
ipv.show()
What I get:
Can I some restrict the calculation of Z to the range of 0 to -100 ? Or is there even a better way to do all this?
So, I finally figured it:
p1 = np.array([Point1X, Point1Y, Topheight])
p2 = np.array([Point2X, Point2Y, Topheight])
p3 = np.array([Point1X,Point1Y,Bottomheight])
v1 = p2 - p1 #horizontal vector
v2 = p3 - p1 #vertical vector
unitv1 = v1 / np.linalg.norm(v1) #horizontal unit vector
unitv2 = v2 / np.linalg.norm(v2) #vertical unit vector
numberofpointsH = int(np.linalg.norm(v1)/Meshsize) #number of points along horizontal vector
numberofpointsV = int(np.linalg.norm(v2)/Meshsize) #number of points along vertical vector
xy = np.empty([numberofpointsH,3])
for r, line in enumerate(xy):
xy[r] = p1+(unitv1*r*Meshsize)
xz = np.empty([numberofpointsV,3])
for r, line in enumerate(xz):
xz[r] = p1+(unitv2*r*Meshsize)
x = xy[:, [0]]
y = xy[:, [1]]
z = xz[:, [2]]
X, Z = np.meshgrid(x, z, indexing='xy') #, indexing='ij'
Y = np.empty_like(X)
for r,line in enumerate(Y):
Y[r] = y.T
#plot
ipv.plot_surface(X, Y, Z, color="orange")
ipv.plot_wireframe(X, Y, Z, color="red")
ipv.show()
The meshgird needed to be applied by X and Z, not by X and Y. The result is now:
Related
Programming in Python (Blender):
I want to create a square and print all vertices (A;B;C;D) into my console on top of a given Vector. The square should be orthogonal to this vector, like this:
def create_verts_around_point(radius, vert):
# given Vector
vec = np.array([vert[0], vert[1], vert[2]])
# side_length of square
side_length = radius
# Vctor x-direction (1,0,0)
x_vec = np.array([1,0,0])
# Vekctor y-direction (0,1,0)
y_vec = np.array([0,1,0])
# Vector z-direction (0,0,1)
z_vec = np.array([0,0,1])
p1 = vec + (side_length/2) * x_vec + (side_length/2) * y_vec + (side_length/2) * z_vec
p2 = vec - (side_length/2) * x_vec + (side_length/2) * y_vec + (side_length/2) * z_vec
p3 = vec - (side_length/2) * x_vec - (side_length/2) * y_vec + (side_length/2) * z_vec
p4 = vec + (side_length/2) * x_vec - (side_length/2) * y_vec + (side_length/2) * z_vec
But my output looks like this in the end (Square is always parallel to my x-axis and y-axis):
I don't think you're really thinking about this problem in 3D, but see if this is close.
I create a square, perpendicular to the X axis. I then rotate that square based on the angles in x, y, and z. I then position the square at the end of the vector and plot it. I add plot points for the origin and the end of the vector, and I duplicate the last point in the square do it draws all the lines.
import math
import numpy as np
from mpl_toolkits import mplot3d
import matplotlib.pyplot as plt
def create_verts_around_point(sides, vert):
x0, y0, z0 = vert
# Here is the unrotated square.
half = sides/2
square = [
[0, -half,-half],
[0, -half, half],
[0, half, half],
[0, half,-half],
]
# Now find the rotation in each direction.
thetax = math.atan2( z0, y0 )
thetay = math.atan2( z0, x0 )
thetaz = math.atan2( y0, x0 )
# Now rotate the cube, first in x.
cubes = []
txcos = math.cos(thetax)
txsin = math.sin(thetax)
tycos = math.cos(thetay)
tysin = math.sin(thetay)
tzcos = math.cos(thetaz)
tzsin = math.sin(thetaz)
for x,y,z in square:
x,y,z = (x, y * txcos - z * txsin, y * txsin + z * txcos)
x,y,z = (x * txcos - z * txsin, y, x * txsin + z * txcos)
x,y,z = (x * txcos - y * txsin, x * txsin + y * txcos, z)
cubes.append( (x0+x, y0+y, z0+z) )
return cubes
point = (10,10,10)
square = create_verts_around_point(5, point)
points = [(0,0,0),point] + square + [square[0]]
x = [p[0] for p in points]
y = [p[1] for p in points]
z = [p[2] for p in points]
ax = plt.figure().add_subplot(111, projection='3d')
ax.plot( x, y, z )
plt.show()
Output:
I have plot with size 10x10
y, x = np.mgrid[-10:10:100j, -10:10:100j]
When i draw small graphics - all good. But when coordinates go beyond the graph i see issue. How autozoom plot canvas?
def f(x,a,b):
return x**3+a*x + b
def show_plot(plt, A,B,P,Q): #A=-7, B=10, P=4, Q=8
y, x = np.mgrid[-10:10:100j, -10:10:100j]
xp = P
yp = np.sqrt(f(xp,A,B))
xq = Q
yq = np.sqrt(f(xq,A,B))
m = 0.0
if P==Q:
xx = 3*pow(xp,2)
m = (xx+A)/(2*yp)
pass
else:
m=(yp-yq)/(xp-xq) #m
xr = m*m-xp-xq
yr1 = yp+m*(xr-xp)
yr2 = yq+m*(xr-xq)
plt.contour(x, y, y**2 - f(x,A,B), levels=[0])
b = -xp*m + yp
poly = np.poly1d([-1, m**2, 2*m*b+3, b**2-5])
x = np.roots(poly)
y = np.sqrt(f(x,A,B))
x = np.linspace(-5, 5)
plt.plot(x, m*x+b)
coordinates = [('P',xp,yp), ('Q',xq,yq), ('-R',xr,-yr1)]
for x in coordinates:
plt.plot(x[1], x[2], 'ro-')
plt.annotate(x[0], xy=(x[1]-0.2, x[2]+0.2), horizontalalignment='right', verticalalignment='top',family='fantasy')
plt.plot([xr,xr],[yr1,-yr1],'g-')
plt.grid(True)
I am attempting to plot the nullcline (steady state) curves of the Oregonator model to assert the existence of a limit cycle by applying the Poincare-Bendixson Theorem. I am close, but for some reason the plot that is produced shows two straight lines. I think it has something to do with the plotting stage. Any ideas?
Also any hints for how to construct a quadrilateral to apply the theorem with would be most appreciated.
Code:
import numpy as np
import matplotlib.pyplot as plt
# Dimensionless parameters
eps = 0.04
q = 0.0008
f = 1
# Oregonator model as numpy array
def Sys(Y, t = 0):
return np.array((Y[0] * (1 - Y[0] - ((Y[0] - q) * f * Y[1]) / (Y[0] + q)) / eps, Y[0] - Y[1] ))
# Oregonator model steady states
def g(x,z):
return (x * (1 - x) + ((q - x) * f * z) / (q + x)) / eps
def h(x,z):
return x - z
# Initial lists containing values
x = []
z = []
def sys(iv1, iv2, dt, time):
# initial values:
x.append(iv1)
z.append(iv2)
# Compute and fill lists
for i in range(time):
x.append(x[i] + (g(x[i],z[i])) * dt)
z.append(z[i] + (h(x[i],z[i])) * dt)
return x, z
sys(1, 0.5, 0.01, 30)
# Locate and find equilibrium points
eqp = []
def find_fixed_points(r):
for x in range(r):
for z in range(r):
if ((g(x, z) == 0) and (h(x, z) == 0)):
eqp.append((x,z))
return eqp
# Plot nullclines
plt.plot([0,2],[2,0], 'r-', lw=2, label='x-nullcline')
plt.plot([1,1],[0,2], 'b-', lw=2, label='z-nullcline')
# Plot equilibrium points
for point in eqp:
plt.plot(point[0],point[1],"red", marker = "o", markersize = 10.0)
plt.legend(loc='best')
x = np.linspace(0, 2, 20)
z = np.linspace(0, 2, 20)
X1 , Z1 = np.meshgrid(x, z) # Create a grid
DX1, DZ1 = Sys([X1, Z1]) # Compute reaction rate on the grid
M = (np.hypot(DX1, DZ1)) # Norm reaction rate
M[ M == 0] = 1. # Avoid zero division errors
DX1 /= M # Normalise each arrows
DZ1 /= M
plt.quiver(X1, Z1, DX1, DZ1, M, pivot='mid')
plt.xlabel("x(\u03C4)")
plt.ylabel("z(\u03C4)")
plt.legend()
plt.grid()
plt.show()
I'm coding photons orbiting a black hole. When the photons go right into the black hole, the trajectory is really weird due to a division by zero. I'd like to ignore the photons of my array with r < 1.5*rs but I don't know how
I've tried using while True and if, it didn't work
h0=[t0, r2, theta, phi2, pt2, pr2, ptheta, pphi2]
T = np.linspace(0, 1000, 9000)
zz=odeint(func, h0, T, args=(rs,))
r22 = zz[:, 1]
theta22 = zz[:, 2]
phi22 = zz[:, 3]
pt22 = zz[:, 4]
pr22 = zz[:, 5]
pphi22 = zz[:, 7]
def sph2cart(r, phi, theta):
X = r * np.cos(phi) * np.sin(theta)
Y = r * np.sin(phi) * np.sin(theta)
Z = r * np.cos(theta)
return(X, Y, Z)
X2, Y2, Z2 = sph2cart(r22, phi22, theta22)
plt.plot(X2, Y2, Z2, 'g')
I don't think you really need the code to help me, but does anyone know how to plot X2, Y2, Z2 for radius r < 1.5*rs (rs is defined in the code)?
Numpy has built in masks which work quite well
r=np.arange(20)
r_mask = np.ma.masked_where(r < 10 , r)
So, since you want to generate X,Y, and Z values only when r<1.5*rs (i think you said it the opposite way at one point, but you'll just need to flip the sign in any case), your code could look like this
import numpy as np
h0=[t0, r2, theta, phi2, pt2, pr2, ptheta, pphi2]
T = np.linspace(0, 1000, 9000)
zz=odeint(func, h0, T, args=(rs,))
r22 = zz[:, 1]
theta22 = zz[:, 2]
phi22 = zz[:, 3]
pt22 = zz[:, 4]
pr22 = zz[:, 5]
pphi22 = zz[:, 7]
def sph2cart(r, phi, theta):
X = r * np.cos(phi) * np.sin(theta)
Y = r * np.sin(phi) * np.sin(theta)
Z = r * np.cos(theta)
return(X, Y, Z)
r22_masked = np.ma.masked_where(r22 > 1.5*rs , r22)
X2, Y2, Z2 = sph2cart(r22_masked, phi22, theta22)
This would generate X2,Y2, and Z2 values only for the unmasked values of r22 (aka where r22 is less than 1.5*rs
I need to assign value to points in a 3D array that are inside an ellipsoid.
The ellipsoid equation should be something like this:
r=b.sin(u)
x=r.cos(v)
y=r.sin(v)
z=a.cos(u).
But I think that this is only visual. I already tried something with a mask over a cubic array:
a, b = (size-1)/2, (size-1)/2
n = size
r = (size-1)/2
y,x = np.ogrid[-a:n-a, -b:n-b]
mask = x*x + y*y <= r*r # circle mask
array = np.zeros((n, n, n))
array[mask] = 10
But this creates a circle only in x and y which gives me: /.
It's not a sphere. (and I need an ellipsoid).
Any ideas?
mask = x*x + y*y <= r*r gives you a circle, because that's the equation for a circle.
By the same rationale,
mask = x*x + y*y + z*z <= r*r should give you a sphere, and
mask = x*x/(a*a) + y*y/(b*b) + z*z/(c*c) <= r*r should give you an ellipsoid with principal axes of half-length a, b, and c.
Of course, you'll have to create a z array in a way akin to that in which you create your x and y arrays.
For me the easiest way would be to use the coordinate equations for a sphere and work from there.
x = a * cos(u) * cos(v)
y = b * cos(u) * sin(v)
z = c * sin(u)
You can construct these coordinates with np.meshgrid and then plot.
a, b, c = 4, 8, 6
space = np.linspace(0, 2 * np.pi, 50)
u, v = np.meshgrid(space)
x = a * np.cos(u) * np.cos(v)
y = b * np.cos(u) * np.sin(v)
z = c * np.sin(u)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.scatter(x, y, z)
fig.show()
Update
To get the interior coordinates of the sphere you would use the mask akin to your example, but with the ellipsoid implicit equation. x^2/a^2 + y^2/b^2 + z^2/c^2 = 1
a, b, c = 4, 8, 6
xs, ys, zs = np.mgrid[-a + 1:a + 1:15j, -b + 1:b + 1:15j, -c + 1:c + 1:15j]
mask = xs**2/(a**2) + ys**2/(b**2) + zs**2/(c**2) <= 1
xs[~mask] = 0
ys[~mask] = 0
zs[~mask] = 0
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.scatter(xs, ys, zs)
fig.show()
Your first equations hints axis aligned ellipsoid centered at (0,0,0) for such is the easiest way I know of to use scaling to/from sphere. so let:
[x ,y ,z ] - ellipsoid (rx,ry,rz)
[x',y',z'] - sphere (r)
So the transforms are:
// sphere -> ellipsoid
x = x' * rx/r
y = y' * ry/r
z = z' * rz/r
// sphere <- ellipsoid
x' = x * r/rx
y' = y * r/ry
z' = z * r/rz
The (rx,ry,rz) are the radiuses of ellipsoid (in your case rx=ry) and r is any nonzero radius of the sphere (for example r=1.0)
So the test for inside ellipsoid boils down to this:
// scale constants
sx = 1/rx
sy = 1/ry
sz = 1/rz
// condition for inside ellipsoid
x*x*sx*sx + y*y*sy*sy + z*z*sz*sz <= 1.0