Furry Blog Logo

Update: I updated the .swf files. So hopefully now those pesky Flash Player errors are gone :( Shame on me for bad coding.

I had a little fun with particles in AS3 the past days. I wrote a small piece based on my Processing port framework that traces the paths of particles. The particles pick up color from a given input texture. As they move, they plot their color onto the canvas. Although i added the condition that the color is only plotted if the target pixel is darker than the particle color. This results in a nice glowing effect around the outline of the image.
I built the swf files against Flash Player 10 this time. There is actually no need at all to do so, but i just couldn’t resist Vector. Arrays are so dirty :(

Here is the larger version with the complete blog logo. ;)

And here’s some source to go with it. Although it’ll hardly compile outside of the Processing Port framework ;D But since i moved servers my SVN repository isn’t publicly available anymore. So if anyone has any interest in the complete source, i’ll wrap it up and put it online ;)

Particle.as

package org.dirty_dirtymotherfucker.tracer {
  import flash.display.BitmapData;
  import flash.geom.Point;
  import flash.geom.Rectangle;
  import org.dirty_dirtymotherfucker.processing.*;

  public class Particle {

    public var position_x:Number;
    public var position_y:Number;
    public var direction_x:Number;
    public var direction_y:Number;
    public var color:uint;
    public var life:Number;
    public var source:BitmapData;
    public var randomSource:BitmapData;

    public function Particle( source:BitmapData, randomSource:BitmapData, sourceOffset:uint ) {
      this.source = source;
      if( Tracer.USE_PERLIN_NOISE ) {
        this.randomSource = new BitmapData( randomSource.width, 1, false );
        this.randomSource.copyPixels( randomSource, new Rectangle( 0, sourceOffset, randomSource.width, 1 ), new Point( 0, 0 ) );
      }
      birth();
    }

    public function birth( ):void {
      life        = Tracer.MAX_PARTICLE_LIFE;
      position_x  = MathHelper.randomRange( -Tracer.CANVAS_SIZE_X, Tracer.CANVAS_SIZE_X );
      position_y  = MathHelper.randomRange( -Tracer.CANVAS_SIZE_Y, Tracer.CANVAS_SIZE_Y );
      direction_x = MathHelper.randomRange( -1.0, 1.0 );
      direction_y = MathHelper.randomRange( -1.0, 1.0 );
      color       = source.getPixel32( position_x, position_y );
    }

    public function travel( source:BitmapData, canvas:BitmapData, blend:Number ):void {
      if( Tracer.USE_PERLIN_NOISE ) {
        direction_x += ( ColorHelper.getR( randomSource.getPixel( life * 10, 0 ) ) - 128 ) / 256;
        direction_y += ( ColorHelper.getG( randomSource.getPixel( life * 10, 0 ) ) - 128 ) / 256;
      } else {
        direction_x += MathHelper.randomRange( -0.1, 0.1 );
        direction_y += MathHelper.randomRange( -0.1, 0.1 );
      }

      // normalize direction
      var len:Number = Math.sqrt( direction_x * direction_x + direction_y * direction_y );
      direction_x /= len;
      direction_y /= len;

      position_x += direction_x;
      position_y += direction_y;
      life -= 0.1;
      //if( 0 == color ) life = 0;

      var sourceColor:uint = source.getPixel32( position_x, position_y );
      var canvasColor:uint = canvas.getPixel32( position_x, position_y );

      if( sourceColor != canvasColor ) {
        color = ColorHelper.blend( color, sourceColor, blend );

        var colorFit:Boolean = ColorHelper.getR( ColorHelper.toGrayScale( color ) ) > ColorHelper.getR( ColorHelper.toGrayScale( canvasColor ) );
        if( 0 != ColorHelper.getA( color ) && colorFit ) {
          canvas.setPixel32( position_x, position_y, ColorHelper.blend( color, canvasColor, 25 ) );
          //canvas.setPixel32( position.x, position.y, color );
        }
      }

      if( 0 > position_x || position_x > Tracer.CANVAS_SIZE_X || 0 > position_y || position_y > Tracer.CANVAS_SIZE_Y || 0 >= life ) birth();
    }

  }

}

Tracer.as

package org.dirty_dirtymotherfucker.tracer {

  import flash.display.*;
  import flash.events.*;
  import flash.geom.Matrix;
  import flash.geom.Point;
  import flash.geom.Rectangle;
  import flash.net.URLLoader;
  import flash.net.URLRequest;

  import org.dirty_dirtymotherfucker.processing.*;

  /**
   * The main implementation of the Tracer project
   */
  public class Tracer extends BlogSprite {

    // Main application settings

    public static const CANVAS_BACKGROUND_COLOR:uint  = 0xFF000000;

    public static const UPDATES_PER_SECOND:uint       = 1200;

    public static const ITERATIONS_PER_UPDATE:uint    = 10;

    public static const CANVAS_SIZE_X:uint            = 466;// 840;
    public static const CANVAS_SIZE_Y:uint            = 220;

    public static const ADDITIVE_BLENDING:Boolean     = true;
    public static const SUBTRACTIVE_BLENDING:Boolean  = false;
    private static const MAX_ITERATIONS:uint          = 0;// 120 * 20;

    public static const MAX_PARTICLE_LIFE:Number      = 100;
    public static const NUM_PARTICLES:int             = 1000;

    public static const USE_PERLIN_NOISE:Boolean      = false;

    private var iterationCount:int = 0;

    private var loader:Loader;
    private var source:BitmapData;
    private var randomSeed:BitmapData;

    private var particles:Vector.<Particle>;

    private var blend:Number;

    public function Tracer():void {
      ColorHelper.loadPalette( );

      super( CANVAS_SIZE_X, CANVAS_SIZE_Y, CANVAS_BACKGROUND_COLOR );

      loader = new Loader();
      //loader.load( new URLRequest( "map.png" ) );
      loader.load( new URLRequest( "http://www.dirty-motherfucker.org/blog/wp-content/uploads/2008/11/motherfucking.png" ) );

      loader.contentLoaderInfo.addEventListener( Event.COMPLETE, drawMap );
    }

    override protected function onDraw( event:TimerEvent ):void {
      canvas.lock();
      for( var iteration:uint = 0; iteration < ITERATIONS_PER_UPDATE; ++iteration ) {
        // draw
        if( null == loader.content ) break;
        for each( var particle:Particle in particles ) {
          particle.travel( source, canvas, blend );
        }

        blend += 0.06;
        if( blend >= 255 ) stopApp();

        // cycle limiter
        if( MAX_ITERATIONS > 0 && ++iterationCount > MAX_ITERATIONS ) {
          restart();
        }
      }
      canvas.unlock();

    }

    override protected function startApp( updatesPerSecond:uint = 0 ):void {
      super.startApp( UPDATES_PER_SECOND );

      particles = new Vector.<Particle>();

      blend = 10;

      iterationCount = 0;

      canvas.fillRect( new Rectangle( 0, 0, CANVAS_SIZE_X, CANVAS_SIZE_Y ), CANVAS_BACKGROUND_COLOR );

      initContent();
    }

    private function drawMap( e:Event ):void {
      loader.removeEventListener( Event.COMPLETE, drawMap );

      initContent();
    }

    private function initContent():void {
      if( null == loader ) return;
      source = new BitmapData( CANVAS_SIZE_X, CANVAS_SIZE_Y, true, 0x00000000 );
      var matrix:Matrix = new Matrix();
      matrix.translate( 0, CANVAS_SIZE_Y / 2 - loader.height / 2 );
      source.draw( loader, matrix );

      if( USE_PERLIN_NOISE ) {
        randomSeed = new BitmapData( MAX_PARTICLE_LIFE * 10, NUM_PARTICLES, false, 0x000000 );
        randomSeed.perlinNoise( MAX_PARTICLE_LIFE * 10, NUM_PARTICLES, 8, Math.random() * uint.MAX_VALUE, true, true, BitmapDataChannel.GREEN | BitmapDataChannel.RED, false );
      }

      for( var i:uint = 0; i < NUM_PARTICLES; ++i ) {
        particles.push( new Particle( source, randomSeed, i ) );
      }

    }

  }

}

2 Responses to “Furry Blog Logo”

  1. arT2 Says:

    i really like the result of this one! would be great for an intro logo or something. why don’t you add it to the header of your blog? :)

  2. gencha Says:

    Nah, it’s too glowy for the main page ;)

Leave a Reply

You must be logged in to post a comment.