Beautifl - Flash Gallery

Thumbnail : flash on 2010-1-15
flash on 2010-1-15
knd 2010-02-05 All rights reserved

再生するにはFlash Playerが必要です。デスクトップのブラウザでご覧ください。

package  
{
	import flash.utils.ByteArray;
    import flash.display.Bitmap;
	import flash.display.BitmapData;
    import flash.display.Graphics;
	import flash.display.Loader;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.net.URLRequest;
	import flash.system.LoaderContext;
	
	/**
	 * 読み込んだ画像をポヨンポヨンさせる。
	 * クリックでdrawTrianglesのラインを表示/非表示。
	 * ドラッグして別の場所をポヨンポヨン。
	 * 半径が小さいと振動が発散してしまうのを何とかしたい。
	 * 
	 * 画像 http://www.flickr.com/photos/iwao_kobayashi/2694620804/
	 */
	[SWF(width="465",height="465",backgroundColor="0x0",frameRate="30")]
	public class FlashTest extends Sprite
	{
		
		private var url:String;
		private var loader:Loader;
		private var domeData:BitmapData;
		private var r:Number;
		private var h:Number;
		private var layer:Sprite;
        
		public function FlashTest() 
		{
			if (stage) init();
			else addEventListener(Event.ADDED_TO_STAGE, init);
		}
		
		private function init(e:Event = null):void 
		{
			removeEventListener(Event.ADDED_TO_STAGE, init);
			// entry point
			url = "http://farm4.static.flickr.com/3195/2694620804_ab0fc9137f.jpg";
			loader = new Loader();
			loader.contentLoaderInfo.addEventListener(Event.COMPLETE, comp)
			loader.load(new URLRequest(url), new LoaderContext(true));
		}
		
		private function comp(e:Event):void 
		{
			domeData = new BitmapData(loader.width, loader.height);
			domeData.draw(loader);
            addChild(loader);
			
            layer = new Sprite();
            addChild(layer);
            
			createDome(168, 290, 113);
			
            stage.addEventListener(MouseEvent.MOUSE_DOWN, onDown);
		}
        
        private var circleX:Number;
        private var circleY:Number;
        private var circleR:Number;
        private function onDown(e:MouseEvent):void 
        {
            stage.removeEventListener(MouseEvent.MOUSE_DOWN, onDown);
            circleX = mouseX;
            circleY = mouseY;
            circleR = 0;
            stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMove);
            stage.addEventListener(MouseEvent.MOUSE_UP, mouseUp);
        }
        
        private function mouseMove(e:MouseEvent):void 
        {
            var dx:Number = mouseX - circleX;
            var dy:Number = mouseY - circleY;
            circleR = Math.sqrt(dx * dx + dy * dy);
            var g:Graphics = layer.graphics;
            g.clear();
            g.lineStyle(1, 0xffffff, 0.5);
            g.drawCircle(circleX, circleY, circleR);
        }
        
        private function mouseUp(e:MouseEvent):void 
        {
            layer.graphics.clear();
            stage.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMove);
            stage.removeEventListener(MouseEvent.MOUSE_UP, mouseUp);
            stage.addEventListener(MouseEvent.MOUSE_DOWN, onDown);
            
            if (circleR == 0) return;
            
			createDome(circleX, circleY, circleR);
       }
	   
       private var dm:Dome;
	   private function createDome(x:Number, y:Number, r:Number):void
	   {
            if (dm) removeChild(dm);
            dm = new Dome(domeData, x, y, r, r, 16, 8);
			dm.x = x;
			dm.y = y;
            addChild(dm);
	   }
		
	}
	
}

    import flash.display.BitmapData;
	import flash.display.Sprite;
	import flash.display.TriangleCulling;
	import flash.events.Event;
    import flash.events.MouseEvent;
	import flash.geom.Matrix;
	
    class Dome extends Sprite
    {
        private var _bitmapData :BitmapData;
        private var _x:Number;
        private var _y:Number;
        private var _r:Number;
        private var _h:Number;
        
        private var _n:uint;
        private var disks:Vector.<Disk>;
		private var startIndices:Vector.<uint>;
        private var func:DomeFunction;
        private var vertices:Vector.<Number>;
        private var indices:Vector.<int>;
        private var uvtData:Vector.<Number>;
		private var mat:Matrix;
		
		private var oscillators:Vector.<Oscillator>;
        
		private var showsLines:Boolean;
        private var isMouseOn:Boolean;
		
		/**
		 * 
		 * @param	bitmapData ターゲットのBitmapData
		 * @param	x BitmapData上の位置
		 * @param	y
		 * @param	r 半径
		 * @param	h 高さ
		 * @param	polygon 円をN角形で近似
		 * @param	diskNumber Diskを何枚重ねてDomeとするか
		 */
        public function Dome(bitmapData:BitmapData,
            x:Number, y:Number, r:Number, h:Number,
            polygon:uint, diskNumber:uint ) 
        {
			_bitmapData = bitmapData;
            _x = x;
            _y = y;
            _r = r;
            _h = h;
            _n = polygon;
			mat = new Matrix(1, 0, 0, 1, -x, -y);
            disks = new Vector.<Disk>();
			startIndices = new Vector.<uint>();
			oscillators = new Vector.<Oscillator>();
            func = new CircFunction(_r, _h, 0.1 * _h);
			vertices = new Vector.<Number>();
            vertices.push(0, 0);
			uvtData = new Vector.<Number>();
            uvtData.push(x / _bitmapData.width, y / _bitmapData.height);
            var i:int;
            var height:Number;
            var radius:Number;
            var disk:Disk;
			var osc:Oscillator;
			var thickness: Number = _h / diskNumber;
            for (i = diskNumber - 1; i > -1; i--) 
            {
                height = i * thickness;
                radius = func.domeRadius(height);
				
                disk = new Disk(bitmapData.rect, x, y, radius, polygon)
                disks.push(disk);
				
				startIndices.push(vertices.length);
                disk.mergeVertices(vertices, vertices.length);
                disk.mergeUVData(uvtData, uvtData.length);
				
				osc = new Oscillator();
				osc.mass = thickness * radius * radius;
				osc.z = height;
				osc.spring = _h * _r * _r;
				oscillators.push(osc);
            }
            
			buildIndices();
            redrawDome();
			
			addEventListener(Event.ENTER_FRAME, update);
            addEventListener(MouseEvent.CLICK, onClick);
            addEventListener(MouseEvent.MOUSE_OVER, onMouseOver);
            addEventListener(MouseEvent.MOUSE_OUT, onMouseOut);
        }
        
        private function onMouseOut(e:MouseEvent):void 
        {
            isMouseOn = false;
        }
        
        private function onMouseOver(e:MouseEvent):void 
        {
            isMouseOn = true;
        }
        
        private function onClick(e:MouseEvent):void 
        {
            showsLines = !showsLines;
        }
		
		private function buildIndices():void
		{
			indices = new Vector.<int>();
			var i:int;
			for (i = 1; i < _n; i++) 
			{
				indices.push(0, i, i + 1);
			}
			indices.push(0, _n, 1);
            var j: int;
			var length:uint = disks.length;
			for (j = 1; j < length; j++)
            {
                var jn:uint = j * _n;
                var j1n:uint = (j - 1) * _n;
                for (i = 1; i < _n; i++) 
                {
                    var i1:uint = i + 1;
                    indices.push(j1n + i, jn + i, jn + i1);
                    indices.push(j1n + i, jn + i1, j1n + i1);
                }
                indices.push(jn, jn + _n, jn + 1);
                indices.push(jn, jn + 1, j1n + 1);
            }
			indices = indices.reverse();
		}
		
		private function update(e:Event):void 
		{
            if (isMouseOn)
            {
				var f0:Number = 0.001 * _h * _r * _r;
                var dist:Number = Math.sqrt(mouseX * mouseX + mouseY * mouseY);
                var fx:Number = - f0 * mouseX / dist;
                var fy:Number = - f0 * mouseY / dist;
            }
            
			var i:int;
			var length:uint = disks.length;
			var disk:Disk;
			var disk1:Disk;
			var osc:Oscillator;
            for (i = length - 2; i > -1; i--) 
            {
                disk = disks[i];
				disk1 = disks[i + 1]
				osc = oscillators[i];
                if (isMouseOn) osc.addForce(fx, fy);
				osc.update();
				disk.moveTo(disk1.x + osc.x, disk1.y + osc.y);
                disk.mergeVertices(vertices, startIndices[i]);
            }
			disk = disks[0];
            disk1 = disks[1]
            vertices[0] = 2 * disk.x - disk1.x;
            vertices[1] = 2 * disk.y - disk1.y;
			redrawDome();
		}
        
        private function redrawDome():void
        {
            graphics.clear();
            if(showsLines)graphics.lineStyle(0, 0xffffff, 0.5);
            graphics.beginBitmapFill(_bitmapData, mat, false, true);
            graphics.drawTriangles(vertices, indices, uvtData, TriangleCulling.POSITIVE);
        }
        
        public function get bitmapData():BitmapData { return _bitmapData; }
        
        public function get r():Number { return _r; }
        
        public function get h():Number { return _h; }
                
    }

	import flash.geom.Rectangle;
	
	class Disk
	{
		private var _x:Number;
		private var _y:Number;
		private var _n:uint;
		private var _uvData:Vector.<Number>;
		private var _dRad:Number;
		
		private var _r:Number;
		private var _vertices:Vector.<Number>;
		private var _rotation:Number;
		
		public function Disk(rect:Rectangle, trimX:Number, trimY:Number, trimR:Number, n:uint) 
		{
			_x = 0;
			_y = 0;
			_r = trimR;
			_n = n;
			_vertices = new Vector.<Number>();
			_uvData = new Vector.<Number>();
			_dRad = 2 * Math.PI / _n;
			_rotation = 0;
			initializeUV(rect.width, rect.height, trimX, trimY, trimR);
			updateVertices();
		}
		
		private function initializeUV(w:Number, h:Number, x:Number, y:Number, r:Number):void
		{
			for (var i:uint = 0; i < _n; i++) 
			{
				var cos:Number = Math.cos(i * _dRad);
				var sin:Number = Math.sin(i * _dRad);
				var _2i:uint = i << 1;
				_uvData[_2i] = (x + r * cos) / w;
				_uvData[_2i + 1] = (y + r * sin) / h;
			}
		}
		
		private function updateVertices():void
		{
			var rad:Number = _rotation;
			for (var i:uint = 0; i < _n; i++) 
			{
				var cos:Number = Math.cos(rad);
				var sin:Number = Math.sin(rad);
				var _2i:uint = i << 1;
				_vertices[_2i] = _x + _r * cos;
				_vertices[_2i + 1] = _y + _r * sin;
				rad += _dRad;
			}
		}
		
		public function move(dx:Number, dy:Number):void
		{
			for (var i:uint = 0; i < _n; i++) 
			{
				var _2i:uint = i << 1;
				_vertices[_2i] += dx;
				_vertices[_2i + 1] += dy;
			}
			_x += dx;
			_y += dy;
		}
		
		public function moveTo(x:Number, y:Number):void
		{
			move(x - _x, y - _y);
		}
		
		public function mergeVertices(vertices:Vector.<Number>, startIndex:uint):void
		{
			mergeVector(vertices, startIndex, _vertices);
		}
		
		public function mergeUVData(uvData:Vector.<Number>, startIndex:uint):void
		{
			mergeVector(uvData, startIndex, _uvData);
		}
		
		private function mergeVector(into:Vector.<Number>, start:uint, from:Vector.<Number>):void
		{
			var last:uint = (_n << 1) + start;
			var i:uint = start;
			var j:uint = 0;
			for (; i < last;) 
			{
				into[i++] = from[j++]
			}
		}
		
		public function get x():Number { return _x; }
		
		public function set x(value:Number):void 
		{
			move(value - _x, 0);
		}
		
		public function get y():Number { return _y; }
		
		public function set y(value:Number):void 
		{
			move(0, value - _y);
		}
		
		public function get r():Number { return _r; }
		
		public function set r(value:Number):void 
		{
			_r = value;
			updateVertices();
		}
		
		public function get rotation():Number { return _rotation; }
		
		public function set rotation(value:Number):void 
		{
			_rotation = value;
			updateVertices();
		}
	}
	
    class Oscillator
    {
		public static const G:Number = 1.0; //振動子にはたらくy方向の重力
		public static const K:Number = 1.1; //ばねの強さ
		public static const R:Number = 0.9; //減衰の大きさ
		
        public var x:Number;
        public var y:Number;
		
        private var vx:Number;
        private var vy:Number;
        private var ax:Number;
        private var ay:Number;
        
        public var mass:Number;
		public var z:Number;
		public var spring:Number;
        
        public function Oscillator() 
        {
			x = 0;
			y = 0;
			vx = 0;
			vy = 0;
			ax = 0;
			ay = 0;
        }
        public function update():void
        {
			var param:Number = K * spring / z / mass;
            ax = - param * x;
			ay = - param * y + G;
            vx += ax;
            vy += ay;
			vx *= R;
			vy *= R;
            x += vx;
            y += vy;
        }
        public function addForce(fx:Number, fy:Number):void
        {
            var param:Number = z / mass;
            vx += param * fx;
            vy += param * fy;
        }
		
    }
	
	class DomeFunction
	{		
		private var alpha:Number;
		private var beta:Number;
		private var gamma:Number;
		
		protected function basicFunction(x:Number):Number
		{
			throw "override basicFunction()";
		}
		protected function basicInverse(y:Number):Number
		{
			throw "override basicInverse()";
		}
		
		public function DomeFunction(radius:Number, height:Number, depth:Number) 
		{
			gamma = depth;
			beta = height + gamma;
			alpha = radius / basicInverse(gamma / beta);
		}
		
		public final function domeHeight(radius:Number):Number
		{
			return beta * basicFunction(radius / alpha) - gamma;
		}
		
		public final function domeRadius(height:Number):Number
		{
			return alpha * basicInverse((height + gamma) / beta);
		}
	}
	
	class CircFunction extends DomeFunction
	{
		override protected function basicFunction(x:Number):Number 
		{
			return Math.sqrt(1 - x * x);
		}
		override protected function basicInverse(y:Number):Number 
		{
			return Math.sqrt(1 - y * y);
		}
		public function CircFunction(radius:Number, height:Number, depth:Number) 
		{
			super(radius, height, depth);
		}
		
	}