All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.flixel.system.FlxQuadTree Maven / Gradle / Ivy

The newest version!
package org.flixel.system;

import org.flixel.FlxBasic;
import org.flixel.FlxGroup;
import org.flixel.FlxObject;
import org.flixel.FlxRect;
import org.flixel.event.IFlxCollision;
import org.flixel.event.IFlxObject;

import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Pool;

/**
 * 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!
 * 
 * @author Ka Wing Chin
 */
public class FlxQuadTree extends FlxRect
{
	/**
	 * Flag for specifying that you want to add an object to the A list.
	 */
	static public final int A_LIST = 0;
	/**
	 * Flag for specifying that you want to add an object to the B list.
	 */
	static public final int B_LIST = 1;

	/**
	 * Controls the granularity of the quad tree.  Default is 6 (decent performance on large and small worlds).
	 */
	static public int divisions;

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

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

	/**
	 * Internal, governs and assists with the formation of the tree.
	 */
	static protected int _min;
	/**
	 * Internal, governs and assists with the formation of the tree.
	 */
	protected FlxQuadTree _northWestTree;
	/**
	 * Internal, governs and assists with the formation of the tree.
	 */
	protected FlxQuadTree _northEastTree;
	/**
	 * Internal, governs and assists with the formation of the tree.
	 */
	protected FlxQuadTree _southEastTree;
	/**
	 * Internal, governs and assists with the formation of the tree.
	 */
	protected FlxQuadTree _southWestTree;
	/**
	 * Internal, governs and assists with the formation of the tree.
	 */
	protected float _leftEdge;
	/**
	 * Internal, governs and assists with the formation of the tree.
	 */
	protected float _rightEdge;
	/**
	 * Internal, governs and assists with the formation of the tree.
	 */
	protected float _topEdge;
	/**
	 * Internal, governs and assists with the formation of the tree.
	 */
	protected float _bottomEdge;
	/**
	 * Internal, governs and assists with the formation of the tree.
	 */
	protected float _halfWidth;
	/**
	 * Internal, governs and assists with the formation of the tree.
	 */
	protected float _halfHeight;
	/**
	 * Internal, governs and assists with the formation of the tree.
	 */
	protected float _midpointX;
	/**
	 * Internal, governs and assists with the formation of the tree.
	 */
	protected float _midpointY;

	/**
	 * Internal, used to reduce recursive method parameters during object placement and tree formation.
	 */
	static protected FlxObject _object;
	/**
	 * Internal, used to reduce recursive method parameters during object placement and tree formation.
	 */
	static protected float _objectLeftEdge;
	/**
	 * Internal, used to reduce recursive method parameters during object placement and tree formation.
	 */
	static protected float _objectTopEdge;
	/**
	 * Internal, used to reduce recursive method parameters during object placement and tree formation.
	 */
	static protected float _objectRightEdge;
	/**
	 * Internal, used to reduce recursive method parameters during object placement and tree formation.
	 */
	static protected float _objectBottomEdge;

	/**
	 * Internal, used during tree processing and overlap checks.
	 */
	static protected int _list;
	/**
	 * Internal, used during tree processing and overlap checks.
	 */
	static protected boolean _useBothLists;
	/**
	 * Internal, used during tree processing and overlap checks.
	 */
	static protected IFlxObject _processingCallback;
	/**
	 * Internal, used during tree processing and overlap checks.
	 */
	static protected IFlxCollision _notifyCallback;
	/**
	 * Internal, used during tree processing and overlap checks.
	 */
	static protected FlxList _iterator;

	/**
	 * Internal, helpers for comparing actual object-to-object overlap - see overlapNode().
	 */
	static protected float _objectHullX;
	/**
	 * Internal, helpers for comparing actual object-to-object overlap - see overlapNode().
	 */
	static protected float _objectHullY;
	/**
	 * Internal, helpers for comparing actual object-to-object overlap - see overlapNode().
	 */
	static protected float _objectHullWidth;
	/**
	 * Internal, helpers for comparing actual object-to-object overlap - see overlapNode().
	 */
	static protected float _objectHullHeight;

	/**
	 * Internal, helpers for comparing actual object-to-object overlap - see overlapNode().
	 */
	static protected float _checkObjectHullX;
	/**
	 * Internal, helpers for comparing actual object-to-object overlap - see overlapNode().
	 */
	static protected float _checkObjectHullY;
	/**
	 * Internal, helpers for comparing actual object-to-object overlap - see overlapNode().
	 */
	static protected float _checkObjectHullWidth;
	/**
	 * Internal, helpers for comparing actual object-to-object overlap - see overlapNode().
	 */
	static protected float _checkObjectHullHeight;

	/**
	 * Internal, a pool of FlxQuadTrees to prevent constant new calls.
	 */
	static private Pool _pool = new Pool()
	{
		@Override
		protected FlxQuadTree newObject()
		{
			return new FlxQuadTree();
		}
	};

	/**
	 * Get a new Quad Tree node from the pool.
	 * 
	 * @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.
	 * 
	 * @return	A new FlxQuadTree.
	 */
	static public FlxQuadTree getNew(float X, float Y, float Width, float Height, FlxQuadTree Parent)
	{
		FlxQuadTree quadTree = _pool.obtain();
		quadTree.init(X,Y,Width,Height,Parent);
		return quadTree;
	}

	/**
	 * Has to be public for GWT reflection.
	 */
	public FlxQuadTree()
	{

	}

	/**
	 * 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.
	 */
	protected void init(float X, float Y, float Width, float Height, FlxQuadTree Parent)
	{
		make(X,Y,Width,Height);
		_headA = _tailA = FlxList.getNew();
		_headB = _tailB = FlxList.getNew();

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

		//Set up comparison/sort helpers
		_northWestTree = null;
		_northEastTree = null;
		_southEastTree = null;
		_southWestTree = null;
		_leftEdge = x;
		_rightEdge = x+width;
		_halfWidth = width/2f;
		_midpointX = _leftEdge+_halfWidth;
		_topEdge = y;
		_bottomEdge = y+height;
		_halfHeight = height/2f;
		_midpointY = _topEdge+_halfHeight;
	}

	/**
	 * 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.
	 */
	protected void init(float X, float Y, float Width, float Height)
	{
		init(X, Y, Width, Height, null);
	}

	/**
	 * Clean up memory.
	 */
	public void destroy()
	{
		if(_headA != null)
			_headA.destroy();
		_headA = null;
		//if(_tailA != null)
		//	_tailA.destroy();
		_tailA = null;
		if(_headB != null)
			_headB.destroy();
		_headB = null;
		//if(_tailB != null)
		//	_tailB.destroy();
		_tailB = null;

		if(_northWestTree != null)
			_northWestTree.destroy();
		_northWestTree = null;
		if(_northEastTree != null)
			_northEastTree.destroy();
		_northEastTree = null;
		if(_southEastTree != null)
			_southEastTree.destroy();
		_southEastTree = null;
		if(_southWestTree != null)
			_southWestTree.destroy();
		_southWestTree = null;

		_object = null;
		_processingCallback = null;
		_notifyCallback = null;

		_pool.free(this);
	}

	/**
	 * Load objects and/or groups into the quad tree, and register notify and processing callbacks.
	 * 
	 * @param	ObjectOrGroup1	Any object that is or extends FlxObject or FlxGroup.
	 * @param	ObjectOrGroup2	Any object that is or extends FlxObject or FlxGroup.  If null, the first parameter will be checked against itself.
	 * @param	NotifyCallback	A function with the form myFunction(Object1:FlxObject,Object2:FlxObject):void that is called whenever two objects are found to overlap in world space, and either no ProcessCallback is specified, or the ProcessCallback returns true.
	 * @param	ProcessCallback	A function with the form myFunction(Object1:FlxObject,Object2:FlxObject):Boolean that is called whenever two objects are found to overlap in world space.  The NotifyCallback is only called if this function returns true.  See FlxObject.separate().
	 */
	public void load(FlxBasic ObjectOrGroup1, FlxBasic ObjectOrGroup2, IFlxCollision NotifyCallback, IFlxObject ProcessCallback)
	{
		add(ObjectOrGroup1, A_LIST);
		if(ObjectOrGroup2 != null)
		{
			add(ObjectOrGroup2, B_LIST);
			_useBothLists = true;
		}
		else
			_useBothLists = false;
		_notifyCallback = NotifyCallback;
		_processingCallback = ProcessCallback;
	}

	/**
	 * Load objects and/or groups into the quad tree, and register notify and processing callbacks.
	 * 
	 * @param	ObjectOrGroup1	Any object that is or extends FlxObject or FlxGroup.
	 * @param	ObjectOrGroup2	Any object that is or extends FlxObject or FlxGroup.  If null, the first parameter will be checked against itself.
	 * @param	NotifyCallback	A function with the form myFunction(Object1:FlxObject,Object2:FlxObject):void that is called whenever two objects are found to overlap in world space, and either no ProcessCallback is specified, or the ProcessCallback returns true.
	 */
	public void load(FlxBasic ObjectOrGroup1, FlxBasic ObjectOrGroup2, IFlxCollision NotifyCallback)
	{
		load(ObjectOrGroup1, ObjectOrGroup2, NotifyCallback, null);
	}

	/**
	 * Load objects and/or groups into the quad tree, and register notify and processing callbacks.
	 * 
	 * @param	ObjectOrGroup1	Any object that is or extends FlxObject or FlxGroup.
	 * @param	ObjectOrGroup2	Any object that is or extends FlxObject or FlxGroup.  If null, the first parameter will be checked against itself.
	 */
	public void load(FlxBasic ObjectOrGroup1, FlxBasic ObjectOrGroup2)
	{
		load(ObjectOrGroup1, ObjectOrGroup2, null, null);
	}

	/**
	 * 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	ObjectOrGroup	FlxObjects are just added, FlxGroups are recursed and their applicable members added accordingly.
	 * @param	List			A int flag indicating the list to which you want to add the objects.  Options are A_LIST and B_LIST.
	 */
	public void add(FlxBasic ObjectOrGroup, int List)
	{
		_list = List;
		if(ObjectOrGroup instanceof FlxGroup)
		{
			int i = 0;
			FlxBasic basic;
			Array members = ((FlxGroup) ObjectOrGroup).members;
			int l = ((FlxGroup) (ObjectOrGroup)).length;
			while(i < l)
			{
				basic = members.get(i++);
				if((basic != null) && basic.exists)
				{
					if(basic instanceof FlxGroup)
						add(basic,List);
					else if(basic instanceof FlxObject)
					{
						_object = (FlxObject) basic;
						if(_object.exists && _object.allowCollisions > 0)
						{
							_objectLeftEdge = _object.x;
							_objectTopEdge = _object.y;
							_objectRightEdge = _object.x + _object.width;
							_objectBottomEdge = _object.y + _object.height;
							addObject();
						}
					}
				}
			}
		}
		else
		{
			_object = (FlxObject) ObjectOrGroup;
			if(_object.exists && _object.allowCollisions > 0)
			{
				_objectLeftEdge = _object.x;
				_objectTopEdge = _object.y;
				_objectRightEdge = _object.x + _object.width;
				_objectBottomEdge = _object.y + _object.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 || ((_leftEdge >= _objectLeftEdge) && (_rightEdge <= _objectRightEdge) && (_topEdge >= _objectTopEdge) && (_bottomEdge <= _objectBottomEdge)))
		{
			addToList();
			return;
		}

		//See if the selected object fits completely inside any of the quadrants
		if((_objectLeftEdge > _leftEdge) && (_objectRightEdge < _midpointX))
		{
			if((_objectTopEdge > _topEdge) && (_objectBottomEdge < _midpointY))
			{
				if(_northWestTree == null)
					_northWestTree = FlxQuadTree.getNew(_leftEdge,_topEdge,_halfWidth,_halfHeight,this);
				_northWestTree.addObject();
				return;
			}
			if((_objectTopEdge > _midpointY) && (_objectBottomEdge < _bottomEdge))
			{
				if(_southWestTree == null)
					_southWestTree = FlxQuadTree.getNew(_leftEdge,_midpointY,_halfWidth,_halfHeight,this);
				_southWestTree.addObject();
				return;
			}
		}
		if((_objectLeftEdge > _midpointX) && (_objectRightEdge < _rightEdge))
		{
			if((_objectTopEdge > _topEdge) && (_objectBottomEdge < _midpointY))
			{
				if(_northEastTree == null)
					_northEastTree = FlxQuadTree.getNew(_midpointX,_topEdge,_halfWidth,_halfHeight,this);
				_northEastTree.addObject();
				return;
			}
			if((_objectTopEdge > _midpointY) && (_objectBottomEdge < _bottomEdge))
			{
				if(_southEastTree == null)
					_southEastTree = FlxQuadTree.getNew(_midpointX,_midpointY,_halfWidth,_halfHeight,this);
				_southEastTree.addObject();
				return;
			}
		}

		//If it wasn't completely contained we have to check out the partial overlaps
		if((_objectRightEdge > _leftEdge) && (_objectLeftEdge < _midpointX) && (_objectBottomEdge > _topEdge) && (_objectTopEdge < _midpointY))
		{
			if(_northWestTree == null)
				_northWestTree = FlxQuadTree.getNew(_leftEdge,_topEdge,_halfWidth,_halfHeight,this);
			_northWestTree.addObject();
		}
		if((_objectRightEdge > _midpointX) && (_objectLeftEdge < _rightEdge) && (_objectBottomEdge > _topEdge) && (_objectTopEdge < _midpointY))
		{
			if(_northEastTree == null)
				_northEastTree = FlxQuadTree.getNew(_midpointX,_topEdge,_halfWidth,_halfHeight,this);
			_northEastTree.addObject();
		}
		if((_objectRightEdge > _midpointX) && (_objectLeftEdge < _rightEdge) && (_objectBottomEdge > _midpointY) && (_objectTopEdge < _bottomEdge))
		{
			if(_southEastTree == null)
				_southEastTree = FlxQuadTree.getNew(_midpointX,_midpointY,_halfWidth,_halfHeight,this);
			_southEastTree.addObject();
		}
		if((_objectRightEdge > _leftEdge) && (_objectLeftEdge < _midpointX) && (_objectBottomEdge > _midpointY) && (_objectTopEdge < _bottomEdge))
		{
			if(_southWestTree == null)
				_southWestTree = FlxQuadTree.getNew(_leftEdge,_midpointY,_halfWidth,_halfHeight,this);
			_southWestTree.addObject();
		}
	}

	/**
	 * Internal function for recursively adding objects to leaf lists.
	 */
	protected void addToList()
	{
		FlxList ot;
		if(_list == A_LIST)
		{
			if(_tailA.object != null)
			{
				ot = _tailA;
				_tailA = FlxList.getNew();
				ot.next = _tailA;
			}
			_tailA.object = _object;
		}
		else
		{
			if(_tailB.object != null)
			{
				ot = _tailB;
				_tailB = FlxList.getNew();
				ot.next = _tailB;
			}
			_tailB.object = _object;
		}
		if(!_canSubdivide)
			return;
		if(_northWestTree != null)
			_northWestTree.addToList();
		if(_northEastTree != null)
			_northEastTree.addToList();
		if(_southEastTree != null)
			_southEastTree.addToList();
		if(_southWestTree != null)
			_southWestTree.addToList();
	}

	/**
	 * FlxQuadTree's other main function.  Call this after adding objects
	 * using FlxQuadTree.load() to compare the objects that you loaded.
	 * 
	 * @return	Whether or not any overlaps were found.
	 */
	public boolean execute()
	{
		boolean overlapProcessed = false;
		FlxList iterator;

		if(_headA.object != null)
		{
			iterator = _headA;
			while(iterator != null)
			{
				_object = iterator.object;
				if(_useBothLists)
					_iterator = _headB;
				else
					_iterator = iterator.next;
				if(_object.exists && (_object.allowCollisions > 0) && (_iterator != null) && (_iterator.object != null) && _iterator.object.exists && overlapNode())
				{
					overlapProcessed = true;
				}
				iterator = iterator.next;
			}
		}

		//Advance through the tree by calling overlap on each child
		if((_northWestTree != null) && _northWestTree.execute())
			overlapProcessed = true;
		if((_northEastTree != null) && _northEastTree.execute())
			overlapProcessed = true;
		if((_southEastTree != null) && _southEastTree.execute())
			overlapProcessed = true;
		if((_southWestTree != null) && _southWestTree.execute())
			overlapProcessed = true;

		return overlapProcessed;
	}

	/**
	 * An internal function for comparing an object against the contents of a node.
	 * 
	 * @return	Whether or not any overlaps were found.
	 */
	protected boolean overlapNode()
	{
		//Walk the list and check for overlaps
		boolean overlapProcessed = false;
		FlxObject checkObject;
		while(_iterator != null)
		{
			if(!_object.exists || (_object.allowCollisions <= 0))
				break;

			checkObject = _iterator.object;
			if((_object == checkObject) || ((_object != null) && (_object.equals(checkObject))) || !checkObject.exists || (checkObject.allowCollisions <= 0))
			{
				_iterator = _iterator.next;
				continue;
			}

			//calculate bulk hull for _object
			_objectHullX = (_object.x < _object.last.x)?_object.x:_object.last.x;
			_objectHullY = (_object.y < _object.last.y)?_object.y:_object.last.y;
			_objectHullWidth = _object.x - _object.last.x;
			_objectHullWidth = _object.width + ((_objectHullWidth>0)?_objectHullWidth:-_objectHullWidth);
			_objectHullHeight = _object.y - _object.last.y;
			_objectHullHeight = _object.height + ((_objectHullHeight>0)?_objectHullHeight:-_objectHullHeight);

			//calculate bulk hull for checkObject
			_checkObjectHullX = (checkObject.x < checkObject.last.x)?checkObject.x:checkObject.last.x;
			_checkObjectHullY = (checkObject.y < checkObject.last.y)?checkObject.y:checkObject.last.y;
			_checkObjectHullWidth = checkObject.x - checkObject.last.x;
			_checkObjectHullWidth = checkObject.width + ((_checkObjectHullWidth>0)?_checkObjectHullWidth:-_checkObjectHullWidth);
			_checkObjectHullHeight = checkObject.y - checkObject.last.y;
			_checkObjectHullHeight = checkObject.height + ((_checkObjectHullHeight>0)?_checkObjectHullHeight:-_checkObjectHullHeight);

			//check for intersection of the two hulls
			if(	(_objectHullX + _objectHullWidth > _checkObjectHullX) &&
				(_objectHullX < _checkObjectHullX + _checkObjectHullWidth) &&
				(_objectHullY + _objectHullHeight > _checkObjectHullY) &&
				(_objectHullY < _checkObjectHullY + _checkObjectHullHeight) )
			{
				//Execute callback functions if they exist
				if((_processingCallback == null) || _processingCallback.callback(_object,checkObject))
				{
					overlapProcessed = true;
					if(_notifyCallback != null)
						_notifyCallback.callback(_object,checkObject);
				}
			}
			_iterator = _iterator.next;
		}

		return overlapProcessed;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy