Browse the notebook

Author: Zevan

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.

Read snippet: Testing New Code Highlighter

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…

Read snippet: 3D Hypocycloid Shape

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.

Read snippet: Hypocycloid Spiral

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.

Read snippet: Pendulums

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.

Read snippet: Dynamic Shapes

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.

Read snippet: Object Argument w/ Defaults

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…

Read snippet: Drawing Gears

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…

Read snippet: Plot of sinh()

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

Read snippet: Relative Positioning