【问题标题】:Best Algorithms to determine the whether a rectangle is inside a circle确定矩形是否在圆内的最佳算法
【发布时间】:2012-11-28 08:51:23
【问题描述】:

我必须根据其与同心圆的交点来绘制具有不同填充颜色的矩形。显示的图片将使您对场景有更好的了解, (仅代表目的)

目前我正在通过应用毕达哥拉斯定理检查每个点的状态

伪代码:

SquareOf Point 到中心的距离 (sqrOfDistance) = square(point X - 圆心 X) + 正方形(点 Y- 圆心 Y)

将这些值与半径平方 (sqrOfInnerR) 进行比较

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

即使当前的逻辑有效;它需要对每个点执行这些检查(4 或 8 次),最后一起确定状态。 在我的实际应用程序中,图片中将出现大约 3,000,000 个矩形。

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

其中 Square() 是获取平方的自定义函数。 Square(x){ return x*x;} PointStatus 和 RectState 是用来确定点的状态的枚举。

【问题讨论】:

  • 只是出于好奇,你介意告诉我这是干什么用的吗?
  • 您可以通过首先检查矩形是否在包围圆的正方形((-r,-r)到(r,r))内进行优化(半径= r,中心=(0,0 ))。
  • “Square() 是获取平方根的自定义函数” - 你真的是指“平方根”,还是仅仅指“平方”?如果是前者,那么请注意,您可以重写算法以使用事物的平方来进行命中测试,而不是平方根事物,这会快得多。此外,对于实际计算平方根的函数来说,Square() 是一个可怕的名称……(实际上,我很确定您的意思只是“平方根”。)
  • 与其对交集检查进行微优化,不如考虑在一些空间划分结构中组织方格——四叉树、k-d树...
  • @Tharwen 是半导体晶圆,矩形是裸片

标签: c# algorithm


【解决方案1】:

如果您要处理很多矩形,并且如果大多数矩形大部分时间都在圆圈之外,那么一种优化检查的方法提前退出方法是首先想象一个包围圆的正方形,从(-r,-r)到(r,r),其中r是圆的半径,圆的中心是(0,0) 并检查矩形是否在这个正方形内。这应该快得多,并且与圆的碰撞检查只有在成功时才需要进行。

编辑:@hvd 添加了一个绝妙的想法,用于提前退出正面检查。如果矩形在内正方形内,它肯定在圆内。

根据矩形与圆形的大小,您还可以深入一级并在内部正方形和圆形之间制作矩形。但是你需要检查查询到的矩形的点是否都在任意一个矩形(+内正方形)中,并且不需要都在同一个。

【讨论】:

  • @hvd 我的矩形可能会变得非常小,因此假设并不总是有效。
【解决方案2】:

因此,在大多数情况下,我们可以确定正方形是圆形,然后我们的任务就会变得更容易。它会是这样的

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
}

希望对你有帮助

【讨论】:

  • 这可能适用于较小的矩形,但不适用于较大的矩形。仅供参考,我正在设计一个支持 1 到 3,000,000 个矩形的控件。
  • 你说的不对。如果矩形很小,它看起来更类似于圆形。真正的问题是大矩形,因为它的角落附近的区域(不适合圆形)更大。但这对你来说不是问题
【解决方案3】:

只需评论@Karthik T 的建议即可。用矩形表示圆形,您可以检查它:

  • 忽略顶部边界高于圆形顶部边界的矩形
  • 忽略底部边界低于圆形底部边界的矩形
  • 在圆的左边界之前忽略左边界的矩形
  • 在圆的右边界之后忽略右边界的矩形
  • 休息是在里面

因此,您只有 4 个检查而不是 8 个。

之后,您可以将圆形分割成象限并将矩形分类为案例:

  • 如果右下角位于左上象限 - 仅检查左上角(按距离)
  • 如果左下角位于右上象限 - 仅检查右上角
  • 如果右上角在左下象限 - 仅检查左下角
  • 如果左上角在右下象限 - 仅检查右下角
  • 如果右下角和左下角在正方形的上半部分 - 仅检查左上角和右上角
  • ...
  • 休息(每个角都在自己的象限中)按距离检查所有角。

更新:实际上,先对矩形进行分类,然后在正方形之外过滤掉,然后按大小写过滤掉,效果会更好。

代码示例:

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()));
        }
    }

}

顺便说一句:考虑在Quadtree中组织矩形

【讨论】:

  • 抱歉延迟回复,我被拉到其他任务。我会在周末看看你的建议。
【解决方案4】:

如果你检查 4 个角 (x,y) 是否更接近球心然后半径的长度会快得多? 例子

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

如果有任何不符合条件的正方形的角,则中断计算。

【讨论】:

  • 我想你应该阅读问题。已经有解决方案,不需要那么慢的sqrt。
  • 如果不使用 sqrt(a^2 + b^2),你如何计算侦察距离?它是简单/基本的数学,对于他所做的计算,如果他发现第一个角落在外面,他不会破坏计算,他会为所有角落计算它,而不管以前的角落......
  • 您可以计算距离的平方。 IE。而不是sqrt(dx^2 + dy^2) &lt; R,您可以使用(dx^2 + dy^2) &lt; R^2。请注意R^2 应该只计算一次这一事实。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多