Browse the notebook

Tag: as3

Wave Trick

[SWF(width=800, height=600)]
var dupes:int = 5
var pntNum:int = 180;

var hh:Number = stage.stageHeight / 2;
var points:Vector.<Number> = new Vector.<Number>();
var vel:Vector.<Number> = new Vector.<Number>();
var cmds:Vector.<int> = new Vector.<int>();
var vectors:Vector.<Shape> = new Vector.<Shape>();

for (var i:int = 0; i<dupes; i++){
	vectors[i] = Shape(addChildAt(new Shape(),0));
	vectors[i].x = 10 * i;
	vectors[i].y = -10 * i;
}
var index:int = 0;
for (i = 0; i<pntNum; i++){
	points[index++] = 10 + i * 4;
	points[index++] = hh;
	cmds[i] = 2;
	vel[i] = 0;
}
cmds[0] = 1;
addEventListener(Event.ENTER_FRAME, onLoop);
function onLoop(evt:Event):void {	
   index = 1;
   points[index] += (mouseY - points[index]) / 12;
   for (var i:int = 3; i<points.length; i+=2){
         points[i] += vel[index];
		 vel[index] += ((points[i] - points[i - 2]) * -0.16 - vel[index]) / 4;
		 index++;
   }
   for (i = 0; i<dupes; i++){
	   var c:uint =  255 - 255 / i;
	   with (vectors[i].graphics){
		   clear();
		   lineStyle(0,c << 16 | c << 8 | c);
		   drawPath(cmds, points);
	   }
   }
}

This snippet uses elasticity to create an interesting wave effect. I first stumbled upon this technique back in my director days…


Have a look at the swf here…

Read snippet: Wave Trick

Summarize Contents of Array

var a:Array = [true, true, true, false, false, true, true, true, false];

var counter:int = 0;
var prev:Boolean;
var summary:Array = [];
for (var i:int = 1; i<a.length; i++){
	prev = a[i - 1]
	counter++;
	if (prev != a[i]){
		if (prev){
			summary.push("true: "+ counter);
		}else{
			summary.push("false: "+ counter);
		}
		counter = 0;
	}
}
summary.push(a[i-1].toString()+": "+ (counter+1));

trace(summary);

/** outputs:
true: 3,false: 2,true: 3,false: 1
*/

This is a handy way to summarize the contents of an array of boolean values.

Read snippet: Summarize Contents of Array

Sphere of Squares

[SWF(width = 600, height = 600, backgroundColor=0x000000)]
var squareNum:int  = 1000;
var hw:Number = stage.stageWidth / 2;
var hh:Number = stage.stageHeight / 2;
// verts defines a single square
var verts:Vector.<Number> = Vector.<Number>([-20, 0, 0, 20, 0, 0, 20, 0, 40, -20, 0, 40, -20, 0, 0]);
var cmds:Vector.<int> = Vector.<int>([1,2,2,2,2]);
var tempVerts:Vector.<Number> = new Vector.<Number>();
var newVerts:Vector.<Number> = new Vector.<Number>();
var pVerts:Vector.<Number> = new Vector.<Number>(10 * squareNum);
var uv:Vector.<Number> = new Vector.<Number>(15 * squareNum);
var vectors:Shape = new Shape();
var canvas:BitmapData = new BitmapData(stage.stageWidth,stage.stageHeight,false, 0x000000);
addChild(new Bitmap(canvas));
var blurred:BitmapData = new BitmapData(stage.stageWidth,stage.stageHeight,false, 0x000000);
var blur:BlurFilter = new BlurFilter(20,20,1);

var m:Matrix3D = new Matrix3D();

var radius:Number = 200;
// duplicate the verts array a bunch of times
// each time moving the square to a random place on the
// circumference of a sphere
for (var i:int = 0; i<squareNum; i++){
	m.identity();
	var s:Number = Math.random()*.5 + .5;
	m.appendScale(s, s, s);
	m.appendRotation(90,Vector3D.X_AXIS);
	m.appendTranslation(0, 0, radius);
	m.appendRotation(Math.random()*360,Vector3D.X_AXIS);
	m.appendRotation(Math.random()*360,Vector3D.Y_AXIS);
	m.appendRotation(Math.random()*360,Vector3D.Z_AXIS);
	m.transformVectors(verts,tempVerts);
	newVerts = newVerts.concat(tempVerts);
	cmds = cmds.concat(Vector.<int>([1,2,2,2,2]));
}
newVerts.fixed = pVerts.fixed = uv.fixed = true;
var dx:Number = 0, dy:Number = 0;
var pnt:Point = new Point();
addEventListener(Event.ENTER_FRAME, onLoop);
function onLoop(evt:Event):void {
	   dx += (mouseX - dx) / 4;
	   dy += (mouseY - dy) / 4;
	   m.identity();
	   m.appendRotation(dx,Vector3D.Z_AXIS);
	   m.appendRotation(dy,Vector3D.X_AXIS);
	   m.appendTranslation(hw,hh, 0);
	   Utils3D.projectVectors(m, newVerts, pVerts, uv);
	   with(vectors.graphics){
		   clear();
		   beginFill(0xFFFFFF);
		   drawCircle(hw, hh, radius+10);
		   beginFill(0x000000);
		   drawPath(cmds, pVerts, GraphicsPathWinding.NON_ZERO);
	   }
	   canvas.fillRect(canvas.rect, 0x000000);
	   canvas.draw(vectors);
	   blurred.copyPixels(canvas, canvas.rect, pnt);
	   blurred.applyFilter(blurred,blurred.rect, pnt, blur);
	   canvas.draw(blurred, null, null, BlendMode.SCREEN);
}

This snippet is similar to yesterdays post but the visual result is rather different. This one does a little more Matrix3D stuff resulting in a sphere made up entirely of squares. This is obscured by the size of the squares and the fact that they overlap and cut each other up. BitmapData a BlurFilter and a BlendMode give the entire thing a slight glow…


Have a look at the swf here…

Read snippet: Sphere of Squares

Recursive 2D Structure

[SWF(width = 600, height = 700, frameRate=24)]
var canvas:BitmapData = new BitmapData(stage.stageWidth,stage.stageHeight,false, 0xFFFFFF);
addChild(new Bitmap(canvas));

var maxBranches:int = 600;
var branches:int = 0;
var startX:Number = 300
makeBranch(startX,690,30,-60, 60);

function makeBranch(xp:Number, yp:Number, step:Number, min:Number, max:Number):void {
	var vectors:Shape = Shape(addChild(new Shape()));
	var cX:Number, cY:Number, eX:Number, eY:Number
	var dcX:Number=xp, dcY:Number=yp, deX:Number=xp, deY:Number=yp;
	var theta:Number = (min + Math.random()*(max-min) - 90) * Math.PI / 180;
	cX = xp + step * Math.cos(theta);
	cY = yp + step * Math.sin(theta);
	theta = (min + Math.random()*(max-min)-90) * Math.PI / 180;
	eX = cX + step * Math.cos(theta);
	eY = cY + step * Math.sin(theta);
	var run:Function = function():void{
	     dcX +=  (cX - dcX) / 2;
		 dcY +=  (cY - dcY) / 2;
		 deX +=  (eX - deX) / 8;
		 deY +=  (eY - deY) / 8;
		 with(vectors.graphics){
			  clear();
			  beginFill(0xFFFFFF,0.8);
			  lineStyle(0,0x000000,0.8);
			  moveTo(startX, yp);
			  lineTo(xp, yp);
			  curveTo(dcX, dcY, deX, deY);
			  lineTo(startX, deY);
		 }
		 if (Math.abs(dcX - cX) < 1 && Math.abs(deX - eX) < 1 && Math.abs(dcY - cY) < 1 && Math.abs(deY - eY) < 1){
			 canvas.draw(vectors);
			 removeChild(vectors);
			 if (branches < maxBranches){
				 setTimeout(makeBranch, 10, deX, deY, step - Math.random(), -90, 90);
				 branches++;
				 if (int(Math.random()*2) == 1){
				    setTimeout(makeBranch, 10, deX, deY, step - Math.random()*3, -90, 90);
					branches++;
				 }
			 }
		 }else{
		     setTimeout(arguments.callee,  1000 / 24);
		 }
	}();
}

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…

Read snippet: Recursive 2D Structure

Smudge Tool #1

[SWF(backgroundColor = 0xCCCCCC)]
var canvas:BitmapData = new BitmapData(stage.stageWidth,stage.stageHeight,true, 0xFFFFFFFF);
addChild(new Bitmap(canvas));

var loader:Loader = new Loader();
loader.load(new URLRequest("https://actionsnippet.com/wp-content/chair.jpg"));
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete);
function onComplete(evt:Event):void{
	canvas.draw(loader);
}

var brush:Shape = new Shape();
var diameter:Number = 120;
var radius:Number = diameter / 2;
var matrix:Matrix = new Matrix();

var brushAlpha:BitmapData = new BitmapData(diameter, diameter, true, 0x00000000);
drawRadial();
brushAlpha.draw(brush);

var xp:Number = 0, yp:Number = 0, px:Number = 0, py:Number = 0;
var dx:Number, dy:Number;

stage.addEventListener(MouseEvent.MOUSE_MOVE, onLoop);
function onLoop(evt:Event):void {
	xp = mouseX - radius;
	yp = mouseY - radius;
	dx = xp - px;
	dy = yp - py;
	canvas.copyPixels(canvas,
					new Rectangle(px, py, diameter, diameter),
					new Point(xp, yp), brushAlpha, new Point(0,0), true);
	px = xp;
	py = yp
}

function drawRadial():void{
	matrix.createGradientBox(diameter, diameter, 0, 0, 0);
	with (brush.graphics){
		beginGradientFill(GradientType.RADIAL, [0xFFFFFF, 0xFFFFFF], [1,0], [0, 255], matrix, SpreadMethod.PAD);
		drawCircle(radius, radius, radius);
	}
}

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…

Read snippet: Smudge Tool #1

Isometric Cones (spikes)

[SWF(width = 600, height = 500, backgroundColor=0xFFFFFF)]

var canvas:BitmapData = new BitmapData(stage.stageWidth,stage.stageHeight,false, 0xFFFFFF);
addChild(new Bitmap(canvas));

var matrix:Matrix = new Matrix();
var grid:Sprite =   new Sprite();
grid.filters = [new DropShadowFilter(4, 0, 0, 0.05, 20, 10)];
matrix.rotate(Math.PI / 4);
matrix.scale(1, 0.6);
matrix.translate(stage.stageWidth / 2, stage.stageHeight / 2 + 50);
grid.transform.matrix = matrix;

var rowCol:int = 9;
var spikeNum:Number = rowCol * rowCol;
var diameter:Number = 30;
var space:Number  = diameter + 10;
var halfGridSize:Number  = rowCol * space / 2;
var radius:Number;
addEventListener(Event.ENTER_FRAME, onLoop);
function onLoop(evt:Event):void {
	radius  = diameter / 2;
	grid.y -= 0.9;
	grid.graphics.clear();
	for (var i:Number = 0; i<spikeNum; i++){
	   var xp:Number = i % rowCol;
	   var yp:Number = int(i / rowCol);
	   drawSpike(xp * space - halfGridSize, yp * space - halfGridSize, (xp + yp) / 4, 0, -yp);
	}
	canvas.draw(grid, grid.transform.matrix);
	diameter -= 0.5;
	if (diameter < 3){
		removeEventListener(Event.ENTER_FRAME, onLoop);
	}
}
function drawSpike(xp:Number, yp:Number, rot:Number = 0, xOff:Number=0, yOff:Number = 0):void{
	matrix.createGradientBox(diameter, diameter, rot, xp - radius + yOff, yp - radius + xOff);
	with (grid.graphics){
		beginGradientFill(GradientType.LINEAR, [0xFFFFFF, 0x999999], [1,1], [0, 255], matrix, SpreadMethod.PAD);
		drawCircle(xp, yp, radius);
	}
}

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

Read snippet: Isometric Cones (spikes)

BlendModes & Blur

[SWF(width = 750, height = 750)]
var canvas:BitmapData = new BitmapData(750,1000,false, 0x000000);
addChild(new Bitmap(canvas));

var loader:Loader = new Loader();
loader.load(new URLRequest("https://actionsnippet.com/imgs/paint.jpg"));
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoaded);
var bit:BitmapData
var blurred:BitmapData;
function onLoaded(evt:Event):void{
	bit = Bitmap(evt.target.content).bitmapData;
	blurred = bit.clone();
	blurred.applyFilter(blurred, blurred.rect, new Point(0,0), new BlurFilter(4, 4, 6));
	var blends:Array = [BlendMode.NORMAL,BlendMode.ADD, BlendMode.DARKEN,BlendMode.HARDLIGHT,BlendMode.LIGHTEN, BlendMode.MULTIPLY, BlendMode.OVERLAY,BlendMode.SCREEN, BlendMode.DIFFERENCE];
	var m:Matrix = new Matrix();
	for (var i:int = 0; i<blends.length; i++){
		m.tx = i % 3 * 250;
		m.ty = int(i / 3) * 250;
		canvas.draw(bit, m);
		if (i > 0){
	    canvas.draw(blurred, m, null, blends[i]);
		}
	}
}

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).

Read snippet: BlendModes & Blur

Cut Image into Squares (copyPixels)

[SWF(width=650, height=650)]
var loader:Loader = new Loader();
loader.load(new URLRequest("https://actionsnippet.com/wp-content/chair.jpg"));
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoaded);

var w:Number;
var h:Number;
var rows:Number = 20;
var cols:Number = 20;
var tiles:Vector.<BitmapData> = new Vector.<BitmapData>();
var locX:Vector.<Number> = new Vector.<Number>();
var locY:Vector.<Number> = new Vector.<Number>();
var rX:Vector.<Number> = new Vector.<Number>();
var rY:Vector.<Number> = new Vector.<Number>();
var sX:Vector.<Number> = new Vector.<Number>();
var sY:Vector.<Number> = new Vector.<Number>();
function onLoaded(evt:Event):void{
	w = evt.target.width;
	h = evt.target.height;
	var image:BitmapData = Bitmap(evt.target.content).bitmapData;
	var tileWidth:Number = w / cols;
	var tileHeight:Number = h / rows;
	var inc:int = 0;
	var pnt:Point = new Point();
	var rect:Rectangle = new Rectangle(0,0,tileWidth,tileHeight);
	for (var i:int = 0; i<rows; i++){
		for (var j:int = 0; j<cols; j ++){
			 var currTile:BitmapData= new BitmapData(tileWidth, tileHeight, true, 0x00000000);
			 rect.x = j * tileWidth;
			 rect.y = i * tileHeight;
			 currTile.copyPixels(image, rect, pnt, null, null, true);
			 tiles[inc] = currTile;
			 rect.x += 25;
			 rect.y += 25;
			 sX[inc] = rect.x;
			 sY[inc] = rect.y;
			 locX[inc] = rX[inc] = -rect.width * 2
			 locY[inc] = rY[inc] =  Math.random() * stage.stageHeight;
			 setTimeout(startAnimation, inc *4 + 100, inc, rect.x, rect.y);
			 inc++;
		}
	}
}

function startAnimation(index:int, dx:Number, dy:Number):void{
	var interval:Number;
	var animate:Function = function(index:int):void{
		locX[index] += (dx - locX[index]) / 4;
		locY[index] += (dy - locY[index]) / 4;
		if (Math.abs(locX[index] - dx) < 1 && Math.abs(locY[index] - dy)<1){
			locX[index] = dx;
			locY[index] = dy;
		    clearInterval(interval);
		}
	}
   interval = setInterval(animate, 32, index);
}

var canvas:BitmapData = new BitmapData(stage.stageWidth,stage.stageHeight,false, 0xFFFFFF);
addChild(new Bitmap(canvas));

var loc:Point = new Point();
addEventListener(Event.ENTER_FRAME, onLoop);
function onLoop(evt:Event):void {
	  canvas.fillRect(canvas.rect, 0xFFFFFF);
      for (var i:int = 0; i<tiles.length; i++){
		    var tile:BitmapData= tiles[i];
			loc.x = locX[i];
			loc.y = locY[i];
		    canvas.copyPixels(tile, tile.rect, loc, null, null, true);
	  }
}

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.

Read snippet: Cut Image into Squares (copyPixels)

Cut Image Into Squares

[SWF(width=650, height=650)]
var loader:Loader = new Loader();
loader.load(new URLRequest("https://actionsnippet.com/wp-content/chair.jpg"));
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoaded);
x = y = 25;
var w:Number;
var h:Number;
var rows:Number = 20;
var cols:Number = 20;
var tiles:Vector.<Sprite> = new Vector.<Sprite>();
function onLoaded(evt:Event):void{
	w = evt.target.width;
	h = evt.target.height;
	var image:BitmapData = Bitmap(evt.target.content).bitmapData;
	var tileWidth:Number = w / cols;
	var tileHeight:Number = h / rows;
	var inc:int = 0;
	var pnt:Point = new Point();
	var rect:Rectangle = new Rectangle(0,0,tileWidth,tileHeight);
	for (var i:int = 0; i<rows; i++){
		for (var j:int = 0; j<cols; j ++){
			 var currTile:BitmapData= new BitmapData(tileWidth, tileHeight, true, 0x00000000);
			 rect.x = j * tileWidth;
			 rect.y = i * tileHeight;
			 currTile.copyPixels(image, rect, pnt, null, null, true);
			 var bit:Bitmap = new Bitmap(currTile, "auto", false); 
			 var s:Sprite = tiles[inc] = Sprite(addChild(new Sprite()));
			 // offset them a little bit to show that they are in fact tiles.
			 s.x = rect.x + Math.random()*10 - 5;
			 s.y = rect.y + Math.random()*10 - 5;
			 
			/* If you have TweenLite, try something like this:
			 s.x = rect.x;
			 s.y = rect.y;
			 TweenLite.from(s, 0.3, {x:Math.random()*stage.stageWidth, y:Math.random()*stage.stageHeight, alpha:0, delay:inc / 50});
			 */
			 s.addChild(bit);
			 inc++;
		}
	}
}

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.

Read snippet: Cut Image Into Squares

BitmapData & Particles

stage.frameRate = 30;
stage.quality = StageQuality.LOW;

var canvas:BitmapData = new BitmapData(400,400,false, 0x000000);
addChild(new Bitmap(canvas));
var overlay:BitmapData = new BitmapData(400,400,true, 0x12000000);
var particles:Dictionary = new Dictionary(true);

var blur:Array = [new BlurFilter(8,8,1)];
	
addEventListener(Event.ENTER_FRAME, onLoop);
function onLoop(evt:Event):void {
	for (var i:int= 0; i<3; i++) createParticle();
	canvas.copyPixels(overlay, canvas.rect, new Point(0,0), null, null, true);
}
 
function createParticle():void{
	var s:MovieClip = new MovieClip();
	var diameter:Number = Math.random()*50 + 2;
	var radius:Number = diameter / 2;
	var mat:Matrix = new Matrix();
    mat.createGradientBox(diameter, diameter, 0, -radius, -radius);
	mat.createGradientBox(diameter, diameter, 0, -radius, -radius);
    s.graphics.beginGradientFill(GradientType.RADIAL, [ 0xFFCC32, 0xFF0000], [0.5,0.8], [0, 255], mat, SpreadMethod.PAD);
	s.graphics.drawCircle(0,0,radius);
	s.velX = 0;
	s.velY =  -Math.random()*6 + 3;
	s.posX = s.x = 150+ Math.random() * 100;
	s.posY = s.y = 200;
	s.theta= Math.random() * Math.PI * 2;
	s.inc = Math.random() * 0.4 + 0.01;
	s.rad = Math.random() * 4 + 1;
	s.filters = blur;
	// since the particles aren't on the display list, we need to keep a reference to them
	particles[s] = s;
	s.addEventListener(Event.ENTER_FRAME, onRunParticle);
}
function onRunParticle(evt:Event):void {
	var s:MovieClip = MovieClip(evt.currentTarget);
	s.posX += s.velX;
	s.posY += s.velY;
	s.velX = s.rad * Math.cos(s.theta);
	s.theta += s.inc;
	s.scaleX = s.scaleY -=  .03;
	if (s.scaleX < 0){
		particles[s] = null;
		s.removeEventListener(Event.ENTER_FRAME, onRunParticle);
	}
	s.x = s.posX;
	s.y = s.posY;
	canvas.draw(s, s.transform.matrix, null, BlendMode.ADD);
}

This is a slight variation on a simple particle system that I show in my intermediate class. It is almost the same as this example. But it makes use of filters and BitmapData.

Here is a still generated with this snippet. You’ll notice it vaguely resembles fire.

If you need more particles, there are other approaches that are more appropriate.

Read snippet: BitmapData & Particles