Mar 25, 2009

Processing "FireCube" example ported to ActionScript 3.0

Processing example FireCube is so interesting to me. I ported it to ActionScript 3.0.

The result is...

Performance improvement

Processing version calculates every pixel color when

  • creating noise,
  • combining values from adjacent pixels and
  • converting color.

So I implement it as follows:

  • creating noise -> BitmapData.noise()
  • combining values from adjacent pixels -> ConvolutionFilter
  • converting color -> BitmapData.paletteMap()

Difficulty

HSV color space is not supported in AS3! I created a function called HSVtoRGB.

Drawing a cube was a pain. So, I changed a cube to circle...

Here is the code: (83 lines)

  1. // Processing FireCube (AS3 version)
  2. package {
  3. import flash.display.*;
  4. import flash.filters.*;
  5. import flash.geom.*;
  6.  
  7. public class FireCube extends Sprite{
  8.     private const WIDTH:int = 200;
  9.     private const HEIGHT:int = 150;
  10.  
  11.     public function FireCube(){
  12.         stage.align = "TL";
  13.         stage.scaleMode = "noScale";
  14.         scaleX = scaleY = 2;
  15.  
  16.         // Create circle
  17.         var circle:Sprite = new Sprite();
  18.         circle.graphics.beginFill(0x808080);
  19.         circle.graphics.drawCircle(0, 0, 10);
  20.         circle.graphics.endFill();
  21.  
  22.         // Create buffered image
  23.         var fire:BitmapData = new BitmapData(WIDTH, HEIGHT, false, 0);
  24.         var pg:BitmapData = fire.clone();
  25.         var noiseBmd:BitmapData = new BitmapData(WIDTH, 1);
  26.  
  27.         var bmp:Bitmap = new Bitmap(pg);
  28.         addChild(bmp);
  29.  
  30.         // Generate the palette
  31.         var r:Array = [], g:Array = [], b:Array = [];
  32.         for(var x:int = 0; x < 256; x++) {
  33.             //Hue goes from 0 to 85: red to yellow
  34.             //Saturation is always the maximum: 255
  35.             //Lightness is 0..255 for x=0..128, and 255 for x=128..255
  36.             HSVtoRGB(x / 3, 1, Math.min(x * 3 / 255.0, 1), r, g, b);
  37.         }
  38.  
  39.         // Use ConvolutionFilter to calculate for every pixel
  40.         var filter:ConvolutionFilter = new ConvolutionFilter(3, 3, [0, 0, 0, 16, 16, 16, 0, 16, 0], 65);
  41.  
  42.         // Prepare points and matrix
  43.         var matrix:Matrix = new Matrix();
  44.         var pt0:Point = new Point(0, HEIGHT - 1);
  45.         var pt1:Point = new Point(0, -1);
  46.         var pt2:Point = new Point(0, 1);
  47.  
  48.         // Do loop
  49.         addEventListener("enterFrame", function(event:*):void{
  50.             // Randomize the bottom row of the fire buffer
  51.             noiseBmd.noise(Math.random() * 0xffffffff, 0, 190, 7, true);
  52.             fire.copyPixels(noiseBmd, noiseBmd.rect, pt0);
  53.  
  54.             // Display circle
  55.             matrix.tx = mouseX;
  56.             matrix.ty = mouseY;
  57.             fire.draw(circle, matrix);
  58.  
  59.             // Add pixel values around current pixel
  60.             fire.applyFilter(fire, fire.rect, pt1, filter);
  61.  
  62.             // Output everything to screen using our palette colors
  63.             pg.paletteMap(fire, fire.rect, pt2, r, g, b);
  64.         });
  65.     }
  66.  
  67.     // AS3 does not natively support HSV...  :-(
  68.     private function HSVtoRGB(h:int, s:Number, v:Number, r:Array, g:Array, b:Array):void {
  69.         if (h < 60) {
  70.             r.push((v * 255) << 16);
  71.             g.push((v * (1 - (1 - h / 60.0) * s) * 255) << 8)
  72.             b.push(v * (1 - s) * 255);
  73.         } else if (h < 120) {
  74.             r.push((v * (1 - (-h / 60.0 - 1) * s) * 255) << 16);
  75.             g.push((v * 255) << 8);
  76.             b.push(v * (1 - s) * 255);
  77.         } else {
  78.             throw Error('not implemented');
  79.         }
  80.     }
  81. }
  82. }