简体   繁体   中英

Best Algorithms to determine the whether a rectangle is inside a circle

I have to draw rectangle with different fill color depending on the its intersection with a concentric circle. Picture shown will give you a better idea about the scenario, 在此处输入图片说明 ( representation purpose only)

Currently I am checking each point status by applying Pythagoras's theorem

pseudo code:

SquareOf Point Distance from center (sqrOfDistance) = square(point X - Circle center X) + square(point Y- Circle center Y)

compare these value with Square of radius (sqrOfInnerR)

if  sqrOfDistance == sqrOfInnerR
    Inline
else if sqrOfDistance > sqrOfInnerR
    Out
else 
    In

even though the current logic works; it need to perform these check with each points (4 or 8 times) and and finally with together to determine the state. in my real world application there will be around 3,000,000 rectangles comes to the picture.

private RectState CheckTheRectangleState(Rect rect, double radius, bool firstCall = true)
        {
            double SquareOfRadius = Square(radius);
            var _x = rect.X - ControlCenter.X;
            var _y = rect.Y - ControlCenter.Y;

            var squareOfDistanceToTopLeftPoint = Square(_x) + Square(_y);
            var squareOfDistanceToTopRight = Square(_x + rect.Width) + Square(_y);
            var squareOfDistanceToBottonLeft = Square(_x) + Square(_y + rect.Height);
            var squareOfDistanceToBottonRight = Square(_x + rect.Width) + Square(_y + rect.Height);

            var topLeftStatus = squareOfDistanceToTopLeftPoint == SquareOfRadius ? PointStatus.Inline : (squareOfDistanceToTopLeftPoint > SquareOfRadius ? PointStatus.Out : PointStatus.In);
            var topRightStatus = squareOfDistanceToTopRight == SquareOfRadius ? PointStatus.Inline : (squareOfDistanceToTopRight > SquareOfRadius ? PointStatus.Out : PointStatus.In);
            var bottonLeftStatus = squareOfDistanceToBottonLeft == SquareOfRadius ? PointStatus.Inline : (squareOfDistanceToBottonLeft > SquareOfRadius ? PointStatus.Out : PointStatus.In);
            var bottonRightStatus = squareOfDistanceToBottonRight == SquareOfRadius ? PointStatus.Inline : (squareOfDistanceToBottonRight > SquareOfRadius ? PointStatus.Out : PointStatus.In);

            if ((topLeftStatus == PointStatus.In || topLeftStatus == PointStatus.Inline) &&
                (topRightStatus == PointStatus.In || topRightStatus == PointStatus.Inline) &&
                (bottonLeftStatus == PointStatus.In || bottonLeftStatus == PointStatus.Inline) &&
                (bottonRightStatus == PointStatus.In || bottonRightStatus == PointStatus.Inline))
            {
                return firstCall ? RectState.In : RectState.Partial;
            }
            else
            {
                if (firstCall)
                    CheckTheRectangleState(rect, outCircleRadius, false);
            }
            return RectState.Out;
        }
    }

where Square() is custom function to get square. Square(x){ return x*x;} PointStatus and RectState are enum to determine the status of points.

If you are dealing with a lot of rectangles and if most of them are going to be outside the circle most of the time, one way to optimize the check in a early exit way is to first imagine a square enclosing the circle , from (-r,-r) to (r,r), where r is the radius of the circle and centre of the circle is (0,0) and check if the rectangles are within this square. This should be much faster , and the check for collision with the circle needs to happen only if this one succeeds.

edit: @hvd has added an excellent idea for an early exit positive check. If the rectangle is within the inner square , it is definitely inside the circle.

Depending on the size of your rectangle vs circle, you could also go 1 level deeper and make rectangles between the inner square and the circle . But you need to check that the points of the queried rectangles are all in any of the rectangles (+ inner square), and all of them do not need to be in the same one.

So, in the most cases, we can decide that square is a circle, and then our task can become more easier. It will look in such way

float distance = Distance(LargeCircle.center, square.center);
if (distance > LargeCircle.radius){
    //two cases here, we can be outside of circle, or intersect it
} else {
    //two cases again. We can be inside a circle, or intersect it
}

Hope it will help

Just comment to what @Karthik T suggests. With representing circle with rectangle you can check it with:

  • ignore rectangles with top bound above of top bound of circle
  • ignore rectangles with bottom bound lower than bottom bound of circle
  • ignore rectangles with left bound before left bound of circle
  • ignore rectangles with right bound after right bound of circle
  • rest is one that inside

Thus you have only 4 checks rather than 8.

After that you can split circle in quadrants and classify rectangles into cases:

  • If bottom-right corner is in left-upper quadrant - check only top-left corner (by distance)
  • If bottom-left corner is in right-upper quadrant - check only top-right corner
  • If top-right corner is in left-lower quadrant - check only bottom-left corner
  • If top-left corner is in right-lower quadrant - check only bottom-right corner
  • If bottom-right corner and bottom-left corner is in upper half of square - check only upper-left and upper-right corner
  • ...
  • Rest (those for which each corner is in its own quadrant) check all corners by distance.

Update: Actually its much better to classify rectangles in first turn and then filter-out outside of square and then filter-out by cases.

Sample of code:

struct Vec {
    public double X, Y;
    public Vec Offset(Vec d) { return new Vec { X = X + d.X, Y = Y + d.Y }; }
    public Vec Negate() { return new Vec { X = -X, Y = -Y }; }
    public Vec OrthX() { return new Vec { X = X }; }
    public Vec OrthY() { return new Vec { Y = Y }; }
}
struct Rect { public Vec TopLeft, Size; }

Vec NextVec(Random rng)
{ return new Vec { X = rng.Next(), Y = rng.Next() }; }

Rect NextRect(Random rng)
{
    var topLeft = NextVec(rng);
    return new Rect { TopLeft = NextVec(rng), Size = NextVec(rng) };
}

Vec Center;
double R, SqR;

private static double Square(double X) { return X*X; }
private bool Contains(Vec point)
{ return (Square(point.X - Center.X) + Square(point.Y - Center.Y)) < SqR; }

private bool Contains(Rect rect)
{
    var a = rect.TopLeft;
    var c = rect.TopLeft.Offset(rect.Size);
    if (c.Y < Center.Y) // in upper half
    {
        if (c.X < Center.X) // in upper-left quadrant
        {
            return Contains(a);
        }
        else if (a.X > Center.X) // in upper-right quadrant
        {
            return Contains(rect.TopLeft.Offset(rect.Size.OrthX()));
        }
        else // spans over upper half
        {
            return Contains(a) &&
                Contains(rect.TopLeft.Offset(rect.Size.OrthX()));
        }
    }
    else if (a.Y > Center.Y) // in lower half
    {
        if (c.X < Center.X) // in lower-left quadrant
        {
            return Contains(rect.TopLeft.Offset(rect.Size.OrthY()));
        }
        else if (a.X > Center.X) // in lower-right quadrant
        {
            return Contains(c);
        }
        else // spans over lower half
        {
            return Contains(c) &&
                Contains(rect.TopLeft.Offset(rect.Size.OrthY()));
        }
    }
    else // rect spans over upper and lower halfs
    {
        if (c.X < Center.X) // spans over left half
        {
            return Contains(a) &&
                Contains(rect.TopLeft.Offset(rect.Size.OrthY()));
        }
        else if (a.X > Center.X) // spans over right half
        {
            return Contains(rect.TopLeft.Offset(rect.Size.OrthX())) &&
                Contains(c);
        }
        else // rect spans over all quadrants
        {
            return Contains(a) &&
                Contains(c) &&
                Contains(rect.TopLeft.Offset(rect.Size.OrthX())) &&
                Contains(rect.TopLeft.Offset(rect.Size.OrthY()));
        }
    }

}

BTW: Consider orginizing rectangles in Quadtree

Much faster if u check if 4 corners(x,y) are closer to center of sphere then lenght of radius? example

sqrt((Xcorner - Xcenter)^2 + (Ycorner - Ycenter)^2) <= R

and break the calculation for each corner of square if any doesn't pass the condition.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM