In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

Particles3D by Dan Gries (Black Version)

@author Dan Gries
www.dangries.com
djg@dangries.com
Prepared for www.flashandmath.com.

Last modified July 22, 2008.

Feel free to use and modify the classes in the package 
com.dangries.* to create your own Flash applications under the
following two conditions:

1) If possible, please include a line acknowledging this work and the author
with your application in a way that is visible in your compiled file
or on your html page. 

2) If you choose to modify the classes in the package 
com.dangries.*, put them into a package different from
the original. In this case too, please acknowledge this work
in a way visible to the user.

本来ならいかのパッケージをimportする必要があるがwonderflでは対応して
いないのでこのソースの一番したにパッケージの中身をぶちまけてます。
import com.dangries.bitmapUtilities.*;
import com.dangries.objects.Particle3D;
import com.dangries.geom3D.*;
import com.dangries.display.*;
/**
 * Copyright bradsedito ( http://wonderfl.net/user/bradsedito )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/mus2
 */

/**
 * @author Dan Gries
 * www.dangries.com
 * djg@dangries.com
 * Prepared for www.flashandmath.com.
 * 
 * Last modified July 22, 2008.
 * 
 * Feel free to use and modify the classes in the package 
 * com.dangries.* to create your own Flash applications under the
 * following two conditions:
 * 
 * 1) If possible, please include a line acknowledging this work and the author
 * with your application in a way that is visible in your compiled file
 * or on your html page. 
 * 
 * 2) If you choose to modify the classes in the package 
 * com.dangries.*, put them into a package different from
 * the original. In this case too, please acknowledge this work
 * in a way visible to the user.
 */

package {
    import flash.display.Bitmap;
    import flash.display.Sprite;
    import flash.filters.BlurFilter;
    import flash.geom.ColorTransform;
    import flash.geom.Point;
    import flash.geom.Rectangle;
    import flash.events.Event;
    import flash.events.TimerEvent;
    import flash.events.MouseEvent;
    import flash.utils.Timer;
    
    // 本来ならいかのパッケージをimportする必要があるがwonderflでは対応して
    // いないのでこのソースの一番したにパッケージの中身をぶちまけてます。
    //import com.dangries.bitmapUtilities.*;
    //import com.dangries.objects.Particle3D;
    //import com.dangries.geom3D.*;
    //import com.dangries.display.*;
    
    import net.hires.debug.Stats;
    
    [SWF(width = "500", height = "500", backgroundColor = "0x000000", frameRate = "60")]


    public class ParticlesOrbits extends Sprite 
    {
        private var noiseMaker:NoiseParticleMaker;
        private var particles:Array/*noiseMaker.particleArray*/;         
        private var canvas:RotatingParticleBoard;
        private var canvasRect:Rectangle;
        private var p:Particle3D;
        private var i:int;//for文で使いまわす
        private var colorTransform:ColorTransform = new ColorTransform(.95, .95, .95, 0.8, 1, 1, 1, -2.5);    
        private const NUM_DESTINATIONS:Number = 1;        
        private const NUM_PARTICLES:Number = 3000;
        private const NUM_DISTANCE:Number = 20;//6;
        
        public function ParticlesOrbits() 
        {
            Wonderfl.capture_delay( 30 );
            canvas = new RotatingParticleBoard(stage.stageWidth, stage.stageHeight, false, 0xFFFFFFFF, 100); 
            canvas.setDepthDarken(0, -50, 2);
            canvas.holder.x = stage.stageWidth / 2 - canvas.width / 2;
            canvas.holder.y = stage.stageHeight / 2 - canvas.height / 2;
            canvas.currentQ = new Quaternion(Math.cos(Math.PI / 12), Math.sin(Math.PI / 12) / Math.sqrt(2), 0, -Math.sin(Math.PI / 12) / Math.sqrt(2));
            canvas.autoQuaternion = new Quaternion(1, 0, 0, 0);
            canvas.arcballRad = Math.sqrt(canvas.width * canvas.width + canvas.height * canvas.height) / 2;
            canvas.moveLightWithRecess = true;
            canvas.recess = NUM_DISTANCE;
            
            canvasRect = new Rectangle(0, 0, canvas.width, canvas.height);
            
            lightDistanceUpdate();
        
            noiseMaker = new NoiseParticleMaker(NUM_PARTICLES);
            noiseMaker.addEventListener(NoiseParticleMaker.PARTICLES_CREATED, onCreatedHandler);
            noiseMaker.createParticles();    
            
            
            addChild(new Stats());
        }        
        //========================================================================
        // パーティクルの準備が出来た?後に実行される処理
        //========================================================================
        private function onCreatedHandler(e:Event):void {
            particles = noiseMaker.particleArray;
            
            noiseMaker.buildDestinationArrays(NUM_DESTINATIONS);
            
            // 3パターン適当に
            var flg:Number = Math.random();
            if (flg >= 0 && flg < .3 ){
                setPositionsRandomCube();
            }else if (flg >= .3 && flg < .6) {
                setPositionsRandomSphere();
            }else if (flg >= .6 && flg < 10) {
                setPositionsRandomCylinder();
            };
            
            for (i = 0; i < particles.length; i++) {
                p = particles[i];
                p.x = p.dest[0].x;
                p.y = p.dest[0].y;
                p.z = p.dest[0].z;
                p.setColor(0xFFFFFF/*0xFF7EAAD6*/);
                //p.setColor(0xFFFFFF/*0xFF7EAAD6*/);
                
                var vx:Number = p.y * 0.02;
                var vy:Number = -p.x * 0.02;
                var vz:Number = 0;
                p.vel = new Point3D(vx, vy, vz);
            }
    
            stage.addChild(canvas.holder);
            
            var timer:Timer = new Timer(0);
            timer.addEventListener(TimerEvent.TIMER, loop);
            timer.start();
            
            stage.addEventListener(Event.ENTER_FRAME, onEnterFrameHandler);        
        }        
        //========================================================================
        // 被写界深度によって色を更新してるっぽい
        //========================================================================
        private function lightDistanceUpdate():void {
            var d:Number = Math.pow(0.01 * 33, 0.2);
            var d2:Number = 0.5 * Math.pow(Math.cos(d * Math.PI), 3) + 1;
            canvas.setDepthDarken(10, 64 - d2 * 200, 1);
        }    
        //========================================================================
        // loop
        //========================================================================
        private function loop(e:TimerEvent):void {
            var q:Number = (Math.random() - .5) * 2;
            var w:Number = (Math.random() - .5) * 2;
            var t:Number = (Math.random() - .5) * 2;
            colorTransform = new ColorTransform(q, w, t, 0.7, 0, 0, 0, -2.5);
            e.currentTarget.delay = Math.random() * 2000 + 500;    
        }
        //========================================================================
        // onEnterFrameHandler
        //========================================================================
        private function onEnterFrameHandler(e:Event):void {
            var scaledAccelFactor:Number = (1.33 + 1.33 * 2);            
            
            for (i = 0; i < particles.length; i++) {
                p = particles[i];
                var dSquared:Number = p.x * p.x + p.y * p.y + p.z * p.z;
                
                var ax:Number = -p.x / (0.000001 + dSquared);
                var ay:Number = -p.y / (0.000001 + dSquared);
                var az:Number = -p.z / (0.000001 + dSquared);
                
                p.vel.x = (p.vel.x + scaledAccelFactor * ax);
                p.vel.y = (p.vel.y + scaledAccelFactor * ay);
                p.vel.z = (p.vel.z + scaledAccelFactor * az);
                
                p.x += p.vel.x;
                p.y += p.vel.y;
                p.z += p.vel.z;
            }
            
            canvas.bitmapData.lock();
            canvas.bitmapData.colorTransform(canvasRect, colorTransform);            
            canvas.drawParticles(particles);
            canvas.bitmapData.unlock();
        }    
                
        //========================================================================
        // Cube
        //========================================================================
        private function setPositionsRandomCube():void {
            for (i = 0; i < particles.length; i++) {
                p = particles[i];
                var s1:Number = 1 * Math.floor(Math.random() * 2) - 1;
                var s2:Number = 1 * Math.random() - 1;
                var s3:Number = 1 * Math.random() - 1;
                var sArray:Array/*Number*/ = [s1, s2, s3];
                var shift:int = Math.floor(Math.random() * 3);                
                p.dest[0].x = 75 * sArray[shift];
                p.dest[0].y = 1000 * sArray[(shift + 1) % 3];
                p.dest[0].z = 15 * sArray[(shift + 2) % 3];
            }
        }        
        //========================================================================
        // Sphere
        //========================================================================
        private function setPositionsRandomSphere():void {
            for (i = 0; i < particles.length; i++) {
                p = particles[i];
                var theta:Number = Math.random() * Math.PI * 2;
                var phi:Number = Math.acos(2 * Math.random() - 1);
                p.dest[0].x = 100 * Math.sin(phi) * Math.sin(theta);
                p.dest[0].y = 500 * Math.sin(phi) * Math.sin(theta);
                p.dest[0].z = 70 * Math.sin(phi);
            }
        }        
        //========================================================================
        // Cylinder
        //========================================================================
        private function setPositionsRandomCylinder():void {
            for (i = 0; i < particles.length; i++) {
                p = particles[i];
                var theta:Number = Math.random() * 2 * Math.PI;
                p.dest[0].x = 400 * Math.sin(theta);
                p.dest[0].y = 70 * Math.cos(theta);
                p.dest[0].z = 70 * (2 * Math.random() - 1);
            }
        }
    }
}





//================================================================================
// これより下はライブラリの中身をそのままぶちまけてます
//
// @author Dan Gries
// www.dangries.com
// djg@dangries.com
// Prepared for www.flashandmath.com.
//
//================================================================================
    import flash.display.*;
    import flash.events.*;
    import flash.events.MouseEvent;
    import flash.text.*;
    
    class RotatingParticleBoard extends Bitmap {
        
        public var zBuffer:Array;
        
        public var fLen:Number;
        public var projOriginX:Number;
        public var projOriginY:Number;
        
        public var recess:Number;
        
        //darkening related variables
        private var useDepthDarken:Boolean;
        private var A:Number;
        private var expRate:Number;
        private var maxBrighten:Number;
        
        private var bgColor:Number;
        
        //Vars used in functions
        private var f:Number;
        private var m:Number;
        private var dColor:uint;
        private var intX:int;
        private var intY:int;
        private var p:Particle3D;
        private var X2:Number;
        private var Y2:Number;
        private var Z2:Number;
        private var wX:Number;
        private var wY:Number;
        private var wZ:Number;
        private var xX:Number;
        private var xY:Number;
        private var xZ:Number;
        private var yY:Number;
        private var yZ:Number;
        private var zZ:Number;
        
        public var frame:Shape;
        public var holder:Sprite;
        
        private var v1:Point3D;
        private var v2:Point3D;
        private var lastV:Point3D;
        private var rSquare:Number;
        private var invR:Number;
        
        private var mouseDragging:Boolean;
        private var lastQ:Quaternion;
        private var changeQ:Quaternion;
        private var requestQ:Quaternion;
        private var inside:Boolean;
        private var numRadii:Number;
        
        private var stepChangeQ:Quaternion;
        private var dV:Point3D;
        
        public var averagingSize:int;
        private var averagingArray:Array;
        private var qSum:Quaternion;
        
        public var arcballRad:Number;
        public var currentQ:Quaternion;
        public var autoQuaternion:Quaternion;
        
        public var moveLightWithRecess:Boolean;
        
        public var easingRatio:Number;
        
        private var s:Number;
        
        //Text box can be used to print parameters, etc., for testing purposes.
        //public var txtMonitor:TextField;
        
        public function RotatingParticleBoard(w:Number, h:Number, transp:Boolean = true, fillColor:Number = 0x00000000, _fLen:Number = 200) {            
            var thisData:BitmapData = new BitmapData(w, h, transp, fillColor);
            this.bitmapData = thisData;
            this.zBuffer = [];
            this.useDepthDarken = false; //changed to true in setDepthDarken
            this.projOriginX = w/2;
            this.projOriginY = h/2;
            this.fLen = _fLen;
            this.bgColor = fillColor;
            this.recess = 0;
            
            this.holder = new Sprite();
            this.holder.addChild(this);
            this.x = 0;
            this.y = 0;
            
            this.arcballRad = 0.5*Math.sqrt(Math.pow(w,2)+Math.pow(h,2));
            this.lastQ = new Quaternion();
            this.currentQ = new Quaternion(1,0,0,0);
            this.autoQuaternion = new Quaternion(1,0,0,0);
            this.requestQ = new Quaternion();
            this.changeQ = new Quaternion(1,0,0,0);
            
            this.stepChangeQ = new Quaternion();
            
            this.v1 = new Point3D();
            this.v2 = new Point3D();
            this.dV = new Point3D();
                
            this.mouseDragging = false;
            
            this.easingRatio = 0.4;
            
            this.averagingSize = 8;
            this.averagingArray = [];
            
            this.holder.addEventListener(Event.ADDED_TO_STAGE, addListeners);
            
            this.moveLightWithRecess = false;
            
            /*
            //FOR TESTING
            txtMonitor = new TextField();
            txtMonitor.autoSize = TextFieldAutoSize.LEFT;
            txtMonitor.x = 0;
            txtMonitor.y = h+25;
            txtMonitor.defaultTextFormat = new TextFormat("Arial",10,0xFFFFFF);
            txtMonitor.text = "testing";
            this.holder.addChild(txtMonitor);
            */
            
        }
        
        private function addListeners(evt:Event):void {
            this.holder.removeEventListener(Event.ADDED_TO_STAGE, addListeners);
            this.holder.addEventListener(MouseEvent.MOUSE_DOWN, onDown);
        }
        
        public function makeFrame(thick:Number, c:uint, inputAlpha:Number):void {
            this.frame = new Shape();
            this.frame.graphics.lineStyle(thick, c, inputAlpha);
            this.frame.graphics.drawRect(0,0,this.width,this.height);
            this.frame.x = 0;
            this.frame.y = 0;
            this.holder.addChild(frame);
        }
        
        public function setDepthDarken(unityDepth:Number, halfDepth:Number, _maxBrighten:Number):void {
            this.useDepthDarken = true;
            this.A = Math.pow(2, unityDepth/(halfDepth-unityDepth));
            this.expRate = Math.LN2/(unityDepth-halfDepth);
            this.maxBrighten = _maxBrighten;
        }
        
        public function clearBoard(particles:Array):void {
            for (var i:Number = 0; i<=particles.length-1; i++) {
                p = particles[i];
                if (p.onScreen) {
                    intX = int(p.projX);
                    intY = int(p.projY);
                    zBuffer[intX+this.height*intY] = Number.NEGATIVE_INFINITY;
                    
                    //could also insert the code below to erase pixels,
                    //but my method has been to use a blur filter and
                    //a colorTransform applied to whole bitmap, which
                    //creates "ghost" which fades out after a few frames.
                    
                    //this.bitmapData.setPixel32(intX, intY, bgColor);
                }
            }
        }//end of clearBoard

        public function drawParticles(particles:Array):void {
            
            clearBoard(particles);
            
            ///////////
            //viewpoint change by mouse
            if (mouseDragging) {
                v2.x = (holder.mouseX-projOriginX)/arcballRad;
                v2.y = (holder.mouseY-projOriginY)/arcballRad;
                rSquare = v2.x*v2.x + v2.y*v2.y;
                //for mouse position outside Arcball perimeter:
                if (rSquare > 1) {
                    invR = 1/Math.sqrt(rSquare);
                    v2.x = v2.x*invR;
                    v2.y = v2.y*invR;
                    v2.z = 0;
                }
                //for mouse position inside Arcball perimeter:
                else {
                    v2.z = Math.sqrt(1 - rSquare);
                    inside = true;
                }
        
                //interpret mouse movement as request for rotation change
                requestQ.w = v1.x*v2.x + v1.y*v2.y + v1.z*v2.z;
                requestQ.x = v1.y*v2.z - v2.y*v1.z;
                requestQ.y = -v1.x*v2.z + v2.x*v1.z;
                requestQ.z = v1.x*v2.y - v2.x*v1.y;
                
                //smooth movement is accomplished by rotating only 40% towards the 
                //requested rotation in each frame.  After about ten frames,
                //the rotation will be within 1% of the requested rotation.
                changeQ.w += easingRatio*(requestQ.w - changeQ.w);
                changeQ.x += easingRatio*(requestQ.x - changeQ.x);
                changeQ.y += easingRatio*(requestQ.y - changeQ.y);
                changeQ.z += easingRatio*(requestQ.z - changeQ.z);
                //ensure that the change quaternion is a unit quaternion:
                s = 1/Math.sqrt(changeQ.w*changeQ.w +changeQ.x*changeQ.x+changeQ.y*changeQ.y+changeQ.z*changeQ.z);
                changeQ.w *= s;
                changeQ.x *= s;
                changeQ.y *= s;
                changeQ.z *= s;                
                
                //tracking each rotation to determine auto rotation after mouse up:
                
                //determine last movement
                stepChangeQ.w = lastV.x*v2.x + lastV.y*v2.y + lastV.z*v2.z;
                stepChangeQ.x = lastV.y*v2.z - v2.y*lastV.z;
                stepChangeQ.y = -lastV.x*v2.z + v2.x*lastV.z;
                stepChangeQ.z = lastV.x*v2.y - v2.x*lastV.y;
                //stepChangeQ = stepChangeQ.normalize();
                
                lastV = v2.clone();
                
                averagingArray.push(stepChangeQ);
                
                if (averagingArray.length > averagingSize) {
                    averagingArray.splice(0,1);
                }
                
                //TESTING:
                //txtMonitor.text = stepChangeQ.toString(4);
                //txtMonitor.appendText("  "+qSum.toString(4));
            }
            
            else {
                lastQ = currentQ.clone();
                changeQ.w = autoQuaternion.w;
                changeQ.x = autoQuaternion.x;
                changeQ.y = autoQuaternion.y;
                changeQ.z = autoQuaternion.z;
            }
            
            //find current resultant rotation by quaternion multiplication
            //of lastQ and changeQ    
            currentQ.w = changeQ.w*lastQ.w - changeQ.x*lastQ.x - changeQ.y*lastQ.y - changeQ.z*lastQ.z;
            currentQ.x = changeQ.w*lastQ.x + lastQ.w*changeQ.x + changeQ.y*lastQ.z - lastQ.y*changeQ.z;
            currentQ.y = changeQ.w*lastQ.y + lastQ.w*changeQ.y - changeQ.x*lastQ.z + lastQ.x*changeQ.z;
            currentQ.z = changeQ.w*lastQ.z + lastQ.w*changeQ.z + changeQ.x*lastQ.y - lastQ.x*changeQ.y;
            
            
            ///////////
            for (var i:Number = 0; i<=particles.length-1; i++) {
                p = particles[i];
                
                //viewpoint-relative coordinates found by quaternion rotation.
                //Quaternion has been converted to rotation matrix, using
                //common subexpressions for efficiency.
                //(Efficient algorithm found on Wikipedia!)
                
                X2 = 2*currentQ.x;
                Y2 = 2*currentQ.y;
                Z2 = 2*currentQ.z;
                wX = currentQ.w*X2;
                wY = currentQ.w*Y2;
                wZ = currentQ.w*Z2;
                xX = currentQ.x*X2;
                xY = currentQ.x*Y2;
                xZ = currentQ.x*Z2;
                yY = currentQ.y*Y2;
                yZ = currentQ.y*Z2;
                zZ = currentQ.z*Z2;
                
                p.u = (1-(yY+zZ))*p.x + (xY-wZ)*p.y + (xZ+wY)*p.z;
                p.v = (xY+wZ)*p.x + (1-(xX+zZ))*p.y + (yZ-wX)*p.z;
                p.w = (xZ-wY)*p.x + (yZ+wX)*p.y + (1-(xX+yY))*p.z - recess;
                
                //Projection coordinates
                m = fLen/(fLen - p.w);
                p.projX = p.u*m + projOriginX;
                p.projY = p.v*m + projOriginY;
                
                //test if particle is in viewable region
                if ((p.projX > this.width)||(p.projX<0)||(p.projY<0)||(p.projY>this.height)||(p.w>fLen-1)) {
                    p.onScreen = false;
                }
                else {
                    p.onScreen = true;
                }
                
                //drawing
                intX = int(p.projX);
                intY = int(p.projY);
                if (p.onScreen) {
                    if (!(p.w < zBuffer[intX+this.height*intY])) {
                        if (this.useDepthDarken) {
                            //position-based darkening - exponential
                            if (moveLightWithRecess) {
                                f = A*Math.exp(expRate*(p.w+recess));
                            }
                            else {
                                f = A*Math.exp(expRate*(p.w));
                            }
                            if (f>maxBrighten) {
                                f = maxBrighten;
                            }
                            dColor = (0xFF000000) |(Math.min(255,f*p.red) << 16) | (Math.min(255,f*p.green) << 8) | (Math.min(255,f*p.blue));
                            this.bitmapData.setPixel32(intX, intY, dColor);
                        }
                        else {
                            this.bitmapData.setPixel32(intX, intY, p.color);
                        }
                                                
                        this.zBuffer[intX+this.height*intY] = p.w;
                    }
                }
            }
        }//end of drawParticles
        
        private function onDown(evt:MouseEvent):void {
            this.holder.removeEventListener(MouseEvent.MOUSE_DOWN, onDown);
            this.holder.root.addEventListener(MouseEvent.MOUSE_UP, onUp);
            mouseDragging = true;
            changeQ = new Quaternion(1,0,0,0);
            qSum = new Quaternion(0,0,0,0);
            averagingArray = [qSum];
            lastQ = currentQ.clone();
            v1.x = (holder.mouseX-projOriginX)/arcballRad;
            v1.y = (holder.mouseY-projOriginY)/arcballRad;
            rSquare = v1.x*v1.x + v1.y*v1.y;
            if (rSquare > 1) {
                invR = 1/Math.sqrt(rSquare);
                v1.x = v1.x*invR;
                v1.y = v1.y*invR;
                v1.z = 0;
                inside = false;
            }
            else {
                v1.z = Math.sqrt(1 - rSquare);
                inside = true;
            }
            lastV = v1.clone();
        }
        
        private function onUp(evt:MouseEvent):void {
            this.holder.addEventListener(MouseEvent.MOUSE_DOWN, onDown);
            this.holder.root.removeEventListener(MouseEvent.MOUSE_UP, onUp);
            mouseDragging = false;
            if (averagingArray.length>0) {
                qSum = new Quaternion(0,0,0,0);
                for (var t:Number = 0; t<= averagingArray.length-1; t++) {
                    qSum = qSum.add(averagingArray[t]);
                }
                if (qSum.magnitudeSquare() != 0) {
                    autoQuaternion = qSum.normalize();
                }
                //else {
                    //autoQuaternion = new Quaternion(1,0,0,0);
                //}
            }
            //TESTING:
            //txtMonitor.text =averagingArray.length.toString();
        }
        
    }//end of class definition


    import flash.display.*;
    class Particle3D {
        
        public var x:Number;
        public var y:Number;
        public var z:Number;        
        
        private var outputPoint:Point3D;
        
        //links, for creating linked lists
        public var next:Particle3D;
        public var prev:Particle3D;
        
        public var onScreen:Boolean;
        
        //velocity and acceleration vectors
        public var vel:Point3D = new Point3D();
        public var accel:Point3D = new Point3D();
        
        public var lastX:Number;
        public var lastY:Number;
        public var lastZ:Number;
        
        //projected coordinates
        public var projX:Number;
        public var projY:Number;
        
        //coords WRT viewpoint axes
        public var u:Number;
        public var v:Number;
        public var w:Number;
        
        //location in source picture
        public var picX:Number;
        public var picY:Number;
        
        //destination array
        public var dest:Array;
        
        //attributes
        public var color:uint;
        public var red:Number;
        public var green:Number;
        public var blue:Number;
        public var lum:Number;
        public var alpha:Number;
        
        public var initColor:uint;
        public var initRed:Number;
        public var initGreen:Number;
        public var initBlue:Number;
        public var initLum:Number;
        
        public var destColor:uint;
        public var destRed:Number;
        public var destGreen:Number;
        public var destBlue:Number;
        public var destLum:Number;
                
        public var colorChanging:Boolean;
        
        function Particle3D(thisColor:Number = 0xFFFFFFFF):void {
            this.dest = [];
            this.color = thisColor;
            this.red = getRed(thisColor);
            this.green = getGreen(thisColor);
            this.blue = getBlue(thisColor);
            this.alpha = getAlpha(thisColor);
            this.lum = 0.2126*this.red + 0.7152*this.green + 0.0722*this.blue;
            this.colorChanging = false;
            this.onScreen = true;
        }
        
        public function setColor(thisColor:Number):void {
            this.color = thisColor;
            this.red = getRed(thisColor);
            this.green = getGreen(thisColor);
            this.blue = getBlue(thisColor);
            this.alpha = getAlpha(thisColor);
            this.lum = 0.2126*this.red + 0.7152*this.green + 0.0722*this.blue;
        }
        
        public function getAlpha(c:Number):Number {
            return Number((c >> 24) & 0xFF);
        }
        public function getRed(c:Number):Number {
            return Number((c >> 16) & 0xFF);
        }
        public function getGreen(c:Number):Number {
            return Number((c >> 8) & 0xFF);
        }
        public function getBlue(c:Number):Number {
            return Number(c & 0xFF);
        }
    }


    import flash.display.*;
    import flash.events.*;
    class NoiseParticleMaker extends EventDispatcher {
        
        public static const PARTICLES_CREATED:String="particlesCreated";
        
        private var _particleArray:Array;
        public var numParticles:Number;
        
        public function NoiseParticleMaker(num:Number):void {
            this._particleArray = [];
            this.numParticles = num;
        }

        public function createParticles():void {
            var c:uint;
            var r:Number;
            var g:Number;
            var b:Number;
            for (var i:Number=0; i<=numParticles-1; i++) {
                r = Math.random()*255;
                g = Math.random()*255;
                b = Math.random()*255;
                c = uint(0xFF000000 | int(r) << 16 | int(g) << 8 | int(b));
                var p:Particle3D = new Particle3D(c);
                //add to array            
                this.particleArray.push(p);
            }
            dispatchEvent(new Event(NoiseParticleMaker.PARTICLES_CREATED));
        }
                
        public function setGlobalAlpha(globalAlpha:Number):void {
            var c:uint;
            var p:Particle3D;
            trace(_particleArray.length);
            for (var t:int=0; t<= _particleArray.length - 1; t++) {
                p = _particleArray[t];
                p.setColor((uint(255*globalAlpha) << 24) | (p.color & 0x00FFFFFF));
            }
        }
        
        public function buildDestinationArrays(numDestinations:Number):void {
            var p:Particle3D;
            for (var t:int=0; t<= _particleArray.length - 1; t++) {
                p = _particleArray[t];
                for (var n:Number = 0; n <= numDestinations-1; n++) {
                    var thisPoint3D:Point3D = new Point3D();
                    p.dest.push(thisPoint3D);
                }
            }
        }
        
        public function get particleArray():Array {
            return _particleArray;
        }
        
    }

    class Quaternion {
        public var w:Number;
        public var x:Number;
        public var y:Number;
        public var z:Number;
        
        private var qOutput:Quaternion;
        
        public function Quaternion(_w:Number=0,_x:Number=0, _y:Number=0, _z:Number=0) {
            this.w = _w;
            this.x = _x;
            this.y = _y;
            this.z = _z;
        }
        
        public function clone():Quaternion {
            return new Quaternion(this.w, this.x, this.y, this.z);
        }
        
        public function add(q:Quaternion):Quaternion {
            qOutput = new Quaternion()
            qOutput.w = this.w + q.w;
            qOutput.x = this.x + q.x;
            qOutput.y = this.y + q.y;
            qOutput.z = this.z + q.z;
            return qOutput;
        }
        
        public function subtract(q:Quaternion):Quaternion {
            qOutput = new Quaternion()
            qOutput.w = this.w - q.w;
            qOutput.x = this.x - q.x;
            qOutput.y = this.y - q.y;
            qOutput.z = this.z - q.z;
            return qOutput;
        }
        
        public function normalize():Quaternion {
            qOutput = new Quaternion()
            var mag:Number = Math.sqrt(this.w*this.w + this.x*this.x + this.y*this.y + this.z*this.z);
            qOutput.w = this.w/mag;
            qOutput.x = this.x/mag;
            qOutput.y = this.y/mag;
            qOutput.z = this.z/mag;
            return qOutput;
        }
        
        public function magnitudeSquare():Number {
            return this.w*this.w + this.x*this.x + this.y*this.y +this.z*this.z;
        }
        
        public function toString(radix:int):String {
            return "(" + this.w.toFixed(radix) + ", " + this.x.toFixed(radix) + ", " + this.y.toFixed(radix) + ", " + this.z.toFixed(radix) + ")";
        }

    }

    class Point3D {
        
        public var x:Number;
        public var y:Number;
        public var z:Number;        
        
        private var outputPoint:Point3D;
        
        public function Point3D(x1:Number=0,y1:Number=0,z1:Number=0):void {
            this.x = x1;
            this.y = y1;
            this.z = z1;
        }
        
        public function clone():Point3D {
            outputPoint = new Point3D();
            outputPoint.x = this.x;
            outputPoint.y = this.y;
            outputPoint.z = this.z;
            return outputPoint;
        }
    }