In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

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

直列多重振り子シミュレーション(改良版)

http://www.aihara.co.jp/~taiji/pendula-equations/present.html さんリスペクトです。
逆行列計算をLU分解にしました。コードはhttp://code.google.com/p/face-recognition-library-as3/source/browse/trunk/FaceRecognitionLib/src/com/karthik/math/lingalg/LUDecomposition.as?r=3を参考にさせて頂きました。
数が多くなっても軽い。
/**
 * Copyright Nos_lkSsvOhB ( http://wonderfl.net/user/Nos_lkSsvOhB )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/5B5a
 */

package {
    import flash.display.*;
    import flash.events.Event;
    import com.bit101.components.*;
    import flash.text.TextField;

    public class Oscillator extends Sprite {
        private var stage_x:int=200;
        private var stage_y:int=80;
        private var i:Number;
        private var j:Number;
        private var n:int=5;
        private var nl:Number=30;
        private var r:Number=5;
        private var mass:Number=1;
        private var g:Number=9.8;
        private var dt:Number=0.05;
        private var atten:Number=100;
        
        private var m:Array=new Array();
        private var summ:Array=new Array();
        private var l:Array=new Array();
        private var th0:Array=new Array();
        private var th1:Array=new Array();
        private var dth0:Array=new Array();
        private var dth1:Array=new Array();
        private var A:Array=new Array();
        private var B:Array=new Array();
        private var C:Array=new Array();
        private var D:Array=new Array();

        private var bar:Array=new Array();
        private var b:Array=new Array();
        private var pole:Sprite=new Sprite();

        private var sli_n:HUISlider;        
        private var sli_dt:HUISlider;        
        private var sli_att:HUISlider;        
        private var sli_m:HUISlider;        

        //コンストラクタ
        public function Oscillator() {
            sli_n = new HUISlider(this, 10, 10, "Number", onChange);
            sli_n.value=n;
            sli_n.maximum=80;
            sli_n.minimum=2;
            sli_n.labelPrecision = 0;
            
            sli_dt = new HUISlider(this, 10, 30, "differential time", onChange);
            sli_dt.value=dt;
            sli_dt.maximum=0.75;
            sli_dt.minimum=0.01;
            
            sli_att = new HUISlider(this, 200, 10, "attenuation", onChange);
            sli_att.value=atten;
            sli_att.maximum=1000;
            sli_att.minimum=10;
            
            sli_m = new HUISlider(this, 200, 30, "mass", onChange);
            sli_m.value=mass;
            sli_m.maximum=1;
            sli_m.minimum=0.1;

            init(n);
        }

        //初期化
        private function init(nm:Number):void {
            //ボールの数
            n=Math.round(nm);
            //スプライトの深度用カウンタ
            var c:int=0;

            for(i=0;i<n;i++,c++){
                if(i==1)
                    c++;
                //i番目の質量
                m[i]=mass;
                //i番目の棒の長さ
                l[i]=nl;
                //棒の描画
                bar[i] = new Sprite();
                bar[i].graphics.lineStyle(2,0x000000,1,false);
                bar[i].graphics.moveTo(-l[i]/2,0);
                bar[i].graphics.lineTo(l[i]/2,0);
                addChildAt(bar[i],c);

                if(i==0){
                    //支点の描画
                    pole.graphics.beginFill(0xFF0000);
                    pole.graphics.drawCircle(0,0,r/2);
                    pole.graphics.endFill();
                    addChildAt(pole,1);
                    pole.x=stage_x;
                    pole.y=stage_y;
                }
                //ボール描画
                b[i] = new Sprite();
                b[i].graphics.beginFill(0xFFFFFF*Math.random());
                b[i].graphics.drawCircle(0,0,r);
                b[i].graphics.endFill();
                addChildAt(b[i],i+n+1);
                //初期角度をランダムにしてみたら面白いかな~って感じで
                var thran:Number=Math.random()*Math.PI*2;
                th0[i]=thran;
                th1[i]=thran;
                dth0[i]=0;
                dth1[i]=0;
                b[i].x=stage_x;
                b[i].y=stage_y;
                //行列操作用2次元配列
                A[i]=new Array();
//                C[i]=new Array();    // inv_mat内でまたnewするため
            }
            //各スプライトの位置
            for(i=0;i<n;i++){
                for(j=0;j<=i;j++){
                    b[i].x+=l[j]*Math.cos(th1[j]);
                    b[i].y+=l[j]*Math.sin(th1[j]);
                }
                bar[i].x=b[i].x-l[i]*Math.cos(th1[i])/2;
                bar[i].y=b[i].y-l[i]*Math.sin(th1[i])/2;
                //単に角度計算すると壊れる現象があるため
                if(i==0)
                    bar[i].rotation=Math.atan2(b[i].y - stage_y, b[i].x - stage_x)*180/Math.PI;
                else
                    bar[i].rotation=Math.atan2(b[i].y - b[i-1].y, b[i].x - b[i-1].x)*180/Math.PI;
                //iからnまでの質量の合計を入れる配列の初期化
                summ[i]=0;
                for(j=i;j<n;j++){
                    summ[i]+=m[j];
                }
            }
            //EnterFrameのイベントリスナ
            addEventListener(Event.ENTER_FRAME,onEnter);
        }

        private function onEnter(event:Event):void {
            for(i=0;i<n;i++){
                B[i]=0;
                for(j=0;j<n;j++){
                    //A[i][j]の計算
                    if(j==i)
                        A[i][j]=summ[i]*Math.pow(l[i],2);
                    else if(j<i)
                        A[i][j]=summ[i]*l[j]*l[i]*Math.cos(th0[j]-th0[i]);
                    else if(j>i)
                        A[i][j]=summ[j]*l[j]*l[i]*Math.cos(th0[j]-th0[i]);

                    //B[i]の計算
                    if(j==i) continue;
                    else if(j<i)
                        B[i]+=summ[i]*l[j]*l[i]*Math.pow(dth0[j],2)*Math.sin(th0[j]-th0[i]);
                    else if(j>i)
                        B[i]+=summ[j]*l[j]*l[i]*Math.pow(dth0[j],2)*Math.sin(th0[j]-th0[i]);
                }
                B[i]+=summ[i]*g*l[i]*Math.cos(th0[i])-atten*dth0[i];
            }
            //Aの逆行列をCに入れる
            inv_mat(A,C);
            //行列CとベクトルBの掛け算の結果をベクトルDに入れる
            D=mat_mul(C,B);
            for(i=0;i<n;i++){
                //微分方程式を有限差分法で解く
                dth1[i]=dth0[i]+D[i]*dt;
                th1[i]=th0[i]+dth1[i]*dt;
            }
            //各スプライトを新しい位置に
            for(i=0;i<n;i++){
                b[i].x=stage_x;
                b[i].y=stage_y;
                for(j=0;j<=i;j++){
                    b[i].x+=l[j]*Math.cos(th1[j]);
                    b[i].y+=l[j]*Math.sin(th1[j]);
                }
                bar[i].x=b[i].x-l[i]*Math.cos(th1[i])/2;
                bar[i].y=b[i].y-l[i]*Math.sin(th1[i])/2;
                //単に角度計算すると壊れる現象があるため
                if(i==0)
                    bar[i].rotation=Math.atan2(b[i].y - stage_y, b[i].x - stage_x)*180/Math.PI;
                else
                    bar[i].rotation=Math.atan2(b[i].y - b[i-1].y, b[i].x - b[i-1].x)*180/Math.PI;
            }
            //漸化式(微小時間後に代入)
            for(i=0;i<n;i++){
                th0[i]=th1[i];
                dth0[i]=dth1[i];
            }
        }

        //スライドバーが変更されたときのメソッド
        private function onChange(event:Event):void {
            var _n:int=Math.round(sli_n.value);
            var _dt:Number=sli_dt.value;
            var _att:Number=sli_att.value;
            var _mass:Number=sli_m.value;
            for(i=0;i<n;i++){
                b[i].graphics.clear();
                bar[i].graphics.clear();
                A[i].splice(_n,n);
            }
            //配列の切り詰め
            if(_n<n){
                A.splice(_n,n);
                B.splice(_n,n);
                C.splice(_n,n);
                D.splice(_n,n);
            }
            dt=_dt;
            atten=_att;
            mass=_mass;
            init(_n);
        }

        private function inv_mat(a:Array,c:Array):void{
            var LU : Array = a;
            var n : int = a.length;
            var m : int = n;

            var i : int = 0;
            var k : int = 0;
            var piv : Array = new Array(n);
            for (i = 0; i < m; i++) {
                piv[i] = i;
            }
            var pivsign : int = 1;
            var LUrowi : Array;
            var LUcolj : Array = new Array(n);

            for (var j : int = 0; j < n; j++) {
                for (i = 0; i < m; i++) {
                    LUcolj[i] = LU[i][j];
                }
                for (i = 0; i < m; i++) {
                    LUrowi = LU[i];
                    var kmax : int = Math.min(i, j);
                    var s : Number = 0.0;
                    for (k = 0; k < kmax; k++) {
                        s += LUrowi[k] * LUcolj[k];
                    }
                    LUrowi[j] = LUcolj[i] -= s;
                }
                var p : int = j;
                for (i = j + 1; i < m; i++) {
                    if (Math.abs(LUcolj[i]) > Math.abs(LUcolj[p])) {
                        p = i;
                    }
                }
                if (p != j) {
                    for (k = 0; k < n; k++) {
                        var t : Number = LU[p][k]; LU[p][k] = LU[j][k]; LU[j][k] = t;
                    }
                    k = piv[p]; piv[p] = piv[j]; piv[j] = k;
                    pivsign = -pivsign;
                }
                if (j < m && LU[j][j] != 0.0) {
                    for (i = j + 1; i < m; i++) {
                        LU[i][j] /= LU[j][j];
                    }
                }
            }

            var nx : int = n;
            var X : Array = new Array(n);
            for (i = 0; i < n; i++){
                X[i] = new Array();
                for (j = 0; j < n; j++){
                    if (i == j) X[i][j] = 1;
                    else X[i][j] = 0;
                }
            }

            for (i = 0; i < n; i++)
            {
                c[i] = new Array(n);
                for (j = 0; j < n; j++)
                {
                    c[i][j] = X[piv[i]][j];
                }
            }

            for (k = 0; k < n; k++) {
                for (i = k + 1; i < n; i++) {
                    for (j = 0; j < nx; j++) {
                        c[i][j] -= c[k][j] * LU[i][k];
                    }
                }
            }
            for (k = n - 1; k >= 0; k--) {
                for (j = 0; j < nx; j++) {
                    c[k][j] /= LU[k][k];
                }
                for (i = 0; i < k; i++) {
                    for (j = 0; j < nx; j++) {
                        c[i][j] -= c[k][j] * LU[i][k];
                    }
                }
            }
        }

        private function mat_mul(a:Array,b:Array):Array{
            var n:int=a.length;
            var c:Array=new Array();
            for(var i:int=0;i<n;i++){
                c[i]=0;
                for(var k:int=0;k<n;k++){
                    c[i]+=a[i][k]*b[k];
                }
            }
            return c;
        }
        
    }
}