我希望计算给定线段上从给定点垂直的点。
我有一条线段AB和一个在线段外的点C。我想计算出AB上的一个点D,使得CD垂直于AB。
我需要找到点D。这与此相似,但我想考虑Z坐标,因为它在3D空间中显示不正确。
我们得到以下三个方程:
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
(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]
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
AB线上的一点可以使用以下参数表示:
M(x)=A+x*(B-A),其中x为实数。
您想要找到D=M(x)使得DC和AB垂直:
dot(B-A,C-M(x))=0。
即:dot(B-A,C-A-x*(B-A))=0,或者dot(B-A,C-A)=x*dot(B-A,B-A),由此得出:
x=dot(B-A,C-A)/dot(B-A,B-A),除非A=B,否则定义良好。
这里我将"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只是一个直角三角形。
正如Ron Warholic和Nicolas Repiquet所回答的那样,这可以通过向量投影来解决。为了完整起见,我在此添加了一个Python / numpy实现,以防它能够为其他人节省时间:
import numpy as np
# Define some test data that you can solve for directly.
first_point = np.array([4, 4])
second_point = np.array([8, 4])
target_point = np.array([6, 6])
# Expected answer
expected_point = np.array([6, 4])
# Create vector for first point on line to perpendicular point.
point_vector = target_point - first_point
# Create vector for first point and second point on line.
line_vector = second_point - first_point
# Create the projection vector that will define the position of the resultant point with respect to the first point.
projection_vector = (np.dot(point_vector, line_vector) / np.dot(line_vector, line_vector)) * line_vector
# Alternative method proposed in another answer if for whatever reason you prefer to use this.
_projection_vector = (np.dot(point_vector, line_vector) / np.linalg.norm(line_vector)**2) * line_vector
# Add the projection vector to the first point
projected_point = first_point + projection_vector
# Test
(projected_point == expected_point).all()
对于任何需要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)