mirror of
https://github.com/wassname/phaser.git
synced 2026-06-27 16:10:15 +08:00
Removed debug info and tidying up ready for release.
This commit is contained in:
-8768
File diff suppressed because it is too large
Load Diff
Vendored
-26
File diff suppressed because one or more lines are too long
@@ -36,6 +36,12 @@ function test8() {
|
||||
game.step();
|
||||
});
|
||||
|
||||
$('#start').click(function(){
|
||||
console.log('---- START DEBUGGING -------------------------------');
|
||||
game.enableStep();
|
||||
sprite2.debug = true;
|
||||
});
|
||||
|
||||
game.physics.gravity.y = 200;
|
||||
|
||||
sprite = game.add.sprite(300, 300, 'atari');
|
||||
@@ -48,7 +54,7 @@ function test8() {
|
||||
sprite2.name = 'gameboy';
|
||||
sprite2.body.collideWorldBounds = true;
|
||||
sprite2.body.bounce.setTo(0.8, 0.8);
|
||||
// sprite2.body.minVelocity.setTo(30, 30);
|
||||
sprite2.body.minVelocity.setTo(10, 10);
|
||||
// sprite.debug = true;
|
||||
|
||||
// game.enableStep();
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
var vec2 = require('../math/vec2')
|
||||
|
||||
module.exports = Broadphase;
|
||||
|
||||
/**
|
||||
* Base class for broadphase implementations.
|
||||
* @class Broadphase
|
||||
* @constructor
|
||||
*/
|
||||
function Broadphase(){
|
||||
|
||||
/**
|
||||
* The resulting overlapping pairs. Will be filled with results during .getCollisionPairs().
|
||||
* @property result
|
||||
* @type {Array}
|
||||
*/
|
||||
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!");
|
||||
};
|
||||
|
||||
var dist = vec2.create();
|
||||
|
||||
/**
|
||||
* 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;
|
||||
};
|
||||
@@ -1,159 +0,0 @@
|
||||
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<Nbins; i++)
|
||||
bins.push([]);
|
||||
|
||||
var xmult = nx / (xmax-xmin);
|
||||
var ymult = ny / (ymax-ymin);
|
||||
|
||||
// Put all bodies into bins
|
||||
for(var i=0; i!==Ncolliding; i++){
|
||||
var bi = collidingBodies[i];
|
||||
var si = bi.shape;
|
||||
if (si === undefined) {
|
||||
continue;
|
||||
} else if(si instanceof Circle){
|
||||
// Put in bin
|
||||
// check if overlap with other bins
|
||||
var x = bi.position[0];
|
||||
var y = bi.position[1];
|
||||
var r = si.radius;
|
||||
|
||||
var xi1 = Math.floor(xmult * (x-r - xmin));
|
||||
var yi1 = Math.floor(ymult * (y-r - ymin));
|
||||
var xi2 = Math.floor(xmult * (x+r - xmin));
|
||||
var yi2 = Math.floor(ymult * (y+r - ymin));
|
||||
|
||||
for(var j=xi1; j<=xi2; j++){
|
||||
for(var k=yi1; k<=yi2; k++){
|
||||
var xi = j;
|
||||
var yi = k;
|
||||
if(xi*(ny-1) + yi >= 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)<y; j++){
|
||||
for(var k=0; k<nx; k++){
|
||||
var xi = k;
|
||||
var yi = Math.floor(ymult * (binsizeY*j - ymin));
|
||||
bins[ xi*(ny-1) + yi ].push(bi);
|
||||
}
|
||||
}
|
||||
} else if(bi.angle == Math.PI*0.5){
|
||||
var x = bi.position[0];
|
||||
for(var j=0; j!==Nbins && xmin+binsizeX*(j-1)<x; j++){
|
||||
for(var k=0; k<ny; k++){
|
||||
var yi = k;
|
||||
var xi = Math.floor(xmult * (binsizeX*j - xmin));
|
||||
bins[ xi*(ny-1) + yi ].push(bi);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(var j=0; j!==Nbins; j++)
|
||||
bins[j].push(bi);
|
||||
}
|
||||
} else {
|
||||
throw new Error("Shape not supported in GridBroadphase!");
|
||||
}
|
||||
}
|
||||
|
||||
// Check each bin
|
||||
for(var i=0; i!==Nbins; i++){
|
||||
var bin = bins[i];
|
||||
|
||||
for(var j=0, NbodiesInBin=bin.length; j!==NbodiesInBin; j++){
|
||||
var bi = bin[j];
|
||||
var si = bi.shape;
|
||||
|
||||
for(var k=0; k!==j; k++){
|
||||
var bj = bin[k];
|
||||
var sj = bj.shape;
|
||||
|
||||
if(si instanceof Circle){
|
||||
if(sj instanceof Circle) c=Broadphase.circleCircle (bi,bj);
|
||||
else if(sj instanceof Particle) c=Broadphase.circleParticle(bi,bj);
|
||||
else if(sj instanceof Plane) c=Broadphase.circlePlane (bi,bj);
|
||||
} else if(si instanceof Particle){
|
||||
if(sj instanceof Circle) c=Broadphase.circleParticle(bj,bi);
|
||||
} else if(si instanceof Plane){
|
||||
if(sj instanceof Circle) c=Broadphase.circlePlane (bj,bi);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
@@ -1,47 +0,0 @@
|
||||
var Circle = require('../shapes/Circle')
|
||||
, Plane = require('../shapes/Plane')
|
||||
, Shape = require('../shapes/Shape')
|
||||
, Particle = require('../shapes/Particle')
|
||||
, Broadphase = require('../collision/Broadphase')
|
||||
, vec2 = require('../math/vec2')
|
||||
|
||||
module.exports = NaiveBroadphase;
|
||||
|
||||
/**
|
||||
* Naive broadphase implementation. Does N^2 tests.
|
||||
*
|
||||
* @class NaiveBroadphase
|
||||
* @constructor
|
||||
* @extends Broadphase
|
||||
*/
|
||||
function NaiveBroadphase(){
|
||||
Broadphase.apply(this);
|
||||
};
|
||||
NaiveBroadphase.prototype = new Broadphase();
|
||||
|
||||
/**
|
||||
* Get the colliding pairs
|
||||
* @method getCollisionPairs
|
||||
* @param {World} world
|
||||
* @return {Array}
|
||||
*/
|
||||
NaiveBroadphase.prototype.getCollisionPairs = function(world){
|
||||
var bodies = world.bodies,
|
||||
result = this.result,
|
||||
i, j, bi, bj;
|
||||
|
||||
result.length = 0;
|
||||
|
||||
for(i=0, Ncolliding=bodies.length; i!==Ncolliding; i++){
|
||||
bi = bodies[i];
|
||||
|
||||
for(j=0; j<i; j++){
|
||||
bj = bodies[j];
|
||||
|
||||
if(Broadphase.boundingRadiusCheck(bi,bj))
|
||||
result.push(bi,bj);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,376 +0,0 @@
|
||||
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<numAdded; k+=2){
|
||||
var r1 = result[k],
|
||||
r2 = result[k+1];
|
||||
if( (r1==item && r2==b) || (r2==item && r1==b) ){
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!found && Broadphase.boundingRadiusCheck(b,item)){
|
||||
result.push(b,item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//console.log("results",result.length);
|
||||
|
||||
// Clear until next
|
||||
this.clear();
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
function Node(bounds, depth, maxDepth, maxChildren){
|
||||
this.bounds = bounds;
|
||||
this.children = [];
|
||||
this.nodes = [];
|
||||
|
||||
if(maxChildren){
|
||||
this.maxChildren = maxChildren;
|
||||
}
|
||||
|
||||
if(maxDepth){
|
||||
this.maxDepth = maxDepth;
|
||||
}
|
||||
|
||||
if(depth){
|
||||
this.depth = depth;
|
||||
}
|
||||
}
|
||||
|
||||
//subnodes
|
||||
Node.prototype.classConstructor = Node;
|
||||
|
||||
//children contained directly in the node
|
||||
Node.prototype.children = null;
|
||||
|
||||
//read only
|
||||
Node.prototype.depth = 0;
|
||||
|
||||
Node.prototype.maxChildren = 4;
|
||||
Node.prototype.maxDepth = 4;
|
||||
|
||||
Node.TOP_LEFT = 0;
|
||||
Node.TOP_RIGHT = 1;
|
||||
Node.BOTTOM_LEFT = 2;
|
||||
Node.BOTTOM_RIGHT = 3;
|
||||
|
||||
Node.prototype.insert = function(item){
|
||||
if(this.nodes.length){
|
||||
var index = this.findIndex(item);
|
||||
this.nodes[index].insert(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 < 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<len; i++){
|
||||
this.insert(this.children[i]);
|
||||
}
|
||||
|
||||
this.children.length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
BoundsNode.prototype.getChildren = function(){
|
||||
return this.children.concat(this.stuckChildren);
|
||||
}
|
||||
|
||||
BoundsNode.prototype.retrieve = function(item){
|
||||
var out = this.out;
|
||||
out.length = 0;
|
||||
|
||||
if(this.nodes.length){
|
||||
var index = this.findIndex(item);
|
||||
out.push.apply(out, this.nodes[index].retrieve(item));
|
||||
}
|
||||
|
||||
out.push.apply(out, this.stuckChildren);
|
||||
out.push.apply(out, this.children);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
BoundsNode.prototype.clear = function(){
|
||||
|
||||
this.stuckChildren.length = 0;
|
||||
|
||||
//array
|
||||
this.children.length = 0;
|
||||
|
||||
var len = this.nodes.length;
|
||||
|
||||
if(!len){
|
||||
return;
|
||||
}
|
||||
|
||||
for(var i = 0; i < len; i++){
|
||||
this.nodes[i].clear();
|
||||
}
|
||||
|
||||
//array
|
||||
this.nodes.length = 0;
|
||||
|
||||
//we could call the super clear function but for now, im just going to inline it
|
||||
//call the hidden super.clear, and make sure its called with this = this instance
|
||||
//Object.getPrototypeOf(BoundsNode.prototype).clear.call(this);
|
||||
}
|
||||
|
||||
@@ -1,132 +0,0 @@
|
||||
var Circle = require('../shapes/Circle')
|
||||
, Plane = require('../shapes/Plane')
|
||||
, Shape = require('../shapes/Shape')
|
||||
, Particle = require('../shapes/Particle')
|
||||
, Broadphase = require('../collision/Broadphase')
|
||||
, vec2 = require('../math/vec2')
|
||||
|
||||
module.exports = SAP1DBroadphase;
|
||||
|
||||
/**
|
||||
* Sweep and prune broadphase along one axis.
|
||||
*
|
||||
* @class SAP1DBroadphase
|
||||
* @constructor
|
||||
* @extends Broadphase
|
||||
* @param {World} world
|
||||
*/
|
||||
function SAP1DBroadphase(world){
|
||||
Broadphase.apply(this);
|
||||
|
||||
/**
|
||||
* List of bodies currently in the broadphase.
|
||||
* @property axisList
|
||||
* @type {Array}
|
||||
*/
|
||||
this.axisList = world.bodies.slice(0);
|
||||
|
||||
/**
|
||||
* The world to search in.
|
||||
* @property world
|
||||
* @type {World}
|
||||
*/
|
||||
this.world = world;
|
||||
|
||||
/**
|
||||
* Axis to sort the bodies along. Set to 0 for x axis, and 1 for y axis. For best performance, choose an axis that the bodies are spread out more on.
|
||||
* @property axisIndex
|
||||
* @type {Number}
|
||||
*/
|
||||
this.axisIndex = 0;
|
||||
|
||||
// Add listeners to update the list of bodies.
|
||||
var axisList = this.axisList;
|
||||
world.on("addBody",function(e){
|
||||
axisList.push(e.body);
|
||||
}).on("removeBody",function(e){
|
||||
var idx = axisList.indexOf(e.body);
|
||||
if(idx !== -1)
|
||||
axisList.splice(idx,1);
|
||||
});
|
||||
};
|
||||
SAP1DBroadphase.prototype = new Broadphase();
|
||||
|
||||
/**
|
||||
* Function for sorting bodies along the X axis. To be passed to array.sort()
|
||||
* @method sortAxisListX
|
||||
* @param {Body} bodyA
|
||||
* @param {Body} bodyB
|
||||
* @return {Number}
|
||||
*/
|
||||
SAP1DBroadphase.sortAxisListX = function(bodyA,bodyB){
|
||||
return (bodyA.position[0]-bodyA.boundingRadius) - (bodyB.position[0]-bodyB.boundingRadius);
|
||||
};
|
||||
|
||||
/**
|
||||
* Function for sorting bodies along the Y axis. To be passed to array.sort()
|
||||
* @method sortAxisListY
|
||||
* @param {Body} bodyA
|
||||
* @param {Body} bodyB
|
||||
* @return {Number}
|
||||
*/
|
||||
SAP1DBroadphase.sortAxisListY = function(bodyA,bodyB){
|
||||
return (bodyA.position[1]-bodyA.boundingRadius) - (bodyB.position[1]-bodyB.boundingRadius);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the colliding pairs
|
||||
* @method getCollisionPairs
|
||||
* @param {World} world
|
||||
* @return {Array}
|
||||
*/
|
||||
SAP1DBroadphase.prototype.getCollisionPairs = function(world){
|
||||
var bodies = this.axisList,
|
||||
result = this.result,
|
||||
axisIndex = this.axisIndex,
|
||||
i,j;
|
||||
|
||||
result.length = 0;
|
||||
|
||||
// Sort the list
|
||||
bodies.sort(axisIndex === 0 ? SAP1DBroadphase.sortAxisListX : SAP1DBroadphase.sortAxisListY );
|
||||
|
||||
// Look through the list
|
||||
for(i=0, N=bodies.length; i!==N; i++){
|
||||
var bi = bodies[i];
|
||||
|
||||
for(j=i+1; j<N; j++){
|
||||
var bj = bodies[j];
|
||||
|
||||
if(!SAP1DBroadphase.checkBounds(bi,bj,axisIndex))
|
||||
break;
|
||||
|
||||
// If we got overlap, add pair
|
||||
if(Broadphase.boundingRadiusCheck(bi,bj))
|
||||
result.push(bi,bj);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the bounds of two bodies overlap, along the given SAP axis.
|
||||
* @static
|
||||
* @method checkBounds
|
||||
* @param {Body} bi
|
||||
* @param {Body} bj
|
||||
* @param {Number} axisIndex
|
||||
* @return {Boolean}
|
||||
*/
|
||||
SAP1DBroadphase.checkBounds = function(bi,bj,axisIndex){
|
||||
var biPos = bi.position[axisIndex],
|
||||
ri = bi.boundingRadius,
|
||||
bjPos = bj.position[axisIndex],
|
||||
rj = bj.boundingRadius,
|
||||
boundA1 = biPos-ri,
|
||||
boundA2 = biPos+ri,
|
||||
boundB1 = bjPos-rj,
|
||||
boundB2 = bjPos+rj;
|
||||
|
||||
return boundB1 < boundA2;
|
||||
};
|
||||
@@ -1,42 +0,0 @@
|
||||
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!");
|
||||
};*/
|
||||
@@ -1,114 +0,0 @@
|
||||
var Equation = require("./Equation"),
|
||||
vec2 = require('../math/vec2'),
|
||||
mat2 = require('../math/mat2');
|
||||
|
||||
module.exports = ContactEquation;
|
||||
|
||||
/**
|
||||
* Non-penetration constraint equation. Tries to make the ri and rj vectors the same point.
|
||||
*
|
||||
* @class ContactEquation
|
||||
* @constructor
|
||||
* @extends Equation
|
||||
* @param {Body} bi
|
||||
* @param {Body} bj
|
||||
*/
|
||||
function ContactEquation(bi,bj){
|
||||
Equation.call(this,bi,bj,0,Number.MAX_VALUE);
|
||||
|
||||
/**
|
||||
* Vector from body i center of mass to the contact point.
|
||||
* @property ri
|
||||
* @type {Array}
|
||||
*/
|
||||
this.ri = vec2.create();
|
||||
this.penetrationVec = vec2.create();
|
||||
|
||||
/**
|
||||
* Vector from body j center of mass to the contact point.
|
||||
* @property rj
|
||||
* @type {Array}
|
||||
*/
|
||||
this.rj = vec2.create();
|
||||
|
||||
/**
|
||||
* The normal vector, pointing out of body i
|
||||
* @property ni
|
||||
* @type {Array}
|
||||
*/
|
||||
this.ni = vec2.create();
|
||||
|
||||
/**
|
||||
* The restitution to use. 0=no bounciness, 1=max bounciness.
|
||||
* @property restitution
|
||||
* @type {Number}
|
||||
*/
|
||||
this.restitution = 0;
|
||||
|
||||
/**
|
||||
* Set to true if this is the first impact between the bodies (not persistant contact).
|
||||
* @property firstImpact
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this.firstImpact = false;
|
||||
|
||||
/**
|
||||
* The shape in body i that triggered this contact.
|
||||
* @property shapeA
|
||||
* @type {Shape}
|
||||
*/
|
||||
this.shapeA = null;
|
||||
|
||||
/**
|
||||
* The shape in body j that triggered this contact.
|
||||
* @property shapeB
|
||||
* @type {Shape}
|
||||
*/
|
||||
this.shapeB = null;
|
||||
};
|
||||
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 penetrationVec = this.penetrationVec,
|
||||
n = this.ni,
|
||||
G = this.G;
|
||||
|
||||
// Caluclate cross products
|
||||
var rixn = vec2.crossLength(ri,n),
|
||||
rjxn = vec2.crossLength(rj,n);
|
||||
|
||||
// G = [-n -rixn n rjxn]
|
||||
G[0] = -n[0];
|
||||
G[1] = -n[1];
|
||||
G[2] = -rixn;
|
||||
G[3] = n[0];
|
||||
G[4] = n[1];
|
||||
G[5] = rjxn;
|
||||
|
||||
// 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);
|
||||
|
||||
// Compute iteration
|
||||
var GW, Gq;
|
||||
if(this.firstImpact && this.restitution !== 0){
|
||||
Gq = 0;
|
||||
GW = (1/b)*(1+this.restitution) * this.computeGW();
|
||||
} else {
|
||||
GW = this.computeGW();
|
||||
Gq = vec2.dot(n,penetrationVec);
|
||||
}
|
||||
|
||||
var GiMf = this.computeGiMf();
|
||||
var B = - Gq * a - GW * b - h*GiMf;
|
||||
|
||||
return B;
|
||||
};
|
||||
@@ -1,85 +0,0 @@
|
||||
var Constraint = require('./Constraint')
|
||||
, Equation = require('./Equation')
|
||||
, 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);
|
||||
|
||||
/**
|
||||
* The distance to keep.
|
||||
* @property distance
|
||||
* @type {Number}
|
||||
*/
|
||||
this.distance = distance;
|
||||
|
||||
if(typeof(maxForce)==="undefined" )
|
||||
maxForce = Number.MAX_VALUE;
|
||||
|
||||
var normal = new Equation(bodyA,bodyB,-maxForce,maxForce); // Just in the normal direction
|
||||
this.equations = [ normal ];
|
||||
|
||||
var r = vec2.create();
|
||||
normal.computeGq = function(){
|
||||
vec2.sub(r, bodyB.position, bodyA.position);
|
||||
return vec2.length(r)-distance;
|
||||
};
|
||||
|
||||
// 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
|
||||
*/
|
||||
var n = vec2.create();
|
||||
DistanceConstraint.prototype.update = function(){
|
||||
var normal = this.equations[0],
|
||||
bodyA = this.bodyA,
|
||||
bodyB = this.bodyB,
|
||||
distance = this.distance,
|
||||
G = normal.G;
|
||||
|
||||
vec2.sub(n, bodyB.position, bodyA.position);
|
||||
vec2.normalize(n,n);
|
||||
G[0] = -n[0];
|
||||
G[1] = -n[1];
|
||||
G[3] = n[0];
|
||||
G[4] = n[1];
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the max force to be used
|
||||
* @method setMaxForce
|
||||
* @param {Number} f
|
||||
*/
|
||||
DistanceConstraint.prototype.setMaxForce = function(f){
|
||||
var normal = this.equations[0];
|
||||
normal.minForce = -f;
|
||||
normal.maxForce = f;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the max force
|
||||
* @method getMaxForce
|
||||
* @return {Number}
|
||||
*/
|
||||
DistanceConstraint.prototype.getMaxForce = function(f){
|
||||
var normal = this.equations[0];
|
||||
return normal.maxForce;
|
||||
};
|
||||
@@ -1,316 +0,0 @@
|
||||
module.exports = Equation;
|
||||
|
||||
var vec2 = require('../math/vec2'),
|
||||
mat2 = require('../math/mat2'),
|
||||
Utils = require('../utils/Utils');
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* The Jacobian entry of this equation. 6 numbers, 3 per body (x,y,angle).
|
||||
* @property G
|
||||
* @type {Array}
|
||||
*/
|
||||
this.G = new Utils.ARRAY_TYPE(6);
|
||||
|
||||
// Constraint frames for body i and j
|
||||
/*
|
||||
this.xi = vec2.create();
|
||||
this.xj = vec2.create();
|
||||
this.ai = 0;
|
||||
this.aj = 0;
|
||||
*/
|
||||
this.offset = 0;
|
||||
|
||||
this.a = 0;
|
||||
this.b = 0;
|
||||
this.eps = 0;
|
||||
this.h = 0;
|
||||
this.updateSpookParams(1/60);
|
||||
|
||||
/**
|
||||
* The resulting constraint multiplier from the last solve. This is mostly equivalent to the force produced by the constraint.
|
||||
* @property multiplier
|
||||
* @type {Number}
|
||||
*/
|
||||
this.multiplier = 0;
|
||||
};
|
||||
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 <a href="http://www8.cs.umu.se/kurser/5DV058/VT09/lectures/spooknotes.pdf">SPOOK notes</a>.
|
||||
* @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;
|
||||
};
|
||||
|
||||
function Gmult(G,vi,wi,vj,wj){
|
||||
return G[0] * vi[0] +
|
||||
G[1] * vi[1] +
|
||||
G[2] * wi +
|
||||
G[3] * vj[0] +
|
||||
G[4] * vj[1] +
|
||||
G[5] * wj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the RHS of the SPOOK equation
|
||||
* @method computeB
|
||||
* @return {Number}
|
||||
*/
|
||||
Equation.prototype.computeB = function(a,b,h){
|
||||
var GW = this.computeGW();
|
||||
var Gq = this.computeGq();
|
||||
var GiMf = this.computeGiMf();
|
||||
return - Gq * a - GW * b - GiMf*h;
|
||||
};
|
||||
|
||||
/**
|
||||
* Computes G*q, where q are the generalized body coordinates
|
||||
* @method computeGq
|
||||
* @return {Number}
|
||||
*/
|
||||
var qi = vec2.create(),
|
||||
qj = vec2.create();
|
||||
Equation.prototype.computeGq = function(){
|
||||
var G = this.G,
|
||||
bi = this.bi,
|
||||
bj = this.bj,
|
||||
xi = bi.position,
|
||||
xj = bj.position,
|
||||
ai = bi.angle,
|
||||
aj = bj.angle;
|
||||
|
||||
// Transform to the given body frames
|
||||
/*
|
||||
vec2.rotate(qi,this.xi,ai);
|
||||
vec2.rotate(qj,this.xj,aj);
|
||||
vec2.add(qi,qi,xi);
|
||||
vec2.add(qj,qj,xj);
|
||||
*/
|
||||
|
||||
return Gmult(G, qi, ai, qj, aj) + this.offset;
|
||||
};
|
||||
|
||||
var tmp_i = vec2.create(),
|
||||
tmp_j = vec2.create();
|
||||
Equation.prototype.transformedGmult = function(G,vi,wi,vj,wj){
|
||||
// Transform velocity to the given body frames
|
||||
// v_p = v + w x r
|
||||
/*
|
||||
vec2.rotate(tmp_i,this.xi,Math.PI / 2 + this.bi.angle); // Get r, and rotate 90 degrees. We get the "x r" part
|
||||
vec2.rotate(tmp_j,this.xj,Math.PI / 2 + this.bj.angle);
|
||||
vec2.scale(tmp_i,tmp_i,wi); // Temp vectors are now (w x r)
|
||||
vec2.scale(tmp_j,tmp_j,wj);
|
||||
vec2.add(tmp_i,tmp_i,vi);
|
||||
vec2.add(tmp_j,tmp_j,vj);
|
||||
*/
|
||||
|
||||
// Note: angular velocity is same
|
||||
return Gmult(G,vi,wi,vj,wj);
|
||||
};
|
||||
|
||||
/**
|
||||
* Computes G*W, where W are the body velocities
|
||||
* @method computeGW
|
||||
* @return {Number}
|
||||
*/
|
||||
Equation.prototype.computeGW = function(){
|
||||
var G = this.G,
|
||||
bi = this.bi,
|
||||
bj = this.bj,
|
||||
vi = bi.velocity,
|
||||
vj = bj.velocity,
|
||||
wi = bi.angularVelocity,
|
||||
wj = bj.angularVelocity;
|
||||
return this.transformedGmult(G,vi,wi,vj,wj);
|
||||
};
|
||||
|
||||
/**
|
||||
* Computes G*Wlambda, where W are the body velocities
|
||||
* @method computeGWlambda
|
||||
* @return {Number}
|
||||
*/
|
||||
Equation.prototype.computeGWlambda = function(){
|
||||
var G = this.G,
|
||||
bi = this.bi,
|
||||
bj = this.bj,
|
||||
vi = bi.vlambda,
|
||||
vj = bj.vlambda,
|
||||
wi = bi.wlambda,
|
||||
wj = bj.wlambda;
|
||||
return this.transformedGmult(G,vi,wi,vj,wj);
|
||||
};
|
||||
|
||||
/**
|
||||
* Computes G*inv(M)*f, where M is the mass matrix with diagonal blocks for each body, and f are the forces on the bodies.
|
||||
* @method computeGiMf
|
||||
* @return {Number}
|
||||
*/
|
||||
var iMfi = vec2.create(),
|
||||
iMfj = vec2.create();
|
||||
Equation.prototype.computeGiMf = function(){
|
||||
var bi = this.bi,
|
||||
bj = this.bj,
|
||||
fi = bi.force,
|
||||
ti = bi.angularForce,
|
||||
fj = bj.force,
|
||||
tj = bj.angularForce,
|
||||
invMassi = bi.invMass,
|
||||
invMassj = bj.invMass,
|
||||
invIi = bi.invInertia,
|
||||
invIj = bj.invInertia,
|
||||
G = this.G;
|
||||
|
||||
vec2.scale(iMfi, fi,invMassi);
|
||||
vec2.scale(iMfj, fj,invMassj);
|
||||
|
||||
return this.transformedGmult(G,iMfi,ti*invIi,iMfj,tj*invIj);
|
||||
};
|
||||
|
||||
/**
|
||||
* Computes G*inv(M)*G'
|
||||
* @method computeGiMGt
|
||||
* @return {Number}
|
||||
*/
|
||||
Equation.prototype.computeGiMGt = function(){
|
||||
var bi = this.bi,
|
||||
bj = this.bj,
|
||||
invMassi = bi.invMass,
|
||||
invMassj = bj.invMass,
|
||||
invIi = bi.invInertia,
|
||||
invIj = bj.invInertia,
|
||||
G = this.G;
|
||||
|
||||
return G[0] * G[0] * invMassi +
|
||||
G[1] * G[1] * invMassi +
|
||||
G[2] * G[2] * invIi +
|
||||
G[3] * G[3] * invMassj +
|
||||
G[4] * G[4] * invMassj +
|
||||
G[5] * G[5] * invIj;
|
||||
};
|
||||
|
||||
var addToWlambda_temp = vec2.create(),
|
||||
addToWlambda_Gi = vec2.create(),
|
||||
addToWlambda_Gj = vec2.create(),
|
||||
addToWlambda_ri = vec2.create(),
|
||||
addToWlambda_rj = vec2.create();
|
||||
var tmpMat1 = mat2.create(),
|
||||
tmpMat2 = mat2.create();
|
||||
|
||||
/**
|
||||
* Add constraint velocity to the bodies.
|
||||
* @method addToWlambda
|
||||
* @param {Number} deltalambda
|
||||
*/
|
||||
Equation.prototype.addToWlambda = function(deltalambda){
|
||||
var bi = this.bi,
|
||||
bj = this.bj,
|
||||
temp = addToWlambda_temp,
|
||||
imMat1 = tmpMat1,
|
||||
imMat2 = tmpMat2,
|
||||
Gi = addToWlambda_Gi,
|
||||
Gj = addToWlambda_Gj,
|
||||
ri = addToWlambda_ri,
|
||||
rj = addToWlambda_rj,
|
||||
G = this.G;
|
||||
|
||||
Gi[0] = G[0];
|
||||
Gi[1] = G[1];
|
||||
Gj[0] = G[3];
|
||||
Gj[1] = G[4];
|
||||
|
||||
mat2.identity(imMat1);
|
||||
mat2.identity(imMat2);
|
||||
imMat1[0] = imMat1[3] = bi.invMass;
|
||||
imMat2[0] = imMat2[3] = bj.invMass;
|
||||
|
||||
/*
|
||||
vec2.rotate(ri,this.xi,bi.angle);
|
||||
vec2.rotate(rj,this.xj,bj.angle);
|
||||
*/
|
||||
|
||||
// Add to linear velocity
|
||||
vec2.scale(temp,vec2.transformMat2(temp,Gi,imMat1),deltalambda);
|
||||
vec2.add( bi.vlambda, bi.vlambda, temp);
|
||||
// This impulse is in the offset frame
|
||||
// Also add contribution to angular
|
||||
//bi.wlambda -= vec2.crossLength(temp,ri);
|
||||
|
||||
vec2.scale(temp,vec2.transformMat2(temp,Gj,imMat2),deltalambda);
|
||||
vec2.add( bj.vlambda, bj.vlambda, temp);
|
||||
//bj.wlambda -= vec2.crossLength(temp,rj);
|
||||
|
||||
// Add to angular velocity
|
||||
bi.wlambda += bi.invInertia * G[2] * deltalambda;
|
||||
bj.wlambda += bj.invInertia * G[5] * deltalambda;
|
||||
};
|
||||
|
||||
/**
|
||||
* Compute the denominator part of the SPOOK equation: C = G*inv(M)*G' + eps
|
||||
* @method computeC
|
||||
* @param {Number} eps
|
||||
* @return {Number}
|
||||
*/
|
||||
Equation.prototype.computeC = function(eps){
|
||||
return this.computeGiMGt() + eps;
|
||||
};
|
||||
@@ -1,108 +0,0 @@
|
||||
var mat2 = require('../math/mat2')
|
||||
, vec2 = require('../math/vec2')
|
||||
, Equation = require('./Equation')
|
||||
, Utils = require('../utils/Utils')
|
||||
|
||||
module.exports = FrictionEquation;
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
/**
|
||||
* A ContactEquation connected to this friction. The contact equation can be used to rescale the max force for the friction.
|
||||
* @property contactEquation
|
||||
* @type {ContactEquation}
|
||||
*/
|
||||
this.contactEquation = null;
|
||||
|
||||
/**
|
||||
* The shape in body i that triggered this friction.
|
||||
* @property shapeA
|
||||
* @type {Shape}
|
||||
*/
|
||||
this.shapeA = null;
|
||||
|
||||
/**
|
||||
* The shape in body j that triggered this friction.
|
||||
* @property shapeB
|
||||
* @type {Shape}
|
||||
*/
|
||||
this.shapeB = null;
|
||||
|
||||
/**
|
||||
* The friction coefficient to use.
|
||||
* @property frictionCoefficient
|
||||
* @type {Number}
|
||||
*/
|
||||
this.frictionCoefficient = 0.3;
|
||||
};
|
||||
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
|
||||
* @deprecated Use .frictionCoefficient instead
|
||||
*/
|
||||
FrictionEquation.prototype.setSlipForce = function(slipForce){
|
||||
this.maxForce = slipForce;
|
||||
this.minForce = -slipForce;
|
||||
};
|
||||
|
||||
FrictionEquation.prototype.computeB = function(a,b,h){
|
||||
var bi = this.bi,
|
||||
bj = this.bj,
|
||||
ri = this.ri,
|
||||
rj = this.rj,
|
||||
t = this.t,
|
||||
G = this.G;
|
||||
|
||||
// G = [-t -rixt t rjxt]
|
||||
// And remember, this is a pure velocity constraint, g is always zero!
|
||||
G[0] = -t[0];
|
||||
G[1] = -t[1];
|
||||
G[2] = -vec2.crossLength(ri,t);
|
||||
G[3] = t[0];
|
||||
G[4] = t[1];
|
||||
G[5] = vec2.crossLength(rj,t);
|
||||
|
||||
var GW = this.computeGW();
|
||||
var GiMf = this.computeGiMf();
|
||||
|
||||
var B = /* - g * a */ - GW * b - h*GiMf;
|
||||
|
||||
return B;
|
||||
};
|
||||
@@ -1,123 +0,0 @@
|
||||
var Constraint = require('./Constraint')
|
||||
, vec2 = require('../math/vec2')
|
||||
, Equation = require('./Equation')
|
||||
|
||||
module.exports = LockConstraint;
|
||||
|
||||
/**
|
||||
* Locks the relative position between two bodies.
|
||||
*
|
||||
* @class LockConstraint
|
||||
* @constructor
|
||||
* @author schteppe
|
||||
* @param {Body} bodyA
|
||||
* @param {Body} bodyB
|
||||
* @param {Object} [options]
|
||||
* @param {Array} [options.localOffsetB] The offset of bodyB in bodyA's frame.
|
||||
* @param {number} [options.localAngleB] The angle of bodyB in bodyA's frame.
|
||||
* @param {number} [options.maxForce]
|
||||
* @extends {Constraint}
|
||||
*/
|
||||
function LockConstraint(bodyA,bodyB,options){
|
||||
Constraint.call(this,bodyA,bodyB);
|
||||
var maxForce = ( typeof(options.maxForce)=="undefined" ? Number.MAX_VALUE : options.maxForce );
|
||||
var localOffsetB = options.localOffsetB || vec2.fromValues(0,0);
|
||||
localOffsetB = vec2.fromValues(localOffsetB[0],localOffsetB[1]);
|
||||
|
||||
var localAngleB = options.localAngleB || 0;
|
||||
|
||||
// Use 3 equations:
|
||||
// gx = (xj - xi - l) * xhat = 0
|
||||
// gy = (xj - xi - l) * yhat = 0
|
||||
// gr = (xi - xj + r) * that = 0
|
||||
//
|
||||
// ...where:
|
||||
// l is the localOffsetB vector rotated to world in bodyA frame
|
||||
// r is the same vector but reversed and rotated from bodyB frame
|
||||
// xhat, yhat are world axis vectors
|
||||
// that is the tangent of r
|
||||
//
|
||||
// For the first two constraints, we get
|
||||
// G*W = (vj - vi - ldot ) * xhat
|
||||
// = (vj - vi - wi x l) * xhat
|
||||
//
|
||||
// Since (wi x l) * xhat = (l x xhat) * wi, we get
|
||||
// G*W = [ -1 0 (-l x xhat) 1 0 0] * [vi wi vj wj]
|
||||
//
|
||||
// The last constraint gives
|
||||
// GW = (vi - vj + wj x r) * that
|
||||
// = [ that 0 -that (r x t) ]
|
||||
|
||||
var x = new Equation(bodyA,bodyB,-maxForce,maxForce),
|
||||
y = new Equation(bodyA,bodyB,-maxForce,maxForce),
|
||||
rot = new Equation(bodyA,bodyB,-maxForce,maxForce);
|
||||
|
||||
var l = vec2.create(),
|
||||
g = vec2.create();
|
||||
x.computeGq = function(){
|
||||
vec2.rotate(l,localOffsetB,bodyA.angle);
|
||||
vec2.sub(g,bodyB.position,bodyA.position);
|
||||
vec2.sub(g,g,l);
|
||||
return g[0];
|
||||
}
|
||||
y.computeGq = function(){
|
||||
vec2.rotate(l,localOffsetB,bodyA.angle);
|
||||
vec2.sub(g,bodyB.position,bodyA.position);
|
||||
vec2.sub(g,g,l);
|
||||
return g[1];
|
||||
};
|
||||
var r = vec2.create(),
|
||||
t = vec2.create();
|
||||
rot.computeGq = function(){
|
||||
vec2.rotate(r,localOffsetB,bodyB.angle - localAngleB);
|
||||
vec2.scale(r,r,-1);
|
||||
vec2.sub(g,bodyA.position,bodyB.position);
|
||||
vec2.add(g,g,r);
|
||||
vec2.rotate(t,r,-Math.PI/2);
|
||||
vec2.normalize(t,t);
|
||||
return vec2.dot(g,t);
|
||||
};
|
||||
|
||||
this.localOffsetB = localOffsetB;
|
||||
this.localAngleB = localAngleB;
|
||||
this.maxForce = maxForce;
|
||||
|
||||
var eqs = this.equations = [ x, y, rot ];
|
||||
}
|
||||
LockConstraint.prototype = new Constraint();
|
||||
|
||||
var l = vec2.create();
|
||||
var r = vec2.create();
|
||||
var t = vec2.create();
|
||||
var xAxis = vec2.fromValues(1,0);
|
||||
var yAxis = vec2.fromValues(0,1);
|
||||
LockConstraint.prototype.update = function(){
|
||||
var x = this.equations[0],
|
||||
y = this.equations[1],
|
||||
rot = this.equations[2],
|
||||
bodyA = this.bodyA,
|
||||
bodyB = this.bodyB;
|
||||
|
||||
vec2.rotate(l,this.localOffsetB,bodyA.angle);
|
||||
vec2.rotate(r,this.localOffsetB,bodyB.angle - this.localAngleB);
|
||||
vec2.scale(r,r,-1);
|
||||
|
||||
vec2.rotate(t,r,Math.PI/2);
|
||||
vec2.normalize(t,t);
|
||||
|
||||
x.G[0] = -1;
|
||||
x.G[1] = 0;
|
||||
x.G[2] = -vec2.crossLength(l,xAxis);
|
||||
x.G[3] = 1;
|
||||
|
||||
y.G[0] = 0;
|
||||
y.G[1] = -1;
|
||||
y.G[2] = -vec2.crossLength(l,yAxis);
|
||||
y.G[4] = 1;
|
||||
|
||||
rot.G[0] = -t[0];
|
||||
rot.G[1] = -t[1];
|
||||
rot.G[3] = t[0];
|
||||
rot.G[4] = t[1];
|
||||
rot.G[5] = vec2.crossLength(r,t);
|
||||
};
|
||||
@@ -1,117 +0,0 @@
|
||||
var Constraint = require('./Constraint')
|
||||
, ContactEquation = require('./ContactEquation')
|
||||
, Equation = require('./Equation')
|
||||
, vec2 = require('../math/vec2')
|
||||
, RotationalLockEquation = require('./RotationalLockEquation')
|
||||
|
||||
module.exports = PrismaticConstraint;
|
||||
|
||||
/**
|
||||
* Constraint that only allows bodies to move along a line, relative to each other. See <a href="http://www.iforce2d.net/b2dtut/joints-prismatic">this tutorial</a>.
|
||||
*
|
||||
* @class PrismaticConstraint
|
||||
* @constructor
|
||||
* @extends {Constraint}
|
||||
* @author schteppe
|
||||
* @param {Body} bodyA
|
||||
* @param {Body} bodyB
|
||||
* @param {Object} options
|
||||
* @param {Number} options.maxForce Max force to be applied by the constraint
|
||||
* @param {Array} options.localAnchorA Body A's anchor point, defined in its own local frame.
|
||||
* @param {Array} options.localAnchorB Body B's anchor point, defined in its own local frame.
|
||||
* @param {Array} options.localAxisA An axis, defined in body A frame, that body B's anchor point may slide along.
|
||||
*/
|
||||
function PrismaticConstraint(bodyA,bodyB,options){
|
||||
options = options || {};
|
||||
Constraint.call(this,bodyA,bodyB);
|
||||
|
||||
// Get anchors
|
||||
var localAnchorA = vec2.fromValues(0,0),
|
||||
localAxisA = vec2.fromValues(1,0),
|
||||
localAnchorB = vec2.fromValues(0,0);
|
||||
if(options.localAnchorA) vec2.copy(localAnchorA, options.localAnchorA);
|
||||
if(options.localAxisA) vec2.copy(localAxisA, options.localAxisA);
|
||||
if(options.localAnchorB) vec2.copy(localAnchorB, options.localAnchorB);
|
||||
|
||||
/**
|
||||
* @property localAnchorA
|
||||
* @type {Array}
|
||||
*/
|
||||
this.localAnchorA = localAnchorA;
|
||||
|
||||
/**
|
||||
* @property localAnchorB
|
||||
* @type {Array}
|
||||
*/
|
||||
this.localAnchorB = localAnchorB;
|
||||
|
||||
/**
|
||||
* @property localAxisA
|
||||
* @type {Array}
|
||||
*/
|
||||
this.localAxisA = localAxisA;
|
||||
|
||||
/*
|
||||
|
||||
The constraint violation for the common axis point is
|
||||
|
||||
g = ( xj + rj - xi - ri ) * t := gg*t
|
||||
|
||||
where r are body-local anchor points, and t is a tangent to the constraint axis defined in body i frame.
|
||||
|
||||
gdot = ( vj + wj x rj - vi - wi x ri ) * t + ( xj + rj - xi - ri ) * ( wi x t )
|
||||
|
||||
Note the use of the chain rule. Now we identify the jacobian
|
||||
|
||||
G*W = [ -t -ri x t + t x gg t rj x t ] * [vi wi vj wj]
|
||||
|
||||
The rotational part is just a rotation lock.
|
||||
|
||||
*/
|
||||
|
||||
var maxForce = this.maxForce = typeof(options.maxForce)==="undefined" ? options.maxForce : Number.MAX_VALUE;
|
||||
|
||||
// Translational part
|
||||
var trans = new Equation(bodyA,bodyB,-maxForce,maxForce);
|
||||
var ri = new vec2.create(),
|
||||
rj = new vec2.create(),
|
||||
gg = new vec2.create(),
|
||||
t = new vec2.create();
|
||||
trans.computeGq = function(){
|
||||
// g = ( xj + rj - xi - ri ) * t
|
||||
return vec2.dot(gg,t);
|
||||
};
|
||||
trans.update = function(){
|
||||
var G = this.G,
|
||||
xi = bodyA.position,
|
||||
xj = bodyB.position;
|
||||
vec2.rotate(ri,localAnchorA,bodyA.angle);
|
||||
vec2.rotate(rj,localAnchorB,bodyB.angle);
|
||||
vec2.add(gg,xj,rj);
|
||||
vec2.sub(gg,gg,xi);
|
||||
vec2.sub(gg,gg,ri);
|
||||
vec2.rotate(t,localAxisA,bodyA.angle+Math.PI/2);
|
||||
|
||||
G[0] = -t[0];
|
||||
G[1] = -t[1];
|
||||
G[2] = -vec2.crossLength(ri,t) + vec2.crossLength(t,gg);
|
||||
G[3] = t[0];
|
||||
G[4] = t[1];
|
||||
G[5] = vec2.crossLength(rj,t);
|
||||
}
|
||||
var rot = new RotationalLockEquation(bodyA,bodyB,-maxForce,maxForce);
|
||||
|
||||
this.equations.push(trans,rot);
|
||||
}
|
||||
|
||||
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 eqs = this.equations,
|
||||
trans = eqs[0];
|
||||
trans.update();
|
||||
};
|
||||
@@ -1,208 +0,0 @@
|
||||
var Constraint = require('./Constraint')
|
||||
, Equation = require('./Equation')
|
||||
, RotationalVelocityEquation = require('./RotationalVelocityEquation')
|
||||
, RotationalLockEquation = require('./RotationalLockEquation')
|
||||
, vec2 = require('../math/vec2')
|
||||
|
||||
module.exports = RevoluteConstraint;
|
||||
|
||||
var worldPivotA = vec2.create(),
|
||||
worldPivotB = vec2.create(),
|
||||
xAxis = vec2.fromValues(1,0),
|
||||
yAxis = vec2.fromValues(0,1),
|
||||
g = vec2.create();
|
||||
|
||||
/**
|
||||
* Connects two bodies at given offset points, letting them rotate relative to each other around this point.
|
||||
* @class RevoluteConstraint
|
||||
* @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 RevoluteConstraint(bodyA, pivotA, bodyB, pivotB, maxForce){
|
||||
Constraint.call(this,bodyA,bodyB);
|
||||
|
||||
maxForce = typeof(maxForce)!="undefined" ? maxForce : Number.MAX_VALUE;
|
||||
|
||||
this.pivotA = pivotA;
|
||||
this.pivotB = pivotB;
|
||||
|
||||
// Equations to be fed to the solver
|
||||
var eqs = this.equations = [
|
||||
new Equation(bodyA,bodyB,-maxForce,maxForce),
|
||||
new Equation(bodyA,bodyB,-maxForce,maxForce),
|
||||
];
|
||||
|
||||
var x = eqs[0];
|
||||
var y = eqs[1];
|
||||
|
||||
x.computeGq = function(){
|
||||
vec2.rotate(worldPivotA, pivotA, bodyA.angle);
|
||||
vec2.rotate(worldPivotB, pivotB, bodyB.angle);
|
||||
vec2.add(g, bodyB.position, worldPivotB);
|
||||
vec2.sub(g, g, bodyA.position);
|
||||
vec2.sub(g, g, worldPivotA);
|
||||
return vec2.dot(g,xAxis);
|
||||
};
|
||||
|
||||
y.computeGq = function(){
|
||||
vec2.rotate(worldPivotA, pivotA, bodyA.angle);
|
||||
vec2.rotate(worldPivotB, pivotB, bodyB.angle);
|
||||
vec2.add(g, bodyB.position, worldPivotB);
|
||||
vec2.sub(g, g, bodyA.position);
|
||||
vec2.sub(g, g, worldPivotA);
|
||||
return vec2.dot(g,yAxis);
|
||||
};
|
||||
|
||||
y.minForce = x.minForce = -maxForce;
|
||||
y.maxForce = x.maxForce = maxForce;
|
||||
|
||||
this.motorEquation = new RotationalVelocityEquation(bodyA,bodyB);
|
||||
this.motorEnabled = false;
|
||||
|
||||
// Angle limits
|
||||
this.lowerLimitEnabled = false;
|
||||
this.upperLimitEnabled = false;
|
||||
this.lowerLimit = 0;
|
||||
this.upperLimit = 0;
|
||||
this.upperLimitEquation = new RotationalLockEquation(bodyA,bodyB);
|
||||
this.lowerLimitEquation = new RotationalLockEquation(bodyA,bodyB);
|
||||
this.upperLimitEquation.minForce = 0;
|
||||
this.lowerLimitEquation.maxForce = 0;
|
||||
}
|
||||
RevoluteConstraint.prototype = new Constraint();
|
||||
|
||||
RevoluteConstraint.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],
|
||||
x = eqs[0],
|
||||
y = eqs[1],
|
||||
upperLimit = this.upperLimit,
|
||||
lowerLimit = this.lowerLimit,
|
||||
upperLimitEquation = this.upperLimitEquation,
|
||||
lowerLimitEquation = this.lowerLimitEquation;
|
||||
|
||||
var relAngle = this.angle = bodyB.angle - bodyA.angle;
|
||||
|
||||
if(this.upperLimitEnabled && relAngle > upperLimit){
|
||||
upperLimitEquation.angle = upperLimit;
|
||||
if(eqs.indexOf(upperLimitEquation)==-1)
|
||||
eqs.push(upperLimitEquation);
|
||||
} else {
|
||||
var idx = eqs.indexOf(upperLimitEquation);
|
||||
if(idx != -1) eqs.splice(idx,1);
|
||||
}
|
||||
|
||||
if(this.lowerLimitEnabled && relAngle < lowerLimit){
|
||||
lowerLimitEquation.angle = lowerLimit;
|
||||
if(eqs.indexOf(lowerLimitEquation)==-1)
|
||||
eqs.push(lowerLimitEquation);
|
||||
} else {
|
||||
var idx = eqs.indexOf(lowerLimitEquation);
|
||||
if(idx != -1) eqs.splice(idx,1);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
The constraint violation is
|
||||
|
||||
g = xj + rj - xi - ri
|
||||
|
||||
...where xi and xj are the body positions and ri and rj world-oriented offset vectors. Differentiate:
|
||||
|
||||
gdot = vj + wj x rj - vi - wi x ri
|
||||
|
||||
We split this into x and y directions. (let x and y be unit vectors along the respective axes)
|
||||
|
||||
gdot * x = ( vj + wj x rj - vi - wi x ri ) * x
|
||||
= ( vj*x + (wj x rj)*x -vi*x -(wi x ri)*x
|
||||
= ( vj*x + (rj x x)*wj -vi*x -(ri x x)*wi
|
||||
= [ -x -(ri x x) x (rj x x)] * [vi wi vj wj]
|
||||
= G*W
|
||||
|
||||
...and similar for y. We have then identified the jacobian entries for x and y directions:
|
||||
|
||||
Gx = [ x (rj x x) -x -(ri x x)]
|
||||
Gy = [ y (rj x y) -y -(ri x y)]
|
||||
|
||||
*/
|
||||
|
||||
vec2.rotate(worldPivotA, pivotA, bodyA.angle);
|
||||
vec2.rotate(worldPivotB, pivotB, bodyB.angle);
|
||||
|
||||
x.G[0] = -1;
|
||||
x.G[1] = 0;
|
||||
x.G[2] = -vec2.crossLength(worldPivotA,xAxis);
|
||||
x.G[3] = 1;
|
||||
x.G[4] = 0;
|
||||
x.G[5] = vec2.crossLength(worldPivotB,xAxis);
|
||||
|
||||
y.G[0] = 0;
|
||||
y.G[1] = -1;
|
||||
y.G[2] = -vec2.crossLength(worldPivotA,yAxis);
|
||||
y.G[3] = 0;
|
||||
y.G[4] = 1;
|
||||
y.G[5] = vec2.crossLength(worldPivotB,yAxis);
|
||||
};
|
||||
|
||||
/**
|
||||
* Enable the rotational motor
|
||||
* @method enableMotor
|
||||
*/
|
||||
RevoluteConstraint.prototype.enableMotor = function(){
|
||||
if(this.motorEnabled) return;
|
||||
this.equations.push(this.motorEquation);
|
||||
this.motorEnabled = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Disable the rotational motor
|
||||
* @method disableMotor
|
||||
*/
|
||||
RevoluteConstraint.prototype.disableMotor = function(){
|
||||
if(!this.motorEnabled) return;
|
||||
var i = this.equations.indexOf(this.motorEquation);
|
||||
this.equations.splice(i,1);
|
||||
this.motorEnabled = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the motor is enabled.
|
||||
* @method motorIsEnabled
|
||||
* @return {Boolean}
|
||||
*/
|
||||
RevoluteConstraint.prototype.motorIsEnabled = function(){
|
||||
return !!this.motorEnabled;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the speed of the rotational constraint motor
|
||||
* @method setMotorSpeed
|
||||
* @param {Number} speed
|
||||
*/
|
||||
RevoluteConstraint.prototype.setMotorSpeed = function(speed){
|
||||
if(!this.motorEnabled) return;
|
||||
var i = this.equations.indexOf(this.motorEquation);
|
||||
this.equations[i].relativeVelocity = speed;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the speed of the rotational constraint motor
|
||||
* @method getMotorSpeed
|
||||
* @return {Number} The current speed, or false if the motor is not enabled.
|
||||
*/
|
||||
RevoluteConstraint.prototype.getMotorSpeed = function(){
|
||||
if(!this.motorEnabled) return false;
|
||||
return this.motorEquation.relativeVelocity;
|
||||
};
|
||||
@@ -1,37 +0,0 @@
|
||||
var Equation = require("./Equation"),
|
||||
vec2 = require('../math/vec2');
|
||||
|
||||
module.exports = RotationalLockEquation;
|
||||
|
||||
/**
|
||||
* Locks the relative angle between two bodies. The constraint tries to keep the dot product between two vectors, local in each body, to zero. The local angle in body i is a parameter.
|
||||
*
|
||||
* @class RotationalLockEquation
|
||||
* @constructor
|
||||
* @extends Equation
|
||||
* @param {Body} bi
|
||||
* @param {Body} bj
|
||||
* @param {Object} options
|
||||
* @param {Number} options.angle Angle to add to the local vector in body i.
|
||||
*/
|
||||
function RotationalLockEquation(bi,bj,options){
|
||||
options = options || {};
|
||||
Equation.call(this,bi,bj,-Number.MAX_VALUE,Number.MAX_VALUE);
|
||||
this.angle = options.angle || 0;
|
||||
|
||||
var G = this.G;
|
||||
G[2] = 1;
|
||||
G[5] = -1;
|
||||
};
|
||||
RotationalLockEquation.prototype = new Equation();
|
||||
RotationalLockEquation.prototype.constructor = RotationalLockEquation;
|
||||
|
||||
var worldVectorA = vec2.create(),
|
||||
worldVectorB = vec2.create(),
|
||||
xAxis = vec2.fromValues(1,0),
|
||||
yAxis = vec2.fromValues(0,1);
|
||||
RotationalLockEquation.prototype.computeGq = function(){
|
||||
vec2.rotate(worldVectorA,xAxis,this.bi.angle+this.angle);
|
||||
vec2.rotate(worldVectorB,yAxis,this.bj.angle);
|
||||
return vec2.dot(worldVectorA,worldVectorB);
|
||||
};
|
||||
@@ -1,32 +0,0 @@
|
||||
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,-Number.MAX_VALUE,Number.MAX_VALUE);
|
||||
this.relativeVelocity = 1;
|
||||
this.ratio = 1;
|
||||
};
|
||||
RotationalVelocityEquation.prototype = new Equation();
|
||||
RotationalVelocityEquation.prototype.constructor = RotationalVelocityEquation;
|
||||
RotationalVelocityEquation.prototype.computeB = function(a,b,h){
|
||||
var G = this.G;
|
||||
G[2] = -1;
|
||||
G[5] = this.ratio;
|
||||
|
||||
var GiMf = this.computeGiMf();
|
||||
var GW = this.computeGW() + this.relativeVelocity;
|
||||
var B = - GW * b - h*GiMf;
|
||||
|
||||
return B;
|
||||
};
|
||||
@@ -1,84 +0,0 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
};
|
||||
@@ -1,81 +0,0 @@
|
||||
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.0;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
};
|
||||
@@ -1,19 +0,0 @@
|
||||
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++;
|
||||
};
|
||||
@@ -1,10 +0,0 @@
|
||||
/**
|
||||
* 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;
|
||||
@@ -1,477 +0,0 @@
|
||||
|
||||
/*
|
||||
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<n; i++)
|
||||
{
|
||||
a1.x = p[2*i ];
|
||||
a1.y = p[2*i+1];
|
||||
if(i==n-1) { a2.x = p[0 ]; a2.y = p[1 ]; }
|
||||
else { a2.x = p[2*i+2]; a2.y = p[2*i+3]; }
|
||||
|
||||
for(var j=0; j<n; j++)
|
||||
{
|
||||
if(Math.abs(i-j) < 2) continue;
|
||||
if(j==n-1 && i==0) continue;
|
||||
if(i==n-1 && j==0) continue;
|
||||
|
||||
b1.x = p[2*j ];
|
||||
b1.y = p[2*j+1];
|
||||
if(j==n-1) { b2.x = p[0 ]; b2.y = p[1 ]; }
|
||||
else { b2.x = p[2*j+2]; b2.y = p[2*j+3]; }
|
||||
|
||||
if(PolyK._GetLineIntersection(a1,a2,b1,b2,c) != null) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
PolyK.IsConvex = function(p)
|
||||
{
|
||||
if(p.length<6) return true;
|
||||
var l = p.length - 4;
|
||||
for(var i=0; i<l; i+=2)
|
||||
if(!PolyK._convex(p[i], p[i+1], p[i+2], p[i+3], p[i+4], p[i+5])) return false;
|
||||
if(!PolyK._convex(p[l ], p[l+1], p[l+2], p[l+3], p[0], p[1])) return false;
|
||||
if(!PolyK._convex(p[l+2], p[l+3], p[0 ], p[1 ], p[2], p[3])) return false;
|
||||
return true;
|
||||
}
|
||||
*/
|
||||
PolyK.GetArea = function(p)
|
||||
{
|
||||
if(p.length <6) return 0;
|
||||
var l = p.length - 2;
|
||||
var sum = 0;
|
||||
for(var i=0; i<l; i+=2)
|
||||
sum += (p[i+2]-p[i]) * (p[i+1]+p[i+3]);
|
||||
sum += (p[0]-p[l]) * (p[l+1]+p[1]);
|
||||
return - sum * 0.5;
|
||||
}
|
||||
/*
|
||||
PolyK.GetAABB = function(p)
|
||||
{
|
||||
var minx = Infinity;
|
||||
var miny = Infinity;
|
||||
var maxx = -minx;
|
||||
var maxy = -miny;
|
||||
for(var i=0; i<p.length; i+=2)
|
||||
{
|
||||
minx = Math.min(minx, p[i ]);
|
||||
maxx = Math.max(maxx, p[i ]);
|
||||
miny = Math.min(miny, p[i+1]);
|
||||
maxy = Math.max(maxy, p[i+1]);
|
||||
}
|
||||
return {x:minx, y:miny, width:maxx-minx, height:maxy-miny};
|
||||
}
|
||||
*/
|
||||
|
||||
PolyK.Triangulate = function(p)
|
||||
{
|
||||
var n = p.length>>1;
|
||||
if(n<3) return [];
|
||||
var tgs = [];
|
||||
var avl = [];
|
||||
for(var i=0; i<n; i++) avl.push(i);
|
||||
|
||||
var i = 0;
|
||||
var al = n;
|
||||
while(al > 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<al; j++)
|
||||
{
|
||||
var vi = avl[j];
|
||||
if(vi==i0 || vi==i1 || vi==i2) continue;
|
||||
if(PolyK._PointInTriangle(p[2*vi], p[2*vi+1], ax, ay, bx, by, cx, cy)) {earFound = false; break;}
|
||||
}
|
||||
}
|
||||
if(earFound)
|
||||
{
|
||||
tgs.push(i0, i1, i2);
|
||||
avl.splice((i+1)%al, 1);
|
||||
al--;
|
||||
i= 0;
|
||||
}
|
||||
else if(i++ > 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<n; i++)
|
||||
{
|
||||
ax = bx; ay = by;
|
||||
bx = p[2*i ] - px;
|
||||
by = p[2*i+1] - py;
|
||||
if(ay< 0 && by< 0) continue; // both "up" or both "donw"
|
||||
if(ay>=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<p.length; i+=2) ps.push(new PolyK._P(p[i], p[i+1]));
|
||||
|
||||
for(var i=0; i<ps.length; i++)
|
||||
{
|
||||
var isc = new PolyK._P(0,0);
|
||||
isc = PolyK._GetLineIntersection(a, b, ps[i], ps[(i+1)%ps.length], isc);
|
||||
|
||||
if(isc)
|
||||
{
|
||||
isc.flag = true;
|
||||
iscs.push(isc);
|
||||
ps.splice(i+1,0,isc);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
if(iscs.length == 0) return [p.slice(0)];
|
||||
var comp = function(u,v) {return PolyK._P.dist(a,u) - PolyK._P.dist(a,v); }
|
||||
iscs.sort(comp);
|
||||
|
||||
var pgs = [];
|
||||
var dir = 0;
|
||||
while(iscs.length > 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<pgs.length; i++)
|
||||
{
|
||||
var pg = pgs[i];
|
||||
var npg = [];
|
||||
for(var j=0; j<pg.length; j++) npg.push(pg[j].x, pg[j].y);
|
||||
result.push(npg);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
PolyK.Raycast = function(p, x, y, dx, dy, isc)
|
||||
{
|
||||
var l = p.length - 2;
|
||||
var tp = PolyK._tp;
|
||||
var a1 = tp[0], a2 = tp[1],
|
||||
b1 = tp[2], b2 = tp[3], c = tp[4];
|
||||
a1.x = x; a1.y = y;
|
||||
a2.x = x+dx; a2.y = y+dy;
|
||||
|
||||
if(isc==null) isc = {dist:0, edge:0, norm:{x:0, y:0}, refl:{x:0, y:0}};
|
||||
isc.dist = Infinity;
|
||||
|
||||
for(var i=0; i<l; i+=2)
|
||||
{
|
||||
b1.x = p[i ]; b1.y = p[i+1];
|
||||
b2.x = p[i+2]; b2.y = p[i+3];
|
||||
var nisc = PolyK._RayLineIntersection(a1, a2, b1, b2, c);
|
||||
if(nisc) PolyK._updateISC(dx, dy, a1, b1, b2, c, i/2, isc);
|
||||
}
|
||||
b1.x = b2.x; b1.y = b2.y;
|
||||
b2.x = p[0]; b2.y = p[1];
|
||||
var nisc = PolyK._RayLineIntersection(a1, a2, b1, b2, c);
|
||||
if(nisc) PolyK._updateISC(dx, dy, a1, b1, b2, c, p.length/2, isc);
|
||||
|
||||
return (isc.dist != Infinity) ? isc : null;
|
||||
}
|
||||
|
||||
PolyK.ClosestEdge = function(p, x, y, isc)
|
||||
{
|
||||
var l = p.length - 2;
|
||||
var tp = PolyK._tp;
|
||||
var a1 = tp[0],
|
||||
b1 = tp[2], b2 = tp[3], c = tp[4];
|
||||
a1.x = x; a1.y = y;
|
||||
|
||||
if(isc==null) isc = {dist:0, edge:0, point:{x:0, y:0}, norm:{x:0, y:0}};
|
||||
isc.dist = Infinity;
|
||||
|
||||
for(var i=0; i<l; i+=2)
|
||||
{
|
||||
b1.x = p[i ]; b1.y = p[i+1];
|
||||
b2.x = p[i+2]; b2.y = p[i+3];
|
||||
PolyK._pointLineDist(a1, b1, b2, 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<isc.dist)
|
||||
{
|
||||
isc.dist = dst;
|
||||
isc.edge = edge;
|
||||
isc.point.x = xx;
|
||||
isc.point.y = yy;
|
||||
}
|
||||
}
|
||||
|
||||
PolyK._updateISC = function(dx, dy, a1, b1, b2, c, edge, isc)
|
||||
{
|
||||
var nrl = PolyK._P.dist(a1, c);
|
||||
if(nrl<isc.dist)
|
||||
{
|
||||
var ibl = 1/PolyK._P.dist(b1, b2);
|
||||
var nx = -(b2.y-b1.y)*ibl;
|
||||
var ny = (b2.x-b1.x)*ibl;
|
||||
var ddot = 2*(dx*nx+dy*ny);
|
||||
isc.dist = nrl;
|
||||
isc.norm.x = nx;
|
||||
isc.norm.y = ny;
|
||||
isc.refl.x = -ddot*nx+dx;
|
||||
isc.refl.y = -ddot*ny+dy;
|
||||
isc.edge = edge;
|
||||
}
|
||||
}
|
||||
|
||||
PolyK._getPoints = function(ps, ind0, ind1)
|
||||
{
|
||||
var n = ps.length;
|
||||
var nps = [];
|
||||
if(ind1<ind0) ind1 += n;
|
||||
for(var i=ind0; i<= ind1; i++) nps.push(ps[i%n]);
|
||||
return nps;
|
||||
}
|
||||
|
||||
PolyK._firstWithFlag = function(ps, ind)
|
||||
{
|
||||
var n = ps.length;
|
||||
while(true)
|
||||
{
|
||||
ind = (ind+1)%n;
|
||||
if(ps[ind].flag) return ind;
|
||||
}
|
||||
}
|
||||
*/
|
||||
PolyK._PointInTriangle = function(px, py, ax, ay, bx, by, cx, cy)
|
||||
{
|
||||
var v0x = cx-ax;
|
||||
var v0y = cy-ay;
|
||||
var v1x = bx-ax;
|
||||
var v1y = by-ay;
|
||||
var v2x = px-ax;
|
||||
var v2y = py-ay;
|
||||
|
||||
var dot00 = v0x*v0x+v0y*v0y;
|
||||
var dot01 = v0x*v1x+v0y*v1y;
|
||||
var dot02 = v0x*v2x+v0y*v2y;
|
||||
var dot11 = v1x*v1x+v1y*v1y;
|
||||
var dot12 = v1x*v2x+v1y*v2y;
|
||||
|
||||
var invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
|
||||
var u = (dot11 * dot02 - dot01 * dot12) * invDenom;
|
||||
var v = (dot00 * dot12 - dot01 * dot02) * invDenom;
|
||||
|
||||
// Check if point is in triangle
|
||||
return (u >= 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.y<a1.y)) return null;
|
||||
if((dax>0 && I.x>a1.x) || (dax<0 && I.x<a1.x)) return null;
|
||||
return I;
|
||||
}
|
||||
|
||||
PolyK._GetLineIntersection = 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;
|
||||
I.x = ( A*dbx - dax*B ) / Den;
|
||||
I.y = ( A*dby - day*B ) / Den;
|
||||
|
||||
if(PolyK._InRect(I, a1, a2) && PolyK._InRect(I, b1, b2)) return I;
|
||||
return null;
|
||||
}
|
||||
|
||||
PolyK._InRect = function(a, b, c)
|
||||
{
|
||||
if (b.x == c.x) return (a.y>=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;
|
||||
@@ -1,122 +0,0 @@
|
||||
/**
|
||||
* 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;
|
||||
@@ -1,548 +0,0 @@
|
||||
var vec2 = require('../math/vec2')
|
||||
, decomp = require('poly-decomp')
|
||||
, Convex = require('../shapes/Convex')
|
||||
|
||||
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 linear damping acting on the body in the velocity direction
|
||||
* @property damping
|
||||
* @type {Number}
|
||||
*/
|
||||
this.damping = typeof(options.damping)=="number" ? options.damping : 0.1;
|
||||
|
||||
/**
|
||||
* The angular force acting on the body
|
||||
* @property angularDamping
|
||||
* @type {Number}
|
||||
*/
|
||||
this.angularDamping = typeof(options.angularDamping)=="number" ? options.angularDamping : 0.1;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
this.concavePath = null;
|
||||
|
||||
this.lastDampingScale = 1;
|
||||
this.lastAngularDampingScale = 1;
|
||||
this.lastDampingTimeStep = -1;
|
||||
};
|
||||
|
||||
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){
|
||||
angle = angle || 0.0;
|
||||
|
||||
// Copy the offset vector
|
||||
if(offset){
|
||||
offset = vec2.fromValues(offset[0],offset[1]);
|
||||
} else {
|
||||
offset = vec2.fromValues(0,0);
|
||||
}
|
||||
|
||||
this.shapes .push(shape);
|
||||
this.shapeOffsets.push(offset);
|
||||
this.shapeAngles .push(angle);
|
||||
this.updateMassProperties();
|
||||
this.updateBoundingRadius();
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove a shape
|
||||
* @method removeShape
|
||||
* @param {Shape} shape
|
||||
* @return {Boolean} True if the shape was found and removed, else false.
|
||||
*/
|
||||
Body.prototype.removeShape = function(shape){
|
||||
var idx = this.shapes.indexOf(shape);
|
||||
|
||||
if(idx != -1){
|
||||
this.shapes.splice(idx,1);
|
||||
this.shapeOffsets.splice(idx,1);
|
||||
this.shapeAngles.splice(idx,1);
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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<N; i++){
|
||||
var shape = shapes[i],
|
||||
r2 = vec2.squaredLength(this.shapeOffsets[i] || zero),
|
||||
Icm = shape.computeMomentOfInertia(m);
|
||||
I += Icm + m*r2;
|
||||
}
|
||||
|
||||
this.inertia = I;
|
||||
|
||||
// Inverse mass properties are easy
|
||||
this.invMass = this.mass > 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);
|
||||
};
|
||||
|
||||
/**
|
||||
* Reads a polygon shape path, and assembles convex shapes from that and puts them at proper offset points.
|
||||
* @method fromPolygon
|
||||
* @param {Array} path An array of 2d vectors, e.g. [[0,0],[0,1],...] that resembles a concave or convex polygon. The shape must be simple and without holes.
|
||||
* @param {Object} [options]
|
||||
* @param {Boolean} [options.optimalDecomp=false] Set to true if you need optimal decomposition. Warning: very slow for polygons with more than 10 vertices.
|
||||
* @param {Boolean} [options.skipSimpleCheck=false] Set to true if you already know that the path is not intersecting itself.
|
||||
* @param {Boolean|Number} [options.removeCollinearPoints=false] Set to a number (angle threshold value) to remove collinear points, or false to keep all points.
|
||||
* @return {Boolean} True on success, else false.
|
||||
*/
|
||||
Body.prototype.fromPolygon = function(path,options){
|
||||
options = options || {};
|
||||
|
||||
// Remove all shapes
|
||||
for(var i=this.shapes.length; i>=0; --i)
|
||||
this.removeShape(this.shapes[i]);
|
||||
|
||||
var p = new decomp.Polygon();
|
||||
p.vertices = path;
|
||||
|
||||
// Make it counter-clockwise
|
||||
p.makeCCW();
|
||||
|
||||
if(typeof(options.removeCollinearPoints)=="number"){
|
||||
p.removeCollinearPoints(options.removeCollinearPoints);
|
||||
}
|
||||
|
||||
// Check if any line segment intersects the path itself
|
||||
if(typeof(options.skipSimpleCheck) == "undefined"){
|
||||
if(!p.isSimple()) return false;
|
||||
}
|
||||
|
||||
// Save this path for later
|
||||
this.concavePath = p.vertices.slice(0);
|
||||
for(var i=0; i<this.concavePath.length; i++){
|
||||
var v = [0,0];
|
||||
vec2.copy(v,this.concavePath[i]);
|
||||
this.concavePath[i] = v;
|
||||
}
|
||||
|
||||
// Slow or fast decomp?
|
||||
var convexes;
|
||||
if(options.optimalDecomp) convexes = p.decomp();
|
||||
else convexes = p.quickDecomp();
|
||||
|
||||
var cm = vec2.create();
|
||||
|
||||
// Add convexes
|
||||
for(var i=0; i!==convexes.length; i++){
|
||||
// Create convex
|
||||
var c = new Convex(convexes[i].vertices);
|
||||
|
||||
// Move all vertices so its center of mass is in the local center of the convex
|
||||
for(var j=0; j!==c.vertices.length; j++){
|
||||
var v = c.vertices[j];
|
||||
vec2.sub(v,v,c.centerOfMass);
|
||||
}
|
||||
|
||||
vec2.scale(cm,c.centerOfMass,1);
|
||||
c.updateTriangles();
|
||||
c.updateCenterOfMass();
|
||||
c.updateBoundingRadius();
|
||||
|
||||
// Add the shape
|
||||
this.addShape(c,cm);
|
||||
}
|
||||
|
||||
this.adjustCenterOfMass();
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
var adjustCenterOfMass_tmp1 = vec2.fromValues(0,0),
|
||||
adjustCenterOfMass_tmp2 = vec2.fromValues(0,0),
|
||||
adjustCenterOfMass_tmp3 = vec2.fromValues(0,0),
|
||||
adjustCenterOfMass_tmp4 = vec2.fromValues(0,0);
|
||||
|
||||
/**
|
||||
* Moves the shape offsets so their center of mass becomes the body center of mass.
|
||||
* @method adjustCenterOfMass
|
||||
*/
|
||||
Body.prototype.adjustCenterOfMass = function(){
|
||||
var zero = adjustCenterOfMass_tmp1,
|
||||
offset_times_area = adjustCenterOfMass_tmp2,
|
||||
sum = adjustCenterOfMass_tmp3,
|
||||
cm = adjustCenterOfMass_tmp4,
|
||||
totalArea = 0;
|
||||
vec2.set(sum,0,0);
|
||||
vec2.set(zero,0,0);
|
||||
|
||||
for(var i=0; i!==this.shapes.length; i++){
|
||||
var s = this.shapes[i],
|
||||
offset = this.shapeOffsets[i] || zero;
|
||||
vec2.scale(offset_times_area,offset,s.area);
|
||||
vec2.add(sum,sum,offset_times_area);
|
||||
totalArea += s.area;
|
||||
}
|
||||
|
||||
vec2.scale(cm,sum,1/totalArea);
|
||||
|
||||
// Now move all shapes
|
||||
for(var i=0; i!==this.shapes.length; i++){
|
||||
var s = this.shapes[i],
|
||||
offset = this.shapeOffsets[i];
|
||||
|
||||
// Offset may be undefined. Fix that.
|
||||
if(!offset){
|
||||
offset = this.shapeOffsets[i] = vec2.create();
|
||||
}
|
||||
|
||||
vec2.sub(offset,offset,cm);
|
||||
}
|
||||
|
||||
// Move the body position too
|
||||
vec2.add(this.position,this.position,cm);
|
||||
|
||||
// And concave path
|
||||
for(var i=0; this.concavePath && i<this.concavePath.length; i++){
|
||||
vec2.sub(this.concavePath[i], this.concavePath[i], cm);
|
||||
}
|
||||
|
||||
this.updateMassProperties();
|
||||
this.updateBoundingRadius();
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the force on the body to zero.
|
||||
* @method setZeroForce
|
||||
*/
|
||||
Body.prototype.setZeroForce = function(){
|
||||
vec2.set(this.force,0.0,0.0);
|
||||
this.angularForce = 0.0;
|
||||
};
|
||||
|
||||
Body.prototype.resetConstraintVelocity = function(){
|
||||
var b = this,
|
||||
vlambda = b.vlambda;
|
||||
vec2.set(vlambda,0,0);
|
||||
b.wlambda = 0;
|
||||
};
|
||||
|
||||
Body.prototype.addConstraintVelocity = function(){
|
||||
var b = this,
|
||||
v = b.velocity;
|
||||
vec2.add( v, v, b.vlambda);
|
||||
b.angularVelocity += b.wlambda;
|
||||
};
|
||||
|
||||
/**
|
||||
* Apply damping, see <a href="http://code.google.com/p/bullet/issues/detail?id=74">this</a> for details.
|
||||
* @method applyDamping
|
||||
* @param {number} dt Current time step
|
||||
*/
|
||||
Body.prototype.applyDamping = function(dt){
|
||||
if(this.motionState & Body.DYNAMIC){ // Only for dynamic bodies
|
||||
|
||||
// Since Math.pow generates garbage we check if we can reuse the scaling coefficient from last step
|
||||
if(dt != this.lastDampingTimeStep){
|
||||
this.lastDampingScale = Math.pow(1.0 - this.damping,dt);
|
||||
this.lastAngularDampingScale = Math.pow(1.0 - this.angularDamping,dt);
|
||||
this.lastDampingTimeStep = dt;
|
||||
}
|
||||
|
||||
var v = this.velocity;
|
||||
vec2.scale(v,v,this.lastDampingScale);
|
||||
this.angularVelocity *= this.lastAngularDampingScale;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 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;
|
||||
@@ -1,181 +0,0 @@
|
||||
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;
|
||||
};
|
||||
@@ -1,38 +0,0 @@
|
||||
// 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'),
|
||||
LockConstraint : require('./constraints/LockConstraint'),
|
||||
Material : require('./material/Material'),
|
||||
NaiveBroadphase : require('./collision/NaiveBroadphase'),
|
||||
Particle : require('./shapes/Particle'),
|
||||
Plane : require('./shapes/Plane'),
|
||||
RevoluteConstraint : require('./constraints/RevoluteConstraint'),
|
||||
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,
|
||||
};
|
||||
@@ -1,146 +0,0 @@
|
||||
var World = require('../world/World')
|
||||
|
||||
var num = { type:"number", required:true };
|
||||
|
||||
/*
|
||||
* Serialize a World instance to JSON
|
||||
* @method serialize
|
||||
* @param {World} world
|
||||
* @return {Object}
|
||||
*/
|
||||
exports.serialize = function(world){
|
||||
return {};
|
||||
};
|
||||
|
||||
/*
|
||||
* Load a World instance from JSON
|
||||
* @param {Object} json
|
||||
* @return {World}
|
||||
*/
|
||||
exports.deserialize = function(json){
|
||||
var world = new World();
|
||||
return world;
|
||||
};
|
||||
|
||||
var schemas = exports.schemas = {};
|
||||
|
||||
schemas['0.3.0'] = {
|
||||
type: "object",
|
||||
additionalProperties:false,
|
||||
properties: {
|
||||
gravity: { $ref:"vec2" },
|
||||
p2: { type:"string", pattern:"^0.3$", required:true },
|
||||
solver: { type:"object", required:true },
|
||||
broadphase: { type:"object", required:true },
|
||||
bodies: {
|
||||
type:"array",
|
||||
required:true,
|
||||
additionalItems:false,
|
||||
items:{
|
||||
type:"object",
|
||||
additionalProperties:false,
|
||||
properties:{
|
||||
id : num,
|
||||
mass : num,
|
||||
angle : num,
|
||||
position : { $ref:"vec2" },
|
||||
velocity : { $ref:"vec2" },
|
||||
angularVelocity : num,
|
||||
force : { $ref:"vec2" },
|
||||
shapes : { required:true, type:"array" },
|
||||
concavePath : { required:true, type:["array","null"] },
|
||||
},
|
||||
}
|
||||
},
|
||||
springs: {
|
||||
type:"array",
|
||||
required:true,
|
||||
additionalItems:false,
|
||||
items:{
|
||||
type:"object",
|
||||
additionalProperties:false,
|
||||
properties:{
|
||||
bodyA : num,
|
||||
bodyB : num,
|
||||
stiffness : num,
|
||||
damping : num,
|
||||
restLength : num,
|
||||
localAnchorA : { $ref:"vec2" },
|
||||
localAnchorB : { $ref:"vec2" },
|
||||
},
|
||||
},
|
||||
},
|
||||
constraints: {
|
||||
type:"array",
|
||||
required:true,
|
||||
items:[{
|
||||
type:"object",
|
||||
additionalProperties:false,
|
||||
properties:{
|
||||
bodyA: num,
|
||||
bodyB: num,
|
||||
type: { type:"string", match:"^DistanceConstraint$" },
|
||||
distance: num,
|
||||
maxForce: num,
|
||||
},
|
||||
},{
|
||||
type:"object",
|
||||
additionalProperties:false,
|
||||
properties:{
|
||||
bodyA: num,
|
||||
bodyB: num,
|
||||
type: { type:"string", match:"^PrismaticConstraint$" },
|
||||
localAxisA: { $ref:"vec2" },
|
||||
localAxisB: { $ref:"vec2" },
|
||||
maxForce: num,
|
||||
},
|
||||
},{
|
||||
type:"object",
|
||||
additionalProperties:false,
|
||||
properties:{
|
||||
bodyA: num,
|
||||
bodyB: num,
|
||||
type: { type:"string", match:'^RevoluteConstraint$' },
|
||||
pivotA: { $ref:"vec2" },
|
||||
pivotB: { $ref:"vec2" },
|
||||
maxForce: num,
|
||||
motorSpeed: { type:["number","boolean"] },
|
||||
lowerLimit: num,
|
||||
lowerLimitEnabled: { type:"boolean" },
|
||||
upperLimit: num,
|
||||
upperLimitEnabled: { type:"boolean" },
|
||||
},
|
||||
}],
|
||||
},
|
||||
contactMaterials: {
|
||||
type:"array",
|
||||
required:true,
|
||||
additionalItems:false,
|
||||
items: {
|
||||
properties : {
|
||||
id: num,
|
||||
materialA: num,
|
||||
materialB: num,
|
||||
friction: num,
|
||||
restitution: num,
|
||||
stiffness: num,
|
||||
relaxation: num,
|
||||
frictionStiffness: num,
|
||||
frictionRelaxation: num,
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
exports.vec2 = {
|
||||
id: "/vec2",
|
||||
type:"array",
|
||||
maxItems:2,
|
||||
minItems:2,
|
||||
items:{
|
||||
type:"number",
|
||||
},
|
||||
additionalItems:false,
|
||||
required:true,
|
||||
};
|
||||
@@ -1,39 +0,0 @@
|
||||
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;
|
||||
};
|
||||
@@ -1,31 +0,0 @@
|
||||
var Shape = require('./Shape');
|
||||
|
||||
module.exports = Circle;
|
||||
|
||||
/**
|
||||
* Circle shape class.
|
||||
* @class Circle
|
||||
* @extends {Shape}
|
||||
* @constructor
|
||||
* @param {number} radius The radius of this circle
|
||||
*/
|
||||
function Circle(radius){
|
||||
|
||||
/**
|
||||
* The radius of the circle.
|
||||
* @property radius
|
||||
* @type {number}
|
||||
*/
|
||||
this.radius = radius || 1;
|
||||
|
||||
Shape.call(this,Shape.CIRCLE);
|
||||
};
|
||||
Circle.prototype = new Shape();
|
||||
Circle.prototype.computeMomentOfInertia = function(mass){
|
||||
var r = this.radius;
|
||||
return mass * r * r / 2;
|
||||
};
|
||||
|
||||
Circle.prototype.updateBoundingRadius = function(){
|
||||
this.boundingRadius = this.radius;
|
||||
};
|
||||
@@ -1,255 +0,0 @@
|
||||
var Shape = require('./Shape')
|
||||
, vec2 = require('../math/vec2')
|
||||
, polyk = require('../math/polyk')
|
||||
, decomp = require('poly-decomp')
|
||||
|
||||
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 || [];
|
||||
|
||||
// Copy the verts
|
||||
for(var i=0; i<this.vertices.length; i++){
|
||||
var v = vec2.fromValues();
|
||||
vec2.copy(v,this.vertices[i]);
|
||||
this.vertices[i] = v;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
/**
|
||||
* Update the .triangles property
|
||||
* @method updateTriangles
|
||||
*/
|
||||
Convex.prototype.updateTriangles = function(){
|
||||
|
||||
this.triangles.length = 0;
|
||||
|
||||
// Rewrite on polyk notation, array of numbers
|
||||
var polykVerts = [];
|
||||
for(var i=0; i<this.vertices.length; i++){
|
||||
var v = this.vertices[i];
|
||||
polykVerts.push(v[0],v[1]);
|
||||
}
|
||||
|
||||
// Triangulate
|
||||
var triangles = polyk.Triangulate(polykVerts);
|
||||
|
||||
// Loop over all triangles, add their inertia contributions to I
|
||||
for(var i=0; i<triangles.length; i+=3){
|
||||
var id1 = triangles[i],
|
||||
id2 = triangles[i+1],
|
||||
id3 = triangles[i+2];
|
||||
|
||||
// Add to triangles
|
||||
this.triangles.push([id1,id2,id3]);
|
||||
}
|
||||
};
|
||||
|
||||
var updateCenterOfMass_centroid = vec2.create(),
|
||||
updateCenterOfMass_centroid_times_mass = vec2.create(),
|
||||
updateCenterOfMass_a = vec2.create(),
|
||||
updateCenterOfMass_b = vec2.create(),
|
||||
updateCenterOfMass_c = vec2.create(),
|
||||
updateCenterOfMass_ac = vec2.create(),
|
||||
updateCenterOfMass_ca = vec2.create(),
|
||||
updateCenterOfMass_cb = vec2.create(),
|
||||
updateCenterOfMass_n = vec2.create();
|
||||
|
||||
/**
|
||||
* Update the .centerOfMass property.
|
||||
* @method updateCenterOfMass
|
||||
*/
|
||||
Convex.prototype.updateCenterOfMass = function(){
|
||||
var triangles = this.triangles,
|
||||
verts = this.vertices,
|
||||
cm = this.centerOfMass,
|
||||
centroid = updateCenterOfMass_centroid,
|
||||
n = updateCenterOfMass_n,
|
||||
a = updateCenterOfMass_a,
|
||||
b = updateCenterOfMass_b,
|
||||
c = updateCenterOfMass_c,
|
||||
ac = updateCenterOfMass_ac,
|
||||
ca = updateCenterOfMass_ca,
|
||||
cb = updateCenterOfMass_cb,
|
||||
centroid_times_mass = updateCenterOfMass_centroid_times_mass;
|
||||
|
||||
vec2.set(cm,0,0);
|
||||
var totalArea = 0;
|
||||
|
||||
for(var i=0; i!==triangles.length; i++){
|
||||
var t = triangles[i],
|
||||
a = verts[t[0]],
|
||||
b = verts[t[1]],
|
||||
c = verts[t[2]];
|
||||
|
||||
vec2.centroid(centroid,a,b,c);
|
||||
|
||||
// Get mass for the triangle (density=1 in this case)
|
||||
// http://math.stackexchange.com/questions/80198/area-of-triangle-via-vectors
|
||||
var m = decomp.Point.area(a,b,c)
|
||||
totalArea += m;
|
||||
|
||||
// Add to center of mass
|
||||
vec2.scale(centroid_times_mass, centroid, m);
|
||||
vec2.add(cm, cm, centroid_times_mass);
|
||||
}
|
||||
|
||||
vec2.scale(cm,cm,1/totalArea);
|
||||
};
|
||||
|
||||
/**
|
||||
* Compute the mass moment of inertia of the Convex.
|
||||
* @method conputeMomentOfInertia
|
||||
* @param {Number} mass
|
||||
* @return {Number}
|
||||
* @todo should use .triangles
|
||||
*/
|
||||
Convex.prototype.computeMomentOfInertia = function(mass){
|
||||
|
||||
// In short: Triangulate the Convex, compute centroid and inertia of
|
||||
// each sub-triangle. Add up to total using parallel axis theorem.
|
||||
|
||||
var I = 0;
|
||||
|
||||
// Rewrite on polyk notation, array of numbers
|
||||
var polykVerts = [];
|
||||
for(var i=0; i<this.vertices.length; i++){
|
||||
var v = this.vertices[i];
|
||||
polykVerts.push(v[0],v[1]);
|
||||
}
|
||||
|
||||
// Triangulate
|
||||
var triangles = polyk.Triangulate(polykVerts);
|
||||
|
||||
// Get total convex area and density
|
||||
var area = polyk.GetArea(polykVerts);
|
||||
this.updateArea();
|
||||
var density = mass / this.area;
|
||||
|
||||
// Temp vectors
|
||||
var a = vec2.create(),
|
||||
b = vec2.create(),
|
||||
c = vec2.create(),
|
||||
centroid = vec2.create(),
|
||||
n = vec2.create(),
|
||||
ac = vec2.create(),
|
||||
ca = vec2.create(),
|
||||
cb = vec2.create(),
|
||||
centroid_times_mass = vec2.create();
|
||||
|
||||
// Loop over all triangles, add their inertia contributions to I
|
||||
for(var i=0; i<triangles.length; i+=3){
|
||||
var id1 = triangles[i],
|
||||
id2 = triangles[i+1],
|
||||
id3 = triangles[i+2];
|
||||
|
||||
// a,b,c are triangle corners
|
||||
vec2.set(a, polykVerts[2*id1], polykVerts[2*id1+1]);
|
||||
vec2.set(b, polykVerts[2*id2], polykVerts[2*id2+1]);
|
||||
vec2.set(c, polykVerts[2*id3], polykVerts[2*id3+1]);
|
||||
|
||||
vec2.centroid(centroid, a, b, c);
|
||||
|
||||
vec2.sub(ca, c, a);
|
||||
vec2.sub(cb, c, b);
|
||||
|
||||
var area_triangle = decomp.Point.area(a,b,c)
|
||||
var base = vec2.length(ca);
|
||||
var height = 2*area_triangle / base; // a=b*h/2 => h=2*a/b
|
||||
|
||||
// Get mass for the triangle
|
||||
var m = area_triangle * density;
|
||||
|
||||
// Get inertia for this triangle: http://answers.yahoo.com/question/index?qid=20080721030038AA3oE1m
|
||||
var I_triangle = m*(base * (Math.pow(height,3))) / 36;
|
||||
|
||||
// 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);
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the .area
|
||||
* @method updateArea
|
||||
*/
|
||||
Convex.prototype.updateArea = function(){
|
||||
this.updateTriangles();
|
||||
this.area = 0;
|
||||
|
||||
var triangles = this.triangles,
|
||||
verts = this.vertices;
|
||||
for(var i=0; i!==triangles.length; i++){
|
||||
var t = triangles[i],
|
||||
a = verts[t[0]],
|
||||
b = verts[t[1]],
|
||||
c = verts[t[2]];
|
||||
|
||||
// Get mass for the triangle (density=1 in this case)
|
||||
// http://math.stackexchange.com/questions/80198/area-of-triangle-via-vectors
|
||||
var m = decomp.Point.area(a,b,c)
|
||||
this.area += m;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
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
|
||||
* @param {Number} length The total length of the 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;
|
||||
};
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
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;
|
||||
};
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
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();
|
||||
|
||||
/**
|
||||
* Compute moment of inertia
|
||||
* @method computeMomentOfInertia
|
||||
*/
|
||||
Plane.prototype.computeMomentOfInertia = function(mass){
|
||||
return 0; // Plane is infinite. The inertia should therefore be infinty but by convention we set 0 here
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the bounding radius
|
||||
* @method updateBoundingRadius
|
||||
*/
|
||||
Plane.prototype.updateBoundingRadius = function(){
|
||||
this.boundingRadius = Number.MAX_VALUE;
|
||||
};
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
var vec2 = require('../math/vec2')
|
||||
, Shape = require('./Shape')
|
||||
, Convex = require('./Convex')
|
||||
|
||||
module.exports = Rectangle;
|
||||
|
||||
/**
|
||||
* Rectangle shape class.
|
||||
* @class Rectangle
|
||||
* @constructor
|
||||
* @param {Number} w Width
|
||||
* @param {Number} h Height
|
||||
* @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)];
|
||||
|
||||
/**
|
||||
* Total width of the rectangle
|
||||
* @property width
|
||||
* @type {Number}
|
||||
*/
|
||||
this.width = w;
|
||||
|
||||
/**
|
||||
* Total height of the rectangle
|
||||
* @property height
|
||||
* @type {Number}
|
||||
*/
|
||||
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;
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the bounding radius
|
||||
* @method updateBoundingRadius
|
||||
*/
|
||||
Rectangle.prototype.updateBoundingRadius = function(){
|
||||
var w = this.width,
|
||||
h = this.height;
|
||||
this.boundingRadius = Math.sqrt(w*w + h*h) / 2;
|
||||
};
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
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 <a href="http://www.aurelienribon.com/blog/2011/07/box2d-tutorial-collision-filtering/">this tutorial</a>.
|
||||
* @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;
|
||||
|
||||
/**
|
||||
* Area of this shape.
|
||||
* @property area
|
||||
* @type {Number}
|
||||
*/
|
||||
this.area = 0;
|
||||
|
||||
this.updateArea();
|
||||
};
|
||||
|
||||
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 <a href="http://en.wikipedia.org/wiki/List_of_moments_of_inertia">Wikipedia's list of moments of inertia</a>.
|
||||
* @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...");
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the .area property of the shape.
|
||||
* @method updateArea
|
||||
*/
|
||||
Shape.prototype.updateArea = function(){
|
||||
// To be implemented in all subclasses
|
||||
};
|
||||
@@ -1,209 +0,0 @@
|
||||
var vec2 = require('../math/vec2')
|
||||
, Solver = require('./Solver')
|
||||
, Utils = require('../utils/Utils')
|
||||
, FrictionEquation = require('../constraints/FrictionEquation')
|
||||
|
||||
module.exports = GSSolver;
|
||||
|
||||
/**
|
||||
* 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 = options || {};
|
||||
|
||||
/**
|
||||
* The number of iterations to do when solving. More gives better results, but is more expensive.
|
||||
* @property iterations
|
||||
* @type {Number}
|
||||
*/
|
||||
this.iterations = options.iterations || 10;
|
||||
|
||||
/**
|
||||
* The error tolerance. If the total error is below this limit, the solver will stop. Set to zero for as good solution as possible.
|
||||
* @property tolerance
|
||||
* @type {Number}
|
||||
*/
|
||||
this.tolerance = options.tolerance || 0;
|
||||
|
||||
this.debug = options.debug || false;
|
||||
this.arrayStep = 30;
|
||||
this.lambda = new Utils.ARRAY_TYPE(this.arrayStep);
|
||||
this.Bs = new Utils.ARRAY_TYPE(this.arrayStep);
|
||||
this.invCs = new Utils.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. Larger number gives harder contacts, etc, but may also be more expensive to compute, or it will make your simulation explode.
|
||||
* @property stiffness
|
||||
* @type {Number}
|
||||
*/
|
||||
this.stiffness = 1e6;
|
||||
|
||||
/**
|
||||
* Global equation relaxation. This is the number of timesteps required for a constraint to be resolved. Larger number will give softer contacts. Set to around 3 or 4 for good enough results.
|
||||
* @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;
|
||||
|
||||
/**
|
||||
* Number of friction iterations to skip. If .skipFrictionIterations=2, then no FrictionEquations will be iterated until the third iteration.
|
||||
* @property skipFrictionIterations
|
||||
* @type {Number}
|
||||
*/
|
||||
this.skipFrictionIterations = 2;
|
||||
};
|
||||
GSSolver.prototype = new Solver();
|
||||
|
||||
/**
|
||||
* Solve the system of equations
|
||||
* @method solve
|
||||
* @param {Number} dt Time step
|
||||
* @param {World} world World to solve
|
||||
*/
|
||||
GSSolver.prototype.solve = function(dt,world){
|
||||
|
||||
this.sortEquations();
|
||||
|
||||
var iter = 0,
|
||||
maxIter = this.iterations,
|
||||
skipFrictionIter = this.skipFrictionIterations,
|
||||
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 Utils.ARRAY_TYPE(Neq + this.arrayStep);
|
||||
this.Bs = new Utils.ARRAY_TYPE(Neq + this.arrayStep);
|
||||
this.invCs = new Utils.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++){
|
||||
bodies[i].resetConstraintVelocity();
|
||||
}
|
||||
|
||||
// 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];
|
||||
|
||||
if(c instanceof FrictionEquation && iter < skipFrictionIter)
|
||||
continue;
|
||||
|
||||
var _eps = useGlobalParams ? eps : c.eps;
|
||||
|
||||
var deltalambda = GSSolver.iterateEquation(j,c,_eps,Bs,invCs,lambda,useZeroRHS,dt);
|
||||
if(tolSquared !== 0) deltalambdaTot += Math.abs(deltalambda);
|
||||
}
|
||||
|
||||
// If the total error is small enough - stop iterate
|
||||
if(tolSquared !== 0 && deltalambdaTot*deltalambdaTot <= tolSquared) break;
|
||||
}
|
||||
|
||||
// Add result to velocity
|
||||
for(i=0; i!==Nbodies; i++){
|
||||
bodies[i].addConstraintVelocity();
|
||||
}
|
||||
}
|
||||
errorTot = deltalambdaTot;
|
||||
};
|
||||
|
||||
GSSolver.iterateEquation = function(j,eq,eps,Bs,invCs,lambda,useZeroRHS,dt){
|
||||
// Compute iteration
|
||||
var B = Bs[j],
|
||||
invC = invCs[j],
|
||||
lambdaj = lambda[j],
|
||||
GWlambda = eq.computeGWlambda(eps);
|
||||
|
||||
if(eq instanceof FrictionEquation){
|
||||
// Rescale the max friction force according to the normal force
|
||||
eq.maxForce = eq.contactEquation.multiplier * eq.frictionCoefficient * dt;
|
||||
eq.minForce = -eq.contactEquation.multiplier * eq.frictionCoefficient * dt;
|
||||
}
|
||||
|
||||
var maxForce = eq.maxForce,
|
||||
minForce = eq.minForce;
|
||||
|
||||
if(useZeroRHS) B = 0;
|
||||
|
||||
var deltalambda = invC * ( B - GWlambda - eps * lambdaj );
|
||||
|
||||
// Clamp if we are not within the min/max interval
|
||||
var 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;
|
||||
eq.multiplier = lambda[j] / dt;
|
||||
eq.addToWlambda(deltalambda);
|
||||
|
||||
return deltalambda;
|
||||
};
|
||||
@@ -1,81 +0,0 @@
|
||||
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});
|
||||
};
|
||||
@@ -1,174 +0,0 @@
|
||||
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
|
||||
* @param {Object} options
|
||||
* @extends Solver
|
||||
*/
|
||||
function IslandSolver(subsolver,options){
|
||||
Solver.call(this,options);
|
||||
var that = this;
|
||||
|
||||
/**
|
||||
* The solver used in the workers.
|
||||
* @property subsolver
|
||||
* @type {Solver}
|
||||
*/
|
||||
this.subsolver = subsolver;
|
||||
|
||||
/**
|
||||
* Number of islands. Read only.
|
||||
* @property numIslands
|
||||
* @type {number}
|
||||
*/
|
||||
this.numIslands = 0;
|
||||
|
||||
// Pooling of node objects saves some GC load
|
||||
this._nodePool = [];
|
||||
|
||||
/**
|
||||
* Fires before an island is solved.
|
||||
* @event beforeSolveIsland
|
||||
* @param {Island} island
|
||||
*/
|
||||
this.beforeSolveIslandEvent = {
|
||||
type : "beforeSolveIsland",
|
||||
island : null,
|
||||
};
|
||||
};
|
||||
IslandSolver.prototype = new Solver();
|
||||
|
||||
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
|
||||
var e = this.beforeSolveIslandEvent;
|
||||
for(var i=0; i<islands.length; i++){
|
||||
var island = islands[i];
|
||||
e.island = island;
|
||||
this.emit(e);
|
||||
island.solve(dt,this.subsolver);
|
||||
}
|
||||
};
|
||||
@@ -1,93 +0,0 @@
|
||||
var Utils = require('../utils/Utils')
|
||||
, EventEmitter = require('../events/EventEmitter')
|
||||
|
||||
module.exports = Solver;
|
||||
|
||||
/**
|
||||
* Base class for constraint solvers.
|
||||
* @class Solver
|
||||
* @constructor
|
||||
* @extends {EventEmitter}
|
||||
*/
|
||||
function Solver(options){
|
||||
options = options || {};
|
||||
|
||||
EventEmitter.call(this);
|
||||
|
||||
/**
|
||||
* Current equations in the solver.
|
||||
*
|
||||
* @property equations
|
||||
* @type {Array}
|
||||
*/
|
||||
this.equations = [];
|
||||
|
||||
/**
|
||||
* Function that is used to sort all equations before each solve.
|
||||
* @property equationSortFunction
|
||||
* @type {function|boolean}
|
||||
*/
|
||||
this.equationSortFunction = options.equationSortFunction || false;
|
||||
};
|
||||
Solver.prototype = new EventEmitter();
|
||||
|
||||
/**
|
||||
* Method to be implemented in each subclass
|
||||
* @method solve
|
||||
* @param {Number} dt
|
||||
* @param {World} world
|
||||
*/
|
||||
Solver.prototype.solve = function(dt,world){
|
||||
throw new Error("Solver.solve should be implemented by subclasses!");
|
||||
};
|
||||
|
||||
/**
|
||||
* Sort all equations using the .equationSortFunction. Should be called by subclasses before solving.
|
||||
* @method sortEquations
|
||||
*/
|
||||
Solver.prototype.sortEquations = function(){
|
||||
if(this.equationSortFunction)
|
||||
this.equations.sort(this.equationSortFunction);
|
||||
};
|
||||
|
||||
/**
|
||||
* Add an equation to be solved.
|
||||
*
|
||||
* @method addEquation
|
||||
* @param {Equation} eq
|
||||
*/
|
||||
Solver.prototype.addEquation = function(eq){
|
||||
this.equations.push(eq);
|
||||
};
|
||||
|
||||
/**
|
||||
* Add equations. Same as .addEquation, but this time the argument is an array of Equations
|
||||
*
|
||||
* @method addEquations
|
||||
* @param {Array} eqs
|
||||
*/
|
||||
Solver.prototype.addEquations = function(eqs){
|
||||
Utils.appendArray(this.equations,eqs);
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove an equation.
|
||||
*
|
||||
* @method removeEquation
|
||||
* @param {Equation} eq
|
||||
*/
|
||||
Solver.prototype.removeEquation = function(eq){
|
||||
var i = this.equations.indexOf(eq);
|
||||
if(i!=-1)
|
||||
this.equations.splice(i,1);
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove all currently added equations.
|
||||
*
|
||||
* @method removeAllEquations
|
||||
*/
|
||||
Solver.prototype.removeAllEquations = function(){
|
||||
this.equations.length=0;
|
||||
};
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
module.exports = Utils;
|
||||
|
||||
/**
|
||||
* Misc utility functions
|
||||
* @class Utils
|
||||
* @constructor
|
||||
*/
|
||||
function Utils(){};
|
||||
|
||||
/**
|
||||
* Append the values in array b to the array a. See <a href="http://stackoverflow.com/questions/1374126/how-to-append-an-array-to-an-existing-javascript-array/1374131#1374131">this</a> 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]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The array type to use for internal numeric computations.
|
||||
* @type {Array}
|
||||
* @static
|
||||
* @property ARRAY_TYPE
|
||||
*/
|
||||
Utils.ARRAY_TYPE = Float32Array || Array;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -234,11 +234,6 @@ Phaser.Physics.Arcade.prototype = {
|
||||
rebounded = true;
|
||||
}
|
||||
|
||||
if (body.sprite.debug)
|
||||
{
|
||||
console.log('checkBounds finished', rebounded, body.blocked);
|
||||
}
|
||||
|
||||
return rebounded;
|
||||
|
||||
},
|
||||
@@ -850,10 +845,6 @@ Phaser.Physics.Arcade.prototype = {
|
||||
return this._intersection;
|
||||
}
|
||||
|
||||
// console.log('____ tileIntersects');
|
||||
// console.log('body: ', body.left, body.top, body.right, body.bottom);
|
||||
// console.log('tile: ', tile.x, tile.y, tile.right, tile.bottom);
|
||||
|
||||
if (!(body.right < tile.x || body.bottom < tile.y || body.left > tile.right || body.top > tile.bottom))
|
||||
{
|
||||
this._intersection[0] = Math.max(body.left, tile.x); // x
|
||||
@@ -866,6 +857,7 @@ Phaser.Physics.Arcade.prototype = {
|
||||
}
|
||||
|
||||
this._intersection[4] = 0;
|
||||
|
||||
return this._intersection;
|
||||
|
||||
},
|
||||
@@ -879,8 +871,6 @@ Phaser.Physics.Arcade.prototype = {
|
||||
*/
|
||||
separateTiles: function (body, tiles) {
|
||||
|
||||
// console.log('!!! separateTiles', tiles);
|
||||
|
||||
var tile;
|
||||
var result = false;
|
||||
|
||||
@@ -912,15 +902,9 @@ Phaser.Physics.Arcade.prototype = {
|
||||
// If the intersection area is either entirely null, or has a width/height of zero, we bail out now
|
||||
if (this._intersection[4] === 0 || this._intersection[2] === 0 || this._intersection[3] === 0)
|
||||
{
|
||||
// console.log('Tile does not intersect body');
|
||||
return false;
|
||||
}
|
||||
|
||||
// console.log('*** separateTile', tile);
|
||||
// console.log('intersection', this._intersection);
|
||||
|
||||
// tile.tile.debug = true;
|
||||
|
||||
// They overlap. Any custom callbacks?
|
||||
if (tile.tile.callback || tile.layer.callbacks[tile.tile.index])
|
||||
{
|
||||
@@ -947,8 +931,6 @@ Phaser.Physics.Arcade.prototype = {
|
||||
// LEFT
|
||||
body.overlapX = body.left - tile.right;
|
||||
|
||||
// console.log('ST left', body.overlapX, body.deltaX(), 'bt', body.left, tile.right);
|
||||
|
||||
if (body.overlapX < 0)
|
||||
{
|
||||
process = true;
|
||||
@@ -963,8 +945,6 @@ Phaser.Physics.Arcade.prototype = {
|
||||
// RIGHT
|
||||
body.overlapX = body.right - tile.x;
|
||||
|
||||
// console.log('ST right', body.overlapX, body.deltaX(), 'bt', body.right, tile.x);
|
||||
|
||||
if (body.overlapX > 0)
|
||||
{
|
||||
process = true;
|
||||
@@ -980,8 +960,6 @@ Phaser.Physics.Arcade.prototype = {
|
||||
// UP
|
||||
body.overlapY = body.top - tile.bottom;
|
||||
|
||||
// console.log('ST up', body.overlapY, body.deltaY(), 'bt', body.top, tile.bottom);
|
||||
|
||||
if (body.overlapY < 0)
|
||||
{
|
||||
process = true;
|
||||
@@ -996,8 +974,6 @@ Phaser.Physics.Arcade.prototype = {
|
||||
// DOWN
|
||||
body.overlapY = body.bottom - tile.y;
|
||||
|
||||
// console.log('ST down', body.overlapY, body.deltaY(), 'bt', body.bottom, tile.y);
|
||||
|
||||
if (body.overlapY > 0)
|
||||
{
|
||||
process = true;
|
||||
@@ -1042,8 +1018,6 @@ Phaser.Physics.Arcade.prototype = {
|
||||
*/
|
||||
processTileSeparation: function (body) {
|
||||
|
||||
// console.log('PRE processTileSeparation xy', body.x, body.y, 'left', body.left, 'right', body.right, 'up', body.up, 'down', body.down);
|
||||
|
||||
if (body.overlapX < 0)
|
||||
{
|
||||
body.x -= body.overlapX;
|
||||
@@ -1052,8 +1026,6 @@ Phaser.Physics.Arcade.prototype = {
|
||||
body.blocked.x = Math.floor(body.x);
|
||||
body.blocked.y = Math.floor(body.y);
|
||||
body.blocked.left = true;
|
||||
// body.touching.left = true;
|
||||
// body.touching.none = false;
|
||||
}
|
||||
else if (body.overlapX > 0)
|
||||
{
|
||||
@@ -1063,8 +1035,6 @@ Phaser.Physics.Arcade.prototype = {
|
||||
body.blocked.x = Math.floor(body.x);
|
||||
body.blocked.y = Math.floor(body.y);
|
||||
body.blocked.right = true;
|
||||
// body.touching.right = true;
|
||||
// body.touching.none = false;
|
||||
}
|
||||
|
||||
if (body.overlapY < 0)
|
||||
@@ -1075,9 +1045,6 @@ Phaser.Physics.Arcade.prototype = {
|
||||
body.blocked.x = Math.floor(body.x);
|
||||
body.blocked.y = Math.floor(body.y);
|
||||
body.blocked.up = true;
|
||||
// body.touching.up = true;
|
||||
// body.touching.none = false;
|
||||
|
||||
}
|
||||
else if (body.overlapY > 0)
|
||||
{
|
||||
@@ -1087,12 +1054,8 @@ Phaser.Physics.Arcade.prototype = {
|
||||
body.blocked.x = Math.floor(body.x);
|
||||
body.blocked.y = Math.floor(body.y);
|
||||
body.blocked.down = true;
|
||||
// body.touching.down = true;
|
||||
// body.touching.none = false;
|
||||
}
|
||||
|
||||
// console.log('POST processTileSeparation xy', body.x, body.y, 'left', body.left, 'right', body.right, 'up', body.up, 'down', body.down);
|
||||
|
||||
return true;
|
||||
|
||||
},
|
||||
|
||||
@@ -393,20 +393,6 @@ Phaser.Physics.Arcade.Body.prototype = {
|
||||
this.updateScale();
|
||||
}
|
||||
|
||||
if (this.sprite.debug)
|
||||
{
|
||||
console.log('Body preUpdate x:', this.x, 'y:', this.y);
|
||||
// console.log('Body preUpdate Sprite x:', this.sprite.x, 'y:', this.sprite.y);
|
||||
// console.log('Body preUpdate Sprite world:', this.sprite.world.x, 'y:', this.sprite.world.y);
|
||||
// console.log('Body preUpdate Sprite position:', this.sprite.position.x, 'y:', this.sprite.position.y);
|
||||
// console.log('Body preUpdate Sprite localTransform:', this.sprite.localTransform[2], 'y:', this.sprite.localTransform[5]);
|
||||
// console.log('Body preUpdate Sprite worldTransform:', this.sprite.worldTransform[2], 'y:', this.sprite.worldTransform[5]);
|
||||
// console.log('Body preUpdate x:', this.x, 'y:', this.y, 'left:', this.left, 'right:', this.right, 'WAS', this.preX, this.preY);
|
||||
console.log('Body preUpdate blocked:', this.blocked, this.blockFlags);
|
||||
console.log('Body preUpdate velocity:', this.velocity.x, this.velocity.y);
|
||||
// console.log('Body preUpdate rotation:', this.rotation, this.preRotation);
|
||||
}
|
||||
|
||||
this.checkBlocked();
|
||||
|
||||
this.touching.none = true;
|
||||
@@ -444,12 +430,6 @@ if (this.sprite.debug)
|
||||
this.updateBounds();
|
||||
}
|
||||
|
||||
if (this.sprite.debug)
|
||||
{
|
||||
console.log('Body preUpdate AFTER integration x:', this.x, 'y:', this.y, 'left:', this.left, 'right:', this.right);
|
||||
console.log('Body preUpdate velocity:', this.velocity.x, this.velocity.y);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -462,14 +442,12 @@ if (this.sprite.debug)
|
||||
|
||||
if ((this.blocked.left || this.blocked.right) && (Math.floor(this.x) !== this.blocked.x || Math.floor(this.y) !== this.blocked.y))
|
||||
{
|
||||
// console.log('resetBlocked unlocked left + right', Math.floor(this.x), this.blocked.x);
|
||||
this.blocked.left = false;
|
||||
this.blocked.right = false;
|
||||
}
|
||||
|
||||
if ((this.blocked.up || this.blocked.down) && (Math.floor(this.x) !== this.blocked.x || Math.floor(this.y) !== this.blocked.y))
|
||||
{
|
||||
// console.log('resetBlocked unlocked up + down', Math.floor(this.y), this.blocked.y, 'x', Math.floor(this.x), this.blocked.x);
|
||||
this.blocked.up = false;
|
||||
this.blocked.down = false;
|
||||
}
|
||||
@@ -547,12 +525,6 @@ if (this.sprite.debug)
|
||||
*/
|
||||
reboundCheck: function (x, y, rebound) {
|
||||
|
||||
if (this.sprite.debug)
|
||||
{
|
||||
console.log('reboundCheck start', this.velocity.x, this.velocity.y);
|
||||
console.log('reBound blocked state', this.blocked);
|
||||
}
|
||||
|
||||
if (x)
|
||||
{
|
||||
if (rebound && this.bounce.x !== 0 && (this.blocked.left || this.blocked.right || this.touching.left || this.touching.right))
|
||||
@@ -562,11 +534,6 @@ if (this.sprite.debug)
|
||||
{
|
||||
this.velocity.x *= -this.bounce.x;
|
||||
this.angle = Math.atan2(this.velocity.y, this.velocity.x);
|
||||
|
||||
if (this.sprite.debug)
|
||||
{
|
||||
console.log('X rebound applied', this._vx, 'to', this.velocity.x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -577,16 +544,6 @@ if (this.sprite.debug)
|
||||
if (((this.blocked.left || this.touching.left) && (gx < 0 || this.velocity.x < 0)) || ((this.blocked.right || this.touching.right) && (gx > 0 || this.velocity.x > 0)))
|
||||
{
|
||||
this.velocity.x = 0;
|
||||
|
||||
if (this.sprite.debug)
|
||||
{
|
||||
console.log('reboundCheck X zeroed');
|
||||
}
|
||||
}
|
||||
|
||||
if (this.sprite.debug)
|
||||
{
|
||||
console.log('reboundCheck X', this.velocity.x, 'gravity', gx);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -600,13 +557,6 @@ if (this.sprite.debug)
|
||||
{
|
||||
this.velocity.y *= -this.bounce.y;
|
||||
this.angle = Math.atan2(this.velocity.y, this.velocity.x);
|
||||
|
||||
if (this.sprite.debug)
|
||||
{
|
||||
console.log('Y rebound applied', this._vy, 'to', this.velocity.y);
|
||||
console.log('Y rebound check 1', !(this._vy <= 0 && this.velocity.y > 0));
|
||||
console.log('Y rebound check 2', !(this._vy >= 0 && this.velocity.y < 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -617,16 +567,6 @@ if (this.sprite.debug)
|
||||
if (((this.blocked.up || this.touching.up) && (gy < 0 || this.velocity.y < 0)) || ((this.blocked.down || this.touching.down) && (gy > 0 || this.velocity.y > 0)))
|
||||
{
|
||||
this.velocity.y = 0;
|
||||
|
||||
if (this.sprite.debug)
|
||||
{
|
||||
console.log('reboundCheck Y zeroed');
|
||||
}
|
||||
}
|
||||
|
||||
if (this.sprite.debug)
|
||||
{
|
||||
console.log('reboundCheck Y', this.velocity.y, 'gravity', gy);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -819,8 +759,16 @@ if (this.sprite.debug)
|
||||
*/
|
||||
processRebound: function (body) {
|
||||
|
||||
// Don't rebound again if they've already rebounded in this frame
|
||||
if (!(this._vx <= 0 && this.velocity.x > 0) && !(this._vx >= 0 && this.velocity.x < 0))
|
||||
{
|
||||
this.velocity.x = body.velocity.x - this.velocity.x * this.bounce.x;
|
||||
}
|
||||
|
||||
if (!(this._vy <= 0 && this.velocity.y > 0) && !(this._vy >= 0 && this.velocity.y < 0))
|
||||
{
|
||||
this.velocity.y = body.velocity.y - this.velocity.y * this.bounce.y;
|
||||
}
|
||||
|
||||
this.angle = Math.atan2(this.velocity.y, this.velocity.x);
|
||||
|
||||
@@ -973,12 +921,10 @@ if (this.sprite.debug)
|
||||
// Which is smaller? Left or Right?
|
||||
if (this._distances[0] < this._distances[1] && (body.checkCollision.right || this.checkCollision.left))
|
||||
{
|
||||
// console.log(this.sprite.name, 'collided on the LEFT with', body.sprite.name, response);
|
||||
hasSeparated = this.hitLeft(body, response);
|
||||
}
|
||||
else if (this._distances[1] < this._distances[0] && (body.checkCollision.left || this.checkCollision.right))
|
||||
{
|
||||
// console.log(this.sprite.name, 'collided on the RIGHT with', body.sprite.name, response);
|
||||
hasSeparated = this.hitRight(body, response);
|
||||
}
|
||||
}
|
||||
@@ -987,19 +933,16 @@ if (this.sprite.debug)
|
||||
// Which is smaller? Top or Bottom?
|
||||
if (this._distances[2] < this._distances[3] && (body.checkCollision.down || this.checkCollision.up))
|
||||
{
|
||||
// console.log(this.sprite.name, 'collided on the TOP with', body.sprite.name, response);
|
||||
hasSeparated = this.hitTop(body, response);
|
||||
}
|
||||
else if (this._distances[3] < this._distances[2] && (body.checkCollision.up || this.checkCollision.down))
|
||||
{
|
||||
// console.log(this.sprite.name, 'collided on the BOTTOM with', body.sprite.name, response);
|
||||
hasSeparated = this.hitBottom(body, response);
|
||||
}
|
||||
}
|
||||
|
||||
if (hasSeparated)
|
||||
{
|
||||
console.log('Body hasSeparated');
|
||||
this.game.physics.checkBounds(this);
|
||||
this.game.physics.checkBounds(body);
|
||||
}
|
||||
@@ -1200,11 +1143,6 @@ if (this.sprite.debug)
|
||||
this._dx = this.game.time.physicsElapsed * (this.velocity.x + this._temp.x / 2);
|
||||
this._dy = this.game.time.physicsElapsed * (this.velocity.y + this._temp.y / 2);
|
||||
|
||||
if (this.sprite.debug)
|
||||
{
|
||||
// console.log('integrateVelocity TEMP:', this._temp.x, this._temp.y);
|
||||
}
|
||||
|
||||
// positive = RIGHT / DOWN
|
||||
// negative = LEFT / UP
|
||||
|
||||
@@ -1212,34 +1150,12 @@ if (this.sprite.debug)
|
||||
{
|
||||
this.x += this._dx;
|
||||
this.velocity.x += this._temp.x;
|
||||
if (this.sprite.debug)
|
||||
{
|
||||
// console.log('integrateVelocity x added', this._dx, this.x);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this.sprite.debug)
|
||||
{
|
||||
// console.log('integrateVelocity x failed or zero, blocked left/right', this._dx);
|
||||
}
|
||||
}
|
||||
|
||||
if ((this._dy < 0 && !this.blocked.up && !this.touching.up) || (this._dy > 0 && !this.blocked.down && !this.touching.down))
|
||||
{
|
||||
this.y += this._dy;
|
||||
this.velocity.y += this._temp.y;
|
||||
if (this.sprite.debug)
|
||||
{
|
||||
// console.log('integrateVelocity y added', this._dy, this.y);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this.sprite.debug)
|
||||
{
|
||||
// console.log('integrateVelocity y failed or zero, blocked up/down', this._dy);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.velocity.x > this.maxVelocity.x)
|
||||
|
||||
+1626
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user