Skip directly to content

Flash

Kohonen Map in Actionscript 3

on Tue, 05/01/2012 - 21:52

Kohonen Map in Actionscript 3

This image is the output of a Kohonen Map, also called a self-organizing map, which is a type of simple neural network, mostly used for sorting and grouping large data sets. I am fairly happy with the results.

This is something I have wanted to do for about four years, starting with a project I worked on back in 2008. Click the image to launch the app. Once launched, click "PLAY/PAUSE" to begin; click "RESET" to restart the sorting process, and change the value in the "grid size" input field to change the dimensions of the color grid. Be careful; anything above 100 (e.g., 10,000 squares) will begin to run slowly.

To create the app, I started with Processing source code I found at jjguy.com. Translating the code to Actionscript 3 took about four hours, and another four to tweak and test and modify to accommodate Flash-specific functionality. All in all, it was surprisingly easy.

The code follows. There are three parts; Main.as, which is contains the UI, global variables, and initialization code; SOM.as, which is the code for the map, and Node.as, which contains the code for the individual blocks of color.

All of the source code, including compiled .swf, can be downloaded here.

 

Main.as

package {
    import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
    import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.events.TimerEvent;
	import flash.text.TextField;
	import flash.text.TextFieldType;
	import flash.text.TextFormat;
	import flash.utils.Timer;
    [SWF(width=720,height=480,frameRate=32,backgroundColor=0x000000)]
    public class Main extends Sprite {
		[Embed(
			source='C:/WINDOWS/Fonts/ARIAL.TTF', 
			fontName='ArialEmbed', 
			unicodeRange='U+0020-U+002F,U+0030-U+0039,U+003A-U+0040,U+0041-U+005A,U+005B-U+0060,U+0061-U+007A,U+007B-U+007E', 
			mimeType="application/x-font-truetype", embedAsCFF="false"
		)]
		private static var _arialEmbed:Class;
		
		internal var _timer:Timer;

		private var isPlaying:Boolean = false;
		
		private var btnPlayPause:Sprite;
		private var btnReset:Sprite;
		private var txtIterations:TextField;
		private var txtGridSize:TextField;
		private var colorTextBG:int = 0xcccccc;
		private var btnTextFormat:TextFormat = new TextFormat("ArialEmbed",12,0x333333,null,null,null,null,null,"center");
		private var labelTextFormat:TextFormat = new TextFormat("ArialEmbed",12,0xededed,null,null,null,null,null,"right");
		private var inputTextFormat:TextFormat = new TextFormat("ArialEmbed",12,0x000000,null,null,null,null,null,"left");

		private var som:SOM;
		private var iter:int;
		private var maxIters:int = 4000;
		public var screenW:int=480;
		public var screenH:int=480;
		private var gridSize:int = 40;

		private var rgb:Array = [
			[1,1,1],
			[0,0,0],
			[1,0,1],
			[1,0,0],
			[0,1,0],
			[0,0,1],
			[1,1,0],
			[0,1,1],
			[1,.4,.4],
			[.25,.25,.25]
		];
		
        public function Main():void {
            addEventListener(Event.ADDED_TO_STAGE,onAddedToStage);
        }
        private function onAddedToStage(e:Event):void {
            removeEventListener(Event.ADDED_TO_STAGE,onAddedToStage);
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.align = StageAlign.TOP_LEFT;
            init();
        }
        private function init():void {
			_timer = new Timer(10);
			_timer.addEventListener(TimerEvent.TIMER,onTimer);
			initInterface();
			initMap();
			_timer.start();
        }
		
		/*	create and populate the UI elements	*/
		private function initInterface():void {
			var txtIterationsLabel:TextField = getTextField("Iterations",10,160,80,20,labelTextFormat);
			addChild(txtIterationsLabel);
			
			txtIterations = getTextField("0",100,160,50,20,labelTextFormat);
			txtIterations.selectable = false;
			addChild(txtIterations);
			
			var txtGridSizeLabel:TextField = getTextField("GRID SIZE",10,200,80,20,labelTextFormat);
			addChild(txtGridSizeLabel);
			
			txtGridSize = getTextField(gridSize.toString(),100,200,80,20,inputTextFormat);
			txtGridSize.background = true;
			txtGridSize.backgroundColor = 0xffffff;
			txtGridSize.border = true;
			txtGridSize.borderColor=0xcccccc;
			txtGridSize.type = TextFieldType.INPUT;
			txtGridSize.restrict = "0-9";
			addChild(txtGridSize);
			
			var playPauseButton:Sprite = getTextButton("PLAY/PAUSE",20,300,80,20);
			playPauseButton.addEventListener(MouseEvent.CLICK,function(e:Event):void {
				isPlaying = !isPlaying;
			});
			addChild(playPauseButton);
			
			var resetButton:Sprite = getTextButton("RESET",120,300,80,20);
			resetButton.addEventListener(MouseEvent.CLICK,function(e:Event):void {
				iter=1;
				gridSize = parseInt(txtGridSize.text);
				maxIters = gridSize*100;
				som.init(maxIters,gridSize,gridSize);
				updateMap();
			});
			addChild(resetButton);
		
		}
		
		/*	create and initialize an instance of the map 	*/
		private function initMap():void {
			som = new SOM(gridSize,gridSize, 3,screenW,screenH);
			som.x = 240;
			som.y = 0;
			addChild(som);
			iter = 1;	
			som.init(maxIters,gridSize,gridSize);
			updateMap();
		}
		
		/*	called on every tick of the timer	*/
		private function onTimer(e:TimerEvent):void {
			if(isPlaying) updateMap();
			e.updateAfterEvent();
		}
		
		/*	tell the map to make another iterations through the data, then render it to the screen	*/
		private function updateMap():void {
			var t:int = Math.floor(Math.random()*rgb.length);
			if (iter < maxIters){
				som.train(iter, rgb[t]);
				som.render();
				txtIterations.text = iter.toString();
				iter++;
			}
		}
		
		/*	functions for building interface elements	*/
		private function getTextField(txt:String,x:int,y:int,w:int,h:int,format:TextFormat):TextField {
			var tf:TextField = new TextField();
			tf.x = x;
			tf.y = y;
			tf.width = w;
			tf.height = h;
			tf.embedFonts = true;
			tf.text = txt;
			tf.setTextFormat(format);
			tf.defaultTextFormat = format;
			return tf;
		}
		private function getTextButton(txt:String,x:int,y:int,w:int,h:int):Sprite {
			var s:Sprite = new Sprite();
			s.x = x;
			s.y = y;
			s.graphics.lineStyle(1,0x808080,1,true);
			s.graphics.beginFill(colorTextBG,1);
			s.graphics.drawRect(0,0,w,h);
			s.graphics.endFill();
			s.buttonMode=true;
			s.mouseChildren = false;
			s.useHandCursor=true;
			var t:TextField = new TextField();
			t.width=w;
			t.height=h;
			t.selectable = false;
			t.embedFonts = true;
			t.text = txt;
			t.setTextFormat(btnTextFormat);
			t.defaultTextFormat = btnTextFormat;
			t.wordWrap = false;
			t.multiline=false;
			s.addChild(t);
			return s;
		}
    }
}

 

SOM.as

package {
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Sprite;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	public class SOM extends Sprite {
		public var mapWidth:int;
		public var mapHeight:int;
		public var nodes:Array;
		public var radius:Number;
		public var timeConstant:Number;
		public var learnRate:Number = 0.05;
		public var inputDimension:int;
		private var pixPerNodeW:Number;
		private var pixPerNodeH:Number;
		
		private var canvasWidth:int;
		private var canvasHeight:int;
		private var canvasData:BitmapData;
		private var canvas:Bitmap;
		
		public var learnDecay:Number;
		public var radiusDecay:Number;
		
		/*	constructor	*/
		public function SOM(w:int,h:int,n:int,mapW:int,mapH:int):void {
			mapWidth = w;
			mapHeight = h;
			radius = (h + w) / 2;
			inputDimension = n;
			canvasWidth = mapW;
			canvasHeight = mapH;
			canvasData = new BitmapData(canvasWidth,canvasHeight,false,0x000000);
			canvas = new Bitmap(canvasData);
			addChild(canvas);
			
		}
		/*	initialize the map	*/
		public function init(iterations:int,w:int,h:int):void {
			mapWidth = w;
			mapHeight = h;
			radius = (h + w) / 2;
			pixPerNodeW = canvasWidth/mapWidth;
			pixPerNodeH = canvasHeight/mapHeight;
			nodes = [];
			for(var i:int = 0; i < mapHeight; i++){
				nodes[i] = [];
				for(var j:int = 0; j < mapWidth; j++) {
					nodes[i][j] = new Node(inputDimension, mapHeight, mapWidth);
					nodes[i][j].x = i;
					nodes[i][j].y = j;
				}//for j
			}//for i
			timeConstant = iterations/Math.log(radius);
			learnDecay = learnRate;
			radiusDecay = (mapWidth + mapHeight) / 2;
		}
		/*	iterate through and update the weights of each node	*/
		public function train(i:int,w:Array):void {  
			radiusDecay = radius*Math.exp(-(i/timeConstant));
			learnDecay = learnRate*Math.exp(-(i/timeConstant));
			//get best matching unit
			var ndxComposite:int = bestMatch(w);
			var x:int = ndxComposite >> 16;
			var y:int = ndxComposite & 0x0000FFFF;
			//scale best match and neighbors...
			for(var a:int = 0; a < mapHeight; a++) {
				for(var b:int = 0; b < mapWidth; b++) {
					var d:Number = dist(nodes[x][y].x, nodes[x][y].y, nodes[a][b].x, nodes[a][b].y);
					var influence:Number = Math.exp((-1 * Math.pow(d,2)) / (2*radiusDecay*i));
					if (d < radiusDecay) {      
						for(var k:int = 0; k < inputDimension; k++) {
							nodes[a][b].w[k] += influence * learnDecay * (w[k] - nodes[a][b].w[k]);
						}//for k
					}	//if d
				} //for j
			} // for i
		} // train()
		
		
		/*	functions used by training method, for calculating node weights and distances	*/
		public function dist(x1:Number,y1:Number,x2:Number,y2:Number):Number {
			return Math.sqrt( Math.pow(x2 - x1,2) + Math.pow(y2 - y1,2) );
		}
		public function distance(node1:Node, node2:Node):Number {
			return Math.sqrt( Math.pow(node2.x - node1.x,2) + Math.pow(node2.y - node1.y,2) );	
		}
		public function bestMatch(w:Array):int {
			var minDist:Number = Math.sqrt(inputDimension);
			var minIndex:int = 0;
			for (var i:int = 0; i < mapHeight; i++) {
				for (var j:int = 0; j < mapWidth; j++) {
				var tmp:Number = weight_distance(nodes[i][j].w, w);
					if (tmp < minDist) {
						minDist = tmp;
						minIndex = (i << 16) + j;
					}  //if
				} //for j
			} //for i
			return minIndex;
		}
		public function weight_distance(x:Array, y:Array):Number {
			if (x.length != y.length) {
				//	trace("Error in SOM::distance(): array lengths don't match");
			}
			var tmp:Number = 0.0;
			for(var i:int = 0; i < x.length; i++) {
				tmp += Math.pow( (x[i] - y[i]),2);
			}
			tmp = Math.sqrt(tmp);
			return tmp;
		}
		
		/*	render node information to the screen	*/
		public function render():void {
			for(var i:int = 0; i < mapWidth; i++) {
				for(var j:int = 0; j < mapHeight; j++) {
					var r:Number = (nodes[i][j].w[0]*255);
					var g:Number = (nodes[i][j].w[1]*255);
					var b:Number = (nodes[i][j].w[2]*255);
					var c:Number = r << 16 ^ g << 8 ^ b;
					canvasData.fillRect(new Rectangle(i*pixPerNodeW, j*pixPerNodeH, pixPerNodeW, pixPerNodeH),c);
				} // for j
			} // for i
		} // render()
	}
}

 

Node.as

package {
	public class Node {
		public var x:int;
		public var y:int;
		public var weightCount:int;
		public var w:Array;
		public function Node(n:int,X:int,Y:int):void {
			x = X;
			y = Y;
			weightCount = n;
			w = [];
			for(var i:int = 0;i<weightCount;i++) {
				w.push(Math.random()*.5+.25);
			}
		}
	}
}

 

Accessing Views From Flash in Drupal Gardens

on Mon, 12/19/2011 - 10:53

Wow, life can be whacky.

So I am pretty sold on Drupal Gardens as a base for most of my future development projects. However, there are still a couple of useful pieces missing, that are out-of-the-box available on a full Drupal install. Specifically, the Services module. Services allow alternate means of accessing data stored in the Drupal database. So if I want to, say, use Drupal as a content management system for a Flash game, I can just pull information in as and when I need it, by referencing a URL and passing the appropriate parameters.

Drupal Gardens doesn't allow that yet. Well, technically they do, but only in very specific ways, none of which are ideal for using Drupal as a straightforward CMS. Having said that, apparently adding this functionality is something they are looking into.

But enough carping! It is possible to use DG as a CMS for a Flash (or AJAX, or Silverlight, etc) application, provided you only need to pull content out of the database, not put it back in. It just takes a bit of a work-around to get everything up and running.

Note: The rest of this post assumes a familiarity with Actionscript, Drupal Views and RSS feeds. If not, take a few minutes to read up on them.

The Views module allows the contents of a view to be published as a Page, as a Block, or as an RSS feed. Say I am making a role-playing game, and I need to populate the world therein with wildlife. To keep things simple, each critter has the following information points:

  • name
  • description
  • terrain (where the creature might be encountered)
  • associated element ( from the classic 5, for combat purposes)

So after entering data for several animals, you would end up with a table which looks something like this:

Name Description Terrain element
squirrel cute, fluffy, voracious forest wood
camel cute, fluffy, spits a lot desert water
walrus truly, nature's most majestic animal plains earth

...at least, that's how it looks in the database. To get it into Flash (in Drupal Gardens, at present) requires a little more work. But not a lot more work.

Pulling the RSS feed of a view is quite simple. Just create a "feed" version of an existing view, and set up a URL path for it, and voila! You have a  feed of the contents of a view.

However, note that the actual contents of a view, when delivered in an RSS feed, are all packed into the <description> tag of the view, and include all of the HTML which would normally be rendered in the page. It looks something like this:

 

http://ecceludum.drupalgardens.com/feeds/monsters/terrain/desert
    enhttp://ecceludum.drupalgardens.com/content/camel
    
&lt;div class=&quot;field field-name-body field-type-text-with-summary field-label-hidden&quot;&gt;
&lt;div class=&quot;field-items&quot;&gt;
&lt;div class=&quot;field-item even&quot; property=&quot;content:encoded&quot;&gt; 
&lt;p&gt;camel text&lt;/p&gt; 
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;field field-name-field-monster-element field-type-taxonomy-term-reference field-label-above&quot;&gt;
&lt;div class=&quot;field-label&quot;&gt;element:&nbsp;
&lt;/div&gt;
&lt;div class=&quot;field-items&quot;&gt;
&lt;div class=&quot;field-item even&quot;&gt;
&lt;a href=&quot;/elements/water&quot; typeof=&quot;skos:Concept&quot; property=&quot;rdfs:label skos:prefLabel&quot;&gt;Water
&lt;/a&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;field field-name-field-monster-terrain field-type-taxonomy-term-reference field-label-above&quot;&gt;
&lt;div class=&quot;field-label&quot;&gt;terrain:&amp;nbsp;
&lt;/div&gt;
&lt;div class=&quot;field-items&quot;&gt;
&lt;div class=&quot;field-item even&quot;&gt;
&lt;a href=&quot;/terrain/desert&quot; typeof=&quot;skos:Concept&quot; property=&quot;rdfs:label skos:prefLabel&quot;&gt;desert
&lt;/a&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
Thu, 15 Dec 2011 18:22:47 +0000John Winkelman71 at http://ecceludum.drupalgardens.comhttp://ecceludum.drupalgardens.com/content/camel#comments

All well and good, except boy, are the contents of the DESCRIPTION tag ugly. That is because the  angle brackets, ampersands, quote marks and non-breaking spaces have  been re-written as plain text character entities. In a sense, they have been rendered as a picture of source code, rather than source code. There is a simple fix for this: When Flash pulls in an RSS feed (or any other XML-based document) it pulls this content in as plain text. It is not structured as XML until after it is loaded into the Flash movie. This means that the string can be parsed internally, and all of the character entities turned into their respective text elements. E.g. each instance of "&gt;" can be replaced with a ">". The simplest way might be the following line of code:

var newString = oldString.split("&lt;").join("<").split("&gt;").join(">").split("&quot;").join("\"").split("&nbsp;").join(" ");
var myXML = new XML(newString);

If that is run on the preceding big ugly bit of RSS feed, then the contents of the DESCRIPTION tags would suddenly look like this:

<div class="field field-name-body field-type-text-with-summary field-label-hidden">
<div class="field-items">
<div class="field-item even" property="content:encoded"> 
<p>camel text</p> 
</div>
</div>
</div>
<div class="field field-name-field-monster-element field-type-taxonomy-term-reference field-label-above">
<div class="field-label">element: 
</div>
<div class="field-items">
<div class="field-item even">
<a href="/elements/water" typeof="skos:Concept" property="rdfs:label skos:prefLabel">Water
</a>
</div>
</div>
</div>
<div class="field field-name-field-monster-terrain field-type-taxonomy-term-reference field-label-above">
<div class="field-label">terrain:&nbsp;
</div>
<div class="field-items">
<div class="field-item even">
<a href="/terrain/desert" typeof="skos:Concept" property="rdfs:label skos:prefLabel">desert
</a>
</div>
</div>
</div>

Now the contents of the DESCRIPTION element of the RSS feed XML are structured and can be parsed using the Flash XML tools. In this instance, you would look for the contents of the elements div.field-name-field-monster-element a, div.field-name-field-monster-terrain a, and div.field-name-body p.

You will note that there are a lot of extra DIV tags, and many of them have huge long class names and/ir IDs. This is not a problem for two reasons: first, the data is well-structured, and second, it is consistent. Unless there are changes to the structure of the View in Drupal Gardens - e.g. changing the "terrain" data field to be called "territory" instead - every time the data is pulled from this RSS feed for this View, it will have exactly the same structure. Having five records or ten thousand won't change things.

So there it is: An overview of how to access Views information from a Flash movie, via RSS, in Drupal Gardens.

Away3d and Flash Player 11 - Source Code

on Sun, 11/06/2011 - 21:48

Here is the source code for the Flash experiment I posted on Thursday.

package
{
	import away3d.containers.ObjectContainer3D;
	import away3d.containers.View3D;
	import away3d.debug.AwayStats;
	import away3d.entities.Sprite3D;
	import away3d.filters.BloomFilter3D;
	import away3d.filters.BlurFilter3D;
	import away3d.filters.DepthOfFieldFilter3D;
	import away3d.lights.DirectionalLight;
	import away3d.lights.LightBase;
	import away3d.lights.PointLight;
	import away3d.materials.BitmapMaterial;
	import away3d.materials.ColorMaterial;
	import away3d.materials.methods.FogMethod;
	import away3d.primitives.Cube;
	import away3d.primitives.Plane;
	import away3d.primitives.Sphere;
	
	import flash.display.BitmapData;
	import flash.display.BlendMode;
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.geom.ColorTransform;
	import flash.geom.Vector3D;
	import flash.utils.getTimer;
	
	/**
	 * Cubes01 Stage3D Demo by Felix Turner - www.airtight.cc
	 * Modified by John Winkelman - www.eccesignum.org
	 */
	
	[SWF( backgroundColor='0xffffff', frameRate='60', width='800', height='600')]
	public class Cubes02 extends Sprite
	{
		private var CUBE_COUNT:int = 500;
		private var MATERIAL_COUNT:int = 30;
		private var CUBE_SIZE:int = 10;
		
		private var view : View3D;
		private var cubes:Array = [];
		private var cubeHolder : ObjectContainer3D;
		
		private var theta:Number = 0;  
		private var radius:Number = 150;
		private var orbitSteps:Number = 1000; // number of steps necessary to complete one orbit
		private var orbitSpeed:Number = Math.PI*2/orbitSteps; //amount by which theta will be incremented at each interval
		private var objectInterval:Number = orbitSteps/CUBE_COUNT;	//	distance between objects on the curve
		private var objectPosition:Number;   
		private var direction:Number = 1;	//	or -1 - controls direction of orbit
		public function Cubes02(){
			super();
			
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.align = StageAlign.TOP_LEFT;
			
			//init 3D world
			view = new View3D();
			view.camera.z = -1000;
			addChild(view);
			//init object to hold cubes and rotate
			cubeHolder = new ObjectContainer3D();
			view.scene.addChild(cubeHolder);
			
			//add lights
			var light:PointLight = new PointLight();
			light.position = new Vector3D(-1000,1000,-1000);
			light.color = 0xffeeaa;
			view.scene.addChild(light);
			
			var light2:PointLight = new PointLight();
			light2.position = new Vector3D(1000,1000,1000);
			light2.color = 0xFFFFFF;
			view.scene.addChild(light2);
			
			//init materials
			var materials:Array = [];
			
			for (var i:int = 0 ; i < MATERIAL_COUNT; i ++ ){
				var material : ColorMaterial = new ColorMaterial(Math.random()*0xFFFFFF,1);
				//material.blendMode = BlendMode.ADD;
				material.lights = [light,light2];
				materials.push(material);
			}
			
			
			for (var j:int = 0 ; j < CUBE_COUNT; j ++ ){
				var s:Number = CUBE_SIZE;
				var cube:Cube = new Cube(materials[j % MATERIAL_COUNT], s,s,s);
				cubeHolder.addChild(cube);
				cube.x = 0;
				cube.y = 0;
				cube.z = 0;
				cubes.push(cube);
				
			}
			
			//add stats
			addChild(new AwayStats(view));
			
			this.addEventListener(Event.ENTER_FRAME, onEnterFrame);
			stage.addEventListener(Event.RESIZE, onStageResize);
			onStageResize(null);
		}
		
		private function onStageResize(event : Event) : void{
			view.width = stage.stageWidth;
			view.height = stage.stageHeight;
		}
		
		
		private function onEnterFrame(ev : Event) : void{
			cubeHolder.rotationX+=.2;
			cubeHolder.rotationY+=.4;
			cubeHolder.rotationZ+=.8;
			for(var i:int=0; i < CUBE_COUNT; i++) {
				objectPosition = orbitSpeed*objectInterval*i;    //    each object is individually updated
				/*	OBJECT MOVEMENT CODE GOES HERE	*/
				
				
				cubes[i].x = radius * (Math.cos(theta + objectPosition) * (Math.pow(5,Math.cos(theta+objectPosition)) - 2 * Math.cos(4 * (theta+objectPosition)) - Math.pow(Math.sin((theta+objectPosition)/12),4)));
				cubes[i].y = radius * (Math.sin(theta + objectPosition) * (Math.pow(5,Math.cos(theta+objectPosition)) - 2 * Math.cos(4 * (theta+objectPosition)) - Math.pow(Math.sin((theta+objectPosition)/12),4)));
				cubes[i].z = radius * Math.sin(theta + objectPosition) - radius*1*(Math.sin((radius/radius + 3) * (theta + objectPosition)));
				
				/*	OBJECT MOVEMENT CODE GOES HERE	*/
			}
			theta += (orbitSpeed*direction);
			view.render();
		}
	}
}

Most everything is a duplication of a block of code I snagged from Airtight Interactive's Stage3D vs. WebGL demo. In addition to finally understanding something of how programming for 3d interfaces works, this gave me an opportunity to dive back into some of the trigonometry experiments I built back in the day.

In the code above, look at the method onEnterFrame. In it, inside the for loop, are three lines which update the x, y, and z coordinates of each block. Those blocks have some scary looking math attached to them; lots of sin and cos and radii, and thetas. I cheated - I actually wrote that code almost three years ago for my Simple Trigonometric Curves Tutorial over on Kongregate. X and Y positions are set using a transcendental Butterfly curve. The Z position is set using a variation on an Epicycloid curve. For fun, grab some of the other curves out of the tutorial, and plug them in in place of the current formulae. If you come up with something interesting, post it.

Away3d and Flash Player 11

on Thu, 11/03/2011 - 22:25

Butterfly Curve animated with Away3d

Click the image above to launch the experiment. Requires Flash Player 11. May beat up on older computers. A lot.

What you see here is a simple example of what the Flash 11 player is capable of. There are 500 cubes, dynamically lit, moving through a "Transcendent Butterfly" curve on the x and y axes, and a variation of an epicycloid in the Z. The whole formation is oscillating through the x, y, and z axes as well. The little box in the upper left corner shows frame rate and the amount of RAM which the animation is using. I have had as many as 1000 cubes running through this animation but the frame rate dropped down below 30 FPS. 500 cubes is plenty for the moment.

This animation was created using the Away3d code library.

Targeting Flash Player 11 in the Flex SDK

on Fri, 10/07/2011 - 17:30

Here are instructions for setting up the Flex SDK to allow Flash development targeting the new Flash 11 player, and how to set up the HTML in which it is embedded to allow for use of hardware acceleration, where appropriate.

  1. Download the Flex 4.5 SDK from here: http://opensource.adobe.com/wiki/display/flexsdk/Download+Flex+4.5
  2. Download the new playerglobal.swc from here: http://www.adobe.com/support/flashplayer/downloads.html
    1. If it is named anything other than "playerglobal.swc" change the file name to "playerglobal.swc"
  3. In the Flex SDK files you just downloaded, create a new folder here: [FLEX SDK]/frameworks/libs/player/11.0
  4. Place the playerglobal.swc you just downloaded into the new 11.0 folder
  5. Target the Flash 11 playerglobal in the flex-config.xml file as follows:
    1. in a text editor, open [FLEX SDK]/frameworks/flex-config.xml
    2. change the <target-player/> value to 11.0
    3. change the <swf-version/> value to 13
  6. Download the standalone Flash 11 projector from here: http://www.adobe.com/support/flashplayer/downloads.html
  7. Update the flash player in your browsers as follows:
    1. Google Chrome - click on the wrench icon in the upper right of the browser, then click on "About Google Chrome". This will force Chrome to automatically update itself, which will include the new version of the Flash plugin
    2. Firefox - visit this url: http://get.adobe.com/flashplayer/ and follow the directions therein
    3. Internet Explorer - visit this url: http://get.adobe.com/flashplayer/ and follow the instructions therein
  8. In order to take advantage of hardware acceleration in your new Flash movies, be sure that in the <object/>and <embed/> tags, you set the wmode attribute to direct. This is the only way that hardware acceleration will work.
  9. If using SWFObject or jQuery or some other JavaScript library to dynamically embed the Flash movie, refer to the appropriate documentation to find out how to change the wmode parameter
  10. Create a new .swf and run it in the new player. See how much faster it runs!

Pages