得到最接近的一条线

我想有一个直接的C#函数来获得最近点(从点P)到线段AB。 抽象函数可能看起来像这样。 我已经通过search,但没有find一个可用(由我)的解决scheme。

public Point getClosestPointFromLine(Point A, Point B, Point P); 

这里的Ruby伪装成伪代码,假设每个Point对象都有一个xy字段。

 def GetClosestPoint(A, B, P) a_to_p = [Px - Ax, Py - Ay] # Storing vector A->P a_to_b = [Bx - Ax, By - Ay] # Storing vector A->B atb2 = a_to_b[0]**2 + a_to_b[1]**2 # **2 means "squared" # Basically finding the squared magnitude # of a_to_b atp_dot_atb = a_to_p[0]*a_to_b[0] + a_to_p[1]*a_to_b[1] # The dot product of a_to_p and a_to_b t = atp_dot_atb / atb2 # The normalized "distance" from a to # your closest point return Point.new( :x => Ax + a_to_b[0]*t, :y => Ay + a_to_b[1]*t ) # Add the distance to A, moving # towards B end 

或者:

从维基百科的线路交叉口 。 首先findQ,这是在“正确的方向”上从P走出的第二点。 这给了我们四点。

 def getClosestPointFromLine(A, B, P) a_to_b = [Bx - Ax, By - Ay] # Finding the vector from A to B This step can be combined with the next perpendicular = [ -a_to_b[1], a_to_b[0] ] # The vector perpendicular to a_to_b; This step can also be combined with the next Q = Point.new(:x => Px + perpendicular[0], :y => Py + perpendicular[1]) # Finding Q, the point "in the right direction" # If you want a mess, you can also combine this # with the next step. return Point.new (:x => ((Ax*By - Ay*Bx)*(Px - Qx) - (Ax-Bx)*(Px*Qy - Py*Qx)) / ((Ax - Bx)*(Py-Qy) - (Ay - By)*(Py-Qy)), :y => ((Ax*By - Ay*Bx)*(Py - Qy) - (Ay-By)*(Px*Qy - Py*Qx)) / ((Ax - Bx)*(Py-Qy) - (Ay - By)*(Py-Qy)) ) end 

为了性能的原因,caching,跳过步骤等是可能的。

如果有人对基于上述的C#XNA函数感兴趣:

  public static Vector2 GetClosestPointOnLineSegment(Vector2 A, Vector2 B, Vector2 P) { Vector2 AP = P - A; //Vector from A to P Vector2 AB = B - A; //Vector from A to B float magnitudeAB = AB.LengthSquared(); //Magnitude of AB vector (it's length squared) float ABAPproduct = Vector2.Dot(AP, AB); //The DOT product of a_to_p and a_to_b float distance = ABAPproduct / magnitudeAB; //The normalized "distance" from a to your closest point if (distance < 0) //Check if P projection is over vectorAB { return A; } else if (distance > 1) { return B; } else { return A + AB * distance; } } 

你的点( X )将是点A和点B的线性组合:

 X = k A + (1-k) B 

对于X实际上在线段上,参数k必须介于0和1之间(含)。 你可以如下计算k:

 k_raw = (PB).(AB) / (AB).(AB) 

(其中周期表示点积)

然后,确保点实际上在线段上:

 if k_raw < 0: k= 0 elif k_raw > 1: k= 1 else: k= k_raw 

Justin L.的答案几乎没有问题,但它并不检查标准化距离是否小于0,或者高于ABvector幅度。 那么当Pvector超出范围(来自线段AB)时,它将无法正常工作。 这里是更正的伪代码:

  function GetClosestPoint(A, B, P) { vectorAP = (px - ax, py - ay) //Vector from A to P vectorAB = (bx - ax, by - ay) //Vector from A to B magnitudeAB = vectorAB[0]^2 + vectorAB[1]^2 //Magnitude of AB vector (it's length) ABAPproduct = vectorAB[0]*vectorAP[0] + vectorAB[1]*vectorAP[1] //The product of a_to_p and a_to_b distance = ABAPproduct / magnitudeAB //The normalized "distance" from a to your closest point if ( distance < 0) //Check if P projection is over vectorAB { returnPoint.x = ax returnPoint.y = ay } else if (distance > magnitudeAB) { returnPoint.x = bx returnPoint.y = by } else { returnPoint.x = ax + vectorAB[0]*distance returnPoint.y = ay + vectorAB[1]*distance } } 

通过将y-差值除以x-差值来找出AB的斜率a1; 然后绘制一条垂直线(斜率a2 = -1 / a1,需要将P的坐标放入y = a2 * x + b2)求解偏移量(b2)。 那么你有两条线(即两个线性方程),你需要解决的交集。 那将是你最近的一点。

做正确的math,function将是非常微不足道的写作。

细化一下:

 Original line: y = a1 * x + b1 a1 = (By - Ay) / (Bx - Ax) <-- b1 = Ay - a1 * Ax <-- Perpendicular line: y = a2 * x + b2 a2 = -1/a1 <-- b2 = Py - a2 * Px <-- Now you have P which lies on both lines: y = a1 * x + b1 y = a2 * x + b2 --------------- subtract: 0 = (a1 - a2) * Px + (b1 - b2) x = - (b1 - b2) / (a1 - a2) <-- y = a1 * x + b1 <-- 

希望我没有搞错:) 更新当然,我做到了。 首先让我把纸上的东西写出来吧。 我当之无愧,但我希望有人来纠正我。 固定(我希望)。

箭头指出了方向。

更新啊,angular落案件。 是的,有些语言不能很好地处理无限。 我曾经说过这个解决scheme是无语言的

你可以检查特殊情况,他们很容易。 第一个是当x差为0时。这意味着该线是垂直的,而最近的点是在水平的垂直线上。 因此, x = Ax, y = Px

第二个是y差值为0,反之亦然。 因此, x = Px, y = Ay

这个答案是基于射影几何的思想。

计算叉积(Ax,Ay,1)×(Bx,By,1)=(u,v,w)。 结果vector描述了连接A和B的直线:它具有方程ux + vy + w =​​ 0。 但是你也可以把(u,v,0)解释为与这条线垂直的方向上无穷远的一个点。 做另一个交叉产品,你得到的线连接到P:(u,v,0)×(Px,Py,1)。 为了与AB线交叉,你做另一个叉积:((u,v,0)×(Px,Py,1))×(u,v,w)。 结果将是一个均匀的坐标向量(x,y,z),从中可以读出这个最近点的坐标(x / z,y / z)。

把所有东西放在一起,你会得到下面的公式:

{\ scriptsize \ begin {pmatrix} x \ y \ z \ end {pmatrix}} = \ Bigl(\ bigl({\ scriptsize \ begin {pmatrix} 1&0& )\ bigr)\ times P \ Bigr)\ times(A \ times B)

使用计算机代数系统,您可以find结果坐标如下:

 x = ((Ax - Bx)*Px + (Ay - By)*Py)*(Ax - Bx) + (Ay*Bx - Ax*By)*(Ay - By) y = -(Ay*Bx - Ax*By)*(Ax - Bx) + ((Ax - Bx)*Px + (Ay - By)*Py)*(Ay - By) z = (Ax - Bx)^2 + (Ay - By)^2 

正如你注意到的,有很多重复的术语。 为这些发明(几乎任意)的名字,你可以得到下面的最终结果,用伪代码写成:

 dx = Ax - Bx dy = Ay - By det = Ay*Bx - Ax*By dot = dx*Px + dy*Py x = dot*dx + det*dy y = dot*dy - det*dx z = dx*dx + dy*dy zinv = 1/z return new Point(x*zinv, y*zinv) 

这种方法的好处:

  • 没有区别
  • 没有平方根
  • 只有一个部门

最近的点C将在一条斜线是AB的倒数并与P相交的线上。 这听起来像是作业,但是我会给出一些非常有力的提示,按照越来越剧透的级别:

  • 只能有一条这样的路线。

  • 这是一个两线方程的系统。 只需解决xy

  • AB之间绘制一条线段; 称这个L L的等式是y = mx + b ,其中m是y坐标与x坐标的比率。 在expression式中使用AB来求解b

  • 像上面那样做,但对于CP 。 现在求解联立线性方程组。

  • 谷歌search会给你一个可供select的例子

我很久以前写了这个,和其他人说的没什么两样,但是如果你有一个名为PointF的类(或结构体),并且成员为XY ,那么它就是C#中的复制/粘贴解决scheme:

 private static PointF ClosestPointToSegment(PointF P, PointF A, PointF B) { PointF a_to_p = new PointF(), a_to_b = new PointF(); a_to_p.X = PX - AX; a_to_p.Y = PY - AY; // # Storing vector A->P a_to_b.X = BX - AX; a_to_b.Y = BY - AY; // # Storing vector A->B float atb2 = a_to_b.X * a_to_b.X + a_to_b.Y * a_to_b.Y; float atp_dot_atb = a_to_p.X * a_to_b.X + a_to_p.Y * a_to_b.Y; // The dot product of a_to_p and a_to_b float t = atp_dot_atb / atb2; // # The normalized "distance" from a to the closest point return new PointF(AX + a_to_b.X * t, AY + a_to_b.Y * t); } 

更新 :看看这些评论,看起来像我接受的答案中提到的相同的源代码适应C#。

这里有扩展方法应该做的伎俩:

 public static double DistanceTo(this Point from, Point to) { return Math.Sqrt(Math.Pow(from.X - to.X, 2) + Math.Pow(from.Y - to.Y, 2)); } public static double DistanceTo(this Point point, Point lineStart, Point lineEnd) { double tI = ((lineEnd.X - lineStart.X) * (point.X - lineStart.X) + (lineEnd.Y - lineStart.Y) * (point.Y - lineStart.Y)) / Math.Pow(lineStart.DistanceTo(lineEnd), 2); double dP = ((lineEnd.X - lineStart.X) * (point.Y - lineStart.Y) - (lineEnd.Y - lineStart.Y) * (point.X - lineStart.X)) / lineStart.DistanceTo(lineEnd); if (tI >= 0d && tI <= 1d) return Math.Abs(dP); else return Math.Min(point.DistanceTo(lineStart), point.DistanceTo(lineEnd)); } 

然后,只需拨打:

 P.DistanceTo(A, B); 

要从线| AB |获得点“P”的距离。 PointF修改应该很容易。

find最接近的点就是search最小距离的问题。 LINQ有这方面的方法。

如果有人正在寻找一种方法来使用Java + LibGdx:

 Intersector.nearestSegmentPoint 

该algorithm会很容易:

你有3分 – 三angular形。 从那里你应该能够findAB,AC,BC。

了解这一点: http : //www.topcoder.com/tc? d1=tutorials&d2=geometry1&module=Static#line_point_distance