从给定点到线段的垂直线

35

我希望计算给定线段上从给定点垂直的点。

我有一条线段AB和一个在线段外的点C。我想计算出AB上的一个点D,使得CD垂直于AB。

Find point D

我需要找到点D。
这与相似,但我想考虑Z坐标,因为它在3D空间中显示不正确。

如果没有编程问题,这个问题最好在[math.se]上提问(那里几乎肯定已经有重复的问题了)。 - AakashM
1
https://dev59.com/GnI-5IYBdhLWcg3whYwr - jdbertron
1
最好指定您想要使用的语言。 - ThomasW
12个回答

38
证明: 点D在线段CD上,且垂直于线段AB,当然D也在线段AB上。 写出两个向量CD.AB的点积为0,并表达D在AB上的事实为D=A+t(B-A)。 最终得到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)

2
这忽略了原始问题的Z分量。 - Stephen
2
如果您想将找到的点D限制在A和B之间的直线上,请将t限制为0≥t≤1。 - Oliver Becker
1
如果这个方法对你不起作用.. 请尝试这个解决方案 https://dev59.com/GnI-5IYBdhLWcg3whYwr - Zumbarlal Saindane

27
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
}

question


8
希望能看到一点您所做的内容的解释。 - Patrick Murphy
嗨。我有点a、b和d,我想计算点c的x和y坐标。我可以使用你的函数吗?如果不行,请告诉我需要进行哪些修改。 - Mean Coder
@MeanCoder 如果只知道点a、b、d,那么点c有无数种可能性。 - cuixiping
如果A、B、C是GPS坐标(纬度、经度),会怎样? - TkrA
它们是GPS还是其他坐标系并不重要,你仍然需要考虑A点和B点是否位于0坐标的两侧。 - Vlad Vyatkin

8

使用向量点积可以得到一个简单的闭式解(不需要循环或近似)。

将您的点想象成向量,其中点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添加到其中来转换回原始坐标系。

6

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,否则定义良好。


2

1

这里我将"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

0

我没有看到这个答案的提供,但Ron Warholic通过矢量投影提出了一个很好的建议。ACD只是一个直角三角形。

  1. 创建向量AC即(Cx-Ax,Cy-Ay)
  2. 创建向量AB即(Bx-Ax,By-Ay)
  3. AC和AB的点积等于向量之间夹角的余弦。即cos(theta)=ACx*ABx+ACy*ABy。
  4. 向量的长度为sqrt(x*x+y*y)
  5. AD的长度=cos(theta)*length(AC)
  6. 标准化向量AB即(ABx/length(AB), ABy/length(AB))
  7. D=A+NAB*length(AD)

0

正如Ron WarholicNicolas 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()

0

对于任何需要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);

0

这里有另一种不使用for循环的Python实现。它适用于任意数量的点和线段。给定p_array作为一组点,以及x_arrayy_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)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接