雪の結晶成長のシミュレーション
雪の結晶成長のモデル
水分子(半透明)の運動をランダムウォークとみなし、中心部分から半径 r の場所(図の円周上)からスタートする。 中心部分にある核(灰色)に接触すると、水分子は吸着して固まる。 固まった後は、核と同様に水分子を吸着していく。 その結果、数珠繋ぎに成長していくこととなる。
参考ページ:顕微鏡のブースと雪の結晶を人工的につくる(第7回「雪の教室」)
アルゴリズムの概要
N個の水分子とCN個の核の当たり判定をすべての場合について行うと、1秒間当たりN×CN×fps回の計算を行う必要がある(fpsはフレームレート)。
もともと、核から遠い場所にいる分子は当たり判定を行う必要はそもそもない。
ある半径r内の水分子のみ当たり判定を行うものとする。
また、分子の吸着数(stickedN個)にかかわらず、ランダムウォークしている分子数を一定(N個)とする。
メインフレーム
/*-- --*/
var mcs:Array = new Array();
//分子
var mcs2:Array = new Array();
//核
var N:Number = 50; //水分子の数
var CN:Number = 7; //コアの数
var stickedN:Number = 0; //吸着した分子数
var r:Number = 5; //分子の半径
var stageX:Number = 600; //ステージのサイズ
var stageY:Number = 600;
var stageR:Number = 200; //水分子スタート時の半径
var R:Number = 20; //当たり判定を行う半径
var k:Number = 1;
Molecule.stageX = stageX; //インスタンスプロパティに値を代入
Molecule.stageY = stageY;
Molecule.stageR = stageR;
var flag:Boolean = true; //
for (var i = 1; i<=N; i++) {
var theta = Math.random()*Math.PI*2;
this.attachMovie("molecule", "molecule-"+i+"_mc", -i, {_x:stageR*Math.cos(theta)+stageX/2, _y:stageR*Math.sin(theta)+stageY/2});
var mc:MovieClip = eval("molecule-"+i+"_mc");
mcs[i] = mc;
mcs[i].obj = new Molecule(mc);
}
for (var i = 1; i<=CN; i++) {
var mc:MovieClip = eval("core-"+i+"_mc");
mcs2[i] = mc;
mcs2[i].obj = new Core(mc);
}
/*-- 1秒間で24回のループで計算を行う(fpsが24の場合) --*/
_root.onEnterFrame = function() {
if (flag) {
for (var i = 1; i<=mcs.length; i++) {
if (mcs[i].obj.sticked) {
continue;
} else {
mcs[i].obj.move();
if (R+10<Math.sqrt(Math.pow(mcs[i].obj.x-stageX/2, 2)+Math.pow(mcs[i].obj.y-stageY/2, 2))) {
mcs[i]._alpha = 30;
continue;
}
mcs[i]._alpha = 50;
for (var j = k; j<=CN; j++) {
if (Math.pow(mcs[i].obj.x-mcs2[j].obj.x, 2)+Math.pow(mcs[i].obj.y-mcs2[j].obj.y, 2)<Math.pow(r*2, 2)) {
mcs[i].obj.sticked = true;
mcs[i]._alpha = 100;
CN++;
mcs2[CN] = mcs[i];
k = Math.round(R*R/1000);
if (k<1) {
k = 1;
}
stageR = R+180;
sirsle_mc._width = stageR*2;
sirsle_mc._height = stageR*2;
Molecule.stageR = stageR;
if (R<Math.sqrt(Math.pow(mcs[i].obj.x-stageX/2, 2)+Math.pow(mcs[i].obj.y-stageY/2, 2))) {
R = Math.sqrt(Math.pow(mcs[i].obj.x-stageX/2, 2)+Math.pow(mcs[i].obj.y-stageY/2, 2));
}
N++;
var theta = Math.random()*Math.PI*2;
this.attachMovie("molecule", "molecule-"+N+"_mc", -N, {_x:stageR*Math.cos(theta)+stageX/2, _y:stageR*Math.sin(theta)+stageY/2});
var mc:MovieClip = eval("molecule-"+N+"_mc");
mcs[i] = mc;
mcs[i].obj = new Molecule(mc);
trace(N+" "+k+" "+Math.sqrt(Math.pow(mcs[i].obj.x-stageX/2, 2)+Math.pow(mcs[i].obj.y-stageY/2, 2)));
break;
}
}
}
}
//updateAfterEvent();
//stop();
}
};
stop_btn.onPress = function() {
if (flag) {
flag = false;
} else {
flag = true;
}
};
Moleculeクラス
水分子と核の性質は、クラスファイルで定義する。
/*-- Molecule --*/
class Molecule {
//クラスプロパティ
static var mcs:Array;
static var stageX:Number;
static var stageY:Number;
static var stageR:Number;
//インスタントプロパティ
var mc :MovieClip;
var x :Number;
var y :Number;
var sticked :Boolean = false;
//コンストラクタ
function Molecule(target:MovieClip) {
mc = target;
x = mc._x;
y = mc._y;
}
function move() : Void{
x = x + (Math.random() - Math.random() )*5;
y = y + (Math.random() - Math.random() )*5;
if( Math.pow(stageX/2 -x ,2) + Math.pow( stageY/2 -y ,2) > stageR*stageR ) {
x = stageX - x;
y = stageY - y;
if(x > stageX/2) x = x-10;
else x = x+10;
if(y > stageY/2) y = y-10;
else y = y+10;
}
mc._x = x;
mc._y = y;
}
}
サンプルファイル
molecule-10-1.fla (62KB)
Molecule.as・・・Moleculeクラスファイル
※「supporters-1.fla」 と 「num.txt」は同じフォルダ内におく。