构建四叉树以使相邻节点之间只有一个层级差异(LOD)

8

我正在尝试构建四叉树,它基于一个位置和一个最大深度来细分一个区域。我想用这个来实现地形的细节级别。换句话说,我有一个位置(x, y),一个区域(x, y, width),并将其传递给一些方法build(region, position, maxDepth),然后应该返回覆盖整个平面的节点数组。

我的实现与此略有不同,因为深度和根区域由Quadtree对象表示。要获得总的细分,需要调用成员方法get(x, y, radius),它将返回一个覆盖整个根区域的节点数组(请查看下面的代码)。

为了避免出现伪影,对于相邻节点之间最多只能有1个级别是非常重要的。

下面是一个可接受结果的示例。相邻节点之间的最大差异为1。(你可以忽略对角线,它们只是三角剖分的结果)

Example 1: Correct

相反,这个示例不可接受,因为三个相邻节点之间的差异为2。

Example 2: Incorrect

为了修复这个问题,我们需要像这样分割相邻的节点:

Example 3: Corrected

另一个可接受的解决方案示例如下:

Example 4: Correct

这是我现在拥有的代码。

class Quadtree {

    constructor({ x, y, width }, levels = 6, parent = null) {

        this.x = x;
        this.y = y;
        this.width = width;

        this.parent = parent;
        this.children = null;

        if (levels > 0) {
            this.children = this.constructor.split(this, levels); // recursively split quadtree.
        }
    }

    /**
     * Checks for intersection.
     * @param  {x, y, radius} circle
     * @param  {x, y, width} square
     * @return {boolean}
     */
    static intersects(circle, square) {
        let deltaX = circle.x - Math.max(square.x, Math.min(circle.x, square.x + square.width));
        let deltaY = circle.y - Math.max(square.y, Math.min(circle.y, square.y + square.width));

        return (deltaX * deltaX + deltaY * deltaY) < (circle.radius * circle.radius);
    }

    /**
     * Splits a node.
     */
    static split(node, levels) {
        let width = node.width / 2;
        let x = node.x;
        let y = node.y;

        // bottom left
        let q1 = new Quadtree({
            x: x,
            y: y,
            width
        }, levels - 1, node);

        // bottom right
        let q2 = new Quadtree({
            x: x + width,
            y: y,
            width
        }, levels - 1, node);

        // top left
        let q3 = new Quadtree({
            x: x,
            y: y + width,
            width
        }, levels - 1, node);

        // top right
        let q4 = new Quadtree({
            x: x + width,
            y: y + width,
            width
        }, levels - 1, node);

        return [q1, q2, q3, q4];
    }

    /**
     * Gets the least amount of nodes covered by the given circle.
     * @param  {x, y, radius} circle
     * @return {Array} An array of Quadtree-nodes.
     */
    get(circle) {

        if (this.children !== null && this.constructor.intersects(circle, { x: this.x, y: this.y, width: this.width })) { // we need to go deeper.
            return this.children.reduce((arr, child) => {

                return arr.concat(child.get(circle));

            }, []);

        } else {
            return [ this ];
        }
    }
}

这是一个关于如何使用它的示例:
let tree = new Quadtree({ x: 0, y: 0, width: 100}, 2);
let nodes = tree.get({x: 15, y: 15, radius: 5}); // returns an array of nodes covering the whole region.

示例:

tree.get({x: -15, y: -15, radius: 5});
[ Quadtree { x: 0, y: 0, width: 100 } ] // returns the top node.

tree.get({x: 15, y: 15, radius: 5});
[ 7 Quadtree-nodes ]

最后一个示例返回了七个类似于以下的四叉树节点:
#-------#-------#
|       |       |
|       |       |
|       |       |
#---#---#-------#
|   |   |       |
#---#---|       |
|   |   |       |
#---#---#-------#

如果有用的话,四叉树节点还会存储它们父节点的指针。
我是不是走错了方向?通过回溯树来强制执行约束条件,并跟踪位置等内容,对我来说似乎过于复杂。这里是否有不同的角度?

请添加更多代码,以便更清楚地了解您使用的数据结构。此外,您似乎在包含的代码片段中没有使用深度概念。 - trincot
你确定邻居之间只有一个级别的差异是可能的吗?我认为如果你有稠密的“数据”簇远离彼此,这可能是不可能的。你必须在树中插入空节点或插入“虚假”的数据点来增加稠密点簇周围的细节级别... - TilmannZ
根据要求,我已经添加了更多的代码和更好的示例。 - crazymage
请@Mario打开有关赏金的对话框。你收到这个ping了吗? - Guy Coder
1
感兴趣的内容:如何向赏金提供者发送消息? - Guy Coder
显示剩余3条评论
1个回答

3

只需稍微修改算法,就可以确保相邻的两个节点之间的深度差不超过两级。这个想法是,在圆与某个矩形相交时,根据节点方块及其深度决定该节点是否分裂。

考虑从最深的级别向上开始,影响一个给定深度的节点是否需要分裂的因素。

  1. 最大深度的节点不能被分裂。

  2. 深度为 maxDepth - 1 的节点只有在与圆相交时才应该被分裂。

  3. 深度为 maxDepth - 2 的节点应该被分裂,如果它与圆相交或者与深度为 maxDepth 的节点相邻(因此保持不分裂将违反要求)。但是后一个条件等价于与已分裂的深度为 maxDepth - 1 的节点相邻。而这又等价于具有相邻深度为 maxDepth - 1 的节点与圆相交(参见前一段)。我们如何确定是否存在这种情况而不必遍历相邻节点并回溯?注意,当前节点 (x, y, x + width, y + width) 及其所有更深层次的相邻节点的并集等于正方形 (x - width/2, y - width/2, x + width*2, y+width*2) 与根正方形的交集。因此整个条件归结为在圆、根正方形和当前正方形膨胀到两倍大小之间找到交点。(参见图片。)

  4. 将类似的推理应用到下一层,深度为 maxDepth - 3 的节点 (x, y, x + width, y + width) 应该被分裂,如果圆、根正方形和正方形 (x - width*3/4, y - width*3/4, x + width*5/2, y + width*5/2) 相交。

  5. 最后,将这个推广到任意深度的节点,节点 (x, y, x + width, y + width) 应该被分裂,当且仅当圆、根正方形和正方形 (x - width*inflationRatio/2, y - inflationRatio/2, x + width*(1+inflationRatio), y + width*(1+inflationRatio)) 相交,其中 inflationRatio = 2^(2-maxDepth+depth)。(可以通过归纳证明,每个膨胀的正方形等于节点及其所有更深层次的相邻膨胀正方形的并集。)

enter image description here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接