# Monthly Archives: July 2009

## Recursive 2D Structure

Actionscript:
1. [SWF(width = 600, height = 700, frameRate=24)]
2. var canvas:BitmapData = new BitmapData(stage.stageWidth,stage.stageHeight,false, 0xFFFFFF);
4.
5. var maxBranches:int = 600;
6. var branches:int = 0;
7. var startX:Number = 300
8. makeBranch(startX,690,30,-60, 60);
9.
10. function makeBranch(xp:Number, yp:Number, step:Number, min:Number, max:Number):void {
11.     var vectors:Shape = Shape(addChild(new Shape()));
12.     var cX:Number, cY:Number, eX:Number, eY:Number
13.     var dcX:Number=xp, dcY:Number=yp, deX:Number=xp, deY:Number=yp;
14.     var theta:Number = (min + Math.random()*(max-min) - 90) * Math.PI / 180;
15.     cX = xp + step * Math.cos(theta);
16.     cY = yp + step * Math.sin(theta);
17.     theta = (min + Math.random()*(max-min)-90) * Math.PI / 180;
18.     eX = cX + step * Math.cos(theta);
19.     eY = cY + step * Math.sin(theta);
20.     var run:Function = function():void{
21.          dcX +=  (cX - dcX) / 2;
22.          dcY +=  (cY - dcY) / 2;
23.          deX +=  (eX - deX) / 8;
24.          deY +=  (eY - deY) / 8;
25.          with(vectors.graphics){
26.               clear();
27.               beginFill(0xFFFFFF,0.8);
28.               lineStyle(0,0x000000,0.8);
29.               moveTo(startX, yp);
30.               lineTo(xp, yp);
31.               curveTo(dcX, dcY, deX, deY);
32.               lineTo(startX, deY);
33.          }
34.          if (Math.abs(dcX - cX) <1 && Math.abs(deX - eX) <1 && Math.abs(dcY - cY) <1 && Math.abs(deY - eY) <1){
35.              canvas.draw(vectors);
36.              removeChild(vectors);
37.              if (branches <maxBranches){
38.                  setTimeout(makeBranch, 10, deX, deY, step - Math.random(), -90, 90);
39.                  branches++;
40.                  if (int(Math.random()*2) == 1){
41.                     setTimeout(makeBranch, 10, deX, deY, step - Math.random()*3, -90, 90);
42.                     branches++;
43.                  }
44.              }
45.          }else{
46.              setTimeout(arguments.callee,  1000 / 24);
47.          }
48.     }();
49. }

This snippet uses a technique similar to what you might use to create a recursive tree. A bit of additional logic is added for bezier branches, filled shapes and animation.

WARNING: may run slow on older machines

Have a look at the swf... Posted in BitmapData, bezier, functions, misc, motion | Tagged , , | 7 Comments

## Smudge Tool #1

Actionscript:
1. [SWF(backgroundColor = 0xCCCCCC)]
2. var canvas:BitmapData = new BitmapData(stage.stageWidth,stage.stageHeight,true, 0xFFFFFFFF);
4.
8. function onComplete(evt:Event):void{
10. }
11.
12. var brush:Shape = new Shape();
13. var diameter:Number = 120;
14. var radius:Number = diameter / 2;
15. var matrix:Matrix = new Matrix();
16.
17. var brushAlpha:BitmapData = new BitmapData(diameter, diameter, true, 0x00000000);
19. brushAlpha.draw(brush);
20.
21. var xp:Number = 0, yp:Number = 0, px:Number = 0, py:Number = 0;
22. var dx:Number, dy:Number;
23.
25. function onLoop(evt:Event):void {
26.     xp = mouseX - radius;
27.     yp = mouseY - radius;
28.     dx = xp - px;
29.     dy = yp - py;
30.     canvas.copyPixels(canvas,
31.                     new Rectangle(px, py, diameter, diameter),
32.                     new Point(xp, yp), brushAlpha, new Point(0,0), true);
33.     px = xp;
34.     py = yp
35. }
36.
38.     matrix.createGradientBox(diameter, diameter, 0, 0, 0);
39.     with (brush.graphics){
42.     }
43. }

This is a quick snippet I wrote while thinking about how to create a smudge tool using BitmapData... it isn't perfect, but it's a good first step. It still needs some linear interpolation and I may need to do some pixel pushing to prevent a strange alpha anomaly that causes the brush to get darker than expected....

Here is the swf...

Posted in BitmapData | Tagged , , | 1 Comment

## Isometric Cones (spikes)

Actionscript:
1. [SWF(width = 600, height = 500, backgroundColor=0xFFFFFF)]
2.
3. var canvas:BitmapData = new BitmapData(stage.stageWidth,stage.stageHeight,false, 0xFFFFFF);
5.
6. var matrix:Matrix = new Matrix();
7. var grid:Sprite =   new Sprite();
8. grid.filters = [new DropShadowFilter(4, 0, 0, 0.05, 20, 10)];
9. matrix.rotate(Math.PI / 4);
10. matrix.scale(1, 0.6);
11. matrix.translate(stage.stageWidth / 2, stage.stageHeight / 2 + 50);
12. grid.transform.matrix = matrix;
13.
14. var rowCol:int = 9;
15. var spikeNum:Number = rowCol * rowCol;
16. var diameter:Number = 30;
17. var space:Number  = diameter + 10;
18. var halfGridSize:Number  = rowCol * space / 2;
21. function onLoop(evt:Event):void {
22.     radius  = diameter / 2;
23.     grid.y -= 0.9;
24.     grid.graphics.clear();
25.     for (var i:Number = 0; i<spikeNum; i++){
26.        var xp:Number = i % rowCol;
27.        var yp:Number = int(i / rowCol);
28.        drawSpike(xp * space - halfGridSize, yp * space - halfGridSize, (xp + yp) / 4, 0, -yp);
29.     }
30.     canvas.draw(grid, grid.transform.matrix);
31.     diameter -= 0.5;
32.     if (diameter <3){
33.         removeEventListener(Event.ENTER_FRAME, onLoop);
34.     }
35. }
36. function drawSpike(xp:Number, yp:Number, rot:Number = 0, xOff:Number=0, yOff:Number = 0):void{
38.     with (grid.graphics){
41.     }
42. }

This snippet draws some isometric cones. Here are some images created by tweaking a few of the values: ## Isometric Transformation Matrix

Actionscript:
1. var grid:Sprite = Sprite(addChild(new Sprite()));
2. var matrix:Matrix = new Matrix();
3. // make the grid sprite look isometric using the transform.matrix property
4. matrix.rotate(Math.PI / 4);
5. matrix.scale(1, 0.5);
6. matrix.translate(stage.stageWidth / 2, stage.stageHeight / 2);
7. grid.transform.matrix = matrix;
8.
9. // draw a grid of circles to show that it does in fact look isometric
10. var rowCol:Number = 8;
11. var num:Number = rowCol * rowCol;
12.
13. var diameter:Number = 40;
14. var radius:Number = diameter / 2;
15. var space:Number = diameter + 10;
16. var halfGridSize:Number = rowCol * space / 2;
17.
18. grid.graphics.beginFill(0xFF0000);
19. for (var i:int = 0; i<num; i++){
20.    grid.graphics.drawCircle(i % 8 * space - halfGridSize, int(i / 8) * space - halfGridSize, radius);
21. }

This snippet shows how to use transform.matrix to make a DisplayObject look isometric. This can also be achieved with nesting.

In the case of this grid of red circles, we rotate it 45 degrees, scale it 50% on the y and move it to the center of the stage (lines 4-6). Posted in DisplayObject, misc | 3 Comments

## Convex/Concave Function

Didn't get a chance to post today but will do two posts tomorrow... been looking into things related to convex and concave functions... here are some links related to that topic:

Will probably have at least two snippets related to this in the near future...

## Another Matrix.transformPoint()

Actionscript:
1. [SWF(width = 628, height=500)]
2. var trans:Matrix = new Matrix();
3. var pnt:Point = new Point();
4. x = stage.stageWidth / 2;
5. y = stage.stageHeight / 2;
7. function onLoop(evt:Event):void {
8.     graphics.clear();
9.     graphics.lineStyle(10, 0x000000, 1, false, LineScaleMode.NORMAL, CapsStyle.SQUARE);
10.     pnt.x = 0;
11.     pnt.y = 0;
12.     trans.identity();
13.     trans.translate(15,15);
14.     trans.rotate(mouseX/10 * Math.PI / 180);
15.     graphics.moveTo(pnt.x, pnt.y);
16.     for (var i:int = 0; i<12; i++){
17.         pnt = trans.transformPoint(pnt);
18.         graphics.lineTo(pnt.x, pnt.y);
19.     }
20. }

I was thinking about processing and OpenGL today... I always really enjoyed using matrix stuff, working with the matrix stack, using push() and pop() etc... Understanding how to use matrices is pretty important when your doing advanced graphics in actionscript. If you haven't messed with matrices, this snippet is a good place to start. It uses Matrix.transformPoint() to draw a series of connected lines that are controlled by the x location of the mouse. Back before flash 8, this kind of thing was usually done using trig or MovieClip nesting...

I posted another snippet on this topic awhile back.

Posted in matrix, motion | 2 Comments

## setVector vs copyPixels

Actionscript:
1. var brushNum:int = 10000;
2. var canvas:BitmapData = new BitmapData(800,600,false, 0x000000);
4.
5. var shape:Shape = new Shape();
6. with(shape.graphics) beginFill(0xFF0000), drawCircle(10,10,5);
7. shape.filters = [new BlurFilter(6, 6, 4)];
8.
9. var brush:BitmapData = new BitmapData(20, 20, false, 0x000000);
10. brush.draw(shape, shape.transform.matrix);
11.
12.
13.
14. // make sure brushVect is fixed length
15. var brushVect:Vector.<uint> = new Vector.<uint>(brush.width * brush.height, true);
16. var tempVect:Vector.<uint> =  brush.getVector(brush.rect);
17. var i:int = 0;
18. // quick hack to retain fixed length
19. for (i = 0; i<brushVect.length; i++){
20.     brushVect[i] = tempVect[i];
21. }
22.
23. canvas.setVector(brush.rect, brushVect);
24.
25. var pnt:Point = new Point();
26. var brushRect:Rectangle = brush.rect;
27. var destRect:Rectangle = brushRect.clone();
28. var locX:Vector.<Number> = new Vector.<Number>(brushNum, true);
29. var locY:Vector.<Number> = new Vector.<Number>(brushNum, true);
30. for (i= 0; i<brushNum; i++){
31.     locX[i] = Math.random() * stage.stageWidth - 10;
32.     locY[i] = Math.random() * stage.stageHeight - 10;
33. }
34.
35. var iterations:int = 50;
36. var timer:Number;
37. var avg:Vector.<Number> = new Vector.<Number>();
38. trace("copyPixels");
40. function onLoop(evt:Event):void {
41.        canvas.fillRect(canvas.rect, 0x000000);
42.        var i:int = brushNum;
43.        if (drawMode == "copyPixels"){
44.            timer = getTimer();
45.            canvas.lock();
46.            while(--i> -1){
47.                 pnt.x = locX[i];
48.                 pnt.y = locY[i];
49.                 // copyPixels can easily do alpha manipulation, setVector cannot (see comment below);
50.                 canvas.copyPixels(brush, brushRect, pnt);//, null, null, true);
51.            }
52.            canvas.unlock();
53.            avg.push(getTimer() - timer);
54.        }else{
55.            timer = getTimer();
56.            canvas.lock();
57.            while(--i> -1){
58.                destRect.x = locX[i];
59.                destRect.y = locY[i];
60.                canvas.setVector(destRect, brushVect);
61.            }
62.            canvas.unlock();
63.            avg.push(getTimer() - timer);
64.        }
65.        // take an average of iterations iterations
66.        if (avg.length == iterations){
67.            var average:Number = 0;
68.            for (i = 0; i<iterations; i++){
69.                  average += avg[i];
70.            }
71.            trace(average / iterations);
72.            avg = new Vector.<Number>();
73.        }
74. }
75.
76. var drawMode:String = "copyPixels";
78. function onKeyReleased(evt:Event):void{
79.     avg = new Vector.<Number>();
80.     drawMode = (drawMode == "copyPixels") ? "setVector" : "copyPixels"
81.     trace(drawMode);
82. }

In the comments of a post from a few days back, Piergiorgio Niero mentioned that setVector may be faster than copyPixels. To see if this was true, Piergiorgio and I each tried a few things and while Piergiorgio seemed to reach some conclusions... I wasn't sure we had properly tested to see which one was actually faster.

I created the above snippet to help test to see which is indeed faster. This snippet draws 10,000 small 20x20 pixel graphics to the stage. There is no animation because I wanted to try and isolate the speed of the setVector and copyPixels calls. These are the results on my macbook pro 2.4 ghz duo:

// average speed of 50 iterations of drawing
copyPixels
15.1
15.68
15.44
15.46
15.62
15.74
15.68
setVector
32.6
32.6
31.1
32.1
32.82
32.54
copyPixels
15.48
15.62
15.74
15.46
15.42
15.44
15.64
setVector
32.62
32.8
33.08
32.48
32.74
32.32

If you interested in this, post your results in the comments along with the type of computer you're using. I have a feeling there will be a wide variety of results... just make sure you're not using the flash debug player, as that can act significantly different than the release version of the player.

# setVector() and copyPixels() Usage

Something to note here is that setVector and copyPixels aren't normally suitable for the same thing. copyPixels is used to move a rectangle of pixels from one BitmapData to another - you can easily do advanced alpha channel manipulation and scaling with it and you don't have to do pixel by pixel logic. setVector is used to do pixel by pixel manipulation - it is a sort of mature/advanced setPixel function that allows you to do logic on a Vector of uints and then set the pixels of a rectangular region of a BitmapData object equal to the data within that Vector. So if you need to do alpha manipulation or image scaling with setVector you'll find yourself running into some more advanced programming than copyPixels requires... and if you tried to do pixel by pixel manipulation with copyPixels... well, that just isn't what it was meant to be used for...

I'm always wary of these kind of speed tests... if anyone has suggestions about how to improve this test, please feel free to comment.

UPDATE katopz pointed out I should use fixed length Vectors... so I changed the brushVect instantiation code slightly. I didn't do it for the avg vector because it only has 50 values and it doesn't help improve the speed of setVector and copyPixels in any way and complicates the code slightly... it's hard to decide which optimization techniques you should choose to make habbit and which ones you should only whip out when you need speed...

Posted in BitmapData, Vector | 7 Comments

## BlendModes & Blur

Actionscript:
1. [SWF(width = 750, height = 750)]
2. var canvas:BitmapData = new BitmapData(750,1000,false, 0x000000);
4.
8. var bit:BitmapData
9. var blurred:BitmapData;
11.     bit = Bitmap(evt.target.content).bitmapData;
12.     blurred = bit.clone();
13.     blurred.applyFilter(blurred, blurred.rect, new Point(0,0), new BlurFilter(4, 4, 6));
14.     var blends:Array = [BlendMode.NORMAL,BlendMode.ADD, BlendMode.DARKEN,BlendMode.HARDLIGHT,BlendMode.LIGHTEN, BlendMode.MULTIPLY, BlendMode.OVERLAY,BlendMode.SCREEN, BlendMode.DIFFERENCE];
15.     var m:Matrix = new Matrix();
16.     for (var i:int = 0; i<blends.length; i++){
17.         m.tx = i % 3 * 250;
18.         m.ty = int(i / 3) * 250;
19.         canvas.draw(bit, m);
20.         if (i> 0){
21.         canvas.draw(blurred, m, null, blends[i]);
22.         }
23.     }
24. }

When I used to use photoshop for more than just the most basic of things, I would use a simple technique that employed layer modes (blend modes in flash) and blur. Sometimes, if I had a low quality image that I wanted to make look a little better, or just wanted to give an image a subtle effect, I would duplicate the layer the image was on, blur it and then go through all the layer modes on that duplicate layer until I found something I liked.

This snippet does the same thing with a few select blend modes: This isn't the greatest image to illustrate the effect, but I didn't feel like digging something better up. Two notable swatches are the upper right (darken) and the lower middle (screen).

Posted in BitmapData, misc | Tagged , , | 6 Comments

## Cut Image into Squares (copyPixels)

Actionscript:
1. [SWF(width=650, height=650)]
5.
6. var w:Number;
7. var h:Number;
8. var rows:Number = 20;
9. var cols:Number = 20;
10. var tiles:Vector.<BitmapData> = new Vector.<BitmapData>();
11. var locX:Vector.<Number> = new Vector.<Number>();
12. var locY:Vector.<Number> = new Vector.<Number>();
13. var rX:Vector.<Number> = new Vector.<Number>();
14. var rY:Vector.<Number> = new Vector.<Number>();
15. var sX:Vector.<Number> = new Vector.<Number>();
16. var sY:Vector.<Number> = new Vector.<Number>();
18.     w = evt.target.width;
19.     h = evt.target.height;
20.     var image:BitmapData = Bitmap(evt.target.content).bitmapData;
21.     var tileWidth:Number = w / cols;
22.     var tileHeight:Number = h / rows;
23.     var inc:int = 0;
24.     var pnt:Point = new Point();
25.     var rect:Rectangle = new Rectangle(0,0,tileWidth,tileHeight);
26.     for (var i:int = 0; i<rows; i++){
27.         for (var j:int = 0; j<cols; j ++){
28.              var currTile:BitmapData= new BitmapData(tileWidth, tileHeight, true, 0x00000000);
29.              rect.x = j * tileWidth;
30.              rect.y = i * tileHeight;
31.              currTile.copyPixels(image, rect, pnt, null, null, true);
32.              tiles[inc] = currTile;
33.              rect.x += 25;
34.              rect.y += 25;
35.              sX[inc] = rect.x;
36.              sY[inc] = rect.y;
37.              locX[inc] = rX[inc] = -rect.width * 2
38.              locY[inc] = rY[inc] =  Math.random() * stage.stageHeight;
39.              setTimeout(startAnimation, inc *4 + 100, inc, rect.x, rect.y);
40.              inc++;
41.         }
42.     }
43. }
44.
45. function startAnimation(index:int, dx:Number, dy:Number):void{
46.     var interval:Number;
47.     var animate:Function = function(index:int):void{
48.         locX[index] += (dx - locX[index]) / 4;
49.         locY[index] += (dy - locY[index]) / 4;
50.         if (Math.abs(locX[index] - dx) <1 && Math.abs(locY[index] - dy)<1){
51.             locX[index] = dx;
52.             locY[index] = dy;
53.             clearInterval(interval);
54.         }
55.     }
56.    interval = setInterval(animate, 32, index);
57. }
58.
59. var canvas:BitmapData = new BitmapData(stage.stageWidth,stage.stageHeight,false, 0xFFFFFF);
61.
62. var loc:Point = new Point();
64. function onLoop(evt:Event):void {
65.       canvas.fillRect(canvas.rect, 0xFFFFFF);
66.       for (var i:int = 0; i<tiles.length; i++){
67.             var tile:BitmapData= tiles[i];
68.             loc.x = locX[i];
69.             loc.y = locY[i];
70.             canvas.copyPixels(tile, tile.rect, loc, null, null, true);
71.       }
72. }

This snippet cuts a dynamically loaded image into a series of smaller BitmapData instances. These BitmapData instances are then drawn to a canvas BitmapData using copyPixels. The BitmapData.copyPixels() method is extremely fast - so this has some advantages over yesterdays post (which did basically the same thing with Sprites). I used setInterval and setTimeout to do a simple demo animation, but I recommend using TweenLite on a real project.

Posted in BitmapData, Vector | Tagged , , | 10 Comments

## Cut Image Into Squares

Actionscript:
1. [SWF(width=650, height=650)]
5. x = y = 25;
6. var w:Number;
7. var h:Number;
8. var rows:Number = 20;
9. var cols:Number = 20;
10. var tiles:Vector.<Sprite> = new Vector.<Sprite>();
12.     w = evt.target.width;
13.     h = evt.target.height;
14.     var image:BitmapData = Bitmap(evt.target.content).bitmapData;
15.     var tileWidth:Number = w / cols;
16.     var tileHeight:Number = h / rows;
17.     var inc:int = 0;
18.     var pnt:Point = new Point();
19.     var rect:Rectangle = new Rectangle(0,0,tileWidth,tileHeight);
20.     for (var i:int = 0; i<rows; i++){
21.         for (var j:int = 0; j<cols; j ++){
22.              var currTile:BitmapData= new BitmapData(tileWidth, tileHeight, true, 0x00000000);
23.              rect.x = j * tileWidth;
24.              rect.y = i * tileHeight;
25.              currTile.copyPixels(image, rect, pnt, null, null, true);
26.              var bit:Bitmap = new Bitmap(currTile, "auto", false);
27.              var s:Sprite = tiles[inc] = Sprite(addChild(new Sprite()));
28.              // offset them a little bit to show that they are in fact tiles.
29.              s.x = rect.x + Math.random()*10 - 5;
30.              s.y = rect.y + Math.random()*10 - 5;
31.
32.             /* If you have TweenLite, try something like this:
33.              s.x = rect.x;
34.              s.y = rect.y;
35.              TweenLite.from(s, 0.3, {x:Math.random()*stage.stageWidth, y:Math.random()*stage.stageHeight, alpha:0, delay:inc / 50});
36.              */
38.              inc++;
39.         }
40.     }
41. }

This snippet shows how to cut a dynamically loaded image up into small squares (or rectangles). Each square is placed into a sprite and these sprites are stored in a vector for later use. This kind of thing could be useful in conjunction with tweening engines to create transition effects. If you have TweenLite on your machine you could add the import statement "import gs.TweenLIte" and uncomment lines 33-35.

For the purposes of this demo I just offset each sprite slightly to show that it the loaded image has in face been cut up. Here are a few stills with created by altering the rows and cols variables: Without any animation, this snippet is a bit boring - again, using TweenLite or some other tweening engine is the way to go.

Posted in BitmapData, external data | Tagged , , , | 12 Comments

• 