TS 四叉树 矩形



export default class QuadtreeRect{

    max_objects = 10;
    max_levels = 4;
    level = 0;
    bounds = null;
    objects = [];
    nodes = [];

    
    /**
     * Quadtree Constructor
     * @class Quadtree
     * @param {Rect} bounds                 bounds of the node ({ x, y, width, height })
     * @param {number} [max_objects=10]     (optional) max objects a node can hold before splitting into 4 subnodes (default: 10)
     * @param {number} [max_levels=4]       (optional) total max levels inside root Quadtree (default: 4) 
     * @param {number} [level=0]            (optional) depth level, required for subnodes (default: 0)
     */
    constructor(bounds : any, max_objects : number = 10, max_levels : number = 4, level :number = 0) { 
        this.max_objects    = max_objects;
        this.max_levels     = max_levels;
        
        this.level  = level;
        this.bounds = bounds;
        
        this.objects    = [];
        this.nodes      = [];
    }

    split(){
        var nextLevel   = this.level + 1,
            subWidth    = this.bounds.width/2,
            subHeight   = this.bounds.height/2,
            x           = this.bounds.x,
            y           = this.bounds.y;        
     
        //top right node
        this.nodes[0] = new QuadtreeRect({
            x       : x + subWidth/2, 
            y       : y + subHeight/2, 
            width   : subWidth, 
            height  : subHeight
        }, this.max_objects, this.max_levels, nextLevel);
        
        //top left node
        this.nodes[1] = new QuadtreeRect({
            x       : x - subWidth/2, 
            y       : y + subHeight/2, 
            width   : subWidth, 
            height  : subHeight
        }, this.max_objects, this.max_levels, nextLevel);
        
        //bottom left node
        this.nodes[2] = new QuadtreeRect({
            x       : x - subWidth/2, 
            y       : y - subHeight/2, 
            width   : subWidth, 
            height  : subHeight
        }, this.max_objects, this.max_levels, nextLevel);
        
        //bottom right node
        this.nodes[3] = new QuadtreeRect({
            x       : x + subWidth/2, 
            y       : y - subHeight/2, 
            width   : subWidth, 
            height  : subHeight
        }, this.max_objects, this.max_levels, nextLevel);
    }

    /**
     * Determine which node the object belongs to
     * @param {Rect} pRect      bounds of the area to be checked ({ x, y, width, height })
     * @return {number[]}       an array of indexes of the intersecting subnodes (0-3 = top-right, top-left, bottom-left, bottom-right / ne, nw, sw, se)
     * @memberof Quadtree
     */
    getIndex(pRect) {
        
        var indexes = [],
            verticalMidpoint    = this.bounds.x,
            horizontalMidpoint  = this.bounds.y,
            rHh                 = pRect.height * 0.5,
            rWh                 = pRect.width  * 0.5;
        var startIsNorth = pRect.y + rHh > horizontalMidpoint,
            startIsWest  = pRect.x - rWh < verticalMidpoint,
            endIsEast    = pRect.x + rWh > verticalMidpoint,
            endIsSouth   = pRect.y - rHh < horizontalMidpoint;

        //top-right quad
        if(startIsNorth && endIsEast) {
            indexes.push(0);
        }
        
        //top-left quad
        if(startIsWest && startIsNorth) {
            indexes.push(1);
        }

        //bottom-left quad
        if(startIsWest && endIsSouth) {
            indexes.push(2);
        }

        //bottom-right quad
        if(endIsEast && endIsSouth) {
            indexes.push(3);
        }

        return indexes;
    }

    
    /**
     * Insert the object into the node. If the node
     * exceeds the capacity, it will split and add all
     * objects to their corresponding subnodes.
     * @param {Rect} pRect      bounds of the object to be added ({ x, y, width, height })
     * @memberof Quadtree
     */
    insert(pRect) {
        
        var i = 0,
            indexes;
        
        //if we have subnodes, call insert on matching subnodes
        if(this.nodes.length) {
            indexes = this.getIndex(pRect);
     
            for(i=0; i<indexes.length; i++) {
                this.nodes[indexes[i]].insert(pRect);     
            }
            return;
        }
     
        //otherwise, store object here
        this.objects.push(pRect);

        //max_objects reached
        if(this.objects.length > this.max_objects && this.level < this.max_levels) {

            //split if we don't already have subnodes
            if(!this.nodes.length) {
                this.split();
            }
            
            //add all objects to their corresponding subnode
            for(i=0; i<this.objects.length; i++) {
                indexes = this.getIndex(this.objects[i]);
                for(var k=0; k<indexes.length; k++) {
                    this.nodes[indexes[k]].insert(this.objects[i]);
                }
            }

            //clean up this node
            this.objects = [];
        }
    }
     
     
    /**
     * Return all objects that could collide with the given object
     * @param {Rect} pRect      bounds of the object to be checked ({ x, y, width, height })
     * @return {Rect[]}         array with all detected objects
     * @memberof Quadtree
     */
    retrieve(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));
            }
        }

        //remove duplicates
        returnObjects = returnObjects.filter(function(item, index) {
            return returnObjects.indexOf(item) >= index;
        });
     
        return returnObjects;
    };
    
    
    /**
     * Clear the quadtree
     * @memberof Quadtree
     */
    clear() {
        
        this.objects = [];
     
        for(var i=0; i < this.nodes.length; i++) {
            if(this.nodes.length) {
                this.nodes[i].clear();
              }
        }

        this.nodes = [];
    };
}
posted @ 2021-04-17 15:11  alps_01  阅读(185)  评论(0编辑  收藏  举报