Box2D Backlash

by Yukulele
♥0 | Line 532 | Modified 2009-05-30 18:56:47 | MIT License
play

ActionScript3 source code

/**
 * Copyright Yukulele ( http://wonderfl.net/user/Yukulele )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/n9mF
 */

package {
	import adobe.utils.ProductManager;
	import Box2D.Dynamics.Joints.b2DistanceJointDef;
	import Box2D.Dynamics.Joints.b2JointDef;
	import Box2D.Dynamics.Joints.b2JointEdge;
	import Box2D.Dynamics.Joints.b2MouseJoint;
	import Box2D.Dynamics.Joints.b2MouseJointDef;
	import Box2D.Dynamics.Joints.b2RevoluteJointDef;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.utils.Timer;
	import flash.events.TimerEvent;
	import Box2D.Dynamics.*;
	import Box2D.Collision.*;
	import Box2D.Collision.Shapes.*;
	import Box2D.Common.Math.*;
	import flash.utils.getTimer;
	import net.hires.debug.Stats;
	[SWF(backgroundColor=0x000000,frameRate=40)]
	public class Main extends Sprite {
		private var the_world:b2World;
		private var timerOld:int;
		private var time_count:Timer = new Timer(100);
		private var mouseJoint:b2MouseJoint;
		private var mousePVec:b2Vec2 = new b2Vec2();
		private var myBoundaryListener:BoundaryListener = new BoundaryListener();
		private const echelle:Number = 10;
		public function Main() {
			stage.frameRate = 40;
			timerOld = getTimer();
			var environment:b2AABB = new b2AABB();
			environment.lowerBound.Set(0, 0);
			environment.upperBound.Set(46, 46);
			var gravity:b2Vec2=new b2Vec2(0.0,9.81);
			the_world=new b2World(environment,gravity,false);
			var debug_draw:b2DebugDraw = new b2DebugDraw();
			var debug_sprite:Sprite = new Sprite();
			addChild(debug_sprite);
			debug_draw.m_sprite=debug_sprite;
			debug_draw.m_drawScale=echelle;
			debug_draw.m_fillAlpha = 0.5;
			debug_draw.m_lineThickness=0;
			debug_draw.m_drawFlags =
				//b2DebugDraw.e_aabbBit|
				//b2DebugDraw.e_centerOfMassBit|
				//b2DebugDraw.e_coreShapeBit|
				b2DebugDraw.e_jointBit|
				//b2DebugDraw.e_obbBit|
				//b2DebugDraw.e_pairBit|
				b2DebugDraw.e_shapeBit |
				0;
			the_world.SetDebugDraw(debug_draw);
			the_world.m_allowSleep = true;
			
			var nbr:int = 9;
			var rayon:Number = .5;
			/*for (var i:int = 0; i < nbr; i++)
			{
				var x:Number = 8.25+(i - nbr / 2) * rayon*2;
				var cercle:b2Body = cree_cercle(x, 9, rayon * 1);
				var jointDist:b2DistanceJointDef = new b2DistanceJointDef();
				jointDist.Initialize(final_body, cercle, new b2Vec2(x, 3.5), cercle.GetWorldCenter());
				jointDist.frequencyHz = 0;
				//jointDist.length = 2;
				jointDist.collideConnected = true;
				the_world.CreateJoint(jointDist);
			}			*/
			var i:int;
			cree_polygone(
			{
				x:[ 0, 0,46,46,0,1,45,45, 1, 1],
				y:[ 0,46,46, 0, 0, 1, 1,45,45, 1]
			},0,0,true);
			cree_polygone(
			{
				x:[0,2,1,1,4,4,3,5,5,0],
				y:[4,4,3,1,1,3,4,4,0,0]
			},7, 5);
			cree_polygone(
			{
				x:[0,-2,-1,-1,-4,-4,-3,-5,-5,0],
				y:[-4,-4,-3,-1,-1,-3,-4,-4,0,0]
			},12,15);
			cree_polygone(
			{
				x:[1,2,2,1,2.5,4,3,3,4,2.5],
				y:[2,3,7,8,7.5,8,7,3,2,2.5]
			},7, 5);
			
			
		/////////
			cree_polygone(
			{
				x:[0,2,1,1,4,4,3,5,5,0],
				y:[4,4,3,1,1,3,4,4,0,0]
			},17, 5);
			cree_polygone(
			{
				x:[0,-2,-1,-1,-4,-4,-3,-5,-5,0],
				y:[-4,-4,-3,-1,-1,-3,-4,-4,0,0]
			},22,15);
			cree_polygone(
			{
				x:[1.5,2,2,1.5,2.5,3.5,3,3,3.5,2.5],
				y:[2,3,7,8,7.5,8,7,3,2,2.5]
			},17, 5);
			/*cree_polygone(
			{
				x:[1.02,2.02,2.02,2.98,2.98,3.98,2.5],
				y:[2,3,4,4,3,2,2.5]
			},7, 5);*/
			/*
			for (i = 0; i < 10; i++)
			{
				cree_polygone(
				{
					x:[0,-0.75,0,1,0.25,1],
					y:[0,0.5,1,1,.5,0]
				},i+20,5);
			}
			*/
			/*
			for (i = 0; i < 7; i++)
			for (var j:int = 0; j < 7; j++)
			{
				
				cree_polygone(
				{
					x:[0,0,1,1],
					y:[(j%2)*.4,(j%2)*.6+.4,((j+1)%2)*.6+.4,((j+1)%2)*.4]
				},j+15,i+10);
			}*/
			the_world.SetBoundaryListener(myBoundaryListener);
			addEventListener(Event.ENTER_FRAME, on_enter_frame);
			time_count.addEventListener(TimerEvent.TIMER, on_time);
			//time_count.start();
			//addChild(new Stats);
			stage.addEventListener(MouseEvent.MOUSE_DOWN, createMouse);
			stage.addEventListener(MouseEvent.MOUSE_UP, destroyMouse);
			}
			private function cree_polygone(points:Object, x:Number = 0, y:Number = 0, fixe:Boolean = false):b2Body
			{
				var poly:Array=Triangulator.polygonizeTriangles(Triangulator.triangulatePolygonFromFlatArray(points.x, points.y));
				trace(poly);
				var bodydef:b2BodyDef = new b2BodyDef();
				bodydef.position.Set(x, y);
				bodydef.isBullet = true;
				var body:b2Body = the_world.CreateBody(bodydef);
				
				for (var i:int = 0; i < poly.length-1 ; i++)
				{
					var p:Polygon = poly[i];
					var b2poly:b2PolygonDef = new b2PolygonDef();
					b2poly.friction = .5;
					b2poly.restitution = .4;
					if(!fixe)
						b2poly.density = 100;
					b2poly.vertexCount = p.nVertices;
					for (var j:int = 0; j < p.nVertices; j++)
					{
						b2poly.vertices[j].Set(p.x[j], p.y[j]);
					}
					body.CreateShape(b2poly);
					
				}
				body.SetMassFromShapes();
				return body;
			}
		private function on_time(e:Event):void {
			creePoly();
		}//er war schone wenn wir freunde gebleiben waren
		private function creePoly():void
		{
			var final_body:b2Body;
			var the_body:b2BodyDef;
			the_body = new b2BodyDef();
			the_body.position.Set(7.75/*Math.random() * 17*/, -5);
			the_body.angle = Math.random() * 6;
			var the_box:b2PolygonDef;
			the_box = new b2PolygonDef();
			var nbrangle:int = Math.floor(3+Math.random()*5);
			var rayon:Number = Math.random()+.2;
			the_box.vertexCount = nbrangle;
			for (var i:int = 0; i < nbrangle; i++)
			{
				the_box.vertices[i]=new b2Vec2((Math.cos(2*Math.PI*i/nbrangle)+(Math.random()-.5)*.3)*rayon, (Math.sin(2*Math.PI*i/nbrangle)+(Math.random()-.5)*.3)*rayon);
			}
			the_box.isSensor
			the_box.restitution = .5;
			the_box.friction = .5;
			the_box.density = 100;
			final_body = the_world.CreateBody(the_body);
			final_body.SetAngularVelocity((Math.random() - .5) * 16);
			final_body.SetLinearVelocity(new b2Vec2((Math.random() - .5) * 10, 0));
			final_body.CreateShape(the_box);
			final_body.SetMassFromShapes();
		}
		private var temps:int;
		private function on_enter_frame(e:Event):void {
			temps = getTimer() - timerOld;
			timerOld = getTimer();
			the_world.Step(temps/1000, 30);
			//the_world.Step(1/stage.frameRate, 30);

			
			myBoundaryListener.lastBodys().forEach(function(elm:b2Body, num:int, vect:Vector.<b2Body>):void {
				var joint:b2JointEdge = elm.GetJointList();
				while(joint)
				{
					if (joint.joint == mouseJoint)
						mouseJoint = null;
					the_world.DestroyJoint(joint.joint);
					joint = joint.next;
				}
				the_world.DestroyBody(elm);
			});
			
			/*
			var the_body:b2Body = the_world.GetBodyList();
			var next:b2Body;
			while(the_body)
			{
				next = the_body.GetNext();
				if (the_body.GetPosition().y > 17 || the_body.GetPosition().x < -2||the_body.GetPosition().x > 17.5)
				{
					var joint:b2JointEdge = the_body.GetJointList();
					while(joint)
					{
						the_world.DestroyJoint(joint.joint);
						joint = joint.next;
					}
					the_world.DestroyBody(the_body);
				}
				the_body = next;
			}
			*/
			if (mouseJoint) {
				var mouseXWorldPhys:Number=mouseX/echelle;
				var mouseYWorldPhys:Number=mouseY/echelle;
				var p2:b2Vec2=new b2Vec2(mouseXWorldPhys,mouseYWorldPhys);
				mouseJoint.SetTarget(p2);
			}
		}
		public function createMouse(evt:MouseEvent):void {
			var body:b2Body=GetBodyAtMouse();
			if (body) {
				var mouseJointDef:b2MouseJointDef=new b2MouseJointDef  ;
				mouseJointDef.body1=the_world.GetGroundBody();
				mouseJointDef.body2=body;
				mouseJointDef.target.Set(mouseX/echelle, mouseY/echelle);
				mouseJointDef.maxForce=100000;
				mouseJointDef.timeStep=temps/1000;
				mouseJoint=the_world.CreateJoint(mouseJointDef) as b2MouseJoint;
			}
		}
		public function destroyMouse(evt:MouseEvent=null):void {
			if (mouseJoint) {
				the_world.DestroyJoint(mouseJoint);
				mouseJoint=null;
			}
		}
		public function GetBodyAtMouse(includeStatic:Boolean=false):b2Body {
			var mouseXWorldPhys:Number = (mouseX)/echelle;
			var mouseYWorldPhys:Number = (mouseY)/echelle;
			mousePVec.Set(mouseXWorldPhys, mouseYWorldPhys);
			var aabb:b2AABB = new b2AABB();
			aabb.lowerBound.Set(mouseXWorldPhys - 0.001, mouseYWorldPhys - 0.001);
			aabb.upperBound.Set(mouseXWorldPhys + 0.001, mouseYWorldPhys + 0.001);
			var k_maxCount:int=10;
			var shapes:Array = new Array();
			var count:int=the_world.Query(aabb,shapes,k_maxCount);
			var body:b2Body=null;
			for (var i:int = 0; i < count; ++i) {
				if (shapes[i].GetBody().IsStatic()==false||includeStatic) {
					var tShape:b2Shape=shapes[i] as b2Shape;
					var inside:Boolean=tShape.TestPoint(tShape.GetBody().GetXForm(),mousePVec);
					if (inside) {
						body=tShape.GetBody();
						break;
					}
				}
			}
			return body;
		}
	}
}
import Box2D.Dynamics.b2BoundaryListener;
import Box2D.Dynamics.b2Body;
import Box2D.Dynamics.b2World;
class BoundaryListener extends b2BoundaryListener
{
	private var bodys:Vector.<b2Body>;
	public function BoundaryListener()
	{
		bodys = new Vector.<b2Body>;
	}
	public override function Violation(body:b2Body):void 
	{
		bodys.push(body);
	}
	public function lastBodys():Vector.<b2Body>
	{
		var bodys2:Vector.<b2Body> = bodys;
		bodys = new Vector.<b2Body>;
		return bodys2;
	}
}

	import flash.geom.Point;
	
	class Triangulator {
		
		public function Triangulator()
		{	
		}
		
		/* give it an array of points (vertexes)
		 * returns an array of Triangles
		 * */
		public static function triangulatePolygon(v:Array):Array
		{
			var xA:Array = new Array();
			var yA:Array = new Array();
			
			for each(var p:Point in v) {
				xA.push(p.x);
				yA.push(p.y);
			}
			
			return(triangulatePolygonFromFlatArray(xA, yA));
		}
		
		/* give it a list of vertexes as flat arrays
		 * returns an array of Triangles
		 * */
		public static function triangulatePolygonFromFlatArray(xv:Array, yv:Array):Array
		{
			if (xv.length < 3 || yv.length < 3 || yv.length != xv.length) {
				trace("Please make sure both arrays or of the same length and have at least 3 vertices in them!");
				return null;
			}
			
			var i:int = 0;
			var vNum:int = xv.length;
		  
			var buffer:Array = new Array();
			var bufferSize:int = 0;
			var xrem:Array = new Array();
			var yrem:Array = new Array();
			
			for (i = 0; i < vNum; ++i) {
				xrem[i] = xv[i];
				yrem[i] = yv[i];
			}

			while (vNum > 3){
				//Find an ear
				var earIndex:int = -1;
				for (i = 0; i < vNum; ++i) {
					if (isEar(i, xrem, yrem)) {
						earIndex = i;
						break;
					}
				}

				//If we still haven't found an ear, we're screwed.
				//The user did Something Bad, so return null.
				//This will probably crash their program, since
				//they won't bother to check the return value.
				//At this we shall laugh, heartily and with great gusto.
				if (earIndex == -1) {
					trace('no ear found');
					return null;
				}

				//Clip off the ear:
				//  - remove the ear tip from the list

				//Opt note: actually creates a new list, maybe
				//this should be done in-place instead.  A linked
				//list would be even better to avoid array-fu.
				--vNum;
				var newx:Array = new Array();
				var newy:Array = new Array();
				var currDest:int = 0;
				for (i = 0; i < vNum; ++i) {
					if (currDest == earIndex) ++currDest;
					newx[i] = xrem[currDest];
					newy[i] = yrem[currDest];
					++currDest;
				}

				//  - add the clipped triangle to the triangle list
				var under:int = (earIndex == 0)?(xrem.length - 1):(earIndex - 1);
				var over:int = (earIndex == xrem.length - 1)?0:(earIndex + 1);
				var toAdd:Triangle = new Triangle(xrem[earIndex], yrem[earIndex], xrem[over], yrem[over], xrem[under], yrem[under]);
				buffer[bufferSize] = toAdd;
				++bufferSize;
					
				//  - replace the old list with the new one
				xrem = newx;
				yrem = newy;
			}
			
			var toAddMore:Triangle = new Triangle(xrem[1], yrem[1], xrem[2], yrem[2], xrem[0], yrem[0]);
			buffer[bufferSize] = toAddMore;
			++bufferSize;

			var res:Array = new Array();
			for (i = 0; i < bufferSize; i++) {
				res[i] = buffer[i];
			}
			return res;
		}
		
		/* takes: array of Triangles 
		 * returns: array of Polygons
		 * */
		public static function polygonizeTriangles(triangulated:Array):Array
		{
			var polys:Array;
			var polyIndex:int = 0;

			var i:int = 0;
			
			if (triangulated == null){
				return null;
			} else {
				polys = new Array();
				var covered:Array = new Array();
				for (i = 0; i < triangulated.length; i++) {
					covered[i] = false;
				}

				var notDone:Boolean = true;

				while(notDone){
					var currTri:int = -1;
					for (i = 0; i < triangulated.length; i++) {
						if (covered[i]) continue;
						currTri = i;
						break;
					}
					if (currTri == -1){
						notDone = false;
					} else{
						var poly:Polygon = new Polygon(triangulated[currTri].x, triangulated[currTri].y);
						covered[currTri] = true;
						for (i = 0; i < triangulated.length; i++) {
							if (covered[i]) continue;
							var newP:Polygon = poly.add(triangulated[i]);
							if (newP == null) continue;
							if (newP.isConvex()){
								poly = newP;
								covered[i] = true;
							}
						}
					}
					polys[polyIndex] = poly;
					polyIndex++;
				}
			}
			
			var ret:Array = new Array();
			for (i = 0; i < polyIndex; i++) {
				ret[i] = polys[i];
			}
			return ret;
		}

		//Checks if vertex i is the tip of an ear
		/*
		 * */
		public static function isEar(i:int, xv:Array, yv:Array):Boolean
		{
			var dx0:Number, dy0:Number, dx1:Number, dy1:Number;
			dx0 = dy0 = dx1 = dy1 = 0;
			if (i >= xv.length || i < 0 || xv.length < 3) {
				return false;
			}
			var upper:int = i + 1;
			var lower:int = i - 1;
			if (i == 0){
				dx0 = xv[0] - xv[xv.length - 1];
				dy0 = yv[0] - yv[yv.length - 1];
				dx1 = xv[1] - xv[0];
				dy1 = yv[1] - yv[0];
				lower = xv.length - 1;
			} else if (i == xv.length - 1) {
				dx0 = xv[i] - xv[i - 1];
				dy0 = yv[i] - yv[i - 1];
				dx1 = xv[0] - xv[i];
				dy1 = yv[0] - yv[i];
				upper = 0;
			} else{
				dx0 = xv[i] - xv[i - 1];
				dy0 = yv[i] - yv[i - 1];
				dx1 = xv[i + 1] - xv[i];
				dy1 = yv[i + 1] - yv[i];
			}
			
			var cross:Number = (dx0*dy1)-(dx1*dy0);
			if (cross > 0) return false;
			var myTri:Triangle = new Triangle(xv[i], yv[i], xv[upper], yv[upper], xv[lower], yv[lower]);

			for (var j:int = 0; j < xv.length; ++j) {
				if (!(j == i || j == lower || j == upper)) {
					if (myTri.isInside(xv[j], yv[j])) return false;
				}
			}
			return true;
		}	
	}
	class Triangle {
		
		public var x:Array = new Array();
		public var y:Array = new Array();
		
		public function Triangle(x1:Number, y1:Number, x2:Number, y2:Number, x3:Number, y3:Number) {
		  
			var dx1:Number = x2-x1;
			var dx2:Number = x3-x1;
			var dy1:Number = y2-y1;
			var dy2:Number = y3-y1;
			var cross:Number = (dx1*dy2)-(dx2*dy1);
			var ccw:Boolean = (cross>0);
			if (ccw){
				x[0] = x1; x[1] = x2; x[2] = x3;
				y[0] = y1; y[1] = y2; y[2] = y3;
			} else{
				x[0] = x1; x[1] = x3; x[2] = x2;
				y[0] = y1; y[1] = y3; y[2] = y2;
			}			
		}
		
		public function isInside(_x:Number, _y:Number):Boolean{
			var vx2:Number = _x - x[0]; var vy2:Number = _y - y[0];
			var vx1:Number = x[1] - x[0]; var vy1:Number = y[1] - y[0];
			var vx0:Number = x[2] - x[0]; var vy0:Number = y[2] - y[0];
			
			var dot00:Number = vx0 * vx0 + vy0 * vy0;
			var dot01:Number = vx0 * vx1 + vy0 * vy1;
			var dot02:Number = vx0 * vx2 + vy0 * vy2;
			var dot11:Number = vx1 * vx1 + vy1 * vy1;
			var dot12:Number = vx1 * vx2 + vy1 * vy2;
			var invDenom:Number = 1.0 / (dot00 * dot11 - dot01 * dot01);
			var u:Number = (dot11 * dot02 - dot01 * dot12) * invDenom;
			var v:Number = (dot00 * dot12 - dot01 * dot02) * invDenom;
			
			return ((u > 0) && (v > 0) && (u + v < 1));
		}		
	}
	
class Polygon {
	
	public var nVertices:int;
	public var x:Array = new Array();
	public var y:Array = new Array();
	
	public function Polygon(_x:Array, _y:Array = null)
	{
		if(_y == null) {
			_y = _x.y;
			_x = _x.x;
		}
		
		nVertices = _x.length;

		for (var i:int = 0; i < nVertices; ++i) {
			x[i] = _x[i];
			y[i] = _y[i];
		}
	}

	public function set(p:Polygon):void
	{
		nVertices = p.nVertices;
		x = new Array();
		y = new Array();
		for (var i:int = 0; i < nVertices; ++i) {
			x[i] = p.x[i];
			y[i] = p.y[i];
		}
	}
	
	/*
	 * Assuming the polygon is simple, checks
	 * if it is convex.
	 */
	public function isConvex():Boolean
	{
		var isPositive:Boolean = false;
		for (var i:int = 0; i < nVertices; ++i) {
			var lower:int = (i == 0)?(nVertices - 1):(i - 1);
			var middle:int = i;
			var upper:int = (i == nVertices - 1)?(0):(i + 1);
			var dx0:Number = x[middle] - x[lower];
			var dy0:Number = y[middle] - y[lower];
			var dx1:Number = x[upper]-x[middle];
			var dy1:Number = y[upper]-y[middle];
			var cross:Number = dx0 * dy1 - dx1 * dy0;
			//Cross product should have same sign
			//for each vertex if poly is convex.
			var newIsP:Boolean = (cross>0)?true:false;
			if (i == 0) {
				isPositive = newIsP;
			} else if (isPositive != newIsP){
				return false;
			}
		}
		return true;
	}

	/*
	 * Tries to add a triangle to the polygon.
	 * Returns null if it can't connect properly.
	 * Assumes bitwise equality of join vertices.
	 */
	public function add(t:Triangle):Polygon
	{
		//First, find vertices that connect
		var firstP:int = -1; 
		var firstT:int = -1;
		var secondP:int = -1; 
		var secondT:int = -1;
		
		var i:int = 0;
		
		for (i = 0; i < nVertices; i++) {
			if (t.x[0] == this.x[i] && t.y[0] == this.y[i]){
				if (firstP == -1){
					firstP = i; firstT = 0;
				} else{
					secondP = i; secondT = 0;
				}
			} else if (t.x[1] == this.x[i] && t.y[1] == this.y[i]) {
				if (firstP == -1){
					firstP = i; firstT = 1;
				} else{
					secondP = i; secondT = 1;
				}
			} else if (t.x[2] == this.x[i] && t.y[2] == this.y[i]){
				if (firstP == -1){
					firstP = i; firstT = 2;
				} else{
					secondP = i; secondT = 2;
				}
			} else {
				//println(t.x[0]+" "+t.y[0]+" "+t.x[1]+" "+t.y[1]+" "+t.x[2]+" "+t.y[2]);
				//println(x[0]+" "+y[0]+" "+x[1]+" "+y[1]);
			}
		}
		
		//Fix ordering if first should be last vertex of poly
		if (firstP == 0 && secondP == nVertices - 1) {
			firstP = nVertices-1;
			secondP = 0;
		}
		
		//Didn't find it
		if (secondP == -1) return null;
		
		//Find tip index on triangle
		var tipT:int = 0;
		if (tipT == firstT || tipT == secondT) tipT = 1;
		if (tipT == firstT || tipT == secondT) tipT = 2;
		
		var newx:Array = new Array();
		var newy:Array = new Array();
		var currOut:int = 0;
		
		for (i = 0; i < nVertices; i++) {
			newx[currOut] = x[i];
			newy[currOut] = y[i];
			if (i == firstP){
				++currOut;
				newx[currOut] = t.x[tipT];
				newy[currOut] = t.y[tipT];
			}
			++currOut;
		}
		return new Polygon(newx, newy);
	}
	public function toString():String
	{
		var string:String = "\n[Polygon:";
		for (var i:int = 0; i < this.nVertices; i++)
		{
			string += "\n ["+this.x[i]+"\t"+this.y[i]+"],";
		}
		return string+"]";
	}
}