b2Settings.b2_maxPolyVertices = 50; /* demos/demo_base.js */ function createWorld() { var worldAABB = new b2AABB(); worldAABB.minVertex.Set(-1000, -1000); worldAABB.maxVertex.Set(1000, 1000); var gravity = new b2Vec2(0, 300); var doSleep = true; var world = new b2World(worldAABB, gravity, doSleep); createGround(world); return world; } function createGround(world) { var groundSd = new b2BoxDef(); groundSd.extents.Set(2000, 50); groundSd.restitution = 0.2; var groundBd = new b2BodyDef(); groundBd.AddShape(groundSd); groundBd.position.Set(-500, 547); return world.CreateBody(groundBd) } function createBall(world, x, y) { var ballSd = new b2CircleDef(); ballSd.density = 1.0; ballSd.radius = 20; ballSd.restitution = 1.0; ballSd.friction = 0; var ballBd = new b2BodyDef(); ballBd.AddShape(ballSd); ballBd.position.Set(x,y); return world.CreateBody(ballBd); } function createBox(world, x, y, width, height, fixed) { if (typeof(fixed) == 'undefined') fixed = true; var boxSd = new b2BoxDef(); if (!fixed) boxSd.density = 1.0; boxSd.extents.Set(width, height); var boxBd = new b2BodyDef(); boxBd.AddShape(boxSd); boxBd.position.Set(x,y); return world.CreateBody(boxBd) } /* physicSketch original part */ function createBall(world, x, y, rad, fixed) { var ballSd = new b2CircleDef(); if (!fixed) ballSd.density = 1.0; ballSd.radius = rad || 10; ballSd.restitution = 0.2; var ballBd = new b2BodyDef(); ballBd.AddShape(ballSd); ballBd.position.Set(x,y); return world.CreateBody(ballBd); }; var Stroke = Class.create({ initialize: function(x, y) { this.points = $A([[0, 0]]); this.baseX = x; this.baseY = y; this.topIndex = 0; this.angles = $A(); this.body = null; this.bodyType = null; this.selected = false; this.draggedPoint = null; this.cullPoints = null; this.fillStyle = '#FFFFFF'; }, getFillStyle: function() { //return this.selected ? '#EEEEFF' : this.fillStyle; return this.fillStyle; }, getStrokeStyle: function() { // not used return this.selected ? '#FF0000' : this.strokeStyle; }, addPoint: function(x, y) { x -= this.baseX; y -= this.baseY; this.points.push([x, y]); if (this.points[this.topIndex][1] < y) { this.topIndex = this.points.length - 1; } }, becomeBodyIn: function(w) { if (this.points.length < 3) return; var firstPoint = new b2Vec2(this.points.first()[0], this.points.first()[1]); var lastPoint = new b2Vec2(this.points.last()[0], this.points.last()[1]); firstPoint.Subtract(lastPoint); if (firstPoint.Length() < 30.0) { this.drawShape(); } else { this.drawLines(); } }, drawShape: function() { var ps = $A(); for (var i = 0; i < this.points.length; i++) { ps.push(this.points[(this.topIndex + i) % this.points.length]); } ps.push(this.points[this.topIndex]); var cw = $A(); var cwAngles = $A(); for (var i = 0; i < ps.length; i++) { this.addConvexPoint(cw, ps[i], cwAngles); } var ccw = $A(); var ccwAngles = $A(); for (var i = ps.length - 1; 0 <= i; i--) { this.addConvexPoint(ccw, ps[i], ccwAngles); } var massPoints; if (cw.length < ccw.length) { massPoints = ccw; this.angles = ccwAngles; } else { massPoints = cw; this.angles = cwAngles; } massPoints.pop(); this.cullPoints = $A(); if (massPoints.length < b2Settings.b2_maxPolyVertices) { this.cullPoints = massPoints; } else { for (var i = 0; i < massPoints.length - 1; i++) { if (i % Stroke.INTERVAL == 0) { this.cullPoints.push(massPoints[i]); } } this.cullPoints.push(massPoints[massPoints.length - 1]); } if (2 < this.cullPoints.length) { this.body = this.createPoly(world, this.baseX, this.baseY, this.cullPoints); this.bodyType = 'poly'; } }, addConvexPoint: function(ary, p, angleAry) { if (ary.length < 2) { ary.push(p); return; } var p1 = ary[ary.length-2]; var p2 = ary[ary.length-1]; var p3 = p; var v12 = new b2Vec2(p2[0] - p1[0], p2[1] - p1[1]); var v13 = new b2Vec2(p3[0] - p1[0], p3[1] - p1[1]); if (0 < b2Math.b2CrossVV(v12, v13)) { var angle = Math.acos(b2Math.b2CrossVV(v12, v13) / (v13.Length() * v12.Length())); angleAry.push(angle); ary.push(p); } else { angleAry.pop(); ary.pop(); this.addConvexPoint(ary, p, angleAry); } }, drawLines: function() { this.cullPoints = $A(); for (var i = 0; i < this.points.length - 1; i++) { //if (i % Stroke.INTERVAL == 0) { if (i % 10 == 0) { this.cullPoints.push(this.points[i]); } } if (this.cullPoints.last()[0] != this.points.last()[0] || this.cullPoints.last()[1] != this.points.last()[1]) { this.cullPoints.push(this.points.last()); } this.body = this.createLines(world, this.baseX, this.baseY, this.cullPoints); this.bodyType = 'lines'; }, createPoly: function(world, x, y, points) { var polySd = new b2PolyDef(); polySd.density = 1.0; polySd.vertexCount = points.length; for (var i = 0; i < points.length; i++) { polySd.vertices[i].Set(points[i][0], points[i][1]); } var polyBd = new b2BodyDef(); polyBd.AddShape(polySd); polyBd.position.Set(x, y); return world.CreateBody(polyBd) }, createLines: function(world, x, y, points) { var linesBd = new b2BodyDef(); for (var i = 1; i < points.length; i++) { var p1 = points[i-1]; var p2 = points[i]; var w = new b2Vec2(p2[1] - p1[1], p1[0] - p2[0]); w.Normalize(); w.Multiply(10); var lineSd = new b2PolyDef(); lineSd.density = 1.0; lineSd.vertexCount = 4; lineSd.vertices[0].Set(p1[0], p1[1]); lineSd.vertices[1].Set(p2[0], p2[1]); lineSd.vertices[2].Set(p2[0] - w.x, p2[1] - w.y); lineSd.vertices[3].Set(p1[0] - w.x, p1[1] - w.y); linesBd.AddShape(lineSd); } linesBd.position.Set(x, y); var body = world.CreateBody(linesBd); return body; }, hasBody: function() { return this.body != null; } }); Stroke.INTERVAL = 3; /* demos/demos.js */ var world = createWorld(); var ctx; var canvasWidth; var canvasHeight; var canvasTop; var canvasLeft; var strokes = $A(); var trackingStroke = null; var selectedStroke = null; var draggingStroke = null; var draggedMouseXY = null; var clocking = true; var bodyVisibility = false; var usePin = true; var trackingStrokeStyle = '#FFFF00'; var jointStrokeStyle = '#8888FF'; var strokeStyle = '#A9A9A9'; var selectedStrokeStyle = '#FF0000'; function getStrokeStyle(selected) { return selected ? selectedStrokeStyle : strokeStyle; } function changeStrokeColor(code) { strokeStyle = code; } function changeSelectedStrokeColor(code) { selectedStrokeStyle = code; } function toggleTimer(elm) { clocking = !clocking; if (clocking) { elm.innerHTML = 'stop timer'; } else { elm.innerHTML = 'start timer'; } } function toggleBodyVisibility(elm) { bodyVisibility = !bodyVisibility; if (bodyVisibility) { elm.innerHTML = 'hide physical body'; } else { elm.innerHTML = 'show physical body'; } } function showInformation(stroke) { var body = stroke.body; var position = body.GetCenterPosition(); $('position-x').innerHTML = Math.floor(position.x); $('position-y').innerHTML = Math.floor(position.y); //$('angle').innerHTML = Math.floor(body.m_rotation * 180.0 % 360); $('angle').innerHTML = Math.floor(body.m_rotation * 60.0 % 360); //TODO: what's 60.0? $('body-color').value = stroke.fillStyle; } function deleteSelectedBody() { if (!selectedStroke) return; var newStrokes = $A(); for (var i = 0; i < strokes.length; i++) { var stroke = strokes[i]; if (selectedStroke == stroke) { world.DestroyBody(stroke.body); } else { newStrokes.push(stroke); } } strokes = newStrokes; } function togglePin(elm) { usePin = !usePin; if (usePin) { elm.innerHTML = 'Not Use Pin'; } else { elm.innerHTML = 'Use Pin'; } } function changeFillColor(code) { selectedStroke.fillStyle = code; } function drawStrokes(context) { function pointInWorld(stroke, index) { var body = stroke.body; var shape = body.GetShapeList(); var point = stroke.points[index]; var vec = new b2Vec2(point[0], point[1]); var baseVec = body.m_center; var tmpPoint = b2Math.SubtractVV(vec, baseVec); return b2Math.AddVV(body.m_position, b2Math.b2MulMV(body.m_R, tmpPoint)); } context.lineWidth = 5; for (var i = 0; i < strokes.length; i++) { //context.strokeStyle = strokes[i].getStrokeStyle(); context.strokeStyle = getStrokeStyle(strokes[i].selected); context.fillStyle = strokes[i].getFillStyle(); var point = pointInWorld(strokes[i], 0); context.beginPath(); context.moveTo(point.x, point.y); for (var j = 1; j < strokes[i].points.length; j++) { point = pointInWorld(strokes[i], j); context.lineTo(point.x, point.y); } if (strokes[i].bodyType == 'poly') { point = pointInWorld(strokes[i], 0); context.lineTo(point.x, point.y); context.fill(); context.stroke(); } else { context.stroke(); } } } function drawTracking(context) { if (!trackingStroke) return; context.strokeStyle = trackingStrokeStyle; context.lineWidth = 5; var points = trackingStroke.points; if (points.length < 2) return; context.beginPath(); context.moveTo(trackingStroke.baseX + points[0][0], trackingStroke.baseY + points[0][1]); for (var i = 1; i < points.length; i++) { context.lineTo(trackingStroke.baseX + points[i][0], trackingStroke.baseY + points[i][1]); } context.stroke(); } function drawJoints(context) { for (var joint = world.m_jointList; joint; joint = joint.m_next) { var p = joint.GetAnchor1(); context.strokeStyle = jointStrokeStyle; context.lineWidth = 3; context.beginPath(); context.moveTo(p.x - 5, p.y - 5) context.lineTo(p.x + 5, p.y + 5) context.stroke(); context.beginPath(); context.moveTo(p.x + 5, p.y - 5) context.lineTo(p.x - 5, p.y + 5) context.stroke(); } } function jointStrokesAt(stroke1, stroke2, x, y) { var jointDef = new b2RevoluteJointDef(); jointDef.anchorPoint.Set(x, y); jointDef.body1 = stroke1.body; jointDef.body2 = stroke2 ? stroke2.body : world.GetGroundBody(); world.CreateJoint(jointDef); } function step(cnt) { cnt = cnt || 0; var stepping = false; var timeStep = 1.0/60; var iteration = 1; if (clocking && !trackingStroke) { world.Step(timeStep, iteration); $('timer').style.WebkitTransform = 'rotate(' + ((cnt * 2) % 360) + 'deg)'; cnt++; } ctx.clearRect(0, 0, canvasWidth, canvasHeight); drawStrokes(ctx); drawTracking(ctx); drawJoints(ctx); if (selectedStroke) { $('body-inspector').show(); showInformation(selectedStroke); } else { $('body-inspector').hide(); } if (bodyVisibility) drawWorld(world, ctx); setTimeout('step(' + cnt + ')', 1); } Event.observe(window, 'load', function() { ctx = $('canvas').getContext('2d'); var canvasElm = $('canvas'); canvasWidth = parseInt(canvasElm.width); canvasHeight = parseInt(canvasElm.height); canvasTop = parseInt(canvasElm.style.top); canvasLeft = parseInt(canvasElm.style.left); function getXY(e) { return [Event.pointerX(e) - canvasLeft, Event.pointerY(e) - canvasTop]; } function strokesAt(xy) { var strokesAtThisPoint = $A(); for (var i = 0; i < strokes.length; i++) { if (!strokes[i].body) continue; var test = false; for (var shape = strokes[i].body.GetShapeList(); shape != null; shape = shape.GetNext()) { if (shape.TestPoint(new b2Vec2(xy[0], xy[1]))) { test = true; } } if (test) strokesAtThisPoint.push(strokes[i]); } return strokesAtThisPoint; } function clearSelectStroke() { for (var i = 0; i < strokes.length; i++) { if (!strokes[i].body) continue; strokes[i].selected = false; } selectedStroke = null; } function selectStroke(xy) { clearSelectStroke(); var selectedStrokes = strokesAt(xy); if (selectedStrokes.length != 0) { selectedStroke = selectedStrokes.last(); selectedStroke.selected = true; } } Event.observe('canvas', 'mousedown', function(e) { var xy = getXY(e); var strokesAtThisPoint = strokesAt(xy); if (selectedStroke && !clocking && strokesAtThisPoint.length != 0) { var clickSelectedStroke = false; for (var i = 0; i < strokesAtThisPoint.length; i++) { clickSelectedStroke = clickSelectedStroke || (strokesAtThisPoint[i] == selectedStroke); } if (clickSelectedStroke) { draggedMouseXY = xy; selectedStroke.draggedPoint = selectedStroke.body.GetCenterPosition().Copy(); } else { trackingStroke = new Stroke(xy[0], xy[1]); } } else { trackingStroke = new Stroke(xy[0], xy[1]); } }); Event.observe('canvas', 'mousemove', function(e) { var xy = getXY(e); if (selectedStroke && !clocking && draggedMouseXY) { if (selectedStroke.draggedPoint) { var selectedBody = selectedStroke.body; var nextPosition = selectedStroke.draggedPoint.Copy(); nextPosition.Add(new b2Vec2(xy[0] - draggedMouseXY[0], xy[1] - draggedMouseXY[1])); selectedBody.SetCenterPosition(nextPosition, selectedBody.m_rotation); } } else if (trackingStroke) { trackingStroke.addPoint(xy[0], xy[1]); } }); Event.observe('canvas', 'mouseup', function(e) { var xy = getXY(e); if (selectedStroke && !clocking && draggedMouseXY) { if (draggedMouseXY[0] == xy[0] && draggedMouseXY[1] == xy[1]) { if (usePin) { var strokesAtThisPoint = strokesAt(xy); if (0 < strokesAtThisPoint.length) { jointStrokesAt(strokesAtThisPoint[0], strokesAtThisPoint[1], xy[0], xy[1]); } } else { selectStroke(xy); } } draggedMouseXY = null; selectedStroke.draggedPoint = null; } else if (trackingStroke) { if (trackingStroke.points.length < 2) { trackingStroke = null; selectStroke(xy); } else { trackingStroke.addPoint(xy[0], xy[1]); trackingStroke.becomeBodyIn(world); if (trackingStroke.hasBody()) strokes.push(trackingStroke); trackingStroke = null; } } }); step(); });