Drawing circles on the fly in Box2D

Only a day after Drawing boxes on the fly in Box2D I got three submissions for drawing circles on the fly.

I will list all of them, and I found interesting how different Flashers obtained the same result in quite the same way.

The first to send the routine was Vitaliy Berov:

This is the source code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package {
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
 
	import Box2D.Dynamics.*;
	import Box2D.Collision.*;
	import Box2D.Collision.Shapes.*;
	import Box2D.Common.Math.*;
 
	public class andrewCircle extends Sprite {
		public var m_dbgSprite;
		public var m_world:b2World;
		public var m_phys_scale:Number = 30.0;
		public var m_timestep:Number = 1.0/30.0;
		public var m_iterations:Number = 10.0;
 
		//initial box coordinates when we first press mouse down
		public var initX:Number = 0.0;
		public var initY:Number = 0.0;
		public var drawing:Boolean = false;
		public function andrewCircle() {
			/*
			A Box2D world needs three parameters: a b2AABB, gravity
			and a Booleand deciding whether or not to let bodies sleep
			when they are not being simulated.
			This saves CPU so should always be left on :)
			*/
 
			var gravity:b2Vec2 = new b2Vec2(0,9.8);
			var worldAABB:b2AABB = new b2AABB();
			worldAABB.lowerBound.Set(-1000,-1000);
			worldAABB.upperBound.Set(1000,1000);
			m_world = new b2World(worldAABB,gravity,true);
 
			//Setup Debug Draw - So that Box2D draws the shapes for us
			m_dbgSprite = new Sprite();
			addChild(m_dbgSprite);
			SetDebugDraw();
 
			//Add Our Ground
			AddStaticBox(300/m_phys_scale,440/m_phys_scale,300/m_phys_scale,50/m_phys_scale);
 
			addEventListener(Event.ENTER_FRAME,Update);
			stage.addEventListener(MouseEvent.MOUSE_DOWN,mousePressed);
			stage.addEventListener(MouseEvent.MOUSE_MOVE,mouseMoved);
			stage.addEventListener(MouseEvent.MOUSE_UP,mouseReleased);
		}
 
		public function mousePressed(e:MouseEvent) {
			//Store initial X and Y position
			initX = e.localX;
			initY = e.localY;
			drawing = true;
		}
 
		public function mouseMoved(e:MouseEvent) {
			if (drawing) {
				//Simply draw the "ghost" of the circle we are about to add
				graphics.clear();
				graphics.beginFill(0xFF0000,0.5);
				graphics.drawCircle(initX, initY, (e.localX - initX));
			}
		}
 
		public function mouseReleased(e:MouseEvent) {
			graphics.clear();
			drawing = false;
 
			//Coordinates of a point laying on the edge of the circle. 
			var finalX:Number = e.localX;
			var finalY:Number = e.localY;
 
			//calculate the radius
			var radius:Number = Math.abs(finalX-initX);
			if (radius > 0) {
				AddCircle(initX/m_phys_scale, initY/m_phys_scale, radius/m_phys_scale)
			}
		}
 
		public function AddCircle(_x:Number,_y:Number,_radius:Number) {
			var bodyDef:b2BodyDef = new b2BodyDef();
			bodyDef.position.Set(_x,_y);
			var circleDef = new b2CircleDef();
			circleDef.radius= _radius;
			circleDef.density = 1.0;
			circleDef.friction = 0.3;
			circleDef.restitution = 0.2;
			var body:b2Body = m_world.CreateBody(bodyDef);
			body.CreateShape(circleDef);
			body.SetMassFromShapes();			
		}
 
		public function AddStaticBox(_x:Number,_y:Number,_halfwidth:Number,_halfheight:Number) {
			var bodyDef:b2BodyDef = new b2BodyDef();
			bodyDef.position.Set(_x,_y);
			var boxDef:b2PolygonDef = new b2PolygonDef();
			boxDef.SetAsBox(_halfwidth,_halfheight);
			boxDef.density = 0.0;
			var body:b2Body = m_world.CreateBody(bodyDef);
			body.CreateShape(boxDef);
			body.SetMassFromShapes();
		}
 
		public function Update(e:Event) {
			//We need to do this to simulate physics
			m_world.Step(m_timestep,m_iterations);
		}
 
		public function SetDebugDraw() {
			//Set Debug Draw (hidden here to reserve space in constructor.)
			var dbgDraw:b2DebugDraw = new b2DebugDraw();
			dbgDraw.m_sprite = m_dbgSprite;
			dbgDraw.m_drawScale = m_phys_scale;
			dbgDraw.m_fillAlpha = 0.8;
			dbgDraw.m_lineThickness = 2.0;
			dbgDraw.m_drawFlags = 0x0001 | 0x0002;
			m_world.SetDebugDraw(dbgDraw);
		}
	}
}

Then came Michael Beech, featuring a menu to select between circles and boxes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
package {
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
 
	import Box2D.Collision.*;
	import Box2D.Collision.Shapes.*;
	import Box2D.Common.Math.*;
	import Box2D.Dynamics.*;	
 
	public class andrew extends Sprite {
		public var m_dbgSprite : Sprite;
		public var m_world:b2World;
		public var m_phys_scale:Number = 30.0;
		public var m_timestep:Number = 1.0/30.0;
		public var m_iterations:Number = 10.0;
 
 
		//initial box coordinates when we first press mouse down
		public var initX:Number = 0.0;
		public var initY:Number = 0.0;
		public var drawing:Boolean = false;
 
		public static const RECTANGLE : String = "rectangleShape";
		public static const CIRCLE : String = "circleShape";
 
		private var rectButton : Sprite;
		private var circButton : Sprite;
		private var shapeType : String = RECTANGLE;
 
 
		public function andrew() {
			/*
			A Box2D world needs three parameters: a b2AABB, gravity
			and a Booleand deciding whether or not to let bodies sleep
			when they are not being simulated.
			This saves CPU so should always be left on :)
			*/
 
			var gravity:b2Vec2 = new b2Vec2(0,9.8);
			var worldAABB:b2AABB = new b2AABB();
			worldAABB.lowerBound.Set(-1000,-1000);
			worldAABB.upperBound.Set(1000,1000);
			m_world = new b2World(worldAABB,gravity,true);
 
			//Setup Debug Draw - So that Box2D draws the shapes for us
			m_dbgSprite = new Sprite();
			addChild(m_dbgSprite);
			SetDebugDraw();
 
			//Add Our Ground
			AddStaticBox(300/m_phys_scale,440/m_phys_scale,300/m_phys_scale,50/m_phys_scale);
 
			createButtons();
 
			addEventListener(Event.ENTER_FRAME,Update);
			stage.addEventListener(MouseEvent.MOUSE_DOWN,mousePressed);
			stage.addEventListener(MouseEvent.MOUSE_MOVE,mouseMoved);
			stage.addEventListener(MouseEvent.MOUSE_UP,mouseReleased);
		}
		public function mousePressed(e:MouseEvent) : void {
			//Store initial X and Y position
			initX = e.localX;
			initY = e.localY;
			drawing = true;
		}
		public function mouseMoved(e:MouseEvent) : void {
			if (drawing) {
				//Simply draw the "ghost" of the box we are about to add
				graphics.clear();
				graphics.beginFill(0xFF0000,0.5);
				switch(shapeType){
					case RECTANGLE :
						graphics.drawRect(initX,initY,e.localX-initX,e.localY-initY);
						break;
					case CIRCLE :
						graphics.drawCircle(e.localX,e.localY,e.localX-initX);
						break;
				}
			}
		}
 
		public function mouseReleased(e:MouseEvent) : void {
			graphics.clear();
			drawing = false;
 
			//Coordinates of bottom-right of box (when drawing from left to right)
			var finalX:Number = e.localX;
			var finalY:Number = e.localY;
 
			//Correct if drawing from right to left
			if (finalX < initX) {
				//If so, swap initX and finalX
				var tempX:Number = initX;
				initX = finalX;
				finalX = tempX;
			}
			if (finalY < initY) {
				//If so, swap initY and finalY
				var tempY:Number = initY;
				initY = finalY;
				finalY = tempY;
			}
			//Work out the half-width and height of the box
			var boxHalfWidth:Number = Math.abs((finalX-initX)/2);
			var boxHalfHeight:Number = Math.abs((finalY-initY)/2);
			if (boxHalfWidth > 0 && boxHalfHeight > 0) {
				switch(shapeType){
					case RECTANGLE :
						AddBox((finalX-boxHalfWidth)/m_phys_scale,(finalY-boxHalfHeight)/m_phys_scale,boxHalfWidth/m_phys_scale,boxHalfHeight/m_phys_scale);
						break;
					case CIRCLE :
						AddCircle((finalX-boxHalfWidth)/m_phys_scale,(finalY-boxHalfHeight)/m_phys_scale,boxHalfWidth/m_phys_scale,boxHalfHeight/m_phys_scale);
						break;
				}
			}
		}
		/*
		NOTE: AddBox takes the _x,_y and halfwidth and halfheight parameters
		in METRES not PIXELS. This means when you call this function, always
		DIVIDE a pixel size by m_phys_scale to get it in meteres.
		*/
		public function AddBox(_x:Number,_y:Number,_halfwidth:Number,_halfheight:Number)  : void {
			var bodyDef:b2BodyDef = new b2BodyDef();
			bodyDef.position.Set(_x,_y);
			var boxDef:b2PolygonDef = new b2PolygonDef();
			boxDef.SetAsBox(_halfwidth,_halfheight);
			boxDef.density = 1.0;
			boxDef.friction = 0.3;
			boxDef.restitution = 0.2;
			var body:b2Body = m_world.CreateBody(bodyDef);
			body.CreateShape(boxDef);
			body.SetMassFromShapes();
 
 
		}
 
		public function AddCircle(_x:Number,_y:Number,_halfwidth:Number,_halfheight:Number)  : void {
			var bodyDef : b2BodyDef = new b2BodyDef();
			bodyDef.position.Set(_x + _halfwidth, _y +_halfheight);
 
			var circleDef : b2CircleDef = new b2CircleDef();
			circleDef.radius = _halfwidth;
			circleDef.density = 1.0;
			circleDef.friction = 0.5;
			circleDef.restitution = 0.2;
			var body : b2Body = m_world.CreateBody(bodyDef);
			body.CreateShape(circleDef);	
			body.SetMassFromShapes();				
		}
 
 
		public function AddStaticBox(_x:Number,_y:Number,_halfwidth:Number,_halfheight:Number)  : void {
			var bodyDef:b2BodyDef = new b2BodyDef();
			bodyDef.position.Set(_x,_y);
			var boxDef:b2PolygonDef = new b2PolygonDef();
			boxDef.SetAsBox(_halfwidth,_halfheight);
			boxDef.density = 0.0;
			var body:b2Body = m_world.CreateBody(bodyDef);
			body.CreateShape(boxDef);
			body.SetMassFromShapes();
		}
 
		private function createButtons() : void {
			rectButton = new Sprite();
 
			var recShape : Shape = new Shape();
			recShape.graphics.beginFill(0xff0000);	
			recShape.graphics.drawRect(0, 0, 15, 15);
			recShape.graphics.endFill();
 
			rectButton.addChild(recShape);
			addChild(rectButton);
			rectButton.x = 10;
			rectButton.y = 10;
			rectButton.addEventListener(MouseEvent.CLICK, setDrawType);
 
			circButton = new Sprite();
 
			var circShape : Shape = new Shape();
			circShape.graphics.beginFill(0xff0000);	
			circShape.graphics.drawCircle(7.5, 7.5, 7.5);
			circShape.graphics.endFill();
 
			circButton.addChild(circShape);
			addChild(circButton);
			circButton.x = rectButton.x + 20;
			circButton.y = 10;
			circButton.addEventListener(MouseEvent.CLICK, setDrawType);
 
			circButton.alpha = 0.25;
		}
 
		private function setDrawType(evt : MouseEvent) : void {
			switch(evt.currentTarget){
				case rectButton :
					shapeType = RECTANGLE;
					circButton.alpha = 0.25;
					rectButton.alpha = 1;
					trace(shapeType);
					break;
				case circButton :
					shapeType = CIRCLE;
					circButton.alpha = 1;
					rectButton.alpha = 0.25;
					trace(shapeType);
					break;
			}
 
		}
 
 
 
		public function Update(e:Event) : void {
			//We need to do this to simulate physics
			m_world.Step(m_timestep,m_iterations);
		}
		public function SetDebugDraw()  : void {
			//Set Debug Draw (hidden here to reserve space in constructor.)
			var dbgDraw:b2DebugDraw = new b2DebugDraw();
			dbgDraw.m_sprite = m_dbgSprite;
			dbgDraw.m_drawScale = m_phys_scale;
			dbgDraw.m_fillAlpha = 0.8;
			dbgDraw.m_lineThickness = 2.0;
			dbgDraw.m_drawFlags = 0x0001 | 0x0002;
			m_world.SetDebugDraw(dbgDraw);
		}
	}
}

and the third one was fleshMaker from Fuori dal Cerchio (translated in english sounds like “outside the circle”… how ironic…)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
package
{
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
 
	import Box2D.Dynamics.*;
	import Box2D.Collision.*;
	import Box2D.Collision.Shapes.*;
	import Box2D.Common.Math.*;
 
	public class TestCircle extends Sprite {
		public var m_dbgSprite;
		public var m_world:b2World;
		public var m_phys_scale:Number = 30.0;
		public var m_timestep:Number = 1.0/30.0;
		public var m_iterations:Number = 10.0;
 
		//initial box coordinates when we first press mouse down
		public var initX:Number = 0.0;
		public var initY:Number = 0.0;
		public var drawing:Boolean = false;
		public var b:b2Body;
		public function TestCircle() {
			/*
			A Box2D world needs three parameters: a b2AABB, gravity
			and a Booleand deciding whether or not to let bodies sleep
			when they are not being simulated.
			This saves CPU so should always be left on :)
			*/
 
			var gravity:b2Vec2 = new b2Vec2(0,9.8);
			var worldAABB:b2AABB = new b2AABB();
			worldAABB.lowerBound.Set(-1000,-1000);
			worldAABB.upperBound.Set(1000,1000);
			m_world = new b2World(worldAABB,gravity,true);
 
			//Setup Debug Draw - So that Box2D draws the shapes for us
			m_dbgSprite = new Sprite();
			addChild(m_dbgSprite);
			SetDebugDraw();
 
			//Add Our Ground
			AddStaticBox(250/m_phys_scale,480/m_phys_scale,250/m_phys_scale,10/m_phys_scale);
 
			addEventListener(Event.ENTER_FRAME,Update);
			stage.addEventListener(MouseEvent.MOUSE_DOWN,mousePressed);
			stage.addEventListener(MouseEvent.MOUSE_MOVE,mouseMoved);
			stage.addEventListener(MouseEvent.MOUSE_UP,mouseReleased);
		}
		public function mousePressed(e:MouseEvent) {
			//Store initial X and Y position
			initX = e.localX;
			initY = e.localY;
			drawing = true;
		}
		public function mouseMoved(e:MouseEvent) {
			if (drawing) {
				graphics.clear();
				graphics.beginFill(0xFF0000, 0.5);
				// getting radius, thank you to Pythagorean theorem (and my mum)
				var radius:Number = Math.sqrt((e.localX - initX)*(e.localX - initX) + (e.localY - initY)*(e.localY - initY));
				graphics.drawCircle(initX, initY, radius);
			}
		}
		public function mouseReleased(e:MouseEvent) {
			graphics.clear();
			drawing = false;
			var radius:Number = Math.sqrt((e.localX - initX)*(e.localX - initX) + (e.localY - initY)*(e.localY - initY));
			addCircle(initX, initY, radius);
		}
 
		public function addCircle(_x:Number, _y:Number, _radius:Number) {
			var bd:b2BodyDef = new b2BodyDef();
			var cd:b2CircleDef = new b2CircleDef();
			cd.radius = Math.abs(_radius)/m_phys_scale;
			cd.density = 2;
			cd.restitution = 0.2;
			cd.friction = 2;
			bd.position.Set(_x/m_phys_scale, _y/ m_phys_scale);
			b = m_world.CreateBody(bd);
			b.CreateShape(cd);
			b.SetMassFromShapes();
		}
 
		public function AddStaticBox(_x:Number,_y:Number,_halfwidth:Number,_halfheight:Number) {
			var bodyDef:b2BodyDef = new b2BodyDef();
			bodyDef.position.Set(_x,_y);
			var boxDef:b2PolygonDef = new b2PolygonDef();
			boxDef.SetAsBox(_halfwidth,_halfheight);
			boxDef.density = 0.0;
			var body:b2Body = m_world.CreateBody(bodyDef);
			body.CreateShape(boxDef);
			body.SetMassFromShapes();
		}
		public function Update(e:Event) {
			//We need to do this to simulate physics
			m_world.Step(m_timestep,m_iterations);
		}
		public function SetDebugDraw() {
			//Set Debug Draw (hidden here to reserve space in constructor.)
			var dbgDraw:b2DebugDraw = new b2DebugDraw();
			dbgDraw.m_sprite = m_dbgSprite;
			dbgDraw.m_drawScale = m_phys_scale;
			dbgDraw.m_fillAlpha = 0.8;
			dbgDraw.m_lineThickness = 2.0;
			dbgDraw.m_drawFlags = 0x0001 | 0x0002;
			m_world.SetDebugDraw(dbgDraw);
		}
	}
 
}

All you need to do to included it in your project is download the source from Drawing boxes on the fly in Box2D and replace the content in andrew.as file with the one you prefer.

Just remember to change class and main function name to andrew.

Now, what about triangles? This seems a little harder…

Rate this post: 1 Star2 Stars3 Stars4 Stars5 Stars (6 votes, average: 4.83 out of 5)
Loading ... Loading ...
If you found this post useful, please consider a small donation.
» Flash Templates provided by Template Monster are pre-made web design products developed using Flash technology.
They can be easily customized to meet the unique requirements of your project.

13 Responses to “Drawing circles on the fly in Box2D”

  1. Shival on January 8th, 2009 1:36 pm

    All of them pretty cool, but in the second example the produced circle is smaller than the ghost.

    Why so?

  2. Adam on January 8th, 2009 3:28 pm

    the second also seems to have some registration point issues, as you drag around to create the circle it seems to come from the corners and move around a bit, as opposed to the other two that do it from the center, seems more logical this way to me.

  3. vitaLee on January 8th, 2009 4:32 pm

    hi emanuele,
    well i’ve tried myself to build triangle polygons but for some reason in some cases the triangles are not affected by the gravity.
    when a tringle is spawned it turns it’s color to green and stays frozen in space.
    we may play some ‘question and anwers’ on the box2d subject to demistify that problem.
    i can send you my my document class if you want.

  4. Emanuele Feronato on January 8th, 2009 4:42 pm

    did you set density?

  5. vitaLee on January 8th, 2009 5:43 pm

    yes i did.
    maybe it has something to do with the fact that one of my vertices is set as (0,0).
    when i try to make a triangle in certain position it doesn’t work. when i try to duplicate the same triangle starting from the same angle i did in the previous but then changing the order for setting the other 2 points it works.
    i’ve been trying to find out whats the reason but in vain.
    i can do some research but thought that it would be more fun to dicuss it here.
    what about such discussion ?
    ;)

  6. Francis Bourre on January 8th, 2009 6:50 pm

    Hello !
    Nice to see your works. I did some in the same way, you can check it on my blog:

    http://blog.tweenpix.net/2009/01/08/3d-and-physics-with-papervision-3d-and-box2d/

    keep up the good work !

  7. Monkios on January 8th, 2009 10:25 pm

    Are you trying to create equilateral or fixed angle triangles or are you trying to make them with an array of different angles ?

    As I see it, it would be much easier the first ones on the fly.

  8. vitaLee on January 8th, 2009 10:51 pm

    i’m traying it by placing 3 points on the stage through mouse clicks.
    then i take the first as starting point and calculate the distance between it and the other two (x,y).
    with all that data I set the polygon vertices.

  9. Michael Beech on January 9th, 2009 12:47 am

    Yeah I created my circles lazily (too quick ‘casue I was supposed to be working) and didn’t account for the m_phys_scale. If you put Fuori dal Cerchio’s radius calculation in it work as expected.

    var radius:Number = Math.sqrt((e.localX – initX)*(e.localX – initX) + (e.localY – initY)*(e.localY – initY));
    AddCircle(initX, initY, radius);

    Good learning experience :)

  10. Nathan on January 9th, 2009 1:04 am

    cool, but the second one has false advertising. The ghost is larger than the circle it produces :)

  11. anlik on January 9th, 2009 10:18 am

    Mr.Emanuele Feronato:
    I knew that NEWGROUNDS.COM is very important for flash game added MochiAds with the help of your articles. But i can’t register on the NG, because i can’t see the security number in China.I really need your help. Can you email me extra a username and password about NG?
    I realy need NG. I really need your help. I don’t know any friend out of China.Thank youO(∩_∩)O
    My Email is mango_kampong@qq.com.
    Please visite my new flash game:
    http://www.mochiads.com/contest/nov08/games/save-the-donkey_v2
    Thanks again

  12. David on May 15th, 2009 5:34 am

    I’ve managed to implement a way of drawing triangles on the fly if you’d like to see it :). It’s fairly convoluted and I’m far from the best coder at Flash, but it still draws triangles.

    David

Leave a Reply




Trackbacks

  1. Basic Filler engine with Box2D - part 1 : Emanuele Feronato on February 23rd, 2009 7:16 pm

    [...] code is mainly taken from Drawing circles on the fly in Box2D – fleshMaker version, but uses movieclips for the balls (so I removed the debug draw) and has a [...]

Posts