最小边界四叉树节点

发布于 2024-09-10 20:24:58 字数 1971 浏览 5 评论 0原文

我正在编写一个基于整数的四叉树结构,它从节点开始构建,而不是向下构建。为此,我需要发现包含所有元素的下一个最大节点。如果我定义了一个预先存在的节点,然后尝试在该节点的边界之外添加一个项目,则需要创建一个更大的节点来包含它们。我有(认为很聪明)用于查找单个点周围的边界框的代码:

public static Rectangle BoundingRectangle(Point p, int magnitude)
{
    Rectangle bounds = new Rectangle()
    {
        X = (p.X & ~((1 << magnitude) - 1)),
        Y = (p.Y & ~((1 << magnitude) - 1)),
        Width = (1 << magnitude),
        Height = (1 << magnitude)
    };
    return bounds;
}

[请注意,点 (0,0) 周围的框是位置处大小为 (1,1) 的框(0,0),不在位置(-.5,-.5),因为它都是基于整数的]

这将始终(据我所知)返回一个适合的框成四叉树作为节点。例如,new Rectangle(0,0,2,2) 是可以接受的,new Rectangle(2,2,2,2) 也是可以接受的,但是 new Rectangle(1,1,2,2) 则不会。

我的问题是我不知道如何为矩形而不是点来完成此操作。我能想到的唯一解决方案是循环遍历大小不断增加的框,但我确信必须有一些我无法想到的 O(1) 解决方案。


示例:

Rectangle(X,Y,1,1) -> Rectangle(X,Y,1,1)
Rectangle(0,0,2,2) -> Rectangle(0,0,2,2)
Rectangle(1,1,2,2) -> Rectangle(0,0,4,4)
Rectangle(1,1,3,3) -> Rectangle(0,0,4,4)
Rectangle(0,5,2,2) -> Rectangle(0,4,4,4)
Rectangle(3,3,2,2) -> Rectangle(0,0,8,8)

实现:

private static int BitScanReverse(int mask)
{
    int index = 0;
    while (mask > 1)
    {
        mask >>= 1;
        index++;
    }
    return index;
}

public static Rectangle BoundingRectangle(Point p, int magnitude)
{
    Rectangle bounds = new Rectangle()
    {
        X = (p.X & ~((1 << magnitude) - 1)),
        Y = (p.Y & ~((1 << magnitude) - 1)),
        Width = (1 << magnitude),
        Height = (1 << magnitude)
    };
    return bounds;
}

public static Rectangle BoundingRectangle(Rectangle r, int magnitude)
{
    int msb = BitScanReverse((r.X ^ (r.X + r.Width - 1)) | (r.Y ^ (r.Y + r.Height - 1)));
    return BoundingRectangle(r.Location, Math.Max(msb + 1, magnitude));
}

I'm writing an integer-based quadtree structure which builds up from the node, and not down. To do this, I need to discover the next largest node which contains all my elements. If I have a preexisting node defined, then try to add an item outside of that node's bounds, it needs to create a larger node to encompass them both. I have (what I think is clever) code for finding the bounding box around a single point:

public static Rectangle BoundingRectangle(Point p, int magnitude)
{
    Rectangle bounds = new Rectangle()
    {
        X = (p.X & ~((1 << magnitude) - 1)),
        Y = (p.Y & ~((1 << magnitude) - 1)),
        Width = (1 << magnitude),
        Height = (1 << magnitude)
    };
    return bounds;
}

[Note that the box around point (0,0) is a boxof size (1,1) at location (0,0), not at location (-.5,-.5), since it's all integer-based]

This will always (from what I can tell,) return a box which would fit into a quadtree as a node. For example, new Rectangle(0,0,2,2) would be acceptable, as would new Rectangle(2,2,2,2), but new Rectangle(1,1,2,2) would not be.

My problem is that I can't figure out how to accomplish this for a rectangle, instead of a point. The only solution I can think of would be to loop through boxes of increasing magnitude, but I'm sure there has to be some O(1) solution I'm just not able to think of.


Examples:

Rectangle(X,Y,1,1) -> Rectangle(X,Y,1,1)
Rectangle(0,0,2,2) -> Rectangle(0,0,2,2)
Rectangle(1,1,2,2) -> Rectangle(0,0,4,4)
Rectangle(1,1,3,3) -> Rectangle(0,0,4,4)
Rectangle(0,5,2,2) -> Rectangle(0,4,4,4)
Rectangle(3,3,2,2) -> Rectangle(0,0,8,8)

Implementation:

private static int BitScanReverse(int mask)
{
    int index = 0;
    while (mask > 1)
    {
        mask >>= 1;
        index++;
    }
    return index;
}

public static Rectangle BoundingRectangle(Point p, int magnitude)
{
    Rectangle bounds = new Rectangle()
    {
        X = (p.X & ~((1 << magnitude) - 1)),
        Y = (p.Y & ~((1 << magnitude) - 1)),
        Width = (1 << magnitude),
        Height = (1 << magnitude)
    };
    return bounds;
}

public static Rectangle BoundingRectangle(Rectangle r, int magnitude)
{
    int msb = BitScanReverse((r.X ^ (r.X + r.Width - 1)) | (r.Y ^ (r.Y + r.Height - 1)));
    return BoundingRectangle(r.Location, Math.Max(msb + 1, magnitude));
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

新一帅帅 2024-09-17 20:24:58

让我们首先考虑它的一维版本。我们没有矩形,而是有偏移量和长度。

边界矩形的“大小”告诉您要忽略多少位。

假设 offset = 1234 且 length = 56。我们希望忽略足够多的位,以便“offset”(1234) 和“offset+length-1”(1289) 映射到相同的数字。

1234 = 10011010010
1289 = 10100001001

显然,我们需要在这里忽略除前 2 位之外的所有位(忽略 9 位)。

我们可以通过编程方式使用 1234 XOR 1289(即 475)找到这一点,

1234 = 10011010010
1289 = 10100001001
 475 = 00111011011

然后找到 475 的最高有效设置位。大多数处理器都有这条指令(在 Windows 上,它是 _BitScanReverse)。

现在,对于 2D 情况,我们需要对 X 轴和 Y 轴进行异或。然后,我们将这两个结果进行“或”运算。最后,找到最高有效的设置位。

因此,在伪代码中:

magnitude = MSBof( ( X ^ (X+width-1) ) | ( Y ^ (Y+height-1) ) )

要获取实际的矩形,只需使用帖子中的函数即可。传入新的Point(X,Y)。

Let's consider the one-dimensional version of this first. Instead of a rectangle, we have an offset and length.

The 'magnitude' of your bounding rectangle is telling you how many bits to ignore.

Let's say offset = 1234 and length = 56. We want to ignore enough bits so that 'offset' (1234) and 'offset+length-1' (1289) map to the same number.

1234 = 10011010010
1289 = 10100001001

Clearly, we need to ignore all but the first 2 bits here (ignore 9 bits).

We can find this programmatically with 1234 XOR 1289 (which is 475)

1234 = 10011010010
1289 = 10100001001
 475 = 00111011011

and then finding the most significant set bit of 475. Most processors have this instruction (on Windows, it's _BitScanReverse).

Now, for your 2D case, we need to get the XOR for both the X-axis and Y-axis. Then, we OR those two results together. Finally, find the most significant set bit.

So, in pseudo-code:

magnitude = MSBof( ( X ^ (X+width-1) ) | ( Y ^ (Y+height-1) ) )

To get the actual rectangle, just use the function in your post. Pass in new Point(X, Y).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文