Fl020_DotsDemo.as (Main)

/**
 * @date    05/11/2008
 * @author    borealkiss    
 * @see     http://boreal-kiss.com/flash/ex/51/
 */ 
package {
    import flash.display.Sprite;
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.events.Event;
    import flash.geom.Matrix3D;
    import flash.geom.Vector3D;
    import flash.geom.PerspectiveProjection;
    import com.borealkiss.display.dots.Dots;
    import com.borealkiss.display.dots.PerlinNoiseSphere;
    
    [SWF(width="700",height="500",backgroundColor="0x0")]
    
    public class Fl020_DotsDemo extends Sprite{
        private var _projectionMatrix:Matrix3D;
        private var _primitive:Dots;
        private var _scene:Sprite;
        private var _canvasBitmap:BitmapData;
        
        /**
         * Constructor
         */ 
        public function Fl020_DotsDemo(){
            init();
        }
        
        private function init():void{
            //Canvas the same as stage in size.
            _canvasBitmap = new BitmapData(stage.stageWidth,stage.stageHeight,false,0x0);
            addChild(new Bitmap(_canvasBitmap));
            
            //Invisible scene in motion
            _scene = new Sprite();
            _scene.transform.matrix3D = new Matrix3D();
                        
            //Primitive            
            _primitive = new PerlinNoiseSphere(0.8,10000);
            
            var pp:PerspectiveProjection= new PerspectiveProjection();
            pp.fieldOfView = 100;
            _projectionMatrix = pp.toMatrix3D();
            
            addEventListener(Event.ENTER_FRAME,drawBitmap);
        }
        
        private function drawBitmap(e:Event):void{
            rotate(_scene);
               var world:Matrix3D = _scene.transform.matrix3D.clone();
            world.append(_projectionMatrix);
            var projectedVerts:Array = project3D(_primitive.vertices,world);
            
            _canvasBitmap.fillRect(_canvasBitmap.rect,0x0);
            for (var i:int=0; i<projectedVerts.length; i++){
                   var v:Vector3D = projectedVerts[i];
                   var x:Number = _canvasBitmap.width/2 + v.x;
                   var y:Number = _canvasBitmap.height/2 + v.y;
                   _canvasBitmap.setPixel(x,y,getColor(x,y));
               }
        }
       
        private function project3D(verts:Array,world:Matrix3D):Array{
            var results:Array = new Array();
               for (var i:int=0; i<verts.length; i++){
                   var v:Vector3D = world.transformVector(verts[i]);
                   results.push(v);
               }
               return results;
        }
        
        private function rotate(target:Sprite):void{
            var dx:Number = (mouseX - stage.stageWidth/2);
            var dy:Number = (mouseY - stage.stageHeight/2);
            target.rotationX = 0.5*dy;
            target.rotationY = -0.8*dx;
        }
        
        private function getColor(x:Number,y:Number):uint{
            var r:int = 255 * Math.random();
            var g:int = 255*x/_canvasBitmap.width;
            var b:int = 255*y/_canvasBitmap.height;
            return r << 16 | g << 8 | b;
        }
    }
}

com.borealkis.display.dots.Dots.as (Abstract)

package com.borealkiss.display.dots{
    import flash.errors.IllegalOperationError;
    
    /**
     * Abstract class.
     */ 
    public dynamic class Dots{
        protected var _vertices:Array;
        public function get vertices():Array{
            return _vertices;
        }
        public function set vertices(value:Array):void{
            _vertices = value
        }
        
        /**
         * Constructor
         */ 
        public function Dots(){
            if(Object(this).constructor == Dots){
                throw new IllegalOperationError("Abstract class  must be inherited in a subclass.");
            }
        }
    }
}

com.borealkis.display.dots.PerlinNoiseSphere.as

package com.borealkiss.display.dots{
    import flash.display.BitmapData;
    import flash.geom.Vector3D;
    import flash.geom.Matrix3D;
    import com.borealkiss.display.dots.Dots;
    import com.borealkiss.display.PerlinNoiseData;
    
    public class PerlinNoiseSphere extends Dots{
        protected var _totalPoints:int;
        protected var _bitmapData:BitmapData;
        protected var _topography:Array;
        protected var _referenceRadius:Number;
        
        private const BITMAP_WIDTH:int = 90;
        private const BITMAP_HEIGHT:int = 90;
        private const SCALE_RAD:Number = 0.2;//By which the rad-value (topography) is scaled.
        
        /**
         * Constructor
         */ 
        public function PerlinNoiseSphere(referenceRadius:Number,totalPoints:int){
            _referenceRadius = referenceRadius;
            _totalPoints = totalPoints;
            _bitmapData = new PerlinNoiseData(BITMAP_WIDTH,BITMAP_HEIGHT,BITMAP_WIDTH/5,BITMAP_HEIGHT/5);
            init();
        }
        
        protected function init():void{
            setTopographyData();
            
            this.vertices = new Array();
            for (var i:int=0; i<_totalPoints; i++){
                var vertex:Vector3D = randomPlot();
                this.vertices.push(vertex);
            }
        }
        
        protected function randomPlot():Vector3D{
            var w:int = BITMAP_WIDTH;
            var h:int = BITMAP_HEIGHT;
            var i:int = Math.floor(w*Math.random());
            var j:int = Math.floor(h*Math.random());
            
            var phi:Number = Math.PI*(2*i - w)/w;    //Longitude (-PI <= phi <= PI)
            var theta:Number = Math.PI*(j - h/2)/h;    //Latitude (-PI/2 <= theta <= PI/2)
            var rad:Number = _referenceRadius*(1 + _topography[i][j]*SCALE_RAD);
            
            var x:Number = rad*Math.cos(theta)*Math.cos(phi);
            var y:Number = rad*Math.cos(theta)*Math.sin(phi);
            var z:Number = rad*Math.sin(theta);
            return new Vector3D(x,y,z);
        }
        
        protected function setTopographyData():void{
            _topography = new Array();
            for (var i:int=0; i<BITMAP_WIDTH; i++){
                var topo:Array = new Array();
                for (var j:int=0; j<BITMAP_HEIGHT; j++){
                    topo.push(getHeight(i,j));
                }
                _topography.push(topo);
            }
        }
        
        /**
         * @return Number Ranges from -1 to 1.
         */
        protected function getHeight(x:int,y:int):Number{
            var color:uint = _bitmapData.getPixel(x,y);
            var r:Number = color >> 16;
            return 2*(r - 255)/255 + 1;//-1<=height<=1
        }
    }
}

com.borealkis.display.PerlinNoiseData.as

package com.borealkiss.display{
    import flash.display.BitmapData;
    
    /**
     * Create BitmapData filled with a specific perlin noise.
     */ 
    public class PerlinNoiseData extends BitmapData{
        protected var _width:int;
        protected var _height:int;
        protected var _baseX:Number;
        protected var _baseY:Number;
        protected var _numOctaves:uint;
        protected var _randomSeed:int;
        protected var _stitch:Boolean;
        protected var _fractalNoise:Boolean;
        protected var _channelOptions:uint;
        protected var _grayScale:Boolean;
        
        /**
         * Constructor
         */ 
        public function PerlinNoiseData(width:int,height:int,baseX:Number,baseY:Number,
                        numOctaves:uint=1,randomSeed:int=1,stitch:Boolean=true,
                        fractalNoise:Boolean=false,channelOptions:uint=7,grayScale:Boolean=true){
            _width = width;
            _height = height;
            _baseX = baseX;
            _baseY = baseY;
            _numOctaves = numOctaves;
            _randomSeed = randomSeed;
            _stitch = stitch;
            _fractalNoise = fractalNoise;
            _channelOptions = channelOptions;
            _grayScale = grayScale;
            super(_width,_height,false,0x0);
            init();
        }
        
        protected function init():void{
            this.perlinNoise(_baseX,_baseY,_numOctaves,_randomSeed,_stitch,_fractalNoise,_channelOptions,_grayScale);
        }
        
        /**
         * @param red Use red channel.
         * @param green User green channel.
         * @param blue Use blue channel.
         * @return uint ChanneOptions for BitmapData.perlinNoise().
         */ 
        public static function getChannelOptions(red:Boolean,green:Boolean,blue:Boolean):uint{
            return uint(red) + 2*uint(green) + 4*uint(blue);
        }
    }
}
Powered by blog Boreal Kiss 2008.