我需要按如下图所示的缠绕顺序对选择的 3D 坐标进行排序。右下角的顶点应该是阵列的第一个元素,左下角的顶点应该是阵列的最后一个元素。考虑到相机面向这些点的任何方向以及这些点的任何方向,这需要作业。由于“左上”、“右下”等是相对的,我假设我可以使用相机作为参考点?我们也可以假设所有 4 个点都是共面的。
我正在使用 Blender API(撰写 Blender 插件),如果有必要,我可以访问相机的视图矩阵。从数学上讲,如果可以的话,这甚至可能吗?也许我把事情复杂化了?
由于 Blender API 在 Python 中,我将其标记为 Python,但我可以使用伪代码或根本没有代码。我主要关心如何在数学上处理这个问题,因为我不知道从哪里开始。
uj5u.com热心网友回复:
由于您假设这四个点是共面的,因此您需要做的就是找到质心,计算从质心到每个点的矢量,然后按矢量的角度对点进行排序。
import numpy as np
def sort_points(pts):
centroid = np.sum(pts, axis=0) / pts.shape[0]
vector_from_centroid = pts - centroid
vector_angle = np.arctan2(vector_from_centroid[:, 1], vector_from_centroid[:, 0])
sort_order = np.argsort(vector_angle) # Find the indices that give a sorted vector_angle array
# Apply sort_order to original pts array.
# Also returning centroid and angles so I can plot it for illustration.
return (pts[sort_order, :], centroid, vector_angle[sort_order])
这个函式计算角度假设点是二维的,但是如果你有共面点,那么在公共平面上找到坐标并消除第三个坐标应该很容易。
让我们撰写一个快速绘图函式来绘制我们的点:
from matplotlib import pyplot as plt
def plot_points(pts, centroid=None, angles=None, fignum=None):
fig = plt.figure(fignum)
plt.plot(pts[:, 0], pts[:, 1], 'or')
if centroid is not None:
plt.plot(centroid[0], centroid[1], 'ok')
for i in range(pts.shape[0]):
lstr = f"pt{i}"
if angles is not None:
lstr = f" ang: {angles[i]:.3f}"
plt.text(pts[i, 0], pts[i, 1], lstr)
return fig
现在让我们测验一下:
随机点:
pts = np.random.random((4, 2))
spts, centroid, angles = sort_points(pts)
plot_points(spts, centroid, angles)
矩形中的点:
pts = np.array([[0, 0], # pt0
[10, 5], # pt2
[10, 0], # pt1
[0, 5]]) # pt3
spts, centroid, angles = sort_points(pts)
plot_points(spts, centroid, angles)
It's easy enough to find the normal vector of the plane containing our points, it's simply the (normalized) cross product of the vectors joining two pairs of points:
plane_normal = np.cross(pts[1, :] - pts[0, :], pts[2, :] - pts[0, :])
plane_normal = plane_normal / np.linalg.norm(plane_normal)
Now, to find the projections of all points in this plane, we need to know the "origin" and basis of the new coordinate system in this plane. Let's assume that the first point is the origin, the x axis joins the first point to the second, and since we know the z axis (plane normal) and x axis, we can calculate the y axis.
new_origin = pts[0, :]
new_x = pts[1, :] - pts[0, :]
new_x = new_x / np.linalg.norm(new_x)
new_y = np.cross(plane_normal, new_x)
Now, the projections of the points onto the new plane are given by this answer:
proj_x = np.dot(pts - new_origin, new_x)
proj_y = np.dot(pts - new_origin, new_y)
Now you have two-dimensional points. Run the code above to sort them.
uj5u.com热心网友回复:
几个小时后,我终于找到了解决方案。@Pranav Hosangadi 的解决方案适用于事物的 2D 方面。但是,我在使用他的解决方案的第二部分将 3D 坐标投影到 2D 坐标时遇到了麻烦。我还尝试按照此答案中的描述投影坐标,但它没有按预期作业。然后我发现了一个名为的 API 函式location_3d_to_region_2d()
(请参阅档案),顾名思义,获取给定 3D 坐标的 2D 荧屏坐标(以像素为单位)。首先,我不需要将任何东西“投影”到 2D 中,让荧屏坐标作业得非常好,而且要简单得多。从那时起,我可以使用 Pranav 的函式对坐标进行排序,并稍作调整,以按照我第一篇文章的荧屏截图中所示的顺序得到它,我希望它以串列而不是 NumPy 阵列的形式回传。
import bpy
from bpy_extras.view3d_utils import location_3d_to_region_2d
import numpy
def sort_points(pts):
"""Sort 4 points in a winding order"""
pts = numpy.array(pts)
centroid = numpy.sum(pts, axis=0) / pts.shape[0]
vector_from_centroid = pts - centroid
vector_angle = numpy.arctan2(
vector_from_centroid[:, 1], vector_from_centroid[:, 0])
# Find the indices that give a sorted vector_angle array
sort_order = numpy.argsort(-vector_angle)
# Apply sort_order to original pts array.
return list(sort_order)
# Get 2D screen coords of selected vertices
region = bpy.context.region
region_3d = bpy.context.space_data.region_3d
corners2d = []
for corner in selected_verts:
corners2d.append(location_3d_to_region_2d(
region, region_3d, corner))
# Sort the 2d points in a winding order
sort_order = sort_points(corners2d)
sorted_corners = [selected_verts[i] for i in sort_order]
谢谢,Pranav 花费您的时间和耐心帮助我解决这个问题!
0 评论