【问题标题】:Smallest bounding quadtree node最小边界四叉树节点
【发布时间】:2010-07-20 21:02:30
【问题描述】:

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

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

【问题讨论】:

  • 很棒的问题。如果到时候没有人回答,我会在家里解决这个问题。
  • 谢谢。它一直困扰着我。我想我会向 SO 更聪明的人大声疾呼。 =]
  • 您打算如何处理重叠多个四叉树节点的矩形?即 (1,1,3,3) 与宽度/高度为 2 的 4 个节点重叠。如果你想说它在 (0,0,4,4) 内部,那么树的根部总会有小东西(最大的节点)。
  • 你还会把它放在哪里?矩形 (1,1,3,3) 将放置在节点 (0,0,4,4) 中,然后如果添加矩形 (0,4,2,2),它将放置在节点 (0, 4,4,4),这两个都将被放置在一个新的根节点(0,0,8,8)中。
  • 不应该 Rectangle(1,1,1,1) 映射到 Rectangle(1,1,1,1) 吗?

标签: c# algorithm quadtree


【解决方案1】:

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

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

假设偏移量 = 1234 和长度 = 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 轴的 XOR。然后,我们将这两个结果组合在一起。最后,找到最重要的设置位。

所以,在伪代码中:

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

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

【讨论】:

  • 这看起来不错,但我找不到在 c# 中实现MSBof 的方法,而无需大量的位旋转。有什么建议吗?
  • 很好的建议。我带着一点玩弄,但效果很好。我在上面发布了我的实现。
  • 好的,太好了!实现的一个挑剔:使用“while (mask > 0)”而不是“while (mask > 1)”。那么您以后就不需要使用 +1 来修复它了。以下是其他实现:graphics.stanford.edu/~seander/bithacks.html#IntegerLog
  • 我已经完成了我正在处理的结构,并且非常好地使用了这种方法。再次感谢,如果您想对我的课程发表评论,可以在此答案中找到:stackoverflow.com/questions/3142919/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-07-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多