首页 文章

圆线段碰撞检测算法?

提问于
浏览
177

我有一条从A到B的线和一条位于C的圆,半径为R.

Image

什么是用于检查线是否与圆相交的好算法?它沿圆圈边缘的坐标发生了什么?

23 回答

  • 3

    • E 是光线的起点,

    • L 是光线的终点,

    • C 是您正在测试的球体的中心

    • r 是该球体的半径

    计算:
    d = L - E(射线的方向矢量,从开始到结束)
    f = E - C(从中心球到射线开始的矢量)

    然后通过..找到交叉点 .
    堵:
    P = E + t * d
    这是一个参数方程:
    Px = Ex tdx
    Py = Ey tdy

    (x - h)2 + (y - k)2 = r2
    (h,k)=圆心 .

    注意:我们在这里将问题简化为2D,我们得到的解决方案也适用于3D

    to get:

    • Expand
      x2 - 2xh h2 y2 - 2yk k2 - r2 = 0

    • Plug
      x = ex tdx
      y = ey tdy
      (ex tdx)2 - 2(ex tdx)h h2(ey tdy)2 - 2(ey tdy)k k2 - r2 = 0

    • 爆炸
      ex2 2extdx t2dx2 - 2exh - 2tdxh h2 ey2 2eytdy t2dy2 - 2eyk - 2tdyk k2 - r2 = 0

    • Group
      t2(dx2 dy2)2t(exdx eydy - dxh - dyk)ex2 ey2 - 2exh - 2eyk h2 k2 - r2 = 0

    • Finally,
      t2(_d * _d)2t(_e * _d - _d * _c)_e * _e - 2(_e * _c)_c * _c - r2 = 0
      *其中_d是向量d,*是点积 . *

    • And then,
      t2(_d * _d)2t(d *( e - c))( e - c)*( e - _c) - r2 = 0

    • Letting _f = _e - _c
      t2(_d * _d)2t(_d * _f)_f * _f - r2 = 0

    所以我们得到:
    t2 * (d DOT d) + 2t( f DOT d ) + ( f DOT f - r2 ) = 0*
    所以求解二次方程:

    float a = d.Dot( d ) ;
    float b = 2*f.Dot( d ) ;
    float c = f.Dot( f ) - r*r ;
    
    float discriminant = b*b-4*a*c;
    if( discriminant < 0 )
    {
      // no intersection
    }
    else
    {
      // ray didn't totally miss sphere,
      // so there is a solution to
      // the equation.
    
      discriminant = sqrt( discriminant );
    
      // either solution may be on or off the ray so need to test both
      // t1 is always the smaller value, because BOTH discriminant and
      // a are nonnegative.
      float t1 = (-b - discriminant)/(2*a);
      float t2 = (-b + discriminant)/(2*a);
    
      // 3x HIT cases:
      //          -o->             --|-->  |            |  --|->
      // Impale(t1 hit,t2 hit), Poke(t1 hit,t2>1), ExitWound(t1<0, t2 hit), 
    
      // 3x MISS cases:
      //       ->  o                     o ->              | -> |
      // FallShort (t1>1,t2>1), Past (t1<0,t2<0), CompletelyInside(t1<0, t2>1)
    
      if( t1 >= 0 && t1 <= 1 )
      {
        // t1 is the intersection, and it's closer than t2
        // (since t1 uses -b - discriminant)
        // Impale, Poke
        return true ;
      }
    
      // here t1 didn't intersect so we are either started
      // inside the sphere or completely past it
      if( t2 >= 0 && t2 <= 1 )
      {
        // ExitWound
        return true ;
      }
    
      // no intn: FallShort, Past, CompletelyInside
      return false ;
    }
    
  • 181

    似乎没有人考虑投影,我完全偏离这里吗?

    将矢量 AC 投影到 AB . 投影向量 AD 给出新点 D .
    如果 DC 之间的距离小于(或等于) R ,则我们有一个交点 .

    像这样:

    Image by SchoolBoy

  • 3

    我会用这个算法计算一个点(圆心)和一个线(AB线)之间的距离 . 然后,这可以用于确定线与圆的交点 .

    假设我们有点A,B,C . Ax和Ay是A点的x和y分量 . B和C相同 . 标量R是圆半径 .

    该算法要求A,B和C是不同的点,并且R不是0 .

    这是算法

    // compute the euclidean distance between A and B
    LAB = sqrt( (Bx-Ax)²+(By-Ay)² )
    
    // compute the direction vector D from A to B
    Dx = (Bx-Ax)/LAB
    Dy = (By-Ay)/LAB
    
    // the equation of the line AB is x = Dx*t + Ax, y = Dy*t + Ay with 0 <= t <= LAB.
    
    // compute the distance between the points A and E, where
    // E is the point of AB closest the circle center (Cx, Cy)
    t = Dx*(Cx-Ax) + Dy*(Cy-Ay)    
    
    // compute the coordinates of the point E
    Ex = t*Dx+Ax
    Ey = t*Dy+Ay
    
    // compute the euclidean distance between E and C
    LEC = sqrt((Ex-Cx)²+(Ey-Cy)²)
    
    // test if the line intersects the circle
    if( LEC < R )
    {
        // compute distance from t to circle intersection point
        dt = sqrt( R² - LEC²)
    
        // compute first intersection point
        Fx = (t-dt)*Dx + Ax
        Fy = (t-dt)*Dy + Ay
    
        // compute second intersection point
        Gx = (t+dt)*Dx + Ax
        Gy = (t+dt)*Dy + Ay
    }
    
    // else test if the line is tangent to circle
    else if( LEC == R )
        // tangent point to circle is E
    
    else
        // line doesn't touch circle
    
  • 2

    好的,我不会给你代码,但既然你已经标记了这个algorithm,我认为这对你不重要 . 首先,您必须得到垂直于线的矢量 .

    你将在 y = ax + c ( c 中有一个未知变量 )
    要解决这个问题,请在线穿过圆心时计算它的值 .

    那是,
    将圆心的位置插入线方程并求解 c .
    然后计算原始线与其法线的交点 .

    这将为您提供圆圈线上的最近点 .
    计算此点与圆心之间的距离(使用矢量的大小) .
    如果这小于圆的半径 - 瞧,我们有一个交叉点!

  • 2

    另一种方法使用三角形ABC区域公式 . 相交测试比投影方法更简单,更有效,但找到交点的坐标需要更多的工作 . 至少它会被延迟到需要的程度 .

    计算三角形区域的公式为:area = bh / 2

    其中b是基本长度,h是高度 . 我们选择AB段作为基础,因此h是从C,圆心到线的最短距离 .

    由于三角形区域也可以通过矢量点积计算,我们可以确定h .

    // compute the triangle area times 2 (area = area2/2)
    area2 = abs( (Bx-Ax)*(Cy-Ay) - (Cx-Ax)(By-Ay) )
    
    // compute the AB segment length
    LAB = sqrt( (Bx-Ax)² + (By-Ay)² )
    
    // compute the triangle height
    h = area2/LAB
    
    // if the line intersects the circle
    if( h < R )
    {
        ...
    }
    

    UPDATE 1 :

    您可以使用here描述的快速平方根计算来优化代码,以获得1 / LAB的良好近似值 .

    计算交叉点并不困难 . 在这里

    // compute the line AB direction vector components
    Dx = (Bx-Ax)/LAB
    Dy = (By-Ay)/LAB
    
    // compute the distance from A toward B of closest point to C
    t = Dx*(Cx-Ax) + Dy*(Cy-Ay)
    
    // t should be equal to sqrt( (Cx-Ax)² + (Cy-Ay)² - h² )
    
    // compute the intersection point distance from t
    dt = sqrt( R² - h² )
    
    // compute first intersection point coordinate
    Ex = Ax + (t-dt)*Dx
    Ey = Ay + (t-dt)*Dy
    
    // compute second intersection point coordinate
    Fx = Ax + (t+dt)*Dx
    Fy = Ay + (t+dt)*Dy
    

    如果h = R,那么线AB与圆相切并且值dt = 0且E = F.点坐标是E和F的坐标 .

    如果在您的应用程序中可能发生这种情况,则应检查A是否与B不同,并且段长度不为空 .

  • 1

    我写了一个小脚本,通过将圆的中心点投射到线上来测试交叉点 .

    vector distVector = centerPoint - projectedPoint;
    if(distVector.length() < circle.radius)
    {
        double distance = circle.radius - distVector.length();
        vector moveVector = distVector.normalize() * distance;
        circle.move(moveVector);
    }
    

    http://jsfiddle.net/ercang/ornh3594/1/

    如果您需要检查与段的碰撞,还需要考虑圆心与起点和终点的距离 .

    vector distVector = centerPoint - startPoint;
    if(distVector.length() < circle.radius)
    {
        double distance = circle.radius - distVector.length();
        vector moveVector = distVector.normalize() * distance;
        circle.move(moveVector);
    }
    

    https://jsfiddle.net/ercang/menp0991/

  • 4

    我发现这个解决方案似乎比其他一些更容易 .

    考虑:

    p1 and p2 as the points for the line, and
    c as the center point for the circle and r for the radius
    

    我将以斜率截距形式求解线的方程 . 但是,我不想用 c 作为一个点处理困难的方程式,所以我只是将坐标系移动到圆形位于 0,0

    p3 = p1 - c
    p4 = p2 - c
    

    顺便说一下,每当我从彼此减去点数时,我减去 x 然后减去 y 's, and putting them into a new point, just in case someone didn't知道 .

    无论如何,我现在用 p3p4 求解线的等式:

    m = (p4_y - p3_y) / (p4_x - p3) (the underscore is an attempt at subscript)
    y = mx + b
    y - mx = b (just put in a point for x and y, and insert the m we found)
    

    好 . 现在我需要将这些方程设置为相等 . 首先,我需要解决 x 的圆的等式

    x^2 + y^2 = r^2
    y^2 = r^2 - x^2
    y = sqrt(r^2 - x^2)
    

    然后我将它们设置为相等:

    mx + b = sqrt(r^2 - x^2)
    

    并求解二次方程( 0 = ax^2 + bx + c ):

    (mx + b)^2 = r^2 - x^2
    (mx)^2 + 2mbx + b^2 = r^2 - x^2
    0 = m^2 * x^2 + x^2 + 2mbx + b^2 - r^2
    0 = (m^2 + 1) * x^2 + 2mbx + b^2 - r^2
    

    现在我有 abc .

    a = m^2 + 1
    b = 2mb
    c = b^2 - r^2
    

    所以我把它放到二次公式中:

    (-b ± sqrt(b^2 - 4ac)) / 2a
    

    然后用值替换然后尽可能地简化:

    (-2mb ± sqrt(b^2 - 4ac)) / 2a
    (-2mb ± sqrt((-2mb)^2 - 4(m^2 + 1)(b^2 - r^2))) / 2(m^2 + 1)
    (-2mb ± sqrt(4m^2 * b^2 - 4(m^2 * b^2 - m^2 * r^2 + b^2 - r^2))) / 2m^2 + 2
    (-2mb ± sqrt(4 * (m^2 * b^2 - (m^2 * b^2 - m^2 * r^2 + b^2 - r^2))))/ 2m^2 + 2
    (-2mb ± sqrt(4 * (m^2 * b^2 - m^2 * b^2 + m^2 * r^2 - b^2 + r^2)))/ 2m^2 + 2
    (-2mb ± sqrt(4 * (m^2 * r^2 - b^2 + r^2)))/ 2m^2 + 2
    (-2mb ± sqrt(4) * sqrt(m^2 * r^2 - b^2 + r^2))/ 2m^2 + 2
    (-2mb ± 2 * sqrt(m^2 * r^2 - b^2 + r^2))/ 2m^2 + 2
    (-2mb ± 2 * sqrt(m^2 * r^2 + r^2 - b^2))/ 2m^2 + 2
    (-2mb ± 2 * sqrt(r^2 * (m^2 + 1) - b^2))/ 2m^2 + 2
    

    这几乎就是简化 . 最后,用±分离出方程式:

    (-2mb + 2 * sqrt(r^2 * (m^2 + 1) - b^2))/ 2m^2 + 2 or     
    (-2mb - 2 * sqrt(r^2 * (m^2 + 1) - b^2))/ 2m^2 + 2
    

    然后只需将这两个方程的结果插入 mx + b 中的 x 即可 . 为清楚起见,我写了一些JavaScript代码来说明如何使用它:

    function interceptOnCircle(p1,p2,c,r){
        //p1 is the first line point
        //p2 is the second line point
        //c is the circle's center
        //r is the circle's radius
    
        var p3 = {x:p1.x - c.x, y:p1.y - c.y} //shifted line points
        var p4 = {x:p2.x - c.x, y:p2.y - c.y}
    
        var m = (p4.y - p3.y) / (p4.x - p3.x); //slope of the line
        var b = p3.y - m * p3.x; //y-intercept of line
    
        var underRadical = Math.pow((Math.pow(r,2)*(Math.pow(m,2)+1)),2)-Math.pow(b,2)); //the value under the square root sign 
    
        if (underRadical < 0){
        //line completely missed
            return false;
        } else {
            var t1 = (-2*m*b+2*Math.sqrt(underRadical))/(2 * Math.pow(m,2) + 2); //one of the intercept x's
            var t2 = (-2*m*b-2*Math.sqrt(underRadical))/(2 * Math.pow(m,2) + 2); //other intercept's x
            var i1 = {x:t1,y:m*t1+b} //intercept point 1
            var i2 = {x:t2,y:m*t2+b} //intercept point 2
            return [i1,i2];
        }
    }
    

    我希望这有帮助!

    附:如果有人发现任何错误或有任何建议,请发表评论 . 我很新,欢迎所有帮助/建议 .

  • 118

    通过将矢量AC投影到矢量AB上,可以在无限直线上找到最接近圆心的点 . 计算该点与圆心之间的距离 . 如果R大于R,则没有交叉点 . 如果距离等于R,则线是圆的切线,最接近圆心的点实际上是交点 . 如果距离小于R,则有2个交叉点 . 它们与距离圆心最近的点位于相同的距离 . 使用毕达哥拉斯定理可以很容易地计算出这个距离 . 这是伪代码中的算法:

    {
    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
      }
    }
    

    编辑:添加代码以检查找到的交叉点是否实际在线段内 .

  • 17

    奇怪的是我可以回答但不是评论......我喜欢Multitaskpro的方法,即将所有东西都移动到圆圈的中心 . 不幸的是,他的代码存在两个问题 . 首先在平方根部分中,您需要移除双倍功率 . 所以不是:

    var underRadical = Math.pow((Math.pow(r,2)*(Math.pow(m,2)+1)),2)-Math.pow(b,2));

    但:

    var underRadical = Math.pow(r,2)*(Math.pow(m,2)+1)) - Math.pow(b,2);

    在最后的坐标中,他忘记了将解决方案转移回去 . 所以不是:

    var i1 = {x:t1,y:m*t1+b}

    但:

    var i1 = {x:t1+c.x, y:m*t1+b+c.y};

    整个功能变为:

    function interceptOnCircle(p1, p2, c, r) {
        //p1 is the first line point
        //p2 is the second line point
        //c is the circle's center
        //r is the circle's radius
    
        var p3 = {x:p1.x - c.x, y:p1.y - c.y}; //shifted line points
        var p4 = {x:p2.x - c.x, y:p2.y - c.y};
    
        var m = (p4.y - p3.y) / (p4.x - p3.x); //slope of the line
        var b = p3.y - m * p3.x; //y-intercept of line
    
        var underRadical = Math.pow(r,2)*Math.pow(m,2) + Math.pow(r,2) - Math.pow(b,2); //the value under the square root sign 
    
        if (underRadical < 0) {
            //line completely missed
            return false;
        } else {
            var t1 = (-m*b + Math.sqrt(underRadical))/(Math.pow(m,2) + 1); //one of the intercept x's
            var t2 = (-m*b - Math.sqrt(underRadical))/(Math.pow(m,2) + 1); //other intercept's x
            var i1 = {x:t1+c.x, y:m*t1+b+c.y}; //intercept point 1
            var i2 = {x:t2+c.x, y:m*t2+b+c.y}; //intercept point 2
            return [i1, i2];
        }
    }
    
  • 1

    你需要一些数学:

    假设A =(Xa,Ya),B =(Xb,Yb)和C =(Xc,Yc) . 从A到B的线上的任何点都有坐标(alpha * Xa(1-alpha)Xb,alphaYa(1-alpha)* Yb)= P

    如果点P的距离为R到C,则它必须在圆上 . 你想要的是解决

    distance(P, C) = R
    

    那是

    (alpha*Xa + (1-alpha)*Xb)^2 + (alpha*Ya + (1-alpha)*Yb)^2 = R^2
    alpha^2*Xa^2 + alpha^2*Xb^2 - 2*alpha*Xb^2 + Xb^2 + alpha^2*Ya^2 + alpha^2*Yb^2 - 2*alpha*Yb^2 + Yb^2=R^2
    (Xa^2 + Xb^2 + Ya^2 + Yb^2)*alpha^2 - 2*(Xb^2 + Yb^2)*alpha + (Xb^2 + Yb^2 - R^2) = 0
    

    如果您将ABC公式应用于此公式以将其求解为alpha,并使用alpha的解决方案计算P的坐标,则会得到交点(如果存在) .

  • 0

    如果你找到球体中心之间的距离(因为它是3D我假设你的意思是球体而不是圆圈)和线条,那么检查该距离是否小于将要做的技巧的半径 .

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

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

  • 2

    这是Javascript中的一个实现 . 我的方法是首先将线段转换为无限线,然后找到交叉点 . 从那里我检查找到的点是否在线段上 . 代码有详细记录,您应该能够遵循 .

    你可以在这个live demo上试试这里的代码 . 代码取自我的algorithms repo .

    enter image description here

    // Small epsilon value
    var EPS = 0.0000001;
    
    // point (x, y)
    function Point(x, y) {
      this.x = x;
      this.y = y;
    }
    
    // Circle with center at (x,y) and radius r
    function Circle(x, y, r) {
      this.x = x;
      this.y = y;
      this.r = r;
    }
    
    // A line segment (x1, y1), (x2, y2)
    function LineSegment(x1, y1, x2, y2) {
      var d = Math.sqrt( (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2) );
      if (d < EPS) throw 'A point is not a line segment';
      this.x1 = x1; this.y1 = y1;
      this.x2 = x2; this.y2 = y2;
    }
    
    // An infinite line defined as: ax + by = c
    function Line(a, b, c) {
      this.a = a; this.b = b; this.c = c;
      // Normalize line for good measure
      if (Math.abs(b) < EPS) {
        c /= a; a = 1; b = 0;
      } else { 
        a = (Math.abs(a) < EPS) ? 0 : a / b;
        c /= b; b = 1; 
      }
    }
    
    // Given a line in standard form: ax + by = c and a circle with 
    // a center at (x,y) with radius r this method finds the intersection
    // of the line and the circle (if any). 
    function circleLineIntersection(circle, line) {
    
      var a = line.a, b = line.b, c = line.c;
      var x = circle.x, y = circle.y, r = circle.r;
    
      // Solve for the variable x with the formulas: ax + by = c (equation of line)
      // and (x-X)^2 + (y-Y)^2 = r^2 (equation of circle where X,Y are known) and expand to obtain quadratic:
      // (a^2 + b^2)x^2 + (2abY - 2ac + - 2b^2X)x + (b^2X^2 + b^2Y^2 - 2bcY + c^2 - b^2r^2) = 0
      // Then use quadratic formula X = (-b +- sqrt(a^2 - 4ac))/2a to find the 
      // roots of the equation (if they exist) and this will tell us the intersection points
    
      // In general a quadratic is written as: Ax^2 + Bx + C = 0
      // (a^2 + b^2)x^2 + (2abY - 2ac + - 2b^2X)x + (b^2X^2 + b^2Y^2 - 2bcY + c^2 - b^2r^2) = 0
      var A = a*a + b*b;
      var B = 2*a*b*y - 2*a*c - 2*b*b*x;
      var C = b*b*x*x + b*b*y*y - 2*b*c*y + c*c - b*b*r*r;
    
      // Use quadratic formula x = (-b +- sqrt(a^2 - 4ac))/2a to find the 
      // roots of the equation (if they exist).
    
      var D = B*B - 4*A*C;
      var x1,y1,x2,y2;
    
      // Handle vertical line case with b = 0
      if (Math.abs(b) < EPS) {
    
        // Line equation is ax + by = c, but b = 0, so x = c/a
        x1 = c/a;
    
        // No intersection
        if (Math.abs(x-x1) > r) return [];
    
        // Vertical line is tangent to circle
        if (Math.abs((x1-r)-x) < EPS || Math.abs((x1+r)-x) < EPS)
          return [new Point(x1, y)];
    
        var dx = Math.abs(x1 - x);
        var dy = Math.sqrt(r*r-dx*dx);
    
        // Vertical line cuts through circle
        return [
          new Point(x1,y+dy),
          new Point(x1,y-dy)
        ];
    
      // Line is tangent to circle
      } else if (Math.abs(D) < EPS) {
    
        x1 = -B/(2*A);
        y1 = (c - a*x1)/b;
    
        return [new Point(x1,y1)];
    
      // No intersection
      } else if (D < 0) {
    
        return [];
    
      } else {
    
        D = Math.sqrt(D);
    
        x1 = (-B+D)/(2*A);
        y1 = (c - a*x1)/b;
    
        x2 = (-B-D)/(2*A);
        y2 = (c - a*x2)/b;
    
        return [
          new Point(x1, y1),
          new Point(x2, y2)
        ];
    
      }
    
    }
    
    // Converts a line segment to a line in general form
    function segmentToGeneralForm(x1,y1,x2,y2) {
      var a = y1 - y2;
      var b = x2 - x1;
      var c = x2*y1 - x1*y2;
      return new Line(a,b,c);
    }
    
    // Checks if a point 'pt' is inside the rect defined by (x1,y1), (x2,y2)
    function pointInRectangle(pt,x1,y1,x2,y2) {
      var x = Math.min(x1,x2), X = Math.max(x1,x2);
      var y = Math.min(y1,y2), Y = Math.max(y1,y2);
      return x - EPS <= pt.x && pt.x <= X + EPS &&
             y - EPS <= pt.y && pt.y <= Y + EPS;
    }
    
    // Finds the intersection(s) of a line segment and a circle
    function lineSegmentCircleIntersection(segment, circle) {
    
      var x1 = segment.x1, y1 = segment.y1, x2 = segment.x2, y2 = segment.y2;
      var line = segmentToGeneralForm(x1,y1,x2,y2);
      var pts = circleLineIntersection(circle, line);
    
      // No intersection
      if (pts.length === 0) return [];
    
      var pt1 = pts[0];
      var includePt1 = pointInRectangle(pt1,x1,y1,x2,y2);
    
      // Check for unique intersection
      if (pts.length === 1) {
        if (includePt1) return [pt1];
        return [];
      }
    
      var pt2 = pts[1];
      var includePt2 = pointInRectangle(pt2,x1,y1,x2,y2);
    
      // Check for remaining intersections
      if (includePt1 && includePt2) return [pt1, pt2];
      if (includePt1) return [pt1];
      if (includePt2) return [pt2];
      return [];
    
    }
    
  • 3

    在该后圆线中,将通过检查圆心和线段(Ipoint)之间的距离来检查线圈,该线段表示从圆心到线段的法线N(图像2)之间的交点 .

    https://i.stack.imgur.com/3o6do.png
    Image 1. Finding vectors E and D

    在图像1上显示一个圆和一个线,矢量A点到线起点,矢量B点到线 endpoints ,矢量C点到圆心 . 现在我们必须找到矢量E(从行起点到圆心)和矢量D(从行起点到行终点),这个计算显示在图像1上 .

    https://i.stack.imgur.com/7098a.png
    Image 2. Finding vector X

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

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

    https://i.stack.imgur.com/p9WIr.png
    Image 3. Finding closest point

    当投影Xp为负时,Ipoint为线段左侧,向量最近等于线起始点的向量,当投影Xp大于矢量D的幅度时,则Ipoint在线段右侧,则最接近的矢量等于线 endpoints 的矢量,在任何其他情况下,最接近的矢量等于矢量Z.

    现在当我们有最接近的向量时,我们需要找到从圆心到Ipoint(dist向量)的向量,其简单我们只需要从中心向量中减去最接近的向量 . 接下来只检查矢量dist幅度是否小于圆半径,如果它是碰撞,如果它没有碰撞 .

    https://i.stack.imgur.com/QJ63q.png
    Image 4. Checking for collision

    最后,我们可以返回一些用于解决碰撞的值,最简单的方法是返回碰撞重叠(从矢量dist幅度减去半径)和返回碰撞轴,它的矢量D.如果需要,交点也是矢量Z.

  • 5

    如果线的坐标是A.x,A.y和B.x,B.y和圆心是C.x,C.y那么线公式是:

    x = A.x * t B.x *(1 - t)

    y = A.y * t B.y *(1 - t)

    其中0 <= t <= 1

    而圆圈是

    (C.x-x)^ 2(C.y-y)^ 2 = R ^ 2

    如果将线的x和y公式替换为圆公式,则得到t的二阶方程,其解是交点(如果有的话) . 如果你得到一个小于0或大于1的t,那么它不是一个解决方案,但它表明该线“指向”圆的方向 .

  • 0

    只是这个帖子的补充...下面是pahlevan发布的代码版本,但对于C#/ XNA并整理了一点:

    /// <summary>
        /// Intersects a line and a circle.
        /// </summary>
        /// <param name="location">the location of the circle</param>
        /// <param name="radius">the radius of the circle</param>
        /// <param name="lineFrom">the starting point of the line</param>
        /// <param name="lineTo">the ending point of the line</param>
        /// <returns>true if the line and circle intersect each other</returns>
        public static bool IntersectLineCircle(Vector2 location, float radius, Vector2 lineFrom, Vector2 lineTo)
        {
            float ab2, acab, h2;
            Vector2 ac = location - lineFrom;
            Vector2 ab = lineTo - lineFrom;
            Vector2.Dot(ref ab, ref ab, out ab2);
            Vector2.Dot(ref ac, ref ab, out acab);
            float t = acab / ab2;
    
            if (t < 0)
                t = 0;
            else if (t > 1)
                t = 1;
    
            Vector2 h = ((ab * t) + lineFrom) - location;
            Vector2.Dot(ref h, ref h, out h2);
    
            return (h2 <= (radius * radius));
        }
    
  • 1

    enter image description here

    ' VB.NET - Code
    
    Function CheckLineSegmentCircleIntersection(x1 As Double, y1 As Double, x2 As Double, y2 As Double, xc As Double, yc As Double, r As Double) As Boolean
        Static xd As Double = 0.0F
        Static yd As Double = 0.0F
        Static t As Double = 0.0F
        Static d As Double = 0.0F
        Static dx_2_1 As Double = 0.0F
        Static dy_2_1 As Double = 0.0F
    
        dx_2_1 = x2 - x1
        dy_2_1 = y2 - y1
    
        t = ((yc - y1) * dy_2_1 + (xc - x1) * dx_2_1) / (dy_2_1 * dy_2_1 + dx_2_1 * dx_2_1)
    
        If 0 <= t And t <= 1 Then
            xd = x1 + t * dx_2_1
            yd = y1 + t * dy_2_1
    
            d = Math.Sqrt((xd - xc) * (xd - xc) + (yd - yc) * (yd - yc))
            Return d <= r
        Else
            d = Math.Sqrt((xc - x1) * (xc - x1) + (yc - y1) * (yc - y1))
            If d <= r Then
                Return True
            Else
                d = Math.Sqrt((xc - x2) * (xc - x2) + (yc - y2) * (yc - y2))
                If d <= r Then
                    Return True
                Else
                    Return False
                End If
            End If
        End If
    End Function
    
  • 1

    我按照 chmike 给出的答案为iOS创建了这个函数

    + (NSArray *)intersectionPointsOfCircleWithCenter:(CGPoint)center withRadius:(float)radius toLinePoint1:(CGPoint)p1 andLinePoint2:(CGPoint)p2
    {
        NSMutableArray *intersectionPoints = [NSMutableArray array];
    
        float Ax = p1.x;
        float Ay = p1.y;
        float Bx = p2.x;
        float By = p2.y;
        float Cx = center.x;
        float Cy = center.y;
        float R = radius;
    
    
        // compute the euclidean distance between A and B
        float LAB = sqrt( pow(Bx-Ax, 2)+pow(By-Ay, 2) );
    
        // compute the direction vector D from A to B
        float Dx = (Bx-Ax)/LAB;
        float Dy = (By-Ay)/LAB;
    
        // Now the line equation is x = Dx*t + Ax, y = Dy*t + Ay with 0 <= t <= 1.
    
        // compute the value t of the closest point to the circle center (Cx, Cy)
        float t = Dx*(Cx-Ax) + Dy*(Cy-Ay);
    
        // This is the projection of C on the line from A to B.
    
        // compute the coordinates of the point E on line and closest to C
        float Ex = t*Dx+Ax;
        float Ey = t*Dy+Ay;
    
        // compute the euclidean distance from E to C
        float LEC = sqrt( pow(Ex-Cx, 2)+ pow(Ey-Cy, 2) );
    
        // test if the line intersects the circle
        if( LEC < R )
        {
            // compute distance from t to circle intersection point
            float dt = sqrt( pow(R, 2) - pow(LEC,2) );
    
            // compute first intersection point
            float Fx = (t-dt)*Dx + Ax;
            float Fy = (t-dt)*Dy + Ay;
    
            // compute second intersection point
            float Gx = (t+dt)*Dx + Ax;
            float Gy = (t+dt)*Dy + Ay;
    
            [intersectionPoints addObject:[NSValue valueWithCGPoint:CGPointMake(Fx, Fy)]];
            [intersectionPoints addObject:[NSValue valueWithCGPoint:CGPointMake(Gx, Gy)]];
        }
    
        // else test if the line is tangent to circle
        else if( LEC == R ) {
            // tangent point to circle is E
            [intersectionPoints addObject:[NSValue valueWithCGPoint:CGPointMake(Ex, Ey)]];
        }
        else {
            // line doesn't touch circle
        }
    
        return intersectionPoints;
    }
    
  • 6

    此Java函数返回DVec2对象 . 它的圆心,圆的半径和直线需要DVec2 .

    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;
    }
    
  • 2

    c#中的另一个(部分Circle类) . 经过测试和工作就像一个魅力 .

    public class Circle : IEquatable<Circle>
    {
        // ******************************************************************
        // The center of a circle
        private Point _center;
        // The radius of a circle
        private double _radius;
    
       // ******************************************************************
        /// <summary>
        /// Find all intersections (0, 1, 2) of the circle with a line defined by its 2 points.
        /// Using: http://math.stackexchange.com/questions/228841/how-do-i-calculate-the-intersections-of-a-straight-line-and-a-circle
        /// Note: p is the Center.X and q is Center.Y
        /// </summary>
        /// <param name="linePoint1"></param>
        /// <param name="linePoint2"></param>
        /// <returns></returns>
        public List<Point> GetIntersections(Point linePoint1, Point linePoint2)
        {
            List<Point> intersections = new List<Point>();
    
            double dx = linePoint2.X - linePoint1.X;
    
            if (dx.AboutEquals(0)) // Straight vertical line
            {
                if (linePoint1.X.AboutEquals(Center.X - Radius) || linePoint1.X.AboutEquals(Center.X + Radius))
                {
                    Point pt = new Point(linePoint1.X, Center.Y);
                    intersections.Add(pt);
                }
                else if (linePoint1.X > Center.X - Radius && linePoint1.X < Center.X + Radius)
                {
                    double x = linePoint1.X - Center.X;
    
                    Point pt = new Point(linePoint1.X, Center.Y + Math.Sqrt(Radius * Radius - (x * x)));
                    intersections.Add(pt);
    
                    pt = new Point(linePoint1.X, Center.Y - Math.Sqrt(Radius * Radius - (x * x)));
                    intersections.Add(pt);
                }
    
                return intersections;
            }
    
            // Line function (y = mx + b)
            double dy = linePoint2.Y - linePoint1.Y;
            double m = dy / dx;
            double b = linePoint1.Y - m * linePoint1.X;
    
            double A = m * m + 1;
            double B = 2 * (m * b - m * _center.Y - Center.X);
            double C = Center.X * Center.X + Center.Y * Center.Y - Radius * Radius - 2 * b * Center.Y + b * b;
    
            double discriminant = B * B - 4 * A * C;
    
            if (discriminant < 0)
            {
                return intersections; // there is no intersections
            }
    
            if (discriminant.AboutEquals(0)) // Tangeante (touch on 1 point only)
            {
                double x = -B / (2 * A);
                double y = m * x + b;
    
                intersections.Add(new Point(x, y));
            }
            else // Secant (touch on 2 points)
            {
                double x = (-B + Math.Sqrt(discriminant)) / (2 * A);
                double y = m * x + b;
                intersections.Add(new Point(x, y));
    
                x = (-B - Math.Sqrt(discriminant)) / (2 * A);
                y = m * x + b;
                intersections.Add(new Point(x, y));
            }
    
            return intersections;
        }
    
        // ******************************************************************
        // Get the center
        [XmlElement("Center")]
        public Point Center
        {
            get { return _center; }
            set
            {
                _center = value;
            }
        }
    
        // ******************************************************************
        // Get the radius
        [XmlElement]
        public double Radius
        {
            get { return _radius; }
            set { _radius = value; }
        }
    
        //// ******************************************************************
        //[XmlArrayItemAttribute("DoublePoint")]
        //public List<Point> Coordinates
        //{
        //    get { return _coordinates; }
        //}
    
        // ******************************************************************
        // Construct a circle without any specification
        public Circle()
        {
            _center.X = 0;
            _center.Y = 0;
            _radius = 0;
        }
    
        // ******************************************************************
        // Construct a circle without any specification
        public Circle(double radius)
        {
            _center.X = 0;
            _center.Y = 0;
            _radius = radius;
        }
    
        // ******************************************************************
        // Construct a circle with the specified circle
        public Circle(Circle circle)
        {
            _center = circle._center;
            _radius = circle._radius;
        }
    
        // ******************************************************************
        // Construct a circle with the specified center and radius
        public Circle(Point center, double radius)
        {
            _center = center;
            _radius = radius;
        }
    
        // ******************************************************************
        // Construct a circle based on one point
        public Circle(Point center)
        {
            _center = center;
            _radius = 0;
        }
    
        // ******************************************************************
        // Construct a circle based on two points
        public Circle(Point p1, Point p2)
        {
            Circle2Points(p1, p2);
        }
    

    需要:

    using System;
    
    namespace Mathematic
    {
        public static class DoubleExtension
        {
            // ******************************************************************
            // Base on Hans Passant Answer on:
            // http://stackoverflow.com/questions/2411392/double-epsilon-for-equality-greater-than-less-than-less-than-or-equal-to-gre
    
            /// <summary>
            /// Compare two double taking in account the double precision potential error.
            /// Take care: truncation errors accumulate on calculation. More you do, more you should increase the epsilon.
            public static bool AboutEquals(this double value1, double value2)
            {
                if (double.IsPositiveInfinity(value1))
                    return double.IsPositiveInfinity(value2);
    
                if (double.IsNegativeInfinity(value1))
                    return double.IsNegativeInfinity(value2);
    
                if (double.IsNaN(value1))
                    return double.IsNaN(value2);
    
                double epsilon = Math.Max(Math.Abs(value1), Math.Abs(value2)) * 1E-15;
                return Math.Abs(value1 - value2) <= epsilon;
            }
    
            // ******************************************************************
            // Base on Hans Passant Answer on:
            // http://stackoverflow.com/questions/2411392/double-epsilon-for-equality-greater-than-less-than-less-than-or-equal-to-gre
    
            /// <summary>
            /// Compare two double taking in account the double precision potential error.
            /// Take care: truncation errors accumulate on calculation. More you do, more you should increase the epsilon.
            /// You get really better performance when you can determine the contextual epsilon first.
            /// </summary>
            /// <param name="value1"></param>
            /// <param name="value2"></param>
            /// <param name="precalculatedContextualEpsilon"></param>
            /// <returns></returns>
            public static bool AboutEquals(this double value1, double value2, double precalculatedContextualEpsilon)
            {
                if (double.IsPositiveInfinity(value1))
                    return double.IsPositiveInfinity(value2);
    
                if (double.IsNegativeInfinity(value1))
                    return double.IsNegativeInfinity(value2);
    
                if (double.IsNaN(value1))
                    return double.IsNaN(value2);
    
                return Math.Abs(value1 - value2) <= precalculatedContextualEpsilon;
            }
    
            // ******************************************************************
            public static double GetContextualEpsilon(this double biggestPossibleContextualValue)
            {
                return biggestPossibleContextualValue * 1E-15;
            }
    
            // ******************************************************************
            /// <summary>
            /// Mathlab equivalent
            /// </summary>
            /// <param name="dividend"></param>
            /// <param name="divisor"></param>
            /// <returns></returns>
            public static double Mod(this double dividend, double divisor)
            {
                return dividend - System.Math.Floor(dividend / divisor) * divisor;
            }
    
            // ******************************************************************
        }
    }
    
  • 4

    这是JavaScript中的好解决方案(包含所有必需的数学和实时插图)https://bl.ocks.org/milkbread/11000965

    虽然该解决方案中的 is_on 功能需要修改:

    function is_on(a, b, c) {
        return Math.abs(distance(a,c) + distance(c,b) - distance(a,b))<0.000001;
    }
    
  • 43

    圈子真的是个坏人:)所以一个好方法是避免真正的圈子,如果可以的话 . 如果你正在对游戏进行碰撞检查,你可以进行一些简化,只有3个点产品和一些比较 .

    我称之为“胖点”或“薄圈” . 它是一种在平行于一个段的方向上具有零半径的椭圆 . 但在垂直于一个段的方向上的全半径

    首先,我会考虑重命名和切换坐标系以避免过多的数据:

    s0s1 = B-A;
    s0qp = C-A;
    rSqr = r*r;
    

    其次,hvec2f中的索引h意味着比矢量必须支持水平操作,如dot()/ det() . 这意味着它的组件将被放置在一个单独的xmm寄存器中,以避免混乱/充电/ hsub'ing . 在这里,我们为2D游戏提供最简单的最简单的碰撞检测版本:

    bool fat_point_collides_segment(const hvec2f& s0qp, const hvec2f& s0s1, const float& rSqr) {
        auto a = dot(s0s1, s0s1);
        //if( a != 0 ) // if you haven't zero-length segments omit this, as it would save you 1 _mm_comineq_ss() instruction and 1 memory fetch
        {
            auto b = dot(s0s1, s0qp);
            auto t = b / a; // length of projection of s0qp onto s0s1
            //std::cout << "t = " << t << "\n";
            if ((t >= 0) && (t <= 1)) // 
            {
                auto c = dot(s0qp, s0qp);
                auto r2 = c - a * t * t;
                return (r2 <= rSqr); // true if collides
            }
        }   
        return false;
    }
    

    我怀疑你可以进一步优化它 . 我正在将它用于神经网络驱动的赛车碰撞检测,以处理数亿次迭代步骤 .

  • 9

    这是一个用golang编写的解决方案 . 该方法类似于此处发布的其他一些答案,但不完全相同 . 它易于实施,并已经过测试 . 以下是步骤:

    • 平移坐标,使圆圈位于原点 .

    • 将线段表示为x和y坐标的t的参数化函数 . 如果t为0,则函数's values are one end point of the segment, and if t is 1, the function' s值是另一个 endpoints .

    • 如果可能,求解由约束t的值产生的二次方程,该值产生x,y坐标,其距离原点的距离等于圆的半径 .

    • 抛出t为<0或> 1的解(对于开放段,<= 0或> = 1) . 这些点不包含在该段中 .

    • 转换回原始坐标 .

    这里导出了二次曲线的A,B和C的值,其中(n-et)和(m-dt)分别是线的x和y坐标的等式 . r是圆的半径 .

    (n-et)(n-et) + (m-dt)(m-dt) = rr
    nn - 2etn + etet + mm - 2mdt + dtdt = rr
    (ee+dd)tt - 2(en + dm)t + nn + mm - rr = 0
    

    因此A = ee dd,B = -2(en dm),C = nn mm - rr .

    这是函数的golang代码:

    package geom
    
    import (
        "math"
    )
    
    // SegmentCircleIntersection return points of intersection between a circle and
    // a line segment. The Boolean intersects returns true if one or
    // more solutions exist. If only one solution exists, 
    // x1 == x2 and y1 == y2.
    // s1x and s1y are coordinates for one end point of the segment, and
    // s2x and s2y are coordinates for the other end of the segment.
    // cx and cy are the coordinates of the center of the circle and
    // r is the radius of the circle.
    func SegmentCircleIntersection(s1x, s1y, s2x, s2y, cx, cy, r float64) (x1, y1, x2, y2 float64, intersects bool) {
        // (n-et) and (m-dt) are expressions for the x and y coordinates
        // of a parameterized line in coordinates whose origin is the
        // center of the circle.
        // When t = 0, (n-et) == s1x - cx and (m-dt) == s1y - cy
        // When t = 1, (n-et) == s2x - cx and (m-dt) == s2y - cy.
        n := s2x - cx
        m := s2y - cy
    
        e := s2x - s1x
        d := s2y - s1y
    
        // lineFunc checks if the  t parameter is in the segment and if so
        // calculates the line point in the unshifted coordinates (adds back
        // cx and cy.
        lineFunc := func(t float64) (x, y float64, inBounds bool) {
            inBounds = t >= 0 && t <= 1 // Check bounds on closed segment
            // To check bounds for an open segment use t > 0 && t < 1
            if inBounds { // Calc coords for point in segment
                x = n - e*t + cx
                y = m - d*t + cy
            }
            return
        }
    
        // Since we want the points on the line distance r from the origin,
        // (n-et)(n-et) + (m-dt)(m-dt) = rr.
        // Expanding and collecting terms yeilds the following quadratic equation:
        A, B, C := e*e+d*d, -2*(e*n+m*d), n*n+m*m-r*r
    
        D := B*B - 4*A*C // discriminant of quadratic
        if D < 0 {
            return // No solution
        }
        D = math.Sqrt(D)
    
        var p1In, p2In bool
        x1, y1, p1In = lineFunc((-B + D) / (2 * A)) // First root
        if D == 0.0 {
            intersects = p1In
            x2, y2 = x1, y1
            return // Only possible solution, quadratic has one root.
        }
    
        x2, y2, p2In = lineFunc((-B - D) / (2 * A)) // Second root
    
        intersects = p1In || p2In
        if p1In == false { // Only x2, y2 may be valid solutions
            x1, y1 = x2, y2
        } else if p2In == false { // Only x1, y1 are valid solutions
            x2, y2 = x1, y1
        }
        return
    }
    

    我用这个函数测试了它,它确认了解决点在线段和圆上 . 它会创建一个测试段并围绕给定的圆圈进行扫描:

    package geom_test
    
    import (
        "testing"
    
        . "**put your package path here**"
    )
    
    func CheckEpsilon(t *testing.T, v, epsilon float64, message string) {
        if v > epsilon || v < -epsilon {
            t.Error(message, v, epsilon)
            t.FailNow()
        }
    }
    
    func TestSegmentCircleIntersection(t *testing.T) {
        epsilon := 1e-10      // Something smallish
        x1, y1 := 5.0, 2.0    // segment end point 1
        x2, y2 := 50.0, 30.0  // segment end point 2
        cx, cy := 100.0, 90.0 // center of circle
        r := 80.0
    
        segx, segy := x2-x1, y2-y1
    
        testCntr, solutionCntr := 0, 0
    
        for i := -100; i < 100; i++ {
            for j := -100; j < 100; j++ {
                testCntr++
                s1x, s2x := x1+float64(i), x2+float64(i)
                s1y, s2y := y1+float64(j), y2+float64(j)
    
                sc1x, sc1y := s1x-cx, s1y-cy
                seg1Inside := sc1x*sc1x+sc1y*sc1y < r*r
                sc2x, sc2y := s2x-cx, s2y-cy
                seg2Inside := sc2x*sc2x+sc2y*sc2y < r*r
    
                p1x, p1y, p2x, p2y, intersects := SegmentCircleIntersection(s1x, s1y, s2x, s2y, cx, cy, r)
    
                if intersects {
                    solutionCntr++
                    //Check if points are on circle
                    c1x, c1y := p1x-cx, p1y-cy
                    deltaLen1 := (c1x*c1x + c1y*c1y) - r*r
                    CheckEpsilon(t, deltaLen1, epsilon, "p1 not on circle")
    
                    c2x, c2y := p2x-cx, p2y-cy
                    deltaLen2 := (c2x*c2x + c2y*c2y) - r*r
                    CheckEpsilon(t, deltaLen2, epsilon, "p2 not on circle")
    
                    // Check if points are on the line through the line segment
                    // "cross product" of vector from a segment point to the point
                    // and the vector for the segment should be near zero
                    vp1x, vp1y := p1x-s1x, p1y-s1y
                    crossProd1 := vp1x*segy - vp1y*segx
                    CheckEpsilon(t, crossProd1, epsilon, "p1 not on line ")
    
                    vp2x, vp2y := p2x-s1x, p2y-s1y
                    crossProd2 := vp2x*segy - vp2y*segx
                    CheckEpsilon(t, crossProd2, epsilon, "p2 not on line ")
    
                    // Check if point is between points s1 and s2 on line
                    // This means the sign of the dot prod of the segment vector
                    // and point to segment end point vectors are opposite for
                    // either end.
                    wp1x, wp1y := p1x-s2x, p1y-s2y
                    dp1v := vp1x*segx + vp1y*segy
                    dp1w := wp1x*segx + wp1y*segy
                    if (dp1v < 0 && dp1w < 0) || (dp1v > 0 && dp1w > 0) {
                        t.Error("point not contained in segment ", dp1v, dp1w)
                        t.FailNow()
                    }
    
                    wp2x, wp2y := p2x-s2x, p2y-s2y
                    dp2v := vp2x*segx + vp2y*segy
                    dp2w := wp2x*segx + wp2y*segy
                    if (dp2v < 0 && dp2w < 0) || (dp2v > 0 && dp2w > 0) {
                        t.Error("point not contained in segment ", dp2v, dp2w)
                        t.FailNow()
                    }
    
                    if s1x == s2x && s2y == s1y { //Only one solution
                        // Test that one end of the segment is withing the radius of the circle
                        // and one is not
                        if seg1Inside && seg2Inside {
                            t.Error("Only one solution but both line segment ends inside")
                            t.FailNow()
                        }
                        if !seg1Inside && !seg2Inside {
                            t.Error("Only one solution but both line segment ends outside")
                            t.FailNow()
                        }
    
                    }
                } else { // No intersection, check if both points outside or inside
                    if (seg1Inside && !seg2Inside) || (!seg1Inside && seg2Inside) {
                        t.Error("No solution but only one point in radius of circle")
                        t.FailNow()
                    }
                }
            }
        }
        t.Log("Tested ", testCntr, " examples and found ", solutionCntr, " solutions.")
    }
    

    以下是测试的输出:

    === RUN   TestSegmentCircleIntersection
    --- PASS: TestSegmentCircleIntersection (0.00s)
        geom_test.go:105: Tested  40000  examples and found  7343  solutions.
    

    最后,该方法很容易扩展到射线从一个点开始,穿过另一个并延伸到无穷大的情况,只测试t> 0或t <1但不是两者 .

  • 3

    我只需要那个,所以我提出了这个解决方案 . 语言是maxscript,但应该很容易翻译成任何其他语言 . sideA,sideB和CircleRadius是标量,其余变量是[x,y,z] . 我假设z = 0在XY平面上求解

    fn projectPoint p1 p2 p3 = --project  p1 perpendicular to the line p2-p3
    (
        local v= normalize (p3-p2)
        local p= (p1-p2)
        p2+((dot v p)*v)
    )
    fn findIntersectionLineCircle CircleCenter CircleRadius LineP1 LineP2=
    (
        pp=projectPoint CircleCenter LineP1 LineP2
        sideA=distance pp CircleCenter
        --use pythagoras to solve the third side
        sideB=sqrt(CircleRadius^2-sideA^2) -- this will return NaN if they don't intersect
        IntersectV=normalize (pp-CircleCenter)
        perpV=[IntersectV.y,-IntersectV.x,IntersectV.z]
        --project the point to both sides to find the solutions
        solution1=pp+(sideB*perpV)
        solution2=pp-(sideB*perpV)
        return #(solution1,solution2)
    )
    

相关问题