0

我在圆内有一个点,在圆外有另一个点。我想找到线与圆相交的点。我怎么能在 Windows Phone 8 中做到这一点。请给我任何想法。

4

2 回答 2

7

这是一个既简单又复杂的问题,很大程度上取决于您的意思。我从您的开篇文章中得知您在谈论线段而不是真正的(无限)线。

在这种情况下,您有几个案例需要解决。仅当一点在圆内并且一点在圆外时才会发生相交。下面的算法没有捕捉到以下情况

  1. 直线与圆相切
  2. 一个或两个点都在圆上
  3. 线相交于两点

这些被归为“无交叉”结果。这只处理严格在圆内一点和圆外一点的情况。

首先你需要一些辅助功能。这些使用基本几何来确定一个点是在圆内还是在圆外(圆上的点算作“外”),以及两点是否形成与圆相交的线段。

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");
}
于 2013-06-08T11:50:55.383 回答
0

数学上有一个小错别字。c# 代码没问题。

... r^2 - B*Cy) = 0

应该

... r^2 - 2B*Cy) = 0

还有 c = (Cx^2 + Cy^2 + B^2 - r^2 - 2B*Cy)

于 2014-06-09T13:50:13.010 回答