我有一条从a到B的直线和一个半径为R的圆。

用什么算法来检查直线是否与圆相交?它在圆边的哪个坐标上?


当前回答

虽然我认为使用线圆交点,然后检查交点是否在端点之间更好,可能更便宜,但我想添加这个更直观的解决方案。

我喜欢把这个问题想象成“香肠上的点问题”,在不改变算法的情况下,它可以在任何维度上工作。 这个解找不到交点。

以下是我想到的:

(我使用“小于”,但“小于或等于”也可以使用,这取决于我们测试的内容。)

确保Circle_Point小于到无限线的半径距离。(这里使用最喜欢的方法)。 计算从两个Segment_Points到Circle_Point的距离。 测试较大的Circle_Point-Segment_Point距离是否小于根号(Segment_Length^2+Radius^2)。 (这是从一个分段点到一个理论点的距离,也就是从另一个分段点到无限线(直角)的半径距离。见图片)。

3 t。如果为true: Circle_Point在sausage内部。 3 f。如果为false:如果较小的Circle_Point- segment_point距离小于Radius,则Circle_Point在sausage内部。

图片:最粗的线段是选定的线段,没有示例圆。有点粗糙,有些像素有点不对。

function boolean pointInSausage(sp1,sp2,r,c) {
  if ( !(pointLineDist(c,sp1,sp2) < r) ) {
    return false;
  }
  double a = dist(sp1,c);  
  double b = dist(sp2,c);
  double l;
  double s;
  if (a>b) {
    l = a;
    s = b;
  } else {
    l = b;
    s = a;
  }
  double segLength = dist(sp1,sp2);
  if ( l < sqrt(segLength*segLength+r*r) ) {
    return true;
  }
  return s < r;
}  

如果发现任何问题,告诉我,我会编辑或撤回。

其他回答

虽然我认为使用线圆交点,然后检查交点是否在端点之间更好,可能更便宜,但我想添加这个更直观的解决方案。

我喜欢把这个问题想象成“香肠上的点问题”,在不改变算法的情况下,它可以在任何维度上工作。 这个解找不到交点。

以下是我想到的:

(我使用“小于”,但“小于或等于”也可以使用,这取决于我们测试的内容。)

确保Circle_Point小于到无限线的半径距离。(这里使用最喜欢的方法)。 计算从两个Segment_Points到Circle_Point的距离。 测试较大的Circle_Point-Segment_Point距离是否小于根号(Segment_Length^2+Radius^2)。 (这是从一个分段点到一个理论点的距离,也就是从另一个分段点到无限线(直角)的半径距离。见图片)。

3 t。如果为true: Circle_Point在sausage内部。 3 f。如果为false:如果较小的Circle_Point- segment_point距离小于Radius,则Circle_Point在sausage内部。

图片:最粗的线段是选定的线段,没有示例圆。有点粗糙,有些像素有点不对。

function boolean pointInSausage(sp1,sp2,r,c) {
  if ( !(pointLineDist(c,sp1,sp2) < r) ) {
    return false;
  }
  double a = dist(sp1,c);  
  double b = dist(sp2,c);
  double l;
  double s;
  if (a>b) {
    l = a;
    s = b;
  } else {
    l = b;
    s = a;
  }
  double segLength = dist(sp1,sp2);
  if ( l < sqrt(segLength*segLength+r*r) ) {
    return true;
  }
  return s < r;
}  

如果发现任何问题,告诉我,我会编辑或撤回。

如果你找到了圆心(因为它是3D的,我想你是指球体而不是圆)和直线之间的距离,然后检查这个距离是否小于可以做到这一点的半径。

碰撞点显然是直线和球面之间最近的点(当你计算球面和直线之间的距离时,会计算出这个点)

点与线之间的距离: http://mathworld.wolfram.com/Point-LineDistance3-Dimensional.html

这个Java函数返回一个DVec2对象。它用DVec2表示圆心,用DVec2表示半径,用Line表示直线。

public static DVec2 CircLine(DVec2 C, double r, Line line)
{
    DVec2 A = line.p1;
    DVec2 B = line.p2;
    DVec2 P;
    DVec2 AC = new DVec2( C );
    AC.sub(A);
    DVec2 AB = new DVec2( B );
    AB.sub(A);
    double ab2 = AB.dot(AB);
    double acab = AC.dot(AB);
    double t = acab / ab2;

    if (t < 0.0) 
        t = 0.0;
    else if (t > 1.0) 
        t = 1.0;

    //P = A + t * AB;
    P = new DVec2( AB );
    P.mul( t );
    P.add( A );

    DVec2 H = new DVec2( P );
    H.sub( C );
    double h2 = H.dot(H);
    double r2 = r * r;

    if(h2 > r2) 
        return null;
    else
        return P;
}

在此post circle中,通过检查圆心与线段上的点(Ipoint)之间的距离来检查线碰撞,该点表示从圆心到线段的法线N(图2)之间的交点。

(https://i.stack.imgur.com/3o6do.png)

在图像1中显示一个圆和一条直线,向量A指向线的起点,向量B指向线的终点,向量C指向圆的中心。现在我们必须找到向量E(从线起点到圆中心)和向量D(从线起点到线终点)这个计算如图1所示。

(https://i.stack.imgur.com/7098a.png)

在图2中,我们可以看到向量E通过向量E与单位向量D的“点积”投影到向量D上,点积的结果是标量Xp,表示向量N与向量D的直线起点与交点(Ipoint)之间的距离。 下一个向量X是由单位向量D和标量Xp相乘得到的。

现在我们需要找到向量Z(向量到Ipoint),它很容易它简单的向量加法向量A(在直线上的起点)和向量x。接下来我们需要处理特殊情况,我们必须检查是Ipoint在线段上,如果不是我们必须找出它是它的左边还是右边,我们将使用向量最接近来确定哪个点最接近圆。

(https://i.stack.imgur.com/p9WIr.png)

当投影Xp为负时,Ipoint在线段的左边,距离最近的向量等于线起点的向量,当投影Xp大于向量D的模时,距离最近的向量在线段的右边,距离最近的向量等于线终点的向量在其他情况下,距离最近的向量等于向量Z。

现在,当我们有最近的向量,我们需要找到从圆中心到Ipoint的向量(dist向量),很简单,我们只需要从中心向量减去最近的向量。接下来,检查向量距离的大小是否小于圆半径,如果是,那么它们就会碰撞,如果不是,就没有碰撞。

(https://i.stack.imgur.com/QJ63q.png)

最后,我们可以返回一些值来解决碰撞,最简单的方法是返回碰撞的重叠(从矢量dist magnitude中减去半径)和碰撞的轴,它的向量d。如果需要,交点是向量Z。

You can find a point on a infinite line that is nearest to circle center by projecting vector AC onto vector AB. Calculate the distance between that point and circle center. If it is greater that R, there is no intersection. If the distance is equal to R, line is a tangent of the circle and the point nearest to circle center is actually the intersection point. If distance less that R, then there are 2 intersection points. They lie at the same distance from the point nearest to circle center. That distance can easily be calculated using Pythagorean theorem. Here's algorithm in pseudocode:

{
dX = bX - aX;
dY = bY - aY;
if ((dX == 0) && (dY == 0))
  {
  // A and B are the same points, no way to calculate intersection
  return;
  }

dl = (dX * dX + dY * dY);
t = ((cX - aX) * dX + (cY - aY) * dY) / dl;

// point on a line nearest to circle center
nearestX = aX + t * dX;
nearestY = aY + t * dY;

dist = point_dist(nearestX, nearestY, cX, cY);

if (dist == R)
  {
  // line segment touches circle; one intersection point
  iX = nearestX;
  iY = nearestY;

  if (t < 0 || t > 1)
    {
    // intersection point is not actually within line segment
    }
  }
else if (dist < R)
  {
  // two possible intersection points

  dt = sqrt(R * R - dist * dist) / sqrt(dl);

  // intersection point nearest to A
  t1 = t - dt;
  i1X = aX + t1 * dX;
  i1Y = aY + t1 * dY;
  if (t1 < 0 || t1 > 1)
    {
    // intersection point is not actually within line segment
    }

  // intersection point farthest from A
  t2 = t + dt;
  i2X = aX + t2 * dX;
  i2Y = aY + t2 * dY;
  if (t2 < 0 || t2 > 1)
    {
    // intersection point is not actually within line segment
    }
  }
else
  {
  // no intersection
  }
}

编辑:增加了代码来检查所找到的交点是否实际上在线段内。