FlxQuadTree.java :  » Game » flixel-android » org » flixel » Android Open Source

Android Open Source » Game » flixel android 
flixel android » org » flixel » FlxQuadTree.java
package org.flixel;

import java.util.ArrayList;

import org.flixel.data.FlxList;

/**
 * A fairly generic quad tree structure for rapid overlap checks.
 * FlxQuadTree is also configured for single or dual list operation.
 * You can add items either to its A list or its B list.
 * When you do an overlap check, you can compare the A list to itself,
 * or the A list against the B list.  Handy for different things!
 */
public class FlxQuadTree extends FlxRect
{
  /**
   * Set this to null to force it to refresh on the next collide.
   */
  static public FlxQuadTree quadTree;
  /**
   * This variable stores the dimensions of the root of the quad tree. This is
   * the eligible game collision space.
   */
  public static FlxRect bounds;
  /**
   * Controls the granularity of the quad tree. Default is 3 (decent
   * performance on large and small worlds).
   */
  public static int divisions;
  /**
   * Flag for specifying that you want to add an object to the A list.
   */
  public static final int A_LIST = 0;
  /**
   * Flag for specifying that you want to add an object to the B list.
   */
  public static final int B_LIST = -1;

  /**
   * Whether this branch of the tree can be subdivided or not.
   */
  protected boolean _canSubdivide;

  /**
   * These variables refer to the internal A and B linked lists, which are
   * used to store objects in the leaves.
   */
  protected FlxList _headA;
  protected FlxList _tailA;
  protected FlxList _headB;
  protected FlxList _tailB;

  /**
   * These variables refer to the potential child quadrants for this node.
   */
  static protected int _min;
  protected FlxQuadTree _nw;
  protected FlxQuadTree _ne;
  protected FlxQuadTree _se;
  protected FlxQuadTree _sw;
  protected float _l;
  protected float _r;
  protected float _t;
  protected float _b;
  protected float _hw;
  protected float _hh;
  protected float _mx;
  protected float _my;

  /**
   * These objects are used to reduce recursive parameters internally.
   */
  static protected FlxObject _o;
  static protected float _ol;
  static protected float _ot;
  static protected float _or;
  static protected float _ob;
  static protected int _oa;
  static protected FlxOverlapListener _oc;

  /**
   * Instantiate a new Quad Tree node.
   * 
   * @param  X      The X-coordinate of the point in space.
   * @param  Y      The Y-coordinate of the point in space.
   * @param  Width    Desired width of this node.
   * @param  Height    Desired height of this node.
   * @param  Parent    The parent branch or node.  Pass null to create a root.
   */
  public FlxQuadTree(float X, float Y, int Width, int Height, FlxQuadTree Parent)
  {
    super(X, Y, Width, Height);
    constructor(X, Y, Width, Height, Parent);  
  }
  
  /**
   * Instantiate a new Quad Tree node.
   * 
   * @param  X      The X-coordinate of the point in space.
   * @param  Y      The Y-coordinate of the point in space.
   * @param  Width    Desired width of this node.
   * @param  Height    Desired height of this node.
   */
  public FlxQuadTree(float X, float Y, int Width, int Height)
  {
    super(X, Y, Width, Height);
    constructor(X, Y, Width, Height, null);    
  }
  
  private void constructor(float X, float Y, int Width, int Height, FlxQuadTree Parent)
  {
    _headA = _tailA = new FlxList();
    _headB = _tailB = new FlxList();

    /*
     * DEBUG: draw a randomly colored rectangle indicating this quadrant
     * (may induce seizures) var brush:FlxSprite = new
     * FlxSprite().createGraphic(Width,Height,0xffffffff*FlxU.random());
     * FlxState.screen.draw(brush,X+FlxG.scroll.x,Y+FlxG.scroll.y);//
     */

    // Copy the parent's children (if there are any)
    if(Parent != null)
    {
      FlxList itr;
      FlxList ot;
      if(Parent._headA.object != null)
      {
        itr = Parent._headA;
        while(itr != null)
        {
          if(_tailA.object != null)
          {
            ot = _tailA;
            _tailA = new FlxList();
            ot.next = _tailA;
          }
          _tailA.object = itr.object;
          itr = itr.next;
        }
      }
      if(Parent._headB.object != null)
      {
        itr = Parent._headB;
        while(itr != null)
        {
          if(_tailB.object != null)
          {
            ot = _tailB;
            _tailB = new FlxList();
            ot.next = _tailB;
          }
          _tailB.object = itr.object;
          itr = itr.next;
        }
      }
    }
    else
      _min = (width + height) / (2 * divisions);
    _canSubdivide = (width > _min) || (height > _min);

    // Set up comparison/sort helpers
    _nw = null;
    _ne = null;
    _se = null;
    _sw = null;
    _l = x;
    _r = x + width;
    _hw = width / 2;
    _mx = _l + _hw;
    _t = y;
    _b = y + height;
    _hh = height / 2;
    _my = _t + _hh;
  }

  /**
   * Call this function to add an object to the root of the tree. This
   * function will recursively add all group members, but not the groups
   * themselves.
   * 
   * @param Object
   *            The <code>FlxObject</code> you want to add.
   *            <code>FlxGroup</code> objects will be recursed and their
   *            applicable members added automatically.
   * @param List
   *            A <code>uint</code> flag indicating the list to which you want
   *            to add the objects. Options are <code>A_LIST</code> and
   *            <code>B_LIST</code>.
   */
  public void add(FlxObject Object, int List)
  {
    _oa = List;
    if(Object._group)
    {
      int i = 0;
      FlxObject m;
      ArrayList<FlxObject> members = ((FlxGroup) Object).members;    
      int l = members.size();
      
      while(i < l)
      {        
        m = members.get(i++);
        if((m != null) && m.exists)
        {
          if(m._group)
            add(m, List);
          else
            if(m.getSolid())
            {
              _o = m;
              _ol = _o.x;
              _ot = _o.y;
              _or = _o.x + _o.width;
              _ob = _o.y + _o.height;
              addObject();
            }
        }
      }
    }
    if(Object.getSolid())
    {
      _o = Object;
      _ol = _o.x;
      _ot = _o.y;
      _or = _o.x + _o.width;
      _ob = _o.y + _o.height;
      addObject();
    }
  }

  /**
   * Internal function for recursively navigating and creating the tree while
   * adding objects to the appropriate nodes.
   */
  protected void addObject()
  {
    // If this quad (not its children) lies entirely inside this object, add
    // it here
    if(!_canSubdivide || ((_l >= _ol) && (_r <= _or) && (_t >= _ot) && (_b <= _ob)))
    {
      addToList();
      return;
    }

    // See if the selected object fits completely inside any of the
    // quadrants
    if((_ol > _l) && (_or < _mx))
    {
      if((_ot > _t) && (_ob < _my))
      {
        if(_nw == null)
          _nw = new FlxQuadTree(_l, _t, (int) _hw, (int) _hh, this);
        _nw.addObject();
        return;
      }
      if((_ot > _my) && (_ob < _b))
      {
        if(_sw == null)
          _sw = new FlxQuadTree(_l, _my, (int) _hw, (int) _hh, this);
        _sw.addObject();
        return;
      }
    }
    if((_ol > _mx) && (_or < _r))
    {
      if((_ot > _t) && (_ob < _my))
      {
        if(_ne == null)
          _ne = new FlxQuadTree(_mx, _t, (int) _hw, (int) _hh, this);
        _ne.addObject();
        return;
      }
      if((_ot > _my) && (_ob < _b))
      {
        if(_se == null)
          _se = new FlxQuadTree(_mx, _my, (int) _hw, (int) _hh, this);
        _se.addObject();
        return;
      }
    }

    // If it wasn't completely contained we have to check out the partial
    // overlaps
    if((_or > _l) && (_ol < _mx) && (_ob > _t) && (_ot < _my))
    {
      if(_nw == null)
        _nw = new FlxQuadTree(_l, _t, (int) _hw, (int) _hh, this);
      _nw.addObject();
    }
    if((_or > _mx) && (_ol < _r) && (_ob > _t) && (_ot < _my))
    {
      if(_ne == null)
        _ne = new FlxQuadTree(_mx, _t, (int) _hw, (int) _hh, this);
      _ne.addObject();
    }
    if((_or > _mx) && (_ol < _r) && (_ob > _my) && (_ot < _b))
    {
      if(_se == null)
        _se = new FlxQuadTree(_mx, _my, (int) _hw, (int) _hh, this);
      _se.addObject();
    }
    if((_or > _l) && (_ol < _mx) && (_ob > _my) && (_ot < _b))
    {
      if(_sw == null)
        _sw = new FlxQuadTree(_l, _my, (int) _hw, (int) _hh, this);
      _sw.addObject();
    }
  }

  /**
   * Internal function for recursively adding objects to leaf lists.
   */
  protected void addToList()
  {
    FlxList ot;
    if(_oa == A_LIST)
    {
      if(_tailA.object != null)
      {
        ot = _tailA;
        _tailA = new FlxList();
        ot.next = _tailA;
      }
      _tailA.object = _o;
    }
    else
    {
      if(_tailB.object != null)
      {
        ot = _tailB;
        _tailB = new FlxList();
        ot.next = _tailB;
      }
      _tailB.object = _o;
    }
    if(!_canSubdivide)
      return;
    if(_nw != null)
      _nw.addToList();
    if(_ne != null)
      _ne.addToList();
    if(_se != null)
      _se.addToList();
    if(_sw != null)
      _sw.addToList();
  }
  
  /**
   * <code>FlxQuadTree</code>'s other main function.  Call this after adding objects
   * using <code>FlxQuadTree.add()</code> to compare the objects that you loaded.
   * 
   * @param  BothLists  Whether you are doing an A-B list comparison, or comparing A against itself.
   * @param  Callback  A function with two <code>FlxObject</code> parameters - e.g. <code>myOverlapFunction(Object1:FlxObject,Object2:FlxObject);</code>  If no function is provided, <code>FlxQuadTree</code> will call <code>kill()</code> on both objects.
   *
   * @return  Whether or not any overlaps were found.
   */
  public boolean overlap(boolean BothLists, FlxOverlapListener Callback)
  {
    _oc = Callback;
    boolean c = false;
    FlxList itr;
    if(BothLists)
    {
      //An A-B list comparison
      _oa = B_LIST;
      if(_headA.object != null)
      {
        itr = _headA;
        while(itr != null)
        {
          _o = itr.object;
          if(_o.exists && _o.getSolid() && overlapNode())
            c = true;
          itr = itr.next;
        }
      }
      _oa = A_LIST;
      if(_headB.object != null)
      {
        itr = _headB;
        while(itr != null)
        {
          _o = itr.object;
          if(_o.exists && _o.getSolid())
          {
            if((_nw != null) && _nw.overlapNode())
              c = true;
            if((_ne != null) && _ne.overlapNode())
              c = true;
            if((_se != null) && _se.overlapNode())
              c = true;
            if((_sw != null) && _sw.overlapNode())
              c = true;
          }
          itr = itr.next;
        }
      }
    }
    else
    {
      //Just checking the A list against itself
      if(_headA.object != null)
      {
        itr = _headA;
        while(itr != null)
        {
          _o = itr.object;
          if(_o.exists && _o.getSolid() && overlapNode(itr.next))
            c = true;
          itr = itr.next;
        }
      }
    }
    
    //Advance through the tree by calling overlap on each child
    if((_nw != null) && _nw.overlap(BothLists,_oc))
      c = true;
    if((_ne != null) && _ne.overlap(BothLists,_oc))
      c = true;
    if((_se != null) && _se.overlap(BothLists,_oc))
      c = true;
    if((_sw != null) && _sw.overlap(BothLists,_oc))
      c = true;
    
    return c;
  }
  
  /**
   * An internal function for comparing an object against the contents of a node.
   * 
   * @param  Iterator  An optional pointer to a linked list entry (for comparing A against itself).
   * 
   * @return  Whether or not any overlaps were found.
   */
  protected boolean overlapNode(FlxList Iterator)
  {
    //member list setup
    boolean c = false;
    FlxObject co;
    FlxList itr = Iterator;
    if(itr == null)
    {
      if(_oa == A_LIST)
        itr = _headA;
      else
        itr = _headB;
    }
    
    //Make sure this is a valid list to walk first!
    if(itr.object != null)
    {
      //Walk the list and check for overlaps
      while(itr != null)
      {
        co = itr.object;
        if( (_o.equals(co) || !co.exists || !_o.exists || !co.getSolid() || !_o.getSolid() ||
          (_o.x + _o.width  < co.x + FlxU.roundingError) ||
          (_o.x + FlxU.roundingError > co.x + co.width) ||
          (_o.y + _o.height < co.y + FlxU.roundingError) ||
          (_o.y + FlxU.roundingError > co.y + co.height) ))
        {
          itr = itr.next;
          continue;
        }
        if(_oc == null)
        {
          _o.kill();
          co.kill();
          c = true;
        }
        else if(_oc.overlap(_o,co))
          c = true;
        itr = itr.next;
      }
    }    
    return c;
  }
  
  /**
   * An internal function for comparing an object against the contents of a node.
   * 
   * @return  Whether or not any overlaps were found.
   */
  protected boolean overlapNode()
  {
    return overlapNode(null);
  }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.