2
头图

Preface

"Data Structure-Using JS to Realize Quadtree" above briefly introduces some implementations and application scenarios of quadtrees.

Not much to talk about what started today.

First look at the quadtree test renderings

quadtree.gif

Body coding part

Realization ideas

  • Quadtree
  • Comparing collision nodes for quadtree search

    The sample uses canvas2d for drawing and JavaScript for implementation.

Implementation process

Define quadtree data structure
/**
    *四叉树构造函数
    *@四叉树 2D类碰撞 需要四叉树边界/节点层数/
    *@param{Rect}节点的边界({x,y,width,height})
    *@param{number}[max\u objects=10]在拆分为4个子节点之前,节点可以容纳的最大对象数 默认:4
    *@param{number}[max\u levels=4]根四叉树内的总最大级别 默认为 4
    *@param{number}[level=0] 深度级别,子节点需要 默认:0  初始深度
*/
function Quadtree(bounds, max_objects, max_levels, level) {
        
        this.max_objects    = max_objects || 4;
        this.max_levels     = max_levels || 4;
        
        this.level  = level || 0;
        this.bounds = bounds;
        
        this.objects    = [];
        this.nodes      = [];
 };

Add objects/If it exceeds max_objects then split the child node

/*
    *将对象插入节点。如果节点
    *超过容量时,将拆分并添加所有
    *对象到其相应的子节点。
    *@param{Rect}要添加的对象的pRect边界({x,y,width,height})
    *@memberof四叉树
*/
Quadtree.prototype.insert = function(pRect) {

    var i = 0,
        indexes;

    //如果存在子节点,那么找到子节点像子节点添加数据(子节点就是子树)
    if(this.nodes.length) {
        indexes = this.getIndex(pRect);

        for(i=0; i<indexes.length; i++) {
            this.nodes[indexes[i]].insert(pRect);     
        }
        return;
    }

    //如果没找到不存在nodes 向objects添加
    this.objects.push(pRect);

    // 如果objects超出最大max_objects 那么就拆分
    // 开始
    if(this.objects.length > this.max_objects && this.level < this.max_levels) {

        //拆分子节点 (split方法可以移步仓库看代码,很简单。后续不贴了)
        if(!this.nodes.length) {
            this.split();
        }

        //将所有objects添加到对应的nodes 
        for(i=0; i<this.objects.length; i++) {
            indexes = this.getIndex(this.objects[i]); //找到对应的nodes
            for(var k=0; k<indexes.length; k++) {
                this.nodes[indexes[k]].insert(this.objects[i]);
            }
        }

        //清空这个objects
        this.objects = [];
        // 超出max_objects逻辑结束
    }
 };

Obtain

/**
    *确定对象属于哪个节点
    *@param{Rect}要检查的区域的pRect边界({x,y,width,height})
    *@return{number[]}相交子节点的索引数组(0-3=右上、左上、左下、右下)
    *@memberof四叉树
*/
Quadtree.prototype.getIndex = function(pRect) {
    
    var indexes = [],
        verticalMidpoint    = this.bounds.x + (this.bounds.width/2), //x中心
        horizontalMidpoint  = this.bounds.y + (this.bounds.height/2);    //y中心
    // 判断属于哪个区域
    var startIsNorth = pRect.y < horizontalMidpoint,
        startIsWest  = pRect.x < verticalMidpoint,
        endIsEast    = pRect.x + pRect.width > verticalMidpoint,
        endIsSouth   = pRect.y + pRect.height > horizontalMidpoint;    

    //右上 quad
    if(startIsNorth && endIsEast) {
        indexes.push(0);
    }
    
    //左上 quad
    if(startIsWest && startIsNorth) {
        indexes.push(1);
    }

    //左下 quad
    if(startIsWest && endIsSouth) {
        indexes.push(2);
    }

    //右下 quad
    if(endIsEast && endIsSouth) {
        indexes.push(3);
    }
    return indexes;
};

Given a rect structure for collision

/**
    *返回所有可能与给定对象碰撞的对象
    *@param{Rect}要检查的对象的pRect边界({x,y,width,height})
    *@return{Rect[]}数组,包含所有检测到的对象
    *@memberof四叉树
*/
Quadtree.prototype.retrieve = function(pRect) {

    var indexes = this.getIndex(pRect),
        returnObjects = this.objects;

    //if we have subnodes, retrieve their objects
    if(this.nodes.length) {
        for(var i=0; i<indexes.length; i++) {
            returnObjects = returnObjects.concat(this.nodes[indexes[i]].retrieve(pRect));
        }
    }

    //objects去重
    returnObjects = returnObjects.filter(function(item, index) {
        return returnObjects.indexOf(item) >= index;
    });

    return returnObjects;
};

Clear quadtree

/**
    * 清除四叉树
    * @memberof Quadtree
*/
Quadtree.prototype.clear = function() {
    
    this.objects = [];
 
    for(var i=0; i < this.nodes.length; i++) {
        if(this.nodes.length) {
            this.nodes[i].clear();
          }
    }

    this.nodes = [];
};

quadtree other related warehouses D3 Quadtree

Let's take D3 for learning. First of all, it is more comprehensive (you can see a specific structural module in the screenshot below). There are add cover, data, find... and the details are great, it is worth learning. Here is a simple example

image.png

Take a module as an example: find.js is also looking for the corresponding nodes and then appending the same implementation. The difference is that it can be understood as access to record/no access to duplicate points, and there is the principle of nearest access (this can do some other work) i = (y >= ym) << 1 | (x >= xm) , yes Those who are interested can go and learn.
image.png

In general, there are some things in D3 that can help you change and improve your thinking, and learning is still necessary. By the way, in practice, you can try to use it in combination with the d3-force module for better results.

Concluding remarks

The quadtree is a structure of the tree. The above example is a good example of the practice of quadtree collision in 2D. If you have any questions, please feel free to leave a message.

Finally~ I am very sorry for the day! ! !

Other links

above sample code warehouse

D3 Quadtree


wlove
6.9k 声望1.8k 粉丝

wx:wywin2023