Beautifl - Flash Gallery

Preview

flash on 2010-1-15
knd 2010年2月5日 All rights reserved
?
読み込んだ画像をポヨンポヨンさせる。  
クリックでdrawTrianglesのラインを表示/非表示。  
ドラッグして別の場所をポヨンポヨン。  
半径が小さいと振動が発散してしまうのを何とかしたい。  
  
画像 http://www.flickr.com/photos/iwao_kobayashi/2694620804/
      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);
        }
        
    }