Circle-Rectangle collision detection (intersection)

前端 未结 24 1376
无人共我
无人共我 2020-11-22 02:55

How can I tell whether a circle and a rectangle intersect in 2D Euclidean space? (i.e. classic 2D geometry)

相关标签:
24条回答
  • 2020-11-22 03:02

    This function detect collisions (intersections) between Circle and Rectangle. He works like e.James method in his answer, but this one detect collisions for all angles of rectangle (not only right up corner).

    NOTE:

    aRect.origin.x and aRect.origin.y are coordinates of bottom left angle of rectangle!

    aCircle.x and aCircle.y are coordinates of Circle Center!

    static inline BOOL RectIntersectsCircle(CGRect aRect, Circle aCircle) {
    
        float testX = aCircle.x;
        float testY = aCircle.y;
    
        if (testX < aRect.origin.x)
            testX = aRect.origin.x;
        if (testX > (aRect.origin.x + aRect.size.width))
            testX = (aRect.origin.x + aRect.size.width);
        if (testY < aRect.origin.y)
            testY = aRect.origin.y;
        if (testY > (aRect.origin.y + aRect.size.height))
            testY = (aRect.origin.y + aRect.size.height);
    
        return ((aCircle.x - testX) * (aCircle.x - testX) + (aCircle.y - testY) * (aCircle.y - testY)) < aCircle.radius * aCircle.radius;
    }
    
    0 讨论(0)
  • 2020-11-22 03:02
    • First check if the rectangle and the square tangent to the circle overlaps (easy). If they do not overlaps, they do not collide.
    • Check if the circle's center is inside the rectangle (easy). If it's inside, they collide.
    • Calculate the minimum squared distance from the rectangle sides to the circle's center (little hard). If it's lower that the squared radius, then they collide, else they don't.

    It's efficient, because:

    • First it checks the most common scenario with a cheap algorithm and when it's sure they do not collide, it ends.
    • Then it checks the next most common scenario with a cheap algorithm (do not calculate square root, use the squared values) and when it's sure they collide it ends.
    • Then it executes the more expensive algorithm to check collision with the rectangle borders.
    0 讨论(0)
  • 2020-11-22 03:03

    Improving a little bit the answer of e.James:

    double dx = abs(circle.x - rect.x) - rect.w / 2,
           dy = abs(circle.y - rect.y) - rect.h / 2;
    
    if (dx > circle.r || dy > circle.r) { return false; }
    if (dx <= 0 || dy <= 0) { return true; }
    
    return (dx * dx + dy * dy <= circle.r * circle.r);
    

    This subtracts rect.w / 2 and rect.h / 2 once instead of up to three times.

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

    I created class for work with shapes hope you enjoy

    public class Geomethry {
      public static boolean intersectionCircleAndRectangle(int circleX, int circleY, int circleR, int rectangleX, int rectangleY, int rectangleWidth, int rectangleHeight){
        boolean result = false;
    
        float rectHalfWidth = rectangleWidth/2.0f;
        float rectHalfHeight = rectangleHeight/2.0f;
    
        float rectCenterX = rectangleX + rectHalfWidth;
        float rectCenterY = rectangleY + rectHalfHeight;
    
        float deltax = Math.abs(rectCenterX - circleX);
        float deltay = Math.abs(rectCenterY - circleY);
    
        float lengthHypotenuseSqure = deltax*deltax + deltay*deltay;
    
        do{
            // check that distance between the centerse is more than the distance between the circumcircle of rectangle and circle
            if(lengthHypotenuseSqure > ((rectHalfWidth+circleR)*(rectHalfWidth+circleR) + (rectHalfHeight+circleR)*(rectHalfHeight+circleR))){
                //System.out.println("distance between the centerse is more than the distance between the circumcircle of rectangle and circle");
                break;
            }
    
            // check that distance between the centerse is less than the distance between the inscribed circle
            float rectMinHalfSide = Math.min(rectHalfWidth, rectHalfHeight);
            if(lengthHypotenuseSqure < ((rectMinHalfSide+circleR)*(rectMinHalfSide+circleR))){
                //System.out.println("distance between the centerse is less than the distance between the inscribed circle");
                result=true;
                break;
            }
    
            // check that the squares relate to angles
            if((deltax > (rectHalfWidth+circleR)*0.9) && (deltay > (rectHalfHeight+circleR)*0.9)){
                //System.out.println("squares relate to angles");
                result=true;
            }
        }while(false);
    
        return result;
    }
    
    public static boolean intersectionRectangleAndRectangle(int rectangleX, int rectangleY, int rectangleWidth, int rectangleHeight, int rectangleX2, int rectangleY2, int rectangleWidth2, int rectangleHeight2){
        boolean result = false;
    
        float rectHalfWidth = rectangleWidth/2.0f;
        float rectHalfHeight = rectangleHeight/2.0f;
        float rectHalfWidth2 = rectangleWidth2/2.0f;
        float rectHalfHeight2 = rectangleHeight2/2.0f;
    
        float deltax = Math.abs((rectangleX + rectHalfWidth) - (rectangleX2 + rectHalfWidth2));
        float deltay = Math.abs((rectangleY + rectHalfHeight) - (rectangleY2 + rectHalfHeight2));
    
        float lengthHypotenuseSqure = deltax*deltax + deltay*deltay;
    
        do{
            // check that distance between the centerse is more than the distance between the circumcircle
            if(lengthHypotenuseSqure > ((rectHalfWidth+rectHalfWidth2)*(rectHalfWidth+rectHalfWidth2) + (rectHalfHeight+rectHalfHeight2)*(rectHalfHeight+rectHalfHeight2))){
                //System.out.println("distance between the centerse is more than the distance between the circumcircle");
                break;
            }
    
            // check that distance between the centerse is less than the distance between the inscribed circle
            float rectMinHalfSide = Math.min(rectHalfWidth, rectHalfHeight);
            float rectMinHalfSide2 = Math.min(rectHalfWidth2, rectHalfHeight2);
            if(lengthHypotenuseSqure < ((rectMinHalfSide+rectMinHalfSide2)*(rectMinHalfSide+rectMinHalfSide2))){
                //System.out.println("distance between the centerse is less than the distance between the inscribed circle");
                result=true;
                break;
            }
    
            // check that the squares relate to angles
            if((deltax > (rectHalfWidth+rectHalfWidth2)*0.9) && (deltay > (rectHalfHeight+rectHalfHeight2)*0.9)){
                //System.out.println("squares relate to angles");
                result=true;
            }
        }while(false);
    
        return result;
      } 
    }
    
    0 讨论(0)
  • 2020-11-22 03:10

    I've a method which avoids the expensive pythagoras if not necessary - ie. when bounding boxes of the rectangle and the circle do not intersect.

    And it'll work for non-euclidean too:

    class Circle {
     // create the bounding box of the circle only once
     BBox bbox;
    
     public boolean intersect(BBox b) {
        // test top intersect
        if (lat > b.maxLat) {
            if (lon < b.minLon)
                return normDist(b.maxLat, b.minLon) <= normedDist;
            if (lon > b.maxLon)
                return normDist(b.maxLat, b.maxLon) <= normedDist;
            return b.maxLat - bbox.minLat > 0;
        }
    
        // test bottom intersect
        if (lat < b.minLat) {
            if (lon < b.minLon)
                return normDist(b.minLat, b.minLon) <= normedDist;
            if (lon > b.maxLon)
                return normDist(b.minLat, b.maxLon) <= normedDist;
            return bbox.maxLat - b.minLat > 0;
        }
    
        // test middle intersect
        if (lon < b.minLon)
            return bbox.maxLon - b.minLon > 0;
        if (lon > b.maxLon)
            return b.maxLon - bbox.minLon > 0;
        return true;
      }
    }
    
    • minLat,maxLat can be replaced with minY,maxY and the same for minLon, maxLon: replace it with minX, maxX
    • normDist ist just a bit faster method then the full distance calculation. E.g. without the square-root in euclidean space (or without a lot of other stuff for haversine): dLat=(lat-circleY); dLon=(lon-circleX); normed=dLat*dLat+dLon*dLon. Of course if you use that normDist method you'll need to do create a normedDist = dist*dist; for the circle

    See the full BBox and Circle code of my GraphHopper project.

    0 讨论(0)
  • 2020-11-22 03:10

    worked for me (only work when angle of rectangle is 180)

    function intersects(circle, rect) {
      let left = rect.x + rect.width > circle.x - circle.radius;
      let right = rect.x < circle.x + circle.radius;
      let top = rect.y < circle.y + circle.radius;
      let bottom = rect.y + rect.height > circle.y - circle.radius;
      return left && right && bottom && top;
    }
    
    0 讨论(0)
提交回复
热议问题