<html>
<head>
<title>interact Box2D demo</title>
<script type="text/javascript" src="../../build/Box2D_v2.2.1_min.js"></script>
<script type="text/javascript" src="../../helpers/MathUtil.js"></script>
<script type="text/javascript" src="../../helpers/embox2d-helpers.js"></script>
<script type="text/javascript" src="../../helpers/embox2d-html5canvas-debugDraw.js"></script>
<script type="text/javascript">
var e_shapeBit = 0x0001;
var e_jointBit = 0x0002;
var e_aabbBit = 0x0004;
var e_pairBit = 0x0008;
var e_centerOfMassBit = 0x0010;
var myDebugDraw;
var mouseJoint = null;
var run = true;
var PTM = 32;
var world = null;
var myQueryCallback;
var mouseDown = false;
var mouseJointGroundBody = null;
var Box2D;
var canvas;
var context;
var canvasOffset = {x: 0, y: 0};
var mousePosPixel = {x: 0, y: 0};
var mousePosWorld = {x: 0, y: 0};
var creating = true;
var slicing = false;
if (!Box2D) Box2D = (typeof Box2D !== 'undefined' ? Box2D : null) || Module;
window.onload = function() {
using(Box2D, "b2.+")
init();
// changeTest();
animate();
};
function init() {
myDebugDraw = getCanvasDebugDraw();
myDebugDraw.SetFlags(e_shapeBit);
world = new Box2D.b2World( new Box2D.b2Vec2(0.0, -10.0) );
world.SetDebugDraw(myDebugDraw);
mouseJointGroundBody = world.CreateBody( new Box2D.b2BodyDef() );
setup();
canvas = document.getElementById("canvas");
context = canvas.getContext( '2d' );
canvasOffset.x = canvas.width/2;
canvasOffset.y = canvas.height/2;
// canvas.addEventListener('click', function(evt) {
// onMouseClick(canvas,evt);
// }, false);
canvas.addEventListener('mousemove', function(evt) {
onMouseMove(canvas,evt);
}, false);
canvas.addEventListener('mousedown', function(evt) {
onMouseDown(canvas,evt);
}, false);
canvas.addEventListener('mouseup', function(evt) {
onMouseUp(canvas,evt);
}, false);
myDebugDraw = getCanvasDebugDraw();
myDebugDraw.SetFlags(e_shapeBit);
myQueryCallback = new Box2D.JSQueryCallback();
myQueryCallback.ReportFixture = function(fixturePtr) {
var fixture = Box2D.wrapPointer( fixturePtr, b2Fixture );
if ( fixture.GetBody().GetType() != Box2D.b2_dynamicBody ) //mouse cannot drag static bodies around
return true;
if ( ! fixture.TestPoint( this.m_point ) )
return true;
this.m_fixture = fixture;
return false;
};
}
function onMouseClick(canvas, evt) {
mouseDown = false;
if ( mouseJoint != null ) {
world.DestroyJoint(mouseJoint);
mouseJoint = null;
return;
}
updateMousePos(canvas, evt);
// console.log(mousePosWorld.x + "," + mousePosWorld.y);
{
var cshape = new b2CircleShape();
cshape.set_m_radius(0.5);
//falling shapes
var ZERO = new b2Vec2(0, 0);
var temp = new b2Vec2(mousePosWorld.x, mousePosWorld.y);
var bd = new b2BodyDef();
// bd.set_type(b2_dynamicBody);
bd.set_type(Module.b2_dynamicBody);
bd.set_position(temp);
var body = world.CreateBody(bd);
var randomValue = Math.random();
if ( randomValue < 0.2 )
body.CreateFixture(cshape, 1.0);
else
body.CreateFixture(createRandomPolygonShape(0.5), 1.0);
// temp.Set(16*(Math.random()-0.5), 4.0 + 2.5*i);
// body.SetTransform(temp, 0.0);
body.SetLinearVelocity(ZERO);
body.SetAwake(1);
body.SetActive(1);
}
}
function startMouseJoint() {
if ( mouseJoint != null )
return;
// Make a small box.
var aabb = new Box2D.b2AABB();
var d = 0.001;
aabb.set_lowerBound(new b2Vec2(mousePosWorld.x - d, mousePosWorld.y - d));
aabb.set_upperBound(new b2Vec2(mousePosWorld.x + d, mousePosWorld.y + d));
// Query the world for overlapping shapes.
myQueryCallback.m_fixture = null;
myQueryCallback.m_point = new Box2D.b2Vec2(mousePosWorld.x, mousePosWorld.y);
world.QueryAABB(myQueryCallback, aabb);
if (myQueryCallback.m_fixture)
{
var body = myQueryCallback.m_fixture.GetBody();
var md = new Box2D.b2MouseJointDef();
md.set_bodyA(mouseJointGroundBody);
md.set_bodyB(body);
md.set_target( new Box2D.b2Vec2(mousePosWorld.x, mousePosWorld.y) );
md.set_maxForce( 1000 * body.GetMass() );
md.set_collideConnected(true);
mouseJoint = Box2D.castObject( world.CreateJoint(md), Box2D.b2MouseJoint );
body.SetAwake(true);
}
}
function verticesToBox2D(vertices) {
var ret = [];
for (var i = 0; i < vertices.length; i++) {
var p = vertices[i];
ret.push( new b2Vec2(p.x, p.y) );
}
return ret;
}
var mouseDownLst = [];
var curObj = null;
var hoveringObj = null;
var hoverIndex;
var laserSegment;
var laserLocked = false;
var slicingPoints = [];
var laserColor = "#aa00aa";
var affectedByLaser = [];
var entryPoints = [];
var deletingBodies = [];
function onMouseDown(canvas, evt) {
updateMousePos(canvas, evt);
if ( !mouseDown )
startMouseJoint();
mouseDown = true;
if ( mouseJoint != null )
return;
if (slicing) {
if (laserLocked) {
laserSegment = null;
slicingPoints = [];
debugIds = [];
// affectedByLaser = [];
entryPoints = [];
}
if (laserSegment == null) {
laserLocked = false;
laserSegment = new Box2D.b2EdgeShape();
laserSegment.p1=new Vertex(mousePosWorld.x, mousePosWorld.y);
} else {
laserSegment.p2=new Vertex(mousePosWorld.x, mousePosWorld.y);
laserSegment.Set(new b2Vec2(laserSegment.p1.x, laserSegment.p1.y), new b2Vec2(laserSegment.p2.x, laserSegment.p2.y));
laserLocked = true;
var laserFired = new Box2D.JSRayCastCallback();
var idd = 0;
laserFired.ReportFixture = function(fixture, point, normal, fraction) {
var ptr = Box2D.wrapPointer( point, Box2D.b2Vec2 );
var fixturePtr = Box2D.wrapPointer(fixture, Box2D.b2Fixture);
// console.log(ptr.get_x() + ", " + ptr.get_y() );
slicingPoints.push(copyVec2(ptr));
var p = new Vertex(ptr.get_x(), ptr.get_y());
p.id = idd;
idd++;
var affectedBody = fixturePtr.GetBody();
if (affectedBody.GetType() != Module.b2_dynamicBody) {
return;
}
console.log("laserFired.ReportFixture");
var bodyIndex = deletingBodies.indexOf(affectedBody);
if (bodyIndex < 0) {
deletingBodies.push(affectedBody);
}
var fixtureIndex = affectedByLaser.indexOf(fixturePtr);
if (fixtureIndex < 0) {
affectedByLaser.push(fixturePtr);
entryPoints.push( p );
} else {
var points = getPointsByPolygonShape(fixturePtr);
var p1 = p;
var p2 = entryPoints[fixtureIndex];
if (affectedBody.sliceDots == null) {
affectedBody.sliceDots = [];
}
affectedBody.sliceDots.push(p1, p2);
// if (affectedBody.sliceDots.indexOf(p1) < 0) {
// affectedBody.sliceDots.push(p1);
// }
// if (affectedBody.sliceDots.indexOf(p2) < 0) {
// affectedBody.sliceDots.push(p2);
// }
}
return 1;
}
world.RayCast(laserFired, laserSegment.get_m_vertex1(), laserSegment.get_m_vertex2());
world.RayCast(laserFired, laserSegment.get_m_vertex2(), laserSegment.get_m_vertex1());
}
} else if (creating) {
if (hoveringObj != null) {
var verts = [];
for (var i = hoverIndex; i < mouseDownLst.length; i++) {
verts.push( new b2Vec2( mouseDownLst[i].x, mouseDownLst[i].y ) );
}
createPolygonBody(verts);
mouseDownLst = [];
hoveringObj = null;
} else {
if (curObj != null) {
mouseDownLst.push(curObj);
curObj = null;
}
}
}
}
var debugIds = [];
function toIdString(arr) {
var lst = [];
for (var i = 0; i < arr.length; i++) {
lst.push(arr[i].id);
}
return lst.toString();
}
function createPolygonBody(verts) {
var first = copyVec2(verts[0]);
var arr2 = [];
for (var i = 0; i < verts.length; i++) {
verts[i]= new b2Vec2( verts[i].get_x() - first.get_x(), verts[i].get_y() - first.get_y() );
arr2.push( new Vertex( verts[i].get_x(), verts[i].get_y() ) );
}
var bd = new b2BodyDef();
bd.set_type(Module.b2_dynamicBody);
bd.set_position(first);
var body = world.CreateBody(bd);
var ploygons = [];
var arr3 = copyArr(arr2);
concavToConvex(arr2, ploygons);
for ( var i = 0; i < ploygons.length; i++) {
var ploygon = ploygons[i];
var isClockwise = isClockwiseSquence(ploygon);
//because we flipped coordinate for one time before, context.scale(1,-1);
isClockwise = !isClockwise;
if (isClockwise) {
ploygon.reverse();
}
var shape = createPolygonShape(verticesToBox2D(ploygon) );
body.CreateFixture(shape, 1.0);
}
body.userData = arr3;
}
function createSlicingBody(verts) {
var first = copyVec2(verts[0]);
for (var i = 0; i < verts.length; i++) {
verts[i]= new b2Vec2( verts[i].get_x() - first.get_x(), verts[i].get_y() - first.get_y() );
}
var shape = createPolygonShape( verts );
var bd = new b2BodyDef();
bd.set_type(Module.b2_dynamicBody);
bd.set_position(first);
var body = world.CreateBody(bd);
body.CreateFixture(shape, 1.0);
return body;
}
function onMouseMove(canvas, evt) {
if (mouseJoint != null) {
prevMousePosPixel = mousePosPixel;
updateMousePos(canvas, evt);
if ( mouseDown ) {
mouseJoint.SetTarget( new Box2D.b2Vec2(mousePosWorld.x, mousePosWorld.y) );
}
laserSegment = null;
slicingPoints = [];
mouseDownLst = [];
} else {
updateMousePos(canvas, evt);
if (slicing && !laserLocked && laserSegment != null) {
laserSegment.p2=new Vertex(mousePosWorld.x, mousePosWorld.y);
} else if (creating) {
curObj = {};
curObj.x = mousePosWorld.x;
curObj.y = mousePosWorld.y;
hoveringObj = null;
if (mouseDownLst.length < 3) return;
for (var i = 0; i < mouseDownLst.length - 2; i++) {
var p = mouseDownLst[i];
var dx = p.x - curObj.x;
var dy = p.y - curObj.y;
if (dx * dx + dy * dy <= 100 / (PTM*PTM)) {
hoveringObj = p;
hoverIndex = i;
break;
}
}
}
}
}
function onMouseUp(canvas, evt) {
mouseDown = false;
if ( mouseJoint != null ) {
world.DestroyJoint(mouseJoint);
mouseJoint = null;
return;
}
}
function updateMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
mousePosPixel = {
x: evt.clientX - rect.left,
y: canvas.height - (evt.clientY - rect.top)
};
mousePosWorld = getWorldPointFromPixelPoint(mousePosPixel);
}
function getWorldPointFromPixelPoint(pixelPoint) {
return {
x: (pixelPoint.x - canvasOffset.x)/PTM,
y: (pixelPoint.y - (canvas.height - canvasOffset.y))/PTM
};
}
function setup() {
var bd_ground = new b2BodyDef();
var groundBody = world.CreateBody(bd_ground);
//ground edges
var shape0 = new b2EdgeShape();
shape0.Set(new b2Vec2(-40.0, -6.0), new b2Vec2(40.0, -6.0));
groundBody.CreateFixture(shape0, 0.0);
shape0.Set(new b2Vec2(-9.0, -6.0), new b2Vec2(-9.0, -4.0));
groundBody.CreateFixture(shape0, 0.0);
shape0.Set(new b2Vec2(9.0, -6.0), new b2Vec2(9.0, -4.0));
groundBody.CreateFixture(shape0, 0.0);
}
function step(timestamp) {
world.Step(1/60, 3, 2);
draw();
}
function draw() {
//black background
context.fillStyle = 'rgb(0,0,0)';
context.fillRect( 0, 0, canvas.width, canvas.height );
context.save();
context.translate(canvasOffset.x, canvasOffset.y);
context.scale(1,-1);
context.scale(PTM,PTM);
context.lineWidth /= PTM;
drawAxes(context);
context.fillStyle = 'rgb(255,255,0)';
world.DrawDebugData();
if (slicing) {
if (laserSegment != null && laserSegment.p2 != null) {
context.moveTo(laserSegment.p1.x, laserSegment.p1.y);
context.lineTo(laserSegment.p2.x, laserSegment.p2.y);
context.strokeStyle = laserColor;
context.stroke();
}
for (var i = 0; i < slicingPoints.length; i++) {
var p = slicingPoints[i];
context.beginPath();
context.arc(p.get_x() , p.get_y() , 5/PTM, 0, Math.PI * 2, true);
context.strokeStyle = "#00aa00";
context.stroke();
context.closePath();
}
var addBodies = [];
for (var i = 0; i < deletingBodies.length; i++) {
var body = deletingBodies[i];
var vertices = getPointsByPolygonBody(body);
// var a = 1;
// debugIds = body.sliceDots;
if (vertices != null && vertices.length > 2) {
var slicingDots = filterOutInnerPoints(vertices, body.sliceDots);
sliceConcavPolygon(vertices, slicingDots, 0, addBodies);
}
world.DestroyBody(body);
}
for (var i = 0; i < addBodies.length; i++) {
// createSlicingBody(verticesToBox2D(addBodies[i]));
createPolygonBody(verticesToBox2D(addBodies[i]));
}
affectedByLaser = [];
deletingBodies = [];
context.scale(1,-1);
context.scale(1/PTM,1/PTM);
for (var i = 0; i < debugIds.length; i++) {
var vertex = debugIds[i];
context.font = "12px Courier New";
context.fillStyle = "white";
context.fillText(vertex.id, vertex.x*PTM, -vertex.y*PTM);
}
context.scale(1,-1);
context.scale(PTM,PTM);
} else if (creating) {
if (mouseDownLst.length > 0) {
var arr2 = mouseDownLst;
if (curObj != null) {
arr2 = mouseDownLst.concat([curObj]);
}
for (var i = 0; i < arr2.length; i++) {
var p1 = arr2[i];
var p2 = i < arr2.length - 1 ? arr2[i+1] : null;
if (p2 != null) {
context.beginPath();
context.arc(p1.x, p1.y, 5/PTM, 0, Math.PI * 2, true);
context.closePath();
context.strokeStyle = "#00aa00";
context.stroke();
context.beginPath();
context.moveTo(p1.x,p1.y);
context.lineTo(p2.x,p2.y);
context.stroke();
}
}
if (hoveringObj != null) {
context.beginPath();
context.arc(hoveringObj.x, hoveringObj.y, 10/PTM, 0, Math.PI * 2, true);
context.closePath();
context.strokeStyle = "orange";
context.stroke();
}
}
}
if ( mouseJoint != null ) {
//mouse joint is not drawn with regular joints in debug draw
var p1 = mouseJoint.GetAnchorB();
var p2 = mouseJoint.GetTarget();
context.strokeStyle = 'rgb(204,204,204)';
context.beginPath();
context.moveTo(p1.get_x(),p1.get_y());
context.lineTo(p2.get_x(),p2.get_y());
context.stroke();
}
context.restore();
}
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function( callback ){
window.setTimeout(callback, 1000 / 60);
};
})();
function animate() {
if ( run )
requestAnimFrame( animate );
step();
}
function pause() {
run = !run;
if (run)
animate();
}
function checkCreating() {
creating = document.getElementById('creatingCheck').checked;
if (creating) {
slicing = false;
laserSegment = null;
slicingPoints = [];
document.getElementById('slicingCheck').checked = false;
} else {
mouseDownLst = [];
}
}
function checkSlicing() {
slicing = document.getElementById('slicingCheck').checked;
if (slicing) {
creating = false;
mouseDownLst = [];
document.getElementById('creatingCheck').checked = false;
} else {
laserSegment = null;
slicingPoints = [];
}
}
</script>
</head>
<body>
<div style="text-align:center">
<h2>Jerome.huang Box2D demo</h2>
This demo uses JavaScript generated from the Box2D C++ source code by the <a href="https://github.com/kripken/emscripten/wiki">emscripten</a> compiler.<br>
See <a href="https://github.com/kripken/box2d.js">https://github.com/kripken/box2d.js</a> for more details.<br>
<br>
<!--
If you change the size of the canvas, you'll also need to change
the value of 'viewCenterPixel' in embox2d-html5canvas-testbed.js
-->
<div style="margin:auto;width:640px;padding:2px;border:1px solid #888;text-align:left">
<!--<canvas id="canvas" width="480" height="320" tabindex='1'></canvas>-->
<canvas id="canvas" width="640" height="480" tabindex='1'></canvas>
<br>
Operation flags:<br>
<input id="creatingCheck" type="checkbox" onclick="checkCreating();" checked="true">creating<br>
<input id="slicingCheck" type="checkbox" onclick="checkSlicing();">slicing laser<br>
<br>
</div>
</div>
</div>
</body>
</html>