package { import flash.display.Sprite; import flash.display.Shape; import flash.events.Event; import flash.geom.*; import flash.display.StageAlign; import flash.display.StageScaleMode; import com.bit101.components.*; [SWF(backgroundColor="0xffffff", width="750", height="650", frameRate="30")] public class TriangleFish extends Sprite { private var pathData:Vector. = new Vector.(); private var commands:Vector. = new Vector.(); private var pts:Vector. = new Vector.(5); private var dummy:Shape; private var len:Number = 300; private var iter:int = 7; private var rules:Array; private var canvas:Shape; public function TriangleFish() { stage.align = StageAlign.TOP_LEFT; stage.scaleMode = StageScaleMode.NO_SCALE; init(); } private function init():void { canvas = new Shape(); canvas.x = stage.stageWidth/2; canvas.y = stage.stageHeight/2; canvas.rotation = -90; addChild(canvas); //create new displayObject to use localToGlobal() (must be in displayList for this to work) dummy = new Shape(); addChild(dummy); defineRules(); drawFractal(); addControls(); } private function defineRules():void { //these rules are applied alternately to each side of the original rhombus, and in same order thereafter //rule1 ... __/\ rules = [Vector.([ new Point(0,0), new Point(len,0), new Point(len + Math.cos(Math.PI*60/180)*len,-Math.sin(Math.PI*60/180)*len), new Point(2*len,0) ]), //the 2nd time it is reflected about its x and y axis: \/-- imagine horizontal line at top Vector.([ new Point(0,0), new Point(Math.cos(Math.PI*60/180)*len,Math.sin(Math.PI*60/180)*len), new Point(len,0), new Point(2*len,0) ]), //the 3rd time it is reflected about its x axis only: --\/ imagine horizontal line at top Vector.([ new Point(0,0), new Point(len,0), new Point(len + Math.cos(Math.PI*60/180)*len,Math.sin(Math.PI*60/180)*len), new Point(2*len,0) ]), //the 4th time it is reflected about its y axis only: /\__ Vector.([ new Point(0,0), new Point(Math.cos(Math.PI*60/180)*len,-Math.sin(Math.PI*60/180)*len), new Point(len,0), new Point(2*len,0), ])]; } private function addControls():void { //depth control var iter_lbl:Label = new Label(this, 450, 15, "iterations:"); var iter_val:Label = new Label(this, 600, 15, ""+iter); var iter_sldr:HSlider = new HSlider(this, 500, 20, function(e:Event):void { iter = Math.ceil(e.target.value); iter_val.text = ""+iter; }); iter_sldr.setSliderParams(1,9,iter); //add redraw button var btn:PushButton = new PushButton(this, 620, 15, "redraw", function(e:Event):void { drawFractal(); }); } private function drawFractal():void { //clear data and commands pathData = new Vector.(); commands = new Vector.(); commands.push(1); //this one starts off with a diamond shape : /* * /\ * \/ */ pathData.push( 0, Math.sin(Math.PI*60/180)*len, -len/2, 0, 0, -Math.sin(Math.PI*60/180)*len, len/2, 0, 0, Math.sin(Math.PI*60/180)*len ); canvas.graphics.clear(); canvas.graphics.beginFill(0x00000); //we must interpolate new points between each two points in pathData //work on a copy of the pathData - keeps same length //whereas interpolated points are injected into pathData for(var i:int = 1; i <= iter; i++){ var pathDataCopy:Vector. = pathData.slice(0); var nmpts:Number = pathDataCopy.length; var ptIndex:Number = 0;//used to keep track of next point to interpolate //start on first point, take every pair after that for(var p:int = 0; p < nmpts-2; p+=2){ //ingterpolate between these two points var absVts:Vector. = getInterpolatedPoints( pathDataCopy[p],pathDataCopy[p+1],//first point pathDataCopy[p+2],pathDataCopy[p+3],//second point rules[(p/2)%4],//rule 0 >> 3 use modulus to loop i); //splice into pathData pathData.splice(ptIndex,4,absVts[0],absVts[1],absVts[2],absVts[3],absVts[4],absVts[5],absVts[6],absVts[7]); ptIndex += 6; } } //populate the commands vector with lineTo's for(var c:int = 1; c < pathData.length/2; c++){ commands.push(2); } //draw! canvas.graphics.drawPath(commands,pathData); } private function getInterpolatedPoints(x1:Number,y1:Number,x2:Number,y2:Number,rule:Vector., it:int/*iteration*/):Vector. { //reset the dummy's transforms dummy.transform.matrix = new Matrix(); //position dummy at first point dummy.x = x1; dummy.y = y1; dummy.scaleX = dummy.scaleY = 1/Math.pow(2,it); dummy.rotation = 180*Math.atan2(y2-y1, x2-x1)/Math.PI; //for each point in the rule... //calculate absolute points var absVts:Vector. = new Vector.(8); //translate points from dummy shape to stage for(var pt:int = 0; pt < rule.length; pt++){ var apt:Point = dummy.localToGlobal(rule[pt]); absVts[pt*2] = apt.x; absVts[pt*2+1] = apt.y; } return absVts; } } }