Physics.jsPhysics engine in js - chained API (ala jQuery) on top of Box2d | |
| physics.js |
the wrapper on top of body+all joint are here
TODO define all type of joint
define a function which create a pjs joint class based box2djs class
do a instanceof and new
using this function in toJoint()
test this in index.html with the engine
first only the Joint
then the body
in jointDef.body(body1, body2) handle box2d body and pjs body
|
|
Define global namespace
|
var pjs = {};
|
discover global namespace. support browser+nodejs
|
pjs._global = typeof window !== "undefined" ? window :
typeof global !== "undefined" ? global :
console.assert(false);
pjs._jointIClassNames = [
"b2DistanceJoint",
"b2GearJoint",
"b2MouseJoint",
"b2PrismaticJoint",
"b2PulleyJoint",
"b2RevoluteJoint"
];
|
Create a base class
|
pjs._createBaseClass = function(opts){
var iClassName = opts._iClassName;
var className = opts._className || (opts._iClassName.substr(2,1).toLowerCase() + opts._iClassName.substr(3));
var attrInfos = opts._attrInfos || console.assert(false);
var attrOneFct = opts._attrOneFct || console.assert(false);
pjs[className] = function(ctorDef) {
return new pjs[className].fn.init(ctorDef);
}
pjs[className].prototype = {
_iClassName : iClassName,
_className : className,
_attrInfos : attrInfos,
get : function(){
return this._iClass;
},
attr : function(param1, param2){
if(typeof param1 == "object"){
console.assert(typeof param2 == "undefined")
for(var key in param1){
this.attr(key, param1[key]);
}
return this;
}
return this._attrOne(param1, param2);
}
}
pjs[className].prototype['_attrOne'] = attrOneFct;
Object.keys(attrInfos).forEach(function(key){
pjs[className].prototype[key] = function(val){
return this._attrOne(key, val)
};
})
console.assert(typeof opts.init === "function")
for(var fctName in opts){
if( fctName[0] === "_" ) continue;
pjs[className].prototype[fctName] = opts[fctName];
}
pjs[className].prototype.init.prototype = pjs[className].fn = pjs[className].prototype;
}
pjs._objDefaultAttrInfos = function(iClassName){
var iClass = pjs._global[iClassName].prototype;
var attrInfos = {};
for(var key in iClass){
if( typeof iClass[key] !== "function" ) continue;
if( !key.match(/^Get/) && !key.match(/^Set/) ) continue;
var attrName = key.substr(3,1).toLowerCase() + key.substring(4)
var access = key.substr(0,3);
if( typeof attrInfos[attrName] === "undefined" ){
attrInfos[attrName] = "";
}
if( access === "Get" ){
attrInfos[attrName] += "r";
}else if( access === "Set" ){
attrInfos[attrName] += "w";
}else console.assert(false);
}
return attrInfos;
}
pjs._objDefaultAttrOneFct = function(key, val){
var keyCapitalized = key.substring(0,1).toUpperCase() + key.substring(1);
console.assert(typeof this._attrInfos[key] !== "undefined")
var attrInfo = this._attrInfos[key];
if(typeof val === 'undefined'){
console.assert(attrInfo.indexOf("r") != -1);
return this._iClass["Get"+keyCapitalized]();
}
console.assert(attrInfo.indexOf("w") != -1);
this._iClass["Set"+keyCapitalized](val);
return this;
};
|
Create a class for Definitions
|
pjs._createObjClass = function(opts){
var iClassName = opts._iClassName;
opts._attrInfos = opts._attrInfos || pjs._objDefaultAttrInfos(iClassName);
opts._attrOneFct= opts._attrOneFct || pjs._objDefaultAttrOneFct;
opts.init = opts.init || function(iClass){
this._iClass = iClass;
return this;
}
return pjs._createBaseClass(opts)
}
pjs._createObjClass({
_iClassName : "b2Body"
})
|
Create a class for Definitions
|
pjs._createJointObjClass = function(opts){
var iClassName = opts._iClassName;
opts.init = opts.init || function(iClass){
console.log("init iClassName", iClassName)
console.assert(iClass instanceof pjs._global[iClassName])
this._iClass = iClass;
return this;
}
return pjs._createObjClass(opts)
}
pjs._jointIClassNames.forEach(function(iClassName){
pjs._createJointObjClass({
_iClassName : iClassName
})
})
pjs._createJointObj = function(iClass){
console.log("iClass", iClass)
for(var i = 0; pjs._jointIClassNames.length;i++){
var iClassName = pjs._jointIClassNames[i];
if( iClass instanceof pjs._global[iClassName] ){
return pjs.revoluteJoint(iClass);
}
}
console.assert(false);
return undefined;
}
pjs._defDefaultAttrInfos = function(iClassName){
var iClass = new pjs._global[iClassName]()
var attrInfos = {};
for(var key in iClass){
var type = typeof iClass[key];
if( type === "function" ) continue;
attrInfos[key] = "rw";
}
return attrInfos;
}
pjs._defDefaultAttrOneFct = function(key, val){
console.assert(typeof this._attrInfos[key] !== "undefined")
var attrInfo = this._attrInfos[key];
if(typeof val === 'undefined'){
console.assert(attrInfo.indexOf("r") != -1);
return this._iClass[key];
}
console.assert(attrInfo.indexOf("w") != -1);
this._iClass[key] = val;
return this;
};
|
Create a class for Definitions
|
pjs._createBaseDefClass = function(opts){
var iClassName = opts._iClassName;
opts._attrInfos = opts._attrInfos || pjs._defDefaultAttrInfos(iClassName);
opts._attrOneFct= opts._attrOneFct || pjs._defDefaultAttrOneFct;
return pjs._createBaseClass(opts)
}
|
Create a ShapeDef Class
- create the class itself, dont instanciate with the object of this class
- derived from pjs._createBaseDefClass
|
pjs._createShapeDefClass = function(opts){
var iClassName = opts._iClassName;
opts.init = function(attrs){
this._iClass = new pjs._global[iClassName]();
if( attrs ) this.attr(attrs);
return this;
};
opts.toBodyDef = function(attrs){
var bodyDef = pjs.bodyDef(this._iClass)
if( attrs ) bodyDef.attr(attrs);
return bodyDef;
};
return pjs._createBaseDefClass(opts)
}
|
Create a JointDef Class
- create the class itself, dont instanciate with the object of this class
- derived from pjs._createBaseDefClass
|
pjs._createJointDefClass = function(opts){
var iClassName = opts._iClassName;
opts.init = function(attrs){
this._iClass = new pjs._global[iClassName]();
if( attrs ) this.attr(attrs);
return this;
};
opts.toJoint = function(world){
var joint = world.CreateJoint(this._iClass);
return pjs._createJointObj(joint)
}
return pjs._createBaseDefClass(opts)
}
|
create the bodyDef class
|
pjs._createBaseDefClass({
_iClassName : "b2BodyDef",
init : function(shapeDef){
this._iClass = new b2BodyDef();
this._iClass.AddShape(shapeDef);
return this;
},
toBody : function(world){
return pjs.body( world.CreateBody(this._iClass) );
},
position : function(x, y){
this._iClass.position.Set(x, y);
return this;
}
})
|
create all the Shape Definition class
|
pjs._createShapeDefClass({
_iClassName : "b2BoxDef",
size : function(w, h){
console.assert(typeof w !== "undefined");
console.assert(typeof h !== "undefined");
this._iClass.extents.Set(w, h);
return this;
}
});
pjs._createShapeDefClass({
_iClassName : "b2CircleDef"
});
pjs._createShapeDefClass({
_iClassName : "b2PolyDef"
});
pjs._createJointDefClass({
_iClassName : "b2DistanceJointDef"
})
pjs._createJointDefClass({
_iClassName : "b2GearJointDef"
})
pjs._createJointDefClass({
_iClassName : "b2MouseJointDef"
})
pjs._createJointDefClass({
_iClassName : "b2PrismaticJointDef",
anchor : function(x, y){
console.assert(typeof x !== "undefined");
console.assert(typeof y !== "undefined");
this._iClass.anchorPoint.Set(x, y);
return this;
},
axis : function(x, y){
console.assert(typeof x !== "undefined");
console.assert(typeof y !== "undefined");
this._iClass.axis.Set(x, y);
return this;
},
translation : function(lower, upper){
console.assert(typeof lower !== "undefined");
console.assert(typeof upper !== "undefined");
this.attr('lowerTranslation', lower);
this.attr('upperTranslation', upper);
return this;
},
body : function(body1, body2){
console.assert(typeof body1 !== "undefined");
console.assert(typeof body2 !== "undefined");
if( body1 instanceof pjs.body ) body1 = body1.get();
if( body2 instanceof pjs.body ) body2 = body2.get();
this._iClass.body1 = body1;
this._iClass.body2 = body2;
return this;
}
})
pjs._createJointDefClass({
_iClassName : "b2PulleyJointDef"
})
pjs._createJointDefClass({
_iClassName : "b2RevoluteJointDef",
anchor : function(x, y){
console.assert(typeof x !== "undefined");
console.assert(typeof y !== "undefined");
this._iClass.anchorPoint.Set(x, y);
return this;
},
body : function(body1, body2){
console.assert(typeof body1 !== "undefined");
console.assert(typeof body2 !== "undefined");
if( body1 instanceof pjs.body ) body1 = body1.get();
if( body2 instanceof pjs.body ) body2 = body2.get();
this._iClass.body1 = body1;
this._iClass.body2 = body2;
return this;
}
})
|
| plugins/physics.world2canvas.js |
physics.js plugin to display a world in canvas
|
|
Define the namespace
|
console.assert(typeof pjs.world2canvas === "undefined");
pjs.world2canvas = {};
|
Draw the world
- param: context
canvas 2d context
|
pjs.world2canvas.drawWorld = function(world, context) {
context.clearRect(0, 0, context.canvas.width, context.canvas.height);
for (var j = world.m_jointList; j; j = j.m_next) {
pjs.world2canvas.drawJoint(j, context);
}
for (var b = world.m_bodyList; b; b = b.m_next) {
for (var s = b.GetShapeList(); s != null; s = s.GetNext()) {
pjs.world2canvas.drawShape(s, context);
}
}
}
|
Draw a joint
- param: context
canvas 2d context
|
pjs.world2canvas.drawJoint = function(joint, context) {
var b1 = joint.m_body1;
var b2 = joint.m_body2;
var x1 = b1.m_position;
var x2 = b2.m_position;
var p1 = joint.GetAnchor1();
var p2 = joint.GetAnchor2();
context.strokeStyle = '#00eeee';
context.beginPath();
switch (joint.m_type) {
case b2Joint.e_distanceJoint:
context.moveTo(p1.x, p1.y);
context.lineTo(p2.x, p2.y);
break;
case b2Joint.e_pulleyJoint:
break;
default:
if (b1 == world.m_groundBody) {
context.moveTo(p1.x, p1.y);
context.lineTo(x2.x, x2.y);
}
else if (b2 == world.m_groundBody) {
context.moveTo(p1.x, p1.y);
context.lineTo(x1.x, x1.y);
}
else {
context.moveTo(x1.x, x1.y);
context.lineTo(p1.x, p1.y);
context.lineTo(x2.x, x2.y);
context.lineTo(p2.x, p2.y);
}
break;
}
context.stroke();
}
|
Draw a shape
- param: context
canvas 2d context
|
pjs.world2canvas.drawShape = function(shape, context) {
context.strokeStyle = '#ffffff';
if (shape.density == 1.0) {
context.fillStyle = "red";
} else {
context.fillStyle = "black";
}
context.beginPath();
switch (shape.m_type) {
case b2Shape.e_circleShape:
{
var circle = shape;
var pos = circle.m_position;
var r = circle.m_radius;
var segments = 16.0;
var theta = 0.0;
var dtheta = 2.0 * Math.PI / segments;
context.moveTo(pos.x + r, pos.y);
for (var i = 0; i < segments; i++) {
var d = new b2Vec2(r * Math.cos(theta), r * Math.sin(theta));
var v = b2Math.AddVV(pos, d);
context.lineTo(v.x, v.y);
theta += dtheta;
}
context.lineTo(pos.x + r, pos.y);
context.moveTo(pos.x, pos.y);
var ax = circle.m_R.col1;
var pos2 = new b2Vec2(pos.x + r * ax.x, pos.y + r * ax.y);
context.lineTo(pos2.x, pos2.y);
}
break;
case b2Shape.e_polyShape:
{
var poly = shape;
var tV = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[0]));
context.moveTo(tV.x, tV.y);
for (var i = 0; i < poly.m_vertexCount; i++) {
var v = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[i]));
context.lineTo(v.x, v.y);
}
context.lineTo(tV.x, tV.y);
}
break;
}
context.fill();
context.stroke();
}
|
| plugins/physics.worldloop.js |
physics.js plugin to loop over the world
|
|
Define the namespace
|
console.assert(typeof pjs.worldloop === "undefined");
pjs.worldloop = {}
|
- externalize the drawing function
- currently depends on easybox2d.world2canvas
|
pjs.worldloop.basicCanvas = function(world, ctx) {
pjs.worldloop.basic(world, function(world){
pjs.world2canvas.drawWorld(world, ctx)
});
}
|
- TODO make it cleaner
- TODO make this function a lot more parametrable
- TODO clean this function for slow computer aka what happen is rendering is too slow
- howto http://gafferongames.com/game-physics/fix-your-timestep/
- param: Function RenderCb(world) will be called to render the world
|
pjs.worldloop.basic = function(world, renderCb) {
var stepping = false;
var timeStep = 1.0/60;
var iteration = 1;
world.Step(timeStep, iteration);
renderCb(world);
setTimeout(function(){
pjs.worldloop.basic(world, renderCb)
}, timeStep*1000);
}
|