Revolute joint in box2d in javascript

By | January 26, 2013

Revolute Joint

Revolute joint is a useful joint in box2d that allows 2 bodies to be pinned together at a point without restricting rotation.

```

```

The joint can be made between 2 dynamic bodies or between a static and dynamic body. The pendulum on left is a static circle joined to dynamic rectangle. Whereas the cart with 2 wheels is made of dynamic bodies.

The joint can even be outside the body. The square in the middle is joined to the ground body, but the anchor point of the ground is outside the shape limits of the ground.

Apart from pinning the bodies together, the revolute joint also allows for a constant rotation of either of the bodies. The fan on the right is rotating non stop.

Revolute joint can be created like this

```//create revolute joint between a and b
var joint_def = new b2RevoluteJointDef();
joint_def.bodyA = a;
joint_def.bodyB = b;

//connect the centers - center in local coordinate - relative to body is 0,0
joint_def.localAnchorA = new b2Vec2(-2, 0);
joint_def.localAnchorB = new b2Vec2(0, 0);

//add the joint to the world
world.CreateJoint(joint_def);
```

Code

```/**
RevoluteJoint in box2d in javascript
Silver Moon ([email protected])
*/
var b2Vec2 = Box2D.Common.Math.b2Vec2
, b2AABB = Box2D.Collision.b2AABB
, b2BodyDef = Box2D.Dynamics.b2BodyDef
, b2Body = Box2D.Dynamics.b2Body
, b2FixtureDef = Box2D.Dynamics.b2FixtureDef
, b2Fixture = Box2D.Dynamics.b2Fixture
, b2World = Box2D.Dynamics.b2World
, b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape
, b2CircleShape = Box2D.Collision.Shapes.b2CircleShape
, b2DebugDraw = Box2D.Dynamics.b2DebugDraw
, b2MouseJointDef =  Box2D.Dynamics.Joints.b2MouseJointDef
, b2RevoluteJointDef =  Box2D.Dynamics.Joints.b2RevoluteJointDef
, b2Shape = Box2D.Collision.Shapes.b2Shape
;

var world;
var ctx;
var canvas_width;
var canvas_height;
var mouse_pressed = false;
var mouse_joint = false;
var mouse_x, mouse_y;

//box2d to canvas scale , therefor 1 metre of box2d = 30px of canvas :)
var scale = 30;

//Draw a world - this method is called in a loop to redraw the world
function draw_world(world, context)
{
//convert the canvas coordinate directions to cartesian coordinate direction by translating and scaling
ctx.save();
ctx.translate(0 , canvas_height);
ctx.scale(1 , -1);
world.DrawDebugData();
ctx.restore();

ctx.font = 'bold 18px arial';
ctx.textAlign = 'center';
ctx.fillStyle = '#fff';
ctx.fillText('Box2d Revolute Joint example in Javascript', canvas_width/2, 20);
ctx.font = 'bold 14px arial';
ctx.fillText('Click on any object and drag it', canvas_width/2, 40);
}

//Create box2d world object
function createWorld()
{
//Gravity vector x, y - 10 m/s2 - thats earth!!
var gravity = new b2Vec2(0, -10);

world = new b2World(gravity , true );

//setup debug draw
var debugDraw = new b2DebugDraw();
debugDraw.SetSprite(document.getElementById("canvas").getContext("2d"));
debugDraw.SetDrawScale(scale);
debugDraw.SetFillAlpha(0.5);
debugDraw.SetLineThickness(1.0);
debugDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit);

world.SetDebugDraw(debugDraw);

//create some objects
ground = createBox(world, 10, 1, 18 , 1, {type : b2Body.b2_staticBody});

var a = createBox(world, 6.50, 3.80, 5 , 1);
var b = createBall(world, 8.5, 5, 1);

//create revolute joint between a and b
var joint_def = new b2RevoluteJointDef();
joint_def.bodyA = a;
joint_def.bodyB = b;

//connect the centers - center in local coordinate - relative to body is 0,0
joint_def.localAnchorA = new b2Vec2(-2, 0);
joint_def.localAnchorB = new b2Vec2(0, 0);

//add the joint to the world
world.CreateJoint(joint_def);

var c = createBall(world, 8.5, 5, 1);
joint_def.bodyA = a;
joint_def.bodyB = c;

//connect the centers - center in local coordinate - relative to body is 0,0
joint_def.localAnchorA = new b2Vec2(2, 0);
joint_def.localAnchorB = new b2Vec2(0, 0);

//add the joint to the world
world.CreateJoint(joint_def);

var a = createBox(world, 6.50, 3.80, 5 , 1);
var c = createBall(world, 4, 10, 1, {type : b2Body.b2_staticBody});

joint_def.bodyA = a;
joint_def.bodyB = c;

//connect the centers - center in local coordinate - relative to body is 0,0
joint_def.localAnchorA = new b2Vec2(2, 0);
joint_def.localAnchorB = new b2Vec2(0, 0);

//add the joint to the world
world.CreateJoint(joint_def);

//right side spinner
var a = createBox(world, 6.50, 3.80, 8 , 0.5);
var c = createBall(world, 16, 10, 0.5, {type : b2Body.b2_staticBody});

joint_def.bodyA = a;
joint_def.bodyB = c;

//connect the centers - center in local coordinate - relative to body is 0,0
joint_def.localAnchorA = new b2Vec2(0, 0);
joint_def.localAnchorB = new b2Vec2(0, 0);

joint_def.enableMotor = true;
joint_def.motorSpeed = 10;
joint_def.maxMotorTorque = 50;

//add the joint to the world
world.CreateJoint(joint_def);

//joint outside ground, middle box
var a = createBox(world, 6.50, 3.80, 2, 2);
var joint_def = new b2RevoluteJointDef();
joint_def.bodyA = a;
joint_def.bodyB = ground;

//connect the centers - center in local coordinate - relative to body is 0,0
joint_def.localAnchorA = new b2Vec2(0, 0);
joint_def.localAnchorB = new b2Vec2(0, 10);

//add the joint to the world
world.CreateJoint(joint_def);

return world;
}

//Function to create a round ball, sphere like object
function createBall(world, x, y, radius, options)
{
//default setting
options = \$.extend(true, {
'density' : 1.0 ,
'friction' : 1.0 ,
'restitution' : 0.5 ,

'type' : b2Body.b2_dynamicBody
}, options);

var body_def = new b2BodyDef();
var fix_def = new b2FixtureDef();

fix_def.density = options.density || 1.0;
fix_def.friction = 0.5;
fix_def.restitution = 0.5;

fix_def.shape = shape;

body_def.position.Set(x , y);

body_def.linearDamping = 0.5;
body_def.angularDamping = 0.5;

body_def.type = options.type;
body_def.userData = options.user_data;

var b = world.CreateBody( body_def );
b.CreateFixture(fix_def);

return b;
}

//Create standard boxes of given height , width at x,y
function createBox(world, x, y, width, height, options)
{
//default setting
options = \$.extend(true, {
'density' : 1.0 ,
'friction' : 1.0 ,
'restitution' : 0.5 ,

'type' : b2Body.b2_dynamicBody
}, options);

var body_def = new b2BodyDef();
var fix_def = new b2FixtureDef();

fix_def.density = options.density;
fix_def.friction = options.friction;
fix_def.restitution = options.restitution;

fix_def.shape = new b2PolygonShape();

fix_def.shape.SetAsBox( width/2 , height/2 );

body_def.position.Set(x , y);
body_def.linearDamping = 0.5;
body_def.angularDamping = 0.5;

body_def.type = options.type;
body_def.userData = options.user_data;

var b = world.CreateBody( body_def );
var f = b.CreateFixture(fix_def);

return b;
}

/*
This method will draw the world again and again
called by settimeout , self looped
*/
function step()
{
var fps = 60;
var timeStep = 1.0/(fps * 0.8);

world.Step(timeStep , 8 , 3);
world.ClearForces();

//redraw the world
draw_world(world , ctx);

//call this function again after 1/60 seconds or 16.7ms
setTimeout(step , 1000 / fps);
}

//Convert coordinates in canvas to box2d world
function get_real(p)
{
return new b2Vec2(p.x + 0, canvas_height_m - p.y);
}

function GetBodyAtMouse(includeStatic)
{
var mouse_p = new b2Vec2(mouse_x, mouse_y);

var aabb = new b2AABB();
aabb.lowerBound.Set(mouse_x - 0.001, mouse_y - 0.001);
aabb.upperBound.Set(mouse_x + 0.001, mouse_y + 0.001);

var body = null;

// Query the world for overlapping shapes.
function GetBodyCallback(fixture)
{
var shape = fixture.GetShape();

if (fixture.GetBody().GetType() != b2Body.b2_staticBody || includeStatic)
{
var inside = shape.TestPoint(fixture.GetBody().GetTransform(), mouse_p);

if (inside)
{
body = fixture.GetBody();
return false;
}
}

return true;
}

world.QueryAABB(GetBodyCallback, aabb);
return body;
}

// main entry point
\$(function()
{
//first create the world
world = createWorld();

var canvas = \$('#canvas');
ctx = canvas.get(0).getContext('2d');

//get internal dimensions of the canvas
canvas_width = parseInt(canvas.attr('width'));
canvas_height = parseInt(canvas.attr('height'));
canvas_height_m = canvas_height / scale;

//If mouse is moving over the thing
\$(canvas).mousemove(function(e)
{
var p = get_real(new b2Vec2(e.pageX/scale, e.pageY/scale))

mouse_x = p.x;
mouse_y = p.y;

if(mouse_pressed && !mouse_joint)
{
var body = GetBodyAtMouse();

if(body)
{
//if joint exists then create
var def = new b2MouseJointDef();

def.bodyA = ground;
def.bodyB = body;
def.target = p;

def.collideConnected = true;
def.maxForce = 1000 * body.GetMass();
def.dampingRatio = 0;

mouse_joint = world.CreateJoint(def);

body.SetAwake(true);
}
}
else
{
//nothing
}

if(mouse_joint)
{
mouse_joint.SetTarget(p);
}
});

\$(canvas).mousedown(function()
{
//flag to indicate if mouse is pressed or not
mouse_pressed = true;
});

/*
When mouse button is release, mark pressed as false and delete the mouse joint if it exists
*/
\$(canvas).mouseup(function()
{
mouse_pressed = false;

if(mouse_joint)
{
world.DestroyJoint(mouse_joint);
mouse_joint = false;
}
});

//start stepping
step();
});

```
```