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
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 ) );
}
}
}
}

November 14th, 2008 at 20:24
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? :)
November 15th, 2008 at 02:23
Nah, it’s too glowy for the main page ;)