Skip to content

Ecce Signum

Immanentize the Empathy

  • Home
  • About Me
  • Published Works and Literary Matters
  • Indexes
  • Laboratory
  • Notebooks
  • RSS Feed

3D Langton’s Ant, in Actionscript 3 Using Away3d

2012-04-12 John Winkelman

Fast on the heels of the 3D Langton’s Ants in Javascript  using Three.js, here is a version done in Actionscript 3 using Away3d. This will look better on faster computers. Click the image to launch the experiment.

Other than some additional rotation around the main axis, it is identical to the Javascript version, including a glitch that kicks in somewhere around 1200 cubes. In the Javascript version, Chrome would crash at around 700 cubes. In this version, it starts to get a little glitchy at 1600, then progressively more glitchy until it eventually stops updating the screen completely. Oddly, the script continues to run; you will be able to see the number of cubes increment in the upper left corner. I am not sure if this is a hard limit built into Away3d, or the Flash 3D API, or if there is a memory limit of some kind being reached. I suspect – based on the occasional warnings which popped up during development – that it is a hard-coded polygon limit within Away3d. There is probably some way around it, but I don’t (yet) have the know-how to go in and fix it.

Anyway, here is the code for the experiment. Comment out any lines which use the “org.eccesignum.*” files; they assume you have the code for my custom InfoPanel in your library path.

package {
	import away3d.cameras.Camera3D;
	import away3d.containers.ObjectContainer3D;
	import away3d.containers.Scene3D;
	import away3d.containers.View3D;
	import away3d.entities.Mesh;
	import away3d.lights.DirectionalLight;
	import away3d.lights.PointLight;
	import away3d.materials.ColorMaterial;
	import away3d.materials.lightpickers.*;
	import away3d.primitives.SphereGeometry;
	import away3d.primitives.CubeGeometry;
	
	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.geom.Vector3D;
	import flash.utils.Timer;
	
	import org.eccesignum.utilities.InfoPanel;
	
	[SWF(width=640,height=480,frameRate=32,backgroundColor=0x000000)]
	
	public class Main extends Sprite {
		internal var _info:InfoPanel;
		private var view:View3D;
		private var cubeContainer:ObjectContainer3D;
		private var scene:Scene3D;
		private var camera:Camera3D;
		private var directionalLight:DirectionalLight;
		private var lightPicker:StaticLightPicker
		private var cMaterial:ColorMaterial;
		private var antX:Number = 32,
			antY:Number = 32,
			antZ:Number = 32,
			nextX:Number,
			nextY:Number,
			nextZ:Number,
			cellsX:int = 64,
			cellsY:int = 64,
			cellsZ:int = 64,
			cellWidth:int = 8,
			cellHeight:int = 8,
			cellDepth:int = 8,
			antSize:int = 7,
			maxDirections:Number = 8,
			colorMultiplier:Number = Math.round(256/cellsX),
			xOff:Number = cellsX/2*cellWidth,
			yOff:Number = cellsY/2*cellHeight,
			zOff:Number = cellsZ/2*cellDepth,
			objects:Array,
			antDirection:Number = 1,
			filledCells:int = 0;
		
		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 {
			_info = new InfoPanel(this,100,50);
			scene = new Scene3D();
			camera = new Camera3D();
			view = new View3D(null,camera);
			view.antiAlias = 2;
			camera.x = 0;
			camera.z = 150;
			camera.y = -300;
			cubeContainer = new ObjectContainer3D();
			cubeContainer.rotationY=0;
			cubeContainer.rotationZ=45;
			
			directionalLight = new DirectionalLight(0,150,-300);
			directionalLight.diffuse = 1;
			directionalLight.specular = 0.3;
			directionalLight.color=0xffffff;
			scene.addChild(directionalLight);
			lightPicker = new StaticLightPicker([directionalLight]);
			
			cMaterial = new ColorMaterial(0x999999);
			cMaterial.lightPicker  = lightPicker;
			
			scene.addChild(cubeContainer);
			camera.lookAt(new Vector3D(0,0,0));
			view.scene = scene;
			addChild(view);

			objects = new Array(cellsX);
			for(var i:int=0;i<objects.length;i++) {
				objects[i] = new Array(cellsY);
				for(var j:int=0;j<objects[i].length;j++) {
					objects[i][j] = new Array(cellsZ);
				}
			}
			view.render();
			addEventListener(Event.ENTER_FRAME,onEnterFrame);
		}
		private function onEnterFrame(e:Event):void {
			if(!objects[antX][antY][antZ]) {
				antDirection++;
				if(antDirection == maxDirections) antDirection = 0;
				addObject(antX,antY,antZ);
			} else {
				removeObject(antX,antY,antZ);
				antDirection--;
				if(antDirection == -1) antDirection = maxDirections-1;
			}
			switch(antDirection) {
				case 0:
					antZ--;
					break;
				case 1:
					antX++;
					break;
				case 2:
					antY++;
					break;
				case 3:
					antX--;
					break;
				case 4:
					antZ++;
					break;
				case 5:
					antX++;
					break;
				case 6:
					antY--;
					break;
				case 7:
					antX--;
					break;
				default:
					break;
			}
			if(antY < 0) antY += cellsY;
			if(antY >= cellsY) antY -= cellsY;
			if(antX < 0) antX += cellsX;
			if(antX >= cellsX) antX -= cellsX;
			if(antZ < 0) antZ += cellsZ;
			if(antZ >= cellsZ) antZ -= cellsZ;

			cubeContainer.rotationZ+=.5;
			cubeContainer.rotationY+=.5;
			cubeContainer.rotationX+=.5;
			_info.update(filledCells.toString(),true);
			view.render();
		}
		private function addObject(x:int,y:int,z:int):void {
			var cGeometry:CubeGeometry = new CubeGeometry();
				cGeometry.width = antSize;
				cGeometry.height = antSize;
				cGeometry.depth = antSize;
			var cMesh:Mesh = new Mesh(cGeometry,cMaterial);
				cMesh.x = x*cellWidth-xOff;
				cMesh.y = y*cellHeight-yOff;
				cMesh.z = z*cellDepth-zOff;
			cubeContainer.addChild(cMesh);
			objects[x][y][z] = cMesh;
			filledCells++;
		}
		private function removeObject(x:int,y:int,z:int):void {
			cubeContainer.removeChild(objects[x][y][z]);
			objects[x][y][z].material.dispose();
			objects[x][y][z].dispose();
			objects[x][y][z] = null;
			filledCells--;
		}
		private function getRGB(r:int,g:int,b:int):int {
			var rgb:int = parseInt((r*colorMultiplier).toString(16) + (g*colorMultiplier).toString(16) + (b*colorMultiplier).toString(16),16);
			return rgb;
		}
			
	}
}

Feel free to use and modify the code to your heart’s content. If you come up with anything nifty, post a link to it in the comments.

Posted in ProgrammingTagged Flash, math, procedural art

Post navigation

Making Tinyscrollbar.js Easier to Implement
Styling Raphael.js Elements With CSS

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Personal website of
John Winkelman

John Winkelman in closeup

Archives

Categories

Posts By Month

April 2012
S M T W T F S
1234567
891011121314
15161718192021
22232425262728
2930  
« Mar   May »

Links of Note

Reading, Writing
Tor.com
Locus Online
The Believer
File 770
IWSG

Watching, Listening
Writing Excuses Podcast
Our Opinions Are Correct
The Naropa Poetics Audio Archive

News, Politics, Economics
Naked Capitalism
Crooked Timber

Meta

  • Log in
  • Entries feed
  • Comments feed
  • WordPress.org

© 2025 Ecce Signum

Proudly powered by WordPress | Theme: x-blog by wpthemespace.com