雪の結晶シミュレーション(2次元DLA)
【アルゴリズム】水分子(水色)の運動をランダムウォークとみなしランダムな位置からスタートさせます。 中心部分にある核(赤)に接触すると、水分子は吸着して固まります。 固まった後は、核と同様に水分子を吸着していく。 その結果、数珠繋ぎに成長していくことになります。
本シミュレーションは拡散律則凝集と呼ばれ、結晶成長の単純なモデルとして考えられています。
参考:DLAによる樹木状クラスタのシミュレーション、DLAによる樹木状クラスタのフラクタル次元
プログラムソース(HTML5, JavaScript)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | ////////////////////////////////////////////// // 雪の結晶シミュレーション ////////////////////////////////////////////// //DLAクラス var DLA2D = function () { //粒子数 this .N = 20000; //初期位置の最小値 this .L0 = 100; //初期位置の最大値 this .Lmax = 500; //内部プロパティ this .perticles = []; //粒子情報が格納される配列 this .fixPerticleIndex = []; //吸着された粒子インデックスが格納される配列 this .maxLengthSq = 1; //原点からの最大距離の2乗 //形状オブジェクトの宣言と生成 this .geometry = new THREE.PlaneGeometry(1, 1); //材質オブジェクトの宣言と生成 this .material = new THREE.MeshBasicMaterial( {color:0x0000FF} ); //材質オブジェクトの宣言と生成 this .fixedMaterial = new THREE.MeshBasicMaterial( {color:0xFF0000} ); //初期化 this .init = function () { //分布密度関数の係数 var A = 2 / ( this .L0* this .L0 + this .Lmax* this .Lmax ); for ( var i = 0; i <= 100; i++ ) this .fixPerticleIndex[ i ] = [ ]; for ( var n = 0; n < this .N; n++ ){ //積分分布密度 var F = Math.random(); //積分分布密度から得られる距離 var L = Math.sqrt( this .L0* this .L0 + 2 * F / A ); //偏角 var theta = 2 * Math.PI * Math.random(); var x = L * Math.cos( theta ) ; var y = L * Math.sin( theta ) ; x = Math.round(x); y = Math.round(y); //平面オブジェクトの生成 this .perticles[n] = new THREE.Mesh( this .geometry, this .material ); //初期値を指定 this .perticles[n].position.set( x, y, 0 ); //平面オブジェクトのシーンへの追加 scene.add( this .perticles[n] ); //固定フラグ this .perticles[n].fixed = false ; } //核を設定 this .perticles[0].position.set( 0, 0, 0 ); this .perticles[0].fixed = true ; this .fixPerticleIndex[ 0 ].push( 0 ); //描画色を変更 this .perticles[0].material = this .fixedMaterial;; this .perticles[0].needsUpdate = true ; } //時間発展 this .timeEvolution = function () { var x, y, r, lengthSq; return function () { for ( var i = 0; i < this .N; i++ ){ if ( this .perticles[i].fixed ) continue ; x = this .perticles[i].position.x; y = this .perticles[i].position.y; r = Math.random(); if ( r < 0.25 ) x += 1.0; else if ( r < 0.50 ) x -= 1.0; else if ( r< 0.75 ) y += 1.0; else y -= 1.0; this .perticles[i].position.x = x; this .perticles[i].position.y = y; lengthSq = x*x + y*y; //吸着テスト if ( lengthSq <= this .maxLengthSq ) this .checkCollision( i , lengthSq); } } }(); //吸着テスト this .checkCollision = function ( n, lengthSq ){ var v = new THREE.Vector3(); var lengthIndex, M, index; var i, j; return function ( n, lengthSq ){ //距離配列要素番号 lengthIndex = Math.floor( Math.sqrt( lengthSq ) ); for ( j = lengthIndex - 1; j <= lengthIndex + 1; j++ ){ if ( j < 0 ) continue ; M = this .fixPerticleIndex[ j ].length; for ( i = 0; i < M ; i++ ){ index = this .fixPerticleIndex[ j ][ i ]; lengthSq = v.copy( this .perticles[ n ].position ).sub( this .perticles[ index ].position ).lengthSq(); if ( lengthSq <= 1.0 ) { this .perticles[n].fixed = true ; this .fixPerticleIndex[ lengthIndex ].push( n ); //描画色を変更 this .perticles[n].material = this .fixedMaterial; this .perticles[n].needsUpdate = true ; //最大半径の更新 if ( this .perticles[ n ].position.lengthSq() >= this .maxLengthSq ) { this .maxLengthSq = Math.pow( this .perticles[ n ].position.length() + 1, 2); //console.log( this.maxLengthSq ); } return ; } } } } }(); } |