Latest snippets

Testing New Code Highlighter

var sprite:Sprite = new Sprite();
addChild(sprite);

Just testing a new highlighter… the site may have some weird issues for the next few minutes

UPDATE: Seems to be working, please let me know via the comments on this post if you see any code highlighting issues.

The new code highlighter won’t copy and paste line numbers into the timeline. For old posts you’ll still need to click the PLAIN TEXT button above each snippet.

3D Hypocycloid Shape

var matrix:Matrix3D = new Matrix3D();
var verts:Vector.<Number> = new Vector.<Number>();
var pVerts:Vector.<Number> = new Vector.<Number>();
var uvts:Vector.<Number> = new Vector.<Number>();
const TWO_PI:Number=Math.PI * 2;
var step:Number=.05;

var brush:BitmapData = new BitmapData(3, 2, true, 0x41FFFFFF);
var n:Number=8;
var xp:Number=0,yp:Number=0,a:Number=12,t:Number=0;
for (var i:Number = 0; i<TWO_PI; i+=step) {
	for (var j:Number = 0; j<TWO_PI; j+=step) {
        // unoptimized for readability
	var cosi:Number = a/n * ((n - 1) * Math.cos(i) + Math.cos(Math.abs((n - 1) * i)));
	var sini:Number = a/n * ((n - 1) * Math.sin(i) - Math.sin(Math.abs((n - 1) * i)));
	var cosj:Number = a/n * ((n - 1) * Math.cos(j) + Math.cos(Math.abs((n - 1) * j)));
	var sinj:Number = a/n * ((n - 1) * Math.sin(j) - Math.sin(Math.abs((n - 1) * j)));
		verts.push(cosi * cosj);
		verts.push(sini * cosj);
		verts.push(a * sinj);
		pVerts.push(0),pVerts.push(0);
		uvts.push(0),uvts.push(0),uvts.push(0);
	}
}
var canvas:BitmapData=new BitmapData(400,400,false,0x000000);
addChild(new Bitmap(canvas));
var dx:Number=0;
var dy:Number=0;
addEventListener(Event.ENTER_FRAME, onLoop);
function onLoop(evt:Event):void {
	dx += (mouseX - dx)/4;
	dy += (mouseY - dy)/4;
	matrix.identity();
	matrix.appendRotation(dy,Vector3D.X_AXIS);
	matrix.appendRotation(dx,Vector3D.Y_AXIS);
	matrix.appendTranslation(200, 200, 0);
	Utils3D.projectVectors(matrix, verts, pVerts, uvts);
	canvas.lock();
	canvas.fillRect(canvas.rect, 0x000000);
	var p = new Point();
	for (var i:int = 0; i<pVerts.length; i+=2) {
		p.x = pVerts[i];
		p.y = pVerts[i + 1];
		canvas.copyPixels(brush, brush.rect, p, null, null, true);
	}
	canvas.unlock();
}

Taking the Hypocycloid stuff from yesterday into 3D…

Hypocycloid Spiral

x = stage.stageWidth / 2;
y = stage.stageHeight / 2;
// change n to alter number of spikes (cuspes)
var n:Number = 8;
var xp:Number = 0, yp:Number = 0, a:Number = 10, t:Number = 0;

graphics.lineStyle(0, 0x000000);
addEventListener(Event.ENTER_FRAME, onLoop);
function onLoop(evt:Event):void {
	for (var i:int = 0; i<10; i++){
	  // unoptimized for simplicity and similarity to original equations from here:
          // http://mathworld.wolfram.com/Hypocycloid.html
      xp = a/n * ((n - 1) * Math.cos(t) + Math.cos(Math.abs((n - 1) * t)));
	  yp = a/n * ((n - 1) * Math.sin(t) - Math.sin(Math.abs((n - 1) * t)))  ;
	  
	  a *= 1.002;
	   if (t == 0){
		   graphics.moveTo(xp, yp);
	   }else{
		   graphics.lineTo(xp, yp);   
	   }
	   t += .1;
	}
}

This code draws a Hypocycloid with a radius that increments – this results in a spiral formation.

I got the math from mathworld… here.

Pendulums

var mouseSpeedX:Number = 0;
var prevX:Number = 0;

var pends:Array = new Array();
for (var i:int = 0; i<10; i++){
       pends.push(makePendulum(100+ i * 40, 100, 15, 100 + i * 10));
}

addEventListener(Event.ENTER_FRAME, onRun);
function onRun(evt:Event):void {
       // mouseSpeed
       mouseSpeedX = prevX - mouseX;
       prevX = mouseX;

       for (var i:int = 0; i<pends.length; i++) pends[i]();
}

function makePendulum(xp:Number, yp:Number, rad:Number, leng:Number):Function {
       var rot:Number = 0;
       var rotDest :Number = 0;
       var rotVel:Number = 0
       var string:Shape = Shape(addChild(new Shape()));
       var ball:Sprite = Sprite(addChild(new Sprite()));
       ball.buttonMode = true;
       with(ball.graphics) beginFill(0xFF0000), drawCircle(0,leng, rad);
       ball.x = xp;
       ball.y = yp;
       string.x = ball.x;
       string.y = ball.y;
       ball.addEventListener(MouseEvent.ROLL_OVER,function(){
             rotDest = mouseSpeedX;
       });
       return function(){
           // force rotDest back to 0
           rotDest *= .8;
               // elasticity (hooke's)
               rotVel += (-1.9 * (rot - rotDest) - rotVel) / 4;
               rot += rotVel
               ball.rotation = rot;
               // draw string:
               string.graphics.clear();
               string.graphics.lineStyle(0,0);
               var pnt:Point = ball.localToGlobal(new Point(0, leng))
               string.graphics.curveTo(0, leng / 2, pnt.x - ball.x, pnt.y-ball.y);
  }
}

This is a variation on something I wrote in response to a student question. It creates a few pendulums that can be pushed with the mouse.

Dynamic Shapes

// build some functions:
var redGradient:Function = sl(16, add(100, mult(5)));

// grid positioning
var xPos:Function = add(50, mult(30, cInt(div(4))));
var yPos:Function = add(50, mult(30, mod(4)));

// create some shapes:
var shapes:Array = createShapes(this, 22, [["beginFill", 0xCCCCCC], ["drawCircle", 0, 0, 10], ["endFill"], ["lineStyle",1, redGradient], ["drawRect", -5, -5, 10, 10]], {x:yPos, y:xPos, rotation:mult(10)});
																 
function createShapes(par:DisplayObjectContainer,  num:Number, 
					                     funcs:Array, props:Object):Array {
	var shapes:Array = [];
	for (var i:int = 0; i<num; i++){
		shapes[i] = par.addChild(new Shape());
		for (var j:int = 0; j<funcs.length; j++) {
			var a:Array = funcs[j].concat();
			for (var k:int = 0; k<a.length; k++){
				if (a[k] is Function){
					a[k] = a[k](i);
				}
			}
			for (var key:String in props){
				var v:* = props[key];
				if (v is Function){
				  shapes[i][key] = v(i);
				}else{
				  shapes[i][key] = v;
				}
			}
			shapes[i].graphics[a[0]].apply(shapes[i].graphics, a.slice(1));
		}
	}
	return shapes;
}

// function building blocks
const F:Function = function(a:*):*{return a};

function cInt(f:Function=null):Function{
	if (f == null) f = F;
	return function(n:Number):Number {
		return int(f(n));
	}
}

function mod(m:Number, f:Function=null):Function{
	if (f == null) f = F;
	return function(n:Number):Number {
		return f(n) % m;
	}
}

function div(d:Number, f:Function=null):Function{
	if (f == null) f = F;
	return function(n:Number):Number {
		return f(n) / d;
	}
}

function mult(scalar:Number, f:Function=null):Function{
	if (f == null) f = F;
	 return function(n:Number):Number {
		return f(n) * scalar;
	}
}

function add(off:Number, f:Function=null):Function{
	 if (f == null) f = F;
	 return function(n:Number):Number {
		return f(n) + off;
	}
}
// shift left
function sl(amount:int, f:Function=null):Function{
	 if (f == null) f = F;
	 return function(n:Number):Number {
		return f(n) << amount
	}
}

This is an unusual snippet I wrote a couple weeks back…. not sure where I was going with this really… has some interesting ideas in it.

Object Argument w/ Defaults

var boxDefaults:Object = {x:10, y:10, width:100, height:100, lineThickness:0, lineColor:0x000000, lineAlpha:1, fillColor:0xCCCCCC, fillAlpha:1}
function drawBox(params:Object=null):void {
	var p:Object=setDefaults(boxDefaults, params);
	graphics.lineStyle(p.lineThickness, p.lineColor, p.lineAlpha);
	graphics.beginFill(p.fillColor, p.fillAlpha);
	graphics.drawRect(p.x, p.y, p.width, p.height);
}

function setDefaults(defaults:Object, params:Object=null):Object {
	if (params==null) {
		params = new Object();
	}
	for (var key:String in defaults) {
		if (params[key]==null) {
			params[key]=defaults[key];
		}
	}
	return params;
}

// test it out... notice that all object properties are optional and have default values										  
drawBox();

drawBox({x:200, y:200, lineColor:0xFF0000, lineThickness:2, fillColor:0xCCCC00});

drawBox({x:200, y:320, width:50, height:150, lineAlpha:0, fillColor:0x416CCF});

This is a technique I’ve been using recently… inspired by tweening engines. I find this to be suitable when a function or object constructor has lots and lots of arguments (80% of the time if I have a function or object constructor with too many arguments I re-think my design, but every now and then I use this technique).

Basically, the function or object constructor takes one argument of type Object – once passed into the function this Object argument is passed to the setDefault() function which populates it with any properties that it’s missing – each property has a default value. As a result you end up with an easy to read function call with a variable number of arguments and well thought out default values.

This snippet just draws a box (not very interesting or usefull) – I wanted to showcase the technique using a simple function. As a real world example… I recently used this technique in a mini-library I created for use with Box2D… will be posting the library in the next few days…. it’s a library for fast prototyping and custom rendering.

Drawing Gears

const TWO_PI:Number=Math.PI*2;
// x, y, max radius, notch number
drawVerts(calcGear(200, 200, 50, 9));

drawVerts(calcGear(400, 200, 30, 3));

drawVerts(calcGear(300, 350, 30, 5));

drawVerts(calcGear(400,400, 30, 2));

function calcGear(x:Number, y:Number, maxRad:Number, s:int):Array {
	var verts:Array = new Array();
	var step:Number=TWO_PI / (s * 4);
	var mod:Number=0;
	for (var i:Number = 0; i<=TWO_PI; i+=step) {
		var r:Number = (int(mod)%2+1) * maxRad;
		mod+=.5;
		verts.push(x + r * Math.cos(i));
		verts.push(y + r * Math.sin(i));
	}
	return verts;
}

// could  use draw path here instead;
function drawVerts(verts:Array):void{
	graphics.lineStyle(0,0x000000);
	graphics.moveTo(verts[0], verts[1]);
	for (var i:int = 2; i<verts.length; i+=2) {
		graphics.lineTo(verts[i], verts[i + 1]);
	}
	graphics.lineTo(verts[0], verts[1]);
}

Needed to draw some gear shapes today…

Plot of sinh()

x = stage.stageWidth / 2;
y = stage.stageHeight / 2;
scaleX = scaleY = 3;

var t:Number =-5;
var xp:Number = 0;
var yp:Number = 0;
  
addEventListener(Event.ENTER_FRAME, onLoop);
function onLoop(evt:Event):void {
	 xp = t;
	 yp = sinh(t);
	 
	graphics.lineStyle(0,0);
	if (t == -5){
		graphics.moveTo(xp, yp);
	}else{
	    graphics.lineTo(xp, yp);
	}
	 if (t > 5){
		 removeEventListener(Event.ENTER_FRAME, onLoop);
	 }
	 t+=.2;
}
function sinh(x:Number):Number{
	return (Math.pow(Math.E, x) - Math.pow(Math.E, -x)) * 0.5;
}

Just a quick plot to test the sinh() function from a few days ago…

Relative Positioning

var size:Array = [1, 1.5, .5, 1, .4, 1, 1, 1, .2, 1.1]
var boxes:Array = new Array();
var spacing:Number = 4;
var container:Sprite = Sprite(addChild(new Sprite()));
container.x = container.y = 100;

for (var i:int = 0; i<size.length; i++){
	var box:Sprite = makeBox();
	var prev:int = i - 1;
	box.scaleX= box.scaleY = size[i];
	if (i == 0){
		box.y = 10;
	}else{
		// here's the trick
		// if you animate the height property you need to do this again and again:
		box.y = boxes[prev].y + boxes[prev].height/2+ box.height/2 + spacing
	}
	boxes.push(box);
}

function makeBox():Sprite{
	var box:Sprite = Sprite(container.addChild(new Sprite()));
	with (box.graphics) beginFill(0xFF0000), drawRect(-50,-10, 100, 20);
	return box;
}

Sometimes you need to position a bunch of Sprites or MovieClips that are different sizes – and you want to keep the spacing between them the same. This snippet shows a simple example of this.

For more info you could also do this tutorial that I wrote on learningactionscript3.com

Shapevent Log (not a snippet)

Recently launched a new project over at shapevent.com. If your enjoy sketchbooks, drawings and interactive ugliness… have a look here:

Shapevent Log

Many of the techniques covered in the code on this website will be used in shapevent log entries… no rss feed yet, but I’ll have one up for it in the next few days….