Quadtree problem, node not properly selected

by omgnoseat
Drag around the square and you will see that it's node is not properly selected. Something is going wrong in the "node:addItem" function. 

I apologize for the messy coding, tried everything to get it to work !
♥0 | Line 453 | Modified 2011-04-09 23:38:12 | MIT License
play

ActionScript3 source code

/**
 * Copyright omgnoseat ( http://wonderfl.net/user/omgnoseat )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/bO6Y
 */

package  
{
    import flash.display.MovieClip;

    [SWF(frameRate=60, width=800, height=600)]

    public class CollisionMain extends MovieClip
    {
        var test:CollisionTest;
        
        public function CollisionMain() 
        {
            init();
        }
        
        private function init():void 
        {
            test = new CollisionTest();
            addChild(test);
            
            var mousecoord:MouseCoord = new MouseCoord();
            addChild(mousecoord);
        }
        
    }

}









    import flash.display.MovieClip;
    import flash.display.Shape;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.geom.Rectangle;
    import net.hires.debug.Stats;
    /**
     * ...
     * @author Martino Wullems
     */
   class CollisionTest extends MovieClip
    {
        public static var shape:Shape; //for drawing debug stuff on
        public static var holder:Sprite;
        var world:TestWorld;
        
        public function CollisionTest() 
        {
            holder = new Sprite();
            addChild(holder);
            
            shape = new Shape();
            addChild(shape);
            
            addEventListener(Event.ADDED_TO_STAGE, onStage);
        }
        
        private function onStage(e:Event):void 
        {
            removeEventListener(Event.ADDED_TO_STAGE, onStage);
            
            SetupWorld();
            //addChild(new Stats());
        }
        
        private function SetupWorld():void 
        {
            world = new TestWorld();
            addChild(world);
            
            addObjects(1);
        }
        
        private function addObjects(amount:int):void 
        {
            for (var i:int = 0; i < amount; ++i) {
                
                var square:Square = new Square(14);
                //square.x = Math.random() * stage.stageWidth;
                //square.y = Math.random() * stage.stageHeight;
                
                square.x = 5;
                square.y = 230;
                square.vx = (Math.random() * 2) + 1;
                square.vy = (Math.random() * 2) + 1;
                square.hitBounds = new Rectangle(5, 230, 14, 14);
                
                world.addObject(square);
            }
        }
        
    }










    import flash.display.MovieClip;
    import flash.display.Sprite;
    import flash.events.Event;
    /**
     * ...
     * @author Martino Wullems
     */
     class TestWorld extends MovieClip
    {
        public var objects:Array;
        public var collisionMethod:String;
        var quadtree:QuadTree;
        
        public function TestWorld()
        {
            collisionMethod = CollisionMethod.QUADTREE;
            addEventListener(Event.ADDED_TO_STAGE, onStage);
        }
        
        private function onStage(e:Event):void 
        {
            removeEventListener(Event.ADDED_TO_STAGE, onStage);
            
            initWorld();
        }

        private function initWorld():void 
        {
            objects = new Array();
            quadtree = new QuadTree(stage.stageWidth, stage.stageHeight);
            addChild(quadtree);
            
            quadtreeDebugDraw();
            
            addEventListener(Event.ENTER_FRAME, loopWorld);
        }
        
        private function quadtreeDebugDraw():void 
        {
        }
        
        
        
        
        
        
        private function loopWorld(e:Event):void 
        {
                        
            for (var i:int = 0; i < objects.length; i++) {
                
                objects[i].isHit = false;
                //MoveObject(objects[i]);
                //CheckCollision(i);// doesn't work here for some reason [case 1]
                
            }
            
            switch(collisionMethod) {
                
                case "REGULAR":
                    CheckCollision(0); //[case 2]
                break;
                
                case "QUADTREE":
                    CheckQuadTreeCollision();
                break;
            }
            
        }
        
        private function CheckQuadTreeCollision():void 
        {
            
            for (var i:int = 0; i < objects.length; i++) {
                
                var object:* = objects[i];    
                if(object.currentNode != null){
                    //object.nodeDraw();
                }
                
                //check collision for every object in the objects node
                //for (var j:int = 0; j < object.currentNode.objects.length; j ++) {
                    
                    
                //}
                
            }

        }
        
        private function CheckCollision(i:int):void 
        {                
                //test collision
                for (var i:int = 0; i < objects.length; i++){ //only use in case 2
                
                    var elementA:CollisionObject;
                    var elementB:CollisionObject;
                    elementA = objects[i];
                    
                for (var j:int = i + 1; j < objects.length; j++) {
                         
                    if (j <= i){
                       continue; //j resets each I loop and therefor sets collision to false while it could be true [fixed with i + 1]
                          }
                          
                    elementB = objects[ j ]// as ObjSprite;
                          
                    if (elementA.hitTestObject(elementB)) {
                       elementA.isHit = elementB.isHit = true;
                              
                              
                            /////////////////
                            //  CHECK X Y //
                            ////////////////
                                              
                            /*  if (elementA.x + elementA.width < elementB.x) {
                    } else if (elementA.x > elementB.x + elementB.width) {
                              // elementA.isHit = elementB.isHit = false;
                              
                    } else if (elementA.y + elementA.height < elementB.y) {
                              // elementA.isHit = elementB.isHit = false;
                              
                    } else if (elementA.y > elementB.y + elementB.height) {
                              
                              // elementA.isHit = elementB.isHit = false;
                    } else {
                        elementA.isHit = elementB.isHit = true;
                    */

                    }
                }
            } //[case 2]

                

                
            
        }
        
        private function MoveObject(object:CollisionObject):void 
        {
            object.x += object.vx;
            object.y += object.vy;
            
            ////////////////////
            //check boundaries//
            ////////////////////
            
            if (object.x > stage.stageWidth)
            {
                object.vx *= -1;
                
            }else if (object.x < 0)
            {
                object.vx *= -1;
                
            }else if (object.y > stage.stageHeight)
            {
                object.vy *= -1;
            
            }else if (object.y < 0)
            {
                object.vy *= -1;
            }
            
            //object.isHit = false;// where do we check when it isn't colliding?
        }
        
        public function addObject(object:CollisionObject):void
        {
            objects.push(object);
            addChild(object);
            
            switch(collisionMethod) {
                
                case "REGULAR":
                    //nothing special
                break;
                
                case "QUADTREE":
                
                    quadtree.addItem(object);
                
                break;
            }
        }
        
    }



    import flash.display.Sprite;
    import flash.events.Event;
    /**
     * ...
     * @author Martino Wullems
     */
    class QuadTree extends Sprite
    {
        public var Width:int;
        public var Height:int;
        public var topNode:Node;
        
        public static var TopNode:Node;
        
        public function QuadTree(WIDTH:int, HEIGHT:int) 
        {
            Width = WIDTH;
            Height = HEIGHT;
            
            topNode = new Node(0, 0, WIDTH, HEIGHT, 5, NodeLabel.TOP); //make top node, generates 4 child nodes for each layer
            topNode.Label = NodeLabel.TOP;
            addChild(topNode);
            TopNode = topNode;
        }
        
        public function addItem(item:*):void {
            
            //topnode is entire screen!
            topNode.addItem(item);

        }
          
    }


    import flash.display.Shape;
    import flash.display.Sprite;
    import flash.events.MouseEvent;
    import flash.geom.Rectangle;
    /**
     * ...
     * @author Martino Wullems
     * 
     * contains 4 child nodes till final layer is reached.
     * checks objects in furthest node agains each other
     */
    class Node extends Sprite
    {
        public var Width:int;
        public var Height:int;
        public var Layer:int;
        
        public var rootNode:Node;
        public var parentNode:Node;
        public var childNodes:Array;
        public var objects:Array;
        public var Label:String;
        
        public var bounds:Rectangle;
        
        public var nw:Node;
        public var ne:Node;
        public var sw:Node;
        public var se:Node;
        
        public function Node(X:int, Y:int, WIDTH:int, HEIGHT:int, LAYER:int, LABEL:String = null) //subspace
        {
            x = X;
            y = Y;
            Width = WIDTH;
            Height = HEIGHT;
            
            Layer = LAYER;
            if (LABEL != null) { Label = LABEL };
            
            objects = new Array();
            bounds = new Rectangle(0, 0, Width, Height);
            childNodes = new Array();
            
            if (Layer > 1) {
                
                generateChildNodes();
            }
            
            CollisionTest.shape = new Shape();
            CollisionTest.shape.graphics.lineStyle(Layer, 0x000000, 0.1);
            CollisionTest.shape.graphics.drawRect(X, Y, WIDTH,
            HEIGHT);
            CollisionTest.holder.addChild(CollisionTest.shape);
            
            addEventListener(MouseEvent.CLICK, getNode);
        }
        
        private function getNode(e:MouseEvent):void 
        {
            trace("[layer " + this.Layer + "] : " + this.Label + "x: " + this.x);
            e.target.generateChildNodes();
            trace("-----");
        }
        
        private function generateChildNodes():void 
        {
            //trace("generating child nodes");
            //trace("[layer " + this.Layer + "[" + this.Label + "]");
            
            for (var i:int = 1; i < 5; i++) { //4 child nodes
                
                var childNode:Node// = new Node(this.Width / 2, this.Height / 2, this.Layer - 1);
    
                
                switch(i) {
                    
                    case 1: //top left
                        nw = new Node(this.x, this.y, this.Width / 2, this.Height / 2, this.Layer - 1, NodeLabel.NW); 
                        //node is placed in current node, so 0 isn't stage 0 -  but parent 0;
                        
                        nw.Label = NodeLabel.NW;
                        childNodes.push(nw);
                        addChild(nw);
                    break;
                    
                    case 2: //top right
                        ne = new Node(this.x + (this.Width / 2), this.y, this.Width / 2, this.Height / 2,
                                        this.Layer - 1, NodeLabel.NE);
                        ne.Label = NodeLabel.NE;
                        childNodes.push(ne);
                        addChild(ne);
                    break;
                    
                    case 3: //down left
                        sw = new Node(this.x , this.y + (this.Height / 2 ), this.Width / 2, 
                                                    this.Height / 2, this.Layer - 1, NodeLabel.SW);
                        //childNode.x = this.x;
                        //childNode.y = this.y + (this.Height / 2 )
                        sw.Label = NodeLabel.SW;
                        childNodes.push(sw);
                        addChild(sw);
                    break;
                    
                    case 4:
                        se = new Node(this.x + (this.Width / 2),  this.y + (this.Height / 2 ), this.Width / 2, 
                                                    this.Height / 2, this.Layer - 1, NodeLabel.SE);
                        //childNode.x = this.x + (this.Width / 2);
                        //childNode.y = this.y + (this.Height / 2)
                        se.Label = NodeLabel.SE;
                        childNodes.push(se);
                        addChild(se);
                    break;
                    
                }
                //childNodes.push(childNode);
                //addChild(childNode);
                
                //trace("creating node: [layer " + childNodes[childNodes.length].Layer + "][" + childNodes[childNodes.length].Label + "]");
                
            }
        }
        
        public function addItem(item:*):void {
            /*
             * child nodes are counted x and y at zero again, not taking in account the location of the parent node !
             * [layer 4][NE] node.x: 275
                [layer 4][NE] node.y: 0

                [layer 3][NW] node.x: 0 --> parent node is 275, so child should be at least 275 aswell !
             * 
             * 
             * */
                
             var foundNode:Node;
            
            /*if(childNodes.length > 0 && this.Layer > 0){ //check if this isn't the bottom node
            
                trace("##########################################");
                trace("[layer " + this.Layer + "[" + this.Label + "]");
                trace("########################################## \n");
            
                for (var i:int = 0; i < childNodes.length; i++) {
                    //!go throgh each node to check if the item is in there!
        
                        
                    //check in which part of the node the item is [contains rectangle function]
                    if (item.x >= childNodes[i].x && //left
                        item.x + item.width <= childNodes[i].x + (childNodes[i].x + childNodes[i].Width) && //right
                        item.y >= childNodes[i].y && //up
                        item.y + item.height <= childNodes[i].y + (childNodes[i].y + childNodes[i].Height) //down
                    ){
                         

                        if (item.currentNode == null) {
                            item.currentNode == this;
                        }
                        
                        if (item.currentNode.Layer > childNodes[i].Layer && item.currentNode.Layer > 0) { //get best depth
                            //adds one layer deeper
                            //childNodes[i].objects.push(item);
                            
                            addToNode(childNodes[i], item);
                            
                        }
                         
                        
                    }else {
                        //this is best depth
                        trace("best depth is current node");
                        objects.push(item);
                        item.currentNode = this;
                    }
                }
                
            }*/
            
            
            //////////////////////////////////
            /////// NE, SE, SW, NW ///////////
            //////////////////////////////////
            
            //itemx - nodex seems to be lifted up 200px...
            //last node always picks nw
            if(this.Layer > 1){ //check if this isn't the bottom layer
        
                /////////
                // NE //
                ////////
                trace("###############################");
                trace("item.x: " + item.x);
                trace("item.y: " + item.y);
                
                if (item.x >= ne.x && //left
                    item.x + item.width <= ne.x + (ne.x + ne.Width) && //right
                    item.y >= ne.y && //up
                    item.y + item.height <= ne.y + (ne.y + ne.Height) //down
                ) {
                    
                    addToNode(ne, item)
                    
                /////////
                // NW //
                ////////    
                }else if (item.x >= nw.x && //left
                    item.x + item.width <= nw.x + (nw.x + nw.Width) && //right
                    item.y >= nw.y && //up
                    item.y + item.height <= (nw.y + (nw.y + nw.Height)) //down
                ) {
                    
                    addToNode(nw, item)
                    
                /////////
                // SW //
                ////////
                }else if (item.x >= sw.x && //left
                    item.x + item.width <= sw.x + (sw.x + sw.Width) && //right
                    item.y >= sw.y && //up
                    item.y + item.height <= sw.y + (sw.y + sw.Height) //down
                ) {
                    
                    addToNode(sw, item)
                    
                /////////
                // SE //
                ////////
                }else if (item.x >= se.x && //left
                    item.x + item.width <= se.x + (se.x + se.Width) && //right
                    item.y >= se.y && //up
                    item.y + item.height <= se.y + (se.y + se.Height) //down
                ) {

                    addToNode(se, item)
                }
                
                //doesn't fit in childnode, put in current node
                
                else {
                    
                    trace("doesn't fit anywhere");
                    addToNode(this, item);
                    this.addEventListener(MouseEvent.CLICK, clickNode);
                }
                
                ////////////////////////////////////
                ///// C O N T A I N S  R E C T /////
                ////////////////////////////////////
                
                /*if (nw.bounds.containsRect(item.hitBounds)) {
                    
                    addToNode(nw, item)
                    
                }else
                
                if (ne.bounds.containsRect(item.hitBounds)) {
                    
                    addToNode(ne, item)
                    
                }else
                
                if (sw.bounds.containsRect(item.hitBounds)) {
                    
                    addToNode(sw, item)
                }else
                
                if (se.bounds.containsRect(item.hitBounds)) {
                    
                    addToNode(se, item)
                }
                else {
                    
                    trace("doesn't fit anywhere");
                    item.currentNode = this;
                    objects.push(item);                */
            }
            
                trace("item currentnode [layer " + item.currentNode.Layer + "][" + item.currentNode.Label + "]");
            
        }
        
        private function clickNode(e:MouseEvent):void 
        {
            trace("node clicked: [" + this.Layer + "][ " + this.Label + "]");
        }
        
        private function addToNode(node:Node, item:*):void 
        {

            trace("adding in layer: " + node.Layer + " [" + node.Label + "]");
            
            trace("[layer " + node.Layer + "][" + node.Label + "] node.x: " + node.x + 
            " - "  + (node.x + node.Width));
            trace("[layer " + node.Layer + "][" + node.Label + "] node.y: " + node.y +
            " - "  + (node.y + node.Height));

            objects.push(item);
            item.currentNode = node;
            item.nodeDraw();
            node.addItem(item); //DO THIS LAST OR IT WILL BUG THE FUCK UP
                    
        }
        
        public function itemMoved(item:*):void {
            
            trace("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
            trace("!!! R E C A L C U L A T E !!!!");
            trace("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! \n\n");
            QuadTree.TopNode.addItem(item);
        }
            
    }




    /**
     * ...
     * @author Martino Wullems
     */
    class CollisionMethod
    {
        
        public static const REGULAR:String = "REGULAR";
        
        public static const QUADTREE:String = "QUADTREE";
        
    }
    
    
    /**
     * ...
     * @author Martino Wullems
     */
    class NodeLabel
    {
        
        public static const TOP:String = "TOP";
        
        public static const NW:String = "NW";
        
        public static const NE:String = "NE";
        
        public static const SW:String = "SW";
        
        public static const SE:String = "SE";
        
    }





    import flash.display.Sprite;
    import flash.events.MouseEvent;
    import flash.geom.Rectangle;
    /**
     * ...
     * @author Martino Wullems
     */
    class CollisionObject extends Sprite
    {
        public var size:int;
        public var vx:int = 0;
        public var vy:int = 0;
        public var graphic:Sprite;
        public var hitGraphic:Sprite;
        
        public var currentNode:Node;
        
        public var hitBounds:Rectangle;
        var sleeping:Boolean = false;
        
        private var _isHit:Boolean;
        
        
        
        public function CollisionObject() 
        {
            addEventListener(MouseEvent.MOUSE_DOWN, grab);
            addEventListener(MouseEvent.MOUSE_UP, letGo);
        }
        
        private function grab(e:MouseEvent):void 
        {
            //trace("currentnode: [layer  " + currentNode.Layer + "]");
            startDrag();
            vx = 0;
            vy = 0;
        }
        
        private function letGo(e:MouseEvent):void 
        {
            stopDrag();
            currentNode.itemMoved(this);
        }
        
        public function Collision():void{
            
            
        }
        
        public function nodeDraw():void {
            
            trace("drawwing node layer[ " + currentNode.Layer + "][" + currentNode.Label + "]");
            
            CollisionTest.shape.graphics.clear();
            CollisionTest.shape.graphics.lineStyle(0, 0x00FF00, .5);
            //CollisionTest.shape.graphics.drawRect(currentNode.x, currentNode.y, currentNode.width,
            //currentNode.bounds.height);
            CollisionTest.shape.graphics.moveTo(currentNode.x, currentNode.y);
            CollisionTest.shape.graphics.lineTo(currentNode.x + currentNode.Width, currentNode.y);
            CollisionTest.shape.graphics.lineTo(currentNode.x + currentNode.Width, currentNode.y + currentNode.Height);
            CollisionTest.shape.graphics.lineTo(currentNode.x, currentNode.y + currentNode.Height);
            CollisionTest.shape.graphics.lineTo(currentNode.x, currentNode.y);
        }
        
        //////////////////////
        // setter and getter//
        //////////////////////
        
                
         public function set isHit(value:Boolean):void {
              _isHit = value;
              graphic.visible = _isHit;
              hitGraphic.visible = !_isHit;
         }
        
        public function get isHit():Boolean {
              return _isHit;
         }
        
    }




    import flash.display.Sprite;
    import flash.geom.Rectangle;
    /**
     * ...
     * @author Martino Wullems
     */
    class Square extends CollisionObject
    {
        
        public function Square(Size:int) 
        {
            size = Size;
            
            drawSquare();
        }
        
        private function drawSquare():void 
        {
            graphic = new Sprite();
            graphic.graphics.beginFill(0xFF0000);
            graphic.graphics.drawRect(0, 0, size, size);
            graphic.graphics.endFill();
            addChild(graphic);
            
            hitGraphic = new Sprite();
            hitGraphic.graphics.beginFill(0x0066FF);
            hitGraphic.graphics.drawRect(0, 0, size, size);
            hitGraphic.graphics.endFill();
            addChild(hitGraphic);
            //hitGraphic.visible = false;
            hitBounds = new Rectangle(0, 0, size, size);
        }
        
        override public function Collision():void {
            
            trace("I collided with a friend");
        }
        
        
        public function debugDraw():void {
            
            if (isHit) {
                
                graphic.visible = false;
                hitGraphic.visible = true;
                
            }else {
                
                graphic.visible = true;
                hitGraphic.visible = false;
            }
        }
        
    }



/* M O U S E   C O O R D */



    import flash.display.MovieClip;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.text.TextField;
    import flash.text.TextFormat;
    /**
     * ...
     * @author Martino Wullems
     */
    class MouseCoord extends MovieClip
    {
        public var coordtext:TextField;
        
        public function MouseCoord() 
        {
            addEventListener(Event.ADDED_TO_STAGE, onStage);
            this.mouseEnabled = false;
            this.mouseChildren = false;
        }
        
        private function onStage(e:Event):void 
        {
            removeEventListener(Event.ADDED_TO_STAGE, onStage);
            
            coordtext = new TextField();
            coordtext.defaultTextFormat = new TextFormat("Arial", 15, null, true);
            addChild(coordtext);
            
            stage.addEventListener(MouseEvent.MOUSE_MOVE, movemouse);
        }
        
        private function movemouse(e:MouseEvent):void 
        {
            //this.x = stage.mouseX;
            //this.y = stage.mouseY;
            
            coordtext.htmlText = "X: " + stage.mouseX + "\nY: " + stage.mouseY;
        }
        
    }