How to make a Weld Joint in Box2D in Javascript

By | January 13, 2023

Weld joint in box2d

The weld joint can be used to join to bodies tightly at a common point. Here is an example

A weld joint is created like this

//create distance joint between b and c
var joint_def = new b2WeldJointDef();
joint_def.bodyA = b;
joint_def.bodyB = c;
    
//connect the centers - center in local coordinate - relative to body is 0,0
joint_def.localAnchorA = new b2Vec2(-0.5, -0.5);
joint_def.localAnchorB = new b2Vec2(0.5, 0.5);
   
//difference in angle of each body
joint_def.referenceAngle = 0 * Math.PI / 3;
    
//add the joint to the world
world.CreateJoint(joint_def);

The joint definition needs to know the bodies to connect and the coordinates on each of them where the weld is to be done. It also has a referenceAngle parameter that defines the angle between the 2 bodies after being welded. Play with the referenceAngle to see the effect.

Source

/**
	Weld Joint 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
	, b2WeldJointDef =  Box2D.Dynamics.Joints.b2WeldJointDef
	, 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 Weld 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, 8.5, 2, 16 , 1, {type : b2Body.b2_staticBody});
	var a = createBox(world, 6.50, 3.80, 1 , 1);
	var b = createBox(world, 10.50, 3.80, 1 , 1);
	var c = createBox(world, 10.50, 3.80, 1 , 1);

	//create distance joint between b and c
	var joint_def = new b2WeldJointDef();
	joint_def.bodyA = b;
	joint_def.bodyB = c;
	
	//connect the centers - center in local coordinate - relative to body is 0,0
	joint_def.localAnchorA = new b2Vec2(-0.5, -0.5);
	joint_def.localAnchorB = new b2Vec2(0.5, 0.5);
	
	//difference in angle of each body
	joint_def.referenceAngle = 0 * Math.PI / 3;
	
	//add the joint to the world
	world.CreateJoint(joint_def);
	
	var a = createBall(world, 3.50, 3.80, 2);
	var b = createBox(world, 10.50, 3.80, 1 , 1);
	
	//create distance joint between b and c
	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);
	world.CreateJoint(joint_def);
	
	var b = createBox(world, 10.50, 3.80, 1 , 1);
	
	//create distance joint between b and c
	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(0, 2);
	joint_def.localAnchorB = new b2Vec2(0, 0);
	world.CreateJoint(joint_def);
	
	var b = createBox(world, 10.50, 3.80, 1 , 1);
	
	//create distance joint between b and c
	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);
	world.CreateJoint(joint_def);
	
	var b = createBox(world, 10.50, 3.80, 1 , 1);
	
	//create distance joint between b and c
	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(0, -2);
	joint_def.localAnchorB = new b2Vec2(0, 0);
	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;
	
	var shape = new b2CircleShape(radius);
	fix_def.shape = shape;
	
	body_def.position.Set(x , y);
	
	body_def.linearDamping = 0.0;
	body_def.angularDamping = 0.0;
	
	body_def.type = b2Body.b2_dynamicBody;
	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.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);
	
	//move the box2d world ahead
	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();
});

Resources

Download box2d javascript port here
http://code.google.com/p/box2dweb/

For documentation check the flash reference
http://www.box2dflash.org/docs/2.1a/reference/

About Silver Moon

A Tech Enthusiast, Blogger, Linux Fan and a Software Developer. Writes about Computer hardware, Linux and Open Source software and coding in Python, Php and Javascript. He can be reached at [email protected].

3 Comments

How to make a Weld Joint in Box2D in Javascript
  1. t tv

    Hi Silver Moon,

    You have excellent tutorials on box2d. I have been folowing them. How ever i am facing some problem getting weld joints working.

    I have attached the image. Why is the joint not holding items together. I the image1 , i have applied a -x gravity. I do not want the items to move because of it. I want them to be stacked on each other so that they move as one complete unit. Can you help me on this. Following is the code i am using for making joint.

    jointArray[jointcount] = new b2WeldJointDef();

    jointArray[jointcount].Initialize(firstPositionShape.body, positionShape.body, firstPositionShape.body.GetWorldCenter());

    this.getWorld().box2dWorld.CreateJoint(jointArray[jointcount]);

    jointArray[jointcount].collideConnected = true;

    jointcount++;

    Another prob i see while using weld joints. is that items smudge into each other like i have shown in image2. Could you please help me on either of this.

    1. Silver Moon

      I created weld joint by setting the individual properties of b2WeldJointDef object.

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

      //difference in angle of each body
      joint_def.referenceAngle = 0 * Math.PI / 3;

      I have not tried the “Initialize” method and am not sure how exactly is it supposed to work.
      Can you try using the other method, that is by setting localAnchorA, localAnchorB and referenceAngle properties.

      Smudging is fine if the local anchors are inside the boundaries of the object. In the above shown example the 4 small squares are overlapping with the large circle.

      Need to see your full code, or a live demo to check the issues.

Leave a Reply

Your email address will not be published. Required fields are marked *