我想计算给定线上与给定点垂直的点。
我有一条线段 AB 并且在线段之外有一个点 C。我想计算 AB 上的点 D,使得 CD 垂直于 AB。
我必须找到 D 点。
它与此非常相似,但我也想考虑 Z 坐标,因为它在 3D 空间中没有正确显示。
证明:点D在垂直于AB的直线CD上,当然D属于AB。写下两个向量 CD.AB = 0 的点积,并将 D 属于 AB 的事实表示为 D=A+t(BA)。
我们最终得到 3 个方程:
Dx=Ax+t(Bx-Ax)
Dy=Ay+t(By-Ay)
(Dx-Cx)(Bx-Ax)+(Dy-Cy)(By-Ay)=0
将前两个方程代入第三个方程得到:
(Ax+t(Bx-Ax)-Cx)(Bx-Ax)+(Ay+t(By-Ay)-Cy)(By-Ay)=0
分布求解 t 给出:
(Ax-Cx)(Bx-Ax)+t(Bx-Ax)(Bx-Ax)+(Ay-Cy)(By-Ay)+t(By-Ay)(By-Ay)=0
这使:
t= -[(Ax-Cx)(Bx-Ax)+(Ay-Cy)(By-Ay)]/[(Bx-Ax)^2+(By-Ay)^2]
摆脱负面迹象:
t=[(Cx-Ax)(Bx-Ax)+(Cy-Ay)(By-Ay)]/[(Bx-Ax)^2+(By-Ay)^2]
一旦你有了 t,你就可以从前两个方程中计算出 D 的坐标。
Dx=Ax+t(Bx-Ax)
Dy=Ay+t(By-Ay)
function getSpPoint(A,B,C){
var x1=A.x, y1=A.y, x2=B.x, y2=B.y, x3=C.x, y3=C.y;
var px = x2-x1, py = y2-y1, dAB = px*px + py*py;
var u = ((x3 - x1) * px + (y3 - y1) * py) / dAB;
var x = x1 + u * px, y = y1 + u * py;
return {x:x, y:y}; //this is D
}
使用矢量点积有一个简单的封闭形式解决方案(不需要循环或近似)。
将您的点想象为向量,其中点 A 位于原点 (0,0),并且所有其他点都从中引用(您可以通过从每个点中减去点 A 轻松地将您的点转换到该参考系)。
在这个参考系中,点 D 只是点 C 在向量 B 上的向量投影,表示为:
// Per wikipedia this is more efficient than the standard (A . Bhat) * Bhat
Vector projection = Vector.DotProduct(A, B) / Vector.DotProduct(B, B) * B
结果向量可以通过向其添加点 A 转换回原始坐标系。
AB线上的一个点可以通过以下方式参数化:
M(x)=A+x*(BA),对于 x 实数。
您希望 D=M(x) 使得 DC 和 AB 是正交的:
点(BA,CM(x))=0。
即:dot(BA,CAx*(BA))=0,或dot(BA,CA)=x*dot(BA,BA),给出:
x=dot(BA,CA)/dot(BA,BA),除非 A=B,否则定义。
您正在尝试做的事情称为矢量投影
对于在 C# 中可能需要此功能的任何人,我将为您节省一些时间:
double Ax = ;
double Ay = ;
double Az = ;
double Bx = ;
double By = ;
double Bz = ;
double Cx = ;
double Cy = ;
double Cz = ;
double t = ((Cx - Ax) * (Bx - Ax) + (Cy - Ay) * (By - Ay)) / (Math.Pow(Bx - Ax, 2) + Math.Pow(By - Ay, 2));
double Dx = Ax + t*(Bx - Ax);
double Dy = Ay + t*(By - Ay);
这是另一个不使用 for 循环的 python 实现。它适用于任意数量的点和任意数量的线段。给定p_array作为一组点,并将x_array,y_array作为连续线段或折线。
这使用方程 Y = mX + n 并考虑到垂直线段的 m 因子为 -1/m。
import numpy as np
def ortoSegmentPoint(self, p_array, x_array, y_array):
"""
:param p_array: np.array([[ 718898.941 9677612.901 ], [ 718888.8227 9677718.305 ], [ 719033.0528 9677770.692 ]])
:param y_array: np.array([9677656.39934991 9677720.27550726 9677754.79])
:param x_array: np.array([718895.88881594 718938.61392781 718961.46])
:return: [POINT, LINE] indexes where point is orthogonal to line segment
"""
# PENDIENTE "m" de la recta, y = mx + n
m_array = np.divide(y_array[1:] - y_array[:-1], x_array[1:] - x_array[:-1])
# PENDIENTE INVERTIDA, 1/m
inv_m_array = np.divide(1, m_array)
# VALOR "n", y = mx + n
n_array = y_array[:-1] - x_array[:-1] * m_array
# VALOR "n_orto" PARA LA RECTA PERPENDICULAR
n_orto_array = np.array(p_array[:, 1]).reshape(len(p_array), 1) + inv_m_array * np.array(p_array[:, 0]).reshape(len(p_array), 1)
# PUNTOS DONDE SE INTERSECTAN DE FORMA PERPENDICULAR
x_intersec_array = np.divide(n_orto_array - n_array, m_array + inv_m_array)
y_intersec_array = m_array * x_intersec_array + n_array
# LISTAR COORDENADAS EN PARES
x_coord = np.array([x_array[:-1], x_array[1:]]).T
y_coord = np.array([y_array[:-1], y_array[1:]]).T
# FILAS: NUMERO DE PUNTOS, COLUMNAS: NUMERO DE TRAMOS
maskX = np.where(np.logical_and(x_intersec_array < np.max(x_coord, axis=1), x_intersec_array > np.min(x_coord, axis=1)), True, False)
maskY = np.where(np.logical_and(y_intersec_array < np.max(y_coord, axis=1), y_intersec_array > np.min(y_coord, axis=1)), True, False)
mask = maskY * maskX
return np.argwhere(mask == True)
在这里,我已将回答的代码从“cuixiping”转换为 matlab 代码。
function Pr=getSpPoint(Line,Point)
% getSpPoint(): find Perpendicular on a line segment from a given point
x1=Line(1,1);
y1=Line(1,2);
x2=Line(2,1);
y2=Line(2,1);
x3=Point(1,1);
y3=Point(1,2);
px = x2-x1;
py = y2-y1;
dAB = px*px + py*py;
u = ((x3 - x1) * px + (y3 - y1) * py) / dAB;
x = x1 + u * px;
y = y1 + u * py;
Pr=[x,y];
end
我没有看到这个答案,但 Ron Warholic 对矢量投影提出了一个很好的建议。ACD 只是一个直角三角形。
这是一个基于 Corey Ogburn对此线程的回答的 python 实现。
它将点投影到由点定义并产生点q
的线段上。如果超出线段
,它将返回 null :p1
p2
r
r
def is_point_on_line(p1, p2, q):
if (p1[0] == p2[0]) and (p1[1] == p2[1]):
p1[0] -= 0.00001
U = ((q[0] - p1[0]) * (p2[0] - p1[0])) + ((q[1] - p1[1]) * (p2[1] - p1[1]))
Udenom = math.pow(p2[0] - p1[0], 2) + math.pow(p2[1] - p1[1], 2)
U /= Udenom
r = [0, 0]
r[0] = p1[0] + (U * (p2[0] - p1[0]))
r[1] = p1[1] + (U * (p2[1] - p1[1]))
minx = min(p1[0], p2[0])
maxx = max(p1[0], p2[0])
miny = min(p1[1], p2[1])
maxy = max(p1[1], p2[1])
is_valid = (minx <= r[0] <= maxx) and (miny <= r[1] <= maxy)
if is_valid:
return r
else:
return None
由于您没有说明您使用的是哪种语言,所以我会给您一个通用的答案:
只需有一个循环穿过 AB 段中的所有点,从它们“画一个段”到 C,得到从 C 到 D 和从 A 到 D 的距离,然后应用 Pithagoras 定理。如果 AD^2 + CD^2 = AC^2,那么你找到了你的观点。
此外,您可以通过从最短边(考虑 AD 和 BD 边)开始循环来优化代码,因为您会更早地找到该点。