Circle line-segment collision detection algorithm?

前端 未结 28 1362
被撕碎了的回忆
被撕碎了的回忆 2020-11-22 06:38

I have a line from A to B and a circle positioned at C with the radius R.

\"Image\"

What is a good alg

相关标签:
28条回答
  • 2020-11-22 06:59

    I would use the algorithm to compute the distance between a point (circle center) and a line (line AB). This can then be used to determine the intersection points of the line with the circle.

    Let say we have the points A, B, C. Ax and Ay are the x and y components of the A points. Same for B and C. The scalar R is the circle radius.

    This algorithm requires that A, B and C are distinct points and that R is not 0.

    Here is the algorithm

    // 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
    
    0 讨论(0)
  • 2020-11-22 06:59

    Here is good solution in JavaScript (with all required mathematics and live illustration) https://bl.ocks.org/milkbread/11000965

    Though is_on function in that solution needs modifications:

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

    0 讨论(0)
  • 2020-11-22 07:00

    You'll need some math here:

    Suppose A = (Xa, Ya), B = (Xb, Yb) and C = (Xc, Yc). Any point on the line from A to B has coordinates (alpha*Xa + (1-alpha)Xb, alphaYa + (1-alpha)*Yb) = P

    If the point P has distance R to C, it must be on the circle. What you want is to solve

    distance(P, C) = R
    

    that is

    (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
    

    if you apply the ABC-formula to this equation to solve it for alpha, and compute the coordinates of P using the solution(s) for alpha, you get the intersection points, if any exist.

    0 讨论(0)
  • 2020-11-22 07:00

    If the line's coordinates are A.x, A.y and B.x, B.y and the circles center is C.x, C.y then the lines formulae are:

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

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

    where 0<=t<=1

    and the circle is

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

    if you substitute x and y formulae of the line into the circles formula you get a second order equation of t and its solutions are the intersection points (if there are any). If you get a t which is smaller than 0 or greater than 1 then its not a solution but it shows that the line is 'pointing' to the direction of the circle.

    0 讨论(0)
  • 2020-11-22 07:00

    Just an addition to this thread... Below is a version of the code posted by pahlevan, but for C#/XNA and tidied up a little:

        /// <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));
        }
    
    0 讨论(0)
  • 2020-11-22 07:01

    Okay, I won't give you code, but since you have tagged this algorithm, I don't think that will matter to you. First, you have to get a vector perpendicular to the line.

    You will have an unknown variable in y = ax + c ( c will be unknown )
    To solve for that, Calculate it's value when the line passes through the center of the circle.

    That is,
    Plug in the location of the circle center to the line equation and solve for c.
    Then calculate the intersection point of the original line and its normal.

    This will give you the closest point on the line to the circle.
    Calculate the distance between this point and the circle center (using the magnitude of the vector).
    If this is less than the radius of the circle - voila, we have an intersection!

    0 讨论(0)
提交回复
热议问题