Mike T. Henderson

I make interactive things.

Categories:


More Mike:

Perlin Particles within a Circular Boundary in AS3

March 6, 2009

Perlin Noise Particles within a Circular Bounds

The above example is another take on moving particles with perlinNoise. This time I'm containing the particles within a circular boundary by using the logic from my previous post, Mirrored Circular Motion in AS3, to regenerate particles on the opposite side of the circle. This experiment, as well as the rest of my perlin particle experiments, inspired by the magic Robert Hodgin created with his Weird Fishes video.

Note: The CPU performance improves a great deal when the PerlinNoise object is NOT added to the display list. For the sake of this example, I added to the display list to show how it effects the movement.

The class looks like:

 
package com {
 
        /**
        * Perlin Particles Within a Circular Boundary by Mike T. Henderson,
        * is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.
        * Based on a work at mikethenderson.com.
        */
 
	import flash.display.Sprite;
	import flash.display.MovieClip;
	import flash.display.Bitmap;
	import flash.display.Stage;
	import flash.display.GradientType;
	import flash.geom.Point;
	import flash.display.BitmapData;
	import flash.events.Event;
 
	public class perlinParticleSphere extends Sprite {
 
		// stage and boundaries properties
		private var _centerX:int = 0;
		private var _centerY:int = 0;
		private var _radius:int = 100;
 
		// perlinNoise Properties
		private var _perlinW:int = 250;
		private var _perlinH:int = 250;
		private var _baseX:int = 100;
		private var _baseY:int = 100;
		private var _octaves:int = 1;
		private var _stitch:Boolean = true;
		private var _fractal:Boolean = true;
		private var _grayScale:Boolean = true;
		private var _channels:int = 1;
		private var _xWind:int = 1;
		private var _yWind:int = 1;
		private var seed:int = Math.floor( Math.random() * 100 );
 
		// particle properties
		private var _tparticles:int = 500;
		private var speed:int = 2;
		private var dx:Number;
		private var dy:Number;
		private var dist:Number;
		private var oppRadians:Number;
		private var radians:Number;
		private var randomX:Number;
		private var randomY:Number;
 
		// private display objects
		private var Flock:MovieClip;
		private var myBD:BitmapData = new BitmapData( _perlinW, _perlinH );
 
		// public display objects
		public var myPerlin:Bitmap = new Bitmap( myBD );
		public var Bounds:Sprite;
 
		// pixel properties
		private var minColor:int = int( Number.MAX_VALUE );
		private var maxColor:int = 0;
		private var pixelData:uint;
 
		// offset properties
		private var point1:Point = new Point( 0, 0 );
		private var point2:Point = new Point( 0, 0 );
		private var offset:Array = [ point1, point2 ];
 
		/* ------------------------------------------------------------------------------------------------
		   // constructor
		   --------------------------------------------------------------------------------------------- */
 
		public function perlinParticleSphere() {}
 
		/* ------------------------------------------------------------------------------------------------
		   // setters
		   --------------------------------------------------------------------------------------------- */
 
		// stage and boundaries setters
		public function set centerX( v:int ):void { _centerX = v; }
		public function set centerY( v:int ):void { _centerY = v; }
		public function set radius( v:int ):void { _radius = v; }
 
		// perlinNoise setters
		public function set perlinW( v:int ):void { _perlinW = v; }
		public function set perlinH( v:int ):void { _perlinH = v; }
		public function set baseX( v:int ):void { _baseX = v; }
		public function set baseY( v:int ):void { _baseY = v; }
		public function set octaves( v:int ):void { _octaves = v; }
		public function set stitch( v:Boolean ):void { _stitch = v; }
		public function set fractal( v:Boolean ):void { _fractal = v; }
		public function set grayScale( v:Boolean ):void { _grayScale = v; }
		public function set channels( v:int ):void { _channels = v; }
		public function set xWind( v:int ):void { _xWind = v; }
		public function set yWind( v:int ):void { _yWind = v; }
 
		// particle setters
		public function set tparticles( v:int ):void { _tparticles = v; }
 
		/* ------------------------------------------------------------------------------------------------
		   // getters
		   --------------------------------------------------------------------------------------------- */
 
		// stage and boundaries getters
		public function get CenterX():int { return _centerX; }
		public function get CenterY():int { return _centerY; }
		public function get Radius():int { return _radius; }
 
		// perlinNoise getters
		public function get PerlinW():int { return _perlinW; }
		public function get PerlinH():int { return _perlinH; }
		public function get BaseX():int { return _baseX; }
		public function get BaseY():int { return _baseY; }
		public function get Octaves():int { return _octaves; }
		public function get Channels():int { return _channels; }
		public function get XWind():int { return _xWind; }
		public function get YWind():int { return _yWind; }
 
		// particle getters
		public function get TParticles():int { return _tparticles; }
 
		/* ------------------------------------------------------------------------------------------------
		   // init
		   --------------------------------------------------------------------------------------------- */
 
		public function init():void {
 
			// build perlin noise
			myPerlin.x = _centerX - ( myPerlin.width/2 );
			myPerlin.y = _centerY - ( myPerlin.height/2 );
			myPerlin.visible = false;
			addChild( myPerlin );
 
			// add circle boundaries
			Bounds = new Sprite();
			updateBounds(_radius);
			Bounds.x = _centerX;
			Bounds.y = _centerY;
			addChild( Bounds );
 
			// add Flock container
			Flock = new MovieClip();
			addChild( Flock );
 
			// add the particles
			updateFlock();
 
			// add enterFrame
			Flock.addEventListener( Event.ENTER_FRAME, updateParticle );
 
		}
 
		/* ------------------------------------------------------------------------------------------------
		   // update Flock
		   --------------------------------------------------------------------------------------------- */
 
		public function updateFlock():void {
 
			if ( _tparticles > Flock.numChildren ) {
 
				while ( Flock.numChildren < _tparticles ) {
 
					// build particle
					var myParticle:MovieClip = new MovieClip();
					myParticle.graphics.lineStyle( 3, 0, 1);
					myParticle.graphics.moveTo(0, 0);
					myParticle.graphics.lineTo(1, 1);
 
					dx = Math.random() * _radius;
					dy = Math.random() * _radius;
					radians = Math.atan2( dy, dx );
 
					randomX = Math.cos( radians ) * dx;
					randomY = Math.sin( radians ) * dy;
 
					myParticle.x = _centerX + ( Math.sin( Math.random() * randomX ) * randomX );
					myParticle.y = _centerY + ( Math.sin( Math.random() * randomY ) * randomY );
 
					// add random speeds
					myParticle.xspeed = Math.sin( Math.random() * speed ) * speed;
					myParticle.yspeed = Math.sin( Math.random() * speed ) * speed;
 
					Flock.addChild( myParticle );
				}
			} else {
 
				while ( Flock.numChildren > _tparticles ) {
 
					Flock.removeChildAt( _tparticles );
 
				}
 
			}
		}
 
		/* ------------------------------------------------------------------------------------------------
		   // update Particle
		   --------------------------------------------------------------------------------------------- */
 
		private function updateParticle( e:Event ):void {
 
			var i:int = 0;
			var myParticle:MovieClip;
 
			updateNoise();
 
			while ( i < Flock.numChildren) {
 
				myParticle = Flock.getChildAt( i ) as MovieClip;
 
				// find pixel color under Particle
				var curColor:uint = myBD.getPixel( int(myParticle.x - (_centerX - _radius)), int(myParticle.y - (_centerY - _radius)) );
 
				// find rotation by taking the difference of current color - minColor and the maxColor
				var pixPerc:Number = Number((curColor - minColor) / maxColor);
 
				// update properties
				myParticle.x += ( myParticle.xspeed * Math.cos(pixPerc * (Math.PI * 2)));
				myParticle.y += ( myParticle.yspeed * Math.sin(pixPerc * (Math.PI * 2)));
 
				myParticle.scaleX = pixPerc;
				myParticle.scaleY = pixPerc;
 
				// find the distance
				dx = myParticle.x - _centerX;
				dy = myParticle.y - _centerY;
				dist = Math.sqrt( dx * dx + dy * dy );
 
				if ( dist >= _radius + 2 ) {
 
					// find opposite location
					oppRadians = Math.atan2( -dy, -dx );
 
					// send it to the other side
					myParticle.x = _centerX + Math.cos( oppRadians ) * _radius;
					myParticle.y = _centerY + Math.sin( oppRadians ) * _radius;
 
					// create new random speed using sine wave
					myParticle.xspeed = Math.sin( Math.random() * speed ) * speed;
					myParticle.yspeed = Math.sin( Math.random() * speed ) * speed;
 
				}
 
				i++;
			}
		}
 
		/* ------------------------------------------------------------------------------------------------
		   // Perlin methods
		   --------------------------------------------------------------------------------------------- */
 
		private function updateNoise() {
			offset[0].x += int( _xWind );
			offset[0].y += int( _yWind );
			myBD.perlinNoise( _baseX, _baseY, _octaves, seed, _stitch, _fractal, _channels, _grayScale, offset );
			updateMinMaxPixel();
		}
 
		private function updateMinMaxPixel():void {
 
			for (var x:int = 0; x < _perlinW; x++) {
				for (var y:int = 0; y < _perlinH; y++) {
					pixelData = uint( myBD.getPixel(x, y) );
					minColor = int( Math.min(pixelData, minColor) );
					maxColor = int( Math.max(pixelData, maxColor) );
				}
			}
 
		}
 
		/* ------------------------------------------------------------------------------------------------
		   // Update bounds
		   --------------------------------------------------------------------------------------------- */
 
		public function updateBounds( r:int ):void {
			_radius = r;
			Bounds.graphics.clear();
			Bounds.graphics.lineStyle( 1, 0xcccccc, 1 );
			Bounds.graphics.beginGradientFill( GradientType.RADIAL, [0xffffff, 0xcccccc], [.35,.35], [0,255]);
			Bounds.graphics.drawCircle( 0, 0, _radius );
		}
 
	}
 
}
 

Then to call it:

 
import com.perlinParticleSphere;
var mySphere:perlinParticleSphere = new perlinParticleSphere();
mySphere.centerX = 125;
mySphere.centerY = 295;
mySphere.init();
addChild( mySphere );
 

Creative Commons License
Perlin Particles Within a Circular Boundary by Mike T. Henderson
is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.
Based on a work at mikethenderson.com.

0 Comments | Posted under BitmapData Bookmark and Share

No Comments »

No comments yet.

Leave a comment