Beautifl - Flash Gallery

Preview

miniapp 2009年8月27日 All rights reserved
?
本格的な布のシミュレーションではありません。  
 
ドラッグでマウスに一番近いポイントを移動させる。  
ctrlキー押しながらドラッグで固定。  
ダブルクリックで固定を解除。  
 
<問題点>  
左右対称になってない。右によってる。なんでだろう。
      /**
 * 本格的な布のシミュレーションではありません。
 *
 * ドラッグでマウスに一番近いポイントを移動させる。
 * ctrlキー押しながらドラッグで固定。
 * ダブルクリックで固定を解除。
 *
 * <問題点>
 * 左右対称になってない。右によってる。なんでだろう。
 */
package {
    import flash.display.Sprite;
    import flash.display.StageQuality;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    import flash.events.KeyboardEvent;
    import flash.events.MouseEvent;

    [SWF(backgroundColor="0xFFFFFF", width="465", height="465", frameRate="60")]
    public class Cloth extends Sprite {
        
        public static const STAGE_WIDTH:uint = 465;
        public static const STAGE_HEIGHT:uint = 465;
        
        public function Cloth() {
            if (stage) init();
            else addEventListener(Event.ADDED_TO_STAGE, init);
        }
        
        private var _lineColor:uint = 0x000000;
        private var _cols:uint = 16;//横の数
        private var _rows:uint = 16;//縦の数
        private var _diffX:uint = 10;
        private var _diffY:uint = 10;
        private var _isCtrlPress:Boolean = false;
        private var _isMouseDown:Boolean = false;
        private var _draggedPoint:Point;
        private var _isFirst:Boolean = false;
        private var _numJoints:uint;
        private var _joints:Vector.<Joint> = new Vector.<Joint>();
        private var _points:Vector.<Point> = new Vector.<Point>();
        private var _pointsXs:Vector.<Vector.<Point>> = new Vector.<Vector.<Point>>(_rows, true);
        private var _pointsYs:Vector.<Vector.<Point>> = new Vector.<Vector.<Point>>(_cols, true);
        
        private function init(e:Event = null):void {
            removeEventListener(Event.ADDED_TO_STAGE, init);
            stage.scaleMode = StageScaleMode.NO_SCALE;
            //stage.quality = StageQuality.MEDIUM;
            
            stage.doubleClickEnabled = true;
            stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
            stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDwonHandler);
            stage.addEventListener(MouseEvent.DOUBLE_CLICK, doubleClickHandler);
            stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDonwHandler);
            stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
            
            putPoint();
            joint();
            
            //上端2つを固定します。
            //左端
            var point1:Point = _pointsXs[0][0];
            point1.x = 100;
            point1.y = 10;
            point1.isPinned = true;
            
            //右端
            var point2:Point = _pointsXs[0][_rows - 1];
            point2.x = STAGE_WIDTH - 100;
            point2.y = 10;
            point2.isPinned = true;
            
            addEventListener(Event.ENTER_FRAME, enterFrameHandler);
        }
        
        /**
         * ポイントを配置します。
         */
        private function putPoint():void {
            //縦方向に並べたポイントを入れる配列を先に用意する
            for (var i:uint = 0; i < _cols; i++) { 
                var pointsY:Vector.<Point> = new Vector.<Point>(_rows, true);
                _pointsYs[i] = pointsY;
            }
            
            var clothWidth:Number = (_cols - 1) * _diffX;
            var clothHeight:Number = (_rows - 1) * _diffY;
            var startX:Number = (STAGE_WIDTH - clothWidth) / 2;
            var startY:Number = (STAGE_HEIGHT - clothHeight) / 2;
            
            //ポイントを格子状に配置
            for (i = 0; i < _rows; i++) {
                var pointsX:Vector.<Point> = new Vector.<Point>(_cols, true);
                _pointsXs[i] = pointsX;
                for (var j:uint = 0; j < _cols; j++) {
                    var point:Point = new Point();
                    _points.push(point);
                    point.name = String(i) + "-" + String(j);//デバッグ用
                    point.x = startX + _diffX * j;
                    point.y = startY + _diffY * i;
                    
                    //横に並ぶポイントを入れる
                    pointsX[j] = point;
                    
                    //縦方向に並ぶポイントを入れる
                    pointsY = _pointsYs[j];
                    pointsY[i] = point;
                }
            }
            _points.fixed = true;
        }
        
        private function joint():void {
            
            //00------01-------02
            //|        |       |
            //|        |       |
            //|        |       |
            //10------11-------12
            //|        |       |
            //|        |       |
            //|        |       |
            //20------21-------22
            
            for (var i:uint = 0; i < _rows; i++) {
                var up:Boolean = (i - 1) >= 0;//上の行があるか。
                var down:Boolean = (i + 1) < _rows;//下の行があるか。
                if (up)
                    var pointsX0:Vector.<Point> = _pointsXs[i - 1];//一つ上の段
                    
                var pointsX1:Vector.<Point> = _pointsXs[i];
                
                if (down)
                    var pointsX2:Vector.<Point> = _pointsXs[i + 1];//一つ下の段
                
                for (var j:uint = 0; j < _cols; j++) {
                    var left:Boolean = (j - 1) >= 0;//左の列があるか。
                    var right:Boolean = (j + 1) < _cols;//右の列があるか。
                    
                    var point11:Point = pointsX1[j];//中央のポイント
                    
                    if (up) {
                        var point01:Point = pointsX0[j];//上
                        //trace(point01.name);
                        _joints.push(new Joint(point11, point01));
                    }
                    
                    if (left) { 
                        var point10:Point = pointsX1[j -1];//左
                        //trace(point10.name);
                        _joints.push(new Joint(point11, point10));
                        
                        if (up) {
                            var point00:Point = pointsX0[j - 1]; //左上
                            //trace(point00.name);
                            _joints.push(new Joint(point11, point00));
                        }
                        
                        if (down) {
                            var point20:Point = pointsX2[j -1];//左下
                            //trace(point20.name);
                            _joints.push(new Joint(point11, point20));
                        }
                    }
                    
                    if (right) { 
                        var point12:Point = pointsX1[j + 1];//右
                        //trace(point12.name);
                        _joints.push(new Joint(point11, point12));
                        
                        if (up) {
                            var point02:Point = pointsX0[j + 1];//右上
                            //trace(point02.name);
                            _joints.push(new Joint(point11, point02));
                        }
                        
                        if (down) {
                            var point22:Point = pointsX2[j + 1];//右下
                            //trace(point22.name);
                            _joints.push(new Joint(point11, point22));
                        }
                    }
                    
                    if (down) {
                        var point21:Point = pointsX2[j];//下
                        //trace(point21.name);
                        _joints.push(new Joint(point11, point21));
                    }
                }
            }
            _numJoints = _joints.length;
            _joints.fixed = true;
        }
        
        /**
         * 一番カーソルに近いポイントを捜す。
         */
        private function searchPoint():Point {
            var lastMinDist:Number = Infinity;
            var target:Point;
            for each(var point:Point in _points) {
                var dx:Number = point.x - mouseX;
                var dy:Number = point.y - mouseY;
                var dist:Number = Math.sqrt(dx * dx + dy * dy);
                if (dist < lastMinDist) {
                    lastMinDist = dist;
                    target = point;
                }
            }
            return target;
        }
        
        /**
         * ポイント同士を繋ぐ線を書く
         */
        private function drawLine():void {
            graphics.clear();
            graphics.lineStyle(1, _lineColor);
            //横のラインを引く
            for each(var pointsX:Vector.<Point> in _pointsXs){
                //横列のポイントが入った配列にアクセス。
                var firstPoint:Point = pointsX[0];
                graphics.moveTo(firstPoint.x, firstPoint.y);
                for (var i:uint = 1; i < _cols; i++) {
                    var point:Point = pointsX[i];
                    graphics.lineTo(point.x, point.y);
                }
            }
            
            for each(var pointsY:Vector.<Point> in _pointsYs) {
                firstPoint = pointsY[0];
                graphics.moveTo(firstPoint.x, firstPoint.y);
                for (i = 1; i < _rows; i++) {
                    point = pointsY[i];
                    graphics.lineTo(point.x, point.y);
                }
            }
        }
        
        private function enterFrameHandler(e:Event):void {
            if (_isMouseDown) {
                _draggedPoint.x = mouseX;
                _draggedPoint.y = mouseY;
            }
            
            for each(var joint:Joint in _joints) {
                joint.update();
            }
            
            drawLine();
        }
        
        private function keyDonwHandler(e:KeyboardEvent = null):void{
            if(e.ctrlKey) _isCtrlPress = true;
        }
        
        private function keyUpHandler(e:KeyboardEvent = null):void{
            _isCtrlPress = false;
        }
        
        private function doubleClickHandler(e:MouseEvent = null):void{
            searchPoint().isPinned = false;
        }
        
        private function mouseDwonHandler(e:MouseEvent):void {
            _isMouseDown = true;
            _draggedPoint = searchPoint();
            _draggedPoint.isDragging = true;
        }
        
        private function mouseUpHandler(e:MouseEvent):void    {
            _isMouseDown = false;
            if (_isCtrlPress)
                _draggedPoint.isPinned = true;
            _draggedPoint.isDragging = false;
            _draggedPoint = undefined;
        }
    }
}

class Joint {
    
    public static var SPRING:Number = 0.03;
    public static var FRICTION:Number = 0.97;
    public static var GRAVITY:Number = 0.005;
    
    public function Joint(point:Point, target:Point) { 
        var initDx:Number = target.x - point.x;
        var initDy:Number = target.y - point.y;
        var length:Number = Math.sqrt(initDx * initDx + initDy * initDy);
        var angle:Number = Math.atan2(initDy, initDx);
        
        var tx:Number = length * Math.cos(angle);
        var ty:Number = length * Math.sin(angle);
        
        //バネ運動させる関数
        update = function():void {
            if (point.isDragging || point.isPinned)
                return;
            
            var dx:Number = target.x - tx - point.x;
            var dy:Number = target.y - ty - point.y;
            point.vx += dx * Joint.SPRING;
            point.vy += dy * Joint.SPRING;
            point.vy += GRAVITY;
            point.x += (point.vx *= FRICTION);
            point.y += (point.vy *= FRICTION);
        }
    }
    
    public var update:Function;
}

class Point {
    public var name:String;
    public var x:Number;
    public var y:Number;
    public var vx:Number = 0;
    public var vy:Number = 0;
    public var isPinned:Boolean = false;
    public var isDragging:Boolean = false;
}