I have a bunch of points in 3d space (x,y and z) and want to find the closest point of surface existing among points. I read this solution but could not solve my issue. My surfaces are created by four points using:
PRECISION = 1e-8 # Arbitrary zero for real-world purposes
def plane_from_points(points):
centroid = np.mean(points, axis=0)
_, eigenvalues, eigenvectors = np.linalg.svd(points - centroid)
if eigenvalues[1] < PRECISION:
raise ValueError("Points are aligned, can't define a plane")
normal = eigenvectors[2]
d = -np.dot(centroid, normal)
plane = np.append(normal, d)
thickness = eigenvalues[2]
return plane, thickness
I have a tilted (shown by black dots in my fig) and a normal (shown by yellow dots) surface among my points. Corners of the surfaces are:
surf_corners=[np.array([[1., 1., 1.], [1.5, 2., 1.], [3., 1., 3.], [3.5, 2., 3.]]),\
np.array([[5., 1., 4.], [5., 2., 4.], [7., 1., 2.], [7., 2., 2.]])]
First array is the tilted one and second array is the normal one. These are points that I want to find the closet points of the surfaces:
surrounding_points=[np.array([[2., 1., 3.2], [3., 2., 3.]]),\
np.array([[6., 1., 2.5], [6., 2., 2.5], [6., 1., 3.5], [6., 2., 3.5]])]
Projection of the first array (shwon by blue squares) of surrounding_points
should be found on the surface created by first arrays of the surf_corners
and second one (shwon by red squares) also with the second suraface. My surface should be only among its four corners and not infinity. I mean the y
value of calculated point should not be less or more than the corners. For example I prefer to maintain the y
value of the surrounding_points
. I mean for first point, I want the closet point of the created surface that its y
value is 1
. For simplicity I copied only data that their y
values are equal to brders of my planes and in reality I may have other points with y
values between the brders of the planes and still I want to find the projection with a fixed y
value. I tried the following method to do so but it is just giving me the horizontal projection for one surface and I cannot hnadle two surfaces (when surf_corners
and surrounding_points
ar numpy arrays and not a list of numpy arrays):
pls=[]
for i in surf_corners:
i=i.tolist()
pl, thickness= plane_from_points(i)
pls.append(pl)
point_on_surf=[]
n_iter=1
for i in range (n_iter):
a, b, c, d = pls[i][0], pls[i][1], pls[i][2], pls[i][3]
def fun(row):
row[0] = -(d + b * row[1] + c * row[2]) / a # calculates new x
return row[0], row[1], row[2] # new x and old y and z
to_be_projected=[copy.copy(surrounding_points[i])]
new_points = np.asarray(list(map(fun, [x for point in to_be_projected for x in point])))
point_on_surf.append(new_points)
For more clarity, I uploaded a fig. green arrows show my desired projection point (closest point with the same y
value). In advance, I do appreciate any help.
I understand you need to solve the following problem:
Problem 1. Determine the plane
I understand you are using the code from this answer, so I won't comment any further.
Problem 2. Project a point
You can calculate the projected point using
projected_point = p - np.dot(p - p0, u) * u
where
p
is the point to projectp0
is a point on the plane (one of the points defining the quadrilateral, their centroid...)u
is the unit normal vector of the planeEdit:
As long as the plane calculation method provides the plane coefficients (a,b,c,d) where (a,b,c) is the plane's normal vector, and it's also a unit vector, you can calculate the projection using:
u = plane[:3] # Plane's normal unit vector
d = plane[3] # Signed distance plane - origin of coordinates
distance = np.dot(u, point) + d # Signed distance point - plane
projected_point = point - distance * u
Problem 3. Point inside quadrilateral
I would recommend using the winding number method. And, if you know that your quadrilaterals are convex, you can take advantage of the optimizations described in the same link.