diff --git a/README.md b/README.md
index 9cbd3c26..c2287ee4 100644
--- a/README.md
+++ b/README.md
@@ -113,6 +113,7 @@ Version 1.0.7 (in progress in the dev branch)
* World.randomX/Y now works with negative World.bounds values.
* Added killOnComplete parameter to Animation.play. Really useful in situations where you want a Sprite to animate once then kill itself on complete, like an explosion effect.
* Added Sprite.loadTexture(key, frame) which allows you to load a new texture set into an existing sprite rather than having to create a new sprite.
+* Tweens .to will now always return the parent (thanks powerfear)
* TODO: look at Sprite.crop (http://www.html5gamedevs.com/topic/1617-error-in-spritecrop/)
diff --git a/build/phaser.js b/build/phaser.js
index bdee7283..9dfe1d67 100644
--- a/build/phaser.js
+++ b/build/phaser.js
@@ -9,7 +9,7 @@
*
* Phaser - http://www.phaser.io
*
-* v1.0.7 - Built at: Wed, 09 Oct 2013 17:16:16 +0100
+* v1.0.7 - Built at: Thu, 10 Oct 2013 16:51:46 +0100
*
* By Richard Davey http://www.photonstorm.com @photonstorm
*
@@ -7340,10 +7340,21 @@ Phaser.Camera.prototype = {
break;
}
+ },
+
+ /**
+ * Move the camera focus on a display object instantly.
+ * @method Phaser.Camera#focusOn
+ * @param {any} displayObject - The display object to focus the camera on. Must have visible x/y properties.
+ */
+ focusOn: function (displayObject) {
+
+ this.setPosition(Math.round(displayObject.x - this.view.halfWidth), Math.round(displayObject.y - this.view.halfHeight));
+
},
/**
- * Move the camera focus to a location instantly.
+ * Move the camera focus on a location instantly.
* @method Phaser.Camera#focusOnXY
* @param {number} x - X position.
* @param {number} y - Y position.
@@ -16007,6 +16018,7 @@ Phaser.Sprite.prototype.preUpdate = function() {
this.prevY = this.y;
this.updateCache();
+ this.updateAnimation();
// Re-run the camera visibility check
if (this._cache.dirty)
@@ -16065,7 +16077,10 @@ Phaser.Sprite.prototype.updateCache = function() {
this._cache.dirty = true;
}
- // Frame updated?
+}
+
+Phaser.Sprite.prototype.updateAnimation = function() {
+
if (this.currentFrame && this.currentFrame.uuid != this._cache.frameID)
{
this._cache.frameWidth = this.texture.frame.width;
@@ -16119,6 +16134,47 @@ Phaser.Sprite.prototype.postUpdate = function() {
}
+Phaser.Sprite.prototype.loadTexture = function (key, frame) {
+
+ this.key = key;
+
+ if (key instanceof Phaser.RenderTexture)
+ {
+ this.currentFrame = this.game.cache.getTextureFrame(key.name);
+ }
+ else
+ {
+ if (key == null || this.game.cache.checkImageKey(key) == false)
+ {
+ key = '__default';
+ }
+
+ if (this.game.cache.isSpriteSheet(key))
+ {
+ this.animations.loadFrameData(this.game.cache.getFrameData(key));
+
+ if (frame !== null)
+ {
+ if (typeof frame === 'string')
+ {
+ this.frameName = frame;
+ }
+ else
+ {
+ this.frame = frame;
+ }
+ }
+ }
+ else
+ {
+ this.currentFrame = this.game.cache.getFrame(key);
+ }
+ }
+
+ this.updateAnimation();
+
+}
+
Phaser.Sprite.prototype.deltaAbsX = function () {
return (this.deltaX() > 0 ? this.deltaX() : -this.deltaX());
}
@@ -16430,6 +16486,18 @@ Object.defineProperty(Phaser.Sprite.prototype, "inCamera", {
});
+/**
+*
+* @returns {boolean}
+*/
+Object.defineProperty(Phaser.Sprite.prototype, "worldX", {
+
+ get: function () {
+ return 1;
+ }
+
+});
+
/**
* Get the input enabled state of this Sprite.
* @returns {Description}
@@ -22959,6 +23027,7 @@ Phaser.Tween.prototype = {
*/
pause: function () {
this._paused = true;
+ this._pausedTime = this.game.time.now;
},
/**
@@ -22968,7 +23037,7 @@ Phaser.Tween.prototype = {
*/
resume: function () {
this._paused = false;
- this._startTime += this.game.time.pauseDuration;
+ this._startTime += (this.game.time.now - this._pausedTime);
},
/**
@@ -29081,6 +29150,7 @@ Phaser.Utils.Debug.prototype = {
this.line('angle: ' + sprite.angle.toFixed(1) + ' rotation: ' + sprite.rotation.toFixed(1));
this.line('visible: ' + sprite.visible + ' in camera: ' + sprite.inCamera);
this.line('body x: ' + sprite.body.x.toFixed(1) + ' y: ' + sprite.body.y.toFixed(1));
+ this.stop();
// 0 = scaleX
// 1 = skewY
@@ -29131,6 +29201,7 @@ Phaser.Utils.Debug.prototype = {
this.line('scaleY: ' + sprite.worldTransform[4]);
this.line('transX: ' + sprite.worldTransform[2]);
this.line('transY: ' + sprite.worldTransform[5]);
+ this.stop();
},
@@ -29160,6 +29231,49 @@ Phaser.Utils.Debug.prototype = {
this.line('scaleY: ' + sprite.localTransform[4]);
this.line('transX: ' + sprite.localTransform[2]);
this.line('transY: ' + sprite.localTransform[5]);
+ this.stop();
+
+ },
+
+ renderSpriteCoords: function (sprite, x, y, color) {
+
+ if (this.context == null)
+ {
+ return;
+ }
+
+ color = color || 'rgb(255, 255, 255)';
+
+ this.start(x, y, color);
+
+ this.line(sprite.name);
+ this.line('x: ' + sprite.x);
+ this.line('y: ' + sprite.y);
+ this.line('local x: ' + sprite.localTransform[2]);
+ this.line('local y: ' + sprite.localTransform[5]);
+ this.line('world x: ' + sprite.worldTransform[2]);
+ this.line('world y: ' + sprite.worldTransform[5]);
+
+ this.stop();
+
+ },
+
+ renderGroupInfo: function (group, x, y, color) {
+
+ if (this.context == null)
+ {
+ return;
+ }
+
+ color = color || 'rgb(255, 255, 255)';
+
+ this.start(x, y, color);
+
+ this.line('Group (size: ' + group.length + ')');
+ this.line('x: ' + group.x);
+ this.line('y: ' + group.y);
+
+ this.stop();
},
diff --git a/examples/collision/transform.php b/examples/collision/transform.php
index c87dbf97..b33b677c 100644
--- a/examples/collision/transform.php
+++ b/examples/collision/transform.php
@@ -13,12 +13,14 @@
game.load.image('atari', 'assets/sprites/atari130xe.png');
game.load.image('mushroom', 'assets/sprites/mushroom2.png');
+ game.load.image('flectrum', 'assets/sprites/flectrum.png');
}
var testGroup;
var sprite1;
var sprite2;
+ var sprite3;
function create() {
@@ -61,6 +63,9 @@
sprite2 = game.add.sprite(-100, 150, 'mushroom');
sprite2.name = 'mushroom';
+ sprite3 = game.add.sprite(-200, 150, 'flectrum');
+ sprite3.name = 'tall';
+
testGroup.x = -600;
testGroup.y = 200;
@@ -88,6 +93,8 @@
game.physics.collide(sprite1, sprite2, collisionHandler, null, this);
+ // sprite3.angle += 0.5;
+
}
function collisionHandler (obj1, obj2) {
@@ -110,6 +117,7 @@
game.debug.renderSpriteBody(sprite1);
game.debug.renderSpriteBody(sprite2);
+ game.debug.renderSpriteBody(sprite3);
game.debug.renderGroupInfo(testGroup, 500, 500);
game.debug.renderPixel(testGroup.x, testGroup.y, 'rgb(255,255,0)');
diff --git a/examples/js.php b/examples/js.php
index 807181eb..e41fb353 100644
--- a/examples/js.php
+++ b/examples/js.php
@@ -114,9 +114,10 @@
+
-
-
+
+
diff --git a/examples/tilemaps/wip1.php b/examples/tilemaps/wip1.php
new file mode 100644
index 00000000..2b429fb0
--- /dev/null
+++ b/examples/tilemaps/wip1.php
@@ -0,0 +1,74 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/tweens/chained tweens.php b/examples/tweens/chained tweens.php
index 000e88ec..8b2acf68 100644
--- a/examples/tweens/chained tweens.php
+++ b/examples/tweens/chained tweens.php
@@ -5,15 +5,14 @@
diff --git a/src/gameobjects/RenderTexture.js b/src/gameobjects/RenderTexture.js
index e9d154cd..3a213106 100644
--- a/src/gameobjects/RenderTexture.js
+++ b/src/gameobjects/RenderTexture.js
@@ -39,7 +39,8 @@ Phaser.RenderTexture = function (game, key, width, height) {
*/
this.height = height || 100;
- /** I know this has a typo in it, but it's because the PIXI.RenderTexture does and we need to pair-up with it
+ /**
+ * I know this has a typo in it, but it's because the PIXI.RenderTexture does and we need to pair-up with it
* once they update pixi to fix the typo, we'll fix it here too :)
* @property {Description} indetityMatrix - Description.
*/
diff --git a/src/loader/Cache.js b/src/loader/Cache.js
index 436a1607..b350718e 100644
--- a/src/loader/Cache.js
+++ b/src/loader/Cache.js
@@ -56,6 +56,11 @@ Phaser.Cache = function (game) {
*/
this._tilemaps = {};
+ /**
+ * @property {object} _tilesets - Tileset key-value container.
+ * @private
+ */
+ this._tilesets = {};
this.addDefaultImage();
@@ -118,6 +123,28 @@ Phaser.Cache.prototype = {
},
+ /**
+ * Add a new tile set in to the cache.
+ *
+ * @method Phaser.Cache#addTileset
+ * @param {string} key - The unique key by which you will reference this object.
+ * @param {string} url - URL of this tile set file.
+ * @param {object} data - Extra tile set data.
+ * @param {number} tileWidth - Width of the sprite sheet.
+ * @param {number} tileHeight - Height of the sprite sheet.
+ * @param {number} tileMax - How many tiles stored in the sprite sheet.
+ */
+ addTileset: function (key, url, data, tileWidth, tileHeight, tileMax) {
+
+ this._tilesets[key] = { url: url, data: data, tileWidth: tileWidth, tileHeight: tileHeight };
+
+ PIXI.BaseTextureCache[key] = new PIXI.BaseTexture(data);
+ PIXI.TextureCache[key] = new PIXI.Texture(PIXI.BaseTextureCache[key]);
+
+ this._tilesets[key].tileData = Phaser.TilemapParser.tileset(this.game, key, tileWidth, tileHeight, tileMax);
+
+ },
+
/**
* Add a new tilemap.
*
@@ -208,6 +235,23 @@ Phaser.Cache.prototype = {
},
+ /**
+ * Add a new text data.
+ *
+ * @method Phaser.Cache#addText
+ * @param {string} key - Asset key for the text data.
+ * @param {string} url - URL of this text data file.
+ * @param {object} data - Extra text data.
+ */
+ addText: function (key, url, data) {
+
+ this._text[key] = {
+ url: url,
+ data: data
+ };
+
+ },
+
/**
* Add a new image.
*
@@ -316,23 +360,6 @@ Phaser.Cache.prototype = {
this._sounds[key].decoded = true;
this._sounds[key].isDecoding = false;
- },
-
- /**
- * Add a new text data.
- *
- * @method Phaser.Cache#addText
- * @param {string} key - Asset key for the text data.
- * @param {string} url - URL of this text data file.
- * @param {object} data - Extra text data.
- */
- addText: function (key, url, data) {
-
- this._text[key] = {
- url: url,
- data: data
- };
-
},
/**
@@ -387,6 +414,42 @@ Phaser.Cache.prototype = {
return null;
},
+ /**
+ * Get tile set image data by key.
+ *
+ * @method Phaser.Cache#getTileSetImage
+ * @param {string} key - Asset key of the image you want.
+ * @return {object} The image data you want.
+ */
+ getTilesetImage: function (key) {
+
+ if (this._tilesets[key])
+ {
+ return this._tilesets[key].data;
+ }
+
+ return null;
+
+ },
+
+ /**
+ * Get tile set image data by key.
+ *
+ * @method Phaser.Cache#getTileset
+ * @param {string} key - Asset key of the image you want.
+ * @return {Phaser.Tileset} The tileset data. The tileset image is in the data property, the tile data in tileData.
+ */
+ getTileset: function (key) {
+
+ if (this._tilesets[key])
+ {
+ return this._tilesets[key];
+ }
+
+ return null;
+
+ },
+
/**
* Get tilemap data by key.
*
diff --git a/src/loader/Loader.js b/src/loader/Loader.js
index 733d5f8b..33ec4920 100644
--- a/src/loader/Loader.js
+++ b/src/loader/Loader.js
@@ -280,7 +280,7 @@ Phaser.Loader.prototype = {
* @param {string} url - URL of the sheet file.
* @param {number} frameWidth - Width of each single frame.
* @param {number} frameHeight - Height of each single frame.
- * @param {number} frameMax - How many frames in this sprite sheet.
+ * @param {number} [frameMax=-1] - How many frames in this sprite sheet. If not specified it will divide the whole image into frames.
*/
spritesheet: function (key, url, frameWidth, frameHeight, frameMax) {
@@ -293,6 +293,27 @@ Phaser.Loader.prototype = {
},
+ /**
+ * Add a new tile set to the loader. These are used in the rendering of tile maps.
+ *
+ * @method Phaser.Loader#tileset
+ * @param {string} key - Unique asset key of the tileset file.
+ * @param {string} url - URL of the tileset.
+ * @param {number} tileWidth - Width of each single tile in pixels.
+ * @param {number} tileHeight - Height of each single tile in pixels.
+ * @param {number} [tileMax=-1] - How many tiles in this tileset. If not specified it will divide the whole image into tiles.
+ */
+ tileset: function (key, url, tileWidth, tileHeight, tileMax) {
+
+ if (typeof tileMax === "undefined") { tileMax = -1; }
+
+ if (this.checkKeyExists(key) === false)
+ {
+ this.addToFileList('tileset', key, url, { tileWidth: tileWidth, tileHeight: tileHeight, tileMax: tileMax });
+ }
+
+ },
+
/**
* Add a new audio file to the loader.
*
@@ -617,6 +638,7 @@ Phaser.Loader.prototype = {
case 'textureatlas':
case 'bitmapfont':
case 'tilemap':
+ case 'tileset':
file.data = new Image();
file.data.name = file.key;
file.data.onload = function () {
@@ -771,6 +793,11 @@ Phaser.Loader.prototype = {
this.game.cache.addSpriteSheet(file.key, file.url, file.data, file.frameWidth, file.frameHeight, file.frameMax);
break;
+ case 'tileset':
+
+ this.game.cache.addTileset(file.key, file.url, file.data, file.tileWidth, file.tileHeight, file.tileMax);
+ break;
+
case 'tilemap':
if (file.mapDataURL == null)
diff --git a/src/physics/advanced/Body.js b/src/physics/advanced/Body.js
deleted file mode 100644
index 03dbcba0..00000000
--- a/src/physics/advanced/Body.js
+++ /dev/null
@@ -1,402 +0,0 @@
-/*
-* Copyright (c) 2012 Ju Hyung Lee
-*
-* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
-* and associated documentation files (the "Software"), to deal in the Software without
-* restriction, including without limitation the rights to use, copy, modify, merge, publish,
-* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
-* Software is furnished to do so, subject to the following conditions:
-*
-* The above copyright notice and this permission notice shall be included in all copies or
-* substantial portions of the Software.
-*
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
-* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-Body = function(type, pos, angle) {
- if (Body.id_counter == undefined) {
- Body.id_counter = 0;
- }
-
- this.id = Body.id_counter++;
-
- // Identifier
- this.name = "body" + this.id;
-
- // STATIC or DYNAMIC
- this.type = type;
-
- // Default values
- pos = pos || new vec2(0, 0);
- angle = angle || 0;
-
- // Local to world transform
- this.xf = new Transform(pos, angle);
-
- // Local center of mass
- this.centroid = new vec2(0, 0);
-
- // World position of centroid
- this.p = new vec2(pos.x, pos.y);
-
- // Velocity
- this.v = new vec2(0, 0);
-
- // Force
- this.f = new vec2(0, 0);
-
- // Orientation (angle)
- this.a = angle;
-
- // Angular velocity
- this.w = 0;
-
- // Torque
- this.t = 0;
-
- // Linear damping
- this.linearDamping = 0;
-
- // Angular damping
- this.angularDamping = 0;
-
- // Sleep time
- this.sleepTime = 0;
-
- // Awaked flag
- this.awaked = false;
-
- // Shape list for this body
- this.shapeArr = [];
-
- // Joint hash for this body
- this.jointArr = [];
- this.jointHash = {};
-
- // Bounds of all shapes
- this.bounds = new Bounds;
-
- this.fixedRotation = false;
-
- this.categoryBits = 0x0001;
- this.maskBits = 0xFFFF;
-
- this.stepCount = 0;
-}
-
-Body.STATIC = 0;
-Body.KINETIC = 1;
-Body.DYNAMIC = 2;
-
-Body.prototype.duplicate = function() {
- var body = new Body(this.type, this.xf.t, this.a);
- for (var i = 0; i < this.shapeArr.length; i++) {
- body.addShape(this.shapeArr[i].duplicate());
- }
- body.resetMassData();
-
- return body;
-}
-
-Body.prototype.serialize = function() {
- var shapes = [];
- for (var i = 0; i < this.shapeArr.length; i++) {
- var obj = this.shapeArr[i].serialize();
- shapes.push(obj);
- }
-
- return {
- "type": ["static", "kinetic", "dynamic"][this.type],
- "name": this.name,
- "position": this.xf.t,
- "angle": this.xf.a,
- "shapes": shapes
- };
-}
-
-Body.prototype.toString = function() {
- return "[{Body (name=" + this.name + " velocity=" + this.v.toString() + " angularVelocity: " + this.w + ")}]";
-}
-
-Body.prototype.isStatic = function() {
- return this.type == Body.STATIC ? true : false;
-}
-
-Body.prototype.isDynamic = function() {
- return this.type == Body.DYNAMIC ? true : false;
-}
-
-Body.prototype.isKinetic = function() {
- return this.type == Body.KINETIC ? true : false;
-}
-
-Body.prototype.setType = function(type) {
- if (type == this.type) {
- return;
- }
-
- this.f.set(0, 0);
- this.v.set(0, 0);
- this.t = 0;
- this.w = 0;
- this.type = type;
-
- this.awake(true);
-}
-
-Body.prototype.addShape = function(shape) {
- shape.body = this;
- this.shapeArr.push(shape);
-}
-
-Body.prototype.removeShape = function(shape) {
- var index = this.shapeArr.indexOf(shape);
- if (index != -1) {
- this.shapeArr.splice(index, 1);
- shape.body = undefined;
- }
-}
-
-// Internal function
-Body.prototype.setMass = function(mass) {
- this.m = mass;
- this.m_inv = mass > 0 ? 1 / mass : 0;
-}
-
-// Internal function
-Body.prototype.setInertia = function(inertia) {
- this.i = inertia;
- this.i_inv = inertia > 0 ? 1 / inertia : 0;
-}
-
-Body.prototype.setTransform = function(pos, angle) {
- this.xf.set(pos, angle);
- this.p = this.xf.transform(this.centroid);
- this.a = angle;
-}
-
-Body.prototype.syncTransform = function() {
- this.xf.setRotation(this.a);
- this.xf.setPosition(vec2.sub(this.p, this.xf.rotate(this.centroid)));
-}
-
-Body.prototype.getWorldPoint = function(p) {
- return this.xf.transform(p);
-}
-
-Body.prototype.getWorldVector = function(v) {
- return this.xf.rotate(v);
-}
-
-Body.prototype.getLocalPoint = function(p) {
- return this.xf.untransform(p);
-}
-
-Body.prototype.getLocalVector = function(v) {
- return this.xf.unrotate(v);
-}
-
-Body.prototype.setFixedRotation = function(flag) {
- this.fixedRotation = flag;
- this.resetMassData();
-}
-
-Body.prototype.resetMassData = function() {
- this.centroid.set(0, 0);
- this.m = 0;
- this.m_inv = 0;
- this.i = 0;
- this.i_inv = 0;
-
- if (!this.isDynamic()) {
- this.p = this.xf.transform(this.centroid);
- return;
- }
-
- var totalMassCentroid = new vec2(0, 0);
- var totalMass = 0;
- var totalInertia = 0;
-
- for (var i = 0; i < this.shapeArr.length; i++) {
- var shape = this.shapeArr[i];
- var centroid = shape.centroid();
- var mass = shape.area() * shape.density;
- var inertia = shape.inertia(mass);
-
- totalMassCentroid.mad(centroid, mass);
- totalMass += mass;
- totalInertia += inertia;
- }
-
- this.centroid.copy(vec2.scale(totalMassCentroid, 1 / totalMass));
- this.setMass(totalMass);
-
- if (!this.fixedRotation) {
- this.setInertia(totalInertia - totalMass * vec2.dot(this.centroid, this.centroid));
- }
-
- // Move center of mass
- var old_p = this.p;
- this.p = this.xf.transform(this.centroid);
-
- // Update center of mass velocity ??
- this.v.mad(vec2.perp(vec2.sub(this.p, old_p)), this.w);
-}
-
-Body.prototype.resetJointAnchors = function() {
- for (var i = 0; i < this.jointArr.length; i++) {
- var joint = this.jointArr[i];
- if (!joint) {
- continue;
- }
-
- var anchor1 = joint.getWorldAnchor1();
- var anchor2 = joint.getWorldAnchor2();
-
- joint.setWorldAnchor1(anchor1);
- joint.setWorldAnchor2(anchor2);
- }
-}
-
-Body.prototype.cacheData = function() {
-
- this.bounds.clear();
-
- for (var i = 0; i < this.shapeArr.length; i++) {
- var shape = this.shapeArr[i];
- shape.cacheData(this.xf);
- this.bounds.addBounds(shape.bounds);
- }
-
-}
-
-Body.prototype.updateVelocity = function(gravity, dt, damping) {
- this.v = vec2.mad(this.v, vec2.mad(gravity, this.f, this.m_inv), dt);
- this.w = this.w + this.t * this.i_inv * dt;
-
- // Apply damping.
- // ODE: dv/dt + c * v = 0
- // Solution: v(t) = v0 * exp(-c * t)
- // Time step: v(t + dt) = v0 * exp(-c * (t + dt)) = v0 * exp(-c * t) * exp(-c * dt) = v * exp(-c * dt)
- // v2 = exp(-c * dt) * v1
- // Taylor expansion:
- // v2 = (1.0f - c * dt) * v1
- this.v.scale(Math.clamp(1 - dt * (damping + this.linearDamping), 0, 1));
- this.w *= Math.clamp(1 - dt * (damping + this.angularDamping), 0, 1);
-
- this.f.set(0, 0);
- this.t = 0;
-}
-
-Body.prototype.updatePosition = function(dt) {
- this.p.addself(vec2.scale(this.v, dt));
- this.a += this.w * dt;
-}
-
-Body.prototype.resetForce = function() {
- this.f.set(0, 0);
- this.t = 0;
-}
-
-Body.prototype.applyForce = function(force, p) {
- if (!this.isDynamic())
- return;
-
- if (!this.isAwake())
- this.awake(true);
-
- this.f.addself(force);
- this.t += vec2.cross(vec2.sub(p, this.p), force);
-}
-
-Body.prototype.applyForceToCenter = function(force) {
- if (!this.isDynamic())
- return;
-
- if (!this.isAwake())
- this.awake(true);
-
- this.f.addself(force);
-}
-
-Body.prototype.applyTorque = function(torque) {
- if (!this.isDynamic())
- return;
-
- if (!this.isAwake())
- this.awake(true);
-
- this.t += torque;
-}
-
-Body.prototype.applyLinearImpulse = function(impulse, p) {
- if (!this.isDynamic())
- return;
-
- if (!this.isAwake())
- this.awake(true);
-
- this.v.mad(impulse, this.m_inv);
- this.w += vec2.cross(vec2.sub(p, this.p), impulse) * this.i_inv;
-}
-
-Body.prototype.applyAngularImpulse = function(impulse) {
- if (!this.isDynamic())
- return;
-
- if (!this.isAwake())
- this.awake(true);
-
- this.w += impulse * this.i_inv;
-}
-
-Body.prototype.kineticEnergy = function() {
- var vsq = this.v.dot(this.v);
- var wsq = this.w * this.w;
- return 0.5 * (this.m * vsq + this.i * wsq);
-}
-
-Body.prototype.isAwake = function() {
- return this.awaked;
-}
-
-Body.prototype.awake = function(flag) {
- this.awaked = flag;
- if (flag) {
- this.sleepTime = 0;
- }
- else {
- this.v.set(0, 0);
- this.w = 0;
- this.f.set(0, 0);
- this.t = 0;
- }
-}
-
-Body.prototype.isCollidable = function(other) {
- if (this == other)
- return false;
-
- if (!this.isDynamic() && !other.isDynamic())
- return false;
-
- if (!(this.maskBits & other.categoryBits) || !(other.maskBits & this.categoryBits))
- return false;
-
- for (var i = 0; i < this.jointArr.length; i++) {
- var joint = this.jointArr[i];
- if (!joint) {
- continue;
- }
-
- if (!joint.collideConnected && other.jointHash[joint.id] != undefined) {
- return false;
- }
- }
-
- return true;
-}
\ No newline at end of file
diff --git a/src/physics/advanced/Collision.js b/src/physics/advanced/Collision.js
deleted file mode 100644
index 848a07aa..00000000
--- a/src/physics/advanced/Collision.js
+++ /dev/null
@@ -1,382 +0,0 @@
-/*
-* Copyright (c) 2012 Ju Hyung Lee
-*
-* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
-* and associated documentation files (the "Software"), to deal in the Software without
-* restriction, including without limitation the rights to use, copy, modify, merge, publish,
-* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
-* Software is furnished to do so, subject to the following conditions:
-*
-* The above copyright notice and this permission notice shall be included in all copies or
-* substantial portions of the Software.
-*
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
-* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-var collision = {};
-
-(function() {
- var colFuncs = [];
-
- function addCollideFunc(a, b, func) {
- colFuncs[a * Shape.NUM_TYPES + b] = func;
- }
-
- function _circle2Circle(c1, r1, c2, r2, contactArr) {
- var rmax = r1 + r2;
- var t = vec2.sub(c2, c1);
- var distsq = t.lengthsq();
-
- if (distsq > rmax * rmax) {
- return 0;
- }
-
- var dist = Math.sqrt(distsq);
-
- var p = vec2.mad(c1, t, 0.5 + (r1 - r2) * 0.5 / dist);
- var n = (dist != 0) ? vec2.scale(t, 1 / dist) : vec2.zero;
- var d = dist - rmax;
-
- contactArr.push(new Contact(p, n, d, 0));
-
- return 1;
- }
-
- function circle2Circle(circ1, circ2, contactArr) {
- return _circle2Circle(circ1.tc, circ1.r, circ2.tc, circ2.r, contactArr);
- }
-
- function circle2Segment(circ, seg, contactArr) {
- var rsum = circ.r + seg.r;
-
- // Normal distance from segment
- var dn = vec2.dot(circ.tc, seg.tn) - vec2.dot(seg.ta, seg.tn);
- var dist = (dn < 0 ? dn * -1 : dn) - rsum;
- if (dist > 0) {
- return 0;
- }
-
- // Tangential distance along segment
- var dt = vec2.cross(circ.tc, seg.tn);
- var dtMin = vec2.cross(seg.ta, seg.tn);
- var dtMax = vec2.cross(seg.tb, seg.tn);
-
- if (dt < dtMin) {
- if (dt < dtMin - rsum) {
- return 0;
- }
-
- return _circle2Circle(circ.tc, circ.r, seg.ta, seg.r, contactArr);
- }
- else if (dt > dtMax) {
- if (dt > dtMax + rsum) {
- return 0;
- }
-
- return _circle2Circle(circ.tc, circ.r, seg.tb, seg.r, contactArr);
- }
-
- var n = (dn > 0) ? seg.tn : vec2.neg(seg.tn);
-
- contactArr.push(new Contact(vec2.mad(circ.tc, n, -(circ.r + dist * 0.5)), vec2.neg(n), dist, 0));
-
- return 1;
- }
-
- function circle2Poly(circ, poly, contactArr) {
- var minDist = -999999;
- var minIdx = -1;
-
- for (var i = 0; i < poly.verts.length; i++) {
- var plane = poly.tplanes[i];
- var dist = vec2.dot(circ.tc, plane.n) - plane.d - circ.r;
-
- if (dist > 0) {
- return 0;
- }
- else if (dist > minDist) {
- minDist = dist;
- minIdx = i;
- }
- }
-
- var n = poly.tplanes[minIdx].n;
- var a = poly.tverts[minIdx];
- var b = poly.tverts[(minIdx + 1) % poly.verts.length];
- var dta = vec2.cross(a, n);
- var dtb = vec2.cross(b, n);
- var dt = vec2.cross(circ.tc, n);
-
- if (dt > dta) {
- return _circle2Circle(circ.tc, circ.r, a, 0, contactArr);
- }
- else if (dt < dtb) {
- return _circle2Circle(circ.tc, circ.r, b, 0, contactArr);
- }
-
- contactArr.push(new Contact(vec2.mad(circ.tc, n, -(circ.r + minDist * 0.5)), vec2.neg(n), minDist, 0));
-
- return 1;
- }
-
- function segmentPointDistanceSq(seg, p) {
- var w = vec2.sub(p, seg.ta);
- var d = vec2.sub(seg.tb, seg.ta);
-
- var proj = w.dot(d);
- if (proj <= 0) {
- return w.dot(w);
- }
-
- var vsq = d.dot(d)
- if (proj >= vsq) {
- return w.dot(w) - 2 * proj + vsq;
- }
-
- return w.dot(w) - proj * proj / vsq;
- }
-
- // FIXME !!
- function segment2Segment(seg1, seg2, contactArr) {
- var d = [];
- d[0] = segmentPointDistanceSq(seg1, seg2.ta);
- d[1] = segmentPointDistanceSq(seg1, seg2.tb);
- d[2] = segmentPointDistanceSq(seg2, seg1.ta);
- d[3] = segmentPointDistanceSq(seg2, seg1.tb);
-
- var idx1 = d[0] < d[1] ? 0 : 1;
- var idx2 = d[2] < d[3] ? 2 : 3;
- var idxm = d[idx1] < d[idx2] ? idx1 : idx2;
- var s, t;
-
- var u = vec2.sub(seg1.tb, seg1.ta);
- var v = vec2.sub(seg2.tb, seg2.ta);
-
- switch (idxm) {
- case 0:
- s = vec2.dot(vec2.sub(seg2.ta, seg1.ta), u) / vec2.dot(u, u);
- s = s < 0 ? 0 : (s > 1 ? 1 : s);
- t = 0;
- break;
- case 1:
- s = vec2.dot(vec2.sub(seg2.tb, seg1.ta), u) / vec2.dot(u, u);
- s = s < 0 ? 0 : (s > 1 ? 1 : s);
- t = 1;
- break;
- case 2:
- s = 0;
- t = vec2.dot(vec2.sub(seg1.ta, seg2.ta), v) / vec2.dot(v, v);
- t = t < 0 ? 0 : (t > 1 ? 1 : t);
- break;
- case 3:
- s = 1;
- t = vec2.dot(vec2.sub(seg1.tb, seg2.ta), v) / vec2.dot(v, v);
- t = t < 0 ? 0 : (t > 1 ? 1 : t);
- break;
- }
-
- var minp1 = vec2.mad(seg1.ta, u, s);
- var minp2 = vec2.mad(seg2.ta, v, t);
-
- return _circle2Circle(minp1, seg1.r, minp2, seg2.r, contactArr);
- }
-
- // Identify vertexes that have penetrated the segment.
- function findPointsBehindSeg(contactArr, seg, poly, dist, coef) {
- var dta = vec2.cross(seg.tn, seg.ta);
- var dtb = vec2.cross(seg.tn, seg.tb);
- var n = vec2.scale(seg.tn, coef);
-
- for (var i = 0; i < poly.verts.length; i++) {
- var v = poly.tverts[i];
- if (vec2.dot(v, n) < vec2.dot(seg.tn, seg.ta) * coef + seg.r) {
- var dt = vec2.cross(seg.tn, v);
- if (dta >= dt && dt >= dtb) {
- contactArr.push(new Contact(v, n, dist, (poly.id << 16) | i));
- }
- }
- }
- }
-
- function segment2Poly(seg, poly, contactArr) {
- var seg_td = vec2.dot(seg.tn, seg.ta);
- var seg_d1 = poly.distanceOnPlane(seg.tn, seg_td) - seg.r;
- if (seg_d1 > 0) {
- return 0;
- }
- var seg_d2 = poly.distanceOnPlane(vec2.neg(seg.tn), -seg_td) - seg.r;
- if (seg_d2 > 0) {
- return 0;
- }
-
- var poly_d = -999999;
- var poly_i = -1;
-
- for (var i = 0; i < poly.verts.length; i++) {
- var plane = poly.tplanes[i];
- var dist = seg.distanceOnPlane(plane.n, plane.d);
- if (dist > 0) {
- return 0;
- }
-
- if (dist > poly_d) {
- poly_d = dist;
- poly_i = i;
- }
- }
-
- var poly_n = vec2.neg(poly.tplanes[poly_i].n);
- var va = vec2.mad(seg.ta, poly_n, seg.r);
- var vb = vec2.mad(seg.tb, poly_n, seg.r);
-
- if (poly.containPoint(va)) {
- contactArr.push(new Contact(va, poly_n, poly_d, (seg.id << 16) | 0));
- }
-
- if (poly.containPoint(vb)) {
- contactArr.push(new Contact(vb, poly_n, poly_d, (seg.id << 16) | 1));
- }
-
- // Floating point precision problems here.
- // This will have to do for now.
- poly_d -= 0.1
- if (seg_d1 >= poly_d || seg_d2 >= poly_d) {
- if (seg_d1 > seg_d2) {
- findPointsBehindSeg(contactArr, seg, poly, seg_d1, 1);
- }
- else {
- findPointsBehindSeg(contactArr, seg, poly, seg_d2, -1);
- }
- }
-
- // If no other collision points are found, try colliding endpoints.
- if (contactArr.length == 0) {
- var poly_a = poly.tverts[poly_i];
- var poly_b = poly.tverts[(poly_i + 1) % poly.verts.length];
-
- if (_circle2Circle(seg.ta, seg.r, poly_a, 0, contactArr))
- return 1;
-
- if (_circle2Circle(seg.tb, seg.r, poly_a, 0, contactArr))
- return 1;
-
- if (_circle2Circle(seg.ta, seg.r, poly_b, 0, contactArr))
- return 1;
-
- if (_circle2Circle(seg.tb, seg.r, poly_b, 0, contactArr))
- return 1;
- }
-
- return contactArr.length;
- }
-
- // Find the minimum separating axis for the given poly and plane list.
- function findMSA(poly, planes, num) {
- var min_dist = -999999;
- var min_index = -1;
-
- for (var i = 0; i < num; i++) {
- var dist = poly.distanceOnPlane(planes[i].n, planes[i].d);
- if (dist > 0) { // no collision
- return { dist: 0, index: -1 };
- }
- else if (dist > min_dist) {
- min_dist = dist;
- min_index = i;
- }
- }
-
- return { dist: min_dist, index: min_index };
- }
-
- function findVertsFallback(contactArr, poly1, poly2, n, dist) {
- var num = 0;
-
- for (var i = 0; i < poly1.verts.length; i++) {
- var v = poly1.tverts[i];
- if (poly2.containPointPartial(v, n)) {
- contactArr.push(new Contact(v, n, dist, (poly1.id << 16) | i));
-
- num++;
- }
- }
-
- for (var i = 0; i < poly2.verts.length; i++) {
- var v = poly2.tverts[i];
- if (poly1.containPointPartial(v, n)) {
- contactArr.push(new Contact(v, n, dist, (poly2.id << 16) | i));
-
- num++;
- }
- }
-
- return num;
- }
-
- // Find the overlapped vertices.
- function findVerts(contactArr, poly1, poly2, n, dist) {
- var num = 0;
-
- for (var i = 0; i < poly1.verts.length; i++) {
- var v = poly1.tverts[i];
- if (poly2.containPoint(v)) {
- contactArr.push(new Contact(v, n, dist, (poly1.id << 16) | i));
-
- num++;
- }
- }
-
- for (var i = 0; i < poly2.verts.length; i++) {
- var v = poly2.tverts[i];
- if (poly1.containPoint(v)) {
- contactArr.push(new Contact(v, n, dist, (poly2.id << 16) | i));
-
- num++;
- }
- }
-
- return num > 0 ? num : findVertsFallback(contactArr, poly1, poly2, n, dist);
- }
-
- function poly2Poly(poly1, poly2, contactArr) {
- var msa1 = findMSA(poly2, poly1.tplanes, poly1.verts.length);
- if (msa1.index == -1) {
- return 0;
- }
-
- var msa2 = findMSA(poly1, poly2.tplanes, poly2.verts.length);
- if (msa2.index == -1) {
- return 0;
- }
-
- // Penetration normal direction shoud be from poly1 to poly2
- if (msa1.dist > msa2.dist) {
- return findVerts(contactArr, poly1, poly2, poly1.tplanes[msa1.index].n, msa1.dist);
- }
-
- return findVerts(contactArr, poly1, poly2, vec2.neg(poly2.tplanes[msa2.index].n), msa2.dist);
- }
-
- collision.init = function() {
- addCollideFunc(Shape.TYPE_CIRCLE, Shape.TYPE_CIRCLE, circle2Circle);
- addCollideFunc(Shape.TYPE_CIRCLE, Shape.TYPE_SEGMENT, circle2Segment);
- addCollideFunc(Shape.TYPE_CIRCLE, Shape.TYPE_POLY, circle2Poly);
- addCollideFunc(Shape.TYPE_SEGMENT, Shape.TYPE_SEGMENT, segment2Segment);
- addCollideFunc(Shape.TYPE_SEGMENT, Shape.TYPE_POLY, segment2Poly);
- addCollideFunc(Shape.TYPE_POLY, Shape.TYPE_POLY, poly2Poly);
- };
-
- collision.collide = function(a, b, contactArr) {
- if (a.type > b.type) {
- var c = a;
- a = b;
- b = c;
- }
-
- return colFuncs[a.type * Shape.NUM_TYPES + b.type](a, b, contactArr);
- };
-})();
diff --git a/src/physics/advanced/Contact.js b/src/physics/advanced/Contact.js
deleted file mode 100644
index f4ab7423..00000000
--- a/src/physics/advanced/Contact.js
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
-* Copyright (c) 2012 Ju Hyung Lee
-*
-* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
-* and associated documentation files (the "Software"), to deal in the Software without
-* restriction, including without limitation the rights to use, copy, modify, merge, publish,
-* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
-* Software is furnished to do so, subject to the following conditions:
-*
-* The above copyright notice and this permission notice shall be included in all copies or
-* substantial portions of the Software.
-*
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
-* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-function Contact(p, n, d, hash) {
- this.hash = hash;
-
- // Contact point
- this.p = p;
-
- // Contact normal (toward shape2)
- this.n = n;
-
- // Penetration depth (d < 0)
- this.d = d;
-
- // Accumulated normal constraint impulse
- this.lambda_n_acc = 0;
-
- // Accumulated tangential constraint impulse
- this.lambda_t_acc = 0;
-}
\ No newline at end of file
diff --git a/src/physics/advanced/ContactSolver.js b/src/physics/advanced/ContactSolver.js
deleted file mode 100644
index b5f2b31b..00000000
--- a/src/physics/advanced/ContactSolver.js
+++ /dev/null
@@ -1,267 +0,0 @@
-/*
-* Copyright (c) 2012 Ju Hyung Lee
-*
-* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
-* and associated documentation files (the "Software"), to deal in the Software without
-* restriction, including without limitation the rights to use, copy, modify, merge, publish,
-* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
-* Software is furnished to do so, subject to the following conditions:
-*
-* The above copyright notice and this permission notice shall be included in all copies or
-* substantial portions of the Software.
-*
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
-* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-//-------------------------------------------------------------------------------------------------
-// Contact Constraint
-//
-// Non-penetration constraint:
-// C = dot(p2 - p1, n)
-// Cdot = dot(v2 - v1, n)
-// J = [ -n, -cross(r1, n), n, cross(r2, n) ]
-//
-// impulse = JT * lambda = [ -n * lambda, -cross(r1, n) * lambda, n * lambda, cross(r1, n) * lambda ]
-//
-// Friction constraint:
-// C = dot(p2 - p1, t)
-// Cdot = dot(v2 - v1, t)
-// J = [ -t, -cross(r1, t), t, cross(r2, t) ]
-//
-// impulse = JT * lambda = [ -t * lambda, -cross(r1, t) * lambda, t * lambda, cross(r1, t) * lambda ]
-//
-// NOTE: lambda is an impulse in constraint space.
-//-------------------------------------------------------------------------------------------------
-
-function ContactSolver(shape1, shape2) {
- // Contact shapes
- this.shape1 = shape1;
- this.shape2 = shape2;
-
- // Contact list
- this.contactArr = [];
-
- // Coefficient of restitution (elasticity)
- this.e = 1;
-
- // Frictional coefficient
- this.u = 1;
-}
-
-ContactSolver.COLLISION_SLOP = 0.0008;
-ContactSolver.BAUMGARTE = 0.28;
-ContactSolver.MAX_LINEAR_CORRECTION = 1;//Infinity;
-
-ContactSolver.prototype.update = function(newContactArr) {
- for (var i = 0; i < newContactArr.length; i++) {
- var newContact = newContactArr[i];
- var k = -1;
- for (var j = 0; j < this.contactArr.length; j++) {
- if (newContact.hash == this.contactArr[j].hash) {
- k = j;
- break;
- }
- }
-
- if (k > -1) {
- newContact.lambda_n_acc = this.contactArr[k].lambda_n_acc;
- newContact.lambda_t_acc = this.contactArr[k].lambda_t_acc;
- }
- }
-
- this.contactArr = newContactArr;
-}
-
-ContactSolver.prototype.initSolver = function(dt_inv) {
- var body1 = this.shape1.body;
- var body2 = this.shape2.body;
-
- var sum_m_inv = body1.m_inv + body2.m_inv;
-
- for (var i = 0; i < this.contactArr.length; i++) {
- var con = this.contactArr[i];
-
- // Transformed r1, r2
- con.r1 = vec2.sub(con.p, body1.p);
- con.r2 = vec2.sub(con.p, body2.p);
-
- // Local r1, r2
- con.r1_local = body1.xf.unrotate(con.r1);
- con.r2_local = body2.xf.unrotate(con.r2);
-
- var n = con.n;
- var t = vec2.perp(con.n);
-
- // invEMn = J * invM * JT
- // J = [ -n, -cross(r1, n), n, cross(r2, n) ]
- var sn1 = vec2.cross(con.r1, n);
- var sn2 = vec2.cross(con.r2, n);
- var emn_inv = sum_m_inv + body1.i_inv * sn1 * sn1 + body2.i_inv * sn2 * sn2;
- con.emn = emn_inv == 0 ? 0 : 1 / emn_inv;
-
- // invEMt = J * invM * JT
- // J = [ -t, -cross(r1, t), t, cross(r2, t) ]
- var st1 = vec2.cross(con.r1, t);
- var st2 = vec2.cross(con.r2, t);
- var emt_inv = sum_m_inv + body1.i_inv * st1 * st1 + body2.i_inv * st2 * st2;
- con.emt = emt_inv == 0 ? 0 : 1 / emt_inv;
-
- // Linear velocities at contact point
- // in 2D: cross(w, r) = perp(r) * w
- var v1 = vec2.mad(body1.v, vec2.perp(con.r1), body1.w);
- var v2 = vec2.mad(body2.v, vec2.perp(con.r2), body2.w);
-
- // relative velocity at contact point
- var rv = vec2.sub(v2, v1);
-
- // bounce velocity dot n
- con.bounce = vec2.dot(rv, con.n) * this.e;
- }
-}
-
-ContactSolver.prototype.warmStart = function() {
-
- var body1 = this.shape1.body;
- var body2 = this.shape2.body;
-
- for (var i = 0; i < this.contactArr.length; i++) {
- var con = this.contactArr[i];
- var n = con.n;
- var lambda_n = con.lambda_n_acc;
- var lambda_t = con.lambda_t_acc;
-
- // Apply accumulated impulses
- //var impulse = vec2.rotate_vec(new vec2(lambda_n, lambda_t), n);
- var impulse = new vec2(lambda_n * n.x - lambda_t * n.y, lambda_t * n.x + lambda_n * n.y);
-
- body1.v.mad(impulse, -body1.m_inv);
- body1.w -= vec2.cross(con.r1, impulse) * body1.i_inv;
-
- body2.v.mad(impulse, body2.m_inv);
- body2.w += vec2.cross(con.r2, impulse) * body2.i_inv;
- }
-}
-
-ContactSolver.prototype.solveVelocityConstraints = function() {
-
- var body1 = this.shape1.body;
- var body2 = this.shape2.body;
-
- var m1_inv = body1.m_inv;
- var i1_inv = body1.i_inv;
- var m2_inv = body2.m_inv;
- var i2_inv = body2.i_inv;
-
- for (var i = 0; i < this.contactArr.length; i++) {
-
- var con = this.contactArr[i];
- var n = con.n;
- var t = vec2.perp(n);
- var r1 = con.r1;
- var r2 = con.r2;
-
- // Linear velocities at contact point
- // in 2D: cross(w, r) = perp(r) * w
- var v1 = vec2.mad(body1.v, vec2.perp(r1), body1.w);
- var v2 = vec2.mad(body2.v, vec2.perp(r2), body2.w);
-
- // Relative velocity at contact point
- var rv = vec2.sub(v2, v1);
-
- // Compute normal constraint impulse + adding bounce as a velocity bias
- // lambda_n = -EMn * J * V
- var lambda_n = -con.emn * (vec2.dot(n, rv) + con.bounce);
-
- // Accumulate and clamp
- var lambda_n_old = con.lambda_n_acc;
- con.lambda_n_acc = Math.max(lambda_n_old + lambda_n, 0);
- lambda_n = con.lambda_n_acc - lambda_n_old;
-
- // Compute frictional constraint impulse
- // lambda_t = -EMt * J * V
- var lambda_t = -con.emt * vec2.dot(t, rv);
-
- // Max friction constraint impulse (Coulomb's Law)
- var lambda_t_max = con.lambda_n_acc * this.u;
-
- // Accumulate and clamp
- var lambda_t_old = con.lambda_t_acc;
- con.lambda_t_acc = Math.clamp(lambda_t_old + lambda_t, -lambda_t_max, lambda_t_max);
- lambda_t = con.lambda_t_acc - lambda_t_old;
-
- // Apply the final impulses
- //var impulse = vec2.rotate_vec(new vec2(lambda_n, lambda_t), n);
- var impulse = new vec2(lambda_n * n.x - lambda_t * n.y, lambda_t * n.x + lambda_n * n.y);
-
- body1.v.mad(impulse, -m1_inv);
- body1.w -= vec2.cross(r1, impulse) * i1_inv;
-
- body2.v.mad(impulse, m2_inv);
- body2.w += vec2.cross(r2, impulse) * i2_inv;
-
- }
-}
-
-ContactSolver.prototype.solvePositionConstraints = function() {
-
- var body1 = this.shape1.body;
- var body2 = this.shape2.body;
-
- var m1_inv = body1.m_inv;
- var i1_inv = body1.i_inv;
- var m2_inv = body2.m_inv;
- var i2_inv = body2.i_inv;
- var sum_m_inv = m1_inv + m2_inv;
-
- var max_penetration = 0;
-
- for (var i = 0; i < this.contactArr.length; i++) {
-
- var con = this.contactArr[i];
- var n = con.n;
-
- // Transformed r1, r2
- var r1 = vec2.rotate(con.r1_local, body1.a);
- var r2 = vec2.rotate(con.r2_local, body2.a);
-
- // Contact points (corrected)
- var p1 = vec2.add(body1.p, r1);
- var p2 = vec2.add(body2.p, r2);
-
- // Corrected delta vector
- var dp = vec2.sub(p2, p1);
-
- // Position constraint
- var c = vec2.dot(dp, n) + con.d;
- var correction = Math.clamp(ContactSolver.BAUMGARTE * (c + ContactSolver.COLLISION_SLOP), -ContactSolver.MAX_LINEAR_CORRECTION, 0);
-
- if (correction == 0) {
- continue;
- }
-
- // We don't need max_penetration less than or equal slop
- max_penetration = Math.max(max_penetration, -c);
-
- // Compute lambda for position constraint
- // Solve (J * invM * JT) * lambda = -C / dt
- var sn1 = vec2.cross(r1, n);
- var sn2 = vec2.cross(r2, n);
- var em_inv = sum_m_inv + body1.i_inv * sn1 * sn1 + body2.i_inv * sn2 * sn2;
- var lambda_dt = em_inv == 0 ? 0 : -correction / em_inv;
-
- // Apply correction impulses
- var impulse_dt = vec2.scale(n, lambda_dt);
-
- body1.p.mad(impulse_dt, -m1_inv);
- body1.a -= sn1 * lambda_dt * i1_inv;
-
- body2.p.mad(impulse_dt, m2_inv);
- body2.a += sn2 * lambda_dt * i2_inv;
- }
-
- return max_penetration <= ContactSolver.COLLISION_SLOP * 3;
-}
diff --git a/src/physics/advanced/Joint.js b/src/physics/advanced/Joint.js
deleted file mode 100644
index 1dc01968..00000000
--- a/src/physics/advanced/Joint.js
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
-* Copyright (c) 2012 Ju Hyung Lee
-*
-* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
-* and associated documentation files (the "Software"), to deal in the Software without
-* restriction, including without limitation the rights to use, copy, modify, merge, publish,
-* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
-* Software is furnished to do so, subject to the following conditions:
-*
-* The above copyright notice and this permission notice shall be included in all copies or
-* substantial portions of the Software.
-*
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
-* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-Joint = function(type, body1, body2, collideConnected) {
- if (arguments.length == 0)
- return;
-
- if (Joint.id_counter == undefined)
- Joint.id_counter = 0;
-
- this.id = Joint.id_counter++;
- this.type = type;
-
- this.body1 = body1;
- this.body2 = body2;
-
- // Allow collision between to cennected body
- this.collideConnected = collideConnected;
-
- // Constraint force limit
- this.maxForce = 9999999999;
-
- // Is breakable ?
- this.breakable = false;
-}
-
-Joint.TYPE_ANGLE = 0;
-Joint.TYPE_REVOLUTE = 1;
-Joint.TYPE_WELD = 2;
-Joint.TYPE_WHEEL = 3;
-Joint.TYPE_PRISMATIC = 4;
-Joint.TYPE_DISTANCE = 5;
-Joint.TYPE_ROPE = 6;
-Joint.TYPE_MOUSE = 7;
-
-Joint.LINEAR_SLOP = 0.0008;
-Joint.ANGULAR_SLOP = deg2rad(2);
-Joint.MAX_LINEAR_CORRECTION = 0.5;
-Joint.MAX_ANGULAR_CORRECTION = deg2rad(8);
-
-Joint.LIMIT_STATE_INACTIVE = 0;
-Joint.LIMIT_STATE_AT_LOWER = 1;
-Joint.LIMIT_STATE_AT_UPPER = 2;
-Joint.LIMIT_STATE_EQUAL_LIMITS = 3;
-
-Joint.prototype.getWorldAnchor1 = function() {
- return this.body1.getWorldPoint(this.anchor1);
-}
-
-Joint.prototype.getWorldAnchor2 = function() {
- return this.body2.getWorldPoint(this.anchor2);
-}
-
-Joint.prototype.setWorldAnchor1 = function(anchor1) {
- this.anchor1 = this.body1.getLocalPoint(anchor1);
-}
-
-Joint.prototype.setWorldAnchor2 = function(anchor2) {
- this.anchor2 = this.body2.getLocalPoint(anchor2);
-}
\ No newline at end of file
diff --git a/src/physics/advanced/Math.js b/src/physics/advanced/Math.js
deleted file mode 100644
index b144f8b0..00000000
--- a/src/physics/advanced/Math.js
+++ /dev/null
@@ -1,817 +0,0 @@
-/*
-* Copyright (c) 2012 Ju Hyung Lee
-*
-* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
-* and associated documentation files (the "Software"), to deal in the Software without
-* restriction, including without limitation the rights to use, copy, modify, merge, publish,
-* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
-* Software is furnished to do so, subject to the following conditions:
-*
-* The above copyright notice and this permission notice shall be included in all copies or
-* substantial portions of the Software.
-*
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
-* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-Math.clamp = function(v, min, max) { return v < min ? min : (v > max ? max : v); }
-Math.log2 = function(a) { return Math.log(a) / Math.log(2); }
-
-function deg2rad(deg) { return (deg / 180) * Math.PI; }
-function rad2deg(rad) { return (rad / Math.PI) * 180; }
-
-function pixel2meter(px) { return px * 0.02; }
-function meter2pixel(mt) { return mt * 50; }
-
-//-----------------------------------
-// 2D Vector
-//-----------------------------------
-
-function vec2(x, y) {
- this.x = x || 0;
- this.y = y || 0;
-}
-
-vec2.zero = new vec2(0, 0);
-
-vec2.prototype.toString = function() {
- //return ["x:", this.x, "y:", this.y].join(" ");
- return "x=" + this.x + " y=" + this.y;
-}
-
-vec2.prototype.set = function(x, y) {
- this.x = x;
- this.y = y;
-
- return this;
-}
-
-vec2.prototype.copy = function(v) {
- this.x = v.x;
- this.y = v.y;
-
- return this;
-}
-
-vec2.prototype.duplicate = function() {
- return new vec2(this.x, this.y);
-}
-
-vec2.prototype.equal = function(v) {
- return (this.x != v.x || this.y != v.y) ? false : true;
-}
-
-vec2.prototype.add = function(v1, v2) {
- this.x = v1.x + v2.x;
- this.y = v1.y + v2.y;
-
- return this;
-}
-
-vec2.prototype.addself = function(v) {
- this.x += v.x;
- this.y += v.y;
-
- return this;
-}
-
-vec2.prototype.sub = function(v1, v2) {
- this.x = v1.x - v2.x;
- this.y = v1.y - v2.y;
-
- return this;
-}
-
-vec2.prototype.subself = function(v) {
- this.x -= v.x;
- this.y -= v.y;
-
- return this;
-}
-
-vec2.prototype.scale = function(s) {
- this.x *= s;
- this.y *= s;
-
- return this;
-}
-
-vec2.prototype.scale2 = function(s) {
- this.x *= s.x;
- this.y *= s.y;
-
- return this;
-}
-
-vec2.prototype.mad = function(v, s) {
- this.x += v.x * s;
- this.y += v.y * s;
-}
-
-vec2.prototype.neg = function() {
- this.x *= -1;
- this.y *= -1;
-
- return this;
-}
-
-vec2.prototype.rcp = function() {
- this.x = 1 / this.x;
- this.y = 1 / this.y;
-
- return this;
-}
-
-vec2.prototype.lengthsq = function() {
- return this.x * this.x + this.y * this.y;
-}
-
-vec2.prototype.length = function() {
- return Math.sqrt(this.x * this.x + this.y * this.y);
-}
-
-vec2.prototype.normalize = function() {
- var inv = (this.x != 0 || this.y != 0) ? 1 / Math.sqrt(this.x * this.x + this.y * this.y) : 0;
- this.x *= inv;
- this.y *= inv;
-
- return this;
-}
-
-vec2.prototype.dot = function(v) {
- return this.x * v.x + this.y * v.y;
-}
-
-// Z-component of 3d cross product (ax, ay, 0) x (bx, by, 0)
-vec2.prototype.cross = function(v) {
- return this.x * v.y - this.y * v.x;
-}
-
-vec2.prototype.toAngle = function() {
- return Math.atan2(this.y, this.x);
-}
-
-vec2.prototype.rotation = function(angle) {
- this.x = Math.cos(angle);
- this.y = Math.sin(angle);
- return this;
-}
-
-vec2.prototype.rotate = function(angle) {
- var c = Math.cos(angle);
- var s = Math.sin(angle);
- return this.set(this.x * c - this.y * s, this.x * s + this.y * c);
-}
-
-vec2.prototype.lerp = function(v1, v2, t) {
- return this.add(vec2.scale(v1, 1 - t), vec2.scale(v2, t));
-}
-
-vec2.add = function(v1, v2) {
- return new vec2(v1.x + v2.x, v1.y + v2.y);
-}
-
-vec2.sub = function(v1, v2) {
- return new vec2(v1.x - v2.x, v1.y - v2.y);
-}
-
-vec2.scale = function(v, s) {
- return new vec2(v.x * s, v.y * s);
-}
-
-vec2.scale2 = function(v, s) {
- return new vec2(v.x * s.x, v.y * s.y);
-}
-
-vec2.mad = function(v1, v2, s) {
- return new vec2(v1.x + v2.x * s, v1.y + v2.y * s);
-}
-
-vec2.neg = function(v) {
- return new vec2(-v.x, -v.y);
-}
-
-vec2.rcp = function(v) {
- return new vec2(1 / v.x, 1 / v.y);
-}
-
-vec2.normalize = function(v) {
- var inv = (v.x != 0 || v.y != 0) ? 1 / Math.sqrt(v.x * v.x + v.y * v.y) : 0;
- return new vec2(v.x * inv, v.y * inv);
-}
-
-vec2.dot = function(v1, v2) {
- return v1.x * v2.x + v1.y * v2.y;
-}
-
-vec2.cross = function(v1, v2) {
- return v1.x * v2.y - v1.y * v2.x;
-}
-
-vec2.toAngle = function(v) {
- return Math.atan2(v.y, v.x);
-}
-
-vec2.rotation = function(angle) {
- return new vec2(Math.cos(angle), Math.sin(angle));
-}
-
-vec2.rotate = function(v, angle) {
- var c = Math.cos(angle);
- var s = Math.sin(angle);
- return new vec2(v.x * c - v.y * s, v.x * s + v.y * c);
-}
-
-// Return perpendicular vector (90 degree rotation)
-vec2.perp = function(v) {
- return new vec2(-v.y, v.x);
-}
-
-// Return perpendicular vector (-90 degree rotation)
-vec2.rperp = function(v) {
- return new vec2(v.y, -v.x);
-}
-
-vec2.dist = function(v1, v2) {
- var dx = v2.x - v1.x;
- var dy = v2.y - v1.y;
- return Math.sqrt(dx * dx + dy * dy);
-}
-
-vec2.distsq = function(v1, v2) {
- var dx = v2.x - v1.x;
- var dy = v2.y - v1.y;
- return dx * dx + dy * dy;
-}
-
-vec2.lerp = function(v1, v2, t) {
- return vec2.add(vec2.scale(v1, 1 - t), vec2.scale(v2, t));
-}
-
-vec2.truncate = function(v, length) {
- var ret = v.duplicate();
- var length_sq = v.x * v.x + v.y * v.y;
- if (length_sq > length * length) {
- ret.scale(length / Math.sqrt(length_sq));
- }
-
- return ret;
-}
-
-//-----------------------------------
-// 3D Vector
-//-----------------------------------
-
-function vec3(x, y, z) {
- this.x = x || 0;
- this.y = y || 0;
- this.z = z || 0;
-}
-
-vec3.zero = new vec3(0, 0, 0);
-
-vec3.prototype.toString = function() {
- return ["x:", this.x, "y:", this.y, "z:", this.z].join(" ");
-}
-
-vec3.prototype.set = function(x, y, z) {
- this.x = x;
- this.y = y;
- this.z = z;
-
- return this;
-}
-
-vec3.prototype.copy = function(v) {
- this.x = v.x;
- this.y = v.y;
- this.z = v.z;
-
- return this;
-}
-
-vec3.prototype.duplicate = function() {
- return new vec3(this.x, this.y, this.z);
-}
-
-vec3.prototype.equal = function(v) {
- return this.x != v.x || this.y != v.y || this.z != v.z ? false : true;
-}
-
-vec3.prototype.add = function(v1, v2) {
- this.x = v1.x + v2.x;
- this.y = v1.y + v2.y;
- this.z = v1.z + v2.z;
-
- return this;
-}
-
-vec3.prototype.addself = function(v) {
- this.x += v.x;
- this.y += v.y;
- this.z += v.z;
-
- return this;
-}
-
-vec3.prototype.sub = function(v1, v2) {
- this.x = v1.x - v2.x;
- this.y = v1.y - v2.y;
- this.z = v1.z - v2.z;
-
- return this;
-}
-
-vec3.prototype.subself = function(v) {
- this.x -= v.x;
- this.y -= v.y;
- this.z -= v.z;
-
- return this;
-}
-
-vec3.prototype.scale = function(s) {
- this.x *= s;
- this.y *= s;
- this.z *= s;
-
- return this;
-}
-
-vec3.prototype.mad = function(v, s) {
- this.x += v.x * s;
- this.y += v.y * s;
- this.z += v.z * s;
-}
-
-vec3.prototype.neg = function() {
- this.x *= -1;
- this.y *= -1;
- this.z *= -1;
-
- return this;
-}
-
-vec3.prototype.rcp = function() {
- this.x = 1 / this.x;
- this.y = 1 / this.y;
- this.z = 1 / this.z;
-
- return this;
-}
-
-vec3.prototype.lengthsq = function() {
- return this.x * this.x + this.y * this.y + this.z * this.z;
-}
-
-vec3.prototype.length = function() {
- return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
-}
-
-vec3.prototype.normalize = function() {
- var inv = (this.x != 0 || this.y != 0 || this.z != 0) ? 1 / Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z) : 0;
- this.x *= inv;
- this.y *= inv;
- this.z *= inv;
-
- return this;
-}
-
-vec3.prototype.dot = function(v) {
- return this.x * v.x + this.y * v.y + this.z * v.z;
-}
-
-vec3.prototype.toVec2 = function() {
- return new vec2(this.x, this.y);
-}
-
-vec3.fromVec2 = function(v, z) {
- return new vec3(v.x, v.y, z);
-}
-
-vec3.truncate = function(v, length) {
- var ret = v.duplicate();
- var length_sq = v.x * v.x + v.y * v.y + v.z * v.z;
- if (length_sq > length * length) {
- ret.scale(length / Math.sqrt(length_sq));
- }
-
- return ret;
-}
-
-//-----------------------------------
-// 2x2 Matrix (row major)
-//-----------------------------------
-
-function mat2(_11, _12, _21, _22) {
- this._11 = _11 || 0;
- this._12 = _12 || 0;
- this._21 = _21 || 0;
- this._22 = _22 || 0;
-}
-
-mat2.zero = new mat2(0, 0, 0, 0);
-
-mat2.prototype.toString = function() {
- return ["[", this._11, this._12, this_21, this._22, "]"].join(" ");
-}
-
-mat2.prototype.set = function(_11, _12, _21, _22) {
- this._11 = _11;
- this._12 = _12;
- this._21 = _21;
- this._22 = _22;
-
- return this;
-}
-
-mat2.prototype.copy = function(m) {
- this._11 = m._11;
- this._12 = m._12;
- this._21 = m._21;
- this._22 = m._22;
-
- return this;
-}
-
-mat2.prototype.duplicate = function() {
- return new mat2(this._11, this._12, this._21, this._22);
-}
-
-mat2.prototype.scale = function(s) {
- this._11 *= s;
- this._12 *= s;
- this._21 *= s;
- this._22 *= s;
-
- return this;
-}
-
-mat2.prototype.mul = function(m) {
- return this.set(
- this._11 * m2._11 + this._12 * m2._21,
- this._11 * m2._12 + this._12 * m2._22,
- this._21 * m2._11 + this._22 * m2._21,
- this._21 * m2._12 + this._22 * m2._22);
-}
-
-mat2.prototype.mulvec = function(v) {
- return new vec2(
- this._11 * v.x + this._12 * v.y,
- this._21 * v.x + this._22 * v.y);
-}
-
-mat2.prototype.invert = function() {
- var det = this._11 * this._22 - this._12 * this._21;
- if (det != 0)
- det = 1 / det;
-
- return this.set(
- this._22 * det, -this._12 * det,
- -this._21 * det, this._11 * det);
-}
-
-// Solve A * x = b
-mat2.prototype.solve = function(b) {
- var det = this._11 * this._22 - this._12 * this._21;
- if (det != 0)
- det = 1 / det;
-
- return new vec2(
- det * (this._22 * b.x - this._12 * b.y),
- det * (this._11 * b.y - this._21 * b.x));
-}
-
-mat2.mul = function(m1, m2) {
- return new mat2(
- m1._11 * m2._11 + m1._12 * m2._21,
- m1._11 * m2._12 + m1._12 * m2._22,
- m1._21 * m2._11 + m1._22 * m2._21,
- m1._21 * m2._12 + m1._22 * m2._22);
-}
-
-//-----------------------------------
-// 3x3 Matrix (row major)
-//-----------------------------------
-
-function mat3(_11, _12, _13, _21, _22, _23, _31, _32, _33) {
- this._11 = _11 || 0;
- this._12 = _12 || 0;
- this._13 = _13 || 0;
- this._21 = _21 || 0;
- this._22 = _22 || 0;
- this._23 = _23 || 0;
- this._31 = _31 || 0;
- this._32 = _32 || 0;
- this._33 = _33 || 0;
-}
-
-mat3.zero = new mat3(0, 0, 0, 0, 0, 0, 0, 0, 0);
-
-mat3.prototype.toString = function() {
- return ["[", this._11, this._12, this._13, this_21, this._22, this._23, this._31, this._32, this._33, "]"].join(" ");
-}
-
-mat3.prototype.set = function(_11, _12, _13, _21, _22, _23, _31, _32, _33) {
- this._11 = _11;
- this._12 = _12;
- this._13 = _13;
- this._21 = _21;
- this._22 = _22;
- this._23 = _23;
- this._31 = _31;
- this._32 = _32;
- this._33 = _33;
-
- return this;
-}
-
-mat3.prototype.copy = function(m) {
- this._11 = m._11;
- this._12 = m._12;
- this._13 = m._13;
- this._21 = m._21;
- this._22 = m._22;
- this._23 = m._23;
- this._31 = m._31;
- this._32 = m._32;
- this._33 = m._33;
-
- return this;
-}
-
-mat3.prototype.duplicate = function() {
- return new mat3(this._11, this._12, this._13, this._21, this._22, this._23, this._31, this._32, this._33);
-}
-
-mat3.prototype.scale = function(s) {
- this._11 *= s;
- this._12 *= s;
- this._13 *= s;
- this._21 *= s;
- this._22 *= s;
- this._23 *= s;
- this._31 *= s;
- this._32 *= s;
- this._33 *= s;
-
- return this;
-}
-
-mat3.prototype.mul = function(m) {
- return this.set(
- this._11 * m2._11 + this._12 * m2._21 + this._13 * m2._31,
- this._11 * m2._12 + this._12 * m2._22 + this._13 * m2._32,
- this._11 * m2._13 + this._12 * m2._23 + this._13 * m2._33,
- this._21 * m2._11 + this._22 * m2._21 + this._23 * m2._31,
- this._21 * m2._12 + this._22 * m2._22 + this._23 * m2._32,
- this._21 * m2._13 + this._22 * m2._23 + this._23 * m2._33,
- this._31 * m2._11 + this._32 * m2._21 + this._33 * m2._31,
- this._31 * m2._12 + this._32 * m2._22 + this._33 * m2._32,
- this._31 * m2._13 + this._32 * m2._23 + this._33 * m2._33);
-}
-
-mat3.prototype.mulvec = function(v) {
- return new vec2(
- this._11 * v.x + this._12 * v.y + this._13 * v.z,
- this._21 * v.x + this._22 * v.y + this._23 * v.z,
- this._31 * v.x + this._32 * v.y + this._33 * v.z);
-}
-
-mat3.prototype.invert = function() {
- var det2_11 = this._22 * this._33 - this._23 * this._32;
- var det2_12 = this._23 * this._31 - this._21 * this._33;
- var det2_13 = this._21 * this._32 - this._22 * this._31;
-
- var det = this._11 * det2_11 + this._12 * det2_12 + this._13 * det2_13;
- if (det != 0)
- det = 1 / det;
-
- var det2_21 = this._13 * this._32 - this._12 * this._33;
- var det2_22 = this._11 * this._33 - this._13 * this._31;
- var det2_23 = this._12 * this._31 - this._11 * this._32;
- var det2_31 = this._12 * this._23 - this._13 * this._22;
- var det2_32 = this._13 * this._21 - this._11 * this._23;
- var det2_33 = this._11 * this._22 - this._12 * this._21;
-
- return this.set(
- det2_11 * det, det2_12 * det, det2_13 * det,
- det2_21 * det, det2_22 * det, det2_23 * det,
- det2_31 * det, det2_32 * det, det2_33 * det);
-}
-
-// Solve A(2x2) * x = b
-mat3.prototype.solve2x2 = function(b) {
- var det = this._11 * this._22 - this._12 * this._21;
- if (det != 0)
- det = 1 / det;
-
- return new vec2(
- det * (this._22 * b.x - this._12 * b.y),
- det * (this._11 * b.y - this._21 * b.x));
-}
-
-// Solve A(3x3) * x = b
-mat3.prototype.solve = function(b) {
- var det2_11 = this._22 * this._33 - this._23 * this._32;
- var det2_12 = this._23 * this._31 - this._21 * this._33;
- var det2_13 = this._21 * this._32 - this._22 * this._31;
-
- var det = this._11 * det2_11 + this._12 * det2_12 + this._13 * det2_13;
- if (det != 0)
- det = 1 / det;
-
- var det2_21 = this._13 * this._32 - this._12 * this._33;
- var det2_22 = this._11 * this._33 - this._13 * this._31;
- var det2_23 = this._12 * this._31 - this._11 * this._32;
- var det2_31 = this._12 * this._23 - this._13 * this._22;
- var det2_32 = this._13 * this._21 - this._11 * this._23;
- var det2_33 = this._11 * this._22 - this._12 * this._21;
-
- return new vec3(
- det * (det2_11 * b.x + det2_12 * b.y + det2_13 * b.z),
- det * (det2_21 * b.x + det2_22 * b.y + det2_23 * b.z),
- det * (det2_31 * b.x + det2_32 * b.y + det2_33 * b.z));
-}
-
-mat3.mul = function(m1, m2) {
- return new mat3(
- m1._11 * m2._11 + m1._12 * m2._21 + m1._13 * m2._31,
- m1._11 * m2._12 + m1._12 * m2._22 + m1._13 * m2._32,
- m1._11 * m2._13 + m1._12 * m2._23 + m1._13 * m2._33,
- m1._21 * m2._11 + m1._22 * m2._21 + m1._23 * m2._31,
- m1._21 * m2._12 + m1._22 * m2._22 + m1._23 * m2._32,
- m1._21 * m2._13 + m1._22 * m2._23 + m1._23 * m2._33,
- m1._31 * m2._11 + m1._32 * m2._21 + m1._33 * m2._31,
- m1._31 * m2._12 + m1._32 * m2._22 + m1._33 * m2._32,
- m1._31 * m2._13 + m1._32 * m2._23 + m1._33 * m2._33);
-}
-
-//-----------------------------------
-// 2D Transform
-//-----------------------------------
-
-Transform = function(pos, angle) {
- this.t = pos.duplicate();
- this.c = Math.cos(angle);
- this.s = Math.sin(angle);
- this.a = angle;
-}
-
-Transform.prototype.toString = function() {
- return 't=' + this.t.toString() + ' c=' + this.c + ' s=' + this.s + ' a=' + this.a;
-}
-
-Transform.prototype.set = function(pos, angle) {
- this.t.copy(pos);
- this.c = Math.cos(angle);
- this.s = Math.sin(angle);
- this.a = angle;
- return this;
-}
-
-Transform.prototype.setRotation = function(angle) {
- this.c = Math.cos(angle);
- this.s = Math.sin(angle);
- this.a = angle;
- return this;
-}
-
-Transform.prototype.setPosition = function(p) {
- this.t.copy(p);
- return this;
-}
-
-Transform.prototype.identity = function() {
- this.t.set(0, 0);
- this.c = 1;
- this.s = 0;
- this.a = 0;
- return this;
-}
-
-Transform.prototype.rotate = function(v) {
- return new vec2(v.x * this.c - v.y * this.s, v.x * this.s + v.y * this.c);
-}
-
-Transform.prototype.unrotate = function(v) {
- return new vec2(v.x * this.c + v.y * this.s, -v.x * this.s + v.y * this.c);
-}
-
-Transform.prototype.transform = function(v) {
- return new vec2(v.x * this.c - v.y * this.s + this.t.x, v.x * this.s + v.y * this.c + this.t.y);
-}
-
-Transform.prototype.untransform = function(v) {
- var px = v.x - this.t.x;
- var py = v.y - this.t.y;
- return new vec2(px * this.c + py * this.s, -px * this.s + py * this.c);
-}
-
-//-----------------------------------
-// 2D AABB
-//-----------------------------------
-
-Bounds = function(mins, maxs) {
- this.mins = mins ? new vec2(mins.x, mins.y) : new vec2(999999, 999999);
- this.maxs = maxs ? new vec2(maxs.x, maxs.y) : new vec2(-999999, -999999);
-}
-
-Bounds.prototype.toString = function() {
- return ["mins:", this.mins.toString(), "maxs:", this.maxs.toString()].join(" ");
-}
-
-Bounds.prototype.set = function(mins, maxs) {
- this.mins.set(mins.x, mins.y);
- this.maxs.set(maxs.x, maxs.y);
-}
-
-Bounds.prototype.copy = function(b) {
- this.mins.copy(b.mins);
- this.maxs.copy(b.maxs);
- return this;
-}
-
-Bounds.prototype.clear = function() {
- this.mins.set(999999, 999999);
- this.maxs.set(-999999, -999999);
- return this;
-}
-
-Bounds.prototype.isEmpty = function() {
- if (this.mins.x > this.maxs.x || this.mins.y > this.maxs.y)
- return true;
-}
-
-Bounds.prototype.getCenter = function() {
- return vec2.scale(vec2.add(this.mins, this.maxs), 0.5);
-}
-
-Bounds.prototype.getExtent = function() {
- return vec2.scale(vec2.sub(this.maxs, this.mins), 0.5);
-}
-
-Bounds.prototype.getPerimeter = function() {
- return (maxs.x - mins.x + maxs.y - mins.y) * 2;
-}
-
-Bounds.prototype.addPoint = function(p) {
- if (this.mins.x > p.x) this.mins.x = p.x;
- if (this.maxs.x < p.x) this.maxs.x = p.x;
- if (this.mins.y > p.y) this.mins.y = p.y;
- if (this.maxs.y < p.y) this.maxs.y = p.y;
- return this;
-}
-
-Bounds.prototype.addBounds = function(b) {
- if (this.mins.x > b.mins.x) this.mins.x = b.mins.x;
- if (this.maxs.x < b.maxs.x) this.maxs.x = b.maxs.x;
- if (this.mins.y > b.mins.y) this.mins.y = b.mins.y;
- if (this.maxs.y < b.maxs.y) this.maxs.y = b.maxs.y;
- return this;
-}
-
-Bounds.prototype.addBounds2 = function(mins, maxs) {
- if (this.mins.x > mins.x) this.mins.x = mins.x;
- if (this.maxs.x < maxs.x) this.maxs.x = maxs.x;
- if (this.mins.y > mins.y) this.mins.y = mins.y;
- if (this.maxs.y < maxs.y) this.maxs.y = maxs.y;
- return this;
-}
-
-Bounds.prototype.addExtents = function(center, extent_x, extent_y) {
- if (this.mins.x > center.x - extent_x) this.mins.x = center.x - extent_x;
- if (this.maxs.x < center.x + extent_x) this.maxs.x = center.x + extent_x;
- if (this.mins.y > center.y - extent_y) this.mins.y = center.y - extent_y;
- if (this.maxs.y < center.y + extent_y) this.maxs.y = center.y + extent_y;
- return this;
-}
-
-Bounds.prototype.expand = function(ax, ay) {
- this.mins.x -= ax;
- this.mins.y -= ay;
- this.maxs.x += ax;
- this.maxs.y += ay;
- return this;
-}
-
-Bounds.prototype.containPoint = function(p) {
- if (p.x < this.mins.x || p.x > this.maxs.x || p.y < this.mins.y || p.y > this.maxs.y)
- return false;
- return true;
-}
-
-Bounds.prototype.intersectsBounds = function(b) {
- if (this.mins.x > b.maxs.x || this.maxs.x < b.mins.x || this.mins.y > b.maxs.y || this.maxs.y < b.mins.y)
- return false;
- return true;
-}
-
-Bounds.expand = function(b, ax, ay) {
- var b = new Bounds(b.mins, b.maxs);
- b.expand(ax, ay);
- return b;
-}
\ No newline at end of file
diff --git a/src/physics/advanced/Shape.js b/src/physics/advanced/Shape.js
deleted file mode 100644
index 0e31f21f..00000000
--- a/src/physics/advanced/Shape.js
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
-* Copyright (c) 2012 Ju Hyung Lee
-*
-* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
-* and associated documentation files (the "Software"), to deal in the Software without
-* restriction, including without limitation the rights to use, copy, modify, merge, publish,
-* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
-* Software is furnished to do so, subject to the following conditions:
-*
-* The above copyright notice and this permission notice shall be included in all copies or
-* substantial portions of the Software.
-*
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
-* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-Shape = function(type) {
- if (arguments.length == 0)
- return;
-
- if (Shape.id_counter == undefined)
- Shape.id_counter = 0;
-
- this.id = Shape.id_counter++;
- this.type = type;
-
- // Coefficient of restitution (elasticity)
- this.e = 0.0;
-
- // Frictional coefficient
- this.u = 1.0;
-
- // Mass density
- this.density = 1;
-
- // Axis-aligned bounding box
- this.bounds = new Bounds;
-}
-
-Shape.TYPE_CIRCLE = 0;
-Shape.TYPE_SEGMENT = 1;
-Shape.TYPE_POLY = 2;
-Shape.NUM_TYPES = 3;
-
diff --git a/src/physics/advanced/Space.js b/src/physics/advanced/Space.js
deleted file mode 100644
index e869af2e..00000000
--- a/src/physics/advanced/Space.js
+++ /dev/null
@@ -1,797 +0,0 @@
-/*
-* Copyright (c) 2012 Ju Hyung Lee
-*
-* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
-* and associated documentation files (the "Software"), to deal in the Software without
-* restriction, including without limitation the rights to use, copy, modify, merge, publish,
-* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
-* Software is furnished to do so, subject to the following conditions:
-*
-* The above copyright notice and this permission notice shall be included in all copies or
-* substantial portions of the Software.
-*
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
-* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-function Space() {
- this.bodyArr = [];
- this.bodyHash = {};
-
- this.jointArr = [];
- this.jointHash = {};
-
- this.numContacts = 0;
- this.contactSolverArr = [];
-
- this.postSolve = function(arb) {};
-
- this.gravity = new vec2(0, 0);
- this.damping = 0;
-
- this.log = [];
-}
-
-Space.TIME_TO_SLEEP = 0.5;
-Space.SLEEP_LINEAR_TOLERANCE = 0.5;
-Space.SLEEP_ANGULAR_TOLERANCE = deg2rad(2);
-
-Space.prototype.clear = function() {
- Shape.id_counter = 0;
- Body.id_counter = 0;
- Joint.id_counter = 0;
-
- for (var i = 0; i < this.bodyArr.length; i++) {
- if (this.bodyArr[i]) {
- this.removeBody(this.bodyArr[i]);
- }
- }
-
- this.bodyArr = [];
- this.bodyHash = {};
-
- this.jointArr = [];
- this.jointHash = {};
-
- this.contactSolverArr = [];
-
- this.stepCount = 0;
-}
-
-Space.prototype.toJSON = function(key) {
- var o_bodies = [];
- for (var i = 0; i < this.bodyArr.length; i++) {
- if (this.bodyArr[i]) {
- o_bodies.push(this.bodyArr[i].serialize());
- }
- }
-
- var o_joints = [];
- for (var i = 0; i < this.jointArr.length; i++) {
- if (this.jointArr[i]) {
- o_joints.push(this.jointHash[i].serialize());
- }
- }
-
- return {
- bodies: o_bodies,
- joints: o_joints
- };
-}
-
-Space.prototype.create = function(text) {
- var config = JSON.parse(text);
-
- this.clear();
-
- for (var i = 0; i < config.bodies.length; i++) {
- var config_body = config.bodies[i];
- var type = {"static": Body.Static, "kinetic": Body.KINETIC, "dynamic": Body.DYNAMIC}[config_body.type];
- var body = new Body(type, config_body.position.x, config_body.position.y, config_body.angle);
-
- for (var j = 0; j < config_body.shapes.length; j++) {
- var config_shape = config_body.shapes[j];
- var shape;
-
- switch (config_shape.type) {
- case "ShapeCircle":
- shape = new ShapeCircle(config_shape.center.x, config_shape.center.y, config_shape.radius);
- break;
- case "ShapeSegment":
- shape = new ShapeSegment(config_shape.a, config_shape.b, config_shape.radius);
- break;
- case "ShapePoly":
- shape = new ShapePoly(config_shape.verts);
- break;
- }
-
- shape.e = config_shape.e;
- shape.u = config_shape.u;
- shape.density = config_shape.density;
-
- body.addShape(shape);
- }
-
- body.resetMassData();
- this.addBody(body);
- }
-
- for (var i = 0; i < config.joints.length; i++) {
- var config_joint = config.joints[i];
- var body1 = this.bodyArr[this.bodyHash[config_joint.body1]];
- var body2 = this.bodyArr[this.bodyHash[config_joint.body2]];
- var joint;
-
- switch (config_joint.type) {
- case "AngleJoint":
- joint = new AngleJoint(body1, body2);
- break;
- case "RevoluteJoint":
- joint = new RevoluteJoint(body1, body2, config_joint.anchor);
- joint.enableLimit(config_joint.limitEnabled);
- joint.setLimits(config_joint.limitLowerAngle, config_joint.limitUpperAngle);
- joint.enableMotor(config_joint.motorEnabled);
- joint.setMotorSpeed(config_joint.motorSpeed);
- joint.setMaxMotorTorque(config_joint.maxMotorTorque);
- break;
- case "WeldJoint":
- joint = new WeldJoint(body1, body2, config_joint.anchor);
- joint.setSpringFrequencyHz(config_joint.frequencyHz);
- joint.setSpringDampingRatio(config_joint.dampingRatio);
- break;
- case "WheelJoint":
- joint = new WheelJoint(body1, body2, config_joint.anchor1, config_joint.anchor2);
- joint.enableMotor(config_joint.motorEnabled);
- joint.setMotorSpeed(config_joint.motorSpeed);
- joint.setMaxMotorTorque(config_joint.maxMotorTorque);
- break;
- case "PrismaticJoint":
- joint = new PrismaticJoint(body1, body2, config_joint.anchor1, config_joint.anchor2);
- break;
- case "DistanceJoint":
- joint = new DistanceJoint(body1, body2, config_joint.anchor1, config_joint.anchor2);
- joint.setSpringFrequencyHz(config_joint.frequencyHz);
- joint.setSpringDampingRatio(config_joint.dampingRatio);
- break;
- case "RopeJoint":
- joint = new RopeJoint(body1, body2, config_joint.anchor1, config_joint.anchor2);
- break;
- }
-
- joint.collideConnected = config_joint.collideConnected;
- joint.maxForce = config_joint.maxForce;
- joint.breakable = config_joint.breakable;
-
- this.addJoint(joint);
- }
-}
-
-Space.prototype.addBody = function(body) {
- if (this.bodyHash[body.id] != undefined) {
- return;
- }
-
- var index = this.bodyArr.push(body) - 1;
- this.bodyHash[body.id] = index;
-
- body.awake(true);
- body.space = this;
- body.cacheData();
-}
-
-Space.prototype.removeBody = function(body) {
- if (this.bodyHash[body.id] == undefined) {
- return;
- }
-
- // Remove linked joint
- for (var i = 0; i < body.jointArr.length; i++) {
- if (body.jointArr[i]) {
- this.removeJoint(body.jointArr[i]);
- }
- }
-
- body.space = null;
-
- var index = this.bodyHash[body.id];
- delete this.bodyHash[body.id];
- delete this.bodyArr[index];
-}
-
-Space.prototype.addJoint = function(joint) {
- if (this.jointHash[joint.id] != undefined) {
- return;
- }
-
- joint.body1.awake(true);
- joint.body2.awake(true);
-
- var index = this.jointArr.push(joint) - 1;
- this.jointHash[joint.id] = index;
-
- var index = joint.body1.jointArr.push(joint) - 1;
- joint.body1.jointHash[joint.id] = index;
-
- var index = joint.body2.jointArr.push(joint) - 1;
- joint.body2.jointHash[joint.id] = index;
-}
-
-Space.prototype.removeJoint = function(joint) {
- if (this.jointHash[joint.id] == undefined) {
- return;
- }
-
- joint.body1.awake(true);
- joint.body2.awake(true);
-
- var index = joint.body1.jointHash[joint.id];
- delete joint.body1.jointHash[joint.id];
- delete joint.body1.jointArr[index];
-
- var index = joint.body2.jointHash[joint.id];
- delete joint.body2.jointHash[joint.id];
- delete joint.body2.jointArr[index];
-
- var index = this.jointHash[joint.id];
- delete this.jointHash[joint.id];
- delete this.jointArr[index];
-}
-
-Space.prototype.findShapeByPoint = function(p, refShape) {
- var firstShape;
-
- for (var i = 0; i < this.bodyArr.length; i++) {
- var body = this.bodyArr[i];
- if (!body) {
- continue;
- }
-
- for (var j = 0; j < body.shapeArr.length; j++) {
- var shape = body.shapeArr[j];
-
- if (shape.pointQuery(p)) {
- if (!refShape) {
- return shape;
- }
-
- if (!firstShape) {
- firstShape = shape;
- }
-
- if (shape == refShape) {
- refShape = null;
- }
- }
- }
- }
-
- return firstShape;
-}
-
-Space.prototype.findBodyByPoint = function(p, refBody) {
- var firstBody;
-
- for (var i = 0; i < this.bodyArr.length; i++) {
- var body = this.bodyArr[i];
- if (!body) {
- continue;
- }
-
- for (var j = 0; j < body.shapeArr.length; j++) {
- var shape = body.shapeArr[j];
-
- if (shape.pointQuery(p)) {
- if (!refBody) {
- return shape.body;
- }
-
- if (!firstBody) {
- firstBody = shape.body;
- }
-
- if (shape.body == refBody) {
- refBody = null;
- }
-
- break;
- }
- }
- }
-
- return firstBody;
-}
-
-// TODO: Replace this function to shape hashing
-Space.prototype.shapeById = function(id) {
- var shape;
- for (var i = 0; i < this.bodyArr.length; i++) {
- var body = this.bodyArr[i];
- if (!body) {
- continue;
- }
-
- for (var j = 0; j < body.shapeArr.length; j++) {
- if (body.shapeArr[j].id == id) {
- return body.shapeArr[j];
- }
- }
- }
-
- return null;
-}
-
-Space.prototype.jointById = function(id) {
- var index = this.jointHash[id];
- if (index != undefined) {
- return this.jointArr[index];
- }
-
- return null;
-}
-
-Space.prototype.findVertexByPoint = function(p, minDist, refVertexId) {
- var firstVertexId = -1;
-
- refVertexId = refVertexId || -1;
-
- for (var i = 0; i < this.bodyArr.length; i++) {
- var body = this.bodyArr[i];
- if (!body) {
- continue;
- }
-
- for (var j = 0; j < body.shapeArr.length; j++) {
- var shape = body.shapeArr[j];
- var index = shape.findVertexByPoint(p, minDist);
- if (index != -1) {
- var vertex = (shape.id << 16) | index;
- if (refVertexId == -1) {
- return vertex;
- }
-
- if (firstVertexId == -1) {
- firstVertexId = vertex;
- }
-
- if (vertex == refVertexId) {
- refVertexId = -1;
- }
- }
- }
- }
-
- return firstVertexId;
-}
-
-Space.prototype.findEdgeByPoint = function(p, minDist, refEdgeId) {
- var firstEdgeId = -1;
-
- refEdgeId = refEdgeId || -1;
-
- for (var i = 0; i < this.bodyArr.length; i++) {
- var body = this.bodyArr[i];
- if (!body) {
- continue;
- }
-
- for (var j = 0; j < body.shapeArr.length; j++) {
- var shape = body.shapeArr[j];
- if (shape.type != Shape.TYPE_POLY) {
- continue;
- }
-
- var index = shape.findEdgeByPoint(p, minDist);
- if (index != -1) {
- var edge = (shape.id << 16) | index;
- if (refEdgeId == -1) {
- return edge;
- }
-
- if (firstEdgeId == -1) {
- firstEdgeId = edge;
- }
-
- if (edge == refEdgeId) {
- refEdgeId = -1;
- }
- }
- }
- }
-
- return firstEdgeId;
-}
-
-Space.prototype.findJointByPoint = function(p, minDist, refJointId) {
- var firstJointId = -1;
-
- var dsq = minDist * minDist;
-
- refJointId = refJointId || -1;
-
- for (var i = 0; i < this.jointArr.length; i++) {
- var joint = this.jointArr[i];
- if (!joint) {
- continue;
- }
-
- var jointId = -1;
-
- if (vec2.distsq(p, joint.getWorldAnchor1()) < dsq) {
- jointId = (joint.id << 16 | 0);
- }
- else if (vec2.distsq(p, joint.getWorldAnchor2()) < dsq) {
- jointId = (joint.id << 16 | 1);
- }
-
- if (jointId != -1) {
- if (refJointId == -1) {
- return jointId;
- }
-
- if (firstJointId == -1) {
- firstJointId = jointId;
- }
-
- if (jointId == refJointId) {
- refJointId = -1;
- }
- }
- }
-
- return firstJointId;
-}
-
-Space.prototype.findContactSolver = function(shape1, shape2) {
-
- for (var i = 0; i < this.contactSolverArr.length; i++) {
- var contactSolver = this.contactSolverArr[i];
- if (shape1 == contactSolver.shape1 && shape2 == contactSolver.shape2) {
- return contactSolver;
- }
- }
-
- return null;
-}
-
-Space.dump = function (phase, body) {
-
- var s = "\n\nPhase: " + phase + "\n";
- s += "Position: " + body.p.toString() + "\n";
- s += "Velocity: " + body.v.toString() + "\n";
- s += "Angle: " + body.a + "\n";
- s += "Force: " + body.f.toString() + "\n";
- s += "Torque: " + body.t + "\n";
- s += "Bounds: " + body.bounds.toString() + "\n";
- s += "Shape ***\n";
- s += "Vert 0: " + body.shapeArr[0].verts[0].toString() + "\n";
- s += "Vert 1: " + body.shapeArr[0].verts[1].toString() + "\n";
- s += "Vert 2: " + body.shapeArr[0].verts[2].toString() + "\n";
- s += "Vert 3: " + body.shapeArr[0].verts[3].toString() + "\n";
- s += "TVert 0: " + body.shapeArr[0].tverts[0].toString() + "\n";
- s += "TVert 1: " + body.shapeArr[0].tverts[1].toString() + "\n";
- s += "TVert 2: " + body.shapeArr[0].tverts[2].toString() + "\n";
- s += "TVert 3: " + body.shapeArr[0].tverts[3].toString() + "\n";
- s += "Plane 0: " + body.shapeArr[0].planes[0].n.toString() + "\n";
- s += "Plane 1: " + body.shapeArr[0].planes[1].n.toString() + "\n";
- s += "Plane 2: " + body.shapeArr[0].planes[2].n.toString() + "\n";
- s += "Plane 3: " + body.shapeArr[0].planes[3].n.toString() + "\n";
- s += "TPlane 0: " + body.shapeArr[0].tplanes[0].n.toString() + "\n";
- s += "TPlane 1: " + body.shapeArr[0].tplanes[1].n.toString() + "\n";
- s += "TPlane 2: " + body.shapeArr[0].tplanes[2].n.toString() + "\n";
- s += "TPlane 3: " + body.shapeArr[0].tplanes[3].n.toString() + "\n";
-
- this.log.push(s);
-
-}
-
-Space.prototype.genTemporalContactSolvers = function() {
-
- var t0 = Date.now();
- var newContactSolverArr = [];
-
- this.numContacts = 0;
-
- for (var body1_index = 0; body1_index < this.bodyArr.length; body1_index++) {
- var body1 = this.bodyArr[body1_index];
- if (!body1) {
- continue;
- }
-
- body1.stepCount = this.stepCount;
-
- for (var body2_index = 0; body2_index < this.bodyArr.length; body2_index++) {
- var body2 = this.bodyArr[body2_index];
- if (!body2) {
- continue;
- }
-
- if (body1.stepCount == body2.stepCount) {
- continue;
- }
-
- var active1 = body1.isAwake() && !body1.isStatic();
- var active2 = body2.isAwake() && !body2.isStatic();
-
- if (!active1 && !active2) {
- continue;
- }
-
- if (!body1.isCollidable(body2)) {
- continue;
- }
-
- if (!body1.bounds.intersectsBounds(body2.bounds)) {
- continue;
- }
-
- for (var i = 0; i < body1.shapeArr.length; i++) {
- for (var j = 0; j < body2.shapeArr.length; j++) {
- var shape1 = body1.shapeArr[i];
- var shape2 = body2.shapeArr[j];
-
- var contactArr = [];
- if (!collision.collide(shape1, shape2, contactArr)) {
- continue;
- }
-
- if (shape1.type > shape2.type) {
- var temp = shape1;
- shape1 = shape2;
- shape2 = temp;
- }
-
- this.numContacts += contactArr.length;
-
- var contactSolver = this.findContactSolver(shape1, shape2);
-
- if (contactSolver) {
- contactSolver.update(contactArr);
- newContactSolverArr.push(contactSolver);
- }
- else {
- body1.awake(true);
- body2.awake(true);
-
- var newContactSolver = new ContactSolver(shape1, shape2);
- newContactSolver.contactArr = contactArr;
- newContactSolver.e = Math.max(shape1.e, shape2.e);
- newContactSolver.u = Math.sqrt(shape1.u * shape2.u);
- newContactSolverArr.push(newContactSolver);
- }
- }
- }
- }
- }
-
- stats.timeCollision = Date.now() - t0;
-
- return newContactSolverArr;
-}
-
-Space.prototype.initSolver = function(dt, dt_inv, warmStarting) {
-
- var t0 = Date.now();
-
- // Initialize contact solvers
- for (var i = 0; i < this.contactSolverArr.length; i++) {
- this.contactSolverArr[i].initSolver(dt_inv);
- }
-
- // Initialize joint solver
- for (var i = 0; i < this.jointArr.length; i++) {
- if (this.jointArr[i]) {
- this.jointArr[i].initSolver(dt, warmStarting);
- }
- }
-
- // Warm starting (apply cached impulse)
- if (warmStarting) {
- for (var i = 0; i < this.contactSolverArr.length; i++) {
- this.contactSolverArr[i].warmStart();
- }
- }
-
- stats.timeInitSolver = Date.now() - t0;
-}
-
-Space.prototype.velocitySolver = function(iteration) {
-
- var t0 = Date.now();
-
- for (var i = 0; i < iteration; i++) {
- for (var j = 0; j < this.jointArr.length; j++) {
- if (this.jointArr[j]) {
- this.jointArr[j].solveVelocityConstraints();
- }
- }
-
- for (var j = 0; j < this.contactSolverArr.length; j++) {
- this.contactSolverArr[j].solveVelocityConstraints();
- }
- }
-
- stats.timeVelocitySolver = Date.now() - t0;
-}
-
-Space.prototype.positionSolver = function(iteration) {
- var t0 = Date.now();
-
- var positionSolved = false;
-
- stats.positionIterations = 0;
-
- for (var i = 0; i < iteration; i++) {
- var contactsOk = true;
- var jointsOk = true;
-
- for (var j = 0; j < this.contactSolverArr.length; j++) {
- var contactOk = this.contactSolverArr[j].solvePositionConstraints();
- contactsOk = contactOk && contactsOk;
- }
-
- for (var j = 0; j < this.jointArr.length; j++) {
- if (this.jointArr[j]) {
- var jointOk = this.jointArr[j].solvePositionConstraints();
- jointsOk = jointOk && jointsOk;
- }
- }
-
- if (contactsOk && jointsOk) {
- // exit early if the position errors are small
- positionSolved = true;
- break;
- }
-
- stats.positionIterations++;
- }
-
- stats.timePositionSolver = Date.now() - t0;
-
- return positionSolved;
-}
-
-Space.prototype.step = function(dt, vel_iteration, pos_iteration, warmStarting, allowSleep) {
-
- var dt_inv = 1 / dt;
-
- this.stepCount++;
-
- // Generate contact & contactSolver
- this.contactSolverArr = this.genTemporalContactSolvers();
-
- // Initialize contacts & joints solver
- this.initSolver(dt, dt_inv, warmStarting);
-
- // Intergrate velocity
- for (var i = 0; i < this.bodyArr.length; i++) {
- var body = this.bodyArr[i];
- if (!body) {
- continue;
- }
-
- if (body.isDynamic() && body.isAwake()) {
- body.updateVelocity(this.gravity, dt, this.damping);
- }
- }
-
- for (var i = 0; i < this.jointArr.length; i++) {
- var joint = this.jointArr[i];
- if (!joint) {
- continue;
- }
-
- var body1 = joint.body1;
- var body2 = joint.body2;
-
- var awake1 = body1.isAwake() && !body1.isStatic();
- var awake2 = body2.isAwake() && !body2.isStatic();
-
- if (awake1 ^ awake2) {
- if (!awake1)
- body1.awake(true);
- if (!awake2)
- body2.awake(true);
- }
- }
-
- // Iterative velocity constraints solver
- this.velocitySolver(vel_iteration);
-
- // Intergrate position
- for (var i = 0; i < this.bodyArr.length; i++) {
- var body = this.bodyArr[i];
- if (!body) {
- continue
- }
-
- if (body.isDynamic() && body.isAwake()) {
- body.updatePosition(dt);
- }
- }
-
- // Process breakable joint
- for (var i = 0; i < this.jointArr.length; i++) {
- var joint = this.jointArr[i];
- if (!joint) {
- continue;
- }
-
- if (joint.breakable) {
- if (joint.getReactionForce(dt_inv).lengthsq() >= joint.maxForce * joint.maxForce)
- this.removeJoint(joint);
- }
- }
-
- // Iterative position constraints solver
- var positionSolved = this.positionSolver(pos_iteration);
-
- for (var i = 0; i < this.bodyArr.length; i++) {
- var body = this.bodyArr[i];
- if (!body) {
- continue;
- }
-
- body.syncTransform();
- }
-
- // Post solve collision callback
- for (var i = 0; i < this.contactSolverArr.length; i++) {
- var arb = this.contactSolverArr[i];
- //this.postSolve(arb);
- }
-
- for (var i = 0; i < this.bodyArr.length; i++) {
- var body = this.bodyArr[i];
- if (!body) {
- continue;
- }
-
- if (body.isDynamic() && body.isAwake()) {
- body.cacheData();
- }
- }
-
- // Process sleeping
- if (allowSleep) {
- var minSleepTime = 999999;
-
- var linTolSqr = Space.SLEEP_LINEAR_TOLERANCE * Space.SLEEP_LINEAR_TOLERANCE;
- var angTolSqr = Space.SLEEP_ANGULAR_TOLERANCE * Space.SLEEP_ANGULAR_TOLERANCE;
-
- for (var i = 0; i < this.bodyArr.length; i++) {
- var body = this.bodyArr[i];
- if (!body) {
- continue;
- }
-
- if (!body.isDynamic()) {
- continue;
- }
-
- if (body.w * body.w > angTolSqr || body.v.dot(body.v) > linTolSqr) {
- body.sleepTime = 0;
- minSleepTime = 0;
- }
- else {
- body.sleepTime += dt;
- minSleepTime = Math.min(minSleepTime, body.sleepTime);
- }
- }
-
- if (positionSolved && minSleepTime >= Space.TIME_TO_SLEEP) {
- for (var i = 0; i < this.bodyArr.length; i++) {
- var body = this.bodyArr[i];
- if (!body) {
- continue;
- }
-
- body.awake(false);
- }
- }
- }
-
-}
-
diff --git a/src/physics/advanced/Util.js b/src/physics/advanced/Util.js
deleted file mode 100644
index 93b0e793..00000000
--- a/src/physics/advanced/Util.js
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
-* Copyright (c) 2012 Ju Hyung Lee
-*
-* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
-* and associated documentation files (the "Software"), to deal in the Software without
-* restriction, including without limitation the rights to use, copy, modify, merge, publish,
-* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
-* Software is furnished to do so, subject to the following conditions:
-*
-* The above copyright notice and this permission notice shall be included in all copies or
-* substantial portions of the Software.
-*
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
-* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-function areaForCircle(radius_outer, radius_inner) {
- return Math.PI * (radius_outer * radius_outer - radius_inner * radius_inner);
-}
-
-function inertiaForCircle(mass, center, radius_outer, radius_inner) {
- return mass * ((radius_outer * radius_outer + radius_inner * radius_inner) * 0.5 + center.lengthsq());
-}
-
-function areaForSegment(a, b, radius) {
- return radius * (Math.PI * radius + 2 * vec2.dist(a, b));
-}
-
-function centroidForSegment(a, b) {
- return vec2.scale(vec2.add(a, b), 0.5);
-}
-
-function inertiaForSegment(mass, a, b) {
- var distsq = vec2.distsq(b, a);
- var offset = vec2.scale(vec2.add(a, b), 0.5);
-
- return mass * (distsq / 12 + offset.lengthsq());
-}
-
-function areaForPoly(verts) {
- var area = 0;
- for (var i = 0; i < verts.length; i++) {
- area += vec2.cross(verts[i], verts[(i + 1) % verts.length]);
- }
-
- return area / 2;
-}
-
-function centroidForPoly(verts) {
- var area = 0;
- var vsum = new vec2(0, 0);
-
- for (var i = 0; i < verts.length; i++) {
- var v1 = verts[i];
- var v2 = verts[(i + 1) % verts.length];
- var cross = vec2.cross(v1, v2);
-
- area += cross;
- vsum.addself(vec2.scale(vec2.add(v1, v2), cross));
- }
-
- return vec2.scale(vsum, 1 / (3 * area));
-}
-
-function inertiaForPoly(mass, verts, offset) {
- var sum1 = 0;
- var sum2 = 0;
-
- for (var i = 0; i < verts.length; i++) {
- var v1 = vec2.add(verts[i], offset);
- var v2 = vec2.add(verts[(i+1) % verts.length], offset);
-
- var a = vec2.cross(v2, v1);
- var b = vec2.dot(v1, v1) + vec2.dot(v1, v2) + vec2.dot(v2, v2);
-
- sum1 += a * b;
- sum2 += a;
- }
-
- return (mass * sum1) / (6 * sum2);
-}
-
-function inertiaForBox(mass, w, h) {
- return mass * (w * w + h * h) / 12;
-}
-
-// Create the convex hull using the Gift wrapping algorithm
-// http://en.wikipedia.org/wiki/Gift_wrapping_algorithm
-function createConvexHull(points) {
- // Find the right most point on the hull
- var i0 = 0;
- var x0 = points[0].x;
- for (var i = 1; i < points.length; i++) {
- var x = points[i].x;
- if (x > x0 || (x == x0 && points[i].y < points[i0].y)) {
- i0 = i;
- x0 = x;
- }
- }
-
- var n = points.length;
- var hull = [];
- var m = 0;
- var ih = i0;
-
- while (1) {
- hull[m] = ih;
-
- var ie = 0;
- for (var j = 1; j < n; j++) {
- if (ie == ih) {
- ie = j;
- continue;
- }
-
- var r = vec2.sub(points[ie], points[hull[m]]);
- var v = vec2.sub(points[j], points[hull[m]]);
- var c = vec2.cross(r, v);
- if (c < 0) {
- ie = j;
- }
-
- // Collinearity check
- if (c == 0 && v.lengthsq() > r.lengthsq()) {
- ie = j;
- }
- }
-
- m++;
- ih = ie;
-
- if (ie == i0) {
- break;
- }
- }
-
- // Copy vertices
- var newPoints = [];
- for (var i = 0; i < m; ++i) {
- newPoints.push(points[hull[i]]);
- }
-
- return newPoints;
-}
\ No newline at end of file
diff --git a/src/physics/advanced/collision/Broadphase.js b/src/physics/advanced/collision/Broadphase.js
new file mode 100644
index 00000000..b0d6129c
--- /dev/null
+++ b/src/physics/advanced/collision/Broadphase.js
@@ -0,0 +1,43 @@
+var vec2 = require('../math/vec2')
+, Nearphase = require('./Nearphase')
+, Shape = require('./../shapes/Shape')
+
+module.exports = Broadphase;
+
+/**
+ * Base class for broadphase implementations.
+ * @class Broadphase
+ * @constructor
+ */
+function Broadphase(){
+ this.result = [];
+};
+
+/**
+ * Get all potential intersecting body pairs.
+ * @method getCollisionPairs
+ * @param {World} world The world to search in.
+ * @return {Array} An array of the bodies, ordered in pairs. Example: A result of [a,b,c,d] means that the potential pairs are: (a,b), (c,d).
+ */
+Broadphase.prototype.getCollisionPairs = function(world){
+ throw new Error("getCollisionPairs must be implemented in a subclass!");
+};
+
+// Temp things
+var dist = vec2.create(),
+ worldNormal = vec2.create(),
+ yAxis = vec2.fromValues(0,1);
+
+/**
+ * Check whether the bounding radius of two bodies overlap.
+ * @method boundingRadiusCheck
+ * @param {Body} bodyA
+ * @param {Body} bodyB
+ * @return {Boolean}
+ */
+Broadphase.boundingRadiusCheck = function(bodyA, bodyB){
+ vec2.sub(dist, bodyA.position, bodyB.position);
+ var d2 = vec2.squaredLength(dist),
+ r = bodyA.boundingRadius + bodyB.boundingRadius;
+ return d2 <= r*r;
+};
diff --git a/src/physics/advanced/collision/GridBroadphase.js b/src/physics/advanced/collision/GridBroadphase.js
new file mode 100644
index 00000000..d0a7172b
--- /dev/null
+++ b/src/physics/advanced/collision/GridBroadphase.js
@@ -0,0 +1,159 @@
+var Circle = require('../shapes/Circle')
+, Plane = require('../shapes/Plane')
+, Particle = require('../shapes/Particle')
+, Broadphase = require('../collision/Broadphase')
+, vec2 = require('../math/vec2')
+
+module.exports = GridBroadphase;
+
+/**
+ * Broadphase that uses axis-aligned bins.
+ * @class GridBroadphase
+ * @constructor
+ * @extends Broadphase
+ * @param {number} xmin Lower x bound of the grid
+ * @param {number} xmax Upper x bound
+ * @param {number} ymin Lower y bound
+ * @param {number} ymax Upper y bound
+ * @param {number} nx Number of bins along x axis
+ * @param {number} ny Number of bins along y axis
+ * @todo test
+ */
+function GridBroadphase(xmin,xmax,ymin,ymax,nx,ny){
+ Broadphase.apply(this);
+
+ nx = nx || 10;
+ ny = ny || 10;
+
+ this.binsizeX = (xmax-xmin) / nx;
+ this.binsizeY = (ymax-ymin) / ny;
+ this.nx = nx;
+ this.ny = ny;
+ this.xmin = xmin;
+ this.ymin = ymin;
+ this.xmax = xmax;
+ this.ymax = ymax;
+};
+GridBroadphase.prototype = new Broadphase();
+
+/**
+ * Get a bin index given a world coordinate
+ * @method getBinIndex
+ * @param {Number} x
+ * @param {Number} y
+ * @return {Number} Integer index
+ */
+GridBroadphase.prototype.getBinIndex = function(x,y){
+ var nx = this.nx,
+ ny = this.ny,
+ xmin = this.xmin,
+ ymin = this.ymin,
+ xmax = this.xmax,
+ ymax = this.ymax;
+
+ var xi = Math.floor(nx * (x - xmin) / (xmax-xmin));
+ var yi = Math.floor(ny * (y - ymin) / (ymax-ymin));
+ return xi*ny + yi;
+}
+
+/**
+ * Get collision pairs.
+ * @method getCollisionPairs
+ * @param {World} world
+ * @return {Array}
+ */
+GridBroadphase.prototype.getCollisionPairs = function(world){
+ var result = [],
+ collidingBodies = world.bodies,
+ Ncolliding = Ncolliding=collidingBodies.length,
+ binsizeX = this.binsizeX,
+ binsizeY = this.binsizeY;
+
+ var bins=[], Nbins=nx*ny;
+ for(var i=0; i= 0 && xi*(ny-1) + yi < Nbins)
+ bins[ xi*(ny-1) + yi ].push(bi);
+ }
+ }
+ } else if(si instanceof Plane){
+ // Put in all bins for now
+ if(bi.angle == 0){
+ var y = bi.position[1];
+ for(var j=0; j!==Nbins && ymin+binsizeY*(j-1) pos0 && pos < pos1){
+ // We got contact!
+
+ if(justTest) return true;
+
+ var c = this.createContactEquation(circleBody,lineBody);
+
+ vec2.scale(c.ni, orthoDist, -1);
+ vec2.normalize(c.ni, c.ni);
+
+ vec2.scale( c.ri, c.ni, circleRadius);
+ add(c.ri, c.ri, circleOffset);
+ sub(c.ri, c.ri, circleBody.position);
+
+ sub(c.rj, projectedPoint, lineOffset);
+ add(c.rj, c.rj, lineOffset);
+ sub(c.rj, c.rj, lineBody.position);
+
+ this.contactEquations.push(c);
+
+ if(this.enableFriction){
+ this.frictionEquations.push(this.createFrictionFromContact(c));
+ }
+
+ return true;
+ }
+ }
+
+ // Add corner
+ var verts = [worldVertex0, worldVertex1];
+
+ for(var i=0; i 0){
+
+ // Now project the circle onto the edge
+ vec2.scale(orthoDist, worldTangent, d);
+ sub(projectedPoint, circleOffset, orthoDist);
+
+ // Check if the point is within the edge span
+ var pos = dot(worldEdgeUnit, projectedPoint);
+ var pos0 = dot(worldEdgeUnit, worldVertex0);
+ var pos1 = dot(worldEdgeUnit, worldVertex1);
+
+ if(pos > pos0 && pos < pos1){
+ // We got contact!
+
+ var c = this.createContactEquation(circleBody,convexBody);
+
+ vec2.scale(c.ni, orthoDist, -1);
+ vec2.normalize(c.ni, c.ni);
+
+ vec2.scale( c.ri, c.ni, circleShape.radius);
+ add(c.ri, c.ri, circleOffset);
+ sub(c.ri, c.ri, circleBody.position);
+
+ sub( c.rj, projectedPoint, convexOffset);
+ add(c.rj, c.rj, convexOffset);
+ sub(c.rj, c.rj, convexBody.position);
+
+ this.contactEquations.push(c);
+
+ if(this.enableFriction){
+ this.frictionEquations.push( this.createFrictionFromContact(c) );
+ }
+
+ return true;
+ }
+ }
+ }
+
+ // Check all vertices
+ for(var i=0; i= 0){
+
+ // Now project the particle onto the edge
+ vec2.scale(orthoDist, worldTangent, d);
+ sub(projectedPoint, particleOffset, orthoDist);
+
+ // Check if the point is within the edge span
+ var pos = dot(worldEdgeUnit, projectedPoint);
+ var pos0 = dot(worldEdgeUnit, worldVertex0);
+ var pos1 = dot(worldEdgeUnit, worldVertex1);
+
+ if(pos > pos0 && pos < pos1){
+ // We got contact!
+ if(justTest) return true;
+
+ if(closestEdgeDistance === null || d*d r*r){
+ return false;
+ }
+
+ if(justTest) return true;
+
+ var c = this.createContactEquation(bodyA,bodyB);
+ sub(c.ni, offsetB, offsetA);
+ vec2.normalize(c.ni,c.ni);
+
+ vec2.scale( c.ri, c.ni, shapeA.radius);
+ vec2.scale( c.rj, c.ni, -shapeB.radius);
+
+ add(c.ri, c.ri, offsetA);
+ sub(c.ri, c.ri, bodyA.position);
+
+ add(c.rj, c.rj, offsetB);
+ sub(c.rj, c.rj, bodyB.position);
+
+ this.contactEquations.push(c);
+
+ if(this.enableFriction){
+ this.frictionEquations.push(this.createFrictionFromContact(c));
+ }
+ return true;
+};
+
+/**
+ * Convex/Plane nearphase
+ * @method convexPlane
+ * @param {Body} bi
+ * @param {Convex} si
+ * @param {Array} xi
+ * @param {Number} ai
+ * @param {Body} bj
+ * @param {Plane} sj
+ * @param {Array} xj
+ * @param {Number} aj
+ */
+Nearphase.prototype.convexPlane = function( bi,si,xi,ai, bj,sj,xj,aj ){
+ var convexBody = bi,
+ convexOffset = xi,
+ convexShape = si,
+ convexAngle = ai,
+ planeBody = bj,
+ planeShape = sj,
+ planeOffset = xj,
+ planeAngle = aj;
+
+ var worldVertex = tmp1,
+ worldNormal = tmp2,
+ dist = tmp3;
+
+ var numReported = 0;
+ vec2.rotate(worldNormal, yAxis, planeAngle);
+
+ for(var i=0; i= 2)
+ break;
+ }
+ }
+ return numReported > 0;
+};
+
+/**
+ * Nearphase for particle vs plane
+ * @method particlePlane
+ * @param {Body} bi The particle body
+ * @param {Particle} si Particle shape
+ * @param {Array} xi World position for the particle
+ * @param {Number} ai World angle for the particle
+ * @param {Body} bj Plane body
+ * @param {Plane} sj Plane shape
+ * @param {Array} xj World position for the plane
+ * @param {Number} aj World angle for the plane
+ */
+Nearphase.prototype.particlePlane = function( bi,si,xi,ai, bj,sj,xj,aj, justTest ){
+ var particleBody = bi,
+ particleShape = si,
+ particleOffset = xi,
+ planeBody = bj,
+ planeShape = sj,
+ planeOffset = xj,
+ planeAngle = aj;
+
+ var dist = tmp1,
+ worldNormal = tmp2;
+
+ planeAngle = planeAngle || 0;
+
+ sub(dist, particleOffset, planeOffset);
+ vec2.rotate(worldNormal, yAxis, planeAngle);
+
+ var d = dot(dist, worldNormal);
+
+ if(d > 0) return false;
+ if(justTest) return true;
+
+ var c = this.createContactEquation(planeBody,particleBody);
+
+ vec2.copy(c.ni, worldNormal);
+ vec2.scale( dist, c.ni, d );
+ // dist is now the distance vector in the normal direction
+
+ // ri is the particle position projected down onto the plane, from the plane center
+ sub( c.ri, particleOffset, dist);
+ sub( c.ri, c.ri, planeBody.position);
+
+ // rj is from the body center to the particle center
+ sub( c.rj, particleOffset, particleBody.position );
+
+ this.contactEquations.push(c);
+
+ if(this.enableFriction){
+ this.frictionEquations.push(this.createFrictionFromContact(c));
+ }
+ return true;
+};
+
+/**
+ * Circle/Particle nearphase
+ * @method circleParticle
+ * @param {Body} bi
+ * @param {Circle} si
+ * @param {Array} xi
+ * @param {Number} ai
+ * @param {Body} bj
+ * @param {Particle} sj
+ * @param {Array} xj
+ * @param {Number} aj
+ */
+Nearphase.prototype.circleParticle = function( bi,si,xi,ai, bj,sj,xj,aj, justTest ){
+ var circleBody = bi,
+ circleShape = si,
+ circleOffset = xi,
+ particleBody = bj,
+ particleShape = sj,
+ particleOffset = xj,
+ dist = tmp1;
+
+ sub(dist, particleOffset, circleOffset);
+ if(vec2.squaredLength(dist) > circleShape.radius*circleShape.radius) return false;
+ if(justTest) return true;
+
+ var c = this.createContactEquation(circleBody,particleBody);
+ vec2.copy(c.ni, dist);
+ vec2.normalize(c.ni,c.ni);
+
+ // Vector from circle to contact point is the normal times the circle radius
+ vec2.scale(c.ri, c.ni, circleShape.radius);
+ add(c.ri, c.ri, circleOffset);
+ sub(c.ri, c.ri, circleBody.position);
+
+ // Vector from particle center to contact point is zero
+ sub(c.rj, particleOffset, particleBody.position);
+
+ this.contactEquations.push(c);
+
+ if(this.enableFriction){
+ this.frictionEquations.push(this.createFrictionFromContact(c));
+ }
+
+ return true;
+};
+
+var capsulePlane_tmpCircle = new Circle(1),
+ capsulePlane_tmp1 = vec2.create(),
+ capsulePlane_tmp2 = vec2.create(),
+ capsulePlane_tmp3 = vec2.create();
+Nearphase.prototype.capsulePlane = function( bi,si,xi,ai, bj,sj,xj,aj ){
+ var end1 = capsulePlane_tmp1,
+ end2 = capsulePlane_tmp2,
+ circle = capsulePlane_tmpCircle,
+ dst = capsulePlane_tmp3;
+
+ // Compute world end positions
+ vec2.set(end1, -si.length/2, 0);
+ vec2.rotate(end1,end1,ai);
+ add(end1,end1,xi);
+
+ vec2.set(end2, si.length/2, 0);
+ vec2.rotate(end2,end2,ai);
+ add(end2,end2,xi);
+
+ circle.radius = si.radius;
+
+ // Do nearphase as two circles
+ this.circlePlane(bi,circle,end1,0, bj,sj,xj,aj);
+ this.circlePlane(bi,circle,end2,0, bj,sj,xj,aj);
+};
+
+/**
+ * Creates ContactEquations and FrictionEquations for a collision.
+ * @method circlePlane
+ * @param {Body} bi The first body that should be connected to the equations.
+ * @param {Circle} si The circle shape participating in the collision.
+ * @param {Array} xi Extra offset to take into account for the Shape, in addition to the one in circleBody.position. Will *not* be rotated by circleBody.angle (maybe it should, for sake of homogenity?). Set to null if none.
+ * @param {Body} bj The second body that should be connected to the equations.
+ * @param {Plane} sj The Plane shape that is participating
+ * @param {Array} xj Extra offset for the plane shape.
+ * @param {Number} aj Extra angle to apply to the plane
+ */
+Nearphase.prototype.circlePlane = function( bi,si,xi,ai, bj,sj,xj,aj ){
+ var circleBody = bi,
+ circleShape = si,
+ circleOffset = xi, // Offset from body center, rotated!
+ planeBody = bj,
+ shapeB = sj,
+ planeOffset = xj,
+ planeAngle = aj;
+
+ planeAngle = planeAngle || 0;
+
+ // Vector from plane to circle
+ var planeToCircle = tmp1,
+ worldNormal = tmp2,
+ temp = tmp3;
+
+ sub(planeToCircle, circleOffset, planeOffset);
+
+ // World plane normal
+ vec2.rotate(worldNormal, yAxis, planeAngle);
+
+ // Normal direction distance
+ var d = dot(worldNormal, planeToCircle);
+
+ if(d > circleShape.radius) return false; // No overlap. Abort.
+
+ // Create contact
+ var contact = this.createContactEquation(planeBody,circleBody);
+
+ // ni is the plane world normal
+ vec2.copy(contact.ni, worldNormal);
+
+ // rj is the vector from circle center to the contact point
+ vec2.scale(contact.rj, contact.ni, -circleShape.radius);
+ add(contact.rj, contact.rj, circleOffset);
+ sub(contact.rj, contact.rj, circleBody.position);
+
+ // ri is the distance from plane center to contact.
+ vec2.scale(temp, contact.ni, d);
+ sub(contact.ri, planeToCircle, temp ); // Subtract normal distance vector from the distance vector
+ add(contact.ri, contact.ri, planeOffset);
+ sub(contact.ri, contact.ri, planeBody.position);
+
+ this.contactEquations.push(contact);
+
+ if(this.enableFriction){
+ this.frictionEquations.push( this.createFrictionFromContact(contact) );
+ }
+
+ return true;
+};
+
+
+/**
+ * Convex/convex nearphase.See this article for more info.
+ * @method convexConvex
+ * @param {Body} bi
+ * @param {Convex} si
+ * @param {Array} xi
+ * @param {Number} ai
+ * @param {Body} bj
+ * @param {Convex} sj
+ * @param {Array} xj
+ * @param {Number} aj
+ */
+Nearphase.prototype.convexConvex = function( bi,si,xi,ai, bj,sj,xj,aj ){
+ var sepAxis = tmp1,
+ worldPoint = tmp2,
+ worldPoint0 = tmp3,
+ worldPoint1 = tmp4,
+ worldEdge = tmp5,
+ projected = tmp6,
+ penetrationVec = tmp7,
+ dist = tmp8,
+ worldNormal = tmp9;
+
+ var found = Nearphase.findSeparatingAxis(si,xi,ai,sj,xj,aj,sepAxis);
+ if(!found) return false;
+
+ // Make sure the separating axis is directed from shape i to shape j
+ sub(dist,xj,xi);
+ if(dot(sepAxis,dist) > 0){
+ vec2.scale(sepAxis,sepAxis,-1);
+ }
+
+ // Find edges with normals closest to the separating axis
+ var closestEdge1 = Nearphase.getClosestEdge(si,ai,sepAxis,true), // Flipped axis
+ closestEdge2 = Nearphase.getClosestEdge(sj,aj,sepAxis);
+
+ if(closestEdge1==-1 || closestEdge2==-1) return false;
+
+ // Loop over the shapes
+ for(var k=0; k<2; k++){
+
+ var closestEdgeA = closestEdge1,
+ closestEdgeB = closestEdge2,
+ shapeA = si, shapeB = sj,
+ offsetA = xi, offsetB = xj,
+ angleA = ai, angleB = aj,
+ bodyA = bi, bodyB = bj;
+
+ if(k==0){
+ // Swap!
+ var tmp;
+ tmp = closestEdgeA; closestEdgeA = closestEdgeB; closestEdgeB = tmp;
+ tmp = shapeA; shapeA = shapeB; shapeB = tmp;
+ tmp = offsetA; offsetA = offsetB; offsetB = tmp;
+ tmp = angleA; angleA = angleB; angleB = tmp;
+ tmp = bodyA; bodyA = bodyB; bodyB = tmp;
+ }
+
+ // Loop over 2 points in convex B
+ for(var j=closestEdgeB; j max) max = value;
+ if(min === null || value < min) min = value;
+ }
+
+ if(min > max){
+ var t = min;
+ min = max;
+ max = t;
+ }
+
+ // Project the position of the body onto the axis - need to add this to the result
+ var offset = dot(convexOffset, worldAxis);
+
+ vec2.set( result, min + offset, max + offset);
+};
+
+// .findSeparatingAxis is called by other functions, need local tmp vectors
+var fsa_tmp1 = vec2.fromValues(0,0)
+, fsa_tmp2 = vec2.fromValues(0,0)
+, fsa_tmp3 = vec2.fromValues(0,0)
+, fsa_tmp4 = vec2.fromValues(0,0)
+, fsa_tmp5 = vec2.fromValues(0,0)
+, fsa_tmp6 = vec2.fromValues(0,0)
+
+/**
+ * Find a separating axis between the shapes, that maximizes the separating distance between them.
+ * @method findSeparatingAxis
+ * @static
+ * @param {Convex} c1
+ * @param {Array} offset1
+ * @param {Number} angle1
+ * @param {Convex} c2
+ * @param {Array} offset2
+ * @param {Number} angle2
+ * @param {Array} sepAxis The resulting axis
+ * @return {Boolean} Whether the axis could be found.
+ */
+Nearphase.findSeparatingAxis = function(c1,offset1,angle1,c2,offset2,angle2,sepAxis){
+ var maxDist = null,
+ overlap = false,
+ found = false,
+ edge = fsa_tmp1,
+ worldPoint0 = fsa_tmp2,
+ worldPoint1 = fsa_tmp3,
+ normal = fsa_tmp4,
+ span1 = fsa_tmp5,
+ span2 = fsa_tmp6;
+
+ for(var j=0; j!==2; j++){
+ var c = c1,
+ angle = angle1;
+ if(j===1){
+ c = c2;
+ angle = angle2;
+ }
+
+ for(var i=0; i!==c.vertices.length; i++){
+ // Get the world edge
+ vec2.rotate(worldPoint0, c.vertices[i], angle);
+ vec2.rotate(worldPoint1, c.vertices[(i+1)%c.vertices.length], angle);
+
+ sub(edge, worldPoint1, worldPoint0);
+
+ // Get normal - just rotate 90 degrees since vertices are given in CCW
+ vec2.rotate(normal, edge, -Math.PI / 2);
+ vec2.normalize(normal,normal);
+
+ // Project hulls onto that normal
+ Nearphase.projectConvexOntoAxis(c1,offset1,angle1,normal,span1);
+ Nearphase.projectConvexOntoAxis(c2,offset2,angle2,normal,span2);
+
+ // Order by span position
+ var a=span1,
+ b=span2,
+ swapped = false;
+ if(span1[0] > span2[0]){
+ b=span1;
+ a=span2;
+ swapped = true;
+ }
+
+ // Get separating distance
+ var dist = b[0] - a[1];
+ overlap = dist < 0;
+
+ if(maxDist===null || dist > maxDist){
+ vec2.copy(sepAxis, normal);
+ maxDist = dist;
+ found = overlap;
+ }
+ }
+ }
+
+ return found;
+};
+
+// .getClosestEdge is called by other functions, need local tmp vectors
+var gce_tmp1 = vec2.fromValues(0,0)
+, gce_tmp2 = vec2.fromValues(0,0)
+, gce_tmp3 = vec2.fromValues(0,0)
+
+/**
+ * Get the edge that has a normal closest to an axis.
+ * @method getClosestEdge
+ * @static
+ * @param {Convex} c
+ * @param {Number} angle
+ * @param {Array} axis
+ * @param {Boolean} flip
+ * @return {Number} Index of the edge that is closest. This index and the next spans the resulting edge. Returns -1 if failed.
+ */
+Nearphase.getClosestEdge = function(c,angle,axis,flip){
+ var localAxis = gce_tmp1,
+ edge = gce_tmp2,
+ normal = gce_tmp3;
+
+ // Convert the axis to local coords of the body
+ vec2.rotate(localAxis, axis, -angle);
+ if(flip){
+ vec2.scale(localAxis,localAxis,-1);
+ }
+
+ var closestEdge = -1,
+ N = c.vertices.length,
+ halfPi = Math.PI / 2;
+ for(var i=0; i!==N; i++){
+ // Get the edge
+ sub(edge, c.vertices[(i+1)%N], c.vertices[i%N]);
+
+ // Get normal - just rotate 90 degrees since vertices are given in CCW
+ vec2.rotate(normal, edge, -halfPi);
+ vec2.normalize(normal,normal);
+
+ var d = dot(normal,localAxis);
+ if(closestEdge == -1 || d > maxDot){
+ closestEdge = i % N;
+ maxDot = d;
+ }
+ }
+
+ return closestEdge;
+};
+
diff --git a/src/physics/advanced/collision/QuadTree.js b/src/physics/advanced/collision/QuadTree.js
new file mode 100644
index 00000000..b0a48a73
--- /dev/null
+++ b/src/physics/advanced/collision/QuadTree.js
@@ -0,0 +1,376 @@
+var Plane = require("../shapes/Plane");
+var Broadphase = require("../collision/Broadphase");
+
+module.exports = {
+ QuadTree : QuadTree,
+ Node : Node,
+ BoundsNode : BoundsNode,
+};
+
+/**
+ * QuadTree data structure. See https://github.com/mikechambers/ExamplesByMesh/tree/master/JavaScript/QuadTree
+ * @class QuadTree
+ * @constructor
+ * @param {Object} An object representing the bounds of the top level of the QuadTree. The object
+ * should contain the following properties : x, y, width, height
+ * @param {Boolean} pointQuad Whether the QuadTree will contain points (true), or items with bounds
+ * (width / height)(false). Default value is false.
+ * @param {Number} maxDepth The maximum number of levels that the quadtree will create. Default is 4.
+ * @param {Number} maxChildren The maximum number of children that a node can contain before it is split into sub-nodes.
+ */
+function QuadTree(bounds, pointQuad, maxDepth, maxChildren){
+ var node;
+ if(pointQuad){
+ node = new Node(bounds, 0, maxDepth, maxChildren);
+ } else {
+ node = new BoundsNode(bounds, 0, maxDepth, maxChildren);
+ }
+
+ /**
+ * The root node of the QuadTree which covers the entire area being segmented.
+ * @property root
+ * @type Node
+ */
+ this.root = node;
+}
+
+/**
+ * Inserts an item into the QuadTree.
+ * @method insert
+ * @param {Object|Array} item The item or Array of items to be inserted into the QuadTree. The item should expose x, y
+ * properties that represents its position in 2D space.
+ */
+QuadTree.prototype.insert = function(item){
+ if(item instanceof Array){
+ var len = item.length;
+ for(var i = 0; i < len; i++){
+ this.root.insert(item[i]);
+ }
+ } else {
+ this.root.insert(item);
+ }
+}
+
+/**
+ * Clears all nodes and children from the QuadTree
+ * @method clear
+ */
+QuadTree.prototype.clear = function(){
+ this.root.clear();
+}
+
+/**
+ * Retrieves all items / points in the same node as the specified item / point. If the specified item
+ * overlaps the bounds of a node, then all children in both nodes will be returned.
+ * @method retrieve
+ * @param {Object} item An object representing a 2D coordinate point (with x, y properties), or a shape
+ * with dimensions (x, y, width, height) properties.
+ */
+QuadTree.prototype.retrieve = function(item){
+ //get a copy of the array of items
+ var out = this.root.retrieve(item).slice(0);
+ return out;
+}
+
+QuadTree.prototype.getCollisionPairs = function(world){
+
+ var result = [];
+
+ // Add all bodies
+ this.insert(world.bodies);
+
+ /*
+ console.log("bodies",world.bodies.length);
+ console.log("maxDepth",this.root.maxDepth,"maxChildren",this.root.maxChildren);
+ */
+
+ for(var i=0; i!==world.bodies.length; i++){
+ var b = world.bodies[i],
+ items = this.retrieve(b);
+
+ //console.log("items",items.length);
+
+ // Check results
+ for(var j=0, len=items.length; j!==len; j++){
+ var item = items[j];
+
+ if(b === item) continue; // Do not add self
+
+ // Check if they were already added
+ var found = false;
+ for(var k=0, numAdded=result.length; k= this.maxDepth) && len > this.maxChildren) {
+ this.subdivide();
+
+ for(var i = 0; i < len; i++){
+ this.insert(this.children[i]);
+ }
+
+ this.children.length = 0;
+ }
+}
+
+Node.prototype.retrieve = function(item){
+ if(this.nodes.length){
+ var index = this.findIndex(item);
+ return this.nodes[index].retrieve(item);
+ }
+
+ return this.children;
+}
+
+Node.prototype.findIndex = function(item){
+ var b = this.bounds;
+ var left = (item.position[0]-item.boundingRadius > b.x + b.width / 2) ? false : true;
+ var top = (item.position[1]-item.boundingRadius > b.y + b.height / 2) ? false : true;
+
+ if(item instanceof Plane){
+ left = top = false; // Will overlap the left/top boundary since it is infinite
+ }
+
+ //top left
+ var index = Node.TOP_LEFT;
+ if(left){
+ if(!top){
+ index = Node.BOTTOM_LEFT;
+ }
+ } else {
+ if(top){
+ index = Node.TOP_RIGHT;
+ } else {
+ index = Node.BOTTOM_RIGHT;
+ }
+ }
+
+ return index;
+}
+
+
+Node.prototype.subdivide = function(){
+ var depth = this.depth + 1;
+
+ var bx = this.bounds.x;
+ var by = this.bounds.y;
+
+ //floor the values
+ var b_w_h = (this.bounds.width / 2);
+ var b_h_h = (this.bounds.height / 2);
+ var bx_b_w_h = bx + b_w_h;
+ var by_b_h_h = by + b_h_h;
+
+ //top left
+ this.nodes[Node.TOP_LEFT] = new this.classConstructor({
+ x:bx,
+ y:by,
+ width:b_w_h,
+ height:b_h_h
+ },
+ depth);
+
+ //top right
+ this.nodes[Node.TOP_RIGHT] = new this.classConstructor({
+ x:bx_b_w_h,
+ y:by,
+ width:b_w_h,
+ height:b_h_h
+ },
+ depth);
+
+ //bottom left
+ this.nodes[Node.BOTTOM_LEFT] = new this.classConstructor({
+ x:bx,
+ y:by_b_h_h,
+ width:b_w_h,
+ height:b_h_h
+ },
+ depth);
+
+
+ //bottom right
+ this.nodes[Node.BOTTOM_RIGHT] = new this.classConstructor({
+ x:bx_b_w_h,
+ y:by_b_h_h,
+ width:b_w_h,
+ height:b_h_h
+ },
+ depth);
+}
+
+Node.prototype.clear = function(){
+ this.children.length = 0;
+
+ var len = this.nodes.length;
+ for(var i = 0; i < len; i++){
+ this.nodes[i].clear();
+ }
+
+ this.nodes.length = 0;
+}
+
+
+// BoundsQuadTree
+
+function BoundsNode(bounds, depth, maxChildren, maxDepth){
+ Node.call(this, bounds, depth, maxChildren, maxDepth);
+ this.stuckChildren = [];
+}
+
+BoundsNode.prototype = new Node();
+BoundsNode.prototype.classConstructor = BoundsNode;
+BoundsNode.prototype.stuckChildren = null;
+
+//we use this to collect and conctenate items being retrieved. This way
+//we dont have to continuously create new Array instances.
+//Note, when returned from QuadTree.retrieve, we then copy the array
+BoundsNode.prototype.out = [];
+
+BoundsNode.prototype.insert = function(item){
+ if(this.nodes.length){
+ var index = this.findIndex(item);
+ var node = this.nodes[index];
+
+ /*
+ console.log("radius:",item.boundingRadius);
+ console.log("item x:",item.position[0] - item.boundingRadius,"x range:",node.bounds.x,node.bounds.x+node.bounds.width);
+ console.log("item y:",item.position[1] - item.boundingRadius,"y range:",node.bounds.y,node.bounds.y+node.bounds.height);
+ */
+
+ //todo: make _bounds bounds
+ if( !(item instanceof Plane) && // Plane is infinite.. Make it a "stuck" child
+ item.position[0] - item.boundingRadius >= node.bounds.x &&
+ item.position[0] + item.boundingRadius <= node.bounds.x + node.bounds.width &&
+ item.position[1] - item.boundingRadius >= node.bounds.y &&
+ item.position[1] + item.boundingRadius <= node.bounds.y + node.bounds.height){
+ this.nodes[index].insert(item);
+ } else {
+ this.stuckChildren.push(item);
+ }
+
+ return;
+ }
+
+ this.children.push(item);
+
+ var len = this.children.length;
+
+ if(this.depth < this.maxDepth && len > this.maxChildren){
+ this.subdivide();
+
+ for(var i=0; i boundA2 ){
+ break;
+ }
+
+ // If we got overlap, add pair
+ if(Broadphase.boundingRadiusCheck(bi,bj))
+ result.push(bi,bj);
+ }
+ }
+
+ return result;
+};
diff --git a/src/physics/advanced/constraints/Constraint.js b/src/physics/advanced/constraints/Constraint.js
new file mode 100644
index 00000000..df838cb1
--- /dev/null
+++ b/src/physics/advanced/constraints/Constraint.js
@@ -0,0 +1,42 @@
+module.exports = Constraint;
+
+/**
+ * Base constraint class.
+ *
+ * @class Constraint
+ * @constructor
+ * @author schteppe
+ * @param {Body} bodyA
+ * @param {Body} bodyB
+ */
+function Constraint(bodyA,bodyB){
+
+ /**
+ * Equations to be solved in this constraint
+ * @property equations
+ * @type {Array}
+ */
+ this.equations = [];
+
+ /**
+ * First body participating in the constraint.
+ * @property bodyA
+ * @type {Body}
+ */
+ this.bodyA = bodyA;
+
+ /**
+ * Second body participating in the constraint.
+ * @property bodyB
+ * @type {Body}
+ */
+ this.bodyB = bodyB;
+};
+
+/**
+ * To be implemented by subclasses. Should update the internal constraint parameters.
+ * @method update
+ */
+/*Constraint.prototype.update = function(){
+ throw new Error("method update() not implmemented in this Constraint subclass!");
+};*/
diff --git a/src/physics/advanced/constraints/ContactEquation.js b/src/physics/advanced/constraints/ContactEquation.js
new file mode 100644
index 00000000..d54ca921
--- /dev/null
+++ b/src/physics/advanced/constraints/ContactEquation.js
@@ -0,0 +1,136 @@
+var Equation = require("./Equation"),
+ vec2 = require('../math/vec2'),
+ mat2 = require('../math/mat2');
+
+module.exports = ContactEquation;
+
+/**
+ * Non-penetration constraint equation.
+ *
+ * @class ContactEquation
+ * @constructor
+ * @extends Equation
+ * @param {Body} bi
+ * @param {Body} bj
+ */
+function ContactEquation(bi,bj){
+ Equation.call(this,bi,bj,0,1e6);
+ this.ri = vec2.create();
+ this.penetrationVec = vec2.create();
+ this.rj = vec2.create();
+ this.ni = vec2.create();
+ this.rixn = 0;
+ this.rjxn = 0;
+};
+ContactEquation.prototype = new Equation();
+ContactEquation.prototype.constructor = ContactEquation;
+ContactEquation.prototype.computeB = function(a,b,h){
+ var bi = this.bi,
+ bj = this.bj,
+ ri = this.ri,
+ rj = this.rj,
+ xi = bi.position,
+ xj = bj.position;
+
+ var vi = bi.velocity,
+ wi = bi.angularVelocity,
+ fi = bi.force,
+ taui = bi.angularForce;
+
+ var vj = bj.velocity,
+ wj = bj.angularVelocity,
+ fj = bj.force,
+ tauj = bj.angularForce;
+
+ var penetrationVec = this.penetrationVec,
+ invMassi = bi.invMass,
+ invMassj = bj.invMass,
+ invIi = bi.invInertia,
+ invIj = bj.invInertia,
+ n = this.ni;
+
+ // Caluclate cross products
+ this.rixn = vec2.crossLength(ri,n);
+ this.rjxn = vec2.crossLength(rj,n);
+
+ // Calculate q = xj+rj -(xi+ri) i.e. the penetration vector
+ vec2.add(penetrationVec,xj,rj);
+ vec2.sub(penetrationVec,penetrationVec,xi);
+ vec2.sub(penetrationVec,penetrationVec,ri);
+
+ var Gq = vec2.dot(n,penetrationVec);
+
+ // Compute iteration
+ var GW = vec2.dot(vj,n) - vec2.dot(vi,n) + wj * this.rjxn - wi * this.rixn;
+ var GiMf = vec2.dot(fj,n)*invMassj - vec2.dot(fi,n)*invMassi + invIj*tauj*this.rjxn - invIi*taui*this.rixn;
+
+ var B = - Gq * a - GW * b - h*GiMf;
+
+ return B;
+};
+
+// Compute C = GMG+eps in the SPOOK equation
+var computeC_tmp1 = vec2.create(),
+ tmpMat1 = mat2.create(),
+ tmpMat2 = mat2.create();
+ContactEquation.prototype.computeC = function(eps){
+ var bi = this.bi,
+ bj = this.bj,
+ n = this.ni,
+ rixn = this.rixn,
+ rjxn = this.rjxn,
+ tmp = computeC_tmp1,
+ imMat1 = tmpMat1,
+ imMat2 = tmpMat2;
+
+ mat2.identity(imMat1);
+ mat2.identity(imMat2);
+ imMat1[0] = imMat1[3] = bi.invMass;
+ imMat2[0] = imMat2[3] = bj.invMass;
+
+ var C = vec2.dot(n,vec2.transformMat2(tmp,n,imMat1)) + vec2.dot(n,vec2.transformMat2(tmp,n,imMat2)) + eps;
+ //var C = bi.invMass + bj.invMass + eps;
+
+ C += bi.invInertia * this.rixn * this.rixn;
+ C += bj.invInertia * this.rjxn * this.rjxn;
+
+ return C;
+};
+
+ContactEquation.prototype.computeGWlambda = function(){
+ var bi = this.bi,
+ bj = this.bj,
+ n = this.ni,
+ dot = vec2.dot;
+
+ return dot(n, bj.vlambda) + bj.wlambda * this.rjxn - dot(n, bi.vlambda) - bi.wlambda * this.rixn;
+};
+
+var addToWlambda_temp = vec2.create();
+ContactEquation.prototype.addToWlambda = function(deltalambda){
+ var bi = this.bi,
+ bj = this.bj,
+ n = this.ni,
+ temp = addToWlambda_temp,
+ imMat1 = tmpMat1,
+ imMat2 = tmpMat2;
+
+ mat2.identity(imMat1);
+ mat2.identity(imMat2);
+ imMat1[0] = imMat1[3] = bi.invMass;
+ imMat2[0] = imMat2[3] = bj.invMass;
+
+ // Add to linear velocity
+ //vec2.scale(temp,n,-bi.invMass*deltalambda);
+ vec2.scale(temp,vec2.transformMat2(temp,n,imMat1),-deltalambda);
+ vec2.add( bi.vlambda,bi.vlambda, temp );
+
+ //vec2.scale(temp,n,bj.invMass*deltalambda);
+ vec2.scale(temp,vec2.transformMat2(temp,n,imMat2),deltalambda);
+ vec2.add( bj.vlambda,bj.vlambda, temp);
+
+ // Add to angular velocity
+ bi.wlambda -= bi.invInertia * this.rixn * deltalambda;
+ bj.wlambda += bj.invInertia * this.rjxn * deltalambda;
+};
+
diff --git a/src/physics/advanced/constraints/DistanceConstraint.js b/src/physics/advanced/constraints/DistanceConstraint.js
new file mode 100644
index 00000000..ccdb6e56
--- /dev/null
+++ b/src/physics/advanced/constraints/DistanceConstraint.js
@@ -0,0 +1,62 @@
+var Constraint = require('./Constraint')
+, ContactEquation = require('./ContactEquation')
+, vec2 = require('../math/vec2')
+
+module.exports = DistanceConstraint;
+
+/**
+ * Constraint that tries to keep the distance between two bodies constant.
+ *
+ * @class DistanceConstraint
+ * @constructor
+ * @author schteppe
+ * @param {Body} bodyA
+ * @param {Body} bodyB
+ * @param {number} dist The distance to keep between the bodies.
+ * @param {number} maxForce
+ * @extends {Constraint}
+ */
+function DistanceConstraint(bodyA,bodyB,distance,maxForce){
+ Constraint.call(this,bodyA,bodyB);
+
+ this.distance = distance;
+
+ if(typeof(maxForce)==="undefined" ) {
+ maxForce = 1e6;
+ }
+
+ var normal = new ContactEquation(bodyA,bodyB); // Just in the normal direction
+
+ this.equations = [ normal ];
+
+ // Make the contact constraint bilateral
+ this.setMaxForce(maxForce);
+}
+DistanceConstraint.prototype = new Constraint();
+
+/**
+ * Update the constraint equations. Should be done if any of the bodies changed position, before solving.
+ * @method update
+ */
+DistanceConstraint.prototype.update = function(){
+ var normal = this.equations[0],
+ bodyA = this.bodyA,
+ bodyB = this.bodyB,
+ distance = this.distance;
+
+ vec2.sub(normal.ni, bodyB.position, bodyA.position);
+ vec2.normalize(normal.ni,normal.ni);
+ vec2.scale(normal.ri, normal.ni, distance*0.5);
+ vec2.scale(normal.rj, normal.ni, -distance*0.5);
+};
+
+DistanceConstraint.prototype.setMaxForce = function(f){
+ var normal = this.equations[0];
+ normal.minForce = -f;
+ normal.maxForce = f;
+};
+
+DistanceConstraint.prototype.getMaxForce = function(f){
+ var normal = this.equations[0];
+ return normal.maxForce;
+};
diff --git a/src/physics/advanced/constraints/Equation.js b/src/physics/advanced/constraints/Equation.js
new file mode 100644
index 00000000..877f1222
--- /dev/null
+++ b/src/physics/advanced/constraints/Equation.js
@@ -0,0 +1,77 @@
+module.exports = Equation;
+
+/**
+ * Base class for constraint equations.
+ * @class Equation
+ * @constructor
+ * @param {Body} bi First body participating in the equation
+ * @param {Body} bj Second body participating in the equation
+ * @param {number} minForce Minimum force to apply. Default: -1e6
+ * @param {number} maxForce Maximum force to apply. Default: 1e6
+ */
+function Equation(bi,bj,minForce,maxForce){
+
+ /**
+ * Minimum force to apply when solving
+ * @property minForce
+ * @type {Number}
+ */
+ this.minForce = typeof(minForce)=="undefined" ? -1e6 : minForce;
+
+ /**
+ * Max force to apply when solving
+ * @property maxForce
+ * @type {Number}
+ */
+ this.maxForce = typeof(maxForce)=="undefined" ? 1e6 : maxForce;
+
+ /**
+ * First body participating in the constraint
+ * @property bi
+ * @type {Body}
+ */
+ this.bi = bi;
+
+ /**
+ * Second body participating in the constraint
+ * @property bj
+ * @type {Body}
+ */
+ this.bj = bj;
+
+ /**
+ * The stiffness of this equation. Typically chosen to a large number (~1e7), but can be chosen somewhat freely to get a stable simulation.
+ * @property stiffness
+ * @type {Number}
+ */
+ this.stiffness = 1e6;
+
+ /**
+ * The number of time steps needed to stabilize the constraint equation. Typically between 3 and 5 time steps.
+ * @property relaxation
+ * @type {Number}
+ */
+ this.relaxation = 4;
+
+ this.a = 0;
+ this.b = 0;
+ this.eps = 0;
+ this.h = 0;
+ this.updateSpookParams(1/60);
+};
+Equation.prototype.constructor = Equation;
+
+/**
+ * Update SPOOK parameters .a, .b and .eps according to the given time step. See equations 9, 10 and 11 in the SPOOK notes.
+ * @method updateSpookParams
+ * @param {number} timeStep
+ */
+Equation.prototype.updateSpookParams = function(timeStep){
+ var k = this.stiffness,
+ d = this.relaxation,
+ h = timeStep;
+ this.a = 4.0 / (h * (1 + 4 * d));
+ this.b = (4.0 * d) / (1 + 4 * d);
+ this.eps = 4.0 / (h * h * k * (1 + 4 * d));
+ this.h = timeStep;
+};
diff --git a/src/physics/advanced/constraints/FrictionEquation.js b/src/physics/advanced/constraints/FrictionEquation.js
new file mode 100644
index 00000000..a8b09184
--- /dev/null
+++ b/src/physics/advanced/constraints/FrictionEquation.js
@@ -0,0 +1,180 @@
+var mat2 = require('../math/mat2')
+, vec2 = require('../math/vec2')
+, Equation = require('./Equation')
+
+module.exports = FrictionEquation;
+
+// 3D cross product from glmatrix, until we get this to work...
+function cross(out, a, b) {
+ var ax = a[0], ay = a[1], az = a[2],
+ bx = b[0], by = b[1], bz = b[2];
+
+ out[0] = ay * bz - az * by;
+ out[1] = az * bx - ax * bz;
+ out[2] = ax * by - ay * bx;
+ return out;
+};
+
+var dot = vec2.dot;
+
+/**
+ * Constrains the slipping in a contact along a tangent
+ *
+ * @class FrictionEquation
+ * @constructor
+ * @param {Body} bi
+ * @param {Body} bj
+ * @param {Number} slipForce
+ * @extends {Equation}
+ */
+function FrictionEquation(bi,bj,slipForce){
+ Equation.call(this,bi,bj,-slipForce,slipForce);
+
+ /**
+ * Relative vector from center of body i to the contact point, in world coords.
+ * @property ri
+ * @type {Float32Array}
+ */
+ this.ri = vec2.create();
+
+ /**
+ * Relative vector from center of body j to the contact point, in world coords.
+ * @property rj
+ * @type {Float32Array}
+ */
+ this.rj = vec2.create();
+
+ /**
+ * Tangent vector that the friction force will act along, in world coords.
+ * @property t
+ * @type {Float32Array}
+ */
+ this.t = vec2.create();
+
+ this.rixt = 0;
+ this.rjxt = 0;
+};
+FrictionEquation.prototype = new Equation();
+FrictionEquation.prototype.constructor = FrictionEquation;
+
+/**
+ * Set the slipping condition for the constraint. The friction force cannot be
+ * larger than this value.
+ * @method setSlipForce
+ * @param {Number} slipForce
+ */
+FrictionEquation.prototype.setSlipForce = function(slipForce){
+ this.maxForce = slipForce;
+ this.minForce = -slipForce;
+};
+
+var rixtVec = [0,0,0];
+var rjxtVec = [0,0,0];
+var ri3 = [0,0,0];
+var rj3 = [0,0,0];
+var t3 = [0,0,0];
+FrictionEquation.prototype.computeB = function(a,b,h){
+ var a = this.a,
+ b = this.b,
+ bi = this.bi,
+ bj = this.bj,
+ ri = this.ri,
+ rj = this.rj,
+ t = this.t;
+
+ // Caluclate cross products
+ ri3[0] = ri[0];
+ ri3[1] = ri[1];
+ rj3[0] = rj[0];
+ rj3[1] = rj[1];
+ t3[0] = t[0];
+ t3[1] = t[1];
+ cross(rixtVec, ri3, t3);//ri.cross(t,rixt);
+ cross(rjxtVec, rj3, t3);//rj.cross(t,rjxt);
+ this.rixt = rixtVec[2];
+ this.rjxt = rjxtVec[2];
+
+ var GW = -dot(bi.velocity,t) + dot(bj.velocity,t) - this.rixt*bi.angularVelocity + this.rjxt*bj.angularVelocity; // eq. 40
+ var GiMf = -dot(bi.force,t)*bi.invMass +dot(bj.force,t)*bj.invMass -this.rixt*bi.invInertia*bi.angularForce + this.rjxt*bj.invInertia*bj.angularForce;
+
+ var B = /* - Gq * a */ - GW * b - h*GiMf;
+
+ return B;
+};
+
+// Compute C = G * iM * G' + eps
+//
+// G*iM*G' =
+//
+// [ iM1 ] [-t ]
+// [-t (-ri x t) t (rj x t)] * [ iI1 ] [-ri x t]
+// [ iM2 ] [t ]
+// [ iI2 ] [rj x t ]
+//
+// = (-t)*iM1*(-t) + (-ri x t)*iI1*(-ri x t) + t*iM2*t + (rj x t)*iI2*(rj x t)
+//
+// = t*iM1*t + (ri x t)*iI1*(ri x t) + t*iM2*t + (rj x t)*iI2*(rj x t)
+//
+var computeC_tmp1 = vec2.create(),
+ tmpMat1 = mat2.create(),
+ tmpMat2 = mat2.create();
+FrictionEquation.prototype.computeC = function(eps){
+ var bi = this.bi,
+ bj = this.bj,
+ t = this.t,
+ C = 0.0,
+ tmp = computeC_tmp1,
+ imMat1 = tmpMat1,
+ imMat2 = tmpMat2,
+ dot = vec2.dot;
+
+ mat2.identity(imMat1);
+ mat2.identity(imMat2);
+
+ imMat1[0] = imMat1[3] = bi.invMass;
+ imMat2[0] = imMat2[3] = bj.invMass;
+
+ C = dot(t,vec2.transformMat2(tmp,t,imMat1)) + dot(t,vec2.transformMat2(tmp,t,imMat2)) + eps;
+
+ //C = bi.invMass + bj.invMass + eps;
+
+ C += bi.invInertia * this.rixt * this.rixt;
+ C += bj.invInertia * this.rjxt * this.rjxt;
+
+ return C;
+};
+
+FrictionEquation.prototype.computeGWlambda = function(){
+ var bi = this.bi,
+ bj = this.bj,
+ t = this.t,
+ dot = vec2.dot;
+
+ return dot(t, bj.vlambda) + bj.wlambda * this.rjxt - bi.wlambda * this.rixt - dot(t, bi.vlambda);
+};
+
+var FrictionEquation_addToWlambda_tmp = vec2.create();
+FrictionEquation.prototype.addToWlambda = function(deltalambda){
+ var bi = this.bi,
+ bj = this.bj,
+ t = this.t,
+ tmp = FrictionEquation_addToWlambda_tmp,
+ imMat1 = tmpMat1,
+ imMat2 = tmpMat2;
+
+ mat2.identity(imMat1);
+ mat2.identity(imMat2);
+ imMat1[0] = imMat1[3] = bi.invMass;
+ imMat2[0] = imMat2[3] = bj.invMass;
+
+ vec2.scale(tmp,vec2.transformMat2(tmp,t,imMat1),-deltalambda);
+ //vec2.scale(tmp, t, -bi.invMass * deltalambda); //t.mult(invMassi * deltalambda, tmp);
+ vec2.add(bi.vlambda, bi.vlambda, tmp); //bi.vlambda.vsub(tmp,bi.vlambda);
+
+ vec2.scale(tmp,vec2.transformMat2(tmp,t,imMat2),deltalambda);
+ //vec2.scale(tmp, t, bj.invMass * deltalambda); //t.mult(invMassj * deltalambda, tmp);
+ vec2.add(bj.vlambda, bj.vlambda, tmp); //bj.vlambda.vadd(tmp,bj.vlambda);
+
+ bi.wlambda -= bi.invInertia * this.rixt * deltalambda;
+ bj.wlambda += bj.invInertia * this.rjxt * deltalambda;
+};
diff --git a/src/physics/advanced/constraints/PointToPointConstraint.js b/src/physics/advanced/constraints/PointToPointConstraint.js
new file mode 100644
index 00000000..6fd343a1
--- /dev/null
+++ b/src/physics/advanced/constraints/PointToPointConstraint.js
@@ -0,0 +1,94 @@
+var Constraint = require('./Constraint')
+, ContactEquation = require('./ContactEquation')
+, RotationalVelocityEquation = require('./RotationalVelocityEquation')
+, vec2 = require('../math/vec2')
+
+module.exports = PointToPointConstraint;
+
+/**
+ * Connects two bodies at given offset points
+ * @class PointToPointConstraint
+ * @constructor
+ * @author schteppe
+ * @param {Body} bodyA
+ * @param {Float32Array} pivotA The point relative to the center of mass of bodyA which bodyA is constrained to.
+ * @param {Body} bodyB Body that will be constrained in a similar way to the same point as bodyA. We will therefore get sort of a link between bodyA and bodyB. If not specified, bodyA will be constrained to a static point.
+ * @param {Float32Array} pivotB See pivotA.
+ * @param {Number} maxForce The maximum force that should be applied to constrain the bodies.
+ * @extends {Constraint}
+ * @todo Ability to specify world points
+ */
+function PointToPointConstraint(bodyA, pivotA, bodyB, pivotB, maxForce){
+ Constraint.call(this,bodyA,bodyB);
+
+ maxForce = typeof(maxForce)!="undefined" ? maxForce : 1e7;
+
+ this.pivotA = pivotA;
+ this.pivotB = pivotB;
+
+ // Equations to be fed to the solver
+ var eqs = this.equations = [
+ new ContactEquation(bodyA,bodyB), // Normal
+ new ContactEquation(bodyA,bodyB), // Tangent
+ ];
+
+ var normal = eqs[0];
+ var tangent = eqs[1];
+
+ tangent.minForce = normal.minForce = -maxForce;
+ tangent.maxForce = normal.maxForce = maxForce;
+
+ this.motorEquation = null;
+}
+PointToPointConstraint.prototype = new Constraint();
+
+PointToPointConstraint.prototype.update = function(){
+ var bodyA = this.bodyA,
+ bodyB = this.bodyB,
+ pivotA = this.pivotA,
+ pivotB = this.pivotB,
+ eqs = this.equations,
+ normal = eqs[0],
+ tangent= eqs[1];
+
+ vec2.subtract(normal.ni, bodyB.position, bodyA.position);
+ vec2.normalize(normal.ni,normal.ni);
+ vec2.rotate(normal.ri, pivotA, bodyA.angle);
+ vec2.rotate(normal.rj, pivotB, bodyB.angle);
+
+ vec2.rotate(tangent.ni, normal.ni, Math.PI / 2);
+ vec2.copy(tangent.ri, normal.ri);
+ vec2.copy(tangent.rj, normal.rj);
+};
+
+/**
+ * Enable the rotational motor
+ * @method enableMotor
+ */
+PointToPointConstraint.prototype.enableMotor = function(){
+ if(this.motorEquation) return;
+ this.motorEquation = new RotationalVelocityEquation(this.bodyA,this.bodyB);
+ this.equations.push(this.motorEquation);
+};
+
+/**
+ * Disable the rotational motor
+ * @method disableMotor
+ */
+PointToPointConstraint.prototype.disableMotor = function(){
+ if(!this.motorEquation) return;
+ var i = this.equations.indexOf(this.motorEquation);
+ this.motorEquation = null;
+ this.equations.splice(i,1);
+};
+
+/**
+ * Set the speed of the rotational constraint motor
+ * @method setMotorSpeed
+ * @param {Number} speed
+ */
+PointToPointConstraint.prototype.setMotorSpeed = function(speed){
+ if(!this.motorEquation) return;
+ var i = this.equations.indexOf(this.motorEquation);
+ this.equations[i].relativeVelocity = speed;
+};
diff --git a/src/physics/advanced/constraints/PrismaticConstraint.js b/src/physics/advanced/constraints/PrismaticConstraint.js
new file mode 100644
index 00000000..ff03a767
--- /dev/null
+++ b/src/physics/advanced/constraints/PrismaticConstraint.js
@@ -0,0 +1,83 @@
+var Constraint = require('./Constraint')
+, ContactEquation = require('./ContactEquation')
+, vec2 = require('../math/vec2')
+
+module.exports = PrismaticConstraint;
+
+/**
+ * Constraint that only allows translation along a line between the bodies, no rotation
+ *
+ * @class PrismaticConstraint
+ * @constructor
+ * @author schteppe
+ * @param {Body} bodyA
+ * @param {Body} bodyB
+ * @param {Object} options
+ * @param {Number} options.maxForce
+ * @param {Array} options.worldAxis
+ * @param {Array} options.localAxisA
+ * @param {Array} options.localAxisB
+ * @extends {Constraint}
+ */
+function PrismaticConstraint(bodyA,bodyB,options){
+ options = options || {};
+ Constraint.call(this,bodyA,bodyB);
+
+ var maxForce = this.maxForce = typeof(options.maxForce)==="undefined" ? options.maxForce : 1e6;
+
+ // Equations to be fed to the solver
+ var eqs = this.equations = [
+ new ContactEquation(bodyA,bodyB), // Tangent for bodyA
+ new ContactEquation(bodyB,bodyA), // Tangent for bodyB
+ ];
+
+ var tangentA = eqs[0],
+ tangentB = eqs[1];
+
+ tangentA.minForce = tangentB.minForce = -maxForce;
+ tangentA.maxForce = tangentB.maxForce = maxForce;
+
+ var worldAxis = vec2.create();
+ if(options.worldAxis){
+ vec2.copy(worldAxis, options.worldAxis);
+ } else {
+ vec2.sub(worldAxis, bodyB.position, bodyA.position);
+ }
+ vec2.normalize(worldAxis,worldAxis);
+
+ // Axis that is local in each body
+ this.localAxisA = vec2.create();
+ this.localAxisB = vec2.create();
+ if(options.localAxisA) vec2.copy(this.localAxisA, options.localAxisA);
+ else vec2.rotate(this.localAxisA, worldAxis, -bodyA.angle);
+
+ if(options.localAxisB) vec2.copy(this.localAxisB, options.localAxisB);
+ else vec2.rotate(this.localAxisB, worldAxis, -bodyB.angle);
+}
+
+PrismaticConstraint.prototype = new Constraint();
+
+/**
+ * Update the constraint equations. Should be done if any of the bodies changed position, before solving.
+ * @method update
+ */
+PrismaticConstraint.prototype.update = function(){
+ var tangentA = this.equations[0],
+ tangentB = this.equations[1],
+ bodyA = this.bodyA,
+ bodyB = this.bodyB;
+
+ // Get tangent directions
+ vec2.rotate(tangentA.ni, this.localAxisA, bodyA.angle - Math.PI/2);
+ vec2.rotate(tangentB.ni, this.localAxisB, bodyB.angle + Math.PI/2);
+
+ // Get distance vector
+ var dist = vec2.create();
+ vec2.sub(dist, bodyB.position, bodyA.position);
+ vec2.scale(tangentA.ri, tangentA.ni, -vec2.dot(tangentA.ni, dist));
+ vec2.scale(tangentB.ri, tangentB.ni, vec2.dot(tangentB.ni, dist));
+ vec2.add(tangentA.rj, tangentA.ri, dist);
+ vec2.sub(tangentB.rj, tangentB.ri, dist);
+ vec2.set(tangentA.ri, 0, 0);
+ vec2.set(tangentB.ri, 0, 0);
+};
diff --git a/src/physics/advanced/constraints/RotationalVelocityEquation.js b/src/physics/advanced/constraints/RotationalVelocityEquation.js
new file mode 100644
index 00000000..346d5a69
--- /dev/null
+++ b/src/physics/advanced/constraints/RotationalVelocityEquation.js
@@ -0,0 +1,70 @@
+var Equation = require("./Equation"),
+ vec2 = require('../math/vec2');
+
+module.exports = RotationalVelocityEquation;
+
+/**
+ * Syncs rotational velocity of two bodies, or sets a relative velocity (motor).
+ *
+ * @class RotationalVelocityEquation
+ * @constructor
+ * @extends Equation
+ * @param {Body} bi
+ * @param {Body} bj
+ */
+function RotationalVelocityEquation(bi,bj){
+ Equation.call(this,bi,bj,-1e6,1e6);
+ this.relativeVelocity = 1;
+ this.ratio = 1;
+};
+RotationalVelocityEquation.prototype = new Equation();
+RotationalVelocityEquation.prototype.constructor = RotationalVelocityEquation;
+RotationalVelocityEquation.prototype.computeB = function(a,b,h){
+ var bi = this.bi,
+ bj = this.bj,
+ vi = bi.velocity,
+ wi = bi.angularVelocity,
+ taui = bi.angularForce,
+ vj = bj.velocity,
+ wj = bj.angularVelocity,
+ tauj = bj.angularForce,
+ invIi = bi.invInertia,
+ invIj = bj.invInertia,
+ Gq = 0,
+ GW = this.ratio * wj - wi + this.relativeVelocity,
+ GiMf = invIj*tauj - invIi*taui;
+
+ var B = - Gq * a - GW * b - h*GiMf;
+
+ return B;
+};
+
+// Compute C = GMG+eps in the SPOOK equation
+RotationalVelocityEquation.prototype.computeC = function(eps){
+ var bi = this.bi,
+ bj = this.bj;
+
+ var C = bi.invInertia + bj.invInertia + eps;
+
+ return C;
+};
+var computeGWlambda_ulambda = vec2.create();
+RotationalVelocityEquation.prototype.computeGWlambda = function(){
+ var bi = this.bi,
+ bj = this.bj;
+
+ var GWlambda = bj.wlambda - bi.wlambda;
+
+ return GWlambda;
+};
+
+var addToWlambda_temp = vec2.create();
+RotationalVelocityEquation.prototype.addToWlambda = function(deltalambda){
+ var bi = this.bi,
+ bj = this.bj;
+
+ // Add to angular velocity
+ bi.wlambda -= bi.invInertia * deltalambda;
+ bj.wlambda += bj.invInertia * deltalambda;
+};
+
diff --git a/src/physics/advanced/events/EventEmitter.js b/src/physics/advanced/events/EventEmitter.js
new file mode 100644
index 00000000..576d2024
--- /dev/null
+++ b/src/physics/advanced/events/EventEmitter.js
@@ -0,0 +1,84 @@
+/**
+ * Base class for objects that dispatches events.
+ * @class EventEmitter
+ * @constructor
+ */
+var EventEmitter = function () {}
+
+module.exports = EventEmitter;
+
+EventEmitter.prototype = {
+ constructor: EventEmitter,
+
+ /**
+ * Add an event listener
+ * @method on
+ * @param {String} type
+ * @param {Function} listener
+ * @return {EventEmitter} The self object, for chainability.
+ */
+ on: function ( type, listener ) {
+ if ( this._listeners === undefined ) this._listeners = {};
+ var listeners = this._listeners;
+ if ( listeners[ type ] === undefined ) {
+ listeners[ type ] = [];
+ }
+ if ( listeners[ type ].indexOf( listener ) === - 1 ) {
+ listeners[ type ].push( listener );
+ }
+ return this;
+ },
+
+ /**
+ * Check if an event listener is added
+ * @method has
+ * @param {String} type
+ * @param {Function} listener
+ * @return {Boolean}
+ */
+ has: function ( type, listener ) {
+ if ( this._listeners === undefined ) return false;
+ var listeners = this._listeners;
+ if ( listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1 ) {
+ return true;
+ }
+ return false;
+ },
+
+ /**
+ * Remove an event listener
+ * @method off
+ * @param {String} type
+ * @param {Function} listener
+ * @return {EventEmitter} The self object, for chainability.
+ */
+ off: function ( type, listener ) {
+ if ( this._listeners === undefined ) return;
+ var listeners = this._listeners;
+ var index = listeners[ type ].indexOf( listener );
+ if ( index !== - 1 ) {
+ listeners[ type ].splice( index, 1 );
+ }
+ return this;
+ },
+
+ /**
+ * Emit an event.
+ * @method emit
+ * @param {Object} event
+ * @param {String} event.type
+ * @return {EventEmitter} The self object, for chainability.
+ */
+ emit: function ( event ) {
+ if ( this._listeners === undefined ) return;
+ var listeners = this._listeners;
+ var listenerArray = listeners[ event.type ];
+ if ( listenerArray !== undefined ) {
+ event.target = this;
+ for ( var i = 0, l = listenerArray.length; i < l; i ++ ) {
+ listenerArray[ i ].call( this, event );
+ }
+ }
+ return this;
+ }
+};
diff --git a/src/physics/advanced/joints/Angle.js b/src/physics/advanced/joints/Angle.js
deleted file mode 100644
index 10b09c8d..00000000
--- a/src/physics/advanced/joints/Angle.js
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
-* Copyright (c) 2012 Ju Hyung Lee
-*
-* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
-* and associated documentation files (the "Software"), to deal in the Software without
-* restriction, including without limitation the rights to use, copy, modify, merge, publish,
-* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
-* Software is furnished to do so, subject to the following conditions:
-*
-* The above copyright notice and this permission notice shall be included in all copies or
-* substantial portions of the Software.
-*
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
-* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-//-------------------------------------------------------------------------------------------------
-// Angle Joint
-//
-// C = a2 - a1 - refAngle
-// Cdot = w2 - w1
-// J = [0, -1, 0, 1]
-//
-// impulse = JT * lambda = [ 0, -lambda, 0, lambda ]
-//-------------------------------------------------------------------------------------------------
-
-AngleJoint = function(body1, body2) {
- Joint.call(this, Joint.TYPE_ANGLE, body1, body2, true);
-
- this.anchor1 = new vec2(0, 0);
- this.anchor2 = new vec2(0, 0);
-
- // Initial angle difference
- this.refAngle = body2.a - body1.a;
-
- // Accumulated lambda for angular velocity constraint
- this.lambda_acc = 0;
-}
-
-AngleJoint.prototype = new Joint;
-AngleJoint.prototype.constructor = AngleJoint;
-
-AngleJoint.prototype.setWorldAnchor1 = function(anchor1) {
- this.anchor1 = new vec2(0, 0);
-}
-
-AngleJoint.prototype.setWorldAnchor2 = function(anchor2) {
- this.anchor2 = new vec2(0, 0);
-}
-
-AngleJoint.prototype.serialize = function() {
- return {
- "type": "AngleJoint",
- "body1": this.body1.id,
- "body2": this.body2.id,
- "collideConnected": this.collideConnected
- };
-}
-
-AngleJoint.prototype.initSolver = function(dt, warmStarting) {
- var body1 = this.body1;
- var body2 = this.body2;
-
- // Max impulse
- this.maxImpulse = this.maxForce * dt;
-
- // invEM = J * invM * JT
- var em_inv = body1.i_inv + body2.i_inv;
- this.em = em_inv == 0 ? 0 : 1 / em_inv;
-
- if (warmStarting) {
- // Apply cached constraint impulses
- // V += JT * lambda * invM
- body1.w -= this.lambda_acc * body1.i_inv;
- body2.w += this.lambda_acc * body2.i_inv;
- }
- else {
- this.lambda_acc = 0;
- }
-}
-
-AngleJoint.prototype.solveVelocityConstraints = function() {
- var body1 = this.body1;
- var body2 = this.body2;
-
- // Compute lambda for velocity constraint
- // Solve J * invM * JT * lambda = -J * V
- var cdot = body2.w - body1.w;
- var lambda = -this.em * cdot;
-
- // Accumulate lambda
- this.lambda_acc += lambda;
-
- // Apply constraint impulses
- // V += JT * lambda * invM
- body1.w -= lambda * body1.i_inv;
- body2.w += lambda * body2.i_inv;
-}
-
-AngleJoint.prototype.solvePositionConstraints = function() {
- var body1 = this.body1;
- var body2 = this.body2;
-
- // Position (angle) constraint
- var c = body2.a - body1.a - this.refAngle;
- var correction = Math.clamp(c, -Joint.MAX_ANGULAR_CORRECTION, Joint.MAX_ANGULAR_CORRECTION);
-
- // Compute lambda for position (angle) constraint
- // Solve J * invM * JT * lambda = -C / dt
- var lambda_dt = this.em * (-correction);
-
- // Apply constraint impulses
- // impulse = JT * lambda
- // X += impulse * invM * dt
- body1.a -= lambda_dt * body1.i_inv;
- body2.a += lambda_dt * body2.i_inv;
-
- return Math.abs(c) < Joint.ANGULAR_SLOP;
-}
-
-AngleJoint.prototype.getReactionForce = function(dt_inv) {
- return vec2.zero;
-}
-
-AngleJoint.prototype.getReactionTorque = function(dt_inv) {
- return this.lambda_acc * dt_inv;
-}
\ No newline at end of file
diff --git a/src/physics/advanced/joints/Distance.js b/src/physics/advanced/joints/Distance.js
deleted file mode 100644
index e016d80f..00000000
--- a/src/physics/advanced/joints/Distance.js
+++ /dev/null
@@ -1,522 +0,0 @@
-/*
-* Copyright (c) 2012 Ju Hyung Lee
-*
-* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
-* and associated documentation files (the "Software"), to deal in the Software without
-* restriction, including without limitation the rights to use, copy, modify, merge, publish,
-* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
-* Software is furnished to do so, subject to the following conditions:
-*
-* The above copyright notice and this permission notice shall be included in all copies or
-* substantial portions of the Software.
-*
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
-* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-//-------------------------------------------------------------------------------------------------
-// Distance Joint
-//
-// d = p2 - p1
-// u = d / norm(d)
-// C = norm(d) - l
-// C = sqrt(dot(d, d)) - l
-// Cdot = dot(u, v2 + cross(w2, r2) - v1 - cross(w1, r1))
-// = -dot(u, v1) - dot(w1, cross(r1, u)) + dot(u, v2) + dot(w2, cross(r2, u))
-// J = [ -u, -cross(r1, u), u, cross(r2, u) ]
-//
-// impulse = JT * lambda = [ -u * lambda, -cross(r1, u) * lambda, u * lambda, cross(r1, u) * lambda ]
-//-------------------------------------------------------------------------------------------------
-
-DistanceJoint = function(body1, body2, anchor1, anchor2) {
- Joint.call(this, Joint.TYPE_DISTANCE, body1, body2, true);
-
- // Local anchor points
- this.anchor1 = this.body1.getLocalPoint(anchor1);
- this.anchor2 = this.body2.getLocalPoint(anchor2);
-
- // Rest length
- this.restLength = vec2.dist(anchor1, anchor2);
-
- // Soft constraint coefficients
- this.gamma = 0;
- this.beta_c = 0;
-
- // Spring coefficients
- this.frequencyHz = 0;
- this.dampingRatio = 0;
-
- // Accumulated impulse
- this.lambda_acc = 0;
-}
-
-DistanceJoint.prototype = new Joint;
-DistanceJoint.prototype.constructor = DistanceJoint;
-
-DistanceJoint.prototype.setWorldAnchor1 = function(anchor1) {
- this.anchor1 = this.body1.getLocalPoint(anchor1);
-
- this.restLength = vec2.dist(anchor1, this.getWorldAnchor2());
-}
-
-DistanceJoint.prototype.setWorldAnchor2 = function(anchor2) {
- this.anchor2 = this.body2.getLocalPoint(anchor2);
-
- this.restLength = vec2.dist(anchor2, this.getWorldAnchor1());
-}
-
-DistanceJoint.prototype.serialize = function() {
- return {
- "type": "DistanceJoint",
- "body1": this.body1.id,
- "body2": this.body2.id,
- "anchor1": this.body1.getWorldPoint(this.anchor1),
- "anchor2": this.body2.getWorldPoint(this.anchor2),
- "collideConnected": this.collideConnected,
- "maxForce": this.maxForce,
- "breakable": this.breakable,
- "frequencyHz": this.frequencyHz,
- "dampingRatio": this.dampingRatio
- };
-}
-
-DistanceJoint.prototype.setSpringFrequencyHz = function(frequencyHz) {
- // NOTE: frequencyHz should be limited to under 4 times time steps
- this.frequencyHz = frequencyHz;
-}
-
-DistanceJoint.prototype.setSpringDampingRatio = function(dampingRatio) {
- this.dampingRatio = dampingRatio;
-}
-
-DistanceJoint.prototype.initSolver = function(dt, warmStarting) {
- var body1 = this.body1;
- var body2 = this.body2;
-
- // Max impulse
- this.maxImpulse = this.maxForce * dt;
-
- // Transformed r1, r2
- this.r1 = body1.xf.rotate(vec2.sub(this.anchor1, body1.centroid));
- this.r2 = body2.xf.rotate(vec2.sub(this.anchor2, body2.centroid));
-
- // Delta vector between two world anchors
- var d = vec2.sub(vec2.add(body2.p, this.r2), vec2.add(body1.p, this.r1));
-
- // Distance between two anchors
- var dist = d.length();
-
- // Unit delta vector
- if (dist > Joint.LINEAR_SLOP) {
- this.u = vec2.scale(d, 1 / dist);
- }
- else {
- this.u = vec2.zero;
- }
-
- // s1, s2
- this.s1 = vec2.cross(this.r1, this.u);
- this.s2 = vec2.cross(this.r2, this.u);
-
- // invEM = J * invM * JT
- var em_inv = body1.m_inv + body2.m_inv + body1.i_inv * this.s1 * this.s1 + body2.i_inv * this.s2 * this.s2;
- this.em = em_inv == 0 ? 0 : 1 / em_inv;
-
- // Compute soft constraint parameters
- if (this.frequencyHz > 0) {
- // Frequency
- var omega = 2 * Math.PI * this.frequencyHz;
-
- // Spring stiffness
- var k = this.em * omega * omega;
-
- // Damping coefficient
- var c = this.em * 2 * this.dampingRatio * omega;
-
- // Soft constraint formulas
- // gamma and beta are divided by dt to reduce computation
- this.gamma = (c + k * dt) * dt;
- this.gamma = this.gamma == 0 ? 0 : 1 / this.gamma;
- var beta = dt * k * this.gamma;
-
- // Position constraint
- var pc = dist - this.restLength;
- this.beta_c = beta * pc;
-
- // invEM = invEM + gamma * I (to reduce calculation)
- em_inv = em_inv + this.gamma;
- this.em = em_inv == 0 ? 0 : 1 / em_inv;
- }
- else {
- this.gamma = 0;
- this.beta_c = 0;
- }
-
- if (warmStarting) {
- // linearImpulse = JT * lambda
- var impulse = vec2.scale(this.u, this.lambda_acc);
-
- // Apply cached constraint impulses
- // V += JT * lambda * invM
- body1.v.mad(impulse, -body1.m_inv);
- body1.w -= this.s1 * this.lambda_acc * body1.i_inv;
-
- body2.v.mad(impulse, body2.m_inv);
- body2.w += this.s2 * this.lambda_acc * body2.i_inv;
- }
- else {
- this.lambda_acc = 0;
- }
-}
-
-DistanceJoint.prototype.solveVelocityConstraints = function() {
- var body1 = this.body1;
- var body2 = this.body2;
-
- // Compute lambda for velocity constraint
- // Solve J * invM * JT * lambda = -(J * V + beta * C + gamma * (lambda_acc + lambda))
- var cdot = this.u.dot(vec2.sub(body2.v, body1.v)) + this.s2 * body2.w - this.s1 * body1.w;
- var soft = this.beta_c + this.gamma * this.lambda_acc;
- var lambda = -this.em * (cdot + soft);
-
- // Accumulate lambda
- this.lambda_acc += lambda;
-
- // linearImpulse = JT * lambda
- var impulse = vec2.scale(this.u, lambda);
-
- // Apply constraint impulses
- // V += JT * lambda * invM
- body1.v.mad(impulse, -body1.m_inv);
- body1.w -= this.s1 * lambda * body1.i_inv;
-
- body2.v.mad(impulse, body2.m_inv);
- body2.w += this.s2 * lambda * body2.i_inv;
-}
-
-DistanceJoint.prototype.solvePositionConstraints = function() {
- // There is no position correction for soft constraints
- if (this.frequencyHz > 0) {
- return true;
- }
-
- var body1 = this.body1;
- var body2 = this.body2;
-
- // Transformed r1, r2
- var r1 = vec2.rotate(vec2.sub(this.anchor1, body1.centroid), body1.a);
- var r2 = vec2.rotate(vec2.sub(this.anchor2, body2.centroid), body2.a);
-
- // Delta vector between two anchors
- var d = vec2.sub(vec2.add(body2.p, r2), vec2.add(body1.p, r1));
-
- // Distance between two anchors
- var dist = d.length();
-
- // Unit delta vector
- var u = vec2.scale(d, 1 / dist);
-
- // Position constraint
- var c = dist - this.restLength;
- var correction = Math.clamp(c, -Joint.MAX_LINEAR_CORRECTION, Joint.MAX_LINEAR_CORRECTION);
-
- // Compute lambda for correction
- // Solve J * invM * JT * lambda = -C / dt
- var s1 = vec2.cross(r1, u);
- var s2 = vec2.cross(r2, u);
- var em_inv = body1.m_inv + body2.m_inv + body1.i_inv * s1 * s1 + body2.i_inv * s2 * s2;
- var lambda_dt = em_inv == 0 ? 0 : -correction / em_inv;
-
- // Apply constraint impulses
- // impulse = JT * lambda
- // X += impulse * invM * dt
- var impulse_dt = vec2.scale(u, lambda_dt);
-
- body1.p.mad(impulse_dt, -body1.m_inv);
- body1.a -= s1 * lambda_dt * body1.i_inv;
-
- body2.p.mad(impulse_dt, body2.m_inv);
- body2.a += s2 * lambda_dt * body2.i_inv;
-
- return Math.abs(c) < Joint.LINEAR_SLOP;
-}
-
-DistanceJoint.prototype.getReactionForce = function(dt_inv) {
- return vec2.scale(this.u, this.lambda_acc * dt_inv);
-}
-
-DistanceJoint.prototype.getReactionTorque = function(dt_inv) {
- return 0;
-}
-/*
-//------------------------------------------
-// MaxDistance Joint
-//------------------------------------------
-
-MaxDistanceJoint = function(body1, body2, anchor1, anchor2, minDist, maxDist) {
- Joint.call(this, body1, body2, true);
-
- // Local anchor points
- this.anchor1 = body1.getLocalPoint(anchor1);
- this.anchor2 = body2.getLocalPoint(anchor2);
-
- this.minDist = minDist || 0;
- this.maxDist = maxDist;
-
- if (maxDist == undefined) {
- var p1 = vec2.add(vec2.rotate(vec2.sub(this.anchor1, body1.centroid), body1.a), body1.p);
- var p2 = vec2.add(vec2.rotate(vec2.sub(this.anchor2, body2.centroid), body2.a), body2.p);
-
- this.maxDist = vec2.dist(p1, p2);
- }
-
- // accumulated impulse
- this.lambda_acc = 0;
-}
-
-MaxDistanceJoint.prototype = new Joint;
-MaxDistanceJoint.prototype.constructor = MaxDistanceJoint;
-
-MaxDistanceJoint.prototype.initSolver = function(dt, warmStarting) {
- var body1 = this.body1;
- var body2 = this.body2;
-
- // max impulse
- this.maxImpulse = this.maxForce * dt;
-
- // transformed r1, r2
- this.r1 = body1.xf.rotate(vec2.sub(this.anchor1, body1.centroid));
- this.r2 = body2.xf.rotate(vec2.sub(this.anchor2, body2.centroid));
-
- // delta vector between two anchors
- var d = vec2.sub(vec2.add(body2.p, this.r2), vec2.add(body1.p, this.r1));
-
- // distance between two anchors
- var dist = d.length();
-
- // unit delta vector
- this.u = vec2.scale(d, 1 / dist);
-
- // s1, s2
- this.s1 = vec2.cross(this.r1, this.u);
- this.s2 = vec2.cross(this.r2, this.u);
-
- // invEM = J * invM * JT
- var em_inv = body1.m_inv + body2.m_inv + body1.i_inv * this.s1 * this.s1 + body2.i_inv * this.s2 * this.s2;
- this.em = em_inv == 0 ? 0 : 1 / em_inv;
-
- // initial error
- this.initial_err = 0;
- if (dist < this.minDist) {
- this.initial_err = dist - this.minDist;
- }
- else if (dist > this.maxDist) {
- this.initial_err = dist - this.maxDist;
- }
-
- if (this.initial_err == 0) {
- this.lambda_acc = 0;
- }
-
- if (warmStarting) {
- // apply cached impulses
- // V += JT * lambda
- var impulse = vec2.scale(this.u, this.lambda_acc);
-
- body1.v.mad(impulse, -body1.m_inv);
- body1.w -= this.s1 * this.lambda_acc * body1.i_inv;
-
- body2.v.mad(impulse, body2.m_inv);
- body2.w += this.s2 * this.lambda_acc * body2.i_inv;
- }
- else {
- this.lambda_acc = 0;
- }
-}
-
-MaxDistanceJoint.prototype.solveVelocityConstraints = function() {
- if (this.initial_err == 0)
- return;
-
- var body1 = this.body1;
- var body2 = this.body2;
-
- // compute lambda for velocity constraint
- // solve J * invM * JT * lambda = -J * V
- var cdot = this.u.dot(vec2.sub(body2.v, body1.v)) + this.s2 * body2.w - this.s1 * body1.w;
- var lambda = -this.em * cdot;
-
- // accumulate lambda for velocity constraint
- this.lambda_acc += lambda;
-
- // apply impulses
- // V += JT * lambda
- var impulse = vec2.scale(this.u, lambda);
-
- body1.v.mad(impulse, -body1.m_inv);
- body1.w -= this.s1 * lambda * body1.i_inv;
-
- body2.v.mad(impulse, body2.m_inv);
- body2.w += this.s2 * lambda * body2.i_inv;
-}
-
-MaxDistanceJoint.prototype.solvePositionConstraints = function() {
- if (this.initial_err == 0)
- return;
-
- var body1 = this.body1;
- var body2 = this.body2;
-
- // transformed r1, r2
- var r1 = vec2.rotate(vec2.sub(this.anchor1, body1.centroid), body1.a);
- var r2 = vec2.rotate(vec2.sub(this.anchor2, body2.centroid), body2.a);
-
- // World Center points
- var pc1 = vec2.add(body1.p, body1.centroid);
- var pc2 = vec2.add(body2.p, body2.centroid);
-
- // delta vector between two anchors
- var d = vec2.sub(vec2.add(pc2, r2), vec2.add(pc1, r1));
-
- // distance between two anchors
- var dist = d.length();
-
- // unit delta vector
- u = vec2.scale(d, 1 / dist);
-
- // position constraint
- var c = 0;
- if (dist < this.minDist) {
- c = dist - this.minDist;
- }
- else if (dist > this.maxDist) {
- c = dist - this.maxDist;
- }
- var correction = Math.clamp(c, -Joint.MAX_LINEAR_CORRECTION, Joint.MAX_LINEAR_CORRECTION);
-
- // compute lambda for position constraint
- // solve J * invM * JT * lambda = -C / dt
- var s1 = vec2.cross(r1, u);
- var s2 = vec2.cross(r2, u);
- var em_inv = body1.m_inv + body2.m_inv + body1.i_inv * s1 * s1 + body2.i_inv * s2 * s2;
- var lambda_dt = em_inv == 0 ? 0 : -correction / em_inv;
-
- // apply impulses
- // X += JT * lambda * dt
- var impulse_dt = vec2.scale(u, lambda_dt);
-
- body1.p.mad(impulse_dt, -body1.m_inv);
- body1.a -= s1 * lambda_dt * body1.i_inv;
-
- body2.p.mad(impulse_dt, body2.m_inv);
- body2.a += s2 * lambda_dt * body2.i_inv;
-
- return Math.abs(c) < Joint.LINEAR_SLOP;
-}
-
-MaxDistanceJoint.prototype.getReactionForce = function(dt_inv) {
- return vec2.scale(this.u, this.lambda_acc * dt_inv);
-}
-
-MaxDistanceJoint.prototype.getReactionTorque = function(dt_inv) {
- return 0;
-}
-
-//------------------------------------------
-// Damped Spring (Deprecated)
-//------------------------------------------
-
-SpringJoint = function(body1, body2, anchor1, anchor2, restLength, stiffness, damping) {
- Joint.call(this, body1, body2, true);
-
- // local anchor points
- this.anchor1 = anchor1;
- this.anchor2 = anchor2;
-
- this.restLength = restLength;
- this.stiffness = stiffness;
- this.damping = damping;
-}
-
-SpringJoint.prototype = new Joint;
-SpringJoint.prototype.constructor = SpringJoint;
-
-SpringJoint.prototype.initSolver = function(dt, warmStarting) {
- var body1 = this.body1;
- var body2 = this.body2;
-
- // transformed r1, r2
- this.r1 = body1.xf.rotate(vec2.sub(this.anchor1, body2.centroid));
- this.r2 = body2.xf.rotate(vec2.sub(this.anchor2, body2.centroid));
-
- var d = vec2.sub(vec2.add(body2.p, this.r2), vec2.add(body1.p, this.r1));
- var dist = d.length();
-
- this.u = vec2.scale(d, 1 / dist);
-
- // s1, s2
- this.s1 = vec2.cross(this.r1, this.u);
- this.s2 = vec2.cross(this.r2, this.u);
-
- // invEM = J * invM * JT
- var em_inv = body1.m_inv + body2.m_inv + body1.i_inv * this.s1 * this.s1 + body2.i_inv * this.s2 * this.s2;
- this.em = em_inv == 0 ? 0 : 1 / em_inv;
-
- //
- this.target_rnv = 0;
- this.v_coeff = 1.0 - Math.exp(-this.damping * dt * em_inv);
-
- // apply spring force
- var spring_f = (this.restLength - dist) * this.stiffness;
- this.spring_impulse = spring_f * dt;
-
- // apply impulses
- // V += JT * lambda
- var impulse = vec2.scale(this.u, this.spring_impulse);
-
- body1.v.mad(impulse, -body1.m_inv);
- body1.w -= this.s1 * this.spring_impulse * body1.i_inv;
-
- body2.v.mad(impulse, body2.m_inv);
- body2.w += this.s2 * this.spring_impulse * body2.i_inv;
-}
-
-SpringJoint.prototype.solveVelocityConstraints = function() {
- var body1 = this.body1;
- var body2 = this.body2;
-
- // compute lambda for velocity constraint
- // solve J * invM * JT * lambda = -J * V
- var cdot = this.u.dot(vec2.sub(body2.v, body1.v)) + this.s2 * body2.w - this.s1 * body1.w;
- var rnv = cdot + this.target_rnv;
-
- // compute velocity loss from drag
- var v_damp = rnv * this.v_coeff;
- this.target_rnv = -rnv + v_damp;
- var lambda = -this.em * v_damp;
-
- // apply impulses
- // V += JT * lambda
- var impulse = vec2.scale(this.u, lambda);
-
- body1.v.mad(impulse, -body1.m_inv);
- body1.w -= this.s1 * lambda * body1.i_inv;
-
- body2.v.mad(impulse, body2.m_inv);
- body2.w += this.s2 * lambda * body2.i_inv;
-}
-
-SpringJoint.prototype.solvePositionConstraints = function() {
- return true;
-}
-
-SpringJoint.prototype.getReactionForce = function(dt_inv) {
- return vec2.scale(this.u, this.spring_impulse * dt_inv);
-}
-
-SpringJoint.prototype.getReactionTorque = function(dt_inv) {
- return 0;
-}*/
diff --git a/src/physics/advanced/joints/Mouse.js b/src/physics/advanced/joints/Mouse.js
deleted file mode 100644
index f900afe5..00000000
--- a/src/physics/advanced/joints/Mouse.js
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
-* Copyright (c) 2012 Ju Hyung Lee
-*
-* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
-* and associated documentation files (the "Software"), to deal in the Software without
-* restriction, including without limitation the rights to use, copy, modify, merge, publish,
-* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
-* Software is furnished to do so, subject to the following conditions:
-*
-* The above copyright notice and this permission notice shall be included in all copies or
-* substantial portions of the Software.
-*
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
-* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-//-------------------------------------------------------------------------------------------------
-// Mouse Joint
-//
-// p = attached point, m = mouse point (constant)
-// C = p - m
-// Cdot = v + cross(w, r)
-// J = [ I, -skew(r) ]
-//
-// impulse = JT * lambda = [ lambda, cross(r2, lambda) ]
-//-------------------------------------------------------------------------------------------------
-
-MouseJoint = function(mouseBody, body, anchor) {
- if (arguments.length == 0)
- return;
-
- Joint.call(this, Joint.TYPE_MOUSE, mouseBody, body, true);
-
- // Local anchor points
- this.anchor1 = this.body1.getLocalPoint(anchor);
- this.anchor2 = this.body2.getLocalPoint(anchor);
-
- // Soft constraint coefficients
- this.gamma = 0;
- this.beta_c = 0;
-
- // Spring coefficients
- this.frequencyHz = 5;
- this.dampingRatio = 0.9;
-
- // Accumulated impulse
- this.lambda_acc = new vec2(0, 0);
-}
-
-MouseJoint.prototype = new Joint;
-MouseJoint.prototype.constructor = MouseJoint;
-
-MouseJoint.prototype.setSpringFrequencyHz = function(frequencyHz) {
- this.frequencyHz = frequencyHz;
-}
-
-MouseJoint.prototype.setSpringDampingRatio = function(dampingRatio) {
- this.dampingRatio = dampingRatio;
-}
-
-MouseJoint.prototype.initSolver = function(dt, warmStarting) {
- var body1 = this.body1;
- var body2 = this.body2;
-
- // Max impulse
- this.maxImpulse = this.maxForce * dt;
-
- // Frequency
- var omega = 2 * Math.PI * this.frequencyHz;
-
- // Spring stiffness
- var k = body2.m * (omega * omega);
-
- // Damping coefficient
- var d = body2.m * 2 * this.dampingRatio * omega;
-
- // Soft constraint formulas
- // gamma and beta are divided by dt to reduce computation
- this.gamma = (d + k * dt) * dt;
- this.gamma = this.gamma == 0 ? 0 : 1 / this.gamma;
- var beta = dt * k * this.gamma;
-
- // Transformed r
- this.r2 = body2.xf.rotate(vec2.sub(this.anchor2, body2.centroid));
-
- // invEM = J * invM * JT
- var r2 = this.r2;
- var r2y_i = r2.y * body2.i_inv;
- var k11 = body2.m_inv + r2.y * r2y_i + this.gamma;
- var k12 = -r2.x * r2y_i;
- var k22 = body2.m_inv + r2.x * r2.x * body2.i_inv + this.gamma;
- this.em_inv = new mat2(k11, k12, k12, k22);
-
- // Position constraint
- var c = vec2.sub(vec2.add(body2.p, this.r2), body1.p);
- this.beta_c = vec2.scale(c, beta);
-
- body2.w *= 0.98;
-
- if (warmStarting) {
- // Apply cached constraint impulse
- // V += JT * lambda * invM
- body2.v.mad(this.lambda_acc, body2.m_inv);
- body2.w += vec2.cross(this.r2, this.lambda_acc) * body2.i_inv;
- }
- else {
- this.lambda_acc.set(0, 0);
- }
-}
-
-MouseJoint.prototype.solveVelocityConstraints = function() {
- var body2 = this.body2;
-
- // Compute lambda for velocity constraint
- // Solve J * invM * JT * lambda = -(J * V + beta * C + gamma * (lambda_acc + lambda))
- // in 2D: cross(w, r) = perp(r) * w
- var cdot = vec2.mad(body2.v, vec2.perp(this.r2), body2.w);
- var soft = vec2.mad(this.beta_c, this.lambda_acc, this.gamma);
- var lambda = this.em_inv.solve(vec2.add(cdot, soft).neg());
-
- // Accumulate lambda
- var lambda_old = this.lambda_acc.duplicate();
- this.lambda_acc.addself(lambda);
- var lsq = this.lambda_acc.lengthsq();
- if (lsq > this.maxImpulse * this.maxImpulse) {
- this.lambda_acc.scale(this.maxImpulse / Math.sqrt(lsq));
- }
- lambda = vec2.sub(this.lambda_acc, lambda_old);
-
- // Apply constraint impulse
- // V += JT * lambda * invM
- body2.v.mad(lambda, body2.m_inv);
- body2.w += vec2.cross(this.r2, lambda) * body2.i_inv;
-}
-
-MouseJoint.prototype.solvePositionConstraints = function() {
- return true;
-}
-
-MouseJoint.prototype.getReactionForce = function(dt_inv) {
- return vec2.scale(this.lambda_acc, dt_inv);
-}
-
-MouseJoint.prototype.getReactionTorque = function(dt_inv) {
- return 0;
-}
-
diff --git a/src/physics/advanced/joints/Prismatic.js b/src/physics/advanced/joints/Prismatic.js
deleted file mode 100644
index 38d4a57c..00000000
--- a/src/physics/advanced/joints/Prismatic.js
+++ /dev/null
@@ -1,241 +0,0 @@
-/*
-* Copyright (c) 2012 Ju Hyung Lee
-*
-* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
-* and associated documentation files (the "Software"), to deal in the Software without
-* restriction, including without limitation the rights to use, copy, modify, merge, publish,
-* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
-* Software is furnished to do so, subject to the following conditions:
-*
-* The above copyright notice and this permission notice shall be included in all copies or
-* substantial portions of the Software.
-*
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
-* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-//-------------------------------------------------------------------------------------------------
-// Prismatic Joint
-//
-// Linear Constraint:
-// d = p2 - p1
-// n = normalize(perp(d))
-// C1 = dot(n, d)
-// C1dot = dot(d, dn/dt) + dot(n dd/dt)
-// = dot(d, cross(w1, n)) + dot(n, v2 + cross(w2, r2) - v1 - cross(w1, r1))
-// = dot(d, cross(w1, n)) + dot(n, v2) + dot(n, cross(w2, r2)) - dot(n, v1) - dot(n, cross(w1, r1))
-// = -dot(n, v1) - dot(cross(d + r1, n), w1) + dot(n, v2) + dot(cross(r2, n), w2)
-// J1 = [ -n, -s1, n, s2 ]
-// s1 = cross(r1 + d, n)
-// s2 = cross(r2, n)
-//
-// Angular Constraint:
-// C2 = a2 - a1 - initial_da
-// C2dot = w2 - w1
-// J2 = [ 0, -1, 0, 1 ]
-//
-// Block Jacobian Matrix:
-// J = [ -n, -s1, n, s2 ]
-// [ 0, -1, 0, 1 ]
-//
-// impulse = JT * lambda = [ -n * lambda_x, -(s1 * lambda_x + lambda_y), n * lambda_x, s2 * lambda_x + lambda_y ]
-//-------------------------------------------------------------------------------------------------
-
-PrismaticJoint = function(body1, body2, anchor1, anchor2) {
- Joint.call(this, Joint.TYPE_PRISMATIC, body1, body2, true);
-
- // Local anchor points
- this.anchor1 = this.body1.getLocalPoint(anchor1);
- this.anchor2 = this.body2.getLocalPoint(anchor2);
-
- var d = vec2.sub(anchor2, anchor1);
-
- // Body1's local line normal
- this.n_local = this.body1.getLocalVector(vec2.normalize(vec2.perp(d)));
-
- this.da = body2.a - body1.a;
-
- // Accumulated lambda
- this.lambda_acc = new vec2(0, 0);
-}
-
-PrismaticJoint.prototype = new Joint;
-PrismaticJoint.prototype.constructor = PrismaticJoint;
-
-PrismaticJoint.prototype.setWorldAnchor1 = function(anchor1) {
- // Local anchor points
- this.anchor1 = this.body1.getLocalPoint(anchor1);
-
- var d = vec2.sub(this.getWorldAnchor2(), anchor1);
-
- // Body1's local line normal
- this.n_local = this.body1.getLocalVector(vec2.normalize(vec2.perp(d)));
-}
-
-PrismaticJoint.prototype.setWorldAnchor2 = function(anchor2) {
- // Local anchor points
- this.anchor2 = this.body2.getLocalPoint(anchor2);
-
- var d = vec2.sub(anchor2, this.getWorldAnchor1());
-
- // Body1's local line normal
- this.n_local = this.body1.getLocalVector(vec2.normalize(vec2.perp(d)));
-}
-
-PrismaticJoint.prototype.serialize = function() {
- return {
- "type": "PrismaticJoint",
- "body1": this.body1.id,
- "body2": this.body2.id,
- "anchor1": this.body1.getWorldPoint(this.anchor1),
- "anchor2": this.body2.getWorldPoint(this.anchor2),
- "collideConnected": this.collideConnected,
- "maxForce": this.maxForce,
- "breakable": this.breakable
- };
-}
-
-PrismaticJoint.prototype.initSolver = function(dt, warmStarting) {
- var body1 = this.body1;
- var body2 = this.body2;
-
- // Max impulse
- this.maxImpulse = this.maxForce * dt;
-
- // Transformed r1, r2
- this.r1 = body1.xf.rotate(vec2.sub(this.anchor1, body1.centroid));
- this.r2 = body2.xf.rotate(vec2.sub(this.anchor2, body2.centroid));
-
- // World anchor points
- var p1 = vec2.add(body1.p, this.r1);
- var p2 = vec2.add(body2.p, this.r2);
-
- // Delta vector between world anchor points
- var d = vec2.sub(p2, p1);
-
- // r1 + d
- this.r1_d = vec2.add(this.r1, d);
-
- // World line normal
- this.n = vec2.normalize(vec2.perp(d));
-
- // s1, s2
- this.s1 = vec2.cross(this.r1_d, this.n);
- this.s2 = vec2.cross(this.r2, this.n);
-
- // invEM = J * invM * JT
- var s1 = this.s1;
- var s2 = this.s2;
- var s1_i = s1 * body1.i_inv;
- var s2_i = s2 * body2.i_inv;
- var k11 = body1.m_inv + body2.m_inv + s1 * s1_i + s2 * s2_i;
- var k12 = s1_i + s2_i;
- var k22 = body1.i_inv + body2.i_inv;
- this.em_inv = new mat2(k11, k12, k12, k22);
-
- if (warmStarting) {
- // linearImpulse = JT * lambda
- var impulse = vec2.scale(this.n, this.lambda_acc.x);
-
- // Apply cached constraint impulses
- // V += JT * lambda * invM
- body1.v.mad(impulse, -body1.m_inv);
- body1.w -= (this.s1 * this.lambda_acc.x + this.lambda_acc.y) * body1.i_inv;
-
- body2.v.mad(impulse, body2.m_inv);
- body2.w += (this.s2 * this.lambda_acc.x + this.lambda_acc.y) * body2.i_inv;
- }
- else {
- this.lambda_acc.set(0, 0);
- }
-}
-
-PrismaticJoint.prototype.solveVelocityConstraints = function() {
- var body1 = this.body1;
- var body2 = this.body2;
-
- // Compute lambda for velocity constraint
- // Solve J * invM * JT * lambda = -J * V
- var cdot1 = this.n.dot(vec2.sub(body2.v, body1.v)) + this.s2 * body2.w - this.s1 * body1.w;
- var cdot2 = body2.w - body1.w;
- var lambda = this.em_inv.solve(new vec2(-cdot1, -cdot2));
-
- // Accumulate lambda
- this.lambda_acc.addself(lambda);
-
- // linearImpulse = JT * lambda
- var impulse = vec2.scale(this.n, lambda.x);
-
- // Apply constraint impulses
- // V += JT * lambda * invM
- body1.v.mad(impulse, -body1.m_inv);
- body1.w -= (this.s1 * lambda.x + lambda.y) * body1.i_inv;
-
- body2.v.mad(impulse, body2.m_inv);
- body2.w += (this.s2 * lambda.x + lambda.y) * body2.i_inv;
-}
-
-PrismaticJoint.prototype.solvePositionConstraints = function() {
- var body1 = this.body1;
- var body2 = this.body2;
-
- // Transformed r1, r2
- var r1 = vec2.rotate(vec2.sub(this.anchor1, body1.centroid), body1.a);
- var r2 = vec2.rotate(vec2.sub(this.anchor2, body2.centroid), body2.a);
-
- // World anchor points
- var p1 = vec2.add(body1.p, r1);
- var p2 = vec2.add(body2.p, r2);
-
- // Delta vector between world anchor points
- var d = vec2.sub(p2, p1);
-
- // r1 + d
- var r1_d = vec2.add(r1, d);
-
- // World line normal
- var n = vec2.rotate(this.n_local, body1.a);
-
- // Position constraint
- var c1 = vec2.dot(n, d);
- var c2 = body2.a - body1.a - this.da;
- var correction = new vec2;
- correction.x = Math.clamp(c1, -Joint.MAX_LINEAR_CORRECTION, Joint.MAX_LINEAR_CORRECTION);
- correction.y = Math.clamp(c2, -Joint.MAX_ANGULAR_CORRECTION, Joint.MAX_ANGULAR_CORRECTION);
-
- // Compute impulse for position constraint
- // Solve J * invM * JT * lambda = -C / dt
- var s1 = vec2.cross(r1_d, n);
- var s2 = vec2.cross(r2, n);
- var s1_i = s1 * body1.i_inv;
- var s2_i = s2 * body2.i_inv;
- var k11 = body1.m_inv + body2.m_inv + s1 * s1_i + s2 * s2_i;
- var k12 = s1_i + s2_i;
- var k22 = body1.i_inv + body2.i_inv;
- var em_inv = new mat2(k11, k12, k12, k22);
- var lambda_dt = em_inv.solve(correction.neg());
-
- // Apply constarint impulses
- // impulse = JT * lambda
- // X += impulse * invM * dt
- var impulse_dt = vec2.scale(n, lambda_dt.x);
-
- body1.p.mad(impulse_dt, -body1.m_inv);
- body1.a -= (vec2.cross(r1_d, impulse_dt) + lambda_dt.y) * body1.i_inv;
-
- body2.p.mad(impulse_dt, body2.m_inv);
- body2.a += (vec2.cross(r2, impulse_dt) + lambda_dt.y) * body2.i_inv;
-
- return Math.abs(c1) <= Joint.LINEAR_SLOP && Math.abs(c2) <= Joint.ANGULAR_SLOP;
-}
-
-PrismaticJoint.prototype.getReactionForce = function(dt_inv) {
- return vec2.scale(this.n, this.lambda_acc.x * dt_inv);
-}
-
-PrismaticJoint.prototype.getReactionTorque = function(dt_inv) {
- return this.lambda_acc.y * dt_inv;
-}
\ No newline at end of file
diff --git a/src/physics/advanced/joints/Revolute.js b/src/physics/advanced/joints/Revolute.js
deleted file mode 100644
index 0708da54..00000000
--- a/src/physics/advanced/joints/Revolute.js
+++ /dev/null
@@ -1,378 +0,0 @@
-/*
-* Copyright (c) 2012 Ju Hyung Lee
-*
-* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
-* and associated documentation files (the "Software"), to deal in the Software without
-* restriction, including without limitation the rights to use, copy, modify, merge, publish,
-* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
-* Software is furnished to do so, subject to the following conditions:
-*
-* The above copyright notice and this permission notice shall be included in all copies or
-* substantial portions of the Software.
-*
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
-* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-//-------------------------------------------------------------------------------------------------
-// Revolute Joint
-//
-// Point-to-Point Constraint:
-// C1 = p2 - p1
-// C1dot = v2 + cross(w2, r2) - v1 - cross(w1, r1)
-// = -v1 + cross(r1, w1) + v2 - cross(r2, w1)
-// J1 = [ -I, skew(r1), I, -skew(r2) ]
-//
-// Angular Constraint (for angle limit):
-// C2 = a2 - a1 - refAngle
-// C2dot = w2 - w1
-// J2 = [ 0, -1, 0, 1 ]
-//
-// Block Jacobian Matrix:
-// J = [ -I, skew(r1), I, -skew(r2) ]
-// [ 0, -1, 0, 1 ]
-//
-// impulse = JT * lambda = [ -lambda_xy, -(cross(r1, lambda_xy) + lambda_z), lambda_xy, cross(r1, lambda_xy) + lambda_z ]
-//-------------------------------------------------------------------------------------------------
-
-RevoluteJoint = function(body1, body2, anchor) {
- Joint.call(this, Joint.TYPE_REVOLUTE, body1, body2, false);
-
- this.anchor1 = this.body1.getLocalPoint(anchor);
- this.anchor2 = this.body2.getLocalPoint(anchor);
-
- // Initial angle difference
- this.refAngle = body2.a - body1.a;
-
- // Accumulated lambda
- this.lambda_acc = new vec3(0, 0, 0);
- this.motorLambda_acc = 0;
-
- // Angle limit
- this.limitEnabled = false;
- this.limitLowerAngle = 0;
- this.limitUpperAngle = 0;
- this.limitState = Joint.LIMIT_STATE_INACTIVE;
-
- // Motor
- this.motorEnabled = false;
- this.motorSpeed = 0;
- this.maxMotorTorque = 0;
-}
-
-RevoluteJoint.prototype = new Joint;
-RevoluteJoint.prototype.constructor = RevoluteJoint;
-
-RevoluteJoint.prototype.setWorldAnchor1 = function(anchor1) {
- this.anchor1 = this.body1.getLocalPoint(anchor1);
- this.anchor2 = this.body2.getLocalPoint(anchor1);
-}
-
-RevoluteJoint.prototype.setWorldAnchor2 = function(anchor2) {
- this.anchor1 = this.body1.getLocalPoint(anchor2);
- this.anchor2 = this.body2.getLocalPoint(anchor2);
-}
-
-RevoluteJoint.prototype.serialize = function() {
- return {
- "type": "RevoluteJoint",
- "body1": this.body1.id,
- "body2": this.body2.id,
- "anchor": this.body1.getWorldPoint(this.anchor1),
- "collideConnected": this.collideConnected,
- "maxForce": this.maxForce,
- "breakable": this.breakable,
- "limitEnabled": this.limitEnabled,
- "limitLowerAngle": this.limitLowerAngle,
- "limitUpperAngle": this.limitUpperAngle,
- "motorEnabled": this.motorEnabled,
- "motorSpeed": this.motorSpeed,
- "maxMotorTorque": this.maxMotorTorque
- };
-}
-
-RevoluteJoint.prototype.enableMotor = function(flag) {
- this.motorEnabled = flag;
-}
-
-RevoluteJoint.prototype.setMotorSpeed = function(speed) {
- this.motorSpeed = speed;
-}
-
-RevoluteJoint.prototype.setMaxMotorTorque = function(torque) {
- this.maxMotorTorque = torque;
-}
-
-RevoluteJoint.prototype.enableLimit = function(flag) {
- this.limitEnabled = flag;
-}
-
-RevoluteJoint.prototype.setLimits = function(lower, upper) {
- this.limitLowerAngle = lower;
- this.limitUpperAngle = upper;
-}
-
-RevoluteJoint.prototype.initSolver = function(dt, warmStarting) {
- var body1 = this.body1;
- var body2 = this.body2;
-
- // Max impulse
- this.maxImpulse = this.maxForce * dt;
-
- if (!this.motorEnabled) {
- this.motorLambda_acc = 0;
- }
- else {
- this.maxMotorImpulse = this.maxMotorTorque * dt;
- }
-
- if (this.limitEnabled) {
- var da = body2.a - body1.a - this.refAngle;
-
- if (Math.abs(this.limitUpperAngle - this.limitLowerAngle) < Joint.ANGULAR_SLOP) {
- this.limitState = Joint.LIMIT_STATE_EQUAL_LIMITS;
- }
- else if (da <= this.limitLowerAngle) {
- if (this.limitState != Joint.LIMIT_STATE_AT_LOWER) {
- this.lambda_acc.z = 0;
- }
- this.limitState = Joint.LIMIT_STATE_AT_LOWER;
- }
- else if (da >= this.limitUpperAngle) {
- if (this.limitState != Joint.LIMIT_STATE_AT_UPPER) {
- this.lambda_acc.z = 0;
- }
- this.limitState = Joint.LIMIT_STATE_AT_UPPER;
- }
- else {
- this.limitState = Joint.LIMIT_STATE_INACTIVE;
- this.lambda_acc.z = 0;
- }
- }
- else {
- this.limitState = Joint.LIMIT_STATE_INACTIVE;
- }
-
- // Transformed r1, r2
- this.r1 = body1.xf.rotate(vec2.sub(this.anchor1, body1.centroid));
- this.r2 = body2.xf.rotate(vec2.sub(this.anchor2, body2.centroid));
-
- // invEM = J * invM * JT
- var sum_m_inv = body1.m_inv + body2.m_inv;
- var r1 = this.r1;
- var r2 = this.r2;
- var r1x_i = r1.x * body1.i_inv;
- var r1y_i = r1.y * body1.i_inv;
- var r2x_i = r2.x * body2.i_inv;
- var r2y_i = r2.y * body2.i_inv;
- var k11 = sum_m_inv + r1.y * r1y_i + r2.y * r2y_i;
- var k12 = -r1.x * r1y_i - r2.x * r2y_i;
- var k13 = -r1y_i - r2y_i;
- var k22 = sum_m_inv + r1.x * r1x_i + r2.x * r2x_i;
- var k23 = r1x_i + r2x_i;
- var k33 = body1.i_inv + body2.i_inv;
- this.em_inv = new mat3(k11, k12, k13, k12, k22, k23, k13, k23, k33);
-
- // K2 = J2 * invM * J2T
- if (k33 != 0) {
- this.em2 = 1 / k33;
- }
-
- if (warmStarting) {
- // Apply cached constraint impulses
- // V += JT * lambda
- var lambda_xy = new vec2(this.lambda_acc.x, this.lambda_acc.y);
- var lambda_z = this.lambda_acc.z + this.motorLambda_acc;
-
- body1.v.mad(lambda_xy, -body1.m_inv);
- body1.w -= (vec2.cross(this.r1, lambda_xy) + lambda_z) * body1.i_inv;
-
- body2.v.mad(lambda_xy, body2.m_inv);
- body2.w += (vec2.cross(this.r2, lambda_xy) + lambda_z) * body2.i_inv;
- }
- else {
- this.lambda_acc.set(0, 0, 0);
- this.motorLambda_acc = 0;
- }
-}
-
-RevoluteJoint.prototype.solveVelocityConstraints = function() {
- var body1 = this.body1;
- var body2 = this.body2;
-
- // Solve motor constraint
- if (this.motorEnabled && this.limitState != Joint.LIMIT_STATE_EQUAL_LIMITS) {
- // Compute motor impulse
- var cdot = body2.w - body1.w - this.motorSpeed;
- var lambda = -this.em2 * cdot;
- var motorLambdaOld = this.motorLambda_acc;
- this.motorLambda_acc = Math.clamp(this.motorLambda_acc + lambda, -this.maxMotorImpulse, this.maxMotorImpulse);
- lambda = this.motorLambda_acc - motorLambdaOld;
-
- // Apply motor constraint impulses
- body1.w -= lambda * body1.i_inv;
- body2.w += lambda * body2.i_inv;
- }
-
- // Solve point-to-point constraint with angular limit
- if (this.limitEnabled && this.limitState != Joint.LIMIT_STATE_INACTIVE) {
- // Compute lambda for velocity constraint
- // Solve J * invM * JT * lambda = -J * V
- // in 2D: cross(w, r) = perp(r) * w
- var v1 = vec2.mad(body1.v, vec2.perp(this.r1), body1.w);
- var v2 = vec2.mad(body2.v, vec2.perp(this.r2), body2.w);
- var cdot1 = vec2.sub(v2, v1);
- var cdot2 = body2.w - body1.w;
- var cdot = vec3.fromVec2(cdot1, cdot2);
- var lambda = this.em_inv.solve(cdot.neg());
-
- if (this.limitState == Joint.LIMIT_STATE_EQUAL_LIMITS) {
- // Accumulate lambda
- this.lambda_acc.addself(lambda);
- }
- else if (this.limitState == Joint.LIMIT_STATE_AT_LOWER || this.limitState == Joint.LIMIT_STATE_AT_UPPER) {
- // Accumulated new lambda.z
- var newLambda_z = this.lambda_acc.z + lambda.z;
-
- var lowerLimited = this.limitState == Joint.LIMIT_STATE_AT_LOWER && newLambda_z < 0;
- var upperLimited = this.limitState == Joint.LIMIT_STATE_AT_UPPER && newLambda_z > 0;
-
- if (lowerLimited || upperLimited) {
- // Modify last equation to get lambda_acc.z to 0
- // That is, lambda.z have to be equal -lambda_acc.z
- // rhs = -J * V - (K_13, K_23, K_33) * (lambda.z + lambda_acc.z)
- // Solve J * invM * JT * reduced_lambda = rhs
- var rhs = vec2.add(cdot1, vec2.scale(new vec2(this.em_inv._13, this.em_inv._23), newLambda_z));
- var reduced = this.em_inv.solve2x2(rhs.neg());
- lambda.x = reduced.x;
- lambda.y = reduced.y;
- lambda.z = -this.lambda_acc.z;
-
- // Accumulate lambda
- this.lambda_acc.x += lambda.x;
- this.lambda_acc.y += lambda.y;
- this.lambda_acc.z = 0;
- }
- else {
- // Accumulate lambda
- this.lambda_acc.addself(lambda);
- }
- }
-
- // Apply constraint impulses
- // V += JT * lambda * invM
- var lambda_xy = new vec2(lambda.x, lambda.y);
-
- body1.v.mad(lambda_xy, -body1.m_inv);
- body1.w -= (vec2.cross(this.r1, lambda_xy) + lambda.z) * body1.i_inv;
-
- body2.v.mad(lambda_xy, body2.m_inv);
- body2.w += (vec2.cross(this.r2, lambda_xy) + lambda.z) * body2.i_inv;
- }
- // Solve point-to-point constraint
- else {
- // Compute lambda for velocity constraint
- // Solve J1 * invM * J1T * lambda = -J1 * V
- // in 2D: cross(w, r) = perp(r) * w
- var v1 = vec2.mad(body1.v, vec2.perp(this.r1), body1.w);
- var v2 = vec2.mad(body2.v, vec2.perp(this.r2), body2.w);
- var cdot = vec2.sub(v2, v1);
- var lambda = this.em_inv.solve2x2(cdot.neg());
-
- // Accumulate lambda
- this.lambda_acc.addself(vec3.fromVec2(lambda, 0));
-
- // Apply constraint impulses
- // V += J1T * lambda * invM
- body1.v.mad(lambda, -body1.m_inv);
- body1.w -= vec2.cross(this.r1, lambda) * body1.i_inv;
-
- body2.v.mad(lambda, body2.m_inv);
- body2.w += vec2.cross(this.r2, lambda) * body2.i_inv;
- }
-}
-
-RevoluteJoint.prototype.solvePositionConstraints = function() {
- var body1 = this.body1;
- var body2 = this.body2;
-
- var angularError = 0;
- var positionError = 0;
-
- // Solve limit constraint
- if (this.limitEnabled && this.limitState != Joint.LIMIT_STATE_INACTIVE) {
- var da = body2.a - body1.a - this.refAngle;
-
- // angular lambda = -EM * C / dt
- var angularImpulseDt = 0;
-
- if (this.limitState == Joint.LIMIT_STATE_EQUAL_LIMITS) {
- var c = Math.clamp(da - this.limitLowerAngle, -Joint.MAX_ANGULAR_CORRECTION, Joint.MAX_ANGULAR_CORRECTION);
-
- angularError = Math.abs(c);
- angularImpulseDt = -this.em2 * c;
- }
- else if (this.limitState == Joint.LIMIT_STATE_AT_LOWER) {
- var c = da - this.limitLowerAngle;
-
- angularError = -c;
- c = Math.clamp(c + Joint.ANGULAR_SLOP, -Joint.MAX_ANGULAR_CORRECTION, 0);
- angularImpulseDt = -this.em2 * c;
- }
- else if (this.limitState == Joint.LIMIT_STATE_AT_UPPER) {
- var c = da - this.limitUpperAngle;
-
- angularError = c;
- c = Math.clamp(c - Joint.ANGULAR_SLOP, 0, Joint.MAX_ANGULAR_CORRECTION);
- angularImpulseDt = -this.em2 * c;
- }
-
- body1.a -= angularImpulseDt * body1.i_inv;
- body2.a += angularImpulseDt * body2.i_inv;
- }
-
- // Solve point-to-point constraint
- {
- // Transformed r1, r2
- var r1 = vec2.rotate(vec2.sub(this.anchor1, body1.centroid), body1.a);
- var r2 = vec2.rotate(vec2.sub(this.anchor2, body2.centroid), body2.a);
-
- // Position constraint
- var c = vec2.sub(vec2.add(body2.p, r2), vec2.add(body1.p, r1));
- var correction = vec2.truncate(c, Joint.MAX_LINEAR_CORRECTION);
- positionError = correction.length();
-
- // Compute lambda for position constraint
- // Solve J1 * invM * J1T * lambda = -C / dt
- var sum_m_inv = body1.m_inv + body2.m_inv;
- var r1y_i = r1.y * body1.i_inv;
- var r2y_i = r2.y * body2.i_inv;
- var k11 = sum_m_inv + r1.y * r1y_i + r2.y * r2y_i;
- var k12 = -r1.x * r1y_i - r2.x * r2y_i;
- var k22 = sum_m_inv + r1.x * r1.x * body1.i_inv + r2.x * r2.x * body2.i_inv;
- var em_inv = new mat2(k11, k12, k12, k22);
- var lambda_dt = em_inv.solve(correction.neg());
-
- // Apply constraint impulses
- // impulse = J1T * lambda
- // X += impulse * invM * dt
- body1.p.mad(lambda_dt, -body1.m_inv);
- body1.a -= vec2.cross(r1, lambda_dt) * body1.i_inv;
-
- body2.p.mad(lambda_dt, body2.m_inv);
- body2.a += vec2.cross(r2, lambda_dt) * body2.i_inv;
- }
-
- return positionError < Joint.LINEAR_SLOP && angularError < Joint.ANGULAR_SLOP;
-}
-
-RevoluteJoint.prototype.getReactionForce = function(dt_inv) {
- return vec2.scale(this.lambda_acc, dt_inv);
-}
-
-RevoluteJoint.prototype.getReactionTorque = function(dt_inv) {
- return 0;
-}
\ No newline at end of file
diff --git a/src/physics/advanced/joints/Rope.js b/src/physics/advanced/joints/Rope.js
deleted file mode 100644
index 5453e459..00000000
--- a/src/physics/advanced/joints/Rope.js
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
-* Copyright (c) 2012 Ju Hyung Lee
-*
-* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
-* and associated documentation files (the "Software"), to deal in the Software without
-* restriction, including without limitation the rights to use, copy, modify, merge, publish,
-* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
-* Software is furnished to do so, subject to the following conditions:
-*
-* The above copyright notice and this permission notice shall be included in all copies or
-* substantial portions of the Software.
-*
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
-* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-//-------------------------------------------------------------------------------------------------
-// Rope Joint
-//
-// d = p2 - p1
-// u = d / norm(d)
-// C = norm(d) - l
-// C = sqrt(dot(d, d)) - l
-// Cdot = dot(u, v2 + cross(w2, r2) - v1 - cross(w1, r1))
-// = -dot(u, v1) - dot(w1, cross(r1, u)) + dot(u, v2) + dot(w2, cross(r2, u))
-// J = [ -u, -cross(r1, u), u, cross(r2, u) ]
-//
-// impulse = JT * lambda = [ -u * lambda, -cross(r1, u) * lambda, u * lambda, cross(r1, u) * lambda ]
-//-------------------------------------------------------------------------------------------------
-
-RopeJoint = function(body1, body2, anchor1, anchor2) {
- Joint.call(this, Joint.TYPE_ROPE, body1, body2, true);
-
- // Local anchor points
- this.anchor1 = this.body1.getLocalPoint(anchor1);
- this.anchor2 = this.body2.getLocalPoint(anchor2);
-
- // Max distance
- this.maxDistance = vec2.dist(anchor1, anchor2);
-
- // Accumulated impulse
- this.lambda_acc = 0;
-}
-
-RopeJoint.prototype = new Joint;
-RopeJoint.prototype.constructor = RopeJoint;
-
-RopeJoint.prototype.setWorldAnchor1 = function(anchor1) {
- this.anchor1 = this.body1.getLocalPoint(anchor1);
-
- this.maxDistance = vec2.dist(anchor1, this.getWorldAnchor2());
-}
-
-RopeJoint.prototype.setWorldAnchor2 = function(anchor2) {
- this.anchor2 = this.body2.getLocalPoint(anchor2);
-
- this.maxDistance = vec2.dist(anchor2, this.getWorldAnchor1());
-}
-
-RopeJoint.prototype.serialize = function() {
- return {
- "type": "RopeJoint",
- "body1": this.body1.id,
- "body2": this.body2.id,
- "anchor1": this.body1.getWorldPoint(this.anchor1),
- "anchor2": this.body2.getWorldPoint(this.anchor2),
- "collideConnected": this.collideConnected,
- "maxForce": this.maxForce,
- "breakable": this.breakable,
- };
-}
-
-RopeJoint.prototype.initSolver = function(dt, warmStarting) {
- var body1 = this.body1;
- var body2 = this.body2;
-
- // Max impulse
- this.maxImpulse = this.maxForce * dt;
-
- // Transformed r1, r2
- this.r1 = body1.xf.rotate(vec2.sub(this.anchor1, body1.centroid));
- this.r2 = body2.xf.rotate(vec2.sub(this.anchor2, body2.centroid));
-
- // Delta vector between two world anchors
- var d = vec2.sub(vec2.add(body2.p, this.r2), vec2.add(body1.p, this.r1));
-
- // Distance between two anchors
- this.distance = d.length();
-
- //
- var c = this.distance - this.maxDistance;
- if (c > 0) {
- this.cdt = 0;
- this.limitState = Joint.LIMIT_STATE_AT_UPPER;
- }
- else {
- this.cdt = c / dt;
- this.limitState = Joint.LIMIT_STATE_INACTIVE;
- }
-
- // Unit delta vector
- if (this.distance > Joint.LINEAR_SLOP) {
- this.u = vec2.scale(d, 1 / this.distance);
- }
- else {
- this.u = vec2.zero;
- }
-
- // s1, s2
- this.s1 = vec2.cross(this.r1, this.u);
- this.s2 = vec2.cross(this.r2, this.u);
-
- // invEM = J * invM * JT
- var em_inv = body1.m_inv + body2.m_inv + body1.i_inv * this.s1 * this.s1 + body2.i_inv * this.s2 * this.s2;
- this.em = em_inv == 0 ? 0 : 1 / em_inv;
-
- if (warmStarting) {
- // linearImpulse = JT * lambda
- var impulse = vec2.scale(this.u, this.lambda_acc);
-
- // Apply cached constraint impulses
- // V += JT * lambda * invM
- body1.v.mad(impulse, -body1.m_inv);
- body1.w -= this.s1 * this.lambda_acc * body1.i_inv;
-
- body2.v.mad(impulse, body2.m_inv);
- body2.w += this.s2 * this.lambda_acc * body2.i_inv;
- }
- else {
- this.lambda_acc = 0;
- }
-}
-
-RopeJoint.prototype.solveVelocityConstraints = function() {
- var body1 = this.body1;
- var body2 = this.body2;
-
- // Compute lambda for velocity constraint
- // Solve J * invM * JT * lambda = -(J * V)
- var cdot = this.u.dot(vec2.sub(body2.v, body1.v)) + this.s2 * body2.w - this.s1 * body1.w;
- var lambda = -this.em * (cdot + this.cdt);
-
- // Accumulate lambda and clamp it to zero
- var lambda_old = this.lambda_acc;
- this.lambda_acc = Math.min(lambda_old + lambda, 0);
- lambda = this.lambda_acc - lambda_old;
-
- // linearImpulse = JT * lambda
- var impulse = vec2.scale(this.u, lambda);
-
- // Apply constraint impulses
- // V += JT * lambda * invM
- body1.v.mad(impulse, -body1.m_inv);
- body1.w -= this.s1 * lambda * body1.i_inv;
-
- body2.v.mad(impulse, body2.m_inv);
- body2.w += this.s2 * lambda * body2.i_inv;
-}
-
-RopeJoint.prototype.solvePositionConstraints = function() {
- var body1 = this.body1;
- var body2 = this.body2;
-
- // Transformed r1, r2
- var r1 = vec2.rotate(vec2.sub(this.anchor1, body1.centroid), body1.a);
- var r2 = vec2.rotate(vec2.sub(this.anchor2, body2.centroid), body2.a);
-
- // Delta vector between two anchors
- var d = vec2.sub(vec2.add(body2.p, r2), vec2.add(body1.p, r1));
-
- // Distance between two anchors
- var dist = d.length();
-
- // Unit delta vector
- var u = vec2.scale(d, 1 / dist);
-
- // Position constraint
- var c = dist - this.maxDistance;
- var correction = Math.clamp(c, 0, Joint.MAX_LINEAR_CORRECTION);
-
- // Compute lambda for correction
- // Solve J * invM * JT * lambda = -C / dt
- var s1 = vec2.cross(r1, u);
- var s2 = vec2.cross(r2, u);
- var em_inv = body1.m_inv + body2.m_inv + body1.i_inv * s1 * s1 + body2.i_inv * s2 * s2;
- var lambda_dt = em_inv == 0 ? 0 : -correction / em_inv;
-
- // Apply constraint impulses
- // impulse = JT * lambda
- // X += impulse * invM * dt
- var impulse_dt = vec2.scale(u, lambda_dt);
-
- body1.p.mad(impulse_dt, -body1.m_inv);
- body1.a -= s1 * lambda_dt * body1.i_inv;
-
- body2.p.mad(impulse_dt, body2.m_inv);
- body2.a += s2 * lambda_dt * body2.i_inv;
-
- return c < Joint.LINEAR_SLOP;
-}
-
-RopeJoint.prototype.getReactionForce = function(dt_inv) {
- return vec2.scale(this.u, this.lambda_acc * dt_inv);
-}
-
-RopeJoint.prototype.getReactionTorque = function(dt_inv) {
- return 0;
-}
\ No newline at end of file
diff --git a/src/physics/advanced/joints/Weld.js b/src/physics/advanced/joints/Weld.js
deleted file mode 100644
index 90922893..00000000
--- a/src/physics/advanced/joints/Weld.js
+++ /dev/null
@@ -1,306 +0,0 @@
-/*
-* Copyright (c) 2012 Ju Hyung Lee
-*
-* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
-* and associated documentation files (the "Software"), to deal in the Software without
-* restriction, including without limitation the rights to use, copy, modify, merge, publish,
-* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
-* Software is furnished to do so, subject to the following conditions:
-*
-* The above copyright notice and this permission notice shall be included in all copies or
-* substantial portions of the Software.
-*
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
-* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-//-------------------------------------------------------------------------------------------------
-// Weld Joint
-//
-// Point-to-Point Constraint:
-// C1 = p2 - p1
-// Cdot1 = v2 + cross(w2, r2) - v1 - cross(w1, r1)
-// = -v1 + cross(r1, w1) + v2 - cross(r2, w1)
-// J1 = [ -I, skew(r1), I, -skew(r2) ]
-//
-// Angular Constraint:
-// C2 = a2 - a1
-// C2dot = w2 - w1
-// J2 = [ 0, -1, 0, 1 ]
-//
-// Block Jacobian Matrix:
-// J = [ -I, skew(r1), I, -skew(r2) ]
-// [ 0, -1, 0, 1 ]
-//
-// impulse = JT * lambda = [ -lambda_xy, -(cross(r1, lambda_xy) + lambda_z), lambda_xy, cross(r1, lambda_xy) + lambda_z ]
-//-------------------------------------------------------------------------------------------------
-
-WeldJoint = function(body1, body2, anchor) {
- Joint.call(this, Joint.TYPE_WELD, body1, body2, false);
-
- this.anchor1 = this.body1.getLocalPoint(anchor);
- this.anchor2 = this.body2.getLocalPoint(anchor);
-
- // Soft constraint coefficients
- this.gamma = 0;
- this.beta_c = 0;
-
- // Spring coefficients
- this.frequencyHz = 0;
- this.dampingRatio = 0;
-
- // Accumulated lambda
- this.lambda_acc = new vec3(0, 0, 0);
-}
-
-WeldJoint.prototype = new Joint;
-WeldJoint.prototype.constructor = WeldJoint;
-
-WeldJoint.prototype.setWorldAnchor1 = function(anchor1) {
- this.anchor1 = this.body1.getLocalPoint(anchor1);
- this.anchor2 = this.body2.getLocalPoint(anchor1);
-}
-
-WeldJoint.prototype.setWorldAnchor2 = function(anchor2) {
- this.anchor1 = this.body1.getLocalPoint(anchor2);
- this.anchor2 = this.body2.getLocalPoint(anchor2);
-}
-
-WeldJoint.prototype.serialize = function() {
- return {
- "type": "WeldJoint",
- "body1": this.body1.id,
- "body2": this.body2.id,
- "anchor1": this.body1.getWorldPoint(this.anchor1),
- "anchor2": this.body2.getWorldPoint(this.anchor2),
- "collideConnected": this.collideConnected,
- "maxForce": this.maxForce,
- "breakable": this.breakable,
- "frequencyHz": this.frequencyHz,
- "dampingRatio": this.dampingRatio
- };
-}
-
-WeldJoint.prototype.setSpringFrequencyHz = function(frequencyHz) {
- // NOTE: frequencyHz should be limited to under 4 times time steps
- this.frequencyHz = frequencyHz;
-}
-
-WeldJoint.prototype.setSpringDampingRatio = function(dampingRatio) {
- this.dampingRatio = dampingRatio;
-}
-
-WeldJoint.prototype.initSolver = function(dt, warmStarting) {
- var body1 = this.body1;
- var body2 = this.body2;
-
- // Max impulse
- this.maxImpulse = this.maxForce * dt;
-
- // Transformed r1, r2
- this.r1 = body1.xf.rotate(vec2.sub(this.anchor1, body1.centroid));
- this.r2 = body2.xf.rotate(vec2.sub(this.anchor2, body2.centroid));
-
- // invEM = J * invM * JT
- var sum_m_inv = body1.m_inv + body2.m_inv;
- var r1 = this.r1;
- var r2 = this.r2;
- var r1x_i = r1.x * body1.i_inv;
- var r1y_i = r1.y * body1.i_inv;
- var r2x_i = r2.x * body2.i_inv;
- var r2y_i = r2.y * body2.i_inv;
- var k11 = sum_m_inv + r1.y * r1y_i + r2.y * r2y_i;
- var k12 = -r1.x * r1y_i - r2.x * r2y_i;
- var k13 = -r1y_i - r2y_i;
- var k22 = sum_m_inv + r1.x * r1x_i + r2.x * r2x_i;
- var k23 = r1x_i + r2x_i;
- var k33 = body1.i_inv + body2.i_inv;
- this.em_inv = new mat3(k11, k12, k13, k12, k22, k23, k13, k23, k33);
-
- // Compute soft constraint parameters
- if (this.frequencyHz > 0) {
- var m = k33 > 0 ? 1 / k33 : 0;
-
- // Frequency
- var omega = 2 * Math.PI * this.frequencyHz;
-
- // Spring stiffness
- var k = m * omega * omega;
-
- // Damping coefficient
- var c = m * 2 * this.dampingRatio * omega;
-
- // Soft constraint formulas
- // gamma and beta are divided by dt to reduce computation
- this.gamma = (c + k * dt) * dt;
- this.gamma = this.gamma == 0 ? 0 : 1 / this.gamma;
- var beta = dt * k * this.gamma;
-
- // Position constraint
- var pc = body2.a - body1.a;
- this.beta_c = beta * pc;
-
- // invEM = invEM + gamma * I (to reduce calculation)
- this.em_inv._33 += this.gamma;
- }
- else {
- this.gamma = 0;
- this.beta_c = 0;
- }
-
- if (warmStarting) {
- // Apply cached constraint impulses
- // V += JT * lambda * invM
- var lambda_xy = new vec2(this.lambda_acc.x, this.lambda_acc.y);
- var lambda_z = this.lambda_acc.z;
-
- body1.v.mad(lambda_xy, -body1.m_inv);
- body1.w -= (vec2.cross(this.r1, lambda_xy) + lambda_z) * body1.i_inv;
-
- body2.v.mad(lambda_xy, body2.m_inv);
- body2.w += (vec2.cross(this.r2, lambda_xy) + lambda_z) * body2.i_inv;
- }
- else {
- this.lambda_acc.set(0, 0, 0);
- }
-}
-
-WeldJoint.prototype.solveVelocityConstraints = function() {
- var body1 = this.body1;
- var body2 = this.body2;
-
- if (this.frequencyHz > 0) {
- // Compute lambda for angular velocity constraint
- // Solve J2 * invM * J2T * lambda = -(J2 * V + beta * C + gamma * (lambda_acc + lambda))
- var cdot2 = body2.w - body1.w;
- lambda_z = -(cdot2 + this.beta_c + this.gamma * this.lambda_acc.z) / this.em_inv._33;
-
- // Apply angular constraint impulses
- // V += J2T * lambda * invM
- body1.w -= lambda_z * body1.i_inv;
- body2.w += lambda_z * body2.i_inv;
-
- // Compute lambda for velocity constraint
- // Solve J1 * invM * J1T * lambda = -J1 * V
- var v1 = vec2.mad(body1.v, vec2.perp(this.r1), body1.w);
- var v2 = vec2.mad(body2.v, vec2.perp(this.r2), body2.w);
- var cdot1 = vec2.sub(v2, v1);
- var lambda_xy = this.em_inv.solve2x2(cdot1.neg());
-
- // Accumulate lambda
- this.lambda_acc.x += lambda_xy.x;
- this.lambda_acc.y += lambda_xy.y;
- this.lambda_acc.z += lambda_z;
-
- // Apply constraint impulses
- // V += J1T * lambda * invM
- body1.v.mad(lambda_xy, -body1.m_inv);
- body1.w -= vec2.cross(this.r1, lambda_xy) * body1.i_inv;
-
- body2.v.mad(lambda_xy, body2.m_inv);
- body2.w += vec2.cross(this.r2, lambda_xy) * body2.i_inv;
- }
- else {
- // Compute lambda for velocity constraint
- // Solve J * invM * JT * lambda = -J * V
- // in 2D: cross(w, r) = perp(r) * w
- var v1 = vec2.mad(body1.v, vec2.perp(this.r1), body1.w);
- var v2 = vec2.mad(body2.v, vec2.perp(this.r2), body2.w);
- var cdot1 = vec2.sub(v2, v1);
- var cdot2 = body2.w - body1.w;
- var cdot = vec3.fromVec2(cdot1, cdot2);
- var lambda = this.em_inv.solve(cdot.neg());
-
- // Accumulate lambda
- this.lambda_acc.addself(lambda);
-
- // Apply constraint impulses
- // V += JT * lambda * invM
- var lambda_xy = new vec2(lambda.x, lambda.y);
-
- body1.v.mad(lambda_xy, -body1.m_inv);
- body1.w -= (vec2.cross(this.r1, lambda_xy) + lambda.z) * body1.i_inv;
-
- body2.v.mad(lambda_xy, body2.m_inv);
- body2.w += (vec2.cross(this.r2, lambda_xy) + lambda.z) * body2.i_inv;
- }
-}
-
-WeldJoint.prototype.solvePositionConstraints = function() {
- var body1 = this.body1;
- var body2 = this.body2;
-
- // Transformed r1, r2
- var r1 = vec2.rotate(vec2.sub(this.anchor1, body1.centroid), body1.a);
- var r2 = vec2.rotate(vec2.sub(this.anchor2, body2.centroid), body2.a);
-
- // Compute J * invM * JT
- var sum_m_inv = body1.m_inv + body2.m_inv;
- var r1x_i = r1.x * body1.i_inv;
- var r1y_i = r1.y * body1.i_inv;
- var r2x_i = r2.x * body2.i_inv;
- var r2y_i = r2.y * body2.i_inv;
- var k11 = sum_m_inv + r1.y * r1y_i + r2.y * r2y_i;
- var k12 = -r1.x * r1y_i - r2.x * r2y_i;
- var k13 = -r1y_i - r2y_i;
- var k22 = sum_m_inv + r1.x * r1x_i + r2.x * r2x_i;
- var k23 = r1x_i + r2x_i;
- var k33 = body1.i_inv + body2.i_inv;
- var em_inv = new mat3(k11, k12, k13, k12, k22, k23, k13, k23, k33);
-
- if (this.frequencyHz > 0) {
- // Position constraint
- var c1 = vec2.sub(vec2.add(body2.p, r2), vec2.add(body1.p, r1));
- var c2 = 0;
- var correction = vec2.truncate(c1, Joint.MAX_LINEAR_CORRECTION);
-
- // Compute lambda for position constraint
- // Solve J1 * invM * J1T * lambda = -C / dt
- var lambda_dt_xy = em_inv.solve2x2(correction.neg());
-
- // Apply constraint impulses
- // impulse = J1T * lambda
- // X += impulse * invM * dt
- body1.p.mad(lambda_dt_xy, -body1.m_inv);
- body1.a -= vec2.cross(r1, lambda_dt_xy) * body1.i_inv;
-
- body2.p.mad(lambda_dt_xy, body2.m_inv);
- body2.a += vec2.cross(r2, lambda_dt_xy) * body2.i_inv;
- }
- else {
- // Position constraint
- var c1 = vec2.sub(vec2.add(body2.p, r2), vec2.add(body1.p, r1));
- var c2 = body2.a - body1.a;
- var correction = vec3.fromVec2(
- vec2.truncate(c1, Joint.MAX_LINEAR_CORRECTION),
- Math.clamp(c2, -Joint.MAX_ANGULAR_CORRECTION, Joint.MAX_ANGULAR_CORRECTION));
-
- // Compute lambda for position constraint
- // Solve J * invM * JT * lambda = -C / dt
- var lambda_dt = em_inv.solve(correction.neg());
-
- // Apply constraint impulses
- // impulse = JT * lambda
- // X += impulse * invM * dt
- var lambda_dt_xy = new vec2(lambda_dt.x, lambda_dt.y);
-
- body1.p.mad(lambda_dt_xy, -body1.m_inv);
- body1.a -= (vec2.cross(r1, lambda_dt_xy) + lambda_dt.z) * body1.i_inv;
-
- body2.p.mad(lambda_dt_xy, body2.m_inv);
- body2.a += (vec2.cross(r2, lambda_dt_xy) + lambda_dt.z) * body2.i_inv;
- }
-
- return c1.length() < Joint.LINEAR_SLOP && Math.abs(c2) <= Joint.ANGULAR_SLOP;
-}
-
-WeldJoint.prototype.getReactionForce = function(dt_inv) {
- return vec2.scale(this.lambda_acc.toVec2(), dt_inv);
-}
-
-WeldJoint.prototype.getReactionTorque = function(dt_inv) {
- return this.lambda_acc.z * dt_inv;
-}
\ No newline at end of file
diff --git a/src/physics/advanced/joints/Wheel.js b/src/physics/advanced/joints/Wheel.js
deleted file mode 100644
index 9036821b..00000000
--- a/src/physics/advanced/joints/Wheel.js
+++ /dev/null
@@ -1,376 +0,0 @@
-/*
-* Copyright (c) 2012 Ju Hyung Lee
-*
-* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
-* and associated documentation files (the "Software"), to deal in the Software without
-* restriction, including without limitation the rights to use, copy, modify, merge, publish,
-* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
-* Software is furnished to do so, subject to the following conditions:
-*
-* The above copyright notice and this permission notice shall be included in all copies or
-* substantial portions of the Software.
-*
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
-* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-//-------------------------------------------------------------------------------------------------
-// Wheel Joint
-//
-// Point-to-Line constraint:
-// d = p2 - p1
-// n = normalize(perp(d))
-// C = dot(n, d)
-// Cdot = dot(d, dn/dt) + dot(n dd/dt)
-// = dot(d, cross(w1, n)) + dot(n, v2 + cross(w2, r2) - v1 - cross(w1, r1))
-// = dot(d, cross(w1, n)) + dot(n, v2) + dot(n, cross(w2, r2)) - dot(n, v1) - dot(n, cross(w1, r1))
-// = -dot(n, v1) - dot(cross(d + r1, n), w1) + dot(n, v2) + dot(cross(r2, n), w2)
-// J = [ -n, -sn1, n, sn2 ]
-// sn1 = cross(r1 + d, n)
-// sn2 = cross(r2, n)
-//
-// impulse = JT * lambda = [ -n * lambda, -(sn1 * lambda), n * lambda, sn2 * lambda ]
-//
-// Spring constraint:
-// u = normalize(d)
-// C = dot(u, d)
-// Cdot = -dot(u, v1) - dot(cross(d + r1, u), w1) + dot(u, v2) + dot(cross(r2, u), w2)
-// J = [ -u, -su1, u, su2 ]
-// su1 = cross(r1 + d, u)
-// su2 = cross(r2, u)
-//
-// impulse = JT * lambda = [ -u * lambda, -(su1 * lambda), u * lambda, su2 * lambda ]
-//
-// Motor rotational constraint:
-// Cdot = w2 - w1
-// J = [ 0, -1, 0, 1 ]
-//-------------------------------------------------------------------------------------------------
-
-WheelJoint = function(body1, body2, anchor1, anchor2) {
- Joint.call(this, Joint.TYPE_WHEEL, body1, body2, true);
-
- // Local anchor points
- this.anchor1 = this.body1.getLocalPoint(anchor1);
- this.anchor2 = this.body2.getLocalPoint(anchor2);
-
- var d = vec2.sub(anchor2, anchor1);
-
- // Rest length
- this.restLength = d.length();
-
- // Body1's local axis
- this.u_local = this.body1.getLocalVector(vec2.normalize(d));
- this.n_local = vec2.perp(this.u_local);
-
- // Accumulated impulse
- this.lambda_acc = 0;
- this.motorLambda_acc = 0;
- this.springLambda_acc = 0;
-
- // Motor
- this.motorEnabled = false;
- this.motorSpeed = 0;
- this.maxMotorTorque = 0;
-
- // Soft constraint coefficients
- this.gamma = 0;
- this.beta_c = 0;
-
- // Spring coefficients
- this.frequencyHz = 0;
- this.dampingRatio = 0;
-}
-
-WheelJoint.prototype = new Joint;
-WheelJoint.prototype.constructor = WheelJoint;
-
-WheelJoint.prototype.setWorldAnchor1 = function(anchor1) {
- this.anchor1 = this.body1.getLocalPoint(anchor1);
-
- var d = vec2.sub(this.getWorldAnchor2(), anchor1);
-
- this.u_local = this.body1.getLocalVector(vec2.normalize(d));
- this.n_local = vec2.perp(this.u_local);
-}
-
-WheelJoint.prototype.setWorldAnchor2 = function(anchor2) {
- this.anchor2 = this.body2.getLocalPoint(anchor2);
-
- var d = vec2.sub(anchor2, this.getWorldAnchor1());
-
- this.u_local = this.body1.getLocalVector(vec2.normalize(d));
- this.n_local = vec2.perp(this.u_local);
-}
-
-WheelJoint.prototype.serialize = function() {
- return {
- "type": "WheelJoint",
- "body1": this.body1.id,
- "body2": this.body2.id,
- "anchor1": this.body1.getWorldPoint(this.anchor1),
- "anchor2": this.body2.getWorldPoint(this.anchor2),
- "collideConnected": this.collideConnected,
- "maxForce": this.maxForce,
- "breakable": this.breakable,
- "motorEnabled": this.motorEnabled,
- "motorSpeed": this.motorSpeed,
- "maxMotorTorque": this.maxMotorTorque,
- "frequencyHz": this.frequencyHz,
- "dampingRatio": this.dampingRatio
- };
-}
-
-WheelJoint.prototype.setSpringFrequencyHz = function(frequencyHz) {
- // NOTE: frequencyHz should be limited to under 4 times time steps
- this.frequencyHz = frequencyHz;
-}
-
-WheelJoint.prototype.setSpringDampingRatio = function(dampingRatio) {
- this.dampingRatio = dampingRatio;
-}
-
-WheelJoint.prototype.enableMotor = function(flag) {
- this.motorEnabled = flag;
-}
-
-WheelJoint.prototype.setMotorSpeed = function(speed) {
- this.motorSpeed = speed;
-}
-
-WheelJoint.prototype.setMaxMotorTorque = function(torque) {
- this.maxMotorTorque = torque;
-}
-
-WheelJoint.prototype.initSolver = function(dt, warmStarting) {
- var body1 = this.body1;
- var body2 = this.body2;
-
- // Max impulse
- this.maxImpulse = this.maxForce * dt;
-
- // Transformed r1, r2
- this.r1 = body1.xf.rotate(vec2.sub(this.anchor1, body1.centroid));
- this.r2 = body2.xf.rotate(vec2.sub(this.anchor2, body2.centroid));
-
- // World anchor points
- var p1 = vec2.add(body1.p, this.r1);
- var p2 = vec2.add(body2.p, this.r2);
-
- // Delta vector between world anchor points
- var d = vec2.sub(p2, p1);
-
- // r1 + d
- this.r1_d = vec2.add(this.r1, d);
-
- // World line normal
- this.n = vec2.rotate(this.n_local, body1.a);
-
- // sn1, sn2
- this.sn1 = vec2.cross(this.r1_d, this.n);
- this.sn2 = vec2.cross(this.r2, this.n);
-
- // invEM = J * invM * JT
- var em_inv = body1.m_inv + body2.m_inv + body1.i_inv * this.sn1 * this.sn1 + body2.i_inv * this.sn2 * this.sn2;
- this.em = em_inv > 0 ? 1 / em_inv : em_inv;
-
- // Compute soft constraint parameters
- if (this.frequencyHz > 0) {
- // World delta axis
- this.u = vec2.rotate(this.u_local, body1.a);
-
- // su1, su2
- this.su1 = vec2.cross(this.r1_d, this.u);
- this.su2 = vec2.cross(this.r2, this.u);
-
- // invEM = J * invM * JT
- var springEm_inv = body1.m_inv + body2.m_inv + body1.i_inv * this.su1 * this.su1 + body2.i_inv * this.su2 * this.su2;
- springEm = springEm_inv == 0 ? 0 : 1 / springEm_inv;
-
- // Frequency
- var omega = 2 * Math.PI * this.frequencyHz;
-
- // Spring stiffness
- var k = springEm * omega * omega;
-
- // Damping coefficient
- var c = springEm * 2 * this.dampingRatio * omega;
-
- // Soft constraint formulas
- // gamma and beta are divided by dt to reduce computation
- this.gamma = (c + k * dt) * dt;
- this.gamma = this.gamma == 0 ? 0 : 1 / this.gamma;
- var beta = dt * k * this.gamma;
-
- // Position constraint
- var pc = vec2.dot(d, this.u) - this.restLength;
- this.beta_c = beta * pc;
-
- // invEM = invEM + gamma * I (to reduce calculation)
- springEm_inv = springEm_inv + this.gamma;
- this.springEm = springEm_inv == 0 ? 0 : 1 / springEm_inv;
- }
- else {
- this.gamma = 0;
- this.beta_c = 0;
- this.springLambda_acc = 0;
- }
-
- if (this.motorEnabled) {
- this.maxMotorImpulse = this.maxMotorTorque * dt;
-
- // invEM2 = J2 * invM * J2T
- var motorEm_inv = body1.i_inv + body2.i_inv;
- this.motorEm = motorEm_inv > 0 ? 1 / motorEm_inv : motorEm_inv;
- }
- else {
- this.motorEm = 0;
- this.motorLambda_acc = 0;
- }
-
- if (warmStarting) {
- // impulse = JT * lambda
- var linearImpulse = vec2.scale(this.n, this.lambda_acc);
- var angularImpulse1 = this.sn1 * this.lambda_acc + this.motorLambda_acc;
- var angularImpulse2 = this.sn2 * this.lambda_acc + this.motorLambda_acc;
-
- if (this.frequencyHz > 0) {
- linearImpulse.addself(vec2.scale(this.u, this.springLambda_acc));
- angularImpulse1 += this.su1 * this.springLambda_acc;
- angularImpulse2 += this.su2 * this.springLambda_acc;
- }
-
- // Apply cached constraint impulses
- // V += JT * lambda * invM
- body1.v.mad(linearImpulse, -body1.m_inv);
- body1.w -= angularImpulse1 * body1.i_inv;
-
- body2.v.mad(linearImpulse, body2.m_inv);
- body2.w += angularImpulse2 * body2.i_inv;
- }
- else {
- this.lambda_acc = 0;
- this.springLambda_acc = 0;
- this.motorLambda_acc = 0;
- }
-}
-
-WheelJoint.prototype.solveVelocityConstraints = function() {
- var body1 = this.body1;
- var body2 = this.body2;
-
- // Solve spring constraint
- if (this.frequencyHz > 0) {
- // Compute lambda for velocity constraint
- // Solve J * invM * JT * lambda = -(J * V + beta * C + gamma * (lambda_acc + lambda))
- var cdot = this.u.dot(vec2.sub(body2.v, body1.v)) + this.su2 * body2.w - this.su1 * body1.w;
- var soft = this.beta_c + this.gamma * this.springLambda_acc;
- var lambda = -this.springEm * (cdot + soft);
-
- // Accumulate lambda
- this.springLambda_acc += lambda;
-
- // linearImpulse = JT * lambda
- var impulse = vec2.scale(this.u, lambda);
-
- // Apply constraint impulses
- // V += JT * lambda * invM
- body1.v.mad(impulse, -body1.m_inv);
- body1.w -= this.su1 * lambda * body1.i_inv;
-
- body2.v.mad(impulse, body2.m_inv);
- body2.w += this.su2 * lambda * body2.i_inv;
- }
-
- // Solve motor constraint
- if (this.motorEnabled) {
- // Compute motor impulse
- var cdot = body2.w - body1.w - this.motorSpeed;
- var lambda = -this.motorEm * cdot;
-
- var motorLambdaOld = this.motorLambda_acc;
- this.motorLambda_acc = Math.clamp(this.motorLambda_acc + lambda, -this.maxMotorImpulse, this.maxMotorImpulse);
- lambda = this.motorLambda_acc - motorLambdaOld;
-
- // Apply motor impulses
- body1.w -= lambda * body1.i_inv;
- body2.w += lambda * body2.i_inv;
- }
-
- // Compute lambda for velocity constraint
- // Solve J * invM * JT * lambda = -J * V
- var cdot = this.n.dot(vec2.sub(body2.v, body1.v)) + this.sn2 * body2.w - this.sn1 * body1.w;
- var lambda = -this.em * cdot;
-
- // Accumulate lambda
- this.lambda_acc += lambda;
-
- // linearImpulse = JT * lambda
- var impulse = vec2.scale(this.n, lambda);
-
- // Apply constraint impulses
- // V += JT * lambda * invM
- body1.v.mad(impulse, -body1.m_inv);
- body1.w -= this.sn1 * lambda * body1.i_inv;
-
- body2.v.mad(impulse, body2.m_inv);
- body2.w += this.sn2 * lambda * body2.i_inv;
-}
-
-WheelJoint.prototype.solvePositionConstraints = function() {
- var body1 = this.body1;
- var body2 = this.body2;
-
- // Transformed r1, r2
- var r1 = vec2.rotate(vec2.sub(this.anchor1, body1.centroid), body1.a);
- var r2 = vec2.rotate(vec2.sub(this.anchor2, body2.centroid), body2.a);
-
- // World anchor points
- var p1 = vec2.add(body1.p, r1);
- var p2 = vec2.add(body2.p, r2);
-
- // Delta vector between world anchor points
- var d = vec2.sub(p2, p1);
-
- // r1 + d
- var r1_d = vec2.add(r1, d);
-
- // World line normal
- var n = vec2.rotate(this.n_local, body1.a);
-
- // Position constraint
- var c = vec2.dot(n, d);
- var correction = Math.clamp(c, -Joint.MAX_LINEAR_CORRECTION, Joint.MAX_LINEAR_CORRECTION);
-
- // Compute lambda for position constraint
- // Solve J * invM * JT * lambda = -C / dt
- var s1 = vec2.cross(r1_d, n);
- var s2 = vec2.cross(r2, n);
- var em_inv = body1.m_inv + body2.m_inv + body1.i_inv * s1 * s1 + body2.i_inv * s2 * s2;
- var k_inv = em_inv == 0 ? 0 : 1 / em_inv;
- var lambda_dt = k_inv * (-correction);
-
- // Apply constraint impulses
- // impulse = JT * lambda
- // X += impulse * invM * dt
- var impulse_dt = vec2.scale(n, lambda_dt);
-
- body1.p.mad(impulse_dt, -body1.m_inv);
- body1.a -= s1 * lambda_dt * body1.i_inv;
-
- body2.p.mad(impulse_dt, body2.m_inv);
- body2.a += s2 * lambda_dt * body2.i_inv;
-
- return Math.abs(c) < Joint.LINEAR_SLOP;
-}
-
-WheelJoint.prototype.getReactionForce = function(dt_inv) {
- return vec2.scale(this.n, this.lambda_acc * dt_inv);
-}
-
-WheelJoint.prototype.getReactionTorque = function(dt_inv) {
- return 0;
-}
-
diff --git a/src/physics/advanced/material/ContactMaterial.js b/src/physics/advanced/material/ContactMaterial.js
new file mode 100644
index 00000000..6040c2d6
--- /dev/null
+++ b/src/physics/advanced/material/ContactMaterial.js
@@ -0,0 +1,81 @@
+module.exports = ContactMaterial;
+
+var idCounter = 0;
+
+/**
+ * Defines a physics material.
+ * @class ContactMaterial
+ * @constructor
+ * @param {Material} materialA
+ * @param {Material} materialB
+ * @param {Object} [options]
+ * @param {Number} options.friction
+ * @param {Number} options.restitution
+ * @author schteppe
+ */
+function ContactMaterial(materialA, materialB, options){
+ options = options || {};
+
+ /**
+ * The contact material identifier
+ * @property id
+ * @type {Number}
+ */
+ this.id = idCounter++;
+
+ /**
+ * First material participating in the contact material
+ * @property materialA
+ * @type {Material}
+ */
+ this.materialA = materialA;
+
+ /**
+ * Second material participating in the contact material
+ * @property materialB
+ * @type {Material}
+ */
+ this.materialB = materialB;
+
+ /**
+ * Friction to use in the contact of these two materials
+ * @property friction
+ * @type {Number}
+ */
+ this.friction = typeof(options.friction) !== "undefined" ? Number(options.friction) : 0.3;
+
+ /**
+ * Restitution to use in the contact of these two materials
+ * @property restitution
+ * @type {Number}
+ */
+ this.restitution = typeof(options.restitution) !== "undefined" ? Number(options.restitution) : 0.3;
+
+ /**
+ * Stiffness of the resulting ContactEquation that this ContactMaterial generate
+ * @property stiffness
+ * @type {Number}
+ */
+ this.stiffness = typeof(options.stiffness) !== "undefined" ? Number(options.stiffness) : 1e7;
+
+ /**
+ * Relaxation of the resulting ContactEquation that this ContactMaterial generate
+ * @property relaxation
+ * @type {Number}
+ */
+ this.relaxation = typeof(options.relaxation) !== "undefined" ? Number(options.relaxation) : 3;
+
+ /**
+ * Stiffness of the resulting FrictionEquation that this ContactMaterial generate
+ * @property frictionStiffness
+ * @type {Number}
+ */
+ this.frictionStiffness = typeof(options.frictionStiffness) !== "undefined" ? Number(options.frictionStiffness) : 1e7;
+
+ /**
+ * Relaxation of the resulting FrictionEquation that this ContactMaterial generate
+ * @property frictionRelaxation
+ * @type {Number}
+ */
+ this.frictionRelaxation = typeof(options.frictionRelaxation) !== "undefined" ? Number(options.frictionRelaxation) : 3;
+};
diff --git a/src/physics/advanced/material/Material.js b/src/physics/advanced/material/Material.js
new file mode 100644
index 00000000..7c878dd9
--- /dev/null
+++ b/src/physics/advanced/material/Material.js
@@ -0,0 +1,19 @@
+module.exports = Material;
+
+var idCounter = 0;
+
+/**
+ * Defines a physics material.
+ * @class Material
+ * @constructor
+ * @param string name
+ * @author schteppe
+ */
+function Material(){
+ /**
+ * The material identifier
+ * @property id
+ * @type {Number}
+ */
+ this.id = idCounter++;
+};
diff --git a/src/physics/advanced/math/mat2.js b/src/physics/advanced/math/mat2.js
new file mode 100644
index 00000000..db48ea85
--- /dev/null
+++ b/src/physics/advanced/math/mat2.js
@@ -0,0 +1,10 @@
+/**
+ * The mat2 object from glMatrix, extended with the functions documented here. See http://glmatrix.net for full doc.
+ * @class mat2
+ */
+
+// Only import mat2 from gl-matrix and skip the rest
+var mat2 = require('../../node_modules/gl-matrix/src/gl-matrix/mat2').mat2;
+
+// Export everything
+module.exports = mat2;
diff --git a/src/physics/advanced/math/polyk.js b/src/physics/advanced/math/polyk.js
new file mode 100644
index 00000000..1431d909
--- /dev/null
+++ b/src/physics/advanced/math/polyk.js
@@ -0,0 +1,477 @@
+
+ /*
+ PolyK library
+ url: http://polyk.ivank.net
+ Released under MIT licence.
+
+ Copyright (c) 2012 Ivan Kuckir
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation
+ files (the "Software"), to deal in the Software without
+ restriction, including without limitation the rights to use,
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following
+ conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+ var PolyK = {};
+
+ /*
+ Is Polygon self-intersecting?
+
+ O(n^2)
+ */
+ /*
+ PolyK.IsSimple = function(p)
+ {
+ var n = p.length>>1;
+ if(n<4) return true;
+ var a1 = new PolyK._P(), a2 = new PolyK._P();
+ var b1 = new PolyK._P(), b2 = new PolyK._P();
+ var c = new PolyK._P();
+
+ for(var i=0; i>1;
+ if(n<3) return [];
+ var tgs = [];
+ var avl = [];
+ for(var i=0; i 3)
+ {
+ var i0 = avl[(i+0)%al];
+ var i1 = avl[(i+1)%al];
+ var i2 = avl[(i+2)%al];
+
+ var ax = p[2*i0], ay = p[2*i0+1];
+ var bx = p[2*i1], by = p[2*i1+1];
+ var cx = p[2*i2], cy = p[2*i2+1];
+
+ var earFound = false;
+ if(PolyK._convex(ax, ay, bx, by, cx, cy))
+ {
+ earFound = true;
+ for(var j=0; j 3*al) break; // no convex angles :(
+ }
+ tgs.push(avl[0], avl[1], avl[2]);
+ return tgs;
+ }
+ /*
+ PolyK.ContainsPoint = function(p, px, py)
+ {
+ var n = p.length>>1;
+ var ax, ay, bx = p[2*n-2]-px, by = p[2*n-1]-py;
+ var depth = 0;
+ for(var i=0; i=0 && by>=0) continue; // both "up" or both "donw"
+ if(ax< 0 && bx< 0) continue;
+
+ var lx = ax + (bx-ax)*(-ay)/(by-ay);
+ if(lx>0) depth++;
+ }
+ return (depth & 1) == 1;
+ }
+
+ PolyK.Slice = function(p, ax, ay, bx, by)
+ {
+ if(PolyK.ContainsPoint(p, ax, ay) || PolyK.ContainsPoint(p, bx, by)) return [p.slice(0)];
+
+ var a = new PolyK._P(ax, ay);
+ var b = new PolyK._P(bx, by);
+ var iscs = []; // intersections
+ var ps = []; // points
+ for(var i=0; i 0)
+ {
+ var n = ps.length;
+ var i0 = iscs[0];
+ var i1 = iscs[1];
+ var ind0 = ps.indexOf(i0);
+ var ind1 = ps.indexOf(i1);
+ var solved = false;
+
+ if(PolyK._firstWithFlag(ps, ind0) == ind1) solved = true;
+ else
+ {
+ i0 = iscs[1];
+ i1 = iscs[0];
+ ind0 = ps.indexOf(i0);
+ ind1 = ps.indexOf(i1);
+ if(PolyK._firstWithFlag(ps, ind0) == ind1) solved = true;
+ }
+ if(solved)
+ {
+ dir--;
+ var pgn = PolyK._getPoints(ps, ind0, ind1);
+ pgs.push(pgn);
+ ps = PolyK._getPoints(ps, ind1, ind0);
+ i0.flag = i1.flag = false;
+ iscs.splice(0,2);
+ if(iscs.length == 0) pgs.push(ps);
+ }
+ else { dir++; iscs.reverse(); }
+ if(dir>1) break;
+ }
+ var result = [];
+ for(var i=0; i>1, isc);
+ }
+ b1.x = b2.x; b1.y = b2.y;
+ b2.x = p[0]; b2.y = p[1];
+ PolyK._pointLineDist(a1, b1, b2, l>>1, isc);
+
+ var idst = 1/isc.dist;
+ isc.norm.x = (x-isc.point.x)*idst;
+ isc.norm.y = (y-isc.point.y)*idst;
+ return isc;
+ }
+
+ PolyK._pointLineDist = function(p, a, b, edge, isc)
+ {
+ var x = p.x, y = p.y, x1 = a.x, y1 = a.y, x2 = b.x, y2 = b.y;
+
+ var A = x - x1;
+ var B = y - y1;
+ var C = x2 - x1;
+ var D = y2 - y1;
+
+ var dot = A * C + B * D;
+ var len_sq = C * C + D * D;
+ var param = dot / len_sq;
+
+ var xx, yy;
+
+ if (param < 0 || (x1 == x2 && y1 == y2)) {
+ xx = x1;
+ yy = y1;
+ }
+ else if (param > 1) {
+ xx = x2;
+ yy = y2;
+ }
+ else {
+ xx = x1 + param * C;
+ yy = y1 + param * D;
+ }
+
+ var dx = x - xx;
+ var dy = y - yy;
+ var dst = Math.sqrt(dx * dx + dy * dy);
+ if(dst= 0) && (v >= 0) && (u + v < 1);
+ }
+ /*
+ PolyK._RayLineIntersection = function(a1, a2, b1, b2, c)
+ {
+ var dax = (a1.x-a2.x), dbx = (b1.x-b2.x);
+ var day = (a1.y-a2.y), dby = (b1.y-b2.y);
+
+ var Den = dax*dby - day*dbx;
+ if (Den == 0) return null; // parallel
+
+ var A = (a1.x * a2.y - a1.y * a2.x);
+ var B = (b1.x * b2.y - b1.y * b2.x);
+
+ var I = c;
+ var iDen = 1/Den;
+ I.x = ( A*dbx - dax*B ) * iDen;
+ I.y = ( A*dby - day*B ) * iDen;
+
+ if(!PolyK._InRect(I, b1, b2)) return null;
+ if((day>0 && I.y>a1.y) || (day<0 && I.y0 && I.x>a1.x) || (dax<0 && I.x=Math.min(b.y, c.y) && a.y<=Math.max(b.y, c.y));
+ if (b.y == c.y) return (a.x>=Math.min(b.x, c.x) && a.x<=Math.max(b.x, c.x));
+
+ if(a.x >= Math.min(b.x, c.x) && a.x <= Math.max(b.x, c.x)
+ && a.y >= Math.min(b.y, c.y) && a.y <= Math.max(b.y, c.y))
+ return true;
+ return false;
+ }
+ */
+ PolyK._convex = function(ax, ay, bx, by, cx, cy)
+ {
+ return (ay-by)*(cx-bx) + (bx-ax)*(cy-by) >= 0;
+ }
+ /*
+ PolyK._P = function(x,y)
+ {
+ this.x = x;
+ this.y = y;
+ this.flag = false;
+ }
+ PolyK._P.prototype.toString = function()
+ {
+ return "Point ["+this.x+", "+this.y+"]";
+ }
+ PolyK._P.dist = function(a,b)
+ {
+ var dx = b.x-a.x;
+ var dy = b.y-a.y;
+ return Math.sqrt(dx*dx + dy*dy);
+ }
+
+ PolyK._tp = [];
+ for(var i=0; i<10; i++) PolyK._tp.push(new PolyK._P(0,0));
+ */
+
+module.exports = PolyK;
diff --git a/src/physics/advanced/math/vec2.js b/src/physics/advanced/math/vec2.js
new file mode 100644
index 00000000..8f4cd0a5
--- /dev/null
+++ b/src/physics/advanced/math/vec2.js
@@ -0,0 +1,122 @@
+/**
+ * The vec2 object from glMatrix, extended with the functions documented here. See http://glmatrix.net for full doc.
+ * @class vec2
+ */
+
+// Only import vec2 from gl-matrix and skip the rest
+var vec2 = require('../../node_modules/gl-matrix/src/gl-matrix/vec2').vec2;
+
+// Now add some extensions
+
+/**
+ * Get the vector x component
+ * @method getX
+ * @static
+ * @param {Float32Array} a
+ * @return {Number}
+ */
+vec2.getX = function(a){
+ return a[0];
+};
+
+/**
+ * Get the vector y component
+ * @method getY
+ * @static
+ * @param {Float32Array} a
+ * @return {Number}
+ */
+vec2.getY = function(a){
+ return a[1];
+};
+
+/**
+ * Make a cross product and only return the z component
+ * @method crossLength
+ * @static
+ * @param {Float32Array} a
+ * @param {Float32Array} b
+ * @return {Number}
+ */
+vec2.crossLength = function(a,b){
+ return a[0] * b[1] - a[1] * b[0];
+};
+
+/**
+ * Cross product between a vector and the Z component of a vector
+ * @method crossVZ
+ * @static
+ * @param {Float32Array} out
+ * @param {Float32Array} vec
+ * @param {Number} zcomp
+ * @return {Number}
+ */
+vec2.crossVZ = function(out, vec, zcomp){
+ vec2.rotate(out,vec,-Math.PI/2);// Rotate according to the right hand rule
+ vec2.scale(out,out,zcomp); // Scale with z
+ return out;
+};
+
+/**
+ * Cross product between a vector and the Z component of a vector
+ * @method crossZV
+ * @static
+ * @param {Float32Array} out
+ * @param {Number} zcomp
+ * @param {Float32Array} vec
+ * @return {Number}
+ */
+vec2.crossZV = function(out, zcomp, vec){
+ vec2.rotate(out,vec,Math.PI/2); // Rotate according to the right hand rule
+ vec2.scale(out,out,zcomp); // Scale with z
+ return out;
+};
+
+/**
+ * Rotate a vector by an angle
+ * @method rotate
+ * @static
+ * @param {Float32Array} out
+ * @param {Float32Array} a
+ * @param {Number} angle
+ */
+vec2.rotate = function(out,a,angle){
+ var c = Math.cos(angle),
+ s = Math.sin(angle),
+ x = a[0],
+ y = a[1];
+ out[0] = c*x -s*y;
+ out[1] = s*x +c*y;
+};
+
+vec2.toLocalFrame = function(out, worldPoint, framePosition, frameAngle){
+ vec2.copy(out, worldPoint);
+ vec2.sub(out, out, framePosition);
+ vec2.rotate(out, out, -frameAngle);
+};
+
+vec2.toGlobalFrame = function(out, localPoint, framePosition, frameAngle){
+ vec2.copy(out, localPoint);
+ vec2.rotate(out, out, frameAngle);
+ vec2.add(out, out, framePosition);
+};
+
+/**
+ * Compute centroid of a triangle spanned by vectors a,b,c. See http://easycalculation.com/analytical/learn-centroid.php
+ * @method centroid
+ * @static
+ * @param {Float32Array} out
+ * @param {Float32Array} a
+ * @param {Float32Array} b
+ * @param {Float32Array} c
+ * @return {Float32Array} The out object
+ */
+vec2.centroid = function(out, a, b, c){
+ vec2.add(out, a, b);
+ vec2.add(out, out, c);
+ vec2.scale(out, out, 1/3);
+ return out;
+};
+
+// Export everything
+module.exports = vec2;
diff --git a/src/physics/advanced/objects/Body.js b/src/physics/advanced/objects/Body.js
new file mode 100644
index 00000000..ed1daead
--- /dev/null
+++ b/src/physics/advanced/objects/Body.js
@@ -0,0 +1,330 @@
+var vec2 = require('../math/vec2');
+
+module.exports = Body;
+
+var zero = vec2.fromValues(0,0);
+
+/**
+ * A rigid body. Has got a center of mass, position, velocity and a number of
+ * shapes that are used for collisions.
+ *
+ * @class Body
+ * @constructor
+ * @param {Object} [options]
+ * @param {Number} [options.mass=0] A number >= 0. If zero, the .motionState will be set to Body.STATIC.
+ * @param {Float32Array|Array} [options.position]
+ * @param {Float32Array|Array} [options.velocity]
+ * @param {Number} [options.angle=0]
+ * @param {Number} [options.angularVelocity=0]
+ * @param {Float32Array|Array} [options.force]
+ * @param {Number} [options.angularForce=0]
+ *
+ * @todo Should not take mass as argument to Body, but as density to each Shape
+ */
+function Body(options){
+ options = options || {};
+
+ /**
+ * The body identifyer
+ * @property id
+ * @type {Number}
+ */
+ this.id = ++Body._idCounter;
+
+ /**
+ * The shapes of the body. The local transform of the shape in .shapes[i] is
+ * defined by .shapeOffsets[i] and .shapeAngles[i].
+ *
+ * @property shapes
+ * @type {Array}
+ */
+ this.shapes = [];
+
+ /**
+ * The local shape offsets, relative to the body center of mass. This is an
+ * array of Float32Array.
+ * @property shapeOffsets
+ * @type {Array}
+ */
+ this.shapeOffsets = [];
+
+ /**
+ * The body-local shape angle transforms. This is an array of numbers (angles).
+ * @property shapeAngles
+ * @type {Array}
+ */
+ this.shapeAngles = [];
+
+ /**
+ * The mass of the body.
+ * @property mass
+ * @type {number}
+ */
+ this.mass = options.mass || 0;
+
+ /**
+ * The inverse mass of the body.
+ * @property invMass
+ * @type {number}
+ */
+ this.invMass = 0;
+
+ /**
+ * The inertia of the body around the Z axis.
+ * @property inertia
+ * @type {number}
+ */
+ this.inertia = 0;
+
+ /**
+ * The inverse inertia of the body.
+ * @property invInertia
+ * @type {number}
+ */
+ this.invInertia = 0;
+
+ this.updateMassProperties();
+
+ /**
+ * The position of the body
+ * @property position
+ * @type {Float32Array}
+ */
+ this.position = vec2.fromValues(0,0);
+ if(options.position) vec2.copy(this.position, options.position);
+
+ /**
+ * The velocity of the body
+ * @property velocity
+ * @type {Float32Array}
+ */
+ this.velocity = vec2.fromValues(0,0);
+ if(options.velocity) vec2.copy(this.velocity, options.velocity);
+
+ /**
+ * Constraint velocity that was added to the body during the last step.
+ * @property vlambda
+ * @type {Float32Array}
+ */
+ this.vlambda = vec2.fromValues(0,0);
+
+ /**
+ * Angular constraint velocity that was added to the body during last step.
+ * @property wlambda
+ * @type {Float32Array}
+ */
+ this.wlambda = 0;
+
+ /**
+ * The angle of the body
+ * @property angle
+ * @type {number}
+ */
+ this.angle = options.angle || 0;
+
+ /**
+ * The angular velocity of the body
+ * @property angularVelocity
+ * @type {number}
+ */
+ this.angularVelocity = options.angularVelocity || 0;
+
+ /**
+ * The force acting on the body
+ * @property force
+ * @type {Float32Array}
+ */
+ this.force = vec2.create();
+ if(options.force) vec2.copy(this.force, options.force);
+
+ /**
+ * The angular force acting on the body
+ * @property angularForce
+ * @type {number}
+ */
+ this.angularForce = options.angularForce || 0;
+
+ /**
+ * The type of motion this body has. Should be one of: Body.STATIC (the body
+ * does not move), Body.DYNAMIC (body can move and respond to collisions)
+ * and Body.KINEMATIC (only moves according to its .velocity).
+ *
+ * @property motionState
+ * @type {number}
+ *
+ * @example
+ * // This body will move and interact with other bodies
+ * var dynamicBody = new Body();
+ * dynamicBody.motionState = Body.DYNAMIC;
+ *
+ * @example
+ * // This body will not move at all
+ * var staticBody = new Body();
+ * staticBody.motionState = Body.STATIC;
+ *
+ * @example
+ * // This body will only move if you change its velocity
+ * var kinematicBody = new Body();
+ * kinematicBody.motionState = Body.KINEMATIC;
+ */
+ this.motionState = this.mass == 0 ? Body.STATIC : Body.DYNAMIC;
+
+ /**
+ * Bounding circle radius
+ * @property boundingRadius
+ * @type {Number}
+ */
+ this.boundingRadius = 0;
+};
+
+Body._idCounter = 0;
+
+/**
+ * Update the bounding radius of the body. Should be done if any of the shapes
+ * are changed.
+ * @method updateBoundingRadius
+ */
+Body.prototype.updateBoundingRadius = function(){
+ var shapes = this.shapes,
+ shapeOffsets = this.shapeOffsets,
+ N = shapes.length,
+ radius = 0;
+
+ for(var i=0; i!==N; i++){
+ var shape = shapes[i],
+ offset = vec2.length(shapeOffsets[i] || zero),
+ r = shape.boundingRadius;
+ if(offset + r > radius)
+ radius = offset + r;
+ }
+
+ this.boundingRadius = radius;
+};
+
+/**
+ * Add a shape to the body. You can pass a local transform when adding a shape,
+ * so that the shape gets an offset and angle relative to the body center of mass.
+ * Will automatically update the mass properties and bounding radius.
+ *
+ * @method addShape
+ * @param {Shape} shape
+ * @param {Float32Array|Array} [offset] Local body offset of the shape.
+ * @param {Number} [angle] Local body angle.
+ *
+ * @example
+ * var body = new Body(),
+ * shape = new Circle();
+ *
+ * // Add the shape to the body, positioned in the center
+ * body.addShape(shape);
+ *
+ * // Add another shape to the body, positioned 1 unit length from the body center of mass along the local x-axis.
+ * body.addShape(shape,[1,0]);
+ *
+ * // Add another shape to the body, positioned 1 unit length from the body center of mass along the local y-axis, and rotated 90 degrees CCW.
+ * body.addShape(shape,[0,1],Math.PI/2);
+ */
+Body.prototype.addShape = function(shape,offset,angle){
+ this.shapes .push(shape);
+ this.shapeOffsets.push(offset);
+ this.shapeAngles .push(angle);
+ this.updateMassProperties();
+ this.updateBoundingRadius();
+};
+
+/**
+ * Updates .inertia, .invMass, .invInertia for this Body. Should be called when
+ * changing the structure or mass of the Body.
+ *
+ * @method updateMassProperties
+ *
+ * @example
+ * body.mass += 1;
+ * body.updateMassProperties();
+ */
+Body.prototype.updateMassProperties = function(){
+ var shapes = this.shapes,
+ N = shapes.length,
+ m = this.mass / N,
+ I = 0;
+
+ for(var i=0; i 0 ? 1/this.mass : 0;
+ this.invInertia = I>0 ? 1/I : 0;
+};
+
+var Body_applyForce_r = vec2.create();
+
+/**
+ * Apply force to a world point. This could for example be a point on the RigidBody surface. Applying force this way will add to Body.force and Body.angularForce.
+ * @method applyForce
+ * @param {Float32Array} force The force to add.
+ * @param {Float32Array} worldPoint A world point to apply the force on.
+ */
+Body.prototype.applyForce = function(force,worldPoint){
+ // Compute point position relative to the body center
+ var r = Body_applyForce_r;
+ vec2.sub(r,worldPoint,this.position);
+
+ // Add linear force
+ vec2.add(this.force,this.force,force);
+
+ // Compute produced rotational force
+ var rotForce = vec2.crossLength(r,force);
+
+ // Add rotational force
+ this.angularForce += rotForce;
+};
+
+/**
+ * Transform a world point to local body frame.
+ * @method toLocalFrame
+ * @param {Float32Array|Array} out The vector to store the result in
+ * @param {Float32Array|Array} worldPoint The input world vector
+ */
+Body.prototype.toLocalFrame = function(out, worldPoint){
+ vec2.toLocalFrame(out, worldPoint, this.position, this.angle);
+};
+
+/**
+ * Transform a local point to world frame.
+ * @method toWorldFrame
+ * @param {Array} out The vector to store the result in
+ * @param {Array} localPoint The input local vector
+ */
+Body.prototype.toWorldFrame = function(out, localPoint){
+ vec2.toGlobalFrame(out, localPoint, this.position, this.angle);
+};
+
+/**
+ * Dynamic body.
+ * @property DYNAMIC
+ * @type {Number}
+ * @static
+ */
+Body.DYNAMIC = 1;
+
+/**
+ * Static body.
+ * @property STATIC
+ * @type {Number}
+ * @static
+ */
+Body.STATIC = 2;
+
+/**
+ * Kinematic body.
+ * @property KINEMATIC
+ * @type {Number}
+ * @static
+ */
+Body.KINEMATIC = 4;
diff --git a/src/physics/advanced/objects/Spring.js b/src/physics/advanced/objects/Spring.js
new file mode 100644
index 00000000..44f7131c
--- /dev/null
+++ b/src/physics/advanced/objects/Spring.js
@@ -0,0 +1,181 @@
+var vec2 = require('../math/vec2');
+
+module.exports = Spring;
+
+/**
+ * A spring, connecting two bodies.
+ *
+ * @class Spring
+ * @constructor
+ * @param {Body} bodyA
+ * @param {Body} bodyB
+ * @param {Object} [options]
+ * @param {number} options.restLength A number > 0. Default: 1
+ * @param {number} options.stiffness A number >= 0. Default: 100
+ * @param {number} options.damping A number >= 0. Default: 1
+ * @param {Array} options.worldAnchorA Where to hook the spring to body A, in world coordinates.
+ * @param {Array} options.worldAnchorB
+ * @param {Array} options.localAnchorA Where to hook the spring to body A, in local body coordinates.
+ * @param {Array} options.localAnchorB
+ */
+function Spring(bodyA,bodyB,options){
+ options = options || {};
+
+ /**
+ * Rest length of the spring.
+ * @property restLength
+ * @type {number}
+ */
+ this.restLength = typeof(options.restLength)=="number" ? options.restLength : 1;
+
+ /**
+ * Stiffness of the spring.
+ * @property stiffness
+ * @type {number}
+ */
+ this.stiffness = options.stiffness || 100;
+
+ /**
+ * Damping of the spring.
+ * @property damping
+ * @type {number}
+ */
+ this.damping = options.damping || 1;
+
+ /**
+ * First connected body.
+ * @property bodyA
+ * @type {Body}
+ */
+ this.bodyA = bodyA;
+
+ /**
+ * Second connected body.
+ * @property bodyB
+ * @type {Body}
+ */
+ this.bodyB = bodyB;
+
+ /**
+ * Anchor for bodyA in local bodyA coordinates.
+ * @property localAnchorA
+ * @type {Array}
+ */
+ this.localAnchorA = vec2.fromValues(0,0);
+
+ /**
+ * Anchor for bodyB in local bodyB coordinates.
+ * @property localAnchorB
+ * @type {Array}
+ */
+ this.localAnchorB = vec2.fromValues(0,0);
+
+ if(options.localAnchorA) vec2.copy(this.localAnchorA, options.localAnchorA);
+ if(options.localAnchorB) vec2.copy(this.localAnchorB, options.localAnchorB);
+ if(options.worldAnchorA) this.setWorldAnchorA(options.worldAnchorA);
+ if(options.worldAnchorB) this.setWorldAnchorB(options.worldAnchorB);
+};
+
+/**
+ * Set the anchor point on body A, using world coordinates.
+ * @method setWorldAnchorA
+ * @param {Array} worldAnchorA
+ */
+Spring.prototype.setWorldAnchorA = function(worldAnchorA){
+ this.bodyA.toLocalFrame(this.localAnchorA, worldAnchorA);
+};
+
+/**
+ * Set the anchor point on body B, using world coordinates.
+ * @method setWorldAnchorB
+ * @param {Array} worldAnchorB
+ */
+Spring.prototype.setWorldAnchorB = function(worldAnchorB){
+ this.bodyB.toLocalFrame(this.localAnchorB, worldAnchorB);
+};
+
+/**
+ * Get the anchor point on body A, in world coordinates.
+ * @method getWorldAnchorA
+ * @param {Array} result The vector to store the result in.
+ */
+Spring.prototype.getWorldAnchorA = function(result){
+ this.bodyA.toWorldFrame(result, this.localAnchorA);
+};
+
+/**
+ * Get the anchor point on body B, in world coordinates.
+ * @method getWorldAnchorB
+ * @param {Array} result The vector to store the result in.
+ */
+Spring.prototype.getWorldAnchorB = function(result){
+ this.bodyB.toWorldFrame(result, this.localAnchorB);
+};
+
+var applyForce_r = vec2.create(),
+ applyForce_r_unit = vec2.create(),
+ applyForce_u = vec2.create(),
+ applyForce_f = vec2.create(),
+ applyForce_worldAnchorA = vec2.create(),
+ applyForce_worldAnchorB = vec2.create(),
+ applyForce_ri = vec2.create(),
+ applyForce_rj = vec2.create(),
+ applyForce_tmp = vec2.create();
+
+/**
+ * Apply the spring force to the connected bodies.
+ * @method applyForce
+ */
+Spring.prototype.applyForce = function(){
+ var k = this.stiffness,
+ d = this.damping,
+ l = this.restLength,
+ bodyA = this.bodyA,
+ bodyB = this.bodyB,
+ r = applyForce_r,
+ r_unit = applyForce_r_unit,
+ u = applyForce_u,
+ f = applyForce_f,
+ tmp = applyForce_tmp;
+
+ var worldAnchorA = applyForce_worldAnchorA,
+ worldAnchorB = applyForce_worldAnchorB,
+ ri = applyForce_ri,
+ rj = applyForce_rj;
+
+ // Get world anchors
+ this.getWorldAnchorA(worldAnchorA);
+ this.getWorldAnchorB(worldAnchorB);
+
+ // Get offset points
+ vec2.sub(ri, worldAnchorA, bodyA.position);
+ vec2.sub(rj, worldAnchorB, bodyB.position);
+
+ // Compute distance vector between world anchor points
+ vec2.sub(r, worldAnchorB, worldAnchorA);
+ var rlen = vec2.len(r);
+ vec2.normalize(r_unit,r);
+
+ //console.log(rlen)
+ //console.log("A",vec2.str(worldAnchorA),"B",vec2.str(worldAnchorB))
+
+ // Compute relative velocity of the anchor points, u
+ vec2.sub(u, bodyB.velocity, bodyA.velocity);
+ vec2.crossZV(tmp, bodyB.angularVelocity, rj);
+ vec2.add(u, u, tmp);
+ vec2.crossZV(tmp, bodyA.angularVelocity, ri);
+ vec2.sub(u, u, tmp);
+
+ // F = - k * ( x - L ) - D * ( u )
+ vec2.scale(f, r_unit, -k*(rlen-l) - d*vec2.dot(u,r_unit));
+
+ // Add forces to bodies
+ vec2.sub( bodyA.force, bodyA.force, f);
+ vec2.add( bodyB.force, bodyB.force, f);
+
+ // Angular force
+ var ri_x_f = vec2.crossLength(ri, f);
+ var rj_x_f = vec2.crossLength(rj, f);
+ bodyA.angularForce -= ri_x_f;
+ bodyB.angularForce += rj_x_f;
+};
diff --git a/src/physics/advanced/p2.js b/src/physics/advanced/p2.js
new file mode 100644
index 00000000..10be36da
--- /dev/null
+++ b/src/physics/advanced/p2.js
@@ -0,0 +1,37 @@
+// Export p2 classes
+module.exports = {
+ Body : require('./objects/Body'),
+ Broadphase : require('./collision/Broadphase'),
+ Capsule : require('./shapes/Capsule'),
+ Circle : require('./shapes/Circle'),
+ Constraint : require('./constraints/Constraint'),
+ ContactEquation : require('./constraints/ContactEquation'),
+ ContactMaterial : require('./material/ContactMaterial'),
+ Convex : require('./shapes/Convex'),
+ DistanceConstraint : require('./constraints/DistanceConstraint'),
+ Equation : require('./constraints/Equation'),
+ EventEmitter : require('./events/EventEmitter'),
+ FrictionEquation : require('./constraints/FrictionEquation'),
+ GridBroadphase : require('./collision/GridBroadphase'),
+ GSSolver : require('./solver/GSSolver'),
+ Island : require('./solver/IslandSolver'),
+ IslandSolver : require('./solver/IslandSolver'),
+ Line : require('./shapes/Line'),
+ Material : require('./material/Material'),
+ NaiveBroadphase : require('./collision/NaiveBroadphase'),
+ Particle : require('./shapes/Particle'),
+ Plane : require('./shapes/Plane'),
+ PointToPointConstraint : require('./constraints/PointToPointConstraint'),
+ PrismaticConstraint : require('./constraints/PrismaticConstraint'),
+ Rectangle : require('./shapes/Rectangle'),
+ RotationalVelocityEquation : require('./constraints/RotationalVelocityEquation'),
+ SAP1DBroadphase : require('./collision/SAP1DBroadphase'),
+ Shape : require('./shapes/Shape'),
+ Solver : require('./solver/Solver'),
+ Spring : require('./objects/Spring'),
+ Utils : require('./utils/Utils'),
+ World : require('./world/World'),
+ QuadTree : require('./collision/QuadTree').QuadTree,
+ vec2 : require('./math/vec2'),
+ version : require('../package.json').version,
+};
diff --git a/src/physics/advanced/shapes/Box.js b/src/physics/advanced/shapes/Box.js
deleted file mode 100644
index 6e6e948d..00000000
--- a/src/physics/advanced/shapes/Box.js
+++ /dev/null
@@ -1,19 +0,0 @@
-//--------------------------------
-// Box
-//--------------------------------
-
-ShapeBox = function(local_x, local_y, w, h) {
- local_x = local_x || 0;
- local_y = local_y || 0;
-
- var hw = w * 0.5;
- var hh = h * 0.5;
- var verts = [
- new vec2(-hw + local_x, +hh + local_y),
- new vec2(-hw + local_x, -hh + local_y),
- new vec2(+hw + local_x, -hh + local_y),
- new vec2(+hw + local_x, +hh + local_y)
- ];
-
- return new ShapePoly(verts);
-}
\ No newline at end of file
diff --git a/src/physics/advanced/shapes/Capsule.js b/src/physics/advanced/shapes/Capsule.js
new file mode 100644
index 00000000..8ff20028
--- /dev/null
+++ b/src/physics/advanced/shapes/Capsule.js
@@ -0,0 +1,39 @@
+var Shape = require('./Shape')
+, vec2 = require('../math/vec2')
+
+module.exports = Capsule;
+
+/**
+ * Capsule shape class.
+ * @class Capsule
+ * @constructor
+ * @extends {Shape}
+ * @param {Number} length The distance between the end points
+ * @param {Number} radius Radius of the capsule
+ */
+function Capsule(length,radius){
+ this.length = length || 1;
+ this.radius = radius || 1;
+
+ Shape.call(this,Shape.CAPSULE);
+};
+Capsule.prototype = new Shape();
+
+/**
+ * Compute the mass moment of inertia of the Capsule.
+ * @method conputeMomentOfInertia
+ * @param {Number} mass
+ * @return {Number}
+ * @todo
+ */
+Capsule.prototype.computeMomentOfInertia = function(mass){
+ // Approximate with rectangle
+ var r = this.radius,
+ w = this.length + r, // 2*r is too much, 0 is too little
+ h = r*2;
+ return mass * (h*h + w*w) / 12;
+};
+
+Capsule.prototype.updateBoundingRadius = function(){
+ this.boundingRadius = this.radius + this.length/2;
+};
diff --git a/src/physics/advanced/shapes/Circle.js b/src/physics/advanced/shapes/Circle.js
index d2c7f18c..447e2cd9 100644
--- a/src/physics/advanced/shapes/Circle.js
+++ b/src/physics/advanced/shapes/Circle.js
@@ -1,102 +1,31 @@
-/*
-* Copyright (c) 2012 Ju Hyung Lee
-*
-* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
-* and associated documentation files (the "Software"), to deal in the Software without
-* restriction, including without limitation the rights to use, copy, modify, merge, publish,
-* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
-* Software is furnished to do so, subject to the following conditions:
-*
-* The above copyright notice and this permission notice shall be included in all copies or
-* substantial portions of the Software.
-*
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
-* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
+var Shape = require('./Shape');
-//------------------------------------------
-// ShapeCircle
-//------------------------------------------
+module.exports = Circle;
-ShapeCircle = function(local_x, local_y, radius) {
- Shape.call(this, Shape.TYPE_CIRCLE);
- this.c = new vec2(local_x || 0, local_y || 0);
- this.r = radius;
+/**
+ * Circle shape class.
+ * @class Circle
+ * @extends {Shape}
+ * @constructor
+ * @param {number} radius
+ */
+function Circle(radius){
- this.tc = vec2.zero;
+ /**
+ * The radius of the circle.
+ * @property radius
+ * @type {number}
+ */
+ this.radius = radius || 1;
- this.finishVerts();
-}
+ Shape.call(this,Shape.CIRCLE);
+};
+Circle.prototype = new Shape();
+Circle.prototype.computeMomentOfInertia = function(mass){
+ var r = this.radius;
+ return mass * r * r / 2;
+};
-ShapeCircle.prototype = new Shape;
-ShapeCircle.prototype.constructor = ShapeCircle;
-
-ShapeCircle.prototype.finishVerts = function() {
- this.r = Math.abs(this.r);
-}
-
-ShapeCircle.prototype.duplicate = function() {
- return new ShapeCircle(this.c.x, this.c.y, this.r);
-}
-
-ShapeCircle.prototype.serialize = function() {
- return {
- "type": "ShapeCircle",
- "e": this.e,
- "u": this.u,
- "density": this.density,
- "center": this.c,
- "radius": this.r
- };
-}
-
-ShapeCircle.prototype.recenter = function(c) {
- this.c.subself(c);
-}
-
-ShapeCircle.prototype.transform = function(xf) {
- this.c = xf.transform(this.c);
-}
-
-ShapeCircle.prototype.untransform = function(xf) {
- this.c = xf.untransform(this.c);
-}
-
-ShapeCircle.prototype.area = function() {
- return areaForCircle(this.r, 0);
-}
-
-ShapeCircle.prototype.centroid = function() {
- return this.c.duplicate();
-}
-
-ShapeCircle.prototype.inertia = function(mass) {
- return inertiaForCircle(mass, this.c, this.r, 0);
-}
-
-ShapeCircle.prototype.cacheData = function(xf) {
- this.tc = xf.transform(this.c);
- this.bounds.mins.set(this.tc.x - this.r, this.tc.y - this.r);
- this.bounds.maxs.set(this.tc.x + this.r, this.tc.y + this.r);
-}
-
-ShapeCircle.prototype.pointQuery = function(p) {
- return vec2.distsq(this.tc, p) < (this.r * this.r);
-}
-
-ShapeCircle.prototype.findVertexByPoint = function(p, minDist) {
- var dsq = minDist * minDist;
-
- if (vec2.distsq(this.tc, p) < dsq) {
- return 0;
- }
-
- return -1;
-}
-
-ShapeCircle.prototype.distanceOnPlane = function(n, d) {
- return vec2.dot(n, this.tc) - this.r - d;
-}
\ No newline at end of file
+Circle.prototype.updateBoundingRadius = function(){
+ this.boundingRadius = this.radius;
+};
diff --git a/src/physics/advanced/shapes/Convex.js b/src/physics/advanced/shapes/Convex.js
new file mode 100644
index 00000000..8599608b
--- /dev/null
+++ b/src/physics/advanced/shapes/Convex.js
@@ -0,0 +1,213 @@
+var Shape = require('./Shape')
+, vec2 = require('../math/vec2')
+, polyk = require('../math/polyk')
+
+module.exports = Convex;
+
+/**
+ * Convex shape class.
+ * @class Convex
+ * @constructor
+ * @extends {Shape}
+ * @param {Array} vertices An array of Float32Array vertices that span this shape. Vertices are given in counter-clockwise (CCW) direction.
+ */
+function Convex(vertices){
+
+ /**
+ * Vertices defined in the local frame.
+ * @property vertices
+ * @type {Array}
+ */
+ this.vertices = vertices || [];
+
+ /**
+ * The center of mass of the Convex
+ * @property centerOfMass
+ * @type {Float32Array}
+ */
+ this.centerOfMass = vec2.fromValues(0,0);
+
+ /**
+ * Triangulated version of this convex. The structure is Array of 3-Arrays, and each subarray contains 3 integers, referencing the vertices.
+ * @property triangles
+ * @type {Array}
+ */
+ this.triangles = [];
+
+ if(this.vertices.length){
+ this.updateTriangles();
+ this.updateCenterOfMass();
+ }
+
+ /**
+ * The bounding radius of the convex
+ * @property boundingRadius
+ * @type {Number}
+ */
+ this.boundingRadius = 0;
+ this.updateBoundingRadius();
+
+ Shape.call(this,Shape.CONVEX);
+};
+Convex.prototype = new Shape();
+
+Convex.prototype.updateTriangles = function(){
+
+ this.triangles.length = 0;
+
+ // Rewrite on polyk notation, array of numbers
+ var polykVerts = [];
+ for(var i=0; i h=2*a/b
+
+ // Get inertia for this triangle: http://answers.yahoo.com/question/index?qid=20080721030038AA3oE1m
+ var I_triangle = (base * (Math.pow(height,3))) / 36;
+
+ // Get mass for the triangle
+ var m = base*height/2 * density;
+
+ // Add to total inertia using parallel axis theorem
+ var r2 = vec2.squaredLength(centroid);
+ I += I_triangle + m*r2;
+ }
+
+ return I;
+};
+
+/**
+ * Updates the .boundingRadius property
+ * @method updateBoundingRadius
+ */
+Convex.prototype.updateBoundingRadius = function(){
+ var verts = this.vertices,
+ r2 = 0;
+
+ for(var i=0; i!==verts.length; i++){
+ var l2 = vec2.squaredLength(verts[i]);
+ if(l2 > r2) r2 = l2;
+ }
+
+ this.boundingRadius = Math.sqrt(r2);
+};
+
diff --git a/src/physics/advanced/shapes/Line.js b/src/physics/advanced/shapes/Line.js
new file mode 100644
index 00000000..30fc9205
--- /dev/null
+++ b/src/physics/advanced/shapes/Line.js
@@ -0,0 +1,30 @@
+var Shape = require('./Shape');
+
+module.exports = Line;
+
+/**
+ * Line shape class. The line shape is along the x direction, and stretches from [-length/2, 0] to [length/2,0].
+ * @class Line
+ * @extends {Shape}
+ * @constructor
+ */
+function Line(length){
+
+ /**
+ * Length of this line
+ * @property length
+ * @type {Number}
+ */
+ this.length = length;
+
+ Shape.call(this,Shape.LINE);
+};
+Line.prototype = new Shape();
+Line.prototype.computeMomentOfInertia = function(mass){
+ return mass * Math.pow(this.length,2) / 12;
+};
+
+Line.prototype.updateBoundingRadius = function(){
+ this.boundingRadius = this.length/2;
+};
+
diff --git a/src/physics/advanced/shapes/Particle.js b/src/physics/advanced/shapes/Particle.js
new file mode 100644
index 00000000..f5de27c3
--- /dev/null
+++ b/src/physics/advanced/shapes/Particle.js
@@ -0,0 +1,22 @@
+var Shape = require('./Shape');
+
+module.exports = Particle;
+
+/**
+ * Particle shape class.
+ * @class Particle
+ * @constructor
+ * @extends {Shape}
+ */
+function Particle(){
+ Shape.call(this,Shape.PARTICLE);
+};
+Particle.prototype = new Shape();
+Particle.prototype.computeMomentOfInertia = function(mass){
+ return 0; // Can't rotate a particle
+};
+
+Particle.prototype.updateBoundingRadius = function(){
+ this.boundingRadius = 0;
+};
+
diff --git a/src/physics/advanced/shapes/Plane.js b/src/physics/advanced/shapes/Plane.js
new file mode 100644
index 00000000..187adcc7
--- /dev/null
+++ b/src/physics/advanced/shapes/Plane.js
@@ -0,0 +1,22 @@
+var Shape = require('./Shape');
+
+module.exports = Plane;
+
+/**
+ * Plane shape class. The plane is facing in the Y direction.
+ * @class Plane
+ * @extends {Shape}
+ * @constructor
+ */
+function Plane(){
+ Shape.call(this,Shape.PLANE);
+};
+Plane.prototype = new Shape();
+Plane.prototype.computeMomentOfInertia = function(mass){
+ return 0; // Plane is infinite. The inertia should therefore be infinty but by convention we set 0 here
+};
+
+Plane.prototype.updateBoundingRadius = function(){
+ this.boundingRadius = Number.MAX_VALUE;
+};
+
diff --git a/src/physics/advanced/shapes/Poly.js b/src/physics/advanced/shapes/Poly.js
deleted file mode 100644
index 8b84fa26..00000000
--- a/src/physics/advanced/shapes/Poly.js
+++ /dev/null
@@ -1,254 +0,0 @@
-/*
-* Copyright (c) 2012 Ju Hyung Lee
-*
-* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
-* and associated documentation files (the "Software"), to deal in the Software without
-* restriction, including without limitation the rights to use, copy, modify, merge, publish,
-* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
-* Software is furnished to do so, subject to the following conditions:
-*
-* The above copyright notice and this permission notice shall be included in all copies or
-* substantial portions of the Software.
-*
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
-* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-//--------------------------------
-// ShapePoly (convex only)
-//--------------------------------
-
-ShapePoly = function(verts) {
- Shape.call(this, Shape.TYPE_POLY);
-
- this.verts = [];
- this.planes = [];
-
- this.tverts = [];
- this.tplanes = [];
-
- if (verts) {
- for (var i = 0; i < verts.length; i++) {
- this.verts[i] = verts[i].duplicate();
- this.tverts[i] = this.verts[i];
-
- this.tplanes[i] = {};
- this.tplanes[i].n = vec2.zero;
- this.tplanes[i].d = 0;
- }
- }
-
- this.finishVerts();
-}
-
-ShapePoly.prototype = new Shape;
-ShapePoly.prototype.constructor = ShapePoly;
-
-ShapePoly.prototype.finishVerts = function() {
- if (this.verts.length < 2) {
- this.convexity = false;
- this.planes = [];
- return;
- }
-
- this.convexity = true;
- this.tverts = [];
- this.tplanes = [];
-
- // Must be counter-clockwise verts
- for (var i = 0; i < this.verts.length; i++) {
- var a = this.verts[i];
- var b = this.verts[(i + 1) % this.verts.length];
- var n = vec2.normalize(vec2.perp(vec2.sub(a, b)));
-
- this.planes[i] = {};
- this.planes[i].n = n;
- this.planes[i].d = vec2.dot(n, a);
-
- this.tverts[i] = this.verts[i];
-
- this.tplanes[i] = {};
- this.tplanes[i].n = vec2.zero;
- this.tplanes[i].d = 0;
- }
-
- for (var i = 0; i < this.verts.length; i++) {
- var b = this.verts[(i + 2) % this.verts.length];
- var n = this.planes[i].n;
- var d = this.planes[i].d;
-
- if (vec2.dot(n, b) - d > 0) {
- this.convexity = false;
- }
- }
-}
-
-ShapePoly.prototype.duplicate = function() {
- return new ShapePoly(this.verts);
-}
-
-ShapePoly.prototype.serialize = function() {
- return {
- "type": "ShapePoly",
- "e": this.e,
- "u": this.u,
- "density": this.density,
- "verts": this.verts
- };
-}
-
-ShapePoly.prototype.recenter = function(c) {
- for (var i = 0; i < this.verts.length; i++) {
- this.verts[i].subself(c);
- }
-}
-
-ShapePoly.prototype.transform = function(xf) {
- for (var i = 0; i < this.verts.length; i++) {
- this.verts[i] = xf.transform(this.verts[i]);
- }
-}
-
-ShapePoly.prototype.untransform = function(xf) {
- for (var i = 0; i < this.verts.length; i++) {
- this.verts[i] = xf.untransform(this.verts[i]);
- }
-}
-
-ShapePoly.prototype.area = function() {
- return areaForPoly(this.verts);
-}
-
-ShapePoly.prototype.centroid = function() {
- return centroidForPoly(this.verts);
-}
-
-ShapePoly.prototype.inertia = function(mass) {
- return inertiaForPoly(mass, this.verts, vec2.zero);
-}
-
-ShapePoly.prototype.cacheData = function(xf) {
-
- this.bounds.clear();
-
- var numVerts = this.verts.length;
-
- if (numVerts == 0) {
- return;
- }
-
- for (var i = 0; i < numVerts; i++) {
- this.tverts[i] = xf.transform(this.verts[i]);
- }
-
- if (numVerts < 2) {
- this.bounds.addPoint(this.tverts[0]);
- return;
- }
-
- for (var i = 0; i < numVerts; i++) {
-
- var a = this.tverts[i];
- var b = this.tverts[(i + 1) % numVerts];
- var n = vec2.normalize(vec2.perp(vec2.sub(a, b)));
-
- this.tplanes[i].n = n;
- this.tplanes[i].d = vec2.dot(n, a);
-
- this.bounds.addPoint(a);
-
- }
-
-}
-
-ShapePoly.prototype.pointQuery = function(p) {
- if (!this.bounds.containPoint(p)) {
- return false;
- }
-
- return this.containPoint(p);
-}
-
-ShapePoly.prototype.findVertexByPoint = function(p, minDist) {
- var dsq = minDist * minDist;
-
- for (var i = 0; i < this.tverts.length; i++) {
- if (vec2.distsq(this.tverts[i], p) < dsq) {
- return i;
- }
- }
-
- return -1;
-}
-
-ShapePoly.prototype.findEdgeByPoint = function(p, minDist) {
- var dsq = minDist * minDist;
- var numVerts = this.tverts.length;
-
- for (var i = 0; i < this.tverts.length; i++) {
- var v1 = this.tverts[i];
- var v2 = this.tverts[(i + 1) % numVerts];
- var n = this.tplanes[i].n;
-
- var dtv1 = vec2.cross(v1, n);
- var dtv2 = vec2.cross(v2, n);
- var dt = vec2.cross(p, n);
-
- if (dt > dtv1) {
- if (vec2.distsq(v1, p) < dsq) {
- return i;
- }
- }
- else if (dt < dtv2) {
- if (vec2.distsq(v2, p) < dsq) {
- return i;
- }
- }
- else {
- var dist = vec2.dot(n, p) - vec2.dot(n, v1);
- if (dist * dist < dsq) {
- return i;
- }
- }
- }
-
- return -1;
-}
-
-ShapePoly.prototype.distanceOnPlane = function(n, d) {
- var min = 999999;
- for (var i = 0; i < this.verts.length; i++) {
- min = Math.min(min, vec2.dot(n, this.tverts[i]));
- }
- return min - d;
-}
-
-ShapePoly.prototype.containPoint = function(p) {
- for (var i = 0; i < this.verts.length; i++) {
- var plane = this.tplanes[i];
- if (vec2.dot(plane.n, p) - plane.d > 0) {
- return false;
- }
- }
-
- return true;
-}
-
-ShapePoly.prototype.containPointPartial = function(p, n) {
- for (var i = 0; i < this.verts.length; i++) {
- var plane = this.tplanes[i];
- if (vec2.dot(plane.n, n) < 0.0001) {
- continue;
- }
-
- if (vec2.dot(plane.n, p) - plane.d > 0) {
- return false;
- }
- }
-
- return true;
-}
-
diff --git a/src/physics/advanced/shapes/Rectangle.js b/src/physics/advanced/shapes/Rectangle.js
new file mode 100644
index 00000000..6c4c4507
--- /dev/null
+++ b/src/physics/advanced/shapes/Rectangle.js
@@ -0,0 +1,43 @@
+var vec2 = require('../math/vec2')
+, Shape = require('./Shape')
+, Convex = require('./Convex')
+
+module.exports = Rectangle;
+
+/**
+ * Rectangle shape class.
+ * @class Rectangle
+ * @constructor
+ * @extends {Convex}
+ */
+function Rectangle(w,h){
+ var verts = [ vec2.fromValues(-w/2, -h/2),
+ vec2.fromValues( w/2, -h/2),
+ vec2.fromValues( w/2, h/2),
+ vec2.fromValues(-w/2, h/2)];
+
+ this.width = w;
+ this.height = h;
+
+ Convex.call(this,verts);
+};
+Rectangle.prototype = new Convex();
+
+/**
+ * Compute moment of inertia
+ * @method computeMomentOfInertia
+ * @param {Number} mass
+ * @return {Number}
+ */
+Rectangle.prototype.computeMomentOfInertia = function(mass){
+ var w = this.width,
+ h = this.height;
+ return mass * (h*h + w*w) / 12;
+};
+
+Rectangle.prototype.updateBoundingRadius = function(){
+ var w = this.width,
+ h = this.height;
+ this.boundingRadius = Math.sqrt(w*w + h*h) / 2;
+};
+
diff --git a/src/physics/advanced/shapes/Segment.js b/src/physics/advanced/shapes/Segment.js
deleted file mode 100644
index 3509e87e..00000000
--- a/src/physics/advanced/shapes/Segment.js
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
-* Copyright (c) 2012 Ju Hyung Lee
-*
-* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
-* and associated documentation files (the "Software"), to deal in the Software without
-* restriction, including without limitation the rights to use, copy, modify, merge, publish,
-* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
-* Software is furnished to do so, subject to the following conditions:
-*
-* The above copyright notice and this permission notice shall be included in all copies or
-* substantial portions of the Software.
-*
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
-* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-//------------------------------------------
-// ShapeSegment (thick rounded line segment)
-//------------------------------------------
-
-ShapeSegment = function(a, b, radius) {
- Shape.call(this, Shape.TYPE_SEGMENT);
- this.a = a.duplicate();
- this.b = b.duplicate();
- this.r = radius;
- this.n = vec2.perp(vec2.sub(b, a));
- this.n.normalize();
-
- this.ta = vec2.zero;
- this.tb = vec2.zero;
- this.tn = vec2.zero;
-
- this.finishVerts();
-}
-
-ShapeSegment.prototype = new Shape;
-ShapeSegment.prototype.constructor = ShapeSegment;
-
-ShapeSegment.prototype.finishVerts = function() {
- this.n = vec2.perp(vec2.sub(this.b, this.a));
- this.n.normalize();
-
- this.r = Math.abs(this.r);
-}
-
-ShapeSegment.prototype.duplicate = function() {
- return new ShapeSegment(this.a, this.b, this.r);
-}
-
-ShapeSegment.prototype.serialize = function() {
- return {
- "type": "ShapeSegment",
- "e": this.e,
- "u": this.u,
- "density": this.density,
- "a": this.a,
- "b": this.b,
- "radius": this.r
- };
-}
-
-ShapeSegment.prototype.recenter = function(c) {
- this.a.subself(c);
- this.b.subself(c);
-}
-
-ShapeSegment.prototype.transform = function(xf) {
- this.a = xf.transform(this.a);
- this.b = xf.transform(this.b);
-}
-
-ShapeSegment.prototype.untransform = function(xf) {
- this.a = xf.untransform(this.a);
- this.b = xf.untransform(this.b);
-}
-
-ShapeSegment.prototype.area = function() {
- return areaForSegment(this.a, this.b, this.r);
-}
-
-ShapeSegment.prototype.centroid = function() {
- return centroidForSegment(this.a, this.b);
-}
-
-ShapeSegment.prototype.inertia = function(mass) {
- return inertiaForSegment(mass, this.a, this.b);
-}
-
-ShapeSegment.prototype.cacheData = function(xf) {
- this.ta = xf.transform(this.a);
- this.tb = xf.transform(this.b);
- this.tn = vec2.perp(vec2.sub(this.tb, this.ta)).normalize();
-
- if (this.ta.x < this.tb.x) {
- l = this.ta.x;
- r = this.tb.x;
- }
- else {
- l = this.tb.x;
- r = this.ta.x;
- }
-
- if (this.ta.y < this.tb.y) {
- b = this.ta.y;
- t = this.tb.y;
- } else {
- b = this.tb.y;
- t = this.ta.y;
- }
-
- this.bounds.mins.set(l - this.r, b - this.r);
- this.bounds.maxs.set(r + this.r, t + this.r);
-}
-
-ShapeSegment.prototype.pointQuery = function(p) {
- if (!this.bounds.containPoint(p)) {
- return false;
- }
-
- var dn = vec2.dot(this.tn, p) - vec2.dot(this.ta, this.tn);
- var dist = Math.abs(dn);
- if (dist > this.r) {
- return false;
- }
-
- var dt = vec2.cross(p, this.tn);
- var dta = vec2.cross(this.ta, this.tn);
- var dtb = vec2.cross(this.tb, this.tn);
-
- if (dt <= dta) {
- if (dt < dta - this.r) {
- return false;
- }
-
- return vec2.distsq(this.ta, p) < (this.r * this.r);
- }
- else if (dt > dtb) {
- if (dt > dtb + this.r) {
- return false;
- }
-
- return vec2.distsq(this.tb, p) < (this.r * this.r);
- }
-
- return true;
-}
-
-ShapeSegment.prototype.findVertexByPoint = function(p, minDist) {
- var dsq = minDist * minDist;
-
- if (vec2.distsq(this.ta, p) < dsq) {
- return 0;
- }
-
- if (vec2.distsq(this.tb, p) < dsq) {
- return 1;
- }
-
- return -1;
-}
-
-ShapeSegment.prototype.distanceOnPlane = function(n, d) {
- var a = vec2.dot(n, this.ta) - this.r;
- var b = vec2.dot(n, this.tb) - this.r;
-
- return Math.min(a, b) - d;
-}
\ No newline at end of file
diff --git a/src/physics/advanced/shapes/Shape.js b/src/physics/advanced/shapes/Shape.js
new file mode 100644
index 00000000..1d349b40
--- /dev/null
+++ b/src/physics/advanced/shapes/Shape.js
@@ -0,0 +1,90 @@
+module.exports = Shape;
+
+/**
+ * Base class for shapes.
+ * @class Shape
+ * @constructor
+ */
+function Shape(type){
+ this.type = type;
+
+ /**
+ * Bounding circle radius of this shape
+ * @property boundingRadius
+ * @type {Number}
+ */
+ this.boundingRadius = 0;
+
+ /**
+ * Collision group that this shape belongs to (bit mask). See this tutorial.
+ * @property collisionGroup
+ * @type {Number}
+ * @example
+ * // Setup bits for each available group
+ * var PLAYER = Math.pow(2,0),
+ * ENEMY = Math.pow(2,1),
+ * GROUND = Math.pow(2,2)
+ *
+ * // Put shapes into their groups
+ * player1Shape.collisionGroup = PLAYER;
+ * player2Shape.collisionGroup = PLAYER;
+ * enemyShape .collisionGroup = ENEMY;
+ * groundShape .collisionGroup = GROUND;
+ *
+ * // Assign groups that each shape collide with.
+ * // Note that the players can collide with ground and enemies, but not with other players.
+ * player1Shape.collisionMask = ENEMY | GROUND;
+ * player2Shape.collisionMask = ENEMY | GROUND;
+ * enemyShape .collisionMask = PLAYER | GROUND;
+ * groundShape .collisionMask = PLAYER | ENEMY;
+ *
+ * @example
+ * // How collision check is done
+ * if(shapeA.collisionGroup & shapeB.collisionMask)!=0 && (shapeB.collisionGroup & shapeA.collisionMask)!=0){
+ * // The shapes will collide
+ * }
+ */
+ this.collisionGroup = 1;
+
+ /**
+ * Collision mask of this shape. See .collisionGroup.
+ * @property collisionMask
+ * @type {Number}
+ */
+ this.collisionMask = 1;
+ if(type) this.updateBoundingRadius();
+
+ /**
+ * Material to use in collisions for this Shape. If this is set to null, the world will use default material properties instead.
+ * @property material
+ * @type {Material}
+ */
+ this.material = null;
+};
+
+Shape.CIRCLE = 1;
+Shape.PARTICLE = 2;
+Shape.PLANE = 4;
+Shape.CONVEX = 8;
+Shape.LINE = 16;
+Shape.RECTANGLE = 32;
+Shape.CAPSULE = 64;
+
+/**
+ * Should return the moment of inertia around the Z axis of the body given the total mass. See Wikipedia's list of moments of inertia.
+ * @method computeMomentOfInertia
+ * @param {Number} mass
+ * @return {Number} If the inertia is infinity or if the object simply isn't possible to rotate, return 0.
+ */
+Shape.prototype.computeMomentOfInertia = function(mass){
+ throw new Error("Shape.computeMomentOfInertia is not implemented in this Shape...");
+};
+
+/**
+ * Returns the bounding circle radius of this shape.
+ * @method updateBoundingRadius
+ * @return {Number}
+ */
+Shape.prototype.updateBoundingRadius = function(){
+ throw new Error("Shape.updateBoundingRadius is not implemented in this Shape...");
+};
diff --git a/src/physics/advanced/shapes/Triangle.js b/src/physics/advanced/shapes/Triangle.js
deleted file mode 100644
index a8150386..00000000
--- a/src/physics/advanced/shapes/Triangle.js
+++ /dev/null
@@ -1,12 +0,0 @@
-//--------------------------------
-// Triangle
-//--------------------------------
-
-ShapeTriangle = function(p1, p2, p3) {
- var verts = [
- new vec2(p1.x, p1.y),
- new vec2(p2.x, p2.y),
- new vec2(p3.x, p3.y)
- ];
- return new ShapePoly(verts);
-}
diff --git a/src/physics/advanced/solver/GSSolver.js b/src/physics/advanced/solver/GSSolver.js
new file mode 100644
index 00000000..a2f25cb6
--- /dev/null
+++ b/src/physics/advanced/solver/GSSolver.js
@@ -0,0 +1,190 @@
+var vec2 = require('../math/vec2'),
+ Solver = require('./Solver');
+
+module.exports = GSSolver;
+
+var ARRAY_TYPE = Float32Array || Array;
+
+/**
+ * Iterative Gauss-Seidel constraint equation solver.
+ *
+ * @class GSSolver
+ * @constructor
+ * @extends Solver
+ * @param {Object} [options]
+ * @param {Number} options.iterations
+ * @param {Number} options.timeStep
+ * @param {Number} options.stiffness
+ * @param {Number} options.relaxation
+ * @param {Number} options.tolerance
+ */
+function GSSolver(options){
+ Solver.call(this);
+ options = options || {};
+ this.iterations = options.iterations || 10;
+ this.tolerance = options.tolerance || 0;
+ this.debug = options.debug || false;
+ this.arrayStep = 30;
+ this.lambda = new ARRAY_TYPE(this.arrayStep);
+ this.Bs = new ARRAY_TYPE(this.arrayStep);
+ this.invCs = new ARRAY_TYPE(this.arrayStep);
+
+ /**
+ * Whether to use .stiffness and .relaxation parameters from the Solver instead of each Equation individually.
+ * @type {Boolean}
+ * @property useGlobalEquationParameters
+ */
+ this.useGlobalEquationParameters = true;
+
+ /**
+ * Global equation stiffness.
+ * @property stiffness
+ * @type {Number}
+ */
+ this.stiffness = 1e6;
+
+ /**
+ * Global equation relaxation.
+ * @property relaxation
+ * @type {Number}
+ */
+ this.relaxation = 4;
+
+ /**
+ * Set to true to set all right hand side terms to zero when solving. Can be handy for a few applications.
+ * @property useZeroRHS
+ * @type {Boolean}
+ */
+ this.useZeroRHS = false;
+};
+GSSolver.prototype = new Solver();
+
+/**
+ * Set stiffness parameters
+ *
+ * @method setSpookParams
+ * @param {number} k
+ * @param {number} d
+ * @deprecated
+ */
+GSSolver.prototype.setSpookParams = function(k,d){
+ this.stiffness = k;
+ this.relaxation = d;
+};
+
+/**
+ * Solve the system of equations
+ * @method solve
+ * @param {Number} dt Time step
+ * @param {World} world World to solve
+ */
+GSSolver.prototype.solve = function(dt,world){
+ var iter = 0,
+ maxIter = this.iterations,
+ tolSquared = this.tolerance*this.tolerance,
+ equations = this.equations,
+ Neq = equations.length,
+ bodies = world.bodies,
+ Nbodies = world.bodies.length,
+ h = dt,
+ d = this.relaxation,
+ k = this.stiffness,
+ eps = 4.0 / (h * h * k * (1 + 4 * d)),
+ a = 4.0 / (h * (1 + 4 * d)),
+ b = (4.0 * d) / (1 + 4 * d),
+ useGlobalParams = this.useGlobalEquationParameters,
+ add = vec2.add,
+ set = vec2.set,
+ useZeroRHS = this.useZeroRHS;
+
+ // Things that does not change during iteration can be computed once
+ if(this.lambda.length < Neq){
+ this.lambda = new ARRAY_TYPE(Neq + this.arrayStep);
+ this.Bs = new ARRAY_TYPE(Neq + this.arrayStep);
+ this.invCs = new ARRAY_TYPE(Neq + this.arrayStep);
+ }
+ var invCs = this.invCs,
+ Bs = this.Bs,
+ lambda = this.lambda;
+ for(var i=0; i!==Neq; i++){
+ var c = equations[i];
+ lambda[i] = 0.0;
+
+ var _a = a,
+ _b = b,
+ _eps = eps;
+ if(!useGlobalParams){
+ if(h !== c.h) c.updateSpookParams(h);
+ _a = c.a;
+ _b = c.b;
+ _eps = c.eps;
+ }
+ Bs[i] = c.computeB(_a,_b,h);
+ invCs[i] = 1.0 / c.computeC(_eps);
+ }
+
+ var q, B, c, invC, deltalambda, deltalambdaTot, GWlambda, lambdaj;
+
+ if(Neq !== 0){
+ var i,j, minForce, maxForce, lambdaj_plus_deltalambda;
+
+ // Reset vlambda
+ for(i=0; i!==Nbodies; i++){
+ var b=bodies[i], vlambda=b.vlambda;
+ set(vlambda,0,0);
+ b.wlambda = 0;
+ }
+
+ // Iterate over equations
+ for(iter=0; iter!==maxIter; iter++){
+
+ // Accumulate the total error for each iteration.
+ deltalambdaTot = 0.0;
+
+ for(j=0; j!==Neq; j++){
+
+ c = equations[j];
+
+ var _eps = useGlobalParams ? eps : c.eps;
+
+ // Compute iteration
+ maxForce = c.maxForce;
+ minForce = c.minForce;
+
+ B = Bs[j];
+ invC = invCs[j];
+ lambdaj = lambda[j];
+ GWlambda = c.computeGWlambda(_eps);
+
+ if(useZeroRHS) B = 0;
+
+ deltalambda = invC * ( B - GWlambda - _eps * lambdaj );
+
+ // Clamp if we are not within the min/max interval
+ lambdaj_plus_deltalambda = lambdaj + deltalambda;
+ if(lambdaj_plus_deltalambda < minForce){
+ deltalambda = minForce - lambdaj;
+ } else if(lambdaj_plus_deltalambda > maxForce){
+ deltalambda = maxForce - lambdaj;
+ }
+ lambda[j] += deltalambda;
+
+ deltalambdaTot += Math.abs(deltalambda);
+
+ c.addToWlambda(deltalambda);
+ }
+
+ // If the total error is small enough - stop iterate
+ if(deltalambdaTot*deltalambdaTot <= tolSquared) break;
+ }
+
+ // Add result to velocity
+ for(i=0; i!==Nbodies; i++){
+ var b=bodies[i], v=b.velocity;
+ add( v, v, b.vlambda);
+ b.angularVelocity += b.wlambda;
+ }
+ }
+ errorTot = deltalambdaTot;
+};
+
diff --git a/src/physics/advanced/solver/Island.js b/src/physics/advanced/solver/Island.js
new file mode 100644
index 00000000..b3c8635d
--- /dev/null
+++ b/src/physics/advanced/solver/Island.js
@@ -0,0 +1,81 @@
+module.exports = Island;
+
+/**
+ * An island of bodies connected with equations.
+ * @class Island
+ * @constructor
+ */
+function Island(){
+
+ /**
+ * Current equations in this island.
+ * @property equations
+ * @type {Array}
+ */
+ this.equations = [];
+
+ /**
+ * Current bodies in this island.
+ * @property bodies
+ * @type {Array}
+ */
+ this.bodies = [];
+}
+
+/**
+ * Clean this island from bodies and equations.
+ * @method reset
+ */
+Island.prototype.reset = function(){
+ this.equations.length = this.bodies.length = 0;
+}
+
+
+/**
+ * Get all unique bodies in this island.
+ * @method getBodies
+ * @return {Array} An array of Body
+ */
+Island.prototype.getBodies = function(){
+ var bodies = [],
+ bodyIds = [],
+ eqs = this.equations;
+ for(var i=0; i!==eqs.length; i++){
+ var eq = eqs[i];
+ if(bodyIds.indexOf(eq.bi.id)===-1){
+ bodies.push(eq.bi);
+ bodyIds.push(eq.bi.id);
+ }
+ if(bodyIds.indexOf(eq.bj.id)===-1){
+ bodies.push(eq.bj);
+ bodyIds.push(eq.bj.id);
+ }
+ }
+ return bodies;
+};
+
+/**
+ * Solves all constraints in the group of islands.
+ * @method solve
+ * @param {Number} dt
+ * @param {Solver} solver
+ */
+Island.prototype.solve = function(dt,solver){
+ var bodies = [];
+
+ solver.removeAllEquations();
+
+ // Add equations to solver
+ var numEquations = this.equations.length;
+ for(var j=0; j!==numEquations; j++){
+ solver.addEquation(this.equations[j]);
+ }
+ var islandBodies = this.getBodies();
+ var numBodies = islandBodies.length;
+ for(var j=0; j!==numBodies; j++){
+ bodies.push(islandBodies[j]);
+ }
+
+ // Solve
+ solver.solve(dt,{bodies:bodies});
+};
diff --git a/src/physics/advanced/solver/IslandSolver.js b/src/physics/advanced/solver/IslandSolver.js
new file mode 100644
index 00000000..9d4e35be
--- /dev/null
+++ b/src/physics/advanced/solver/IslandSolver.js
@@ -0,0 +1,159 @@
+var Solver = require('./Solver')
+, vec2 = require('../math/vec2')
+, Island = require('../solver/Island')
+, Body = require('../objects/Body')
+, STATIC = Body.STATIC
+
+module.exports = IslandSolver;
+
+/**
+ * Splits the system of bodies and equations into independent islands
+ *
+ * @class IslandSolver
+ * @constructor
+ * @param {Solver} subsolver
+ * @extends Solver
+ */
+function IslandSolver(subsolver){
+ Solver.call(this);
+ var that = this;
+
+ /**
+ * The solver used in the workers.
+ * @property subsolver
+ * @type {Solver}
+ */
+ this.subsolver = subsolver;
+
+ /**
+ * Number of islands
+ * @property numIslands
+ * @type {number}
+ */
+ this.numIslands = 0;
+
+ // Pooling of node objects saves some GC load
+ this._nodePool = [];
+};
+IslandSolver.prototype = new Object(Solver.prototype);
+
+function getUnvisitedNode(nodes){
+ var Nnodes = nodes.length;
+ for(var i=0; i!==Nnodes; i++){
+ var node = nodes[i];
+ if(!node.visited && !(node.body.motionState & STATIC)){ // correct?
+ return node;
+ }
+ }
+ return false;
+}
+
+function bfs(root,visitFunc){
+ var queue = [];
+ queue.push(root);
+ root.visited = true;
+ visitFunc(root);
+ while(queue.length) {
+ var node = queue.pop();
+ // Loop over unvisited child nodes
+ var child;
+ while((child = getUnvisitedNode(node.children))) {
+ child.visited = true;
+ visitFunc(child);
+ queue.push(child);
+ }
+ }
+}
+
+/**
+ * Solves the full system.
+ * @method solve
+ * @param {Number} dt
+ * @param {World} world
+ */
+IslandSolver.prototype.solve = function(dt,world){
+ var nodes = [],
+ bodies=world.bodies,
+ equations=this.equations,
+ Neq=equations.length,
+ Nbodies=bodies.length,
+ subsolver=this.subsolver,
+ workers = this._workers,
+ workerData = this._workerData,
+ workerIslandGroups = this._workerIslandGroups;
+
+ // Create needed nodes, reuse if possible
+ for(var i=0; i!==Nbodies; i++){
+ if(this._nodePool.length)
+ nodes.push( this._nodePool.pop() );
+ else {
+ nodes.push({
+ body:bodies[i],
+ children:[],
+ eqs:[],
+ visited:false
+ });
+ }
+ }
+
+ // Reset node values
+ for(var i=0; i!==Nbodies; i++){
+ var node = nodes[i];
+ node.body = bodies[i];
+ node.children.length = 0;
+ node.eqs.length = 0;
+ node.visited = false;
+ }
+
+ // Add connectivity data. Each equation connects 2 bodies.
+ for(var k=0; k!==Neq; k++){
+ var eq=equations[k],
+ i=bodies.indexOf(eq.bi),
+ j=bodies.indexOf(eq.bj),
+ ni=nodes[i],
+ nj=nodes[j];
+ ni.children.push(nj);
+ ni.eqs.push(eq);
+ nj.children.push(ni);
+ nj.eqs.push(eq);
+ }
+
+ // The BFS search algorithm needs a traversal function. What we do is gather all bodies and equations connected.
+ var child, n=0, eqs=[], bds=[];
+ function visitFunc(node){
+ bds.push(node.body);
+ var Neqs = node.eqs.length;
+ for(var i=0; i!==Neqs; i++){
+ var eq = node.eqs[i];
+ if(eqs.indexOf(eq) === -1){
+ eqs.push(eq);
+ }
+ }
+ }
+
+ // Get islands
+ var islands = [];
+ while((child = getUnvisitedNode(nodes))){
+ var island = new Island(); // @todo Should be reused from somewhere
+ eqs.length = 0;
+ bds.length = 0;
+ bfs(child,visitFunc); // run search algo to gather an island of bodies
+
+ // Add equations to island
+ var Neqs = eqs.length;
+ for(var i=0; i!==Neqs; i++){
+ var eq = eqs[i];
+ island.equations.push(eq);
+ }
+
+ n++;
+ islands.push(island);
+ }
+
+ this.numIslands = n;
+
+ // Solve islands
+ for(var i=0; ithis for an explanation.
+ * @method appendArray
+ * @static
+ * @param {Array} a
+ * @param {Array} b
+ */
+Utils.appendArray = function(a,b){
+ if (b.length < 150000) {
+ a.push.apply(a, b)
+ } else {
+ for (var i = 0, len = b.length; i !== len; ++i) {
+ a.push(b[i]);
+ }
+ }
+};
diff --git a/src/physics/advanced/world/World.js b/src/physics/advanced/world/World.js
new file mode 100644
index 00000000..1c209d39
--- /dev/null
+++ b/src/physics/advanced/world/World.js
@@ -0,0 +1,870 @@
+var GSSolver = require('../solver/GSSolver')
+, NaiveBroadphase = require('../collision/NaiveBroadphase')
+, vec2 = require('../math/vec2')
+, Circle = require('../shapes/Circle')
+, Rectangle = require('../shapes/Rectangle')
+, Convex = require('../shapes/Convex')
+, Line = require('../shapes/Line')
+, Plane = require('../shapes/Plane')
+, Capsule = require('../shapes/Capsule')
+, Particle = require('../shapes/Particle')
+, EventEmitter = require('../events/EventEmitter')
+, Body = require('../objects/Body')
+, Spring = require('../objects/Spring')
+, Material = require('../material/Material')
+, ContactMaterial = require('../material/ContactMaterial')
+, DistanceConstraint = require('../constraints/DistanceConstraint')
+, PointToPointConstraint = require('../constraints/PointToPointConstraint')
+, PrismaticConstraint = require('../constraints/PrismaticConstraint')
+, pkg = require('../../package.json')
+, Broadphase = require('../collision/Broadphase')
+, Nearphase = require('../collision/Nearphase')
+
+module.exports = World;
+
+function now(){
+ if(performance.now)
+ return performance.now();
+ else if(performance.webkitNow)
+ return performance.webkitNow();
+ else
+ return new Date().getTime();
+}
+
+/**
+ * The dynamics world, where all bodies and constraints lives.
+ *
+ * @class World
+ * @constructor
+ * @param {Object} [options]
+ * @param {Solver} options.solver Defaults to GSSolver.
+ * @param {Float32Array} options.gravity Defaults to [0,-9.78]
+ * @param {Broadphase} options.broadphase Defaults to NaiveBroadphase
+ * @extends {EventEmitter}
+ */
+function World(options){
+ EventEmitter.apply(this);
+
+ options = options || {};
+
+ /**
+ * All springs in the world.
+ *
+ * @property springs
+ * @type {Array}
+ */
+ this.springs = [];
+
+ /**
+ * All bodies in the world.
+ *
+ * @property bodies
+ * @type {Array}
+ */
+ this.bodies = [];
+
+ /**
+ * The solver used to satisfy constraints and contacts.
+ *
+ * @property solver
+ * @type {Solver}
+ */
+ this.solver = options.solver || new GSSolver();
+
+ /**
+ * The nearphase to use to generate contacts.
+ *
+ * @property nearphase
+ * @type {Nearphase}
+ */
+ this.nearphase = new Nearphase();
+
+ /**
+ * Gravity in the world. This is applied on all bodies in the beginning of each step().
+ *
+ * @property
+ * @type {Float32Array}
+ */
+ this.gravity = options.gravity || vec2.fromValues(0, -9.78);
+
+ /**
+ * Whether to do timing measurements during the step() or not.
+ *
+ * @property doPofiling
+ * @type {Boolean}
+ */
+ this.doProfiling = options.doProfiling || false;
+
+ /**
+ * How many millisecconds the last step() took. This is updated each step if .doProfiling is set to true.
+ *
+ * @property lastStepTime
+ * @type {Number}
+ */
+ this.lastStepTime = 0.0;
+
+ /**
+ * The broadphase algorithm to use.
+ *
+ * @property broadphase
+ * @type {Broadphase}
+ */
+ this.broadphase = options.broadphase || new NaiveBroadphase();
+
+ /**
+ * User-added constraints.
+ *
+ * @property constraints
+ * @type {Array}
+ */
+ this.constraints = [];
+
+ /**
+ * Friction between colliding bodies. This value is used if no matching ContactMaterial is found for the body pair.
+ * @property defaultFriction
+ * @type {Number}
+ */
+ this.defaultFriction = 0.1;
+
+ /**
+ * For keeping track of what time step size we used last step
+ * @property lastTimeStep
+ * @type {Number}
+ */
+ this.lastTimeStep = 1/60;
+
+ /**
+ * Enable to automatically apply spring forces each step.
+ * @property applySpringForces
+ * @type {Boolean}
+ */
+ this.applySpringForces = true;
+
+ /**
+ * Enable/disable constraint solving in each step.
+ * @property solveConstraints
+ * @type {Boolean}
+ */
+ this.solveConstraints = true;
+
+ /**
+ * The ContactMaterials added to the World.
+ * @property contactMaterials
+ * @type {Array}
+ */
+ this.contactMaterials = [];
+
+ // Id counters
+ this._constraintIdCounter = 0;
+ this._bodyIdCounter = 0;
+
+ // Event objects that are reused
+ this.postStepEvent = {
+ type : "postStep",
+ };
+ this.addBodyEvent = {
+ type : "addBody",
+ body : null
+ };
+ this.removeBodyEvent = {
+ type : "removeBody",
+ body : null
+ };
+ this.addSpringEvent = {
+ type : "addSpring",
+ body : null
+ };
+};
+World.prototype = new Object(EventEmitter.prototype);
+
+/**
+ * Add a constraint to the simulation.
+ *
+ * @method addConstraint
+ * @param {Constraint} c
+ */
+World.prototype.addConstraint = function(c){
+ this.constraints.push(c);
+};
+
+/**
+ * Add a ContactMaterial to the simulation.
+ * @method addContactMaterial
+ * @param {ContactMaterial} contactMaterial
+ */
+World.prototype.addContactMaterial = function(contactMaterial){
+ this.contactMaterials.push(contactMaterial);
+};
+
+/**
+ * Get a contact material given two materials
+ * @method getContactMaterial
+ * @param {Material} materialA
+ * @param {Material} materialB
+ * @return {ContactMaterial} The matching ContactMaterial, or false on fail.
+ * @todo Use faster hash map to lookup from material id's
+ */
+World.prototype.getContactMaterial = function(materialA,materialB){
+ var cmats = this.contactMaterials;
+ for(var i=0, N=cmats.length; i!==N; i++){
+ var cm = cmats[i];
+ if( (cm.materialA === materialA) && (cm.materialB === materialB) ||
+ (cm.materialA === materialB) && (cm.materialB === materialA) )
+ return cm;
+ }
+ return false;
+};
+
+/**
+ * Removes a constraint
+ *
+ * @method removeConstraint
+ * @param {Constraint} c
+ */
+World.prototype.removeConstraint = function(c){
+ var idx = this.constraints.indexOf(c);
+ if(idx!==-1){
+ this.constraints.splice(idx,1);
+ }
+};
+
+var step_r = vec2.create(),
+ step_runit = vec2.create(),
+ step_u = vec2.create(),
+ step_f = vec2.create(),
+ step_fhMinv = vec2.create(),
+ step_velodt = vec2.create(),
+ xiw = vec2.fromValues(0,0),
+ xjw = vec2.fromValues(0,0),
+ zero = vec2.fromValues(0,0);
+
+/**
+ * Step the physics world forward in time.
+ *
+ * @method step
+ * @param {Number} dt The time step size to use.
+ *
+ * @example
+ * var world = new World();
+ * world.step(0.01);
+ */
+World.prototype.step = function(dt){
+ var that = this,
+ doProfiling = this.doProfiling,
+ Nsprings = this.springs.length,
+ springs = this.springs,
+ bodies = this.bodies,
+ g = this.gravity,
+ solver = this.solver,
+ Nbodies = this.bodies.length,
+ broadphase = this.broadphase,
+ np = this.nearphase,
+ constraints = this.constraints,
+ t0, t1,
+ fhMinv = step_fhMinv,
+ velodt = step_velodt,
+ scale = vec2.scale,
+ add = vec2.add,
+ rotate = vec2.rotate;
+
+ this.lastTimeStep = dt;
+
+ if(doProfiling){
+ t0 = now();
+ }
+
+ // add gravity to bodies
+ for(var i=0; i!==Nbodies; i++){
+ var fi = bodies[i].force;
+ add(fi,fi,g);
+ }
+
+ // Add spring forces
+ if(this.applySpringForces){
+ for(var i=0; i!==Nsprings; i++){
+ var s = springs[i];
+ s.applyForce();
+ }
+ }
+
+ // Broadphase
+ var result = broadphase.getCollisionPairs(this);
+
+ // Nearphase
+ var glen = vec2.length(this.gravity);
+ np.reset();
+ for(var i=0, Nresults=result.length; i!==Nresults; i+=2){
+ var bi = result[i],
+ bj = result[i+1];
+
+ // Loop over all shapes of body i
+ for(var k=0; k!==bi.shapes.length; k++){
+ var si = bi.shapes[k],
+ xi = bi.shapeOffsets[k] || zero,
+ ai = bi.shapeAngles[k] || 0;
+
+ // All shapes of body j
+ for(var l=0; l!==bj.shapes.length; l++){
+ var sj = bj.shapes[l],
+ xj = bj.shapeOffsets[l] || zero,
+ aj = bj.shapeAngles[l] || 0;
+
+ if(!((si.collisionGroup & sj.collisionMask) !== 0 && (sj.collisionGroup & si.collisionMask) !== 0))
+ continue;
+
+ var reducedMass = (bi.invMass + bj.invMass);
+ if(reducedMass > 0)
+ reducedMass = 1/reducedMass;
+
+ var mu = this.defaultFriction;
+
+ if(si.material && sj.material){
+ var cm = this.getContactMaterial(si.material,sj.material);
+ if(cm){
+ mu = cm.friction;
+ }
+ }
+
+ var mug = mu * glen * reducedMass,
+ doFriction = mu > 0;
+
+ // Get world position and angle of each shape
+ rotate(xiw, xi, bi.angle);
+ rotate(xjw, xj, bj.angle);
+ add(xiw, xiw, bi.position);
+ add(xjw, xjw, bj.position);
+ var aiw = ai + bi.angle;
+ var ajw = aj + bj.angle;
+
+ // Run nearphase
+ np.enableFriction = mu > 0;
+ np.slipForce = mug;
+ if(si instanceof Circle){
+ if(sj instanceof Circle) np.circleCircle (bi,si,xiw,aiw, bj,sj,xjw,ajw);
+ else if(sj instanceof Particle) np.circleParticle(bi,si,xiw,aiw, bj,sj,xjw,ajw);
+ else if(sj instanceof Plane) np.circlePlane (bi,si,xiw,aiw, bj,sj,xjw,ajw);
+ else if(sj instanceof Rectangle) np.circleConvex (bi,si,xiw,aiw, bj,sj,xjw,ajw);
+ else if(sj instanceof Convex) np.circleConvex (bi,si,xiw,aiw, bj,sj,xjw,ajw);
+ else if(sj instanceof Line) np.circleLine (bi,si,xiw,aiw, bj,sj,xjw,ajw);
+ else if(sj instanceof Capsule) np.circleCapsule (bi,si,xiw,aiw, bj,sj,xjw,ajw);
+
+ } else if(si instanceof Particle){
+ if(sj instanceof Circle) np.circleParticle (bj,sj,xjw,ajw, bi,si,xiw,aiw);
+ else if(sj instanceof Plane) np.particlePlane (bi,si,xiw,aiw, bj,sj,xjw,ajw);
+ else if(sj instanceof Rectangle) np.particleConvex (bi,si,xiw,aiw, bj,sj,xjw,ajw);
+ else if(sj instanceof Convex) np.particleConvex (bi,si,xiw,aiw, bj,sj,xjw,ajw);
+ else if(sj instanceof Capsule) np.particleCapsule (bi,si,xiw,aiw, bj,sj,xjw,ajw);
+
+ } else if(si instanceof Plane){
+ if(sj instanceof Circle) np.circlePlane (bj,sj,xjw,ajw, bi,si,xiw,aiw);
+ else if(sj instanceof Particle) np.particlePlane (bj,sj,xjw,ajw, bi,si,xiw,aiw);
+ else if(sj instanceof Rectangle) np.convexPlane (bj,sj,xjw,ajw, bi,si,xiw,aiw);
+ else if(sj instanceof Convex) np.convexPlane (bj,sj,xjw,ajw, bi,si,xiw,aiw);
+ else if(sj instanceof Line) np.planeLine (bi,si,xiw,aiw, bj,sj,xjw,ajw);
+ else if(sj instanceof Capsule) np.capsulePlane (bj,sj,xjw,ajw, bi,si,xiw,aiw);
+
+ } else if(si instanceof Rectangle){
+ if(sj instanceof Plane) np.convexPlane (bi,si,xiw,aiw, bj,sj,xjw,ajw);
+ else if(sj instanceof Circle) np.circleConvex (bj,sj,xjw,ajw, bi,si,xiw,aiw);
+ else if(sj instanceof Rectangle) np.convexConvex (bj,sj,xjw,ajw, bi,si,xiw,aiw);
+ else if(sj instanceof Convex) np.convexConvex (bj,sj,xjw,ajw, bi,si,xiw,aiw);
+ else if(sj instanceof Particle) np.particleConvex (bj,sj,xjw,ajw, bi,si,xiw,aiw);
+
+ } else if(si instanceof Convex){
+ if(sj instanceof Plane) np.convexPlane (bi,si,xiw,aiw, bj,sj,xjw,ajw);
+ else if(sj instanceof Circle) np.circleConvex (bj,sj,xjw,ajw, bi,si,xiw,aiw);
+ else if(sj instanceof Rectangle) np.convexConvex (bj,sj,xjw,ajw, bi,si,xiw,aiw);
+ else if(sj instanceof Convex) np.convexConvex (bi,si,xiw,aiw, bj,sj,xjw,ajw);
+ else if(sj instanceof Particle) np.particleConvex (bj,sj,xjw,ajw, bi,si,xiw,aiw);
+
+ } else if(si instanceof Line){
+ if(sj instanceof Circle) np.circleLine (bj,sj,xjw,ajw, bi,si,xiw,aiw);
+ else if(sj instanceof Plane) np.planeLine (bj,sj,xjw,ajw, bi,si,xiw,aiw);
+
+ } else if(si instanceof Capsule){
+ if(sj instanceof Plane) np.capsulePlane (bi,si,xiw,aiw, bj,sj,xjw,ajw);
+ if(sj instanceof Circle) np.circleCapsule (bj,sj,xjw,ajw, bi,si,xiw,aiw);
+ if(sj instanceof Particle) np.particleCapsule(bj,sj,xjw,ajw, bi,si,xiw,aiw);
+
+ }
+ }
+ }
+ }
+
+ // Add contact equations to solver
+ solver.addEquations(np.contactEquations);
+ solver.addEquations(np.frictionEquations);
+
+ // Add user-defined constraint equations
+ var Nconstraints = constraints.length;
+ for(i=0; i!==Nconstraints; i++){
+ var c = constraints[i];
+ c.update();
+ solver.addEquations(c.equations);
+ }
+
+ if(this.solveConstraints)
+ solver.solve(dt,this);
+
+ solver.removeAllEquations();
+
+ // Step forward
+ for(var i=0; i!==Nbodies; i++){
+ var body = bodies[i];
+
+ if(body.mass>0){
+ var minv = body.invMass,
+ f = body.force,
+ pos = body.position,
+ velo = body.velocity;
+
+ // Angular step
+ body.angularVelocity += body.angularForce * body.invInertia * dt;
+ body.angle += body.angularVelocity * dt;
+
+ // Linear step
+ scale(fhMinv,f,dt*minv);
+ add(velo,fhMinv,velo);
+ scale(velodt,velo,dt);
+ add(pos,pos,velodt);
+ }
+ }
+
+ // Reset force
+ for(var i=0; i!==Nbodies; i++){
+ var bi = bodies[i];
+ vec2.set(bi.force,0.0,0.0);
+ bi.angularForce = 0.0;
+ }
+
+ if(doProfiling){
+ t1 = now();
+ that.lastStepTime = t1-t0;
+ }
+
+ this.emit(this.postStepEvent);
+};
+
+/**
+ * Add a spring to the simulation
+ *
+ * @method addSpring
+ * @param {Spring} s
+ */
+World.prototype.addSpring = function(s){
+ this.springs.push(s);
+ this.addSpringEvent.spring = s;
+ this.emit(this.addSpringEvent);
+};
+
+/**
+ * Remove a spring
+ *
+ * @method removeSpring
+ * @param {Spring} s
+ */
+World.prototype.removeSpring = function(s){
+ var idx = this.springs.indexOf(s);
+ if(idx===-1)
+ this.springs.splice(idx,1);
+};
+
+/**
+ * Add a body to the simulation
+ *
+ * @method addBody
+ * @param {Body} body
+ *
+ * @example
+ * var world = new World(),
+ * body = new Body();
+ * world.addBody(body);
+ *
+ */
+World.prototype.addBody = function(body){
+ this.bodies.push(body);
+ this.addBodyEvent.body = body;
+ this.emit(this.addBodyEvent);
+};
+
+/**
+ * Remove a body from the simulation
+ *
+ * @method removeBody
+ * @param {Body} body
+ */
+World.prototype.removeBody = function(body){
+ var idx = this.bodies.indexOf(body);
+ if(idx!==-1){
+ this.bodies.splice(idx,1);
+ this.removeBodyEvent.body = body;
+ this.emit(this.removeBodyEvent);
+ }
+};
+
+/**
+ * Convert the world to a JSON-serializable Object.
+ *
+ * @method toJSON
+ * @return {Object}
+ */
+World.prototype.toJSON = function(){
+ var json = {
+ p2 : pkg.version.split(".").slice(0,2).join("."), // "X.Y"
+ bodies : [],
+ springs : [],
+ solver : {},
+ gravity : v2a(this.gravity),
+ broadphase : {},
+ constraints : [],
+ contactMaterials : [],
+ };
+
+ // Serialize springs
+ for(var i=0; i=0; i--){
+ this.removeConstraint(cs[i]);
+ }
+
+ // Remove all bodies
+ var bodies = this.bodies;
+ for(var i=bodies.length-1; i>=0; i--){
+ this.removeBody(bodies[i]);
+ }
+
+ // Remove all springs
+ var springs = this.springs;
+ for(var i=springs.length-1; i>=0; i--){
+ this.removeSpring(springs[i]);
+ }
+};
+
+/**
+ * Get a copy of this World instance
+ * @method clone
+ * @return {World}
+ */
+World.prototype.clone = function(){
+ var world = new World();
+ world.fromJSON(this.toJSON());
+ return world;
+};
+
+var hitTest_tmp1 = vec2.create(),
+ hitTest_zero = vec2.fromValues(0,0),
+ hitTest_tmp2 = vec2.fromValues(0,0);
+
+/**
+ * Test if a world point overlaps bodies
+ * @method hitTest
+ * @param {Array} worldPoint Point to use for intersection tests
+ * @param {Array} bodies A list of objects to check for intersection
+ * @param {Number} precision Used for matching against particles and lines. Adds some margin to these infinitesimal objects.
+ * @return {Array} Array of bodies that overlap the point
+ */
+World.prototype.hitTest = function(worldPoint,bodies,precision){
+ precision = precision || 0;
+
+ // Create a dummy particle body with a particle shape to test against the bodies
+ var pb = new Body({ position:worldPoint }),
+ ps = new Particle(),
+ px = worldPoint,
+ pa = 0,
+ x = hitTest_tmp1,
+ zero = hitTest_zero,
+ tmp = hitTest_tmp2;
+ pb.addShape(ps);
+
+ var n = this.nearphase,
+ result = [];
+
+ // Check bodies
+ for(var i=0, N=bodies.length; i!==N; i++){
+ var b = bodies[i];
+ for(var j=0, NS=b.shapes.length; j!==NS; j++){
+ var s = b.shapes[j],
+ offset = b.shapeOffsets[j] || zero,
+ angle = b.shapeAngles[j] || 0.0;
+
+ // Get shape world position + angle
+ vec2.rotate(x, offset, b.angle);
+ vec2.add(x, x, b.position);
+ var a = angle + b.angle;
+
+ if( (s instanceof Circle && n.circleParticle (b,s,x,a, pb,ps,px,pa, true)) ||
+ (s instanceof Convex && n.particleConvex (pb,ps,px,pa, b,s,x,a, true)) ||
+ (s instanceof Plane && n.particlePlane (pb,ps,px,pa, b,s,x,a, true)) ||
+ (s instanceof Capsule && n.particleCapsule (pb,ps,px,pa, b,s,x,a, true)) ||
+ (s instanceof Particle && vec2.squaredLength(vec2.sub(tmp,x,worldPoint)) < precision*precision)
+ ){
+ result.push(b);
+ }
+ }
+ }
+
+ return result;
+};
diff --git a/src/tilemap/Tile.js b/src/tilemap/Tile.js
new file mode 100644
index 00000000..b55e23fa
--- /dev/null
+++ b/src/tilemap/Tile.js
@@ -0,0 +1,190 @@
+/**
+* @author Richard Davey
+* @copyright 2013 Photon Storm Ltd.
+* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
+* @module Phaser.Tile
+*/
+
+
+/**
+* Create a new Tile.
+*
+* @class Phaser.Tile
+* @classdesc A Tile is a single representation of a tile within a Tilemap.
+* @constructor
+* @param {Phaser.Game} game - A reference to the currently running game.
+* @param {Tilemap} tilemap - The tilemap this tile belongs to.
+* @param {number} index - The index of this tile type in the core map data.
+* @param {number} width - Width of the tile.
+* @param {number} height - Height of the tile.
+*/
+Phaser.Tile = function (tileset, index, x, y, width, height) {
+
+ /**
+ * @property {string} tileset - The tileset this tile belongs to.
+ */
+ this.tileset = tileset;
+
+ /**
+ * @property {number} index - The index of this tile within the tileset.
+ */
+ this.index = index;
+
+ /**
+ * @property {number} width - The width of the tile in pixels.
+ */
+ this.width = width;
+
+ /**
+ * @property {number} height - The height of the tile in pixels.
+ */
+ this.height = height;
+
+ /**
+ * @property {number} x - The top-left corner of the tile within the tileset.
+ */
+ this.x = x;
+
+ /**
+ * @property {number} y - The top-left corner of the tile within the tileset.
+ */
+ this.y = y;
+
+ // Any extra meta data info we need here
+
+ /**
+ * @property {number} mass - The virtual mass of the tile.
+ * @default
+ */
+ this.mass = 1.0;
+
+ /**
+ * @property {boolean} collideNone - Indicating this Tile doesn't collide at all.
+ * @default
+ */
+ this.collideNone = true;
+
+ /**
+ * @property {boolean} collideLeft - Indicating collide with any object on the left.
+ * @default
+ */
+ this.collideLeft = false;
+
+ /**
+ * @property {boolean} collideRight - Indicating collide with any object on the right.
+ * @default
+ */
+ this.collideRight = false;
+
+ /**
+ * @property {boolean} collideUp - Indicating collide with any object on the top.
+ * @default
+ */
+ this.collideUp = false;
+
+ /**
+ * @property {boolean} collideDown - Indicating collide with any object on the bottom.
+ * @default
+ */
+ this.collideDown = false;
+
+ /**
+ * @property {boolean} separateX - Enable separation at x-axis.
+ * @default
+ */
+ this.separateX = true;
+
+ /**
+ * @property {boolean} separateY - Enable separation at y-axis.
+ * @default
+ */
+ this.separateY = true;
+
+};
+
+Phaser.Tile.prototype = {
+
+ /**
+ * Clean up memory.
+ * @method destroy
+ */
+ destroy: function () {
+ this.tilemap = null;
+ },
+
+ /**
+ * Set collision configs.
+ * @method setCollision
+ * @param {boolean} left - Indicating collide with any object on the left.
+ * @param {boolean} right - Indicating collide with any object on the right.
+ * @param {boolean} up - Indicating collide with any object on the top.
+ * @param {boolean} down - Indicating collide with any object on the bottom.
+ * @param {boolean} reset - Description.
+ * @param {boolean} separateX - Separate at x-axis.
+ * @param {boolean} separateY - Separate at y-axis.
+ */
+ setCollision: function (left, right, up, down, reset, separateX, separateY) {
+
+ if (reset)
+ {
+ this.resetCollision();
+ }
+
+ this.separateX = separateX;
+ this.separateY = separateY;
+
+ this.collideNone = true;
+ this.collideLeft = left;
+ this.collideRight = right;
+ this.collideUp = up;
+ this.collideDown = down;
+
+ if (left || right || up || down)
+ {
+ this.collideNone = false;
+ }
+
+ },
+
+ /**
+ * Reset collision status flags.
+ * @method resetCollision
+ */
+ resetCollision: function () {
+
+ this.collideNone = true;
+ this.collideLeft = false;
+ this.collideRight = false;
+ this.collideUp = false;
+ this.collideDown = false;
+
+ }
+
+};
+
+Object.defineProperty(Phaser.Tile.prototype, "bottom", {
+
+ /**
+ * The sum of the y and height properties. Changing the bottom property of a Rectangle object has no effect on the x, y and width properties, but does change the height property.
+ * @method bottom
+ * @return {number}
+ **/
+ get: function () {
+ return this.y + this.height;
+ }
+
+});
+
+Object.defineProperty(Phaser.Tile.prototype, "right", {
+
+ /**
+ * The sum of the x and width properties. Changing the right property of a Rectangle object has no effect on the x, y and height properties.
+ * However it does affect the width property.
+ * @method right
+ * @return {number}
+ **/
+ get: function () {
+ return this.x + this.width;
+ }
+
+});
diff --git a/src/tilemap/TilemapLayer.js b/src/tilemap/TilemapLayer.js
index 80fd7516..146f9041 100644
--- a/src/tilemap/TilemapLayer.js
+++ b/src/tilemap/TilemapLayer.js
@@ -1,217 +1,80 @@
-/**
-* @author Richard Davey
-* @copyright 2013 Photon Storm Ltd.
-* @license https://github.com/photonstorm/phaser/blob/master/license.txt MIT License
-* @module Phaser.TilemapLayer
-*/
-/**
-* Create a new TilemapLayer.
-* @class Phaser.TilemapLayer
-* @classdesc A Tilemap Layer. Tiled format maps can have multiple overlapping layers.
-* @constructor
-* @param parent {Tilemap} The tilemap that contains this layer.
-* @param id {number} The ID of this layer within the Tilemap array.
-* @param key {string} Asset key for this map.
-* @param mapformat {number} Format of this map data, available: Tilemap.CSV or Tilemap.JSON.
-* @param name {string} Name of this layer, so you can get this layer by its name.
-* @param tileWidth {number} Width of tiles in this map.
-* @param tileHeight {number} Height of tiles in this map.
-*/
-Phaser.TilemapLayer = function (parent, id, key, mapFormat, name, tileWidth, tileHeight) {
+Phaser.TilemapLayer = function (game, x, y, renderWidth, renderHeight, mapData, tileset) {
- /**
- * @property {boolean} exists - Controls whether update() and draw() are automatically called.
- * @default
- */
- this.exists = true;
-
- /**
- * @property {boolean} visible - Controls whether draw() are automatically called.
- * @default
- */
- this.visible = true;
-
- /**
- * How many tiles in each row.
- * Read-only variable, do NOT recommend changing after the map is loaded!
- * @property {number} widthInTiles
- * @default
- */
- this.widthInTiles = 0;
-
- /**
- * How many tiles in each column.
- * Read-only variable, do NOT recommend changing after the map is loaded!
- * @property {number} heightInTiles
- * @default
- */
- this.heightInTiles = 0;
-
- /**
- * Read-only variable, do NOT recommend changing after the map is loaded!
- * @property {number} widthInPixels
- * @default
- */
- this.widthInPixels = 0;
-
- /**
- * Read-only variable, do NOT recommend changing after the map is loaded!
- * @property {number} heightInPixels
- * @default
- */
- this.heightInPixels = 0;
-
- /**
- * Distance between REAL tiles to the tileset texture bound.
- * @property {number} tileMargin
- * @default
- */
- this.tileMargin = 0;
-
- /**
- * Distance between every 2 neighbor tile in the tileset texture.
- * @property {number} tileSpacing
- * @default
- */
- this.tileSpacing = 0;
-
- /**
- * @property {Description} parent - Description.
- */
- this.parent = parent;
-
/**
* @property {Phaser.Game} game - Description.
*/
- this.game = parent.game;
+ this.game = game;
- /**
- * @property {Description} ID - Description.
- */
- this.ID = id;
-
- /**
- * @property {Description} name - Description.
- */
- this.name = name;
-
- /**
- * @property {Description} key - Description.
- */
- this.key = key;
-
- /**
- * @property {Description} type - Description.
- */
- this.type = Phaser.TILEMAPLAYER;
-
- /**
- * @property {tileWidth} mapFormat - Description.
- */
- this.mapFormat = mapFormat;
-
- /**
- * @property {Description} tileWidth - Description.
- */
- this.tileWidth = tileWidth;
-
- /**
- * @property {Description} tileHeight - Description.
- */
- this.tileHeight = tileHeight;
-
- /**
- * @property {Phaser.Rectangle} boundsInTiles - Description.
- */
- this.boundsInTiles = new Phaser.Rectangle();
-
- var map = this.game.cache.getTilemap(key);
-
- /**
- * @property {Description} tileset - Description.
- */
- this.tileset = map.data;
-
- /**
- * @property {Description} _alpha - Description.
- * @private
- * @default
- */
- this._alpha = 1;
-
/**
* @property {Description} canvas - Description.
* @default
*/
- this.canvas = null;
+ this.canvas = Phaser.Canvas.create(renderWidth, renderHeight);
/**
* @property {Description} context - Description.
* @default
*/
- this.context = null;
+ this.context = this.canvas.getContext('2d');
/**
* @property {Description} baseTexture - Description.
* @default
*/
- this.baseTexture = null;
+ this.baseTexture = new PIXI.BaseTexture(this.canvas);
/**
* @property {Description} texture - Description.
* @default
*/
- this.texture = null;
+ this.texture = new PIXI.Texture(this.baseTexture);
/**
* @property {Description} sprite - Description.
* @default
*/
- this.sprite = null;
+ this.sprite = new PIXI.Sprite(this.texture);
- /**
- * @property {array} mapData - Description.
- */
+ /**
+ * @property {array} mapData - Description.
+ */
this.mapData = [];
-
- /**
- * @property {array} _tempTileBlock - Description.
- * @private
- */
- this._tempTileBlock = [];
-
- /**
- * @property {array} _tempBlockResults - Description.
- * @private
- *
- */
- this._tempBlockResults = [];
+
+ /**
+ * @property {Description} tileset - Description.
+ */
+ this.tileset = tileset;
+
+ this.widthInTiles = 0;
+ this.heightInTiles = 0;
+
+ this.renderWidth = renderWidth;
+ this.renderHeight = renderHeight;
};
Phaser.TilemapLayer.prototype = {
- /**
- * Set a specific tile with its x and y in tiles.
- * @method putTileWorldXY
- * @param {number} x - X position of this tile in world coordinates.
- * @param {number} y - Y position of this tile in world coordinates.
- * @param {number} index - The index of this tile type in the core map data.
- */
- putTileWorldXY: function (x, y, index) {
+ create: function (width, height) {
- x = this.game.math.snapToFloor(x, this.tileWidth) / this.tileWidth;
- y = this.game.math.snapToFloor(y, this.tileHeight) / this.tileHeight;
+ this.mapData = [];
- if (y >= 0 && y < this.mapData.length)
+ var data;
+
+ for (var y = 0; y < height; y++)
{
- if (x >= 0 && x < this.mapData[y].length)
+ this.mapData[y] = [];
+
+ for (var x = 0; x < width; x++)
{
- this.mapData[y][x] = index;
+ this.mapData[y][x] = 0;
}
}
+ this.widthInTiles = width;
+ this.heightInTiles = height;
+
},
/**
@@ -233,430 +96,34 @@ Phaser.TilemapLayer.prototype = {
},
- /**
- * Swap tiles with 2 kinds of indexes.
- * @method swapTile
- * @param {number} tileA - First tile index.
- * @param {number} tileB - Second tile index.
- * @param {number} [x] - specify a Rectangle of tiles to operate. The x position in tiles of Rectangle's left-top corner.
- * @param {number} [y] - specify a Rectangle of tiles to operate. The y position in tiles of Rectangle's left-top corner.
- * @param {number} [width] - specify a Rectangle of tiles to operate. The width in tiles.
- * @param {number} [height] - specify a Rectangle of tiles to operate. The height in tiles.
- */
- swapTile: function (tileA, tileB, x, y, width, height) {
+ dump: function () {
- x = x || 0;
- y = y || 0;
- width = width || this.widthInTiles;
- height = height || this.heightInTiles;
-
- this.getTempBlock(x, y, width, height);
+ var txt = '';
+ var args = [''];
- for (var r = 0; r < this._tempTileBlock.length; r++)
+ for (var y = 0; y < this.heightInTiles; y++)
{
- // First sweep marking tileA as needing a new index
- if (this._tempTileBlock[r].tile.index == tileA)
+ for (var x = 0; x < this.widthInTiles; x++)
{
- this._tempTileBlock[r].newIndex = true;
- }
+ txt += "%c ";
- // In the same pass we can swap tileB to tileA
- if (this._tempTileBlock[r].tile.index == tileB)
- {
- this.mapData[this._tempTileBlock[r].y][this._tempTileBlock[r].x] = tileA;
- }
- }
-
- for (var r = 0; r < this._tempTileBlock.length; r++)
- {
- // And now swap our newIndex tiles for tileB
- if (this._tempTileBlock[r].newIndex == true)
- {
- this.mapData[this._tempTileBlock[r].y][this._tempTileBlock[r].x] = tileB;
- }
- }
-
- },
-
- /**
- * Fill a tile block with a specific tile index.
- * @method fillTile
- * @param {number} index - Index of tiles you want to fill with.
- * @param {number} [x] - X position (in tiles) of block's left-top corner.
- * @param {number} [y] - Y position (in tiles) of block's left-top corner.
- * @param {number} [width] - width of block.
- * @param {number} [height] - height of block.
- */
- fillTile: function (index, x, y, width, height) {
-
- x = x || 0;
- y = y || 0;
- width = width || this.widthInTiles;
- height = height || this.heightInTiles;
-
- this.getTempBlock(x, y, width, height);
-
- for (var r = 0; r < this._tempTileBlock.length; r++)
- {
- this.mapData[this._tempTileBlock[r].y][this._tempTileBlock[r].x] = index;
- }
-
- },
-
- /**
- * Set random tiles to a specific tile block.
- * @method randomiseTiles
- * @param {number[]} tiles - Tiles with indexes in this array will be randomly set to the given block.
- * @param {number} [x] - X position (in tiles) of block's left-top corner.
- * @param {number} [y] - Y position (in tiles) of block's left-top corner.
- * @param {number} [width] - width of block.
- * @param {number} [height] - height of block.
- */
- randomiseTiles: function (tiles, x, y, width, height) {
-
- x = x || 0;
- y = y || 0;
- width = width || this.widthInTiles;
- height = height || this.heightInTiles;
-
- this.getTempBlock(x, y, width, height);
-
- for (var r = 0; r < this._tempTileBlock.length; r++)
- {
- this.mapData[this._tempTileBlock[r].y][this._tempTileBlock[r].x] = this.game.math.getRandom(tiles);
- }
-
- },
-
- /**
- * Replace one kind of tiles to another kind.
- * @method replaceTile
- * @param {number} tileA - First tile index.
- * @param {number} tileB - Second tile index.
- * @param {number} [x] - X position (in tiles) of block's left-top corner.
- * @param {number} [y] - Y position (in tiles) of block's left-top corner.
- * @param {number} [width] - width of block.
- * @param {number} [height] - height of block.
- */
- replaceTile: function (tileA, tileB, x, y, width, height) {
-
- x = x || 0;
- y = y || 0;
- width = width || this.widthInTiles;
- height = height || this.heightInTiles;
-
- this.getTempBlock(x, y, width, height);
-
- for (var r = 0; r < this._tempTileBlock.length; r++)
- {
- if (this._tempTileBlock[r].tile.index == tileA)
- {
- this.mapData[this._tempTileBlock[r].y][this._tempTileBlock[r].x] = tileB;
- }
- }
-
- },
-
- /**
- * Get a tile block with specific position and size (both are in tiles).
- * @method getTileBlock
- * @param {number} [x] - X position (in tiles) of block's left-top corner.
- * @param {number} [y] - Y position (in tiles) of block's left-top corner.
- * @param {number} [width] - width of block.
- * @param {number} [height] - height of block.
- */
- getTileBlock: function (x, y, width, height) {
-
- var output = [];
-
- this.getTempBlock(x, y, width, height);
-
- for (var r = 0; r < this._tempTileBlock.length; r++)
- {
- output.push({
- x: this._tempTileBlock[r].x,
- y: this._tempTileBlock[r].y,
- tile: this._tempTileBlock[r].tile
- });
- }
-
- return output;
-
- },
-
- /**
- * Get a tile with specific position (in world coordinate). (thus you give a position of a point which is within the tile)
- * @method getTileFromWorldXY
- * @param {number} [x] - X position (in tiles) of block's left-top corner.
- * @param {number} [y] - Y position (in tiles) of block's left-top corner.
- */
- getTileFromWorldXY: function (x, y) {
-
- x = Phaser.Math.snapToFloor(x, this.tileWidth) / this.tileWidth;
- y = Phaser.Math.snapToFloor(y, this.tileHeight) / this.tileHeight;
-
- return this.getTileIndex(x, y);
-
- },
-
- /**
- * Get tiles overlaps the given object.
- * @method getTileOverlaps
- * @param {GameObject} object - Tiles you want to get that overlaps this.
- * @return {array} Array with tiles informations (each contains x, y, and the tile).
- */
- getTileOverlaps: function (object) {
-
- this._tempBlockResults.length = 0;
-
- // If the object is outside of the world coordinates then abort the check (tilemap has to exist within world bounds)
- if (object.body.x < 0 || object.body.x > this.widthInPixels || object.body.y < 0 || object.body.bottom > this.heightInPixels)
- {
- return this._tempBlockResults;
- }
-
- // What tiles do we need to check against?
- this._tempTileX = this.game.math.snapToFloor(object.body.x, this.tileWidth) / this.tileWidth;
- this._tempTileY = this.game.math.snapToFloor(object.body.y, this.tileHeight) / this.tileHeight;
- this._tempTileW = (this.game.math.snapToCeil(object.body.width, this.tileWidth) + this.tileWidth) / this.tileWidth;
- this._tempTileH = (this.game.math.snapToCeil(object.body.height, this.tileHeight) + this.tileHeight) / this.tileHeight;
-
- // Loop through the tiles we've got and check overlaps accordingly (the results are stored in this._tempTileBlock)
- this.getTempBlock(this._tempTileX, this._tempTileY, this._tempTileW, this._tempTileH, true);
-
- for (var r = 0; r < this._tempTileBlock.length; r++)
- {
- // separateTile: function (object, x, y, width, height, mass, collideLeft, collideRight, collideUp, collideDown, separateX, separateY)
- if (this.game.physics.separateTile(object, this._tempTileBlock[r].x * this.tileWidth, this._tempTileBlock[r].y * this.tileHeight, this.tileWidth, this.tileHeight, this._tempTileBlock[r].tile.mass, this._tempTileBlock[r].tile.collideLeft, this._tempTileBlock[r].tile.collideRight, this._tempTileBlock[r].tile.collideUp, this._tempTileBlock[r].tile.collideDown, this._tempTileBlock[r].tile.separateX, this._tempTileBlock[r].tile.separateY))
- {
- this._tempBlockResults.push({ x: this._tempTileBlock[r].x, y: this._tempTileBlock[r].y, tile: this._tempTileBlock[r].tile });
- }
- }
-
- return this._tempBlockResults;
-
- },
-
- /**
- * Get a tile block with its position and size (this method does not return, it'll set result to _tempTileBlock).
- * @method getTempBlock
- * @param {number} [x] - X position (in tiles) of block's left-top corner.
- * @param {number} [y] - Y position (in tiles) of block's left-top corner.
- * @param {number} [width] - width of block.
- * @param {number} [height] - height of block.
- * @param {boolean} collisionOnly - Whethor or not ONLY return tiles which will collide (its allowCollisions value is not Collision.NONE).
- */
- getTempBlock: function (x, y, width, height, collisionOnly) {
-
- if (typeof collisionOnly === "undefined") { collisionOnly = false; }
-
- if (x < 0)
- {
- x = 0;
- }
-
- if (y < 0)
- {
- y = 0;
- }
-
- if (width > this.widthInTiles)
- {
- width = this.widthInTiles;
- }
-
- if (height > this.heightInTiles)
- {
- height = this.heightInTiles;
- }
-
- this._tempTileBlock = [];
-
- for (var ty = y; ty < y + height; ty++)
- {
- for (var tx = x; tx < x + width; tx++)
- {
- if (collisionOnly)
+ if (this.mapData[y][x] > 0)
{
- // We only want to consider the tile for checking if you can actually collide with it
- if (this.mapData[ty] && this.mapData[ty][tx] && this.parent.tiles[this.mapData[ty][tx]].collideNone == false)
- {
- this._tempTileBlock.push({
- x: tx,
- y: ty,
- tile: this.parent.tiles[this.mapData[ty][tx]]
- });
- }
+ args.push("background: rgb(50, 50, 50)");
}
else
{
- if (this.mapData[ty] && this.mapData[ty][tx])
- {
- this._tempTileBlock.push({
- x: tx,
- y: ty,
- tile: this.parent.tiles[this.mapData[ty][tx]]
- });
- }
+ args.push("background: rgb(0, 0, 0)");
}
}
- }
- },
- /**
- * Get the tile index of specific position (in tiles).
- * @method getTileIndex
- * @param {number} x - X position of the tile.
- * @param {number} y - Y position of the tile.
- * @return {number} Index of the tile at that position. Return null if there isn't a tile there.
- */
- getTileIndex: function (x, y) {
-
- if (y >= 0 && y < this.mapData.length)
- {
- if (x >= 0 && x < this.mapData[y].length)
- {
- return this.mapData[y][x];
- }
+ txt += "\n";
}
- return null;
-
- },
-
- /**
- * Add a column of tiles into the layer.
- * @method addColumn
- * @param {string[]|number[]} column - An array of tile indexes to be added.
- */
- addColumn: function (column) {
-
- var data = [];
-
- for (var c = 0; c < column.length; c++)
- {
- data[c] = parseInt(column[c]);
- }
-
- if (this.widthInTiles == 0)
- {
- this.widthInTiles = data.length;
- this.widthInPixels = this.widthInTiles * this.tileWidth;
- }
-
- this.mapData.push(data);
-
- this.heightInTiles++;
- this.heightInPixels += this.tileHeight;
-
- },
-
- /**
- * Description.
- * @method createCanvas
- */
- createCanvas: function () {
-
- var width = this.game.width;
- var height = this.game.height;
-
- if (this.widthInPixels < width)
- {
- width = this.widthInPixels;
- }
-
- if (this.heightInPixels < height)
- {
- height = this.heightInPixels;
- }
-
- this.canvas = Phaser.Canvas.create(width, height);
- this.context = this.canvas.getContext('2d');
-
- this.baseTexture = new PIXI.BaseTexture(this.canvas);
- this.texture = new PIXI.Texture(this.baseTexture);
- this.sprite = new PIXI.Sprite(this.texture);
-
- this.parent.addChild(this.sprite);
-
- },
-
- createQuadTree: function (width, height) {
-
- this.quadTree = new Phaser.QuadTree(this, 0, 0, width, height, 20, 4);
-
- },
-
- /**
- * Update boundsInTiles with widthInTiles and heightInTiles.
- * @method updateBounds
- */
- updateBounds: function () {
-
- this.boundsInTiles.setTo(0, 0, this.widthInTiles, this.heightInTiles);
-
- },
-
- /**
- * Parse tile offsets from map data.
- * Basically this creates a large array of objects that contain the x/y coordinates to grab each tile from
- * for the entire map. Yes we could calculate this at run-time by using the tile index and some math, but we're
- * trading a quite small bit of memory here to not have to process that in our main render loop.
- * @method parseTileOffsets
- * @return {number} Length of tileOffsets array.
- */
- parseTileOffsets: function () {
-
- this.tileOffsets = [];
-
- var i = 0;
-
- if (this.mapFormat == Phaser.Tilemap.JSON)
- {
- // For some reason Tiled counts from 1 not 0
- this.tileOffsets[0] = null;
- i = 1;
- }
-
- for (var ty = this.tileMargin; ty < this.tileset.height; ty += (this.tileHeight + this.tileSpacing))
- {
- for (var tx = this.tileMargin; tx < this.tileset.width; tx += (this.tileWidth + this.tileSpacing))
- {
- this.tileOffsets[i] = {
- x: tx,
- y: ty
- };
- i++;
- }
- }
-
- return this.tileOffsets.length;
+ args[0] = txt;
+ console.log.apply(console, args);
}
};
-/**
-* Get
-* @return {Description}
-*//**
-* Set
-* @param {Description} value - Description.
-*/
-Object.defineProperty(Phaser.TilemapLayer.prototype, 'alpha', {
-
- get: function() {
- return this._alpha;
- },
-
- set: function(value) {
-
- if (this.sprite)
- {
- this.sprite.alpha = value;
- }
-
- this._alpha = value;
- }
-
-});
diff --git a/src/tilemap/TilemapParser.js b/src/tilemap/TilemapParser.js
new file mode 100644
index 00000000..ecb076bb
--- /dev/null
+++ b/src/tilemap/TilemapParser.js
@@ -0,0 +1,120 @@
+Phaser.TilemapParser = {
+
+ /**
+ * Parse a Sprite Sheet and extract the animation frame data from it.
+ *
+ * @method Phaser.AnimationParser.spriteSheet
+ * @param {Phaser.Game} game - A reference to the currently running game.
+ * @param {string} key - The Game.Cache asset key of the Sprite Sheet image.
+ * @param {number} frameWidth - The fixed width of each frame of the animation.
+ * @param {number} frameHeight - The fixed height of each frame of the animation.
+ * @param {number} [frameMax=-1] - The total number of animation frames to extact from the Sprite Sheet. The default value of -1 means "extract all frames".
+ * @return {Phaser.FrameData} A FrameData object containing the parsed frames.
+ */
+ tileset: function (game, key, tileWidth, tileHeight, tileMax) {
+
+ // How big is our image?
+ var img = game.cache.getTilesetImage(key);
+
+ if (img == null)
+ {
+ return null;
+ }
+
+ var width = img.width;
+ var height = img.height;
+
+ if (tileWidth <= 0)
+ {
+ tileWidth = Math.floor(-width / Math.min(-1, tileWidth));
+ }
+
+ if (tileHeight <= 0)
+ {
+ tileHeight = Math.floor(-height / Math.min(-1, tileHeight));
+ }
+
+ var row = Math.round(width / tileWidth);
+ var column = Math.round(height / tileHeight);
+ var total = row * column;
+
+ if (tileMax !== -1)
+ {
+ total = tileMax;
+ }
+
+ // Zero or smaller than tile sizes?
+ if (width == 0 || height == 0 || width < tileWidth || height < tileHeight || total === 0)
+ {
+ console.warn("Phaser.TilemapParser.tileSet: width/height zero or width/height < given tileWidth/tileHeight");
+ return null;
+ }
+
+ // Let's create some tiles
+ var x = 0;
+ var y = 0;
+
+ var tileset = new Phaser.Tileset(key, tileWidth, tileHeight);
+
+ for (var i = 0; i < total; i++)
+ {
+ tileset.addTile(new Phaser.Tile(tileset, i, x, y, tileWidth, tileHeight));
+
+ x += tileWidth;
+
+ if (x === width)
+ {
+ x = 0;
+ y += tileHeight;
+ }
+ }
+
+ return tileset;
+
+ },
+
+ /**
+ * Parse csv map data and generate tiles.
+ *
+ * @method Phaser.Tilemap.prototype.parseCSV
+ * @param {string} data - CSV map data.
+ * @param {string} key - Asset key for tileset image.
+ * @param {number} tileWidth - Width of its tile.
+ * @param {number} tileHeight - Height of its tile.
+ */
+ parseCSV: function (data, key, tileWidth, tileHeight) {
+
+ // var layer = new Phaser.TilemapLayer(this, 0, key, Phaser.Tilemap.CSV, 'TileLayerCSV' + this.layers.length.toString(), tileWidth, tileHeight);
+
+ // Trim any rogue whitespace from the data
+ data = data.trim();
+
+ var rows = data.split("\n");
+
+ for (var i = 0; i < rows.length; i++)
+ {
+ var column = rows[i].split(",");
+
+ if (column.length > 0)
+ {
+ // layer.addColumn(column);
+ }
+ }
+
+ // layer.updateBounds();
+ // layer.createCanvas();
+
+ // var tileQuantity = layer.parseTileOffsets();
+
+ // this.currentLayer = layer;
+ // this.collisionLayer = layer;
+ // this.layers.push(layer);
+
+ // this.width = this.currentLayer.widthInPixels;
+ // this.height = this.currentLayer.heightInPixels;
+
+ // this.generateTiles(tileQuantity);
+
+ }
+
+}
diff --git a/src/tilemap/Tileset.js b/src/tilemap/Tileset.js
new file mode 100644
index 00000000..2116bea3
--- /dev/null
+++ b/src/tilemap/Tileset.js
@@ -0,0 +1,50 @@
+
+Phaser.Tileset = function (key, tileWidth, tileHeight) {
+
+ /**
+ * @property {string} key - The cache ID.
+ */
+ this.key = key;
+
+ this.tilewidth = tileWidth;
+ this.tileHeight = tileHeight;
+
+ this._tiles = [];
+
+}
+
+Phaser.Tileset.prototype = {
+
+ addTile: function (tile) {
+
+ this._tiles.push(tile);
+
+ return tile;
+
+ },
+
+ getTile: function (index) {
+
+ if (this._tiles[index])
+ {
+ return this._tiles[index];
+ }
+
+ return null;
+
+ }
+
+}
+
+/**
+* @name Phaser.Tileset#total
+* @property {number} total - The total number of tiles in this Tileset.
+* @readonly
+*/
+Object.defineProperty(Phaser.Tileset.prototype, "total", {
+
+ get: function () {
+ return this._ties.length;
+ }
+
+});
diff --git a/src/tween/Tween.js b/src/tween/Tween.js
index b722d6da..17ce03cc 100644
--- a/src/tween/Tween.js
+++ b/src/tween/Tween.js
@@ -203,13 +203,14 @@ Phaser.Tween.prototype = {
if (this._parent)
{
self = this._manager.create(this._object);
- self._parent = this._parent;
- this.chain(self);
+ this._lastChild.chain(self);
+ this._lastChild = self;
}
else
{
self = this;
- self._parent = self;
+ this._parent = this;
+ this._lastChild = this;
}
self._repeat = repeat;
@@ -229,9 +230,9 @@ Phaser.Tween.prototype = {
self._yoyo = yoyo;
if (autoStart) {
- return self.start();
+ return this.start();
} else {
- return self;
+ return this;
}
},
@@ -399,11 +400,11 @@ Phaser.Tween.prototype = {
* .to({ y: 0 }, 1000, Phaser.Easing.Linear.None)
* .loop();
* @method Phaser.Tween#loop
- * @return {Tween} Itself.
+ * @return {Phaser.Tween} Itself.
*/
loop: function() {
- if (this._parent) this.chain(this._parent);
+ this._lastChild.chain(this);
return this;
},
@@ -457,6 +458,7 @@ Phaser.Tween.prototype = {
*/
pause: function () {
this._paused = true;
+ this._pausedTime = this.game.time.now;
},
/**
@@ -466,7 +468,7 @@ Phaser.Tween.prototype = {
*/
resume: function () {
this._paused = false;
- this._startTime += this.game.time.pauseDuration;
+ this._startTime += (this.game.time.now - this._pausedTime);
},
/**