The engine behind Splitter Flash game : working code

I think we finally have a working slicing engine. It started with The engine behind Splitter Flash game, then come The engine behind Splitter Flash game – first AS3 prototype and The engine behind Splitter Flash game – new AS3 prototypes.

Now Guillaume Pommey aka pompom sent us a working version.

I revised my prototype of slicing engine two days ago and I corrected a lot of errors. After an afternoon on my code, I resolved almost all my problems.

I am proud ^^, now the slicing engine works.

I joint the Main.as and the .fla cause I use radio button to change the cutter style, I let you discover it :) .

Ps : Don’t forget that I use the Raycast function so speak about this on your blog.

For more information about Raycast function check Box2D Raycasts post.

The engine works perfectly, and has two ways of cutting objects: the laser mode cuts objects when you press C while the cutter one lets you cut by drawing a line.

Here it 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
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
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
/*
* Copyright (c) 2006-2007 Erin Catto http://www.gphysics.com
*
* This software is provided 'as-is', without any express or implied
* warranty.  In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
 
/*
*This a port in AS3 by Guillaume Pommey of a slicing engine in C
*Thanks to Emanuele Feronato for his help : http://www.emanueleferonato.com/category/box2d/
*Thanks to Boris The Brave for his Raycast function : http://personal.boristhebrave.com/
*I hope you will enjoy this, have fun !
*/
 
package{
 
import Box2D.Dynamics.*
import Box2D.Collision.*
import Box2D.Collision.Shapes.*
import Box2D.Dynamics.Joints.*
import Box2D.Dynamics.Contacts.*
import Box2D.Common.Math.*
import Box2D.Common.*
import General.*
import flash.display.*;
import flash.events.*;
import flash.utils.getTimer
import flash.display.MovieClip;
 
	public class Main extends MovieClip{
		public function Main(){
 
			m_fpsCounter.x = 7;
			m_fpsCounter.y = 5;
			addChildAt(m_fpsCounter, 0);
 
			m_sprite = new Sprite();
			addChild(m_sprite);
 
			m_input = new Input(m_sprite);
 
			addEventListener(Event.ENTER_FRAME, update, false, 0, true);
			stage.addEventListener(KeyboardEvent.KEY_DOWN, key_pressed);
			stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpEvent);
 
			//Create a b2World
			var worldAABB:b2AABB = new b2AABB();
			worldAABB.lowerBound.Set(-1000.0, -1000.0);
			worldAABB.upperBound.Set(1000.0, 1000.0);
 
			// Define the gravity vector
			var gravity:b2Vec2 = new b2Vec2(0.0, 10.0);
 
			// Allow bodies to sleep
			var doSleep:Boolean = true;
 
			// Construct a world object
			m_world = new b2World(worldAABB, gravity, doSleep);
			dbgDraw = new b2DebugDraw();
			dbgDraw.m_sprite = m_sprite;
			dbgDraw.m_drawScale = 15.0;
			dbgDraw.m_fillAlpha = 0.3;
			dbgDraw.m_lineThickness = 1.0;
			dbgDraw.m_drawFlags = b2DebugDraw.e_shapeBit;
			m_world.SetDebugDraw(dbgDraw);
 
			//Create some nice shapes
			CutterTest();
		}
 
		public function CutterTest(){
 
				var ground:b2Body = null;
 
				//Top
				var bd:b2BodyDef = new b2BodyDef();
				bd.position.Set(0.0, -6.0);
				ground = m_world.CreateBody(bd);
 
				var sd:b2PolygonDef = new b2PolygonDef();
				sd.SetAsBox(50.0, 10.0);
				ground.CreateShape(sd);
 
				//Bottom
				bd.position.Set(0.0, 45);
				ground = m_world.CreateBody(bd);
 
				sd.SetAsBox(50.0, 10.0);
				ground.CreateShape(sd);
 
				//Laser body
				bd.position.Set(6.0, 15.0);
				laserBody = m_world.CreateBody(bd);
 
				sd.SetAsBox(5.0, 1.0);
				sd.density = 4.0;
				laserBody.CreateShape(sd);
				laserBody.SetMassFromShapes();
 
				//Boxes
				sd.SetAsBox(3.0, 3.0);
				sd.density = 5.0;
 
				bd.userData = 1;
				bd.position.Set(25.0, 15.0);
				var body1:b2Body = m_world.CreateBody(bd);
				body1.CreateShape(sd);
				body1.SetMassFromShapes();
 
				sd.SetAsBox(3.0, 3.0);
				sd.density = 5.0;
 
				bd.userData = 1;
				bd.position.Set(25.0, 15.0);
				body1 = m_world.CreateBody(bd);
				body1.CreateShape(sd);
				body1.SetMassFromShapes();
		}
 
		/***************************************************/
		/*********************ETAPE 1***********************/
		/***************************************************/
 
		public function CheckPolyShape(poly:b2PolygonDef)
		{
			if (!(3 <= poly.vertexCount && poly.vertexCount <= b2_maxPolygonVertices)){
					return -1;
			}
			var m_normals:Array = new Array(poly.vertexCount);
 
		 		// Compute normals. Ensure the edges have non-zero length.
				for (var i=0; i < poly.vertexCount; i++)
				{
					var i1 = i;
					var i2 = 0;
 
					if((i + 1) < poly.vertexCount){
						i2 = i + 1;
					} else {
						i2 = 0;
					}
 
					var edge:b2Vec2 = poly.vertices[i2].Copy();
					edge.Subtract(poly.vertices[i1]);
 
 
					if (!(edge.LengthSquared()> Number.MIN_VALUE * Number.MIN_VALUE)){//Peut être une erreur
						return -1;
					}
					m_normals[i] = b2Math.b2CrossVF(edge, 1.0);
					m_normals[i].Normalize();
				}
 
				// Ensure the polygon is convex.
				for (i=0; i <poly.vertexCount; i++)
				{
					for (var j=0; j <poly.vertexCount; j++)
					{
						// Don't check vertices on the current edge.
						if (j == i || j == (i + 1) % poly.vertexCount)
						{
							continue;
						}
						// Your polygon is non-convex (it has an indentation).
						// Or your polygon is too skinny.
						var vecPlus:b2Vec2 = b2Math.SubtractVV(poly.vertices[j], poly.vertices[i]);
						var s = b2Math.b2Dot(m_normals[i], vecPlus);//Problème ??
						if (!(s < -b2Settings.b2_linearSlop)){//Idem
							return -1;
						}
					}
				}
 
				// Ensure the polygon is counter-clockwise.
				for (i=1; i <poly.vertexCount; i++)
				{
					var cross:Number = b2Math.b2CrossVV(m_normals[i-1], m_normals[i]);
 
					// Keep asinf happy.
					cross = b2Math.b2Clamp(cross, -1, 1);
 
					// You have consecutive edges that are almost parallel on your polygon.
					var angle:Number = Math.asin(cross);
					if (!(angle> b2Settings.b2_angularSlop))
						return -1;
				}
 
				// Compute the polygon centroid.
				var m_centroid:b2Vec2 = new b2Vec2(); 
				var area:Number = 0;
				var pRef:b2Vec2 = new b2Vec2(0.0, 0.0);
				var inv3:Number = 1 / 3;
 
				for (i=0; i <poly.vertexCount; i++)
				{
					// Triangle vertices.
					var p1:b2Vec2 = pRef;
					var p2:b2Vec2 = poly.vertices[i];
					var p3 = 0;
 
					if((i + 1) < poly.vertexCount){
						p3 = poly.vertices[i+1];
					} else {
						p3 = poly.vertices[0];
					}
 
					var e1:b2Vec2 = b2Math.SubtractVV(p2, p1);
					var e2:b2Vec2 = b2Math.SubtractVV(p3, p1);
 
					var D:Number = b2Math.b2CrossVV(e1, e2);
					var triangleArea:Number = 0.5 * D;
					area += triangleArea;
 
					// Area weighted centroid
					var p123:b2Vec2 = p1.Copy();
					p123.Add(p1);
					p123.Add(p2);
					var trInv3:Number = triangleArea * inv3;
					var p123Mul:b2Vec2 = b2Math.MulFV(trInv3, p123);
					m_centroid.Add(p123);
				}
 
				// Centroid
				if (!(area> Number.MIN_VALUE)){
					return -1;
				}
				b2Math.MulFV((1.0 / area), m_centroid); // ??????
 
				// Compute the oriented bounding box.
				//ComputeOBB(&m_obb, m_vertices, m_vertexCount);
 
				// Create core polygon shape by shifting edges inward.
				// Also compute the min/max radius for CCD.
				for (i=0; i <poly.vertexCount; i++)
				{
					if((i - 1) >= 0){
						i1 = i - 1;
					}else {
						i1 = (poly.vertexCount - 1)
					}
					i2 = i;
 
					var n1:b2Vec2 = m_normals[i1];
					var n2:b2Vec2 = m_normals[i2];
					var v:b2Vec2 = b2Math.SubtractVV(poly.vertices[i], m_centroid); // ?????
 
					var d:b2Vec2 = new b2Vec2();
					d.x = b2Math.b2Dot(n1, v) - b2Settings.b2_toiSlop;
					d.y = b2Math.b2Dot(n2, v) - b2Settings.b2_toiSlop;
 
					// Shifting the edge inward by b2_toiSlop should
					// not cause the plane to pass the centroid.
 
					//Those conditions are very annoying !!
 
					// Your shape has a radius/extent less than b2_toiSlop.
					/*if (!(d.x>= 0)){
						return -1;
					}
					if (!(d.y>= 0)){
						return -1;
					}*/
				}
			return 0;
 
		}
 
	 /// Split a shape trough a segment
    /// @return
    /// -1 - Error on split
    ///  0 - Normal result is two new shape definitions.
    public function SplitShape(shape:b2PolygonShape, segment, splitSize, newPolygon:Array)// ????
    {
        /*assert(shape != NULL);
        assert(newPolygon != NULL);
        assert(splitSize>= 0);*/
 
		if(shape == null)
			return -1;
 
		if(newPolygon == null)
			return -1;
 
		if(splitSize <= 0)
			return -1;
 
     	var lambda:Array = [1];
        var normal:b2Vec2 = new b2Vec2();
 
		var b:b2Body = shape.GetBody();
      	var xf:b2XForm = b.GetXForm();
        if (shape.TestSegment(xf, lambda, normal, segment, 1) != b2Shape.e_hitCollide){
            return -1;
		}
 
		var entryPoint:b2Vec2 = segment.p1.Copy();
		entryPoint.Multiply(1 - lambda[0]);
		var tmp:b2Vec2 = segment.p2.Copy();
		tmp.Multiply(lambda[0]);
		entryPoint.Add(tmp);			
 
        var reverseSegment:b2Segment= new b2Segment;
        reverseSegment.p1 = segment.p2;
        reverseSegment.p2 = segment.p1;
 
        if (shape.TestSegment(xf, lambda, normal, reverseSegment, 1.0) != e_hitCollide){
            return -1;
		}
 
		var exitPoint:b2Vec2 = reverseSegment.p1.Copy();
		exitPoint.Multiply(1 - lambda[0]);
		tmp = reverseSegment.p2.Copy();
		tmp.Multiply(lambda[0]);
		exitPoint.Add(tmp);		
 
        var localEntryPoint:b2Vec2 = b.GetLocalPoint(entryPoint);
        var localExitPoint:b2Vec2  = b.GetLocalPoint(exitPoint);
 
        var vertices:Array = shape.GetVertices();
        var cutAdded:Array = [-1,-1];
        var lastA = -1;
 
        for(var i = 0; i<shape.GetVertexCount(); i++)
        {
            var n;
            //Find out if this vertex is on the old or new shape.
            if (b2Math.b2Dot(b2Math.b2CrossVF(b2Math.SubtractVV(localExitPoint,localEntryPoint), 1), b2Math.SubtractVV(vertices[i], localEntryPoint))> 0) //Clash ??
                n = 0;
            else
                n = 1;
            if (lastA != n)
            {
                //If we switch from one shape to the other add the cut vertices.
                if (lastA == 0)
                {
                    //assert(cutAdded[0] == -1); Couiiic
					if(cutAdded[0] != -1){
						return -1;
					}
                    cutAdded[0] = newPolygon[lastA].vertexCount;
                    newPolygon[lastA].vertices[newPolygon[lastA].vertexCount] = localExitPoint;
                    newPolygon[lastA].vertexCount++;
                    newPolygon[lastA].vertices[newPolygon[lastA].vertexCount] = localEntryPoint;
                    newPolygon[lastA].vertexCount++;
                }
                if (lastA == 1)
                {
                    //assert(cutAdded[lastA] == -1); Recouiiic
					if(cutAdded[lastA] != -1){
						return -1;
					}
                    cutAdded[lastA] = newPolygon[lastA].vertexCount;
                    newPolygon[lastA].vertices[newPolygon[lastA].vertexCount] = localEntryPoint;
                    newPolygon[lastA].vertexCount++;
                    newPolygon[lastA].vertices[newPolygon[lastA].vertexCount] = localExitPoint;
                    newPolygon[lastA].vertexCount++;
                }
            }
            newPolygon[n].vertices[newPolygon[n].vertexCount] = vertices[i];
            newPolygon[n].vertexCount++;
            lastA = n;
        }
 
        //Add the cut in case it has not been added yet.
        if (cutAdded[0] == -1)
        {
            cutAdded[0] = newPolygon[0].vertexCount;
            newPolygon[0].vertices[newPolygon[0].vertexCount] = localExitPoint;
            newPolygon[0].vertexCount++;
            newPolygon[0].vertices[newPolygon[0].vertexCount] = localEntryPoint;
            newPolygon[0].vertexCount++;
        }
        if (cutAdded[1] == -1)
        {
            cutAdded[1] = newPolygon[1].vertexCount;
            newPolygon[1].vertices[newPolygon[1].vertexCount] = localEntryPoint;
            newPolygon[1].vertexCount++;
            newPolygon[1].vertices[newPolygon[1].vertexCount] = localExitPoint;
            newPolygon[1].vertexCount++;
        }
 
        for(n = 0; n<2 ; n++)
        {
            var offset:b2Vec2;
            if (cutAdded[n]> 0)
            {
				offset =  b2Math.SubtractVV(newPolygon[n].vertices[cutAdded[n]-1],  newPolygon[n].vertices[cutAdded[n]])//Substitution
            }else{
				//Substitution
				offset = b2Math.SubtractVV(newPolygon[n].vertices[newPolygon[n].vertexCount-1], newPolygon[n].vertices[0]);
            }
            offset.Normalize();
 
			var aNewVec:b2Vec2 = b2Math.MulFV(splitSize, offset);
            newPolygon[n].vertices[cutAdded[n]] = b2Math.AddVV(newPolygon[n].vertices[cutAdded[n]], aNewVec);
 
            if (cutAdded[n] <newPolygon[n].vertexCount-2)
            {
				offset = b2Math.SubtractVV(newPolygon[n].vertices[cutAdded[n]+2], newPolygon[n].vertices[cutAdded[n]+1]);
            }else{
				offset = b2Math.SubtractVV(newPolygon[n].vertices[0], newPolygon[n].vertices[newPolygon[n].vertexCount-1]);
            }
            offset.Normalize();
 
			aNewVec = b2Math.MulFV(splitSize, offset);
            newPolygon[n].vertices[cutAdded[n]+1] = b2Math.AddVV(newPolygon[n].vertices[cutAdded[n]+1], aNewVec);
		}
 
 
        //Check if the new shapes are not too tiny. (TODO: still generates shapes which fail assert checks)
        for(n=0;n<2;n++)
            for(i=0;i<newPolygon[n].vertexCount;i++)
                for(var j=0;j<newPolygon[n].vertexCount;j++)
                    if (i != j && (b2Math.SubtractVV(newPolygon[n].vertices[i], newPolygon[n].vertices[j])).Length() <0.1)
                        return -1;
 
        for(n=0;n<2;n++){
            if (CheckPolyShape(newPolygon[n])){
                return -1;
			}
        return 0;
		}
 
    }
 
	public function Cut():void
    {
 
     	/*laserStart = new b2Vec2(5.0-0.1, 0.0);
      	laserDir = new b2Vec2(segmentLength, 0.0);
 
        segment.p1 = laserBody.GetWorldPoint(laserStart);
        segment.p2 = b2Math.AddVV(segment.p1, laserBody.GetWorldVector(laserDir));*/
 
		var shapes:Array = new Array();
		max_shapes = 64;
		var count = 1;
        count = m_world.Raycast(segment, shapes, max_shapes, false, null);
        for(var i=0;i<count;i++)
        {
            //Make sure it's a polygon, we cannot cut circles.
            if(shapes[i].GetType() != b2Shape.e_polygonShape){
                continue;
			}
 
			var b:b2Body;
            b = shapes[i].GetBody();
            //Custom check to make sure we don't cut stuff we don't want to cut.
            if (b.GetUserData() != 1){ //Ouille !
                continue;//return if we cannot pass trough uncutable shapes.
			}
 
			var polyShape:b2PolygonShape;
 
			polyShape = shapes[i];
			var pd:Array = new Array(1);
			pd[0] = new b2PolygonDef();
			pd[1] = new b2PolygonDef();
            pd[0].density = 5.0;
            pd[1].density = 5.0;
 
           if (SplitShape(polyShape, segment, 0.1, pd) == 0)
            {  
				b.DestroyShape(shapes[i]);
				b.CreateShape(pd[0]);
                b.SetMassFromShapes();
                b.WakeUp();
 
				var bd:b2BodyDef = new b2BodyDef();
                bd.userData = 1;
                bd.position = b.GetPosition();
                bd.angle = b.GetAngle();
 
				var newBody:b2Body;
               	newBody = m_world.CreateBody(bd);
				newBody.CreateShape(pd[1]);
                newBody.SetMassFromShapes();
                newBody.SetAngularVelocity(b.GetAngularVelocity());
                newBody.SetLinearVelocity(b.GetLinearVelocity());
 
				var oldBody:b2Body;
				oldBody = m_world.CreateBody(bd);
				oldBody.CreateShape(pd[0]);
               	oldBody.SetMassFromShapes();
 
				m_world.DestroyBody(b);
				bd = new b2BodyDef();
                bd.position.Set(15, 5);
            }
        }
    }
 
	public function Step(ite, ate)
    {
		if(tool == 1){
 
			laserStart = new b2Vec2(5.0-0.1, 0.0);
			laserDir = new b2Vec2(segmentLength,0.0);
			segment.p1 = laserBody.GetWorldPoint(laserStart);
			segment.p2 = laserBody.GetWorldVector(laserDir);
			segment.p2 = b2Math.AddVV(segment.p2, segment.p1);
 
			laserColor = new b2Color(0,0,1);
			dbgDraw.DrawSegment(segment.p1, segment.p2, laserColor);
 
		} else {
 
			if(Input.mouseDown){
 
			if(!firstClic){
				depX = (Input.mouseX / m_physScale);
				depY = (Input.mouseY / m_physScale);
				firstClic = true;
			}
 
			arrX = (Input.mouseX / m_physScale);
			arrY = (Input.mouseY / m_physScale);
 
			laserStart = new b2Vec2(depX, depY);
			laserDir = new b2Vec2((arrX - depX), (arrY - depY));
			segment.p1 = laserStart;
			segment.p2 = laserDir;
			segment.p2 = b2Math.AddVV(segment.p2, segment.p1);
 
			laserColor = new b2Color(0,0,1);
			dbgDraw.DrawSegment(segment.p1, segment.p2, laserColor);
 
			m_cut = 1;
			}
 
			if(!(Input.mouseDown)){
				firstClic = false;
			}
		}
    }
 
	public function mouseUpEvent(e:MouseEvent){
		if(m_cut){
			Cut();
			m_cut = null;
		}
	}
 
		/*********MEMBER DATA*********/
 
		//World
		public var m_world:b2World;
		public var m_physScale:Number = 15;
		public var m_iterations:int = 10;
		public var m_timeStep:Number = 1/30;
		public var m_input:Input;
		public var m_sprite:Sprite;
		public var dbgDraw:b2DebugDraw;
		static public var m_fpsCounter:FpsCounter = new FpsCounter();
 
		//Basics
		public const b2_maxPolygonVertices:int = 8;
		public var e_hitCollide = 1;
 
		//Laser
		public var segment:b2Segment = new b2Segment();
		public var laserBody:b2Body;
		public var segmentLength:Number = 30.0;
		public var laserStart:b2Vec2;
		public var laserDir:b2Vec2;
		public var laserColor:b2Color;
		public var max_shapes:Number = 64;
 
		//Radio Button
		public var tool:Number = 1;
		public var firstClic:Boolean = false;
		public var depX:Number;
		public var depY:Number;
		public var arrX:Number;
		public var arrY:Number;
		public var m_cut = null;
 
		public function update(e:Event):void{
 
			m_sprite.graphics.clear();
 
			// For the mouse
			if(tool == 1){
				UpdateMouseWorld()
				MouseDestroy();
				MouseDrag();
			}
 
			var physStart:uint = getTimer();
			m_world.Step(m_timeStep, m_iterations);
			m_fpsCounter.updatePhys(physStart);
 
			Step(m_timeStep, m_iterations);
 
			//Update text
			updateText();
 
			// Update counter and limit framerate
			m_fpsCounter.update();
			FRateLimiter.limitFrame(40);
		}
 
		function key_pressed(e:KeyboardEvent):void {
            switch (e.keyCode) {
               case 67 :
			  		if(tool == 1){
                    Cut();
					}
                    break;
            }
        }
 
		public function updateText():void{
 
			if(tool ==1)
				var a:String = "On";
			else
				a = "Off";
 
			mouseS.text = 'Select object with mouse : '+a;
 
			var shapes:Array = new Array();
		 	var count = m_world.Raycast(segment, shapes, 64, false, null);
 
			shapesR.text = 'Number off shapes crossed by laser : '+count;
 
			bodyCount.text = 'Bodies enable to cut : '+(m_world.GetBodyCount() - 4); //Cause we have the top, the bottom, the laser...
		}
 
 
		//The mouse
		public var m_mouseJoint:b2MouseJoint;
		static public var mouseXWorldPhys:Number;
		static public var mouseYWorldPhys:Number;
		static public var mouseXWorld:Number;
		static public var mouseYWorld:Number;
 
		public function UpdateMouseWorld():void{
			mouseXWorldPhys = ((Input.mouseX)/m_physScale);
			mouseYWorldPhys = ((Input.mouseY)/m_physScale); 
 
			mouseXWorld = (Input.mouseX); 
			mouseYWorld = (Input.mouseY); 
		}
 
		public function MouseDrag():void{
			// mouse press
			if (Input.mouseDown && !m_mouseJoint){
 
				var body:b2Body = GetBodyAtMouse();
 
				if (body)
				{
					var md:b2MouseJointDef = new b2MouseJointDef();
					md.body1 = m_world.GetGroundBody();
					md.body2 = body;
					md.target.Set(mouseXWorldPhys, mouseYWorldPhys);
					md.maxForce = 500.0 * body.GetMass();
					md.timeStep = m_timeStep;
					m_mouseJoint = m_world.CreateJoint(md) as b2MouseJoint;
					body.WakeUp();
				}
			}
 
 
			// mouse release
			if (!Input.mouseDown){
				if (m_mouseJoint)
				{
					m_world.DestroyJoint(m_mouseJoint);
					m_mouseJoint = null;
				}
			}
 
 
			// mouse move
			if (m_mouseJoint)
			{
				var p2:b2Vec2 = new b2Vec2(mouseXWorldPhys, mouseYWorldPhys);
				m_mouseJoint.SetTarget(p2);
			}
		}
 
 
 
		//======================
		// Mouse Destroy
		//======================
		public function MouseDestroy():void{
			// mouse press
			if (!Input.mouseDown && Input.isKeyPressed(68/*D*/)){
 
				var body:b2Body = GetBodyAtMouse(true);
 
				if (body)
				{
					m_world.DestroyBody(body);
					return;
				}
			}
		}
 
		//======================
		// GetBodyAtMouse
		//======================
		private var mousePVec:b2Vec2 = new b2Vec2();
		public function GetBodyAtMouse(includeStatic:Boolean=false):b2Body{
			// Make a small box.
			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);
 
			// Query the world for overlapping shapes.
			var k_maxCount:int = 10;
			var shapes:Array = new Array();
			var count:int = m_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;
		}
 
	}
}

And this is the result:

Download the full source code with all needed libraries.

Rate this post: 1 Star2 Stars3 Stars4 Stars5 Stars (10 votes, average: 4.90 out of 5)
Loading ... Loading ...
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.
Be my fan on Facebook and follow me on Twitter! Exclusive content for my Facebook fans and Twitter followers

This post has 10 comments

  1. Niall Lavigne

    on March 9, 2009 at 9:17 pm

    Wow, great job pompom! Looks like it must’ve been pretty difficult to figure out. One thing though, I can’t seem to cut the longest rectangle with the Cutter tool.

  2. szataniol

    on March 9, 2009 at 9:51 pm

    Works nice, but code is awful ;)

  3. Niall Lavigne

    on March 9, 2009 at 10:10 pm

    Oh, just noticed that the uncuttable rectangle is for using the laser, never mind :P

  4. miyijura

    on March 11, 2009 at 5:16 pm

    Beautiful work…this application have a lot of uses. I will be expecting more tuts from you.

    Thanks!

  5. Monkios

    on March 11, 2009 at 6:00 pm

    This is really incredible.

    Nice work Pompom.

  6. Gawd

    on March 11, 2009 at 8:35 pm

    The laser was very interesting.

  7. pyrite

    on March 14, 2009 at 10:10 pm

    It works but if you try to cut the box through a corner it wont work. It would be good if you could find a fix.

  8. sketchbookgames

    on March 31, 2009 at 9:43 pm

    getting an error

    i speculate that it occurred because i threw the laser box out to the left, which eventually fell out of the world.

    great work pompom
    could use a reset button, so i can have more slicing fun :)

  9. Triqui’s Picks #10 : Emanuele Feronato - italian geek and PROgrammer

    on December 21, 2009 at 11:39 am

    [...] difficulty: You can make something similar following The engine behind Splitter Flash game : working code. [...]

  10. Bruno

    on April 8, 2011 at 6:35 pm

    Someone can make this version with the last box2D? 2.1.2?

    Seems everything has changed =/