28

我想计算给定线上与给定点垂直的点。

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

找到点 D

我必须找到 D 点。

它与此非常相似,但我也想考虑 Z 坐标,因为它在 3D 空间中没有正确显示。

4

11 回答 11

33

证明:点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)
于 2013-03-03T15:56:40.560 回答
23
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
}

问题

于 2012-09-19T17:01:44.480 回答
6

使用矢量点积有一个简单的封闭形式解决方案(不需要循环或近似)。

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

于 2012-04-24T16:01:05.470 回答
4

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

于 2012-04-24T18:01:42.267 回答
2

您正在尝试做的事情称为矢量投影

于 2012-04-24T15:57:30.530 回答
1

对于在 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);
于 2021-03-19T07:38:21.760 回答
1

这是另一个不使用 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)

于 2021-12-30T03:33:13.980 回答
0

在这里,我已将回答的代码从“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
于 2016-03-09T11:50:17.283 回答
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*长度(AD)
于 2016-12-21T05:12:18.843 回答
0

这是一个基于 Corey Ogburn对此线程的回答的 python 实现。
它将点投影到由点定义并产生点q的线段上。如果超出线段 ,它将返回 null :p1p2r
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
于 2017-02-03T00:47:58.460 回答
-1

由于您没有说明您使用的是哪种语言,所以我会给您一个通用的答案:

只需有一个循环穿过 AB 段中的所有点,从它们“画一个段”到 C,得到从 C 到 D 和从 A 到 D 的距离,然后应用 Pithagoras 定理。如果 AD^2 + CD^2 = AC^2,那么你找到了你的观点。

此外,您可以通过从最短边(考虑 AD 和 BD 边)开始循环来优化代码,因为您会更早地找到该点。

于 2012-04-24T15:31:58.813 回答