Dead Code Preservation :: Archived AS3 works from wonderfl.net

loco roco v0.2 (改造PuyoDot)

改造 from http://wonderfl.net/c/kwty
too many changes are made, i'll say it's more than fork
i think i should keep most of the original comments

now support various shapes, much stable gas

thanks @tail_y @http://wonderfl.net/user/tail_y

performance update
/**
 * Copyright zob ( http://wonderfl.net/user/zob )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/ev9L
 */

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

/**
 * 
 * 改造 from http://wonderfl.net/c/kwty
 * too many changes are made, i'll say it's more than fork
 * i think i should keep most of the original comments
 *
 * thanks @tail_y @http://wonderfl.net/user/tail_y
 *
 * zonnbe@2010
 */

package 
{
    /*
    PuyoDot
    プヨっとしたドット。 
    下のほうにあるパレットから拾ってきて表示するよ。
    ぐりぐりしたり、引っ張ったり、新しいドット絵を追加して遊んでね。
    (マップが意外と見ずらくなった。wonderflって等倍フォントじゃないんだね
    等倍のテキストエディタか何かで編集すると楽かも)
    
    本当はドットを編集する機能も入れたかったんだけど
    力尽きるどころの話じゃなかったから今回は諦めた。
    でもいつか作りたいね。
     */
     
     /*
    
    ドット状、任意外形の弾性体を表現します。
    こういう、ぐにぐにしたものは、各頂点をテンションで繋ぐfladdict式が一番軽くて綺麗なのですが、
    そうすると自由な形にはしにくいという欠点があります。
    今回の手法では、小さな点が、バネで繋がっているモデルをしており、一部が欠けてもそれらしい動作をします。
    バネは回転方向への力も持ち、隣の点を、距離だけではなく正常な角度に保とうとします。
    欠点として、点の数が多くなるため圧倒的に重いことと
    力の伝わり方が遅いため、伸びやすい物体になってしまうことです。
    前者はリファクタリングしていく必要があります。
    後者は、今回解決のために点を2個先まで接続する手法をとりました。
    
    
    キング・カズマのドットバージョンを入れたかったんだけど
    16x32ドットは重くなりすぎて断念。
    軽量化して、そのくらいは動くようになりたい。
    */
    
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.GradientType;
    import flash.display.Graphics;
    import flash.display.Shape;
    import flash.display.Sprite;
    import flash.display.StageQuality;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.events.KeyboardEvent;
    import flash.geom.Matrix;
    import flash.geom.Point;
    import flash.geom.Rectangle;
    import flash.display.Loader;
    import flash.display.MovieClip;
    import flash.system.LoaderContext;
    import flash.net.URLRequest;
    import flash.utils.*;
    import flash.ui.Keyboard;
    import frocessing.color.ColorHSV;
    import com.bit101.components.PushButton;
    import net.hires.debug.Stats;

    public class PuyoDot3 extends Sprite
    {
        public static const STAGE_W:uint = 465;
        public static const STAGE_H:uint = 465;
        
        private static const _WALL_LEFT:Number = 0;
        private static const _WALL_RIGHT:Number = 465;
        private static const _GROUND_LINE:Number = 350;

        // パーティクル
        //private var _dotMap:DotMap;
        private var _particleList:Array = [];    //:Array :Particle
        private var _particleDistance:int;
        private var _w:int;
        private var _h:int;
        
        // ドラッグ
        private var _dragIdX:int = -1;
        private var _dragIdY:int = -1;
        
        // レイヤー
        private var _bgLayer:Bitmap;
        private var _displayLayer:Bitmap;
        private var _debugLayer:Sprite;
        private var _debugDisplayList:Array = [];
        private var _dragLayer:Sprite;
        private var _dragList:Array = [];
        
        // ビットマップ
        private var _clearBitmap:BitmapData = new BitmapData(STAGE_W, STAGE_H, true, 0x00000000);
        private var _displayBitmap:BitmapData = new BitmapData(STAGE_W, STAGE_H);
        private var _bgBitmap:BitmapData = new BitmapData(STAGE_W, STAGE_H);
        private var _gradiationBitmap:BitmapData = new BitmapData(STAGE_W, STAGE_H);
        private var _reflectAlphaBitmap:BitmapData = new BitmapData(STAGE_W, STAGE_H, true, 0x00000000);
        
        private var _rect:Rectangle = new Rectangle(0, 0, STAGE_W, STAGE_H);
        private var _point:Point = new Point();
        private var _refrectPoint:Point = new Point(0, -2*_GROUND_LINE + STAGE_H);
        
        private var blobPuyos:Vector.<BlobPuyo> = new Vector.<BlobPuyo>(0, false);
        
        //plastic surgery
        public var GRAPHIC_URL:String = "http://nullurban.appspot.com/loco3.png";
        private var loader      :Loader;
        private var bmpd        :BitmapData;
        private var mouth_bmpd  :BitmapData;
        private var mouth2_bmpd :BitmapData;
        private var eyes_bmpd   :BitmapData;
        private var hair_bmpd   :BitmapData;
        
        private var blobsMouth  :Vector.<Bitmap>     = new Vector.<Bitmap>(0, false);
        private var blobsFace   :Vector.<MovieClip>     = new Vector.<MovieClip>(0, false);
        private var blobsEyes   :Vector.<MovieClip>     = new Vector.<MovieClip>(0, false);
        
        private var vcolor:ColorHSV = new ColorHSV(0,0.6,1,0.5);
        private var fps:int = 2;
        
        // improvization
        private var GRID_WIDTH : int = 20;
        private var GRID_HEIGHT : int = 20;
        
        public function PuyoDot3()
        {
            addEventListener(Event.ADDED_TO_STAGE, initGRAPHIC);    // flexBuilderとの互換性。
        }
        private function initGRAPHIC(e:Event):void {    // ここから開始
            removeEventListener(Event.ADDED_TO_STAGE, initGRAPHIC);
            loader = new Loader();
            loader.contentLoaderInfo.addEventListener(Event.COMPLETE, init);
            loader.load(new URLRequest(GRAPHIC_URL), new LoaderContext(true));
        }
        
        private function init(e:Event):void
        {
            //prepare organs
            bmpd = Bitmap(loader.content).bitmapData;
            mouth_bmpd = new BitmapData(7, 7, true, 0x0);
            mouth_bmpd.copyPixels(bmpd, new Rectangle(17,0,7,7), new Point());
            mouth2_bmpd = new BitmapData(7, 7, true, 0x0);
            mouth2_bmpd.copyPixels(bmpd, new Rectangle(24,0,3,7), new Point(3,0));
            eyes_bmpd = new BitmapData(17, 7, true, 0x0);
            eyes_bmpd.copyPixels(bmpd, new Rectangle(0,0,17,7), new Point());
            hair_bmpd = new BitmapData(25, 13, true, 0x0);
            hair_bmpd.copyPixels(bmpd, new Rectangle(0,8,25,14), new Point());
            
            // SWF設定
            stage.frameRate = 60;
////v0.2
            stage.quality = StageQuality.HIGH;  //LOW
            var bg:Sprite = new Sprite();    // wonderflではキャプチャに背景色が反映されないので、背景色Spriteで覆う。
            bg.graphics.beginFill(0xaaaaaa);
            bg.graphics.drawRect(0, 0, STAGE_W, STAGE_H);
            addChild(bg);
            
            addChild(_bgLayer = new Bitmap(_bgBitmap));
            addChild(_displayLayer = new Bitmap(_displayBitmap));
            addChild(_debugLayer = new Sprite());
            addChild(_dragLayer = new Sprite());
            _debugLayer.visible = false;
            _bgLayer.scaleY = -1;
            _bgLayer.y = STAGE_H;

            
            // spawn 3 blobs
            for(var I:int = 0; I < 3; I++) addBlob(I);

            // デバッグ表示
            //debugInit();
            
            displayInit();
            
            var panel:Sprite = new Sprite();
            addChild(panel);
            new PushButton(panel, stage.stageWidth-85, 5, "add blob", addMoreBlob).setSize(80, 16);
            
            // フレームの処理を登録
            addEventListener(Event.ENTER_FRAME, frame);
            // マウスドラッグ
            //stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpEvent());
////v0.2
            stage.addEventListener(MouseEvent.MOUSE_DOWN, onmouseDown);
            stage.addEventListener(MouseEvent.MOUSE_MOVE, onmouseMove);
            stage.addEventListener(MouseEvent.MOUSE_UP  , onmouseUp  );
            stage.addEventListener(KeyboardEvent.KEY_DOWN, onkeyDown);
            addChild(new Stats());
        }
        
        private function onkeyDown(e:KeyboardEvent):void
        {
            if(e.keyCode==Keyboard.UP)
            {
                if(++fps>3) fps = 1;
                stage.frameRate = fps * 30;
            }
        }
        
        private function addMoreBlob(...arg):void
        {
            addBlob(blobPuyos.length);
        }
        
        private function addBlob(id:int):void
        {
            vcolor.h = Math.random()*100;
            var plastic_organs:Bitmap;
            blobsFace.push(new MovieClip());
            addChild(blobsFace[id]);
            
            // plastic surgery
            plastic_organs = new Bitmap(hair_bmpd.clone());
            transformColor(plastic_organs.bitmapData, vcolor.value);
            plastic_organs.x = -hair_bmpd.width/2; plastic_organs.y = -hair_bmpd.height*0.9;
            blobsFace[id].addChild(plastic_organs);
            //eye
            blobsEyes.push(new MovieClip());
            plastic_organs = new Bitmap(eyes_bmpd);
            blobsEyes[id].addChild(plastic_organs);
            plastic_organs.y = -eyes_bmpd.height/2;
            blobsEyes[id].x = -eyes_bmpd.width/2; blobsEyes[id].y = 3+eyes_bmpd.height/2;
            blobsFace[id].addChild(blobsEyes[id]);
            //mouth
            plastic_organs = new Bitmap(mouth_bmpd);
            plastic_organs.x = -mouth_bmpd.width/2; plastic_organs.y = eyes_bmpd.height+3;
            blobsFace[id].addChild(plastic_organs);
            blobsMouth.push(plastic_organs);
            
            
            blobPuyos.push(new BlobPuyo(id, vcolor.value, 50+Math.random()*365, 50, 30 + Math.random()*5));
            blobPuyos[id].blink = Math.random() * 3000;
        }
        
        private function transformColor(bmd:BitmapData, c:uint):void
        {
            var i:int = 0;
            
            var to_R:uint = (c & 0xFF0000);
            var to_G:uint = (c & 0x00FF00);
            var to_B:uint = (c & 0x0000FF);
            var paletteR:Array = [];
            var paletteG:Array = [];
            var paletteB:Array = [];
            var r:Number;
            
            for (i = 0; i < 256; i++) {
                paletteR[i] = (to_R);
                paletteG[i] = (to_G);
                paletteB[i] = (to_B);
            }
            
            bmd.paletteMap(hair_bmpd, hair_bmpd.rect, hair_bmpd.rect.topLeft, paletteR, paletteG, paletteB);
        }
        
        // フレーム挙動
        private function frame(event:Event):void{
            var i:int = 0;
            var j:int = 0;
            var d:int = 0;
            //for (var d:int=0; d<GB._DERIVATION; d++){
            for (d=0; d<1; d++){
                //calculate diff
                for(i = 0; i<blobPuyos.length; i++)
                    blobPuyos[i].setup();
                
                //check contacts
                for(i = 0; i<blobPuyos.length; i++)
                    for(j = 0; j <blobPuyos.length; j++)
                        if(i != j)
                            blobPuyos[i].contact(blobPuyos[j]);
                /**/
                //response
                for(i = 0; i<blobPuyos.length; i++)
                    blobPuyos[i].update();
            }
            
            draw();    // 描画処理
        }
        
        private var _drawShape:Shape = new Shape();
        
        private function displayInit():void{
            var g:Graphics = _drawShape.graphics;
            g.clear();
            var matrix:Matrix = new Matrix();
            matrix.createGradientBox(STAGE_W, STAGE_H, Math.PI / 2, 0, 0);
            g.beginGradientFill(GradientType.LINEAR, [0xa3a3a3, 0x676767], [1, 1], [0, 255], matrix);
            g.drawRect(0, 0, STAGE_W, STAGE_H);
            _gradiationBitmap.draw(_drawShape);
            
            g.clear();
            g.beginGradientFill(GradientType.LINEAR, [0x000000, 0x000000], [0, 0.7], [125, 230], matrix);
            g.drawRect(0, 0, STAGE_W, STAGE_H);
            _reflectAlphaBitmap.draw(_drawShape);
        }
        private function draw():void{
            var g:Graphics = _drawShape.graphics;
            var particle:Particle;
            g.clear();
            var I:int = 0;
            var J:int = 0;
            var K:int = 0;
            var BVI:Particle;
            var BPI:Point;
            for(I = 0; I < blobPuyos.length; I++)
            {
                BVI = blobPuyos[I]._VERTS[blobPuyos[I].head];
                BPI = BVI.pv;
                blobsFace[I].x = BVI.x;
                blobsFace[I].y = BVI.y;
                blobsFace[I].rotation = Math.atan2(BPI.y, BPI.x)/(Math.PI/180) - 90;
                if(blobPuyos[I]._DRAG_ID!=-1)
                    blobsMouth[I].bitmapData = mouth2_bmpd;
                else 
                    blobsMouth[I].bitmapData = mouth_bmpd;
                
                if(int((blobPuyos[I].blink+getTimer())%3000)<200) blobsEyes[I].scaleY = ((blobPuyos[I].blink+getTimer())%200)/200;
                
                    
                g.beginFill(blobPuyos[I].color);
                g.moveTo(blobPuyos[I]._VERTS[0].mid.x, blobPuyos[I]._VERTS[0].mid.y);
                for(J = 0;J < blobPuyos[I]._VERTS.length; J++)
                {
                    K = (J+1) % blobPuyos[I]._VERTS.length;
                    g.curveTo(blobPuyos[I]._VERTS[K].x, blobPuyos[I]._VERTS[K].y, blobPuyos[I]._VERTS[K].mid.x, blobPuyos[I]._VERTS[K].mid.y);
                }
                
                
                g.endFill();
            }
            
            _displayBitmap.copyPixels(_clearBitmap, _rect, _point);
            _displayBitmap.draw(_drawShape);
            
            _bgBitmap.copyPixels(_gradiationBitmap, _rect, _point);
            _bgBitmap.copyPixels(_displayBitmap, _rect, _refrectPoint, _reflectAlphaBitmap, _point, true);
        }
        
////v0.2
        private function onmouseDown(e:MouseEvent):void
        {
            var I:int = 0;
            for(I = 0; I < blobPuyos.length; I++)
            {
                blobPuyos[I].detectDrag(mouseX, mouseY);
            }
        }
        
        private function onmouseMove(e:MouseEvent):void
        {
            var I:int = 0;
            for(I = 0; I < blobPuyos.length; I++)
            {
                blobPuyos[I].moveDrag(mouseX, mouseY);
            }
        }
        
        private function onmouseUp(e:MouseEvent):void
        {
            var I:int = 0;
            for(I = 0; I < blobPuyos.length; I++)
            {
                blobPuyos[I].cancelDrag();
            }
        }
    }
}

class BlobPuyo {
    public var ID:int = 0;

    //EXXXXXTRA!!!!
    private var _OLD_PARTICLE_AREA  :Number            = 0;
    
    private var VERT_COUNT          :int               = 16;
    public var RADIUS               :Number            = 30;
    public var _VERTS              :Vector.<Particle> = new Vector.<Particle>(0, false);
    public var _MIDS               :Vector.<Particle> = new Vector.<Particle>(0, false);
    public var _DRAG_ID            :int               = -1;
    public var CENT_PARTICLE       :Particle          = new Particle();  //SPECIAL, center of blob
    
    public var d                   :Vector.<Particle> = new Vector.<Particle>(0, false);
    public var disp                :Vector.<Particle> = new Vector.<Particle>(0, false);
    public var cdiv                :int               = 0;
    public var fdiv                :Vector.<int>      = new Vector.<int>(0, false);
    public var pvn                 :Vector.<Particle> = new Vector.<Particle>(0, false);
    public var sd                  :Number            = 0.3;
    
    private var mouse_x:Number = 0;
    private var mouse_y:Number = 0;
    
    public var blink:Number = 0;
    
    public var color:uint = 0;
    
    public var head:int = 0;
    
    // improvization
    public var EDGES : Vector.<Edge> = new Vector.<Edge>(0, false);
    private var AREA:Number = 0;
    public var FIRST_EDGE:Edge;
    public var FIRST_PARTICLE:Particle;

    public function BlobPuyo(id:int, c:uint, px:Number, py:Number, ra:Number = 30, v:int = 16) {
        ID = id;
        color = c;
        VERT_COUNT = v;
        CENT_PARTICLE.x = px;
        CENT_PARTICLE.y = py;
        RADIUS = ra;
        // constructor code
        var D_RAD:Number = GB._RADIAN360 / VERT_COUNT;
        var I:int = 0;
        var J:int = 0;
        var K:int = 0;
        var VX:Number = 0;
        var VY:Number = 0;
        var TRADIUS:Number = 0;
        var CRADIUS:Number = 0;
        
        switch(int(Math.random()*5))
        {
            case 0:
                //ピーナッツ
                head = 4;
                for(I = 0; I < VERT_COUNT; I++)
                {
                    CRADIUS = Math.abs(Math.abs(I*D_RAD)%(Math.PI)-(Math.PI/2));
                    TRADIUS = RADIUS*1/2 + (RADIUS*1/2 * (CRADIUS)/(Math.PI/2));
                    VX = CENT_PARTICLE.x + Math.cos(I*D_RAD) * TRADIUS;
                    VY = CENT_PARTICLE.y + Math.sin(I*D_RAD) * TRADIUS;
                    _VERTS.push(new Particle(VX, VY, I));
                }
                break;
            case 1:
                //三角
                head = 5;
                for(I = 0; I < VERT_COUNT; I++)
                {
                    CRADIUS = Math.abs(Math.abs(I*D_RAD)%(Math.PI*2/3)-(Math.PI/3));
                    TRADIUS = RADIUS*3/4 + (RADIUS*1/4 * (CRADIUS)/(Math.PI/3));
                    VX = CENT_PARTICLE.x + Math.cos(I*D_RAD) * TRADIUS;
                    VY = CENT_PARTICLE.y + Math.sin(I*D_RAD) * TRADIUS;
                    _VERTS.push(new Particle(VX, VY, I));
                }
                break;
            case 2:
                //方
                head = 4;
                for(I = 0; I < VERT_COUNT; I++)
                {
                    CRADIUS = Math.abs(Math.abs(I*D_RAD)%(Math.PI/2)-(Math.PI/4));
                    TRADIUS = RADIUS*3/4 + (RADIUS/4 * (Math.PI/4-CRADIUS)/(Math.PI/4));
                    VX = CENT_PARTICLE.x + Math.cos(I*D_RAD+Math.PI/4) * TRADIUS;
                    VY = CENT_PARTICLE.y + Math.sin(I*D_RAD+Math.PI/4) * TRADIUS;
                    _VERTS.push(new Particle(VX, VY, I));
                }
                break;
            case 3:
                //円
                head = 0;
                for(I = 0; I < VERT_COUNT; I++)
                {
                    VX = CENT_PARTICLE.x + Math.cos(I*D_RAD) * RADIUS;
                    VY = CENT_PARTICLE.y + Math.sin(I*D_RAD) * RADIUS;
                    _VERTS.push(new Particle(VX, VY, I));
                }
                break;
            case 4:
                //星
                head = 0;
                for(I = 0; I < VERT_COUNT; I++)
                {
                    CRADIUS = Math.abs(Math.abs(I*D_RAD)%(Math.PI*2/5)-(Math.PI/5));
                    TRADIUS = RADIUS*1/2 + (RADIUS*1/2 * (CRADIUS)/(Math.PI/5));
                    VX = CENT_PARTICLE.x + Math.cos(I*D_RAD) * TRADIUS;
                    VY = CENT_PARTICLE.y + Math.sin(I*D_RAD) * TRADIUS;
                    _VERTS.push(new Particle(VX, VY, I));
                }
                break;
        }
        _VERTS.fixed = true;
        var NEXT_PARTICLE:Particle;
        var PREV_PARTICLE:Particle;
        var CURR_PARTICLE:Particle;
        var TARR:Array;
        for(I = 0; I < VERT_COUNT; I++)
        {
            J = (VERT_COUNT+I-1)%VERT_COUNT;  //PREV
            K = (I+1)%VERT_COUNT;
            CURR_PARTICLE = _VERTS[I];
            PREV_PARTICLE = _VERTS[J];
            NEXT_PARTICLE = _VERTS[K];
            CURR_PARTICLE.next = NEXT_PARTICLE;
            CURR_PARTICLE.prev = PREV_PARTICLE;
            CURR_PARTICLE.upRADIAN   = calcRADIAN  (CENT_PARTICLE, CURR_PARTICLE);
            CURR_PARTICLE.downRADIAN = calcRADIAN  (CURR_PARTICLE, CENT_PARTICLE);
            CURR_PARTICLE.nextRADIAN = calcRADIAN  (CURR_PARTICLE, NEXT_PARTICLE);
            CURR_PARTICLE.prevRADIAN = calcRADIAN  (CURR_PARTICLE, PREV_PARTICLE);
            CURR_PARTICLE.hDISTANCE  = calcDISTANCE(CURR_PARTICLE, NEXT_PARTICLE);
            CURR_PARTICLE.vDISTANCE  = calcDISTANCE(CURR_PARTICLE, CENT_PARTICLE);
            TARR = [CURR_PARTICLE, NEXT_PARTICLE, CENT_PARTICLE];
            CURR_PARTICLE.AREA = calcAREA(TARR);  //FOR GAS
            EDGES.push(new Edge(CURR_PARTICLE, NEXT_PARTICLE, ID));
        }
        for(I = 0; I < VERT_COUNT-1; I++) EDGES[I].next = EDGES[I+1];
        FIRST_EDGE = EDGES[0];
        FIRST_PARTICLE = _VERTS[0];
        AREA = calcAreaB();
    }
    
    private function   calcRADIAN(PA:*, PB:*):Number { return Math.atan2(PB.y - PA.y, PB.x - PA.x); }
    private function calcDISTANCE(PA:*, PB:*):Number 
    {
        var VX:Number = PB.x - PA.x;
        var VY:Number = PB.y - PA.y;
        return Math.sqrt(VX*VX+VY*VY);
    }
    
    private function calcMID():void
    {
        for ( var e:Edge = FIRST_EDGE; e != null; e = e.next)
        {
            e.A.mid.x = (e.A.x+e.B.x)/2;
            e.A.mid.y = (e.A.y+e.B.y)/2;
        }
    }
    
    private function calcAREA(arr:Array):Number
    {
        var a:Number=0;
        var n1:*;
        var n2:*;
        for (var i:int=0; i<arr.length; i++)
        {
            n1 = arr[i];
            n2 = arr[(i+1)%arr.length];
            a += (n1.x-n2.x)*(n1.y+n2.y);
        }
        return a/2;
    }
    
    private function calcAreaB():Number
    {
        var a:Number = 0;
        for ( var e:Edge = FIRST_EDGE; e != null; e = e.next) a += (e.A.x-e.B.x)*(e.A.y+e.B.y);
        return a/2;
    }
    
    // ボーンの向きを決定する
    private function rotate():void{
        var CURR_PARTICLE:Particle;
        var NEXT_PARTICLE:Particle;
        for ( var e:Edge = FIRST_EDGE; e != null; e = e.next)
        {
            CURR_PARTICLE = e.A;
            NEXT_PARTICLE = e.B;
            calcConnectRForce(CURR_PARTICLE, NEXT_PARTICLE, CURR_PARTICLE.nextRADIAN);
            calcConnectRForce(NEXT_PARTICLE, CURR_PARTICLE, NEXT_PARTICLE.prevRADIAN);
            calcConnectRForce(CURR_PARTICLE, CENT_PARTICLE, CURR_PARTICLE.downRADIAN);
            calcConnectRForce(CENT_PARTICLE, CURR_PARTICLE, CURR_PARTICLE.upRADIAN  );
            if (_DRAG_ID == CURR_PARTICLE.ID)
                CURR_PARTICLE.vr *= GB._MOUSE_ROTATE_FRICTION;
            else
                CURR_PARTICLE.vr *= GB._ROTATE_FRICTION;    // 摩擦
            CURR_PARTICLE.radian += CURR_PARTICLE.vr;
        }
        CENT_PARTICLE.vr *= GB._ROTATE_FRICTION;
        CENT_PARTICLE.radian += CENT_PARTICLE.vr;
    }
    // 接続されたパーツの回転方向を計算する
    private function calcConnectRForce(particle:Particle, targetParticle:Particle, connectAngle:Number):void{
        var angle:Number = Math.atan2(targetParticle.y - particle.y, targetParticle.x - particle.x);
        particle.vr += ajustRadian(angle - (connectAngle + particle.radian)) * GB._ROTATION_RATE;
    }

    private function force():void{
        var CURR_PARTICLE:Particle;
        var NEXT_PARTICLE:Particle;
        var FCOMP:Number = 0;
        var AREA_DIFF:Number = 0;
        var GAS_PRESSURE:Number = 0;
        var NEW_AREA:Number = calcAreaB();
        for ( var e:Edge = FIRST_EDGE; e != null; e = e.next)
        {
            CURR_PARTICLE = e.A;
            NEXT_PARTICLE = e.B;
            AREA_DIFF = NEW_AREA - AREA;
            FCOMP = (AREA_DIFF>1000 || AREA_DIFF<-1000)?0.00333:GB._COMP;
            GAS_PRESSURE = (AREA_DIFF)*FCOMP/_VERTS.length;
            CURR_PARTICLE.updateNormal();
            CURR_PARTICLE.v.x += CURR_PARTICLE.pv.x*GAS_PRESSURE;
            CURR_PARTICLE.v.y += CURR_PARTICLE.pv.y*GAS_PRESSURE;
            calcConnectFoce(CURR_PARTICLE, NEXT_PARTICLE, CURR_PARTICLE.nextRADIAN, CURR_PARTICLE.hDISTANCE);
            calcConnectFoce(NEXT_PARTICLE, CURR_PARTICLE, NEXT_PARTICLE.prevRADIAN, CURR_PARTICLE.hDISTANCE);
            calcConnectFoce(CURR_PARTICLE, CENT_PARTICLE, CURR_PARTICLE.downRADIAN, CURR_PARTICLE.vDISTANCE, true, false);
            calcConnectFoce(CENT_PARTICLE, CURR_PARTICLE, CURR_PARTICLE.upRADIAN  , CURR_PARTICLE.vDISTANCE, false);
            
            CURR_PARTICLE.a.y += GB._GRAVITY;
            if (_DRAG_ID == e.A.ID){    // マウスで引っ張る
                var point:Particle = pullForce(CURR_PARTICLE.x, CURR_PARTICLE.y, mouse_x, mouse_y, GB._MOUSE_PULL_RATE);
                CURR_PARTICLE.a.x += point.x;
                CURR_PARTICLE.a.y += point.y;
                CURR_PARTICLE.v.x *= GB._MOUSE_MOVE_FRICTION;
                CURR_PARTICLE.v.y *= GB._MOUSE_MOVE_FRICTION;
            }
        }
        CENT_PARTICLE.a.y += GB._GRAVITY;
    }
    // 接続された2パーツの力を計算する
    private function calcConnectFoce(particle:Particle, targetParticle:Particle, connectAngle:Number, distance:Number, cleanA:Boolean = true, cleanB:Boolean = true):void{
        var toAngle:Number = ajustRadian(connectAngle + particle.radian);
        var toX:Number = particle.x + Math.cos(toAngle) * distance;
        var toY:Number = particle.y + Math.sin(toAngle) * distance;
        var ax:Number = (targetParticle.x - toX) * GB._VERTICAL_RATE;
        var ay:Number = (targetParticle.y - toY) * GB._VERTICAL_RATE;
        particle.a.x += ax;
        particle.a.y += ay;
        targetParticle.a.x -= ax;
        targetParticle.a.y -= ay;
        
        if(cleanA) particle.fdiv++;
        if(cleanB) targetParticle.fdiv++;
    }
    
    // radian角度を、-π~πの範囲に修正する
    private function ajustRadian(radian:Number):Number{
        return radian - GB._PI2 * Math.floor( 0.5 + radian / GB._PI2);
    }
    // ポイントx1 y1を、ポイントx2 y2へ、係数rateだけ移動させる場合の、XYの力を返す
    private function pullForce(x1:Number, y1:Number, x2:Number, y2:Number, rate:Number):Particle{
        var point:Particle = new Particle();
        var distance:Number = calcDistance(x1, y1, x2, y2);
        
        var angle:Number = Math.atan2(y2 - y1, x2 - x1);
        point.x = Math.cos(angle) * distance * rate;
        point.y = Math.sin(angle) * distance * rate;
        return point;
    }
    // ポイントx1 y1から、ポイントx2 y2までの距離
    private function calcDistance(x1:Number, y1:Number, x2:Number, y2:Number):Number{
        return Math.sqrt(Math.pow(x2-x1, 2) + Math.pow(y2-y1, 2));
    }
    
    private function updatePARTICLE(CURR_PARTICLE:Particle):void
    {
        CURR_PARTICLE.a.x += -GB._FRICTION * CURR_PARTICLE.v.x;
        CURR_PARTICLE.a.y += -GB._FRICTION * CURR_PARTICLE.v.y;
        
        // 速度、位置への反映
        CURR_PARTICLE.v.x += CURR_PARTICLE.a.x;
        CURR_PARTICLE.v.y += CURR_PARTICLE.a.y;
        CURR_PARTICLE.x += CURR_PARTICLE.v.x;
        CURR_PARTICLE.y += CURR_PARTICLE.v.y;
        CURR_PARTICLE.a.x = 0;
        CURR_PARTICLE.a.y = 0;    // 力をクリア
        
        // 壁チェック
        if (0 < CURR_PARTICLE.v.y && GB._GROUND_LINE < CURR_PARTICLE.y){
            CURR_PARTICLE.y = GB._GROUND_LINE;
            CURR_PARTICLE.v.y *= -0.8;
            if (CURR_PARTICLE.v.y < -50) CURR_PARTICLE.v.y = -50;
            CURR_PARTICLE.v.x *= GB._GROUND_FRICTION;
        }
        if (CURR_PARTICLE.v.x < 0 && CURR_PARTICLE.x < GB._WALL_LEFT){
            CURR_PARTICLE.x = GB._WALL_LEFT;
            CURR_PARTICLE.v.x = 0;
            CURR_PARTICLE.v.y *= GB._GROUND_FRICTION;
        }else if (0 < CURR_PARTICLE.v.x && GB._WALL_RIGHT < CURR_PARTICLE.x){
            CURR_PARTICLE.x = GB._WALL_RIGHT;
            CURR_PARTICLE.v.x = 0;
            CURR_PARTICLE.v.y *= GB._GROUND_FRICTION;
        }
    }
    
    private function move():void
    {
        for ( var e:Edge = FIRST_EDGE; e != null; e = e.next) updatePARTICLE(e.A);
        updatePARTICLE(CENT_PARTICLE);
    }
    
    public function detectDrag(m_x:Number, m_y:Number):void
    {
        var MAX_DISTANCE:Number = 9999;
        var VX:Number = 0;
        var VY:Number = 0;
        var DIST:Number = 0;
        for ( var e:Edge = FIRST_EDGE; e != null; e = e.next)
        {
            VX = m_x - e.A.x;
            VY = m_y - e.A.y;
            DIST = Math.sqrt(VX*VX+VY*VY);
            if(DIST<MAX_DISTANCE && DIST<15)
            {
                MAX_DISTANCE = DIST;
                _DRAG_ID = e.A.ID;
            }
        }
    }
    
    public function moveDrag(m_x:Number, m_y:Number):void
    {
        mouse_x = m_x; mouse_y = m_y;
    }
    
    public function cancelDrag():void
    {
        _DRAG_ID = -1;
    }
    
    public function setup():void
    {
        var vx   :Number = 0;
        var vy   :Number = 0;
        var len  :Number = 0;
        var diff :Number = 0;
        var nx   :Number = 0;
        var ny   :Number = 0;
        var ux   :Number = 0;
        var uy   :Number = 0;
        var VI   :Particle;
        var VK   :Particle;
        for ( var e:Edge = FIRST_EDGE; e != null; e = e.next)
        {
            VI = e.A;
            VK = e.B;
            vx = e.vx;
            vy = e.vy;
            len = Math.sqrt(vx * vx + vy * vy);
            diff = VI.nextRADIAN - len;
            nx = vx / len;
            ny = vy / len;
            ux = diff * nx;
            uy = diff * ny;
            VI.d.x = nx;
            VI.d.y = ny;
        }
    }
    
    public function update():void
    {
        response();
        rotate();    // 回転の計算
        force();    // 力の計算
        move();    // 移動処理
        calcMID();
    }
    
    private function response():void
    {
        var gx:Number =0;
        var gy:Number = 0;
        var xlen:Number = 0;
        var VI:Particle;
        var DI:Point;
        for ( var e:Edge = FIRST_EDGE; e != null; e = e.next)
        {
            VI = e.A;
            if(VI.fdiv > 0)
            {
                DI = VI.disp;
                gx = DI.x / VI.fdiv;
                gy = DI.y / VI.fdiv;
                VI.x = VI.x + gx;
                VI.y = VI.y + gy;
                VI.v.x += gx;
                VI.v.y += gy;
                DI.x = 0.0;
                DI.y = 0.0;
                VI.fdiv = 0;
            }
        }
    }
    
    public function inPoly(px:Number, py:Number):Boolean
    {
        var c:Boolean = false;
        var VI:Particle;
        var VJ:Particle;
        for ( var e:Edge = FIRST_EDGE; e != null; e = e.next)
        {
            VI = e.A; VJ = e.A.prev;
            if ( ((VI.y>py) != (VJ.y>py)) && (px < (VJ.x-VI.x) * (py-VI.y) / (VJ.y-VI.y) + VI.x) )
                c = !c;
        }
        return c;
    }
    
    public function notTooClose(b:BlobPuyo):Boolean
    {
        return (calcDISTANCE(CENT_PARTICLE, b.CENT_PARTICLE)>RADIUS+b.RADIUS);
    }
    
    public function intersected(p1:*, p2:*, p3:*, p4:*):Boolean
    {
        var s1_x:Number, s1_y:Number, s2_x:Number, s2_y:Number;
        s1_x = p2.x - p1.x;     s1_y = p2.y - p1.y;
        s2_x = p4.x - p3.x;     s2_y = p4.y - p3.y;
    
        var s:Number, t:Number;
        s = (-s1_y * (p1.x - p3.x) + s1_x * (p1.y - p3.y)) / (-s2_x * s1_y + s1_x * s2_y);
        t = ( s2_x * (p1.y - p3.y) - s2_y * (p1.x - p3.x)) / (-s2_x * s1_y + s1_x * s2_y);
    
        if (s >= 0 && s <= 1 && t >= 0 && t <= 1) return true;
        return false;
    }
    
    public function contact(blob:BlobPuyo):void
    {
        if(notTooClose(blob)) return; 
        var gx   :Number = 0;
        var gy   :Number = 0;
        var cx   :Number = 0;
        var cy   :Number = 0;
        var dp   :Number = 0;
        var f1   :Number = 0;
        var f2   :Number = 0;
        var vx   :Number = 0;
        var vy   :Number = 0;
        var len  :Number = 0;
        var diff :Number = 0;

        var bvx:Number = 0;
        var bvy:Number = 0;
        var blen:Number = 0;
        var pavn:Particle = new Particle(0, 0);
        
        var VI:Particle;
        var VJ:Particle;
        var VK:Particle;
        var VO:Particle;
        var BV:Particle;
        var PV:Point;
        var DI:Point;
        var BD:Point;
        var BDO:Point;
        var BDK:Point;
        for ( var e:Edge = FIRST_EDGE; e != null; e = e.next)
        {
            VI = e.A;
            if(blob.inPoly(VI.x, VI.y))
            {
                PV = VI.pv;
                DI = VI.disp;
                for ( var be:Edge = blob.FIRST_EDGE; be != null; be = be.next)
                {
                    VO = be.A;
                    VK = be.B;

                    pavn.x = VI.x + PV.x * RADIUS;
                    pavn.y = VI.y + PV.y * RADIUS;

                    if (intersected(VI, pavn, VK, VO)) {
                        BD = VO.d;
                        BDO = VO.disp;
                        BDK = VK.disp;

                        vx = VI.x - VO.x;
                        vy = VI.y - VO.y;
                        
                        dp = vx * BD.x + vy * BD.y;
                        
                        bvx = be.vx;
                        bvy = be.vy;
                        
                        blen = Math.sqrt(bvx*bvx+bvy*bvy);

                        cx = VO.x + dp * BD.x;
                        cy = VO.y + dp * BD.y;
                        
                        vx = VI.x - cx;
                        vy = VI.y - cy;
                        
                        diff = 4 /4;
                        gx = diff * vx;
                        gy = diff * vy;

                        DI.x -= gx;
                        DI.y -= gy;
                        VI.fdiv++;

                        f1 = dp / blen;
                        f2 = 1.0 - f1;
                        
                        BDO.x += f2 * gx;
                        BDO.y += f2 * gy;
                        VO.fdiv++;

                        BDK.x += f1 * gx;
                        BDK.y += f1 * gy;
                        VK.fdiv++;
                        
                        VI.v.x = 0;
                        VI.v.y = 0;
                        break;
                    }
                }
            }
        }
    }
}

import flash.geom.Point;

class Particle
{
    public var ID:int = 0;
    public var x:Number = 0;    // 位置
    public var y:Number = 0;
    public var v:Point = new Point();    // 速度
    public var a:Point = new Point();    // 加速度=力    TOTO:最後まで意味無かったら消す
    public var pv:Point = new Point();
    public var d:Point = new Point();
    public var mid:Point = new Point();
    public var disp:Point = new Point();
    public var o:Point = new Point();
    public var fdiv:int = 0;
    
    public var radian:Number = 0;    // 向き
    public var vr:Number = 0;    // 向き速度

    public var   upRADIAN :Number = 0;
    public var downRADIAN :Number = 0;
    public var prevRADIAN :Number = 0;
    public var nextRADIAN :Number = 0;
    public var       AREA :Number = 0;
    public var  vDISTANCE :Number = 0;
    public var  hDISTANCE :Number = 0;
    
    public var next :Particle;
    public var prev :Particle;
    
    public function Particle(px:Number = 0, py:Number = 0, id:int = 0) { x = px; y = py; ID = id;}
    public function updateNormal():void
    {
        var dpx:Number=prev.x-x;
        var dpy:Number=prev.y-y;
        var dnx:Number=next.x-x;
        var dny:Number=next.y-y;

        var nx:Number=-dny;
        var ny:Number=dnx;
        var na:Number=Math.sqrt(nx*nx+ny*ny);
        if (na==0)
        {
            nx=1; ny=0;
        } else {
            nx/=na; ny/=na;
        }

        //rotate dp anti-clockwise by 90C
        var px:Number=dpy;
        var py:Number=-dpx;
        var pa:Number=Math.sqrt(px*px+py*py);
        if (pa==0)
        {
            px=1; py=0;
        } else {
            px/=pa; py/=pa;
        }

        var fx:Number=nx+px;
        var fy:Number=ny+py;
        var fa:Number=Math.sqrt(fx*fx+fy*fy);
        if (fa==0)
        {
            fx=1; fy=0;
        } else {
            fx/=fa; fy/=fa;
        }

        pv.x = fx;
        pv.y = fy;
    }
}

class GB {
    public static const _WALL_LEFT             :Number = 0;
    public static const _WALL_RIGHT            :Number = 465;
    public static const _GROUND_LINE           :Number = 350;
    
    public static const _DOT_CONNECT_MAX       :int    = 4;
    public static const _DERIVATION            :int    = 3;    // 計算の分割数。  //3
    public static const _MAP_SIZE              :Number = 400;
    
    public static const _PI                    :Number = Math.PI;
    public static const _PI2                   :Number = 2.0 * _PI;
    public static const _RADIAN90              :Number = _PI * 0.5;
    public static const _RADIAN180             :Number = _PI * 1.0;
    public static const _RADIAN270             :Number = _PI * -0.5;
////v0.2
    public static const _RADIAN360             :Number = _PI * 2;
    public static const _TO_DEGREE             :Number = 180 / _PI;
    
    //public static const _COMP                  :Number = 0.0133;  //0.1
    public static const _COMP                  :Number = 0.166;  //0.1
    public static const _GRAVITY               :Number = 0.6 / _DERIVATION;  //0.3  //0.6
    public static const _ROTATION_RATE         :Number = 0.05 / _DERIVATION;    // 自身バネ(根元) //0.05
    public static const _VERTICAL_RATE         :Number = 0.166 / _DERIVATION;    // ターゲットバネ(さきっぽ) //0.2  //0.133
    public static const _MOUSE_PULL_RATE       :Number = 2.0 / _DERIVATION;
    
    public static const _FRICTION              :Number = 0.1 / _DERIVATION;
    public static const _ROTATE_FRICTION       :Number = 1 - 0.2 / _DERIVATION;
    public static const _MOUSE_ROTATE_FRICTION :Number = 1 - 0.8 / _DERIVATION;
    public static const _MOUSE_MOVE_FRICTION   :Number = 1 - 0.5 / _DERIVATION;
    public static const _GROUND_FRICTION       :Number = 1 - 0.5 / _DERIVATION;  // 1 - 0.2  // 1 - 0.5
    
    public function GB() {}
}
// improvization
class Edge
{
    public var ID:String = "";
    public var A:Particle; // B.index > A.index
    public var B:Particle;
    public var next:Edge;
    private var v:Point = new Point();
    private var vlen:Number = 0;
    public function Edge(a:Particle, b:Particle, bid:int = 0) {A=a; B=b; ID=""+bid+"-"+a.ID+"-"+b.ID;}
    public function update():void { v.x = B.x - A.x; v.y = B.y - A.y; }
    //public function get vx():Number { return v.x; }
    //public function get vy():Number { return v.y; }
    public function get vx():Number { return B.x - A.x; }
    public function get vy():Number { return B.y - A.y; }
    public function get length():Number { return Math.sqrt(v.x*v.x+v.y*v.y); }
}
import flash.utils.Dictionary;
class Hash extends Dictionary
{
    private var weakKey:Boolean;
    public function Hash(weak:Boolean = false) { weakKey = weak; super(weak); }
    public function push(inst:*, prop:*):void { this[inst] = prop; }
    public function destroy(inst:*):void { delete this[inst]; }
    public function flush():void { for (var item:* in this) { delete this[item]; } }
    public function concat(h:Hash):void { for (var item:* in h) { this[item] = h[item]; } }
    public function hasInst(inst:*):Boolean { if (this[inst]) { return true; } return false; }
    public function empty():Boolean { for (var item:* in this) { return false; } return true; }
    public function hasProp(inst:*):Boolean { for (var item:* in this) { if (this[item] === inst) return true; } return false; }
    public function get size():int { var count:int; for (var item:* in this) { count++; } return count; }

    public function clone():Hash
    {
        var newHash:* = new Hash(weakKey);
        for (var item:* in this) { newHash[item] = this[item]; }
        return newHash;
    }

    public function toArray():Array
    {
        var newArray:Array;
        for (var item:* in this) { newArray.push(this[item]); }
        return newArray;
    }
}
class GridMap
{
    private var MAP:Vector.<Vector.<Edge>>;
    private var WIDTH:int = 0;
    private var HEIGHT:int = 0;
    public function GridMap(w:int,h:int)
    {
        WIDTH = w; HEIGHT = h;
        MAP = new Vector.<Vector.<Edge>>(w*h, true);
        for(var i:* in MAP) MAP[i] = new Vector.<Edge>(0, false);
    }
    public function push(e:Edge, at:int):void { MAP[at].push(e); }
    public function pushXY(e:Edge, x:int, y:int):void { MAP[y*WIDTH+x].push(e); }
    public function get(at:int):Vector.<Edge> { return MAP[at]; }
    public function getXY(x:int, y:int):Vector.<Edge> { return MAP[y*WIDTH+x]; }
    public function flush():void { for each(var ve:Vector.<Edge> in MAP) ve.splice(0, ve.length); }
    public function linePush(e:Edge, from:int, to:int):void
    {
        lineFast(e, from%WIDTH, int(from/HEIGHT), to%WIDTH, int(to/HEIGHT));
    }
    public function linePushXY(e:Edge, fromX:int, fromY:int, toX:int, toY:int):void
    {
        lineFast(e, fromX, fromY, toX, toY);
    }
    public function lineFast(e:Edge, x0:int, y0:int, x1:int, y1:int):void
    {    
        var dy       :int = y1 - y0;
        var dx       :int = x1 - x0;
        var stepx    :int;
        var stepy    :int;
        var fraction :int;
        
        if (dy < 0) { dy = -dy;  stepy = -1; } else { stepy = 1; }
        if (dx < 0) { dx = -dx;  stepx = -1; } else { stepx = 1; }
        dy <<= 1;
        dx <<= 1;
        MAP[y0 * WIDTH + x0].push(e);
        if (dx > dy)
        {
            fraction = dy - (dx >> 1);
            while (x0 != x1)
            {
                if (fraction >= 0)
                {
                    y0 += stepy;
                    fraction -= dx;
                }
                x0 += stepx;
                fraction += dy;
                MAP[y0 * WIDTH + x0].push(e);
            }
        } else {
            fraction = dx - (dy >> 1);
            while (y0 != y1)
            {
                if (fraction >= 0)
                {
                    x0 += stepx;
                    fraction -= dy;
                }
                y0 += stepy;
                fraction += dx;
                MAP[y0 * WIDTH + x0].push(e);
            }
        }
    }
}

/* anti-aliasing
public function aaLine ( x1:int, y1:int, x2:int, y2:int, color:uint ):void
{    
    var steep:Boolean = (y2 - y1) < 0 ? -(y2 - y1) : (y2 - y1) > (x2 - x1) < 0 ? -(x2 - x1) : (x2 - x1);
    var swap:int;
    
    if (steep)
    {
        swap=x1; x1=y1; y1=swap;
        swap=x2; x2=y2; y2=swap;
    }
    
    if (x1 > x2)
    {
        swap=x1; x1=x2; x2=swap;
        swap=y1; y1=y2; y2=swap;
    }

    var dx:int = x2 - x1;
    var dy:int = y2 - y1
    
    var gradient:Number = dy / dx;

    var xend:int = x1;
    var yend:Number = y1 + gradient * (xend - x1);
    var xgap:Number = 1-((x1 + 0.5)%1);
    var xpx1:int = xend;
    var ypx1:int = yend;
    var alpha:Number;
    
    alpha = ((yend)%1) * xgap;

    var intery:Number = yend + gradient;

    xend = x2;
    yend = y2 + gradient * (xend - x2)
    xgap = (x2 + 0.5)%1;
    
    var xpx2:int = xend; 
    var ypx2:int = yend;
    
    alpha = (1-((yend)%1)) * xgap;
    
    if (steep)
        drawAlphaPixel(ypx2,xpx2,alpha,color);
    else drawAlphaPixel(xpx2, ypx2,alpha,color);
    
    alpha = ((yend)%1) * xgap;
    
    if (steep)
        drawAlphaPixel(ypx2 + 1,xpx2,alpha,color);
    else drawAlphaPixel(xpx2, ypx2 + 1,alpha,color);
     
    var x:int=xpx1;
    
    while (x++<xpx2)
    {
        alpha = 1-((intery)%1);
        
        if (steep)
            drawAlphaPixel(intery,x,alpha,color);
        else drawAlphaPixel(x,intery,alpha,color);
        
        alpha=intery%1;
        
        if (steep)
            drawAlphaPixel(intery+1,x,alpha,color);
        else drawAlphaPixel(x,intery+1,alpha,color);
        
        intery = intery + gradient
    }
}
/**/