読者です 読者をやめる 読者になる 読者になる

Q068891のメモ帳

気が向いたときにLinuxだったり、プログラムだったり、適当なメモをざっくばらんに書いてます。

HTML5 canvas + javascript でDLAシミュレーションを描画

Lévy walkに続いて、HTML5 canvas + javascript で DLA(Diffusion-Limited Aggregation)シミュレーションをアニメーションにして見ました。

q068891.hatenablog.com

DLAシミュレーションはウォーカーがブラウン運動でチョコチョコ動き、他のウォーカーと隣り合ったらそこに停止するという感じのシミュレーションです。

つまり、時間が経てば経つほど、ウォーカーがどんどんくっつき、1つのクラスタとなって、形を作り出すというシミュレーションです。

ずっと見ているとわかりますが(結構な時間がかかります)、単純なブラウン運動をしているだけなのに、枝分かれした複雑な形ができることがわかります。 現実世界でシャーレ上の菌も似たような模様を描くことがありますが、「ランダムに動く&触れ合ったらくっつく」という極単純な2つのルールが、その模様を描く主な原因となっていることがこのシミュレーションからわかります。

このプログラムでは円の境界上からランダムにスタートさせて、四角の囲いで周期的境界条件の境界としています。

本当はウォーカーが円から出たら「再スタート or 円からはみ出たら円を周期的境界条件の境界とする」とかした方が良いのですが、 アニメーションを見たいだけなのであんまり厳密じゃなくてもよいかと思ってそこら辺適当です。すみません。

時間が経つとこんな感じになります。

f:id:Q068891:20161002173705p:plain

ソース

<script type="text/javascript">

      //キャンパスの大きさ    
      var height = 600;
      var width = 600;
      var r = width / 2.0;

      //walkerの数
      var walker_num = 800;

      //現在歩いているwalkerの番号
      var current_walker = 1;
      
      //walkerが1回で進む距離 & この距離分だけ近づいたらwalkerがくっつく
      var d = 18;

      var ctx;
      var radian;

      var first_flg = 1;

      //walkerの現在位置配列x,y
      var x = new Array(walker_num);
      var y = new Array(walker_num);

      var color_r = new Array(walker_num);
      var color_g = new Array(walker_num);
      var color_b = new Array(walker_num);

      for (var i=1;i<walker_num;i++){
        
        //walkerをとりあえず範囲外においておく
        x[i]= width*1000;
        y[i]= height*1000;

        color_r[i] = Math.floor(Math.random() * 256);
        color_g[i] = Math.floor(Math.random() * 256);
        color_b[i] = Math.floor(Math.random() * 256);
      }
     
      //walkerを1つ中心点に置いておく
      x[0]= width/2.0;
      y[0]= height/2.0;
      color_r[0] = Math.floor(0);
      color_g[0] = Math.floor(0);
      color_b[0] = Math.floor(0);

      function init(){
        var canvas = document.getElementById('dla');
        ctx = canvas.getContext('2d');
        setInterval(draw, 1);
      }

      function draw(){

        ctx.clearRect(0, 0, width, height);
        
        //枠の四角描画
        ctx.strokeStyle = 'rgb(0,0,0)';
        ctx.strokeRect(0, 0, width, height);

        //walkerの初期位置を設定
        if(first_flg == 1){
          rad = Math.random() * 360
          x[current_walker]= (width/2.0) + r * Math.cos(rad);
          y[current_walker]= (height/2.0) - r * Math.sin(rad);
          first_flg = 0;
        }

        //walker描画
        for (var i=0;i<walker_num;i++){
          ctx.beginPath();
          ctx.fillStyle = 'rgb(' + (color_r[i]) + ',' + (color_g[i]) + ',' + (color_b[i]) + ')';
          //walker描画
          ctx.beginPath();
          ctx.arc(x[i],y[i],5,0,Math.PI*2,false);
          ctx.fill();
        }

        //walkerひとつだけwalkさせる
        radian = 2.0 * Math.PI * Math.random();

        //計算した角度に距離d分移動し次の座標へ移動
        x[current_walker] = x[current_walker] + d * Math.cos(radian);
        y[current_walker] = y[current_walker] + d * Math.sin(radian);
        

        //周期的境界条件
        if(width <= x[current_walker]){
          x[current_walker] = x[current_walker] - width;
        }
        if(height <= y[current_walker]){
          y[current_walker] = y[current_walker] - height;
        }
        if(x[current_walker] <= 0){
          x[current_walker] = x[current_walker] + width;
        }
        if(y[current_walker] <= 0){
          y[current_walker] = y[current_walker] + height;
        }

        for (var i=0;i<walker_num;i++){
          
          if(current_walker != i){
            diff_x = Math.abs(x[current_walker] - x[i]);
            diff_y = Math.abs(y[current_walker] - y[i]);

            //walker間の距離を計算
            tmp_distance_x = Math.abs(x[i] - x[current_walker]);
            tmp_distance_y = Math.abs(y[i] - y[current_walker]);

            if(height/2.0 < tmp_distance_y){
              tmp_distance_y = height - tmp_distance_y;
            }

            if(width/2.0 < tmp_distance_x){
              tmp_distance_x = width - tmp_distance_x;
            }

            distance_between_currentwalker_i = Math.pow(tmp_distance_x,2) + Math.pow(tmp_distance_y,2);
            distance_between_currentwalker_i = Math.sqrt(distance_between_currentwalker_i);

            if(distance_between_currentwalker_i < d){
              current_walker++;
              first_flg = 1;
              break;
            }
          }
        }
      }

</script>

<body onload="init();">
  <canvas id="dla" width="600" height="600"></canvas>
</body>