我在圆内有一个点,在圆外有另一个点。我想找到线与圆相交的点。我怎么能在 Windows Phone 8 中做到这一点。请给我任何想法。
2 回答
这是一个既简单又复杂的问题,很大程度上取决于您的意思。我从您的开篇文章中得知您在谈论线段而不是真正的(无限)线。
在这种情况下,您有几个案例需要解决。仅当一点在圆内并且一点在圆外时才会发生相交。下面的算法没有捕捉到以下情况
- 直线与圆相切
- 一个或两个点都在圆上
- 线相交于两点
这些被归为“无交叉”结果。这只处理严格在圆内一点和圆外一点的情况。
首先你需要一些辅助功能。这些使用基本几何来确定一个点是在圆内还是在圆外(圆上的点算作“外”),以及两点是否形成与圆相交的线段。
private bool IsInsideCircle(Point CirclePos, float CircleRad, Point checkPoint)
{
if (Math.Sqrt(Math.Pow((CirclePos.X - checkPoint.X), 2) +
Math.Pow((CirclePos.Y - checkPoint.Y), 2)) < CircleRad)
{ return true; } else return false;
}
private bool IsIntersecting(Point CirclePos, float CircleRad, Point LineStart,
Point LineEnd)
{
if (IsInsideCircle(CirclePos, CircleRad, LineStart) ^
IsInsideCircle(CirclePos, CircleRad, LineEnd))
{ return true; } else return false;
}
注意^
(Exclusive OR) 的使用——我们想要一个点在里面,一个点在外面。
有了这个,我们可以使用更大的功能:
private int Intersect (Point CirclePos, float CircleRad,
Point LineStart, Point LineEnd, ref Point Intersection)
{
if (IsIntersecting(CirclePos, CircleRad, LineStart, LineEnd))
{
//Calculate terms of the linear and quadratic equations
var M = (LineEnd.Y - LineStart.Y) / (LineEnd.X - LineStart.X);
var B = LineStart.Y - M * LineStart.X;
var a = 1 + M*M;
var b = 2 * (M*B - M*CirclePos.Y - CirclePos.X);
var c = CirclePos.X * CirclePos.X + B * B + CirclePos.Y * CirclePos.Y -
CircleRad * CircleRad - 2 * B * CirclePos.Y;
// solve quadratic equation
var sqRtTerm = Math.Sqrt(b * b - 4 * a * c);
var x = ((-b) + sqRtTerm)/(2*a);
// make sure we have the correct root for our line segment
if ((x < Math.Min(LineStart.X, LineEnd.X) ||
(x > Math.Max(LineStart.X, LineEnd.X))))
{ x = ((-b) - sqRtTerm) / (2 * a); }
//solve for the y-component
var y = M * x + B;
// Intersection Calculated
Intersection = new Point(x, y);
return 0;
} else {
// Line segment does not intersect at one point. It is either
// fully outside, fully inside, intersects at two points, is
// tangential to, or one or more points is exactly on the
// circle radius.
Intersection = new Point(0, 0);
return -1;
}
}
此函数将交点作为ref
参数并返回-1
(无交点)或0
(找到交点)。int
如果您想扩展它以区分边缘情况,我使用了返回值。交点是根据基本几何计算的 - 请记住一条线表示为(请参阅:斜率截距和点斜率形式)
- y = M*x + B
和一个圆(以(C.x, C.y)
半径为中心r
)是
- (x - Cx)^2 + (y - Cy)^2 - r^2 = 0
你通过代入求解这个方程组:
- (x - Cx)^2 + ((M*x + B) - Cy)^2 - r^2 = 0
扩展和收集您获得的术语:
- (1+M^2)x^2 + 2*(M B - M C.y - Cx)x + (Cx^2 + Cy^2 + B^2 - r^2 - 2B*Cy) = 0
这是形式的标准二次方程
- ax^2 + bx + c = 0
- 在哪里 :
- a = (1+M^2)
- b = 2*(M B - M C.y - Cx)
- c = (Cx^2 + Cy^2 + B^2 - r^2 - 2B*Cy)
可以通过二次公式求解(参见:二次公式):
- x = (-b ± Sqrt(b^2 - 4ac))/(2a)
这为我们的线段所在的无限线提供了两个根 - 我们在上面进行最后检查以确保我们为特定线段选择解决方案。
这就是为什么在数学课上专心很重要!
现在......取决于你在做什么,有很多方法可以优化它。上述解决方案概述了基本方法,但如果您需要,当然可以更快地完成此操作。显然,也可以将参数调整为您使用的任何类型的点或对象。我试图使它尽可能通用。
此外,为了展示如何调用它的示例:
Point iPt = new Point();
var rslt = Intersect(new Point(2,3), 5.0f, new Point(2,2),
new Point(8,6), ref iPt);
if (rslt == 0) {
MessageBox.Show(String.Format("Intersection at: x = {0}, y = {1}",
iPt.X, iPt.Y));
}
else {
MessageBox.Show("No Intersection");
}
数学上有一个小错别字。c# 代码没问题。
... r^2 - B*Cy) = 0
应该
... r^2 - 2B*Cy) = 0
还有 c = (Cx^2 + Cy^2 + B^2 - r^2 - 2B*Cy)