I have an irregular 3d object and want to know the surface of this object. The object can be both convex or non convex type. I can get the surface of this object applying any method like marching cube, surface contour, or isosurface.
All this methods give me triangulated mesh which is basically contains edges and vertex.
My task is to generate random and lattice points inside the object.
How should i check whether my point is inside or outside?
Any suggestion?
Thanks a lot.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
from skimage import measure, io
from skimage.draw import ellipsoid
import skimage as sk
import random
I=np.zeros((50,50,50),dtype=np.float)
for i in range(50):
for j in range(50):
for k in range(50):
dist=np.linalg.norm([i,j,k]-O)
if dist<8:
I[i,j,k]=0.8#random.random()
dist=np.linalg.norm([i,j,k]-O2)
if dist<16:
I[i,j,k]=1#random.random()
verts, faces, normals, values = measure.marching_cubes_lewiner(I,0.7)
fig = plt.figure(figsize=(10, 10))
ax = fig.add_subplot(111, projection='3d')
mesh = Poly3DCollection(verts[faces])
mesh.set_edgecolor('k')
ax.add_collection3d(mesh)
plt.show()
%now forget the above code and suppose i have only verts and
%faces information. Now how to generate random points inside this Data
Data=verts[faces]
???????
For random points inside the closed shape:
Select linear density of samples
Make bounding box enclosing the shape
Select entry point on the box
Select exit point, compute direction cosines (wx, wy, wz). Find all segments inside the shape along the ray
Start the ray from entry point
Get to first segment and and set it to pstart
Sample length s from exponential distribution with selected linear density
Find point pend = pstart + s (wx, wy, wz)
If it is in the first segment, store it, and make pstart = pend. Go to step 7.
If it is not, go to the start of another segment, and set it to pstart. Go to step 7. If there is no segment left, you're done with one ray, go to step 3 and generate another ray.
Generate some predefined number of rays, collect all stored points, and you're done
I am sharing the code which I have written. It might be useful for others if anybody is interested for similar kind of problem. This is not the optimize code. As grid spacing value decrease computation time increase. Also depends upon the number of triangle of mesh. Any suggestion for optimizing or improve the code is welcome. Thanks
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
import numpy as np
#from mayavi import mlab
verts # numpy array of vertex (triangulated mesh)
faces # numpy array of faces (triangulated mesh)
%This function is taken from here
%https://www.erikrotteveel.com/python/three-dimensional-ray-tracing-in-python/
def ray_intersect_triangle(p0, p1, triangle):
# Tests if a ray starting at point p0, in the direction
# p1 - p0, will intersect with the triangle.
#
# arguments:
# p0, p1: numpy.ndarray, both with shape (3,) for x, y, z.
# triangle: numpy.ndarray, shaped (3,3), with each row
# representing a vertex and three columns for x, y, z.
#
# returns:
# 0.0 if ray does not intersect triangle,
# 1.0 if it will intersect the triangle,
# 2.0 if starting point lies in the triangle.
v0, v1, v2 = triangle
u = v1 - v0
v = v2 - v0
normal = np.cross(u, v)
b = np.inner(normal, p1 - p0)
a = np.inner(normal, v0 - p0)
# Here is the main difference with the code in the link.
# Instead of returning if the ray is in the plane of the
# triangle, we set rI, the parameter at which the ray
# intersects the plane of the triangle, to zero so that
# we can later check if the starting point of the ray
# lies on the triangle. This is important for checking
# if a point is inside a polygon or not.
if (b == 0.0):
# ray is parallel to the plane
if a != 0.0:
# ray is outside but parallel to the plane
return 0
else:
# ray is parallel and lies in the plane
rI = 0.0
else:
rI = a / b
if rI < 0.0:
return 0
w = p0 + rI * (p1 - p0) - v0
denom = np.inner(u, v) * np.inner(u, v) - \
np.inner(u, u) * np.inner(v, v)
si = (np.inner(u, v) * np.inner(w, v) - \
np.inner(v, v) * np.inner(w, u)) / denom
if (si < 0.0) | (si > 1.0):
return 0
ti = (np.inner(u, v) * np.inner(w, u) - \
np.inner(u, u) * np.inner(w, v)) / denom
if (ti < 0.0) | (si + ti > 1.0):
return 0
if (rI == 0.0):
# point 0 lies ON the triangle. If checking for
# point inside polygon, return 2 so that the loop
# over triangles can stop, because it is on the
# polygon, thus inside.
return 2
return 1
def bounding_box_of_mesh(triangle):
return [np.min(triangle[:,0]), np.max(triangle[:,0]), np.min(triangle[:,1]), np.max(triangle[:,1]), np.min(triangle[:,2]), np.max(triangle[:,2])]
def boundingboxoftriangle(triangle,x,y,z):
localbox= [np.min(triangle[:,0]), np.max(triangle[:,0]), np.min(triangle[:,1]), np.max(triangle[:,1]), np.min(triangle[:,2]), np.max(triangle[:,2])]
#print 'local', localbox
for i in range(1,len(x)):
if (x[i-1] <= localbox[0] < x[i]):
x_min=i-1
if (x[i-1] < localbox[1] <= x[i]):
x_max=i
for i in range(1,len(y)):
if (y[i-1] <= localbox[2] < y[i]):
y_min=i-1
if (y[i-1] < localbox[3] <= y[i]):
y_max=i
for i in range(1,len(z)):
if (z[i-1] <= localbox[4] < z[i]):
z_min=i-1
if (z[i-1] < localbox[5] <= z[i]):
z_max=i
return [x_min, x_max, y_min, y_max, z_min, z_max]
spacing=5 # grid spacing
boundary=bounding_box_of_mesh(verts)
print boundary
x=np.arange(boundary[0]-2*spacing,boundary[1]+2*spacing,spacing)
y=np.arange(boundary[2]-2*spacing,boundary[3]+2*spacing,spacing)
z=np.arange(boundary[4]-2*spacing,boundary[5]+2*spacing,spacing)
Grid=np.zeros((len(x),len(y),len(z)),dtype=np.int)
print Grid.shape
data=verts[faces]
xarr=[]
yarr=[]
zarr=[]
# actual number of grid is very high so checking every grid is
# inside or outside is inefficient. So, I am looking for only
# those grid which is near to mesh boundary. This will reduce
#the time and later on internal grid can be interpolate easily.
for i in range(len(data)):
#print '\n', data[i]
AABB=boundingboxoftriangle(data[i],x,y,z) ## axis aligned bounding box
#print AABB
for gx in range(AABB[0],AABB[1]+1):
if gx not in xarr:
xarr.append(gx)
for gy in range(AABB[2],AABB[3]+1):
if gy not in yarr:
yarr.append(gy)
for gz in range(AABB[4],AABB[5]+1):
if gz not in zarr:
zarr.append(gz)
print len(xarr),len(yarr),len(zarr)
center=np.array([np.mean(verts[:,0]), np.mean(verts[:,1]), np.mean(verts[:,2])])
print center
fw=open('Grid_value_output_spacing__.dat','w')
p1=center #np.array([0,0,0])
for i in range(len(xarr)):
for j in range(len(yarr)):
for k in range(len(zarr)):
p0=np.array([x[xarr[i]],y[yarr[j]],z[zarr[k]]])
for go in range(len(data)):
value=ray_intersect_triangle(p0, p1, data[go])
if value>0:
Grid[i,j,k]=value
break
fw.write(str(xarr[i])+'\t'+str(yarr[j])+'\t'+str(zarr[k])+'\t'+str(x[xarr[i]])+'\t'+str(y[yarr[j]])+'\t'+str(z[zarr[k]])+'\t'+str(Grid[i,j,k])+'\n')
print i
fw.close()
#If the grid value is greater than 0 then it is inside the triangulated mesh.
#I am writing the value of only confusing grid near boundary.
#Deeper inside grid of mesh can be interpolate easily with above information.
#If grid spacing is very small then generating random points inside the
#mesh is equivalent to choosing the random grid.
Related
Problem
I am trying to implement Edelsbrunner's Algorithm for the alpha shape of a 3D point cloud in python as presented in this SO post. However, I'm having trouble plotting results. Half my sphere looks good, and the other half garbled.
I suspect it may have something to do with the fact that I have negative coordinates, but I'm not sure.
Definitions
I'm adding these so programmers can contribute without being bogged down by math. These are simplified definitions and not meant to be precise (Feel free to skip this part; for more, see see Introduction to Alpha Shapes and Discrete Differential Geometry):
Delaunay triangulation: for a 2D point set, a tesselation of the points into triangles (i.e. "triangulation") where the circumscribed circle (i.e. "circumcircle") about every triangle contains no other points in the set. For 3D points, replace "triangle" with "tetrahedron" and "circumcircle" with "circumsphere".
Affinely independent: a collection of points p0, ..., pk such that all vectors vi := pi-p0 are linearly independent (i.e. in 2D not collinear, in 3D not coplanar); also called "points in general position"
k-simplex: the convex hull of k+1 affinely-independent points; we call the points its vertices.
0-simplex = point (consists of 0+1 = 1 points)
1-simplex = line (consists of 1+1 = 2 points)
2-simplex = triangle (consists of 2+1 = 3 points)
3-simplex = tetrahedron (consists of 3+1 = 4 points)
face: any simplex whose vertices are a subset of the vertices of another simplex; i.e. "a part of a simplex"
(geometric) simplicial complex: a collection of simplices where (1) the intersection of two simplices is a simplex, and (2) every face of a simplex is in the complex; i.e. "a bunch of simplices"
alpha-exposed: a simplex within a point set where the circle (2D) or ball (3D) of radius alpha through its vertices doesn't contain any other point in the point set
alpha shape: the boundary of all alpha-exposed simplices of a point set
Algorithm
Edelsbrunner's Algorithm is as follows:
Given a point cloud pts:
Compute the Delaunay triangulation DT of the point cloud
Find the alpha-complex: search all simplices in the Delaunay triangulation and (a) if any ball around a simplex is empty and has
a radius less than alpha (called the "alpha test"), then add it to the
alpha complex
The boundary of the alpha complex is the alpha shape
Code
from scipy.spatial import Delaunay
import numpy as np
from collections import defaultdict
from matplotlib import pyplot as plt
import pyvista as pv
fig = plt.figure()
ax = plt.axes(projection="3d")
plotter = pv.Plotter()
def alpha_shape_3D(pos, alpha):
"""
Compute the alpha shape (concave hull) of a set of 3D points.
Parameters:
pos - np.array of shape (n,3) points.
alpha - alpha value.
return
outer surface vertex indices, edge indices, and triangle indices
"""
tetra = Delaunay(pos)
# Find radius of the circumsphere.
# By definition, radius of the sphere fitting inside the tetrahedral needs
# to be smaller than alpha value
# http://mathworld.wolfram.com/Circumsphere.html
tetrapos = np.take(pos,tetra.vertices,axis=0)
normsq = np.sum(tetrapos**2,axis=2)[:,:,None]
ones = np.ones((tetrapos.shape[0],tetrapos.shape[1],1))
a = np.linalg.det(np.concatenate((tetrapos,ones),axis=2))
Dx = np.linalg.det(np.concatenate((normsq,tetrapos[:,:,[1,2]],ones),axis=2))
Dy = -np.linalg.det(np.concatenate((normsq,tetrapos[:,:,[0,2]],ones),axis=2))
Dz = np.linalg.det(np.concatenate((normsq,tetrapos[:,:,[0,1]],ones),axis=2))
c = np.linalg.det(np.concatenate((normsq,tetrapos),axis=2))
r = np.sqrt(Dx**2+Dy**2+Dz**2-4*a*c)/(2*np.abs(a))
# Find tetrahedrals
tetras = tetra.vertices[r<alpha,:]
# triangles
TriComb = np.array([(0, 1, 2), (0, 1, 3), (0, 2, 3), (1, 2, 3)])
Triangles = tetras[:,TriComb].reshape(-1,3)
Triangles = np.sort(Triangles,axis=1)
# Remove triangles that occurs twice, because they are within shapes
TrianglesDict = defaultdict(int)
for tri in Triangles:TrianglesDict[tuple(tri)] += 1
Triangles=np.array([tri for tri in TrianglesDict if TrianglesDict[tri] ==1])
#edges
EdgeComb=np.array([(0, 1), (0, 2), (1, 2)])
Edges=Triangles[:,EdgeComb].reshape(-1,2)
Edges=np.sort(Edges,axis=1)
Edges=np.unique(Edges,axis=0)
Vertices = np.unique(Edges)
return Vertices,Edges,Triangles
def ptcloud_sphere():
r = 3
phi = np.linspace(0, np.pi, 18)
theta = np.linspace(0, 2 * np.pi, 36)
PHI, THETA = np.meshgrid(phi, theta)
x = r * np.sin(PHI) * np.cos(THETA)
y = r * np.sin(PHI) * np.sin(THETA)
z = r * np.cos(PHI)
ax.scatter(x, y, z)
plt.show()
pts = np.stack((x.ravel(), y.ravel(), z.ravel()), axis=1)
return np.unique(pts, axis=0)
if __name__ == "__main__":
pts = ptcloud_sphere()
verts, edges, faces = alpha_shape_3D(pts, alpha=10)
faces_conn_list = np.insert(faces, 0, 3, axis=1)
num_faces = faces.shape[0]
mesh = pv.PolyData(pts[verts], faces_conn_list, n_faces=num_faces)
plotter.add_mesh(mesh, reset_camera=True)
plotter.show()
Output
Point Cloud:
Alpha Shape:
Update 09-SEP-2021
Per #akaszynski, the problem does indeed appear to be a combination of unique and negative points. He fixed this issue with the following:
pts = np.stack((x.ravel(), y.ravel(), z.ravel()), axis=1) + 10
return np.unique(pts, axis=0) - 10
However, if someone could perform a deeper investigation to determine why this is the problem, that would help.
Update 09-SEP-2021 #2
Per #AndrasDeak, both pyvista and VTK support the creation of 2d and 3d alpha shapes. The pyvista function delaunay_3d uses vtkDelaunay3D under the hood, which accepts an alpha parameter.
(See vtkDelaunay3D docs)
I need to integrate over the arcs that are resulted from the intersection of a circle with a rectangle and fall inside the rectangle. I can find the intersection points using the shapely package. However, I don't know how to obtain integration intervals. For example, in the below figure my code returns [-2.1562, 2.1562] in radians (with respect to the center of the circle), while it should be able to automatically understand that the integration intervals that falls inside the rectangle are [[2.1562, 3.1415],[-3.1415, -2.1562]] (assuming pi = 3.1415).
Here is another example:
My code returns [-0.45036, -0.29576, 0.29576, 0.45036] and the expected intervals will be [[0.29576, 0.45036], [-0.45036, -0.29576]].
The code should also work for any other location that the circle is located (with any radius), whether its center is outside or inside the rectangle.
Here is my code, written using iPython:
import matplotlib.pyplot as plt
import math
import numpy as np
from shapely.geometry import LineString, MultiPoint
from shapely.geometry import Polygon
from shapely.geometry import Point
# Utilities
def cart2pol(xy, center):
x,y = xy
x_0,y_0 = center
rho = np.sqrt((x-x_0)**2 + (y-y_0)**2)
phi = np.arctan2(y-y_0, x-x_0)
return(rho, phi)
def pol2cart(rho, phi, center):
x_0,y_0 = center
x = rho * np.cos(phi)+x_0
y = rho * np.sin(phi)+y_0
return(x, y)
def distance(A,B):
return math.sqrt((A[0]-B[0])**2+(A[1]-B[1])**2)
#######################
rad = 6
center = (-1,5)
p = Point(center)
c = p.buffer(rad).boundary
A = (10,0)
B = (0,0)
C = (0,10)
D = (10,10)
coords = [Point(A), Point(B), Point(C), Point(D)]
poly = MultiPoint(coords).convex_hull
i=c.intersection(poly)
lines = [LineString([A, D]), LineString([D, C]),
LineString([C, B]), LineString([B, A])]
points = []
for l in lines:
i = c.intersection(l)
if not i.is_empty:
if i.geom_type == 'MultiPoint':
for j in range(len(i.geoms)):
points.append(i.geoms[j].coords[0])
else:
points.append(i.coords[0])
# Repeat the tangential points
for k, point in enumerate(points.copy()):
if abs(distance(center, point)**2 + distance(point, B)**2 - distance(B, center)**2) < 1e-4:
points.insert(k+1,point)
elif abs(distance(center, point)**2 + distance(point, D)**2 -distance(D, center)**2) < 1e-4:
points.insert(k+1,point)
# Sort points in polar coordinates
phis = [cart2pol(point,center)[1] for point in points]
phis.sort()
print(phis)
# Plot the shapes
x,y = c.xy
plt.plot(*c.xy)
for l in lines:
plt.plot(*l.xy, 'b')
plt.gca().set_aspect('equal', adjustable='box')
I tried to sort the intersection points according to their angle in a way that each two adjacent items in the list of intersection points corresponds to an arc. The problem is that there will be a jump in the angles from -pi to pi when rotating along the unit circle. Also I don't know how to find that whether an arc is inside the rectangle or not given its 2 end points.
Dealing with angle ranges is not straightforward.
1) select a non-ambiguous representation range, such as [-π, π) radians.
2) write a function that finds the intersections of the circle with a (h/v) half-plane and returns an angle interval. It the interval straddles the ±π border, split it in two.
3) write a function that finds the intersection between two lists of intervals (this is a modified merging problem).
4) process the four edges and intersect the resulting intervals.
5) possibly merge intervals that straddle the ±π border.
I have been writing a Python script (GitHub LINK) for visualizing asteroid/comet/meteoroid orbits. The script also plots the position of planets and their orbits.
It works just right for orbits with small semi-major axis (i.e. "smaller" orbits). But when I have an orbit that goes way beyond Neptune (e.g. of a Halley-type comet), and from certain perspectives, there is a weird "wraparound" (for lack of a better word) effect.
Let me show you what I mean:
Image compilation: http://i.imgur.com/onSZG8s.png
This image shows the plot from a perspective where it does not break.
When you rotate the same plot a bit to the right, it is as if the orbit folded in half and reversed its direction!
And if you look at the plot from a great distance, you can see that the elipse is plotted as it should be.
And here is a minimal version of the code with which the issue can be reproduced. The "wraparound" occurs only when the perspective of the camera is closely parallel with the large orbit.
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt
def orbitalElements2Cartesian(a, e, I, peri, node, E):
""" Convert orbital elements to Cartesian coordinates in the Solar System.
Args:
a (float): semi-major axis (AU)
e (float): eccentricity
I (float): inclination (degrees)
peri (float): longitude of perihelion (degrees)
node (float): longitude of ascending node (degrees)
E (float): eccentric anomaly (radians)
"""
# The source of equations used:
# http://farside.ph.utexas.edu/teaching/celestial/Celestialhtml/node34.html
# Check if the orbit is parabolic or hyperbolic
if e >=1:
e = 0.99999999
# Convert degrees to radians
I, peri, node = map(np.radians, [I, peri, node])
# True anomaly
theta = 2*np.arctan(np.sqrt((1.0 + e)/(1.0 - e))*np.tan(E/2.0))
# Distance from the Sun to the poin on orbit
r = a*(1.0 - e*np.cos(E))
# Cartesian coordinates
x = r*(np.cos(node)*np.cos(peri + theta) - np.sin(node)*np.sin(peri + theta)*np.cos(I))
y = r*(np.sin(node)*np.cos(peri + theta) + np.cos(node)*np.sin(peri + theta)*np.cos(I))
z = r*np.sin(peri + theta)*np.sin(I)
return x, y, z
if __name__ == '__main__':
# Example orbital elements
# a, e, incl, peri, node
orb_elements = np.array([
[2.363, 0.515, 4.0, 205.0, 346.1],
[0.989, 0.089, 3.1, 55.6, 21.2],
[0.898, 0.460, 1.3, 77.1, 331.2],
[104.585332285, 0.994914, 89.3950, 130.8767, 282.4633]
])
# Setup the plot
fig = plt.figure()
ax = fig.gca(projection='3d')
# Eccentric anomaly (full range)
E = np.linspace(-np.pi, np.pi, 100)
# Plot the given orbits
for i, orbit in enumerate(orb_elements):
a, e, I, peri, node = orbit
# Take extra steps in E if the orbit is very large
if a > 50:
E = np.linspace(-np.pi, np.pi, (a/20.0)*100)
# Get the orbit in the cartesian space
x, y, z = orbitalElements2Cartesian(a, e, I, peri, node, E)
# Plot orbits
ax.plot(x, y, z, c='#32CD32')
# Add limits (in AU)
ax.set_xlim3d(-5,5)
ax.set_ylim3d(-5,5)
ax.set_zlim3d(-5,5)
plt.tight_layout()
plt.show()
I am a bit dumbfounded by this and cannot seem to find a proper solution. I would greatly appreciate some help!
matplotlib isn't great for complex 3D plots in my experience (I've had similar strange behaviour with out of axis values). Something like mayavi could be worth considering as it's designed for 3D plots...
A possible workaround is given in this blog, basically just set out of axis values to np.NaN for your required axis. If I add the following to your example,
for r in [x,y,z]:
for i in np.arange(len(r)):
if r[i] < -5:
x[i] = np.NaN
y[i] = np.NaN
z[i] = np.NaN
elif r[i] > 5:
x[i] = np.NaN
y[i] = np.NaN
z[i] = np.NaN
else:
pass
it removes the wraparound.
I had similar issues and wanted to make something a bit more user friendly. I moved all of the functions in this library over to javascript and created a webGL interface in Three.js which lets you do what you want here but also plots the location of the asteroid / comet with animation via time functions. Just need a web browser to use it. Check it out :)
http://rankinstudio.com/asteroids/asteroids.html
I am trying to find pairs of (x,y) points within a maximum distance of each other. I thought the simplest thing to do would be to generate a DataFrame and go through each point, one by one, calculating if there are points with coordinates (x,y) within distance r of the given point (x_0, y_0). Then, divide the total number of discovered pairs by 2.
%pylab inline
import pandas as pd
def find_nbrs(low, high, num, max_d):
x = random.uniform(low, high, num)
y = random.uniform(low, high, num)
points = pd.DataFrame({'x':x, 'y':y})
tot_nbrs = 0
for i in arange(len(points)):
x_0 = points.x[i]
y_0 = points.y[i]
pt_nbrz = points[((x_0 - points.x)**2 + (y_0 - points.y)**2) < max_d**2]
tot_nbrs += len(pt_nbrz)
plot (pt_nbrz.x, pt_nbrz.y, 'r-')
plot (points.x, points.y, 'b.')
return tot_nbrs
print find_nbrs(0, 1, 50, 0.1)
First of all, it's not always finding the right pairs (I see points that are within the stated distance that are not labeled).
If I write plot(..., 'or'), it highlights all the points. Which means that pt_nbrz = points[((x_0 - points.x)**2 + (y_0 - points.y)**2) < max_d**2] returns at least one (x,y). Why? Shouldn't it return an empty array if the comparison is False?
How do I do all of the above more elegantly in Pandas? For example, without having to loop through each element.
The functionality you're looking for is included in scipy's spatial distance module.
Here's an example of how you could use it. The real magic is in squareform(pdist(points)).
from scipy.spatial.distance import pdist, squareform
import numpy as np
import matplotlib.pyplot as plt
points = np.random.uniform(-.5, .5, (1000,2))
# Compute the distance between each different pair of points in X with pdist.
# Then, just for ease of working, convert to a typical symmetric distance matrix
# with squareform.
dists = squareform(pdist(points))
poi = points[4] # point of interest
dist_min = .1
close_points = dists[4] < dist_min
print("There are {} other points within a distance of {} from the point "
"({:.3f}, {:.3f})".format(close_points.sum() - 1, dist_min, *poi))
There are 27 other points within a distance of 0.1 from the point (0.194, 0.160)
For visualization purposes:
f,ax = plt.subplots(subplot_kw=
dict(aspect='equal', xlim=(-.5, .5), ylim=(-.5, .5)))
ax.plot(points[:,0], points[:,1], 'b+ ')
ax.plot(poi[0], poi[1], ms=15, marker='s', mfc='none', mec='g')
ax.plot(points[close_points,0], points[close_points,1],
marker='o', mfc='none', mec='r', ls='') # draw all points within distance
t = np.linspace(0, 2*np.pi, 512)
circle = dist_min*np.vstack([np.cos(t), np.sin(t)]).T
ax.plot((circle+poi)[:,0], (circle+poi)[:,1], 'k:') # Add a visual check for that distance
plt.show()
I am working with some points in spherical coordinates. I need to generate new points as the error points for them and a kind of offset for the old points.
The new point should be in a specific distance from the old one which distributing by gaussian distribution. The angle of new point compared to old one is not important.I am trying to generate new points for r direction. no matter what are phi and theta (Spherical coordinates)
To generate the new point distributing by gaussian function, I tried the numpy.rand.normal(mean,std,..). But It is generating 1D random points over mean value and this mean value is a real number. In my case I need an approach to specify the position of the old point and I have one given standard deviation for this distance from the original points.
Honesty, I dont have a copy of my code. It is on the university's server. But let's assume I have an array of size 100*3 including the spherical (or cartesian) coordinates of some points on a surface of a cylinder. In spherical case, the first column presents the radius value, the second column is theta and third one shows the phi for the points. now I want to generate random points from them using gaussian distribution. there is a given standard deviation for the gaussian distribution. The only important thing is that the new points generated by gaussian distribution are limited in r value. No matter the position of points in term of theta and phi.
When I tried numpy.rand.normal(mean,std,..), this generate some random points over the mean value. It does not help me. I want new points over my old ones with the given STD.
any idea would be appreciated.
This is a code, similar to mine written By Ophion How to generate regular points on cylindrical surface
def make_cylinder(radius, length, nlength, alpha, nalpha, center, orientation):
#Create the length array
I = np.linspace(0, length, nlength)
#Create alpha array avoid duplication of endpoints
#Conditional should be changed to meet your requirements
if int(alpha) == 360:
A = np.linspace(0, alpha, num=nalpha, endpoint=False)/180*np.pi
else:
A = np.linspace(0, alpha, num=nalpha)/180*np.pi
#Calculate X and Y
X = radius * np.cos(A)
Y = radius * np.sin(A)
#Tile/repeat indices so all unique pairs are present
pz = np.tile(I, nalpha)
px = np.repeat(X, nlength)
py = np.repeat(Y, nlength)
points = np.vstack(( pz, px, py )).T
#Shift to center
shift = np.array(center) - np.mean(points, axis=0)
points += shift
#Orient tube to new vector
#Grabbed from an old unutbu answer
def rotation_matrix(axis,theta):
a = np.cos(theta/2)
b,c,d = -axis*np.sin(theta/2)
return np.array([[a*a+b*b-c*c-d*d, 2*(b*c-a*d), 2*(b*d+a*c)],
[2*(b*c+a*d), a*a+c*c-b*b-d*d, 2*(c*d-a*b)],
[2*(b*d-a*c), 2*(c*d+a*b), a*a+d*d-b*b-c*c]])
ovec = orientation / np.linalg.norm(orientation)
cylvec = np.array([1,0,0])
if np.allclose(cylvec, ovec):
return points
#Get orthogonal axis and rotation
oaxis = np.cross(ovec, cylvec)
rot = np.arccos(np.dot(ovec, cylvec))
R = rotation_matrix(oaxis, rot)
return points.dot(R)
now calling the function:
points = make_cylinder(3, 5, 5, 360, 10, [0,2,0], [1,0,0])
sigma = 0.5 # given STD
ossfet_points = numpy.random.normal(np.mean(point[:,0]), sigma, size=(n,3))
If I'm not mistaken, you want random points on a spherical manifold with a gaussian distribution of distances from the center. If so, then you have the latter problem solved by sampling gaussian values of the radius using numpy.rand.normal
To get random spherical points is a little bit more tricky, but here's some code to do it (and a description of the math behind it at Wolfram MathWorld):
import numpy as np
num_points = 500
U = np.random.random(num_points)
V = np.random.random(num_points)
import math as m
def spherical_to_cartesian(vec):
'''
Convert spherical polar coordinates to cartesian coordinates:
See the definition of spherical_cartesian_to_polar.
#param vec: A vector of the 3 polar coordinates (r, u, v)
#return: (x, y, z)
'''
(r, u, v) = vec
x = r * m.sin(u) * m.cos(v)
y = r * m.sin(u) * m.sin(v)
z = r * m.cos(u)
return [x, y, z]
radius = 1.
points = np.array([spherical_to_cartesian([radius, 2 * np.pi * u, np.arccos(2*v - 1)]) for u,v in zip(U,V)])
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax = Axes3D(fig)
ax.plot(points[:,0], points[:,1], points[:,2], 'o')
Which will give you points like this:
Now if you want them to have normally distributed radii, you just need to substitute your randomly generated values in the list comprehension which uses the variable radius like this:
radii = np.random.normal(10, 3, 100)
points = np.array([spherical_to_cartesian([r, 2 * np.pi * u, np.arccos(2*v - 1)]) for r,u,v in zip(radii, U,V)])
Is this more or less what you're looking for?