C# 获取离直线最近的点
我想要一个直接的C#函数来得到一个最近的点(从点p)到线段AB。一个抽象函数可能看起来像这样。我已经搜索了这么多,但没有找到一个可用的(由我)解决方案C# 获取离直线最近的点,c#,math,geometry,C#,Math,Geometry,我想要一个直接的C#函数来得到一个最近的点(从点p)到线段AB。一个抽象函数可能看起来像这样。我已经搜索了这么多,但没有找到一个可用的(由我)解决方案 public Point getClosestPointFromLine(Point A, Point B, Point P); 算法将非常简单: 你有3个点-三角形。从那里你应该可以找到AB,AC,BC 把这件事说出来: 通过将y差除以x差,求出AB的斜率a1;然后画一条垂直线(坡度为a2=-1/a1,需要通过将P的坐标转换为y=a2*x+b
public Point getClosestPointFromLine(Point A, Point B, Point P);
算法将非常简单: 你有3个点-三角形。从那里你应该可以找到AB,AC,BC 把这件事说出来:
通过将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 <--
原始行:
y=a1*x+b1
a1=(By-Ay)/(Bx-Ax)最近的点C
将位于斜率为AB倒数且与p
相交的直线上。这听起来像是家庭作业,但为了提高扰流板警戒级别,我将给出一些非常有力的提示:
- 这样的线路只能有一条
- 这是一个双线方程组。只需求解
x
和y
- 在
a
和B
之间画一条线段;称之为L
。L
的方程式为y=mx+b
,其中m
是y坐标与x坐标的比值。在表达式中使用A
或b
求解b
- 执行与上面相同的操作,但对于
CP
。现在求解联立线性方程组
- 谷歌搜索将为您提供选择
您的点(
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
如果k_原始值<0:
k=0
elif k_raw>1:
k=1
其他:
k=k_原始
这里的Ruby伪装成伪代码,假设点
对象都有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
或者:
来自维基百科。首先,找到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
出于性能原因,可以进行缓存、跳过步骤等操作。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
}
}
函数GetClosestPoint(A、B、P)
{
vectorAP=(p.x-a.x,p.y-a.y)//从a到p的向量
vectorAB=(b.x-a.x,b.y-a.y)//从a到b的向量
震级AB=向量AB[0]^2+向量AB[1]^2
//AB向量的大小(其长度)
ABAPproduct=vectorAB[0]*vectorAP[0]+vectorAB[1]*vectorAP[1]
//a_到p和a_到b的乘积
距离=ABAppProduct/magnitudeAB
//从a到最近点的标准化“距离”
if(距离<0)//检查P投影是否在向量ab上
{
returnPoint.x=a.x
returnPoint.y=a.y
}
否则如果(距离>震级AB)
{
returnPoint.x=b.x
返回点y=b.y
}
其他的
{
returnPoint.x=a.x+vectorAB[0]*距离
returnPoint.y=a.y+vectorAB[1]*距离
}
}
如果有人对基于上述内容的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 GetClosestPointOnLineSegment(向量2 A、向量2 B、向量2 P)
{
Vector2ap=P-A;//从A到P的向量
Vector2ab=B-A;//从A到B的向量
float magnityAB=AB.LengthSquared();//AB向量的大小(它的长度平方)
float ABAPproduct=Vector2.Dot(AP,AB);//a_到p和a_到b的点积
float distance=ABAPproduct/magnificatab;//从a到最近点的标准化“距离”
if(距离<0)//检查P投影是否在向量ab上
{
返回A;
}
否则,如果(距离>1){
返回B;
}
其他的
{
返回A+AB*距离;
}
}
这个答案基于射影几何的思想
计算叉积(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-Bx)*Py)*(Ay-By)
z=(Ax-Bx)^2+(Ay-By)^2
正如您所注意到的,有很多重复出现的术语。为这些发明(几乎任意)名称,您可以得到以下最终结果,使用伪代码编写:
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
}
}
dx=A.x-B.x
dy=A.y-B.y
det=A.y*B.x-A.x*B.y
点=dx*P.x
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 = P.X - A.X;
a_to_p.Y = P.Y - A.Y; // # Storing vector A->P
a_to_b.X = B.X - A.X;
a_to_b.Y = B.Y - A.Y; // # 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(A.X + a_to_b.X * t, A.Y + a_to_b.Y * t);
}
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);
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