Mat*_*ias 5 python numpy python-3.x
我在两个 XY 点(p1 和 p2)和该线外的第三个 XY 点(p3)之间有一条线/向量。根据这篇文章,我知道如何获得该点到线的距离。但我实际上要寻找的是该线上的一个点 (p4),它与第三个点 (p3) 的距离 (d) 最小。我找到了这篇文章,但我觉得这不是正确的解决方案。也许 Numpy 或 Python 中包含了一些东西?
根据@allo,我尝试了以下操作。您可以将我的代码下载为Python 文件或Jupyter Notebook(都是 Python3)。
points = [[1, 1], [3, 1], [2.5, 2], [2.5, 1]]
import matplotlib.pyplot as plt
%matplotlib inline
fig, ax = plt.subplots()
fig.set_size_inches(6,6)
x, y = zip(*points[:2])
l1, = ax.plot(x,y, color='blue')
scatter1 = ax.scatter(x=x,y=y, color='blue', marker='x', s=80, alpha=1.0)
x, y = zip(*points[2:])
l2, = ax.plot(x,y, color='red')
scatter2 = ax.scatter(x=x,y=y, color='red', marker='x', s=80, alpha=1.0)
p1 = Vector2D(*points[0])
p2 = Vector2D(*points[1])
p3 = Vector2D(*points[2])
p1p2 = p2.sub_vector(p1)
p1p3 = p3.sub_vector(p1)
angle_p1p2_p1p3 = p1p2.get_angle_radians(p1p3)
length_p1p3 = p1p3.get_length()
length_p1p2 = p1p2.get_length()
p4 = p1.add_vector(p1p2.multiply(p1p3.get_length()/p1p2.get_length()).multiply(math.cos(p1p2.get_angle_radians(p1p3))))
#p4 = p1 + p1p2 * length(p1p3)/length(p1p2)*cos(angle(p1p2, p1p3))
p4 = p1.add_vector(p1p2.multiply(length_p1p3/length_p1p2*math.cos(angle_p1p2_p1p3)))
p4
Run Code Online (Sandbox Code Playgroud)
这导致 p4 = (1.8062257748298551, 1.0)但显然应该是(2.5, 1.0)。
让我们从指定的线开始,我们用 和 上的两个点来定义该(x1, y1)线(x2, y2)。
利用dx = x2-x1和 ,dy = y2-y1我们可以正式地将直线上的每个点写为(x12, y12) = (x1, y1) + a*(dx, dy)其中a是实数。
使用类似的符号,穿过(x3, y3)并垂直于指定点的直线上的点是(x34, y34) = (x3, y3) + b*(-dy, +dx)。
为了找到交集,我们必须施加(x12, y12) = (x34, y34)or\n (x1, y1) + a*(dx, dy) = (x3, y3) + b*(-dy, +dx)。
x分别写出和 的方程y
y1 + a dy - y3 - b dx = 0\nx1 + a dx + b dy - x3 = 0\nRun Code Online (Sandbox Code Playgroud)\n它是一个线性系统a,b其解为
a = (dy y3 - dy y1 + dx x3 - dx x1) / (dy^2 + dx^2)\nb = (dy x3 - dy x1 - dx y3 + dx y1) / (dy^2 + dx^2)\nRun Code Online (Sandbox Code Playgroud)\n(x3, y3)位于直线上的最近点的坐标为(x1+a*dx, y1+a*dy)\xe2\x80\x94,您只需计算系数a。
从数值上来说,线性系统的行列式是,dx**2+dy**2只有当两个初始点相对于第三点的距离 w/r 彼此非常接近时,才会出现问题。
我们使用 2-uple 浮点来表示 2D 点,并定义一个函数,其参数为 3 个 2-uple,表示定义线 ( ) 的点和确定 的位置的p1, p2点 ( )p3p4和确定所述直线上
In [16]: def p4(p1, p2, p3):\n ...: x1, y1 = p1\n ...: x2, y2 = p2\n ...: x3, y3 = p3\n ...: dx, dy = x2-x1, y2-y1\n ...: det = dx*dx + dy*dy\n ...: a = (dy*(y3-y1)+dx*(x3-x1))/det\n ...: return x1+a*dx, y1+a*dy\nRun Code Online (Sandbox Code Playgroud)\n为了测试实现,我使用OP使用的三个点来演示他们在这个问题上的问题:
\nIn [17]: p4((1.0, 1.0), (3.0, 1.0), (2.5, 2))\nOut[17]: (2.5, 1.0)\nRun Code Online (Sandbox Code Playgroud)\n看来结果p4(...)与OP的预期不谋而合。
import matplotlib.pyplot as plt\n\ndef p(p1, p2, p3):\n (x1, y1), (x2, y2), (x3, y3) = p1, p2, p3\n dx, dy = x2-x1, y2-y1\n det = dx*dx + dy*dy\n a = (dy*(y3-y1)+dx*(x3-x1))/det\n return x1+a*dx, y1+a*dy\n\np1, p2, p3 = (2, 4), (7, 3), (1, 1)\np4 = p(p1, p2, p3)\n\nfig, ax = plt.subplots()\n\n# if we are after right angles, anything else would be wrong\nax.set_aspect(1)\n\nplt.plot(*zip(p1, p2, p4, p3), marker=\'*\')\nRun Code Online (Sandbox Code Playgroud)\n
你想要做的是矢量投影。
边缘p1p3旋转到边缘上p1p2,您需要找到线段的正确长度p1p4。然后你就可以使用p1+FACTOR*p1p2 / length(p1p2). p1p2所需的因子由和之间的角度的余弦给出p1p3。然后你得到
p4 = p1 + p1p2 * length(p1p3)/length(p1p2)*cos(angle(p1p2, p1p3))
Run Code Online (Sandbox Code Playgroud)
这里以两种边缘情况为例:
0如果p1p3正交于p1p2,则p4位于 上p1。p1p3当为 时,余弦为 1 p1p2,因此p1p2只需按比例缩放即可length(p1p3)/length(p1p2)得到p1p4。您还可以用点积替换余弦dot(p1p2 / length(p1p2), p1p3 / length(p1p3)。
您可以在有关线性代数的维基书中找到更多详细信息和精美插图。
这是从您的 python 代码派生的完整示例。我在这里使用 numpy 而不是 Vector2D。
points = [[1, 1], [3, 1], [2.5, 2], [2.5, 1]]
import matplotlib.pyplot as plt
%matplotlib inline
import numpy as np
fig, ax = plt.subplots()
fig.set_size_inches(6,6)
x, y = zip(*points[:2])
l1, = ax.plot(x,y, color='blue')
scatter1 = ax.scatter(x=x,y=y, color='blue', marker='x', s=80, alpha=1.0)
x, y = zip(*points[2:])
l2, = ax.plot(x,y, color='red')
scatter2 = ax.scatter(x=x,y=y, color='red', marker='x', s=80, alpha=1.0)
p1 = np.array(points[0])
p2 = np.array(points[1])
p3 = np.array(points[2])
p1p2 = p2 - p1
p1p3 = p3 - p1
p4 = p1 + p1p2 / np.linalg.norm(p1p2) * np.linalg.norm(p1p3) * ((p1p2/np.linalg.norm(p1p2)).T * (p1p3/np.linalg.norm(p1p3)))
p1, p2, p3, p4, p1p2, p1p3
Run Code Online (Sandbox Code Playgroud)
我们可以p4使用标量积的线性来缩短这条线:
p4 = p1 + p1p2 / np.linalg.norm(p1p2) * ((p1p2/np.linalg.norm(p1p2)).T * (p1p3))
p4 = p1 + p1p2 / np.linalg.norm(p1p2)**2 * (p1p2.T * (p1p3))
Run Code Online (Sandbox Code Playgroud)