How to make 3-D sphere cluster? - python

#I want to generate 3-D sphere cluster which contains number of points in it.
I am using this code but I am unable to generate.#
import math
N = 50
thetavec = np.linspace(0,pi,N)
phivec = np.linspace(0,2*pi,2*N)
[th, ph] = np.meshgrid(thetavec,phivec)
R = np.ones(th.shape)
x = R*numpy.sin(th)*numpy.cos(ph)
y = R*numpy.sin(th)*numpy.sin(ph)
z = R*numpy.cos(th)
ax = plt.axes(projection='3d')
ax.plot_surface(x, y, z, cmap='viridis', edgecolor='none')
ax.set_title('sphere')
I have generated these spherical clusters from matlab but now I want to generate from python:
After that code I am getting points on the sphere but I want the points to be inside the sphere as shown in 1st image means the number of points building a cluster in spherical shape
Help me out

Problem 1: You're using math.sin and math.cos, which expect numbers but you're giving them arrays (th and ph). Use numpy.sin and numpy.cos
Problem 2: I think you have misunderstood how spherical coordinates work. In spherical coordinates, we have R, theta, and phi where R is the _distance of the point from the origin.
When you use R = 1, you get points on a sphere of radius 1. To get points inside the sphere, you have to use a smaller radius than 1. For example, if you want to create 10 concentric spheres inside your sphere of radius 1, define an rvec using linspace and add it to the meshgrid like so:
N = 50
thetavec = np.linspace(0,pi,N)
phivec = np.linspace(0,2*pi,2*N)
rvec = np.linspace(0, 1, 10)
[th, ph, R] = np.meshgrid(thetavec,phivec, rvec)
ax = plt.axes(projection='3d')
ax.plot(x.flatten(), y.flatten(), z.flatten(), '.')
Alternatively, if you don't want concentric circles but just want random points inside the sphere, you can use random numbers using numpy.random.rand. Unlike numpy.ones, numpy.random.rand requires the shape of the array as separate arguments instead of a single tuple, so we unpack the th.shape tuple using *th.shape.
N = 50
max_rad = 1
thetavec = np.linspace(0,pi,N)
phivec = np.linspace(0,2*pi,2*N)
[th, ph] = np.meshgrid(thetavec,phivec)
R = np.random.rand(*th.shape) * max_rad
x = R*np.sin(th)*np.cos(ph)
y = R*np.sin(th)*np.sin(ph)
z = R*np.cos(th)
ax = plt.axes(projection='3d')
ax.plot(x.flatten(), y.flatten(), z.flatten(), '*')

Related

How can I calculate arbitrary values from a spline created with scipy.interpolate.Rbf?

I have several data points in 3 dimensional space (x, y, z) and have interpolated them using scipy.interpolate.Rbf. This gives me a spline nicely representing the surface of my 3D object. I would now like to determine several x and y pairs that have the same, arbitrary z value. I would like to do that in order to compute the cross section of my 3D object at any given value of z. Does someone know how to do that? Maybe there is also a better way to do that instead of using scipy.interpolate.Rbf.
Up to now I have evaluated the cross sections by making a contour plot using matplotlib.pyplot and extracting the displayed segments. 3D points and interpolated spline
segments extracted using a contour plot
I was able to solve the problem. I have calculated the area by triangulating the x-y data and cutting the triangles with the z-plane I wanted to calculate the cross-sectional area of (z=z0). Specifically, I have searched for those triangles whose z-values are both above and below z0. Then I have calculated the x and y values of the sides of these triangles where the sides are equal to z0. Then I use scipy.spatial.ConvexHull to sort the intersected points. Using the shoelace formula I can then determine the area.
I have attached the example code here:
import numpy as np
from scipy import spatial
import matplotlib.pyplot as plt
# Generation of random test data
n = 500
x = np.random.random(n)
y = np.random.random(n)
z = np.exp(-2*(x-.5)**2-4*(y-.5)**2)
z0 = .75
# Triangulation of the test data
triang= spatial.Delaunay(np.array([x, y]).T)
# Determine all triangles where not all points are above or below z0, i.e. the triangles that intersect z0
tri_inter=np.zeros_like(triang.simplices, dtype=np.int) # The triangles which intersect the plane at z0, filled below
i = 0
for tri in triang.simplices:
if ~np.all(z[tri] > z0) and ~np.all(z[tri] < z0):
tri_inter[i,:] = tri
i += 1
tri_inter = tri_inter[~np.all(tri_inter==0, axis=1)] # Remove all rows with only 0
# The number of interpolated values for x and y has twice the length of the triangles
# Because each triangle intersects the plane at z0 twice
x_inter = np.zeros(tri_inter.shape[0]*2)
y_inter = np.zeros(tri_inter.shape[0]*2)
for j, tri in enumerate(tri_inter):
# Determine which of the three points are above and which are below z0
points_above = []
points_below = []
for i in tri:
if z[i] > z0:
points_above.append(i)
else:
points_below.append(i)
# Calculate the intersections and put the values into x_inter and y_inter
t = (z0-z[points_below[0]])/(z[points_above[0]]-z[points_below[0]])
x_new = t * (x[points_above[0]]-x[points_below[0]]) + x[points_below[0]]
y_new = t * (y[points_above[0]]-y[points_below[0]]) + y[points_below[0]]
x_inter[j*2] = x_new
y_inter[j*2] = y_new
if len(points_above) > len(points_below):
t = (z0-z[points_below[0]])/(z[points_above[1]]-z[points_below[0]])
x_new = t * (x[points_above[1]]-x[points_below[0]]) + x[points_below[0]]
y_new = t * (y[points_above[1]]-y[points_below[0]]) + y[points_below[0]]
else:
t = (z0-z[points_below[1]])/(z[points_above[0]]-z[points_below[1]])
x_new = t * (x[points_above[0]]-x[points_below[1]]) + x[points_below[1]]
y_new = t * (y[points_above[0]]-y[points_below[1]]) + y[points_below[1]]
x_inter[j*2+1] = x_new
y_inter[j*2+1] = y_new
# sort points to calculate area
hull = spatial.ConvexHull(np.array([x_inter, y_inter]).T)
x_hull, y_hull = x_inter[hull.vertices], y_inter[hull.vertices]
# Calculation of are using the shoelace formula
area = 0.5*np.abs(np.dot(x_hull,np.roll(y_hull,1))-np.dot(y_hull,np.roll(x_hull,1)))
print('Area:', area)
plt.figure()
plt.plot(x_inter, y_inter, 'ro')
plt.plot(x_hull, y_hull, 'b--')
plt.triplot(x, y, triangles=tri_inter, color='k')
plt.show()

Fitting a closed curve to a set of discrete points and finding it's perimeter

I have a set of points pts which form a loop which looks like this:
Closed curve
The code I used for the same is as follows:
pts=np.array( [ [1145,1130],
[1099.5,1056.5],
[1026,1062],
[950.3,1054.3],
[909,1130],
[940.4,1215.6],
[1026,1264],
[1111.6,1215.6]
])
pts = np.vstack([pts, pts[0]])
#pts = np.array([...]) # Your points
x, y = pts.T
i = np.arange(len(pts))
# 5x the original number of points
interp_i = np.linspace(0, i.max(), 5 * i.max())
xi = interp1d(i, x, kind='cubic')(interp_i)
yi = interp1d(i, y, kind='cubic')(interp_i)
fig, ax = plt.subplots()
ax.plot(xi, yi)
ax.plot(x, y, 'ko')
plt.show()
I want to know how to find the length of this curve/ the perimeter of this figure. Is there any way to do it?
You can approach the perimeter of the curve to a sum of line segments.
If (xj,yj) and (xj+1,yj+1) are coordinates of start and end points of a line segment lying on the xy plane, then its length can be written as:
L_j = sqrt{[x_(j+1) - x_(j)]^2 + [y_(j+1) - y_(j)]^2}
So you can just sum over all L_j segments to get the approximate value for the perimeter of a closed curve.
A python code example to do it is:
L = np.sqrt((xi[-1] - xi[0])**2 + (yi[-1] - yi[0])**2) # let the initial value of L be the length of the line segment between the last and the first points
for j in range(0,len(xi)):
L = L + np.sqrt((xi[j+1] - xi[j])**2 + (yi[j+1] - yi[j])**2)
print L

Symmetric streamplot with matplotlib

I'm trying to plot the streamlines of a magnetic field around a sphere using matplotlib, and it does work quite nicely. However, the resulting image is not symmetric, but it should be (I think).
This is the code used to generate the image. Excuse the length, but I thought it would be better than just posting a non-working snippet. Also, it's not very pythonic; that's because I converted it from Matlab, which was easier than I expected.
from __future__ import division
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Circle
def cart2spherical(x, y, z):
r = np.sqrt(x**2 + y**2 + z**2)
phi = np.arctan2(y, x)
theta = np.arccos(z/r)
if r == 0:
theta = 0
return (r, theta, phi)
def S(theta, phi):
S = np.array([[np.sin(theta)*np.cos(phi), np.cos(theta)*np.cos(phi), -np.sin(phi)],
[np.sin(theta)*np.sin(phi), np.cos(theta)*np.sin(phi), np.cos(phi)],
[np.cos(theta), -np.sin(theta), 0]])
return S
def computeB(r, theta, phi, a=1, muR=100, B0=1):
delta = (muR - 1)/(muR + 2)
if r > a:
Bspherical = B0*np.array([np.cos(theta) * (1 + 2*delta*a**3 / r**3),
np.sin(theta) * (delta*a**3 / r**3 - 1),
0])
B = np.dot(S(theta, phi), Bspherical)
else:
B = 3*B0*(muR / (muR + 2)) * np.array([0, 0, 1])
return B
Z, X = np.mgrid[-2.5:2.5:1000j, -2.5:2.5:1000j]
Bx = np.zeros(np.shape(X))
Bz = np.zeros(np.shape(X))
Babs = np.zeros(np.shape(X))
for i in range(len(X)):
for j in range(len(Z)):
r, theta, phi = cart2spherical(X[0, i], 0, Z[j, 0])
B = computeB(r, theta, phi)
Bx[i, j], Bz[i, j] = B[0], B[2]
Babs[i, j] = np.sqrt(B[0]**2 + B[1]**2 + B[2]**2)
fig=plt.figure()
ax=fig.add_subplot(111)
plt.streamplot(X, Z, Bx, Bz, color='k', linewidth=0.8*Babs, density=1.3,
minlength=0.9, arrowstyle='-')
ax.add_patch(Circle((0, 0), radius=1, facecolor='none', linewidth=2))
plt.axis('equal')
plt.axis('off')
fig.savefig('streamlines.pdf', transparent=True, bbox_inches='tight', pad_inches=0)
First of all, for curiosity, why would you want to plot symmetric data? Why plotting half of isn't fine?
Said that, this is a possible hack. You can use mask arrays as Hooked suggested to plot half of it:
mask = X>0
BX_OUT = Bx.copy()
BZ_OUT = Bz.copy()
BX_OUT[mask] = None
BZ_OUT[mask] = None
res = plt.streamplot(X, Z, BX_OUT, BZ_OUT, color='k',
arrowstyle='-',linewidth=1,density=2)
then you save in res the result from streamplot, extract the lines and plot them with the opposite X coordinate.
lines = res.lines.get_paths()
for l in lines:
plot(-l.vertices.T[0],l.vertices.T[1],'k')
I used this hack to extract streamlines and arrows from a 2D plot, then apply a 3D transformation and plot it with mplot3d. A picture is in one of my questions here.
Quoting from the documentation:
density : float or 2-tuple
Controls the closeness of streamlines. When density = 1,
the domain is divided into
a 25x25 grid—density linearly scales this grid.
Each cell in the grid can have, at most, one traversing streamline.
For different densities in each direction, use [density_x, density_y].
so you are getting aliasing effects between the cells it uses to decide where the stream lines are, and the symmetries of your problem. You need to carefully choose your grid size (of the data) and the density.
It is also sensitive to where the box boundaries are relative to the top of the sphere. Is the center of your sphere on a data grid point or between the data grid points? If it is on a grid point then the box that contains the center point will be different than the boxes adjacent to it.
I am not familiar with exactly how it decides which stream lines to draw, but I could imagine that it is some sort of greedy algorithm and hence will give different results walking towards the high density region and away density region.
To be clear, you issue is not that the stream lines are wrong, they are valid stream lines, it is that you find the result not aesthetically pleasing.
Use a mask to separate the two regions of interest:
mask = np.sqrt(X**2+Z**2)<1
BX_OUT = Bx.copy()
BZ_OUT = Bz.copy()
BX_OUT[mask] = None
BZ_OUT[mask] = None
plt.streamplot(X, Z, BX_OUT, BZ_OUT, color='k',
arrowstyle='-', density=2)
BX_IN = Bx.copy()
BZ_IN = Bz.copy()
BX_IN[~mask] = None
BZ_IN[~mask] = None
plt.streamplot(X, Z, BX_IN, BZ_IN, color='r',
arrowstyle='-', density=2)
The resulting plot isn't exactly symmetric, but by giving the algorithm a hint, it's far closer than what you had before. Play with the density of the grid via meshgrid and the density parameter to achieve the effect you are looking for.
Use physics, instead... The magnetic field is symmetrical with respect to the z (vertical) axis! So you just need two streamplot's:
plt.streamplot(X, Z, Bx, Bz, color='k', linewidth=0.8*Babs, density=1.3, minlength=0.9, arrowstyle='-')
plt.streamplot(-X, Z, -Bx, Bz, color='k', linewidth=0.8*Babs, density=1.3, minlength=0.9, arrowstyle='-')

Mayavi: interpolate face colors in triangular_mesh

I have pieced together the
following code to plot a triangular mesh with the colors specified by an
additional scalar function:
#! /usr/bin/env python
import numpy as np
from mayavi import mlab
# Create cone
n = 8
t = np.linspace(-np.pi, np.pi, n)
z = np.exp(1j*t)
x = z.real.copy()
y = z.imag.copy()
z = np.zeros_like(x)
triangles = [(0, i, i+1) for i in range(n)]
x = np.r_[0, x]
y = np.r_[0, y]
z = np.r_[1, z]
t = np.r_[0, t]
# These are the scalar values for each triangle
f = np.mean(t[np.array(triangles)], axis=1)
# Plot it
mesh = mlab.triangular_mesh(x, y, z, triangles,
representation='wireframe',
opacity=0)
cell_data = mesh.mlab_source.dataset.cell_data
cell_data.scalars = f
cell_data.scalars.name = 'Cell data'
cell_data.update()
mesh2 = mlab.pipeline.set_active_attribute(mesh,
cell_scalars='Cell data')
mlab.pipeline.surface(mesh2)
mlab.show()
This works reasonably well. However, instead of having every triangle
with a uniform color and sharp transitions between the triangles, I'd
much rather have a smooth interpolation over the entire surface.
Is there a way to do that?
I think you want to use point data instead of cell data. With cell data, a single scalar value is not localized to any point. It is assigned to the entire face. It looks like you just want to assign the t data to the vertices instead. The default rendering of point scalars will smoothly interpolate across each face.
point_data = mesh.mlab_source.dataset.point_data
point_data.scalars = t
point_data.scalars.name = 'Point data'
point_data.update()
mesh2 = mlab.pipeline.set_active_attribute(mesh,
point_scalars='Point data')

creating a 3D Cone or disk and keep updating its axis of symmetry with matplotlib

I mean the cone or disk is moving or rotating with its axis of symmetry. To be exact, I am creating this axis, which is constantly changing with time:
line = ax.plot([x,0],[y,0],[z,z- n_o],color='#000066', marker= 'o')
I need the face of the cone or circle always perpendicular to that axis. I tried simpler one first by creating a 2D circle then lift it up to the position I want:
circle = Circle((0, 0), .3, color='r')
ax.add_patch(circle)
art3d.pathpatch_2d_to_3d(circle, z=1)
but that won't make the face of the circle perpendicular to the moving axis. I wonder is there any function in matplotlib I can use to rotate that face of the cone/circle?
If, I started from another way by creating a 3D object, like an ellipsoid, the problem remains: how do I let the object moving with its axis of symmetry like a rigid body(stick with its axis) rather than a lantern hanging there(attached to a fixed point only)?
u, v = np.mgrid[0:2*np.pi:20j, 0:np.pi:10j]
x=np.cos(u)*np.sin(v)
y=np.sin(u)*np.sin(v)
z=.3*np.cos(v)
ax.plot_wireframe(x, y, z, color="r")
from mpl_toolkits.mplot3d import Axes3D
def euler_rot(XYZ,phi,theta,psi):
'''Returns the points XYZ rotated by the given euler angles'''
ERot = np.array([[np.cos(theta)*np.cos(psi),
-np.cos(phi)*np.sin(psi) + np.sin(phi)*np.sin(theta)*np.cos(psi),
np.sin(phi)*np.sin(psi) + np.cos(phi)*np.sin(theta)*np.cos(psi)],
[np.cos(theta)*np.sin(psi),
np.cos(phi)*np.cos(psi) + np.sin(phi)*np.sin(theta)*np.sin(psi),
-np.sin(phi)*np.cos(psi) + np.cos(phi)*np.sin(theta)*np.sin(psi)],
[-np.sin(theta),
np.sin(phi)*np.cos(theta),
np.cos(phi)*np.cos(theta)]])
return ERot.dot(XYZ)
u = np.linspace(0,2*np.pi,50)
num_levels = 10
r0 = 1 # maximum radius of cone
h0 = 5 # height of cone
phi = .5 # aka alpha
theta = .25 # aka beta
psi = 0 # aka gamma
norm = np.array([0,0,h0]).reshape(3,1)
normp = euler_rot(norm,phi,theta,psi)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot([0,normp[0]],[0,normp[1]],zs= [0,normp[2]])
x = np.hstack([r0*(1-h)*np.cos(u) for h in linspace(0,1,num_levels)])
y = np.hstack([r0*(1-h)*np.sin(u) for h in linspace(0,1,num_levels)])
z = np.hstack([np.ones(len(u))*h*h0 for h in linspace(0,1,num_levels)])
XYZ = np.vstack([x,y,z])
xp,yp,zp = euler_rot(XYZ,phi,theta,psi)
ax.plot_wireframe(xp,yp,zp)
This will draw a cone around the line pointing along the direction of the z-axis after rotation through the Euler angles phi,theta,psi. (in this case psi will have no effect as the cone is axi-symmetric around the z-axis) Also see rotation matrix.
To draw a single circle shifted along the normal by h0:
x=r0*np.cos(u)
y=r0*np.sin(u)
z=h0*np.ones(len(x))
XYZ = np.vstack([x,y,z])
xp,yp,zp = euler_rot(XYZ,phi,theta,psi)
ax.plot(xp,yp,zs=zp)
It is left as an exercise to get the euler angles from a given vector.
euler_rot in a gist

Categories

Resources