我希望有一个直接的C#函数来获得最近点(从点P)到线段AB。抽象函数可能如下所示。我搜索了SO,但没有找到可用的(由我)解决方案。
public Point getClosestPointFromLine(Point A, Point B, Point P);
答案 0 :(得分:36)
这里的Ruby伪装成伪代码,假设Point
个对象都有x
和y
字段。
def GetClosestPoint(A, B, P)
a_to_p = [P.x - A.x, P.y - A.y] # Storing vector A->P
a_to_b = [B.x - A.x, B.y - A.y] # 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 => A.x + a_to_b[0]*t,
:y => A.y + a_to_b[1]*t )
# Add the distance to A, moving
# towards B
end
可替换地:
来自维基百科的Line-Line Intersection。首先,找到Q,这是从“正确的方向”P开始迈出的第二个要点。这给了我们四点。
def getClosestPointFromLine(A, B, P)
a_to_b = [B.x - A.x, B.y - A.y] # 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 => P.x + perpendicular[0], :y => P.y + 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 => ((A.x*B.y - A.y*B.x)*(P.x - Q.x) - (A.x-B.x)*(P.x*Q.y - P.y*Q.x)) / ((A.x - B.x)*(P.y-Q.y) - (A.y - B.y)*(P.y-Q.y)),
:y => ((A.x*B.y - A.y*B.x)*(P.y - Q.y) - (A.y-B.y)*(P.x*Q.y - P.y*Q.x)) / ((A.x - B.x)*(P.y-Q.y) - (A.y - B.y)*(P.y-Q.y)) )
end
出于性能原因,可以使用缓存,跳过步骤等。
答案 1 :(得分:30)
如果有人对基于上述内容的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;
}
}
答案 2 :(得分:10)
您的观点(X
)将是点A
和B
的线性组合:
X = k A + (1-k) B
要使X
实际上在线段上,参数k
必须介于0和1之间(包括0和1)。您可以按如下方式计算k:
k_raw = (P-B).(A-B) / (A-B).(A-B)
(其中句点表示点积)
然后,确保该点实际上在线段上:
if k_raw < 0:
k= 0
elif k_raw > 1:
k= 1
else:
k= k_raw
答案 3 :(得分:6)
Justin L.的答案几乎没问题,但它没有检查归一化距离是否小于0或高于AB矢量幅度。然后当P矢量提取超出界限(来自线段AB)时,它将不能很好地工作。 这是更正后的伪代码:
function GetClosestPoint(A, B, P)
{
vectorAP = (p.x - a.x, p.y - a.y) //Vector from A to P
vectorAB = (b.x - a.x, b.y - a.y) //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 = a.x
returnPoint.y = a.y
}
else if (distance > magnitudeAB)
{
returnPoint.x = b.x
returnPoint.y = b.y
}
else
{
returnPoint.x = a.x + vectorAB[0]*distance
returnPoint.y = a.y + vectorAB[1]*distance
}
}
答案 4 :(得分:4)
我很久以前写过这篇文章,它与其他人所说的没什么不同,但如果你有一个名为{{1的类(或结构),它就是C#中的复制/粘贴解决方案成员PointF
和X
:
Y
更新:查看评论看起来我已根据接受的答案中提到的相同源代码将其改编为C#。
答案 5 :(得分:3)
通过将y差除以x差来求出AB的斜率a1;然后绘制一条垂直线(斜率a2 = -1 / a1,你需要通过将P坐标放入y = a2 * x + b2来求解偏移量(b2));然后你有两条线(即两个线性方程),你需要解决交叉点。这将是你最接近的一点。
正确的数学运算,编写函数非常简单。
详细说明一下:
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 <--
希望我没有在某处搞砸:) 更新当然我做到了。为我做正确的事,因为他没有先在纸上解决问题。我应该得到所有的支持,但我希望有人能够纠正我。固定(我希望)。
箭头指明了方向。
更新啊,角落的情况。是的,有些语言不能很好地处理无穷大。我确实说解决方案没有语言......
您可以查看特殊情况,它们非常简单。第一个是x差为0时。这意味着线是垂直的,最近的点是水平垂线。因此,x = Ax, y = Px
。
第二个是y差为0时,反之亦然。因此,x = Px, y = Ay
答案 6 :(得分:3)
这个答案是基于投影几何的想法。
计算叉积(Ax,Ay,1)×(Bx,By,1)=(u,v,w)。得到的矢量描述连接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)。
把所有东西放在一起,你得到以下公式:
使用计算机代数系统,您可以找到以下结果坐标:
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 = A.x - B.x
dy = A.y - B.y
det = A.y*B.x - A.x*B.y
dot = dx*P.x + dy*P.y
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)
这种方法的好处:
答案 7 :(得分:1)
最近的点C
将在一条线上,该线的斜率是AB的倒数并且与P
相交。听起来这可能是家庭作业,但我会给出一些非常强烈的提示,以增加剧透警报级别:
只能有一条这样的线。
这是一个由两个线方程组成的系统。只需解决x
和y
。
在A
和B
之间绘制一条线段;请拨打此L
。 L
的等式为y = mx + b
,其中m
是y坐标与x坐标的比率。使用表达式中的b
或A
解析B
。
执行与上述相同的操作,但适用于CP
。现在求解同时线性方程组。
Google搜索会为您提供 a bevy of examples 供您选择。
答案 8 :(得分:1)
以下是可以解决这个问题的扩展方法:
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
修改此内容。
找到最近的点只是搜索最小距离的问题。 LINQ
有相应的方法。
答案 9 :(得分:0)
如果有人正在寻找使用Java + LibGdx执行此操作的方法:
Intersector.nearestSegmentPoint
答案 10 :(得分:0)
这是从某个点(已测试)(vb.net)获取线段最近点的正确算法
s2 = ClosestPointToSegment(point_x, Point_y, Segment_start_x, Segment_start_y, Segment_end_X, Segment_end_Y)
Public Shared Function DistanceTo(x1 As Double, y1 As Double, x2 As Double, y2 As Double) As Double
Return Math.Sqrt(Math.Pow(x1 - x2, 2) + Math.Pow(y1 - y2, 2))
End Function
Public Shared Function DistanceTo(point_x As Double, point_y As Double, lineStart_x As Double, lineStart_y As Double, lineEnd_x As Double, lineEnd_y As Double) As Double
Dim tI As Double = ((lineEnd_x - lineStart_x) * (point_x - lineStart_x) + (lineEnd_y - lineStart_y) * (point_y - lineStart_x)) / Math.Pow(DistanceTo(lineStart_x, lineStart_y, lineEnd_x, lineEnd_y), 2)
Dim dP As Double = ((lineEnd_x - lineStart_x) * (point_y - lineStart_y) - (lineEnd_y - lineStart_y) * (point_x - lineStart_x)) / DistanceTo(lineStart_x, lineStart_y, lineEnd_x, lineEnd_y)
If tI >= 0R AndAlso tI <= 1.0R Then
Return Math.Abs(dP)
Else
Return Math.Min(DistanceTo(point_x, point_y, lineStart_x, lineStart_y), DistanceTo(point_x, point_y, lineEnd_x, lineEnd_y))
End If
End Function
Private Shared Function ClosestPointToSegment(P_x As Double, p_y As Double, A_x As Double, a_y As Double, B_x As Double, b_y As Double) As Double()
Dim a_to_p As PointF = New PointF(), a_to_b As PointF = New PointF()
Dim rikthex As Double, rikthey As Double
Dim s1(1) As Double
Dim p1_v1_X As Double, p1_v1_y As Double, distanca1 As Double, distanca2 As Double
a_to_p.X = P_x - A_x
a_to_p.Y = p_y - a_y
a_to_b.X = B_x - A_x
a_to_b.Y = b_y - a_y
Dim atb2 As Single = a_to_b.X * a_to_b.X + a_to_b.Y * a_to_b.Y
Dim atp_dot_atb As Single = a_to_p.X * a_to_b.X + a_to_p.Y * a_to_b.Y
Dim t As Single = atp_dot_atb / atb2
rikthex = A_x + a_to_b.X * t
rikthey = a_y + a_to_b.Y * t
If A_x > B_x Then
If rikthex < A_x And rikthex > B_x Then 'pika duhet ne rregulll
If a_y > b_y Then
If rikthey < a_y And rikthey > b_y Then 'pika duhet ne rregulll
Else
distanca1 = DistanceTo(P_x, p_y, A_x, a_y)
distanca2 = DistanceTo(P_x, p_y, B_x, b_y)
If distanca1 < distanca2 Then
rikthex = A_x
rikthey = a_y
Else
rikthex = B_x
rikthey = b_y
End If
End If
Else
If rikthey > a_y And rikthey < b_y Then 'pika duhet ne rregulll
Else
distanca1 = DistanceTo(P_x, p_y, A_x, a_y)
distanca2 = DistanceTo(P_x, p_y, B_x, b_y)
If distanca1 < distanca2 Then
rikthex = A_x
rikthey = a_y
Else
rikthex = B_x
rikthey = b_y
End If
End If
End If
Else
distanca1 = DistanceTo(P_x, p_y, A_x, a_y)
distanca2 = DistanceTo(P_x, p_y, B_x, b_y)
If distanca1 < distanca2 Then
rikthex = A_x
rikthey = a_y
Else
rikthex = B_x
rikthey = b_y
End If
End If
Else
If rikthex > A_x And rikthex < B_x Then 'pika duhet ne rregulll
If a_y > b_y Then
If rikthey < a_y And rikthey > b_y Then 'pika duhet ne rregulll
Else
distanca1 = DistanceTo(P_x, p_y, A_x, a_y)
distanca2 = DistanceTo(P_x, p_y, B_x, b_y)
If distanca1 < distanca2 Then
rikthex = A_x
rikthey = a_y
Else
rikthex = B_x
rikthey = b_y
End If
End If
Else
If rikthey > a_y And rikthey < b_y Then 'pika duhet ne rregulll
Else
distanca1 = DistanceTo(P_x, p_y, A_x, a_y)
distanca2 = DistanceTo(P_x, p_y, B_x, b_y)
If distanca1 < distanca2 Then
rikthex = A_x
rikthey = a_y
Else
rikthex = B_x
rikthey = b_y
End If
End If
End If
Else
distanca1 = DistanceTo(P_x, p_y, A_x, a_y)
distanca2 = DistanceTo(P_x, p_y, B_x, b_y)
If distanca1 < distanca2 Then
rikthex = A_x
rikthey = a_y
Else
rikthex = B_x
rikthey = b_y
End If
End If
End If
s1(0) = rikthex
s1(1) = rikthey
Return s1
End Function
答案 11 :(得分:0)
如果有人在寻找 python 实现,这里是代码:
p1 和 p2 是线,p3 是点
def p4(p1, p2, p3):
x1, y1 = p1
x2, y2 = p2
x3, y3 = p3
dx, dy = x2-x1, y2-y1
det = dx*dx + dy*dy
a = (dy*(y3-y1)+dx*(x3-x1))/det
x= x1+a*dx, y1+a*dy
# print(x)
if x[0]<x1 or x[1]<y1:
return p1
elif x[0]>x2 or x[1]>y2:
return p2
else:
return x
这是从另一个线程中提取的并稍作修改。 Python: point on a line closest to third point
答案 12 :(得分:-3)
算法非常简单:
你有3分 - 三角形。从那里你应该能够找到AB,AC,BC。问这个: http://www.topcoder.com/tc?d1=tutorials&d2=geometry1&module=Static#line_point_distance