按顺时针的顺序排列点?
给定一组 x、y 点的数组,如何按顺时针顺序(围绕它们整体平均中心点)对这个数组的点进行排序?我的目标是将这些点传递给线条创建函数,以获得看起来非常“坚实”的东西,尽可能凸出,没有线条交错。
值得一提的是,我正在使用 Lua,但任何伪代码都将非常感激。
更新: 参考 Ciamej 出色的答案,这是基于 Lua 的代码(忽略我的“app”前缀):
原文链接 https://stackoverflow.com/questions/6989100
将下面翻译成中文并且保留原本的 markdown 格式
另一种有趣的解决问题的方法是找到旅行商问题(TSP)的近似最小值,即连接所有点的最短路径。如果您的点形成一个凸形状,则应该是正确的解决方案,否则,它仍然应该看起来很好(“固体”形状可以定义为周长/面积比低的形状,这就是我们在此优化的内容)。
您可以使用任何TSP优化器的实现,在您选择的语言中,我相信您可以找到很多这样的实现。
- 创建向量a = new vector3(1, 0, 0),相对于X轴
- 创建向量b = 任意点 - 中心点
- y = |a * b|,x = a · b
- Atan2(y,x)…………………………返回弧度制下 -PI 到 + PI 之间的角度
- (Input % 360 + 360) % 360……将其转换为 0 到 2PI 的角度制
- 按照得到的角度从0到360,按添加点的顺序将其排序加入list_of_polygon_verts
最终,您将获得按逆时针排序的顶点
list.Reverse()……………………按时针顺序排列
以下是一种方法按顺时针顺序排序矩形的顶点。我修改了pyimagesearch提供的原始解决方案,并摆脱了scipy依赖项。
import numpy as np
def pointwise_distance(pts1, pts2):
"""计算点对之间的距离
Args:
pts1 (np.ndarray): 形式为[[x1, y1], [x2, y2], ...]的数组
pts2 (np.ndarray): 形式为[[x1, y1], [x2, y2], ...]的数组
Returns:
np.array: 相应点之间的距离
"""
dist = np.sqrt(np.sum((pts1 - pts2)**2, axis=1))
return dist
def order_points(pts):
"""按照顺序排序形式为[top left, top right, bottom right, bottom left]的点。
来源:https://www.pyimagesearch.com/2016/03/21/ordering-coordinates-clockwise-with-python-and-opencv/
Args:
pts (np.ndarray): 形式为[[x1, y1], [x2, y2], [x3, y3], [x4, y4]]的点列表
Returns:
np.ndarray: 按照顺序排序的坐标
"""
# 根据x坐标对点进行排序
x_sorted = pts[np.argsort(pts[:, 0]), :]
# 从排序后的x坐标点中获取最左和最右的点
left_most = x_sorted[:2, :]
right_most = x_sorted[2:, :]
# 根据它们的y坐标对最左边的坐标进行排序,从而可以分别获取左上和左下的点
left_most = left_most[np.argsort(left_most[:, 1]), :]
tl, bl = left_most
# 现在我们有了左上坐标,以此为锚点计算左上和最右边点之间的欧几里得距离;
# 根据勾股定理,具有最大距离的点将是我们的右下点。注意:这是一个有效的假设,因为我们只处理矩形。
# 我们需要使用这个来代替只使用min/max来处理有相同x或y值的点的情况。
D = pointwise_distance(np.vstack([tl, tl]), right_most)
br, tr = right_most[np.argsort(D)[::-1], :]
# 按照顶点的顺序返回坐标,分别是左上,右上,右下和左下
return np.array([tl, tr, br, bl], dtype="float32")
我知道这是一篇旧的帖子,有着一个优秀的被接受的答案,但是我觉得我还可以为此提供一些有用的东西。到目前为止,所有的答案本质上都使用了一个比较函数来比较两个点并确定它们的顺序,但是如果你只想一个点一个点地使用关键函数呢?
这不仅是可能的,而且生成的代码也非常紧凑。下面是使用 Python 的内置排序函数的完整解决方案:
# 创建一些随机点
num = 7
points = np.random.random((num, 2))
# 计算它们的中心点
center = np.mean(points, axis=0)
# 创建一个 arctan2 函数以返回一个从 [0, 2 pi) 而非 [-pi, pi) 区间内的值
arctan2 = lambda s, c: angle if (angle := np.arctan2(s, c)) >= 0 else 2 * np.pi + angle
# 定义关键函数
def clockwise_around_center(point):
diff = point - center
rcos = np.dot(diff, center)
rsin = np.cross(diff, center)
return arctan2(rsin, rcos)
# 使用关键函数对点进行排序
sorted_points = sorted(points, key=clockwise_around_center)
如果这些点位于嵌入到三维空间中的二维平面上,则此答案同样适用于 3D。我们只需要通过对它与该平面的法向量进行点积来修改 rsin
的计算。例如:
rsin = np.dot([0,0,1], np.cross(diff, center))
如果该平面的法向量为 e_z
。
这段代码的优点在于,它只使用了一个关键函数在一个点一个点地排序。通过在系数级别上推导出这个量 rsin
,发现它与被接受的答案中所称的 det
完全相同,只是我计算 point - center
和 center
之间,而不是 point1 - center
和 point2 - center
之间。但是这个量的几何意义是半径乘以正弦值,因此我称这个变量为 rsin
。对于点积,同理,这是半径乘以余弦值,因此被称为 rcos
。
你可以认为这个解决方案使用了 arctan2
,因此不太干净。然而,我个人认为使用关键函数的清晰度优于只需要一次三角函数调用的需要。请注意,我倾向于使 arctan2
返回来自 [0, 2 pi)
而非 [-pi, pi)
的值,因为当 point
恰好等于 center
时,我们得到角度为 0
,因此它将成为我们排序列表中的第一个点。这是一个可选的选择。
为了理解这段代码是如何工作的,关键的见解是,所有我们的点都是定义为相对于原点的箭头,包括中心点 center
本身。因此,如果我们计算 point - center
,这相当于将箭头从指向 center
的箭头的顶端放到指向 point
的箭头的顶端,在原点处。因此,我们可以根据与指向 center
的箭头所成角度的大小对箭头 point - center
进行排序。
使用numpy:
import matplotlib.pyplot as plt
import numpy as np
# 坐标列表
coords = np.array([7,7, 5, 0, 0, 0, 5, 10, 10, 0, 0, 5, 10, 5, 0, 10, 10, 10]).reshape(-1, 2)
centroid = np.mean(coords, axis=0)
sorted_coords = coords[np.argsort(np.arctan2(coords[:, 1] - centroid[1], coords[:, 0] - centroid[0])), :]
plt.scatter(coords[:,0],coords[:,1])
plt.plot(coords[:,0],coords[:,1])
plt.plot(sorted_coords[:,0],sorted_coords[:,1])
plt.show()
- 如何在roblox studio中1:1导入真实世界的地形?
- 求解,lua_resume的第二次调用继续执行协程问题。
- 【上海普陀区】内向猫网络招募【Skynet游戏框架Lua后端程序员】
- SF爱好求教:如何用lua实现游戏内调用数据库函数实现账号密码注册?
- Lua实现网站后台开发
- LUA错误显式返回,社区常见的规约是怎么样的
- lua5.3下载库失败
- 请问如何实现文本框内容和某个网页搜索框内容连接,并把网页输出来的结果反馈到另外一个文本框上
- lua lanes多线程使用
- 一个kv数据库
- openresty 有没有比较轻量的 docker 镜像
- 想问一下,有大佬用过luacurl吗
- 在Lua执行过程中使用Load函数出现问题
- 为什么 neovim 里没有显示一些特殊字符?
- Lua比较两个表的值(不考虑键的顺序)
- 有个lua简单的项目,外包,有意者加微信 liuheng600456详谈,最好在成都
- 如何在 Visual Studio 2022 中运行 Lua 代码?
- addEventListener 返回 nil Lua
- Lua中获取用户配置主目录的跨平台方法
- 如何编写 Lua 模式将字符串(嵌套数组)转换为真正的数组?
你所需要的系统就是所谓的polar coordinates(极坐标系)。在任何编程语言中,将笛卡尔坐标系转换为极坐标系都非常容易。转换的公式可以在这个章节中找到。
转换为极坐标系之后,只需要按角度theta进行排序即可。