Rotating Particles with PerlinNoise
Continuing on with my previous post on scaling particles with PerlinNoise, I decided to take it a little further by playing with each particle's rotation in relation to the white and dark values in the PerlinNoise. This is another little trick I learned from Robert Hodgin's presentation at FlashBelt on how to simulate realistic motion on how objects reacted to wind.
When wind blows, it doesn't just blow all objects at the same speed, direction, and force, all in a straight line. There are many parameters that can alter the way one object moves from within a large group of objects.
While PerlinNoise does not simulate wind patterns, the sheer randomness it provides allows us the ability to fake an effect such as the way a field of grass blows in the breeze, or the way a group of leaves on a tree blow in the wind.
The below example takes an object from the library, duplicates it a set amount of times, and applies it to the stage at a random position. The basic logic of the rotation is; to scan the entire PerlinNoise bitmap and gather the minimum and maximum color numbers from all the pixels within that bitmap, the take the current pixel color directly beneath the particle and subtract it by the minimum color value. Then take that value divided by the maximum color value, and then multiply that value by 360.
(Note: If the below example is not displaying the particles, it's probably because you are using FireFox 3.0 on a Mac with Add-ons. This is something I just noticed, and seems to be a bug with FF3 being a little more picky in how you call your code. Working on the the fix — when I get some time. Until then, I'm afraid you'll have to disable you add-ons if you want to view).
import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.display.BitmapData; import flash.display.Graphics; import flash.display.Sprite; import flash.events.Event; import flash.geom.Point; /* ------------------------------------------------------------------------------------------------ // set up Perlin Noise Properties --------------------------------------------------------------------------------------------- */ var perlinW:int = stage.stageWidth; var perlinH:int = 250; var baseX:int = 275; var baseY:int = 275; var octaves:int = 1; var seed:int = Math.floor(Math.random()*100); var stitch:Boolean = true; var fractal:Boolean = true; var grayScale:Boolean = true; var channels:int = 1; var xSpeed:int = 15; var ySpeed:int = 15; var tnodes:int = 2500; /* ------------------------------------------------------------------------------------------------ // set up min/max color vars --------------------------------------------------------------------------------------------- */ var minColor:int = Number.MAX_VALUE; var maxColor:int = 0; var pixelData:int; /* ------------------------------------------------------------------------------------------------ // create offset --------------------------------------------------------------------------------------------- */ var point1:Point = new Point(0, 0); var point2:Point = new Point(0, 0); var offset:Array = [point1, point2]; /* ------------------------------------------------------------------------------------------------ // create bitmapData object --------------------------------------------------------------------------------------------- */ var myBD:BitmapData = new BitmapData(perlinW, perlinH); var myImage:Bitmap = new Bitmap(myBD); addChild(myImage); /* ------------------------------------------------------------------------------------------------ // create nodes --------------------------------------------------------------------------------------------- */ for (var i:int = 0; i < tnodes; i++) { var myNode:Ball = new Ball(); addChild(myNode); myNode.x = int(Math.random() * perlinW); myNode.y = int(Math.random() * perlinH); myNode.addEventListener(Event.ENTER_FRAME, scaleNode); } /* ------------------------------------------------------------------------------------------------ // run perlinNoise --------------------------------------------------------------------------------------------- */ addEventListener(Event.ENTER_FRAME, onEnterFrame); function onEnterFrame(e:Event) { offset[0].x += xSpeed; offset[0].y += ySpeed; myBD.perlinNoise(baseX, baseY, octaves, seed, stitch, fractal, channels, grayScale, offset); } /* ------------------------------------------------------------------------------------------------ // get min/max pixel info --------------------------------------------------------------------------------------------- */ function updateMinMaxPixel():void { for (var i : int = 0;i < perlinW; i++) { for (var j : int = 0;j < perlinH; j++) { pixelData = int(myBD.getPixel(i, j)); minColor = int(Math.min(pixelData, minColor)); maxColor = int(Math.max(pixelData, maxColor)); } } } /* ------------------------------------------------------------------------------------------------ // update Node --------------------------------------------------------------------------------------------- */ function scaleNode(e:Event):void { // find pixel color under node var curColor:int = myBD.getPixel(e.currentTarget.x, e.currentTarget.y); // find rotation by taking the difference of current color - minColor and the maxColor var Rotation:Number = (curColor - minColor) / maxColor; // update properties e.currentTarget.rotation = Rotation * 360; e.currentTarget.scaleX = curColor * .0000001; e.currentTarget.scaleY = curColor * .0000001; } // hide noise and get min/max color values myImage.visible = false; updateMinMaxPixel();
