【问题标题】:Perpendicular on a line segment from a given point从给定点垂直于线段
【发布时间】:2012-04-24 15:23:38
【问题描述】:

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

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

我必须找到D点。

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

【问题讨论】:

标签: math 3d geometry


【解决方案1】:

证明: D点在垂直于AB的直线CD上,当然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)

【讨论】:

  • 这忽略了原始问题的 Z 分量。
  • 如果要将找到的点 D 限制在 A 和 B 之间的连线上,限制 t 为 0 >= t
  • 如果这对你不起作用..试试这个解决方案stackoverflow.com/questions/1811549/…
【解决方案2】:
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、b 和 d,我想计算点 c 的 x 和 y 坐标。我可以使用你的功能吗?如果没有,请告诉我修改。
  • @MeanCoder 如果只知道 a,b,d 点,c 点有无数种可能性。
【解决方案3】:

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

将您的点想象成向量,其中点 A 位于原点 (0,0) 并且所有其他点都以此为参考(您可以通过从每个点中减去点 A 轻松地将您的点转换到该参考系)。

在这个参考系中,点D就是向量B上点C的vector projection,表示为:

// Per wikipedia this is more efficient than the standard (A . Bhat) * Bhat
Vector projection = Vector.DotProduct(A, B) / Vector.DotProduct(B, B) * B

结果向量可以通过添加点 A 转换回原始坐标系。

【讨论】:

    【解决方案4】:

    在线 AB 上的点可以通过以下方式参数化:

    M(x)=A+x*(B-A),对于 x 实数。

    您希望 D=M(x) 使得 DC 和 AB 正交:

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

    【讨论】:

      【解决方案5】:

      您正在尝试做的事情称为vector projection

      【讨论】:

        【解决方案6】:

        这里我已经将回答的代码从“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
        

        【讨论】:

          【解决方案7】:

          我没有看到这个答案,但 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)

          【讨论】:

            【解决方案8】:

            对于在 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);
            

            【讨论】:

              【解决方案9】:

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

              【讨论】:

                【解决方案10】:

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

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

                此外,您可以通过从最短边(考虑 AD 和 BD 边)开始循环来优化您的代码,因为您会更早发现这一点。

                【讨论】:

                  【解决方案11】:

                  这是一个基于 Corey Ogburn 来自 this thread 的回答的 python 实现。
                  它将点 q 投影到由 p1p2 定义的线段上,从而得到点 @987654325 @
                  如果 r 落在线段之外,它将返回 null:

                  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
                  

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2022-07-20
                    • 2020-04-27
                    相关资源
                    最近更新 更多