API Docs for: 1.0.0
Show:

File: src\fork\box2d\dynamics\b2World.js

/*
 * Copyright (c) 2006-2007 Erin Catto http:
 *
 * This software is provided 'as-is', without any express or implied
 * warranty.  In no event will the authors be held liable for any damages
 * arising from the use of this software.
 * Permission is granted to anyone to use this software for any purpose,
 * including commercial applications, and to alter it and redistribute it
 * freely, subject to the following restrictions:
 * 1. The origin of this software must not be misrepresented; you must not
 * claim that you wrote the original software. If you use this software
 * in a product, an acknowledgment in the product documentation would be
 * appreciated but is not required.
 * 2. Altered source versions must be plainly marked, and must not be
 * misrepresented the original software.
 * 3. This notice may not be removed or altered from any source distribution.
 *
 * Removing prototype dependency by piksel@mail.ru
 */



/**
 * @class b2World
 * @constructor
 * @param worldAABB {b2AABB} axis aligned bounding box
 * @param gravity {b2Vec2} gravity force vector
 * @param doSleep {Boolean} bodies can sleep
 */
var b2World = function (worldAABB, gravity, doSleep) {
	this.step = new b2TimeStep();
	this.m_contactManager = new b2ContactManager();

	this.m_listener = null;
	this.m_filter = b2CollisionFilter.b2_defaultFilter;

	this.m_bodyList = null;
	this.m_contactList = null;
	this.m_jointList = null;

	this.m_bodyCount = 0;
	this.m_contactCount = 0;
	this.m_jointCount = 0;

	this.m_bodyDestroyList = null;

	this.m_allowSleep = doSleep;

	this.m_gravity = gravity;

	this.m_contactManager.m_world = this;
	this.m_broadPhase = new b2BroadPhase(worldAABB, this.m_contactManager);

	var bd = new b2BodyDef();
	this.m_groundBody = this.CreateBody(bd);
};

b2World.prototype = 
{
	//~b2World(){
	//	this.DestroyBody(this.m_groundBody);
	//	delete this.m_broadPhase;
	//}

	// Set a callback to notify you when a joint is implicitly destroyed
	// when an attached body is destroyed.
	/**
	 * @method SetListener
	 */
	SetListener: function(listener){
		this.m_listener = listener;
	},

	// Register a collision filter to provide specific control over collision.
	// Otherwise the default filter is used (b2CollisionFilter).
	/**
	 * @method SetFilter
	 */
	SetFilter: function(filter){
		this.m_filter = filter;
	},

	// Create and destroy rigid bodies. Destruction is deferred until the
	// the next call to this.Step. This is done so that bodies may be destroyed
	// while you iterate through the contact list.
	/**
	 * @method CreateBody
	 */
	CreateBody: function(def){
		//void* mem = this.m_blockAllocator.Allocate(sizeof(b2Body));
		var b = new b2Body(def, this);
		b.m_prev = null;

		b.m_next = this.m_bodyList;
		if (this.m_bodyList)
		{
			this.m_bodyList.m_prev = b;
		}
		this.m_bodyList = b;
		++this.m_bodyCount;

		return b;
	},
	// Body destruction is deferred to make contact processing more robust.
	/**
	 * @method DestroyBody
	 */
	DestroyBody: function(b)
	{

		if (b.m_flags & b2Body.e_destroyFlag)
		{
			return;
		}

		// Remove from normal body list.
		if (b.m_prev)
		{
			b.m_prev.m_next = b.m_next;
		}

		if (b.m_next)
		{
			b.m_next.m_prev = b.m_prev;
		}

		if (b == this.m_bodyList)
		{
			this.m_bodyList = b.m_next;
		}

		b.m_flags |= b2Body.e_destroyFlag;
		//b2Settings.b2Assert(this.m_bodyCount > 0);
		--this.m_bodyCount;

		//b->~b2Body();
		//b.Destroy();
		// Add to the deferred destruction list.
		b.m_prev = null;
		b.m_next = this.m_bodyDestroyList;
		this.m_bodyDestroyList = b;
	},

	/**
	 * @method CleanBodyList
	 */
	CleanBodyList: function()
	{
		this.m_contactManager.m_destroyImmediate = true;

		var b = this.m_bodyDestroyList;
		while (b)
		{
			//b2Settings.b2Assert((b.m_flags & b2Body.e_destroyFlag) != 0);

			// Preserve the next pointer.
			var b0 = b;
			b = b.m_next;

			// Delete the attached joints
			var jn = b0.m_jointList;
			while (jn)
			{
				var jn0 = jn;
				jn = jn.next;

				if (this.m_listener)
				{
					this.m_listener.NotifyJointDestroyed(jn0.joint);
				}

				this.DestroyJoint(jn0.joint);
			}

			b0.Destroy();
			//this.m_blockAllocator.Free(b0, sizeof(b2Body));
		}

		// Reset the list.
		this.m_bodyDestroyList = null;

		this.m_contactManager.m_destroyImmediate = false;
	},

	/**
	 * @method CreateJoint
	 */
	CreateJoint: function(def){
		var j = b2Joint.Create(def, this.m_blockAllocator);

		// Connect to the world list.
		j.m_prev = null;
		j.m_next = this.m_jointList;
		if (this.m_jointList)
		{
			this.m_jointList.m_prev = j;
		}
		this.m_jointList = j;
		++this.m_jointCount;

		// Connect to the bodies
		j.m_node1.joint = j;
		j.m_node1.other = j.m_body2;
		j.m_node1.prev = null;
		j.m_node1.next = j.m_body1.m_jointList;
		if (j.m_body1.m_jointList) j.m_body1.m_jointList.prev = j.m_node1;
		j.m_body1.m_jointList = j.m_node1;

		j.m_node2.joint = j;
		j.m_node2.other = j.m_body1;
		j.m_node2.prev = null;
		j.m_node2.next = j.m_body2.m_jointList;
		if (j.m_body2.m_jointList) j.m_body2.m_jointList.prev = j.m_node2;
		j.m_body2.m_jointList = j.m_node2;

		// If the joint prevents collisions, then reset collision filtering.
		if (def.collideConnected == false)
		{
			// Reset the proxies on the body with the minimum number of shapes.
			var b = def.body1.m_shapeCount < def.body2.m_shapeCount ? def.body1 : def.body2;
			for (var s = b.m_shapeList; s; s = s.m_next)
			{
				s.ResetProxy(this.m_broadPhase);
			}
		}

		return j;
	},
	/**
	 * @method DestroyJoint
	 */
	DestroyJoint: function(j)
	{

		var collideConnected = j.m_collideConnected;

		// Remove from the world.
		if (j.m_prev)
		{
			j.m_prev.m_next = j.m_next;
		}

		if (j.m_next)
		{
			j.m_next.m_prev = j.m_prev;
		}

		if (j == this.m_jointList)
		{
			this.m_jointList = j.m_next;
		}

		// Disconnect from island graph.
		var body1 = j.m_body1;
		var body2 = j.m_body2;

		// Wake up touching bodies.
		body1.WakeUp();
		body2.WakeUp();

		// Remove from body 1
		if (j.m_node1.prev)
		{
			j.m_node1.prev.next = j.m_node1.next;
		}

		if (j.m_node1.next)
		{
			j.m_node1.next.prev = j.m_node1.prev;
		}

		if (j.m_node1 == body1.m_jointList)
		{
			body1.m_jointList = j.m_node1.next;
		}

		j.m_node1.prev = null;
		j.m_node1.next = null;

		// Remove from body 2
		if (j.m_node2.prev)
		{
			j.m_node2.prev.next = j.m_node2.next;
		}

		if (j.m_node2.next)
		{
			j.m_node2.next.prev = j.m_node2.prev;
		}

		if (j.m_node2 == body2.m_jointList)
		{
			body2.m_jointList = j.m_node2.next;
		}

		j.m_node2.prev = null;
		j.m_node2.next = null;

		b2Joint.Destroy(j, this.m_blockAllocator);

		//b2Settings.b2Assert(this.m_jointCount > 0);
		--this.m_jointCount;

		// If the joint prevents collisions, then reset collision filtering.
		if (collideConnected == false)
		{
			// Reset the proxies on the body with the minimum number of shapes.
			var b = body1.m_shapeCount < body2.m_shapeCount ? body1 : body2;
			for (var s = b.m_shapeList; s; s = s.m_next)
			{
				s.ResetProxy(this.m_broadPhase);
			}
		}
	},

	// The world provides a single ground body with no collision shapes. You
	// can use this to simplify the creation of joints.
	/**
	 * @method GetGroundBody
	 */
	GetGroundBody: function(){
		return this.m_groundBody;
	},


	step: new b2TimeStep(),
	// this.Step
	/**
	 * @method Step
	 */
	Step: function(dt, iterations){

		var b;
		var other;


		this.step.dt = dt;
		this.step.iterations	= iterations;
		if (dt > 0.0)
		{
			this.step.inv_dt = 1.0 / dt;
		}
		else
		{
			this.step.inv_dt = 0.0;
		}

		this.m_positionIterationCount = 0;

		// Handle deferred contact destruction.
		this.m_contactManager.CleanContactList();

		// Handle deferred body destruction.
		this.CleanBodyList();

		// Update contacts.
		this.m_contactManager.Collide();

		// Size the island for the worst case.
		var island = new b2Island(this.m_bodyCount, this.m_contactCount, this.m_jointCount, this.m_stackAllocator);

		// Clear all the island flags.
		for (b = this.m_bodyList; b != null; b = b.m_next)
		{
			b.m_flags &= ~b2Body.e_islandFlag;
		}
		for (var c = this.m_contactList; c != null; c = c.m_next)
		{
			c.m_flags &= ~b2Contact.e_islandFlag;
		}
		for (var j = this.m_jointList; j != null; j = j.m_next)
		{
			j.m_islandFlag = false;
		}

		// Build and simulate all awake islands.
		var stackSize = this.m_bodyCount;
		//var stack = (b2Body**)this.m_stackAllocator.Allocate(stackSize * sizeof(b2Body*));
		var stack = new Array(this.m_bodyCount);
		for (var k = 0; k < this.m_bodyCount; k++)
			stack[k] = null;

		for (var seed = this.m_bodyList; seed != null; seed = seed.m_next)
		{
			if (seed.m_flags & (b2Body.e_staticFlag | b2Body.e_islandFlag | b2Body.e_sleepFlag | b2Body.e_frozenFlag))
			{
				continue;
			}

			// Reset island and stack.
			island.Clear();
			var stackCount = 0;
			stack[stackCount++] = seed;
			seed.m_flags |= b2Body.e_islandFlag;;

			// Perform a depth first search (DFS) on the constraint graph.
			while (stackCount > 0)
			{
				// Grab the next body off the stack and add it to the island.
				b = stack[--stackCount];
				island.AddBody(b);

				// Make sure the body is awake.
				b.m_flags &= ~b2Body.e_sleepFlag;

				// To keep islands, we don't
				// propagate islands across static bodies.
				if (b.m_flags & b2Body.e_staticFlag)
				{
					continue;
				}

				// Search all contacts connected to this body.
				for (var cn = b.m_contactList; cn != null; cn = cn.next)
				{
					if (cn.contact.m_flags & b2Contact.e_islandFlag)
					{
						continue;
					}

					island.AddContact(cn.contact);
					cn.contact.m_flags |= b2Contact.e_islandFlag;

					other = cn.other;
					if (other.m_flags & b2Body.e_islandFlag)
					{
						continue;
					}

					//b2Settings.b2Assert(stackCount < stackSize);
					stack[stackCount++] = other;
					other.m_flags |= b2Body.e_islandFlag;
				}

				// Search all joints connect to this body.
				for (var jn = b.m_jointList; jn != null; jn = jn.next)
				{
					if (jn.joint.m_islandFlag == true)
					{
						continue;
					}

					island.AddJoint(jn.joint);
					jn.joint.m_islandFlag = true;

					other = jn.other;
					if (other.m_flags & b2Body.e_islandFlag)
					{
						continue;
					}

					//b2Settings.b2Assert(stackCount < stackSize);
					stack[stackCount++] = other;
					other.m_flags |= b2Body.e_islandFlag;
				}
			}

			island.Solve(this.step, this.m_gravity);

			this.m_positionIterationCount = b2Math.b2Max(this.m_positionIterationCount, b2Island.m_positionIterationCount);

			if (this.m_allowSleep)
			{
				island.UpdateSleep(dt);
			}

			// Post solve cleanup.
			for (var i = 0; i < island.m_bodyCount; ++i)
			{
				// Allow static bodies to participate in other islands.
				b = island.m_bodies[i];
				if (b.m_flags & b2Body.e_staticFlag)
				{
					b.m_flags &= ~b2Body.e_islandFlag;
				}

				// Handle newly frozen bodies.
				if (b.IsFrozen() && this.m_listener)
				{
					var response = this.m_listener.NotifyBoundaryViolated(b);
					if (response == b2WorldListener.b2_destroyBody)
					{
						this.DestroyBody(b);
						b = null;
						island.m_bodies[i] = null;
					}
				}
			}
		}

		this.m_broadPhase.Commit();

		//this.m_stackAllocator.Free(stack);
	},

	// this.Query the world for all shapes that potentially overlap the
	// provided AABB. You provide a shape pointer buffer of specified
	// size. The number of shapes found is returned.
	/**
	 * @method Query
	 */
	Query: function(aabb, shapes, maxCount){

		//void** results = (void**)this.m_stackAllocator.Allocate(maxCount * sizeof(void*));
		var results = new Array();
		var count = this.m_broadPhase.QueryAABB(aabb, results, maxCount);

		for (var i = 0; i < count; ++i)
		{
			shapes[i] = results[i];
		}

		//this.m_stackAllocator.Free(results);
		return count;
	},

	// You can use these to iterate over all the bodies, joints, and contacts.
	/**
	 * @method GetBodyList
	 */
	GetBodyList: function(){
		return this.m_bodyList;
	},
	/**
	 * @method GetJointList
	 */
	GetJointList: function(){
		return this.m_jointList;
	},
	/**
	 * @method GetContactList
	 */
	GetContactList: function(){
		return this.m_contactList;
	},

	/**
	 * @method QueryPoint
	 * @param callback {Function}
	 * @param point {b2Vec2}
	 */
	QueryPoint : function (callback, point) {
	    var body, shape;
	    for (body = this.m_bodyList; body; body = body.m_next) {
	        for (shape = body.GetShapeList(); shape != null; shape = shape.GetNext()) {
	            if (shape.TestPoint(point)) {
	                callback(body);
	            }
	        }
	    }
	},

	/**
	 * @method DebugDraw
	 */
	DebugDraw : function () {
	    var item, shape;
	    this.ctx.clearRect(0, 0, this.ctx.width, this.ctx.height);
	    for (item = world.m_jointList; item; item = item.m_next) {
	        this._drawJoint(item);
	    }
	    for (item = world.m_bodyList; item; item = item.m_next) {
	        for (shape = item.GetShapeList(); shape != null; shape = shape.GetNext()) {
	            this._drawShape(shape);
	        }
	    }
	},
	/**
	 * @method SetDebugDraw
	 */
	SetDebugDraw : function (params) {
	    params = params || {};
	    this.ctx = params.ctx; 
	    this.ctx.width = params.width; 
	    this.ctx.height = params.height;
	},
	_drawJoint : function (joint) {
	    var b1 = joint.m_body1,
	        b2 = joint.m_body2,
	        x1 = b1.m_position,
	        x2 = b2.m_position,
	        p1 = joint.GetAnchor1(),
	        p2 = joint.GetAnchor2();
	    this.ctx.strokeStyle = '#ffffff';
	    this.ctx.beginPath();
	    switch (joint.m_type) {
	        case b2Joint.e_distanceJoint:
	            this.ctx.moveTo(p1.x, p1.y);
	            this.ctx.lineTo(p2.x, p2.y);
	            break;
	        case b2Joint.e_pulleyJoint:
	            // TODO
	            break;
	        default:
	            if (b1 == world.m_groundBody) {
	                this.ctx.moveTo(p1.x, p1.y);
	                this.ctx.lineTo(x2.x, x2.y);
	            }
	            else if (b2 == world.m_groundBody) {
	                this.ctx.moveTo(p1.x, p1.y);
	                this.ctx.lineTo(x1.x, x1.y);
	            }
	            else {
	                this.ctx.moveTo(x1.x, x1.y);
	                this.ctx.lineTo(p1.x, p1.y);
	                this.ctx.lineTo(x2.x, x2.y);
	                this.ctx.lineTo(p2.x, p2.y);
	            }
	            break;
	    }
	    this.ctx.stroke();
	},
	_drawShape : function (shape) {
	    var i, v, tV, pos, r, segments, theta, dtheta, d, ax;
	    this.ctx.strokeStyle = 'rgba(255,255,255,.75)';
	    this.ctx.fillStyle = 'rgba(255,255,255,.33)';
	    this.ctx.beginPath();
	    switch (shape.m_type) {
	        case b2Shape.e_circleShape:
	            pos = shape.m_position;
	            r = shape.m_radius;
	            segments = 16.0;
	            theta = 0.0;
	            dtheta = 2.0 * Math.PI / segments;
	            // draw circle
	            this.ctx.moveTo(pos.x + r, pos.y);
	            for (i = 0; i < segments; i++) {
	                d = new b2Vec2(r * Math.cos(theta), r * Math.sin(theta));
	                v = b2Math.AddVV(pos, d);
	                this.ctx.lineTo(v.x, v.y);
	                theta += dtheta;
	            }
	            this.ctx.lineTo(pos.x + r, pos.y);
	            // draw radius
	            this.ctx.moveTo(pos.x, pos.y);
	            ax = shape.m_R.col1;
	            pos = new b2Vec2(pos.x + r * ax.x, pos.y + r * ax.y);
	            this.ctx.lineTo(pos.x, pos.y);
	            break;
	        case b2Shape.e_polyShape:
	            tV = b2Math.AddVV(
	                shape.m_position,
	                b2Math.b2MulMV(shape.m_R, shape.m_vertices[0])
	            );
	            this.ctx.moveTo(tV.x, tV.y);
	            for (i = 0; i < shape.m_vertexCount; i++) {
	                v = b2Math.AddVV(
	                    shape.m_position,
	                    b2Math.b2MulMV(shape.m_R, shape.m_vertices[i])
	                );
	                this.ctx.lineTo(v.x, v.y);
	            }
	            this.ctx.lineTo(tV.x, tV.y);
	            break;
	    }
	    this.ctx.stroke();
	    this.ctx.fill();
	},

	//--------------- Internals Below -------------------

	m_blockAllocator: null,
	m_stackAllocator: null,

	m_broadPhase: null,
	m_contactManager: new b2ContactManager(),
	/**
	 * @property m_bodyList
	 */
	m_bodyList: null,
	/**
	 * @property m_contactList
	 */
	m_contactList: null,
	/**
	 * @property m_jointList
	 */
	m_jointList: null,

	m_bodyCount: 0,
	m_contactCount: 0,
	m_jointCount: 0,

	// These bodies will be destroyed at the next time this.step.
	m_bodyDestroyList: null,

	m_gravity: null,
	m_allowSleep: null,

	m_groundBody: null,

	m_listener: null,
	m_filter: null,

	m_positionIterationCount: 0

};

b2World.s_enablePositionCorrection = 1;
b2World.s_enableWarmStarting = 1;