forked from: Dynamic 2D Shadows

by heroboy forked from Dynamic 2D Shadows (diff: 316)
use shader
♥2 | Line 387 | Modified 2011-12-16 16:39:16 | MIT License
play

ActionScript3 source code

/**
 * Copyright heroboy ( http://wonderfl.net/user/heroboy )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/39zp
 */

package 
{
    import com.bit101.components.PushButton;
    import flash.display.Bitmap;
    import flash.display.BitmapData;
	import flash.display.Shader;
	import flash.display.ShaderJob;
    import flash.display.Shape;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.filters.BlurFilter;
	import flash.filters.ShaderFilter;
    import flash.geom.Matrix;
    import flash.geom.Point;
	import flash.utils.ByteArray;
	import flash.utils.getTimer;
    /*
<languageVersion : 1.0;>

kernel DistanceBasedBlur
<   namespace : "Your Namespace";
    vendor : "Your Vendor";
    version : 1;
    
>

{
    parameter float2 center
    <
        parameterType:"position";
        minValue:float2(0.0);
        maxValue:float2(1024.0);
    >;
    parameter float factor
    <
        minValue:0.0;
        maxValue:1.0;
        defaultValue:0.25;
    >;
    parameter float threshold
    <
        minValue:0.0;
        maxValue:1000.0;
        defaultValue:0.1;
    >;
    input image4 src;
    output pixel4 dst;

    void
    evaluatePixel()
    {
        float d = factor * (distance(outCoord(),center) - threshold);
        if (d<0.0) d = 0.0;
        float d2 = d/2.0;
        dst = (4.0*sampleNearest(src,outCoord()) + 
            2.0 * sampleNearest(src,outCoord()+float2(-d2,-d2)) + 
            2.0 * sampleNearest(src,outCoord()+float2(d2,-d2)) + 
            2.0 * sampleNearest(src,outCoord()+float2(-d2,d2)) + 
            2.0 * sampleNearest(src,outCoord()+float2(d2,d2)) + 
            sampleNearest(src,outCoord()+float2(d,0.0))+
            sampleNearest(src,outCoord()+float2(0.0,d))+
            sampleNearest(src,outCoord()+float2(-d2,0.0))+
            sampleNearest(src,outCoord()+float2(0.0,-d2))
        )/16.0;
    }
}
	*/
/*
<languageVersion : 1.0;>

kernel DistanceMap
<   namespace : "Your Namespace";
    vendor : "Your Vendor";
    version : 1;
>
{
    parameter float lightRadius
    <
        defaultValue:100.0;
    >;
    parameter float2 outputSize
    <
        defaultValue:float2(400.0);
    >;
    parameter float2 srcSize
    <
        parameterType:"inputSize";
        inputSizeName:"src";
    >;
    parameter float2 center
    <
        defaultValue:float2(200.0);
    >;
    input image4 src;
    output pixel4 dst;

    void
    evaluatePixel()
    {
        float r = lightRadius * (outCoord().x / outputSize.x);
        float t = 2.0 * 3.141592654 * outCoord().y / outputSize.y;
        float cx = r * cos(t) + center.x;
        float cy = r * sin(t) + center.y;
        //float2 c = float2(r * cos(t) + center.x,r * sin(t) + center.y);
        if ( cx>=0.0 && cy>=0.0 && cx<(srcSize.x) && cy<(srcSize.y) )
        {
            float dist = distance(float2(cx,cy),center);
            if (dist > lightRadius)
            {
                dst = float4(1.0,1.0,1.0,1.0);
            }
            else
            {
                if (sampleLinear(src,float2(cx,cy)).x < 1.0)
                {
                    dist /= lightRadius;
                    dist = clamp(dist,0.0,1.0);
                    dst = float4(dist,dist,dist,1.0);
                }
                else
                {
                    dst = float4(1.0,1.0,1.0,1.0);
                }
            }
        }
        else
        {
            dst = float4(1.0,1.0,1.0,1.0);
        }
    }
}

*/
/*
<languageVersion : 1.0;>

kernel RenderLight
<   namespace : "Your Namespace";
    vendor : "Your Vendor";
    version : 1;
>
{
    parameter float2 srcSize
    <
        parameterType:"inputSize";
        inputSizeName:"src";
    >;
    parameter float lightRadius
    <
        defaultValue:100.0;
    >;
    parameter float2 center
    <
        defaultValue:float2(100.0,100.0);
    >;
    input image4 src;
    output pixel4 dst;

    void
    evaluatePixel()
    {
        float2 dpos = outCoord() - center;
        float dist = length(dpos);
        float distFactor = atan(dpos.y,dpos.x);
        if (distFactor < 0.0)
        {
            distFactor += 2.0 * 3.141592654;
        }
        distFactor /= 2.0*3.141592654;
        float obstacleDist = lightRadius * sampleNearest(src,float2(0.0,
            distFactor * srcSize.y
        )).x;
        if (dist < obstacleDist)
        {
            float val = clamp(1.0 - dist/lightRadius,0.0,1.0);
            dst = float4(val,val,val,1.0);
        }
        else
        {
            dst = float4(0.0,0.0,0.0,1.0);
        }
        //dst = sampleNearest(src,outCoord());
    }
}

*/
    /**
     * Inspired by:
     * http://www.catalinzima.com/2010/07/my-technique-for-the-shader-based-dynamic-2d-shadows/
     */
    public class Main extends Sprite 
    {
        
        private const PI                 :Number = Math.PI;
        private const TWO_PI             :Number = 2 * Math.PI;
        private const HALF_PI            :Number = 0.5 * Math.PI;
        private const QUARTER_PI         :Number = 0.25 * Math.PI;
        
        private const NUM_OBSTACLES      :int    = 10;
        private const OBSTACLE_SIZE      :Number = 50;
        private const OBSTACLE_SIZE_VAR  :Number = 15;
        private const LIGHT_RADIUS       :Number = 100;
        
        private const BLUR               :Number = 2;
        private const DIST_BLUR_FACTOR   :Number = 0.25;
        private const DIST_BLUR_THRESHOLD:Number = 27;
        
        private var resultBMP            :Bitmap;
        private var dataBMP              :Bitmap;
        private var obstacles            :Sprite;
        private var result               :BitmapData;
        private var resultBlurTemp       :BitmapData;
        private var obstacleMap          :BitmapData;
        private var distanceMap          :BitmapData;
        private var reducedDistanceMap   :BitmapData;
        
		private var useShader:Boolean = false;
		
		//[Embed("DistanceMap.pbj",mimeType="application/octet-stream")]
        //private var distanceMapShaderData:Class;
		//private var distanceMapShader:Shader = new Shader(new distanceMapShaderData() as ByteArray);

		private var distanceMapShaderData:String = "a501000000a40b0044697374616e63654d6170a00c6e616d65737061636500596f7572204e616d65737061636500a00c76656e646f7200596f75722056656e646f7200a00876657273696f6e000100a1010200000c5f4f7574436f6f726400a101010000026c6967687452616469757300a20164656661756c7456616c75650042c80000a1010201000c6f757470757453697a6500a20264656661756c7456616c75650043c8000043c80000a1010201000373726353697a6500a20c706172616d657465725479706500696e70757453697a6500a20c696e70757453697a654e616d650073726300a1010202000c63656e74657200a20264656661756c7456616c7565004348000043480000a3000473726300a1020403000f64737400040000100100000003000010000000001d02002000008000030200200000c0001d000010020080003202002040c90fdb1d0200100200800003020010000040000402002001004000030200200200c0001d020010020080000d0200200200c0001d0400800000c00003040080020080001d0200200400000001020020020000001d040080020080000c0200200200c0001d0400400000c00003040040020080001d0200200400400001020020020040001d0400400200800032020020000000002b020020040000001d0180800080000032020020000000002b020020040040001d018040008000001d018020018000002d018020018040002a040080010080001d018080008000001d018040018080002d018040018000002a0400400100c0001d018080008000001d018020018040002d0180200180000034000000018080001d040020040000001d040010040040001d0500c10400b000250500c1020010001d020020050000002a000020020080001d018080008000003400000001800000320300803f800000320300403f800000320300203f800000320300103f80000035000000000000001d040020040000001d04001004004000310500f10400b000320400203f8000002a050080040080001d018040008000003400000001804000040400200000800003020020040080001d040020020080003204001000000000320500803f8000001d050040040080000a0500400400c0001d0500200500400009050020050000001d020020050080001d050080020080001d050040020080001d05002002008000320400203f8000001d050010040080001d0300f305001b003500000000000000320300803f800000320300403f800000320300203f800000320300103f800000360000000000000036000000000000003500000000000000320300803f800000320300403f800000320300203f800000320300103f8000003600000000000000";
		private var distanceMapShader:Shader = new Shader(byteArrayFromString(distanceMapShaderData));
		
		//[Embed("RenderLight.pbj", mimeType = "application/octet-stream")]
		//private var renderLightShaderData:Class;
		//private var renderLightShader:Shader = new Shader(new renderLightShaderData() as ByteArray);
		
		private var renderLightShaderData:String = "a501000000a40b0052656e6465724c69676874a00c6e616d65737061636500596f7572204e616d65737061636500a00c76656e646f7200596f75722056656e646f7200a00876657273696f6e000100a1010200000c5f4f7574436f6f726400a1010200000373726353697a6500a20c706172616d657465725479706500696e70757453697a6500a20c696e70757453697a654e616d650073726300a101010100086c6967687452616469757300a20164656661756c7456616c75650042c80000a1010201000663656e74657200a20264656661756c7456616c75650042c8000042c80000a3000473726300a1020402000f647374001d0300c100001000020300c1010060001d03003103001000240100110300b0001d0300800100c0001d0100100300c00006010010030080001d0300400100c00032010010000000002a0300400100c0001d0180800080000034000000018000003201001040c90fdb010300400100c00036000000000000003201001040c90fdb040400800100c000030300400400000032010010000000001d0400800100c0001d01001003004000030100100000c0001d0400400100c000300500f1040010001d0100100100000003010010050000001d0400800100c0002a030080040000001d018080008000003400000001800000320100103f800000040400400100000003040040030000001d0400200100c00002040020040040001d010010040080003204004000000000320400103f8000001d0500800100c0000a050080040040001d05004005000000090500400400c0001d010010050040001d0500800100c0001d0500400100c0001d0500200100c000320400403f8000001d050010040040001d0200f305001b003500000000000000320200800000000032020040000000003202002000000000320200103f8000003600000000000000";
		private var renderLightShader:Shader = new Shader(byteArrayFromString(renderLightShaderData));

		//[Embed("DistanceBasedBlur.pbj",mimeType="application/octet-stream")]
		//private var BlurShaderData:Class;
		//private var blurShader:Shader = new Shader(new BlurShaderData() as ByteArray);
		
		private var BlurShaderData:String = "a501000000a4110044697374616e63654261736564426c7572a00c6e616d65737061636500596f7572204e616d65737061636500a00c76656e646f7200596f75722056656e646f7200a00876657273696f6e000100a1010200000c5f4f7574436f6f726400a1010200000363656e74657200a20c706172616d657465725479706500706f736974696f6e00a2026d696e56616c7565000000000000000000a2026d617856616c7565004480000044800000a10101010008666163746f7200a2016d696e56616c75650000000000a2016d617856616c7565003f800000a20164656661756c7456616c7565003e800000a101010100047468726573686f6c6400a2016d696e56616c75650000000000a2016d617856616c756500447a0000a20164656661756c7456616c7565003dcccccda3000473726300a1020402000f647374001d01003100001000250100310000b0001d0100100100800002010010010040001d01002001000000030100200100c0001d0100100100800032010020000000002a010010010080001d0180800080000034000000018000003201001000000000360000000000000032010020400000000403008001008000030300800100c0001d010020030000003203008040800000300400f1000010001d0500f303000000030500f304001b003203008040000000320300100000000002030010010080001d0300400300c000320300100000000002030010010080001d0300200300c0001d0400c100001000010400c103006000300600f1040010001d0400f303000000030400f306001b001d0300f305001b00010300f304001b0032040080400000001d04004001008000320400100000000002040010010080001d0400200400c0001d0500c100001000010500c104006000300600f1050010001d0500f304000000030500f306001b001d0400f303001b00010400f305001b003203008040000000320300100000000002030010010080001d0300400300c0001d030020010080001d0500c100001000010500c103006000300600f1050010001d0500f303000000030500f306001b001d0300f304001b00010300f305001b0032040080400000001d040040010080001d040020010080001d0500c100001000010500c104006000300600f1050010001d0500f304000000030500f306001b001d0400f303001b00010400f305001b001d0300800100c00032030020000000001d030040030080001d030031000010000103003103001000300500f10300b0001d0300f304001b00010300f305001b0032040020000000001d040080040080001d0400400100c0001d040031000010000104003104001000300500f10400b0001d0400f303001b00010400f305001b00320300200000000002030020010080001d0300800300800032030020000000001d030040030080001d030031000010000103003103001000300500f10300b0001d0300f304001b00010300f305001b0032040020000000001d04008004008000320400200000000002040020010080001d040040040080001d040031000010000104003104001000300500f10400b0001d0400f303001b00010400f305001b003203008041800000040500f303000000030500f304001b001d0200f305001b00";
		private var blurShader:Shader = new Shader(byteArrayFromString(BlurShaderData));
		
		private function byteArrayToString(arr:ByteArray):String
		{
			var result:String = "";
			var s:String;
			for (var i:uint = 0; i < arr.length;++i )
			{
				s = arr[i].toString(16);
				if (s.length == 1) s = "0" + s;
				result += s;
			}
			return result;
		}
		private function byteArrayFromString(s:String):ByteArray
		{
			var result:ByteArray = new ByteArray();
			for (var i:uint = 0; i < s.length; i += 2 )
			{
				result.writeByte( parseInt(s.substr(i,2),16 ));
			}
			result.position = 0;
			return result;
		}
		
        public function Main() 
        {
            init();
            stage.addEventListener(MouseEvent.MOUSE_MOVE, mainLoop);
        }
        
        private function init():void 
        {
            
            result             = new BitmapData(150, 150, false, 0xFFFFFF);
            resultBlurTemp     = new BitmapData(150, 150, false, 0xFFFFFF);
            obstacleMap        = new BitmapData(150, 150, false, 0xFFFFFF);
            distanceMap        = new BitmapData(100, 100, false, 0xFFFFFF);
            reducedDistanceMap = new BitmapData(  1, 100, false, 0xFFFFFF);
            
            dataBMP = new Bitmap();
            addChild(dataBMP);
            
            resultBMP = new Bitmap(result);
            resultBMP.scaleX =  stage.stageWidth / result.width;
            resultBMP.scaleY =  stage.stageHeight / result.height;
            addChild(resultBMP);
            
            obstacles = new Sprite();
            //addChild(obstacles);
            
            new PushButton(this, 10, 10, "Regenerate Obstacles", regenerateObstacles);
            new PushButton(this, 10, 30, "Result",          showResult);
            new PushButton(this, 10, 50, "Obstacle Map",    showObstacleMap);
            new PushButton(this, 10, 70, "Distance Map",    showDistanceMap);
            new PushButton(this, 10, 90, "Reduced Distance Map",    showReducedDistanceMap);
            new PushButton(this, 10, 110, "use shader", function(e:Event):void
			{
				useShader = (e.target as PushButton).selected;
				mainLoop();
			}).toggle = true;
            regenerateObstacles();
			//trace("distanceMapShaderData:" + byteArrayToString(new distanceMapShaderData() as ByteArray));
			//trace("renderLightShaderData:" + byteArrayToString(new renderLightShaderData() as ByteArray));
			//trace("BlurShaderData:" + byteArrayToString(new BlurShaderData() as ByteArray));
        }
        
        private function mainLoop(e:Event = null):void 
        {
            result.lock();
            distanceMap.lock();
            reducedDistanceMap.lock();
            
			
			//var t1:int = getTimer();
            //pre-processing
            updateDistanceMap(useShader);
			//var t2:int = getTimer();
			
			//trace("updateDistanceMap:"+(t2-t1));
			
            reduceDistanceMap();
            //var t3:int = getTimer();
			//trace("reduceDistanceMap:"+(t3-t2));
            //rendering
            result.fillRect(result.rect, 0xFFFFFF);
			//var t4:int = getTimer();
            renderLight(useShader);
			//var t5:int = getTimer();
			//trace("renderLight:" + (t5 - t4));
            blurLight(useShader);
			//var t6:int = getTimer();
			//trace("blurLight:" + (t6 - t5));
            renderObstacles();
            
            result.unlock();
            distanceMap.unlock();
            reducedDistanceMap.unlock();
			//var t7:int = getTimer();
			//trace("total:" + (t7 - t1));
			//trace("\n");
        }
		
        private function updateDistanceMap(useShader:Boolean = true):void 
        {
			var mx:Number = stage.mouseX * obstacleMap.width / stage.stageWidth;
            var my:Number = stage.mouseY * obstacleMap.height / stage.stageHeight;
			if (useShader)
			{
				distanceMapShader.data.lightRadius.value = [LIGHT_RADIUS];
				distanceMapShader.data.outputSize.value = [distanceMap.rect.width, distanceMap.rect.height];
				distanceMapShader.data.src.input = obstacleMap;
				distanceMapShader.data.srcSize.value = [obstacleMap.rect.width, obstacleMap.rect.height];
				distanceMapShader.data.center.value = [mx, my];
				var job:ShaderJob = new ShaderJob(distanceMapShader, distanceMap);
				job.start(true);
				return;
			}
			
            distanceMap.fillRect(distanceMap.rect, 0xFFFFFF);
            
            
            
            for (var j:int = 0; j < distanceMap.height; ++j)
            {
                for (var i:int = 0; i < distanceMap.width; ++i)
                {
                    //polar to cartesian
                    var r:Number = LIGHT_RADIUS * (i / distanceMap.width);
                    var t:Number = TWO_PI * (j/ distanceMap.height);
                    var cx:Number = r * Math.cos(t) + mx;
                    var cy:Number = r * Math.sin(t) + my;
                    
                    if 
                    (
                        cx >= 0 && cx <= obstacleMap.width 
                        && cy >= 0 && cy <= obstacleMap.height
                    )
                    {
                        var dx:Number = cx - mx;
                        var dy:Number = cy - my;
                        var dist:Number = Math.sqrt(dx * dx + dy * dy);
                        
                        if (dist > LIGHT_RADIUS)
                        {
                            //out of light radius
                            distanceMap.setPixel(i, j, 0xFFFFFF);
                        }
                        else
                        {
                            //draw distance color
                            var channel:uint = 0xFF * (dist / LIGHT_RADIUS);
                            var color:uint = 0;
                            color |= channel << 16;
                            color |= channel << 8;
                            color |= channel;
                            distanceMap.setPixel
                            (
                                i, j, 
                                (obstacleMap.getPixel(cx, cy) < 0xFFFFFF)
                                ?(color):(0xFFFFFF)
                            );
                        }
                    }
                }
            }
        }
        
        private function reduceDistanceMap():void 
        {
            reducedDistanceMap.fillRect(reducedDistanceMap.rect, 0xFFFFFF);
            for (var t:int = 0; t < reducedDistanceMap.height; ++t)
            {
                for (var i:int = 0; i < distanceMap.width; ++i)
                {
                    var color:uint = distanceMap.getPixel(i, t);
                    if (color != 0xFFFFFF)
                    {
                        reducedDistanceMap.setPixel(0, t, color);
                        break;
                    }
                }
            }
        }
		
		private function reduceDistanceMap2():void
		{
			reducedDistanceMap.fillRect(reducedDistanceMap.rect, 0xFFFFFF);
			var vec:Vector.<uint> = distanceMap.getVector(distanceMap.rect);
			for (var t:int = 0; t < reducedDistanceMap.height; ++t)
            {
                for (var i:int = 0; i < distanceMap.width; ++i)
                {
                    var color:uint = vec[t * distanceMap.width + i];//distanceMap.getPixel(i, t);
					//color = distanceMap.getPixel(i, t);
                    if (color != 0xFFFFFFff)
                    {
                        reducedDistanceMap.setPixel(0, t, color);
                        break;
                    }
                }
            }
		}
		
        
        private function renderLight(useShader:Boolean=true):void 
        {
            var mx:Number = stage.mouseX * result.width / stage.stageWidth;
            var my:Number = stage.mouseY * result.height / stage.stageHeight;
			
			if (useShader)
			{
				renderLightShader.data.src.input = reducedDistanceMap;
				renderLightShader.data.srcSize.value = [reducedDistanceMap.width, reducedDistanceMap.height];
				renderLightShader.data.center.value = [mx, my];
				renderLightShader.data.lightRadius.value = [LIGHT_RADIUS];
				//renderLightShader.data.outputSize.value = [result.width, result.height];
				
				var job:ShaderJob = new ShaderJob(renderLightShader, result);
				job.start(true);
				return;
			}
			
            for (var j:int = 0; j < result.width; ++j)
            {
                for (var i:int = 0; i < result.height; ++i)
                {
                    var dx:Number = i - mx;
                    var dy:Number = j - my;
                    var dist:Number = Math.sqrt(dx * dx + dy * dy);
                    var obstacleDist:Number;
                    var color:uint;
                    var channel:uint;
                    var distFactor:Number = Math.atan2(dy, dx);
                    if (distFactor < 0) distFactor += TWO_PI;
                    distFactor /= TWO_PI;
                    
                    obstacleDist = 
                        LIGHT_RADIUS
                        * reducedDistanceMap.getPixel
                        (0, reducedDistanceMap.height * distFactor);
                        
                    obstacleDist /= 0xFFFFFF;
                    
                    if (dist < obstacleDist)
                    {
                        channel = 0xFF * (1 - dist / LIGHT_RADIUS);
                        color = 0;
                        color |= channel << 16;
                        color |= channel << 8;
                        color |= channel;
                    }
                    else
                    {
                        color = 0x000000;
                    }
                    
                    result.setPixel(i, j, color);
                }
            }
        }
        
        private function blurLight(useShader:Boolean = true):void
        {
			
			
            var mx:Number = stage.mouseX * result.width / stage.stageWidth;
            var my:Number = stage.mouseY * result.height / stage.stageHeight;
            if (useShader)
			{
				blurShader.data.center.value = [ mx, my];
				blurShader.data.factor.value = [DIST_BLUR_FACTOR];
				blurShader.data.threshold.value = [DIST_BLUR_THRESHOLD];
				blurShader.data.src.input = result;

				var job:ShaderJob = new ShaderJob(blurShader, resultBlurTemp);
				job.start(true);
			}
			else
			{
				resultBlurTemp.copyPixels(result, result.rect, new Point(0, 0));
				//distance-based blur
				for (var j:int = 0; j < result.height; ++j)
				{
					for (var i:int = 0; i < result.width; ++i)
					{
						var dx:Number = i - mx;
						var dy:Number = j - my;
						var d:Number = 
							DIST_BLUR_FACTOR 
							* (Math.sqrt(dx * dx + dy * dy) - DIST_BLUR_THRESHOLD);
							
						d = (d < 0)?(0):(d);
						var channel:uint = 
							(
								4 * (result.getPixel(i, j) & 0xFF)
								+ 2 * (result.getPixel(i - d / 2, j - d / 2) & 0xFF)
								+ 2 * (result.getPixel(i + d / 2, j - d / 2) & 0xFF)
								+ 2 * (result.getPixel(i + d / 2, j + d / 2) & 0xFF)
								+ 2 * (result.getPixel(i - d / 2, j + d / 2) & 0xFF)
								+ (result.getPixel(i - d, j) & 0xFF)
								+ (result.getPixel(i, j - d) & 0xFF)
								+ (result.getPixel(i + d, j) & 0xFF)
								+ (result.getPixel(i, j + d) & 0xFF)
							) / 16;
						var color:uint = 0;
						color |= channel << 16;
						color |= channel << 8;
						color |= channel;
						
						resultBlurTemp.setPixel(i, j, color);
					}
				}
			}
            
            var temp:BitmapData = result;
            result = resultBlurTemp;
            resultBlurTemp = temp;
            resultBMP.bitmapData = result;
            
            //global blur
            result.applyFilter
            (
                result, result.rect, new Point(0, 0), 
                new BlurFilter(BLUR, BLUR, 2)
            );
        }
        
        private function renderObstacles():void 
        {
            result.draw
            (
                obstacles, 
                new Matrix
                (
                    result.width / stage.stageWidth, 0, 
                    0, result.height / stage.stageHeight
                )
            );
        }
        
        private function regenerateObstacles(e:Event = null):void
        {
            while (obstacles.numChildren) obstacles.removeChildAt(0);
            
            for (var i:int = 0; i < NUM_OBSTACLES; ++i)
            {
                var shape:Shape = new Shape();
                var size:Number = 
                    OBSTACLE_SIZE 
                    + OBSTACLE_SIZE_VAR * (2 * (0.5 - Math.random()));
                
                shape.graphics.beginFill(0x808080, 1);
                shape.graphics.drawRect( -0.5 * size, -0.5 * size, size, size);
                shape.rotation = 360 * Math.random();
                shape.x = stage.stageWidth * Math.random();
                shape.y = stage.stageHeight * Math.random();
                obstacles.addChild(shape);
            }
            
            obstacleMap.lock();
            
            obstacleMap.fillRect(obstacleMap.rect, 0xFFFFFF);
            obstacleMap.draw
            (
                obstacles, 
                new Matrix
                (
                    obstacleMap.width / stage.stageWidth, 0, 
                    0, obstacleMap.height / stage.stageHeight
                )
            );
            obstacleMap.threshold
            (
                obstacleMap, obstacleMap.rect, new Point(0, 0), "<", 
                0xFF0000, 0x000000, 0xFF0000, true
            );
            
            obstacleMap.unlock();
            
            //update
            mainLoop();
        }
        
        private function showResult(e:Event = null):void
        {
            resultBMP.visible = true;
            obstacles.visible = true;
            dataBMP.visible = false;
        }
        
        private function showObstacleMap(e:Event = null):void
        {
            resultBMP.visible = false;
            obstacles.visible = false;
            dataBMP.visible = true;
            
            dataBMP.bitmapData = obstacleMap;
            dataBMP.scaleX = stage.stageWidth / obstacleMap.width;
            dataBMP.scaleY = stage.stageHeight / obstacleMap.height;
        }
        
        private function showDistanceMap(e:Event = null):void
        {
            resultBMP.visible = false;
            obstacles.visible = false;
            dataBMP.visible = true;
            
            dataBMP.bitmapData = distanceMap;
            dataBMP.scaleX = stage.stageWidth / distanceMap.width;
            dataBMP.scaleY = stage.stageHeight / distanceMap.height;
        }
        
        private function showReducedDistanceMap(e:Event = null):void
        {
            resultBMP.visible = false;
            obstacles.visible = false;
            dataBMP.visible = true;
            
            dataBMP.bitmapData = reducedDistanceMap;
            dataBMP.scaleX = stage.stageWidth / reducedDistanceMap.width;
            dataBMP.scaleY = stage.stageHeight / reducedDistanceMap.height;
        }
    }
}