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

AS3で流体解析テスト

動作重いです。要注意 ***
*
* AS3で数値流体力学テスト
*
* 解析		:二次元浅水流方程式(ただし、拡散項を省略)
* 解析方法	:非構造格子有限体積法・流束差分離法(FDS)
* 解析データ	:水工学委員会基礎水理部会 河床変動計算法研究グループ
*       	 (http://www.civil.hokudai.ac.jp/yasu/hendou/index.htm)
*        	 で公開されている計算用テストデータを使用
* 初期条件 	:適当
* 境界条件	:上流端=流入0.1m3/s 下流端=水位-0.1448m 閉境界=ノンスリップ
* 結果表示	:流速のみ(カラーコンターで表示)
* 結果出力	:未実装。
*
Get Adobe Flash player
by termat 24 Feb 2010
/**
 * Copyright termat ( http://wonderfl.net/user/termat )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/zQXz
 */

/*** 動作重いです。要注意 ***
 *
 * AS3で数値流体力学テスト
 *
 * 解析		:二次元浅水流方程式(ただし、拡散項を省略)
 * 解析方法	:非構造格子有限体積法・流束差分離法(FDS)
 * 解析データ	:水工学委員会基礎水理部会 河床変動計算法研究グループ
 *       	 (http://www.civil.hokudai.ac.jp/yasu/hendou/index.htm)
 *        	 で公開されている計算用テストデータを使用
 * 初期条件 	:適当
 * 境界条件	:上流端=流入0.1m3/s 下流端=水位-0.1448m 閉境界=ノンスリップ
 * 結果表示	:流速のみ(カラーコンターで表示)
 * 結果出力	:未実装。
 *
 */
package
{
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.system.Security;
	import flash.events.MouseEvent;
	import flash.text.engine.JustificationStyle;
	import flash.text.TextField;

	[SWF(framerate="60",width="500",height="500",backgroundColor="0xffffff")]
	public class Flow2d extends Sprite{
		private static const G:Number = 9.80665;//重力加速度
		private static const H:int = 0;	
		private static const UH:int = 1;
		private static const VH:int = 2;
		private static const NONE:int = 0;
		private static const NONSLIP:int = 1;
		private static const FLOW:int = 2;
		private static const LEVEL:int = 3;
		private static const H_MIN:Number = 0.005;	
		private var cells:Vector.<Cell>;
		private var map:Vector.<Array>;
		private var iter:int = 0;
		private var time:Number = 0.0;
		private var dt:Number = 0.5;
		private var loader:URLLoader;
		private var rect:Rectangle;
		private var canvas:MeshCanvas;
		private var pos:Point = null;
		private var cpos:Point = null;
		private var flowQ:Number;
		private var level:Number;
		private var preF:Vector.<Array>;
		private var text:TextField;
		private var start:Boolean = false;
		private var calc:Boolean = true;
		private var bt:Button;

		public function Flow2d():void {
			cells = new Vector.<Cell>();
			/*5iVESTAR.ORGさん(http://5ivestar.org/blog/)のプロキシを利用させていただいている。*/
			load("http://5ivestar.org/proxy/http://termat.sakura.ne.jp/air/mesh.csv",
			"http://5ivestar.org/proxy/crossdomain.xml");
			addEventListener(Event.EXIT_FRAME, update);
			text = new TextField();
			text.x = 10;
			text.y = 25;
			text.width = text.textWidth + 10;
			addChild(text);
			bt = new Button(30, 20, 5, "Start", 11);
			bt.addEventListener(MouseEvent.MOUSE_DOWN, buttonDown);
			addChild(bt);
			Wonderfl.capture_delay(900);
		}
		
		private function buttonDown(e:MouseEvent):void {
			start = (!start);
			if (start) { 
				bt.setLabelText("Stop", 11);
			} else {
				bt.setLabelText("Start", 11);
			}
		}
		
		private function load(url:String, sec:String = null):void {
			loader = new URLLoader();
			if (sec != null) Security.loadPolicyFile(sec);
			loader.addEventListener(Event.COMPLETE,onCompleted);
			loader.load(new URLRequest(url));
		}
		
		private function onCompleted(e:Event):void {
			var mesh:MeshData = new MeshData(loader.data);
			flowQ = mesh.flowQ;
			level = mesh.level;
			dt = mesh.dt;
			iter = 0;
			var node:Array = mesh.node;
			var elem:Vector.<Array> = mesh.elem;
			for (var i:int = 0; i < elem.length; i++) {
				cells[i] = new Cell(node[elem[i][0]], node[elem[i][1]], node[elem[i][2]]);
				cells[i].boundType = mesh.boundary[i];
			}
			map = mesh.map;
			rect = mesh.rect;
			preF = new Vector.<Array>(elem.length);
			canvas = new MeshCanvas(cells,rect);
			addChild(canvas);
			stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown);
			stage.addEventListener(MouseEvent.MOUSE_UP, mouseUp);
			stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMove);
			stage.addEventListener(MouseEvent.MOUSE_WHEEL, onWheel);
		}
		
		private function update(e:Event):void {
			if (canvas != null) canvas.update();
			if (start) {
				text.text = "計算中:time=" + time.toString() + "  iter=" + iter.toString();
				text.width = text.textWidth + 10;
				if (iter == 0) {
					if(calc)this.calc1d();
				}else {
					if(calc)this.calc2d();
				}
			}else {
				text.text = "停止中:time=" + time.toString()+"  iter="+iter.toString();
				text.width = text.textWidth + 10;
			}
		}
		
		private function mouseDown(e:MouseEvent):void {
			pos = new Point(e.stageX, e.stageY);
			cpos = new Point(canvas.x, canvas.y);
		}
		
		private function mouseUp(e:MouseEvent):void {
			mouseMove(e);
			pos = null;
			cpos = null;
		}
		
		private function mouseMove(e:MouseEvent):void {
			if (pos != null) {
				canvas.x = cpos.x + (e.stageX - pos.x);
				canvas.y = cpos.y + (e.stageY - pos.y);
			}
		}
		
		private function onWheel(e:MouseEvent):void {
			if (canvas == null) return;
			var n:Number = e.delta;
			if (n > 0) {
				canvas.scale /= 1.2;
			}else {
				canvas.scale *= 1.2;
			}
		}
		
// Array処理関数 -------------------------------------------------
		private function add(a:Array,b:Array):Array {
			for (var i:int = 0; i < a.length; i++) a[i] += b[i];
			return a;
		}
		
		private function sub(a:Array,b:Array):Array{
			for (var i:int = 0; i < a.length; i++) a[i] -= b[i];
			return a;
		}
		
		public function mul(a:Array,b:Number):Array{
			for (var i:int = 0; i < a.length; i++) a[i] *= b;
			return a;
		}
		
		private function copy(a:Array):Array {
			var ret:Array = new Array();
			for (var i:int = 0; i < a.length; i++) ret[i] = a[i];
			return ret;
		}
// 計算用関数 -------------------------------------------------
		// Roe平均の計算
		private  function getRoe(Ul:Array,Ur:Array):Array{
			var hl:Number=Ul[H];
			var hr:Number=Ur[H];
			var ul:Number=Ul[UH]/hl;
			var ur:Number=Ur[UH]/hr;
			var vl:Number=Ul[VH]/hl;
			var vr:Number=Ur[VH]/hr;
			var hh:Number=(Math.sqrt(hl)+Math.sqrt(hr));
			var _u:Number=(Math.sqrt(hl)*ul+Math.sqrt(hr)*ur)/hh;
			var _v:Number=(Math.sqrt(hl)*vl+Math.sqrt(hr)*vr)/hh;
			var _h:Number=(hl+hr)/2.0;
			var _c:Number=Math.sqrt(G*_h);
			return [_u, _v, _c, _h];
		}
		
		// 流束の計算
		public function getFN(nx:Number,ny:Number,Ex:Array,Fx:Array):Array{
			var e:Array = mul(copy(Ex), nx);
			var f:Array = mul(copy(Fx), ny);
			return add(e, f);
		}
		
		// 流束 第二項の計算
		public function getEDA3d(uvch:Array,nx:Number,ny:Number,Ul:Array,Ur:Array):Array{
			var e:Array=getRoeE3d(uvch, nx, ny);
			var d:Array=getRoeAbsDn3d(Ul,Ur,uvch,nx,ny);
			var a:Array=getRoeAlpha3d(uvch,nx,ny,Ul,Ur);
			e[0]=mul(e[0],a[0]*d[0]);
			e[1]=mul(e[1],a[1]*d[1]);
			e[2]=mul(e[2],a[2]*d[2]);
			return add(add(e[0], e[1]), e[2]);
		}
		
		// 近似ヤコビアンの右固有行列
		public function getRoeE3d(uvch:Array,nx:Number,ny:Number):Array{
			var m0:Array = [1.0, uvch[0] + uvch[2] * nx, uvch[1] + uvch[2] * ny];
			var m1:Array = [0.0, -uvch[2] * ny, uvch[2] * nx];
			var m2:Array = [1.0, uvch[0] - uvch[2] * nx, uvch[1] - uvch[2] * ny];
			return [m0, m1, m2];
		}
		
		// 近似ヤコビアンの固有値
		private function getRoeAbsDn3d(Ul:Array,Ur:Array,uvch:Array,nx:Number,ny:Number):Array{
			var ck:Array=getRoeDelta3d(Ul,Ur,nx,ny);
			var a0:Number = Math.abs(uvch[0] * nx + uvch[1] * ny + uvch[2]);
			var a1:Number = Math.abs(uvch[0] * nx + uvch[1] * ny);
			var a2:Number = Math.abs(uvch[0] * nx + uvch[1] * ny - uvch[2]);
			if(a0<ck[0])a0=ck[0];
			if(a1<ck[1])a1=ck[1];
			if(a2<ck[2])a2=ck[2];
			return [a0, a1, a2];
		}
		
		private function getRoeDelta3d(Ul:Array,Ur:Array,nx:Number,ny:Number):Array{
			var hl:Number = Ul[H];
			var ul:Number = Ul[UH] / hl;
			var vl:Number = Ul[VH] / hl;
			var cl:Number = Math.sqrt(G * hl);
			var hr:Number = Ur[H];
			var ur:Number = Ur[UH] / hr;
			var vr:Number = Ur[VH] / hr;
			var cr:Number = Math.sqrt(G * hr);
			var ff:Array = getRoeDn3d(getRoe(Ul, Ur), nx, ny);
			var fl:Array = getRoeDn3d(new Array(ul, vl, cl, hl), nx, ny);
			var fr:Array=getRoeDn3d(new Array(ur,vr,cr,hr),nx,ny);
			return [
				Math.max(0.0, ff[0] - fl[0], fr[0] - ff[0]),
				Math.max(0.0, ff[1] - fl[1], fr[1] - ff[1]),
				Math.max(0.0, ff[2] - fl[2], fr[2] - ff[2])];
		}
		
		// 近似ヤコビアンの固有値
		private function getRoeDn3d(uvch:Array,nx:Number,ny:Number):Array{
			return [
				uvch[0] * nx + uvch[1] * ny + uvch[2],
				uvch[0] * nx + uvch[1] * ny,
				uvch[0] * nx + uvch[1] * ny - uvch[2]];
		}
		
		// 波の強さaの計算
		private function getRoeAlpha3d(uvch:Array,nx:Number,ny:Number,Ul:Array,Ur:Array):Array{
			var c:Number = uvch[2];
			var r:Number = (1.0 / (2.0 * c));
			var dh:Number = (Ur[H] - Ul[H]);
			var duh:Number = (Ur[UH] - Ul[UH]);
			var dvh:Number = (Ur[VH] - Ul[VH]);
			var m0:Number = 0.5 * dh + r * (duh * nx + dvh * ny - (uvch[0] * nx + uvch[1] * ny) * dh)
			var m1:Number = (1.0 / c) * ((dvh - uvch[1] * dh) * nx - (duh - uvch[0] * dh) * ny);
			var m2:Number = 0.5 * dh - r * (duh * nx + dvh * ny - (uvch[0] * nx + uvch[1] * ny) * dh);
			return [m0, m1, m2];
		}
		
		// 生成・消滅項の計算(1)
		private function getSxSy3d(h:Number,u:Number,v:Number,n:Number):Array{
			var uv:Number = Math.sqrt(u * u + v * v);
			var h43:Number = Math.pow(h, 4.0 / 3.0);
			var sx:Number = n * n * u * uv / h43;
			var sy:Number = n * n * v * uv / h43;
			return [0.0, sx, sy];
		}
		
		// 生成・消滅項の計算(2)
		private function getSk3d(db:Number,l:Number,nx:Number,ny:Number,uvch:Array):Array{
			var ret:Array = getBarSk3d(db, l, nx, ny, uvch);
			var f0:Array = getAbsFByFMatrix3d(uvch, nx, ny);
			var e:Array = getRoeE3d(uvch, nx, ny);
			var beta:Array = getBeta3d(ret, uvch, nx, ny);
			e[0] = mul(e[0], f0[0] * beta[0]);
			e[1] = mul(e[1], f0[1] * beta[1]);
			e[2] = mul(e[2], f0[2] * beta[2]);
			e[0] = add(add(e[0], e[1]), e[2]);
			return mul(sub(ret, e[0]), 0.5);
		}
		
		// 生成・消滅項の計算(3)
		private function getBarSk3d(db:Number,l:Number,nx:Number,ny:Number,uvch:Array):Array{
			var s2:Number = G * uvch[3] * (l * db * nx);
			var s3:Number = G * uvch[3] * (l * db * ny);
			return [0.0, s2, s3];
		}
		
		// 生成・消滅項の計算(4)
		private function getBeta3d(barSk:Array,uvch:Array,nx:Number,ny:Number):Array{
			var r:Number=(1.0/(2.0*uvch[2]));
			var b0:Number = r * (barSk[1] * nx + barSk[2] * ny);
			var b2:Number = -r * (barSk[1] * nx + barSk[2] * ny);
			return [b0, 0.0, b2];
		}
		
		// |f|/fを取得
		private function getAbsFByFMatrix3d(uvch:Array,nx:Number,ny:Number):Array{
			var f0:Number=uvch[0]*nx+uvch[1]*ny+uvch[2];
			var f1:Number=uvch[0]*nx+uvch[1]*ny;
			var f2:Number=uvch[0]*nx+uvch[1]*ny-uvch[2];
			var a:Number;
			var b:Number;
			var c:Number;
			if(f0!=0.0){a=Math.abs(f0)/f0;}else{a=1;}
			if(f1!=0.0){b=Math.abs(f1)/f1;}else{b=1;}
			if(f2!=0.0){c=Math.abs(f2)/f2;}else{c=1;}
			return [a, b, c];
		}
		
// 計算 有限体積法 -----------------------------------------------------	
		private function updateCell():void {
			for (var i:int = 0; i < cells.length; i++) cells[i].updateData();
		}
		
		//時間積分(一次)
		private function calc1d():void {
			calc = false;
			iter++;
			time += dt;
			for (var i:int = 0; i < cells.length; i++) {
				var tmp:Array = calcFVM(i);	
				var ans0:Array = add(tmp[0], tmp[1]);
				var sxsy:Array = getSxSy3d(cells[i].h, cells[i].u, cells[i].v, cells[i].n);
				ans0 = add(ans0, mul(sxsy, G * cells[i].h * cells[i].area));
				ans0 = mul(ans0, 1.0 / cells[i].area);
				preF[i] = copy(ans0);
				ans0 = mul(ans0, dt);
				cells[i].U1[H] = cells[i].U[H] - ans0[H];
				cells[i].U1[UH] = cells[i].U[UH] - ans0[UH];
				cells[i].U1[VH] = cells[i].U[VH] - ans0[VH];
			}
			updateCell();
			calc = true;
		}
		
		//時間積分(二次)
		private function calc2d():void {
			calc = false;
			iter++;
			time += dt;
			var ans:Array=calc2df();
			for(var i:int=0;i<cells.length;i++){
				var ans0:Array = ans[i];
				var tp0:Number = ans0[H];
				var tp1:Number = ans0[UH];
				var tp2:Number = ans0[VH];
				ans0 = mul(ans0, 3.0);
				ans0 = sub(ans0, preF[i]);
				ans0 = mul(ans0, dt / 2.0);
				cells[i].U1[H] = cells[i].U[H] - ans0[H];
				cells[i].U1[UH] = cells[i].U[UH] - ans0[UH];
				cells[i].U1[VH] = cells[i].U[VH] - ans0[VH];
				preF[i] = [tp0, tp1, tp2];
			}
			updateCell();
			calc = true;
		}
		
		private function calc2df():Array{
			var ret:Array = new Array();
			for(var i:int=0;i<cells.length;i++){
				var tmp:Array = calcFVM(i);
				var ans0:Array = add(tmp[0], tmp[1]);
				var sxsy:Array = getSxSy3d(cells[i].h, cells[i].u, cells[i].v, cells[i].n);
				ans0 = add(ans0, mul(sxsy, G * cells[i].h * cells[i].area));
				ans0 = mul(ans0, 1.0 / cells[i].area);
				ret[i] = ans0;
			}
			return ret;
		}

		private function calcFVM(id:int):Array {
			var ans0:Array = [0.0, 0.0, 0.0];
			var ans1:Array = [0.0, 0.0, 0.0];
			for(var i:int=0;i<map[id].length;i++){
				if (cells[id].isWet()) {
					var tmp:Array = calcFVM_Wet(id, i);
					ans0 = add(ans0, tmp[0]);
					ans1 = add(ans1, tmp[1]);
				}else {
					tmp = calcFVM_Dry(id, i);
					ans0 = add(ans0, tmp[0]);
					ans1 = add(ans1, tmp[1]);
				}
			}
			return [ans0, ans1];
		}
		
		//陸地セルの計算
		private function calcFVM_Dry(id:int,i:int):Array {
			var an:Array = null;
			if(cells[id].boundType[i]==NONE){
				if (!cells[map[id][i]].isWet() || (cells[map[id][i]].wl < cells[id].b)) return new Array([0.0, 0.0, 0.0], [0.0, 0.0, 0.0]);
				an=calcNone(cells[id].U,cells[id].E,cells[id].F,cells[map[id][i]].U,cells[map[id][i]].E,cells[map[id][i]].F,
					cells[id].edgesLength[i], cells[id].n, cells[id].b, cells[map[id][i]].b, cells[id].nolm[i]);
			}else if(cells[id].boundType[i]==NONSLIP){
				return new Array([0.0, 0.0, 0.0], [0.0, 0.0, 0.0]);
			}else if (cells[id].boundType[i]==FLOW) {
				an = calcFlow(cells[id].U, cells[id].E, cells[id].F, flowQ, cells[id].edgesLength[i], cells[id].n, cells[id].nolm[i]);
			}else if(cells[id].boundType[i]==LEVEL){
				if(cells[id].b>level)return new Array([0.0, 0.0, 0.0], [0.0, 0.0, 0.0]);
				an=calcLevel(cells[id].U,cells[id].E,cells[id].F,level,cells[id].b,cells[id].edgesLength[i],cells[id].n,cells[id].nolm[i]);
			}
			return an;
		}

		//水面セルの計算
		private function calcFVM_Wet(id:int,i:int):Array {
			var an:Array = null;
			if(cells[id].boundType[i]==NONE){
				if (!cells[map[id][i]].isWet() && (cells[id].wl < cells[map[id][i]].b)) {
					an=calcNonSlip(
						cells[id].U, cells[id].E, cells[id].F, cells[id].edgesLength[i], cells[id].n, cells[id].nolm[i]);
				}else{
					an = calcNone(cells[id].U, cells[id].E, cells[id].F, cells[map[id][i]].U, cells[map[id][i]].E, cells[map[id][i]].F,
						cells[id].edgesLength[i], cells[id].n, cells[id].b, cells[map[id][i]].b, cells[id].nolm[i]);
				}
			}else if(cells[id].boundType[i]==NONSLIP){
				an=calcNonSlip(
					cells[id].U, cells[id].E, cells[id].F, cells[id].edgesLength[i], cells[id].n, cells[id].nolm[i]);
			}else if(cells[id].boundType[i]==FLOW){
				an = calcFlow(cells[id].U, cells[id].E, cells[id].F, flowQ, cells[id].edgesLength[i], cells[id].n, cells[id].nolm[i]);
			}else if (cells[id].boundType[i] == LEVEL) {
				if (cells[id].b > level) return new Array([0.0, 0.0, 0.0], [0.0, 0.0, 0.0]);
				an = calcLevel(cells[id].U, cells[id].E, cells[id].F, level, cells[id].b, cells[id].edgesLength[i], cells[id].n, cells[id].nolm[i]);
			}
			return an;
		}
		
// 境界別 流束の計算----------------------------------------------------
		private function calcNone(Ul:Array,El:Array,Fl:Array,
			Ur:Array, Er:Array, Fr:Array, L:Number, n:Number, bl:Number, br:Number, nolm:Point):Array {
			var uvch:Array=getRoe(Ul,Ur);
			var fr:Array = getFN(nolm.x, nolm.y, Er, Fr);
			var fl:Array = getFN(nolm.x, nolm.y, El, Fl);
			fr=add(fr,fl);
			var eda:Array = getEDA3d(uvch, nolm.x, nolm.y, Ul, Ur);
			fr=sub(fr,eda);
			fr=mul(fr,0.5);
			fr=mul(fr,L);
			var sk:Array = getSk3d((br - bl), L, nolm.x, nolm.y, uvch);
			return [fr, sk];
		}
		
		private function calcNonSlip(Ul:Array, El:Array, Fl:Array, L:Number, n:Number, nolm:Point):Array {
			var Ur:Array=copy(Ul);
			var Er:Array=copy(El);
			var Fr:Array=copy(Fl);
			Ur[1]=Ur[1]*-1.0;	Ur[2]=Ur[2]*-1.0;
			Er[0]=Er[0]*-1.0;	Fr[0]=Fr[0]*-1.0;
			var uvch:Array = getRoe(Ul, Ur);
			var fr:Array = getFN(nolm.x, nolm.y, Er, Fr);
			var fl:Array = getFN(nolm.x, nolm.y, El, Fl);
			fr = add(fr, fl);
			var eda:Array = getEDA3d(uvch, nolm.x, nolm.y, Ul, Ur);
			fr = sub(fr, eda);
			fr = mul(fr, 0.5);
			fr = mul(fr, L);
			var sk:Array = getSk3d(0, L, nolm.x, nolm.y, uvch);
			return [fr, sk];
		}
		
		private function calcFlow(Ul:Array, El:Array, Fl:Array, Q:Number, L:Number, n:Number, nolm:Point):Array {
			var hr:Number = Ul[0];
			var ur:Number = Q / hr * -nolm.x;
			var vr:Number = Q / hr * -nolm.y;
			var Ur:Array = [hr, hr * ur, hr * vr];
			var Er:Array = [ur * hr, ur * ur * hr + 0.5 * G * hr * hr, ur * vr * hr];
			var Fr:Array = [vr * hr, ur * vr * hr, vr * vr * hr + 0.5 * G * hr * hr];
			var uvch:Array = [ur, vr, Math.sqrt(G * hr), hr];
			var fr:Array = getFN(nolm.x, nolm.y, Er, Fr);
			var fl:Array = getFN(nolm.x, nolm.y, El, Fl);
			fr = add(fr, fl);
			var eda:Array = getEDA3d(uvch, nolm.x, nolm.y, Ul, Ur);
			fr = sub(fr, eda);
			fr = mul(fr, 0.5);
			fr = mul(fr, L);
			return [fr, [0.0, 0.0, 0.0]];
		}
		
		private function calcLevel(Ul:Array,El:Array,Fl:Array,WL:Number,gl:Number,L:Number,n:Number,nolm:Point):Array{
			var hl:Number = Ul[0];
			var ul:Number = Ul[1] / hl;
			var vl:Number = Ul[2] / hl;
			var _h:Number = WL - gl;
			var hr:Number = 2.0 * _h - hl;
			var ur:Number = ul + 2.0 * (Math.sqrt(G * hl) - Math.sqrt(G * hr)) * nolm.x;
			var vr:Number = vl + 2.0 * (Math.sqrt(G * hl) - Math.sqrt(G * hr)) * nolm.y;
			var Ur:Array = [hr, hr * ur, hr * vr];
			var Er:Array = [ur * hr, ur * ur * hr + 0.5 * G * hr * hr, ur * vr * hr];
			var Fr:Array = [vr * hr, ur * vr * hr, vr * vr * hr + 0.5 * G * hr * hr];
			var fr:Array = getFN(nolm.x, nolm.y, Er, Fr);
			var fl:Array = getFN(nolm.x, nolm.y, El, Fl);
			fr = add(fr, fl);
			var uvch:Array = getRoe(Ul, Ur);
			var eda:Array = getEDA3d(uvch, nolm.x, nolm.y, Ul, Ur);
			fr = sub(fr, eda);
			fr = mul(fr, 0.5);
			fr = mul(fr, L);
			var sk:Array = getSk3d(0.0, L, nolm.x, nolm.y, uvch);
			return [fr, sk];
		}
	}
}

import flash.display.Sprite;
import flash.display.Graphics;
import flash.display.MovieClip;
import flash.events.MouseEvent;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.events.Event;
import flash.filters.ColorMatrixFilter;
import flash.geom.Matrix;
import flash.filters.GlowFilter;
import flash.text.TextField;
class MeshCanvas extends MovieClip {
	private var cells:Vector.<Cell>;
	private var rect:Rectangle;
	public var scale:Number = 1.0;
	private var hh:Array;
	
	public function MeshCanvas(v:Vector.<Cell>,r:Rectangle):void {
		cells = v;
		rect = r;
		scale = Math.max(scale, 500/rect.width);
		scale = Math.min(scale, 500/rect.height);
		this.x = 250-scale*(rect.x+rect.width/2);
		this.y = 250+scale*(rect.y+rect.height/2);
	}
	
	public function update():void {
		this.scaleX = scale;
		this.scaleY = scale;
		var min:Number = 99999999;
		var max:Number = -99999999;
		for (var i:int = 0; i < cells.length; i++) {
			if (isNaN(cells[i].velocity)) continue;
			if (min > cells[i].velocity) min = cells[i].velocity;
			if (max < cells[i].velocity) max = cells[i].velocity;
		}
		hh =[];
		for (i = 0; i < cells.length; i++) {
			hh[i] = (cells[i].velocity - min) / (max - min);
		}
		graphics.clear();
		for (i = 0; i < cells.length;i++) {
			graphics.beginFill(getColor(hh[i]));
			cells[i].draw(graphics);
			graphics.endFill();
		}
	}
	
	private function getColor(v:Number):Number {
		var r:Number = Math.cos(Math.PI * ((v - 0.81) * 1.1)) * 255;
		var g:Number = Math.cos(Math.PI * ((v - 0.50) * 1.0)) * 255;
		var b:Number = Math.cos(Math.PI * ((v - 0.19) * 1.1)) * 255;
		r = Math.max(0, Math.min(255, r));
		g = Math.max(0, Math.min(255, g));
		b = Math.max(0, Math.min(255, b));
		return (r<<16)+(g<<8)+b;
	}
}

// 要素(非構造格子)
class Cell {
	public var node:Vector.<Point>;
	public var pos:Point;
	public var b:Number;
	public var n:Number;
	public var h:Number;
	public var u:Number;
	public var v:Number;
	public var wl:Number;
	public var velocity:Number;
	public var boundType:Array;
	public var edgesLength:Vector.<Number>;
	public var area:Number;
	public var nolm:Vector.<Point>;
	public var U0:Array;
	public var U:Array;
	public var U1:Array;
	public var E:Array;
	public var F:Array;
	private var vertex:Vector.<Number>;
	
	private static const H_MIN:Number = 0.005;
	private static const G:Number = 9.80665;
	
	public function Cell(p0:Array, p1:Array, p2:Array):void {
		node = new Vector.<Point>();
		node[0]=new Point(p0[0], p0[1]);
		node[1]=new Point(p1[0], p1[1]);
		node[2]=new Point(p2[0], p2[1]);
		pos = new Point((p0[0] + p1[0] + p2[0]) / 3.0, (p0[1] + p1[1] + p2[1]) / 3.0);
		b = (p0[2] + p1[2] + p2[2]) / 3.0;
		h = (p0[3] + p1[3] + p2[3]) / 3.0;
		n = (p0[4] + p1[4] + p2[4]) / 3.0;
		u = (p0[5] + p1[5] + p2[5]) / 3.0;
		v = (p0[6] + p1[6] + p2[6]) / 3.0;
		wl = b + h;
		velocity = Math.sqrt(u * u + v * v);
		edgesLength = new Vector.<Number>();
		edgesLength[0] = dist(p0, p1);
		edgesLength[1] = dist(p1, p2);
		edgesLength[2] = dist(p2, p0);
		area = getTriArea(p0, p1, p2);
		nolm = new Vector.<Point>();
		nolm[0]=createNolm(p0, p1);
		nolm[1]=createNolm(p1, p2);
		nolm[2]=createNolm(p2, p0);
		U0 = [0.0, 0.0, 0.0];
		U = [0.0, 0.0, 0.0];
		U[0] = h;
		U[1] = h * u;
		U[2] = h * v;
		U1 = [0.0, 0.0, 0.0];
		E = calcE(U);
		F = calcF(U);
		vertex = new Vector.<Number>();
		vertex.push(node[0].x); vertex.push(node[0].y);
		vertex.push(node[1].x); vertex.push(node[1].y);
		vertex.push(node[2].x); vertex.push(node[2].y);
	}
	
	public function draw(g:Graphics):void {
		g.drawTriangles(vertex);
	}
	
	private function calcE(u:Array):Array {
		var ret:Array = new Array();
		ret[0]=u[1];
		ret[1]=getU2hGh2(u);
		ret[2]=getUVH(u);
		return ret;
	}
	
	private function getU2hGh2(u:Array):Number{
		if (u[0] <= H_MIN) return 0.0;
		var uu:Number=u[1]/u[0];
		return uu*u[1]+0.5*G*u[0]*u[0];
	}

	private function getUVH(u:Array):Number{
		if (u[0] <= H_MIN) return 0.0;
		var uu:Number=u[1]/u[0];
		return uu*u[2];
	}
	
	private function getV2hGh2(u:Array):Number{
		if (u[0] <= H_MIN) return 0.0;
		var vv:Number=u[2]/u[0];
		return vv*u[2]+0.5*G*u[0]*u[0];
	}

	private function calcF(u:Array):Array{
		var ret:Array = new Array();
		ret[0]=u[2];
		ret[1]=getUVH(u);
		ret[2]=getV2hGh2(u);
		return ret;
	}
	
	public function isWet():Boolean {
		return (U[0] > H_MIN);
	}
	
	private function dist(p0:Array, p1:Array):Number {
		var x0:Number = p0[0] - p1[0];
		var y0:Number = p0[1] - p1[1];
		return Math.sqrt(x0 * x0 + y0 * y0);
	}
	
	private function getTriArea(p0:Array, p1:Array, p2:Array):Number{
		var aa:Number = p0[0] * p1[1];
		var bb:Number = p1[0] * p2[1];
		var cc:Number = p2[0] * p0[1];
		var dd:Number = p0[0] * p2[1];
		var ee:Number = p1[0] * p0[1];
		var ff:Number = p2[0] * p1[1];
		return 0.5 * (aa + bb + cc - dd - ee - ff);
	}
	
	public function createNolm(p0:Array, p1:Array):Point {
		var x:Number = p1[0] - p0[0];
		var y:Number = p1[1] - p0[1];
		var vc:Point = rotatePos(x, y);
		var ll:Number = Math.sqrt(vc.x * vc.x + vc.y * vc.y);
		vc.x /= ll;	vc.y /= ll;
		return vc;
	}
	
	private static const ROTATE:Number = -Math.PI / 2.0;
	private function rotatePos(x:Number, y:Number):Point {
		var p:Point = new Point();
		p.x = Math.cos(ROTATE) * x - Math.sin(ROTATE) * y;
		p.y = Math.sin(ROTATE) * x + Math.cos(ROTATE) * y;
		return p;
	}
	
	public function updateData():void {
		var i:int;
		if (U1[0] <= H_MIN) {
			U0[0] = U[0];
			U0[1] = U[1];
			U0[2] = U[2];
			U[0] = H_MIN;
			U[1] = 0.0;
			U[2] = 0.0;
			E = [0.0,0.0,0.0];
			F = [0.0, 0.0, 0.0];
		}else {
			U0[0] = U[0];
			U0[1] = U[1];
			U0[2] = U[2];
			U[0] = U1[0];
			U[1] = U1[1];
			U[2] = U1[2];
			E = calcE(U);
			F = calcF(U);
		}
		this.h = U[0];
		this.u = U[1] / U[0];
		this.v = U[2] / U[0];
		this.wl = this.b + this.h;
		velocity = Math.sqrt(u * u + v * v);
	}
}

class MeshData {
	public var node:Array;
	public var elem:Vector.<Array>;
	public var map:Vector.<Array>;
	public var boundary:Vector.<Array>;
	public var cell:Vector.<Array>;
	public var rect:Rectangle;
	public var flowQ:Number;
	public var level:Number;
	public var dt:Number;

	public function MeshData(str:String):void {
		var data:Array = parseCSV(str);
		node = new Array();
		var n:Number = parseInt((data.shift() as Array)[0] as String);
		var minX:Number = 9999999999;
		var maxX:Number = -9999999999;
		var minY:Number = 9999999999;
		var maxY:Number = -9999999999;
		for (var i:Number = 0; i < n; i++) {
			var tmp:Array = data.shift() as Array;
			var p:Array = new Array();
			for (var j:Number = 1; j < 8; j++) {
				p.push(parseFloat(tmp[j] as String));
			}
			node.push(p);
			minX = Math.min(minX, p[0] as Number);
			maxX = Math.max(maxX, p[0] as Number);
			minY = Math.min(minY, p[1] as Number);
			maxY = Math.max(maxY, p[1] as Number);
		}
		rect = new Rectangle(minX,minY,maxX-minX,maxY-minY);
		elem = new Vector.<Array>();
		map = new Vector.<Array>();
		boundary = new Vector.<Array>();
		n = parseInt((data.shift() as Array)[0] as String);
		for (i = 0; i < n; i++) {
			tmp = data.shift() as Array;
			elem.push(new Array(parseInt(tmp[1] as String), parseInt(tmp[2] as String), parseInt(tmp[3] as String)));
			map.push(new Array(parseInt(tmp[4] as String), parseInt(tmp[5] as String), parseInt(tmp[6] as String)));
			boundary.push(new Array(parseInt(tmp[7] as String), parseInt(tmp[8] as String), parseInt(tmp[9] as String)));
		}
		flowQ = parseFloat((data.shift() as Array)[0] as String);
		level = parseFloat((data.shift() as Array)[0] as String);
		dt = parseFloat((data.shift() as Array)[0] as String);
	}
		
	private function parseCSV(str:String):Array {
		str = (str.split("\r\n")).join("\n");
		str = (str.split("\r")).join("\n");
		var data:Array = new Array();
		var tmp:Array = str.split("\n");
		var l:Number = tmp.length;
		for(var i:Number=0; i<l; i++){
			data.push(tmp[i].split(","));
		}
		if(data[data.length-1].length == 1 && data[data.length-1][0] == ""){data.pop();}
		return data;
	}
}

class Button extends Sprite{
	private static const mono:ColorMatrixFilter = new ColorMatrixFilter([
		1 / 3, 1 / 3, 1 / 3, 0, 10, 1 / 3, 1 / 3, 1 / 3, 0, 10,
		1 / 3, 1 / 3, 1 / 3, 0, 10, 0,0,0, 1, 0]);
	private var _hover:Boolean = false;
	private var textField:TextField;
	public function get hover():Boolean{
		return _hover;
	}
	public function set hover(value:Boolean):void{
		if(_hover != value){
			_hover = value;
			filters = (_hover ? null : [mono]);
		}
	}

	public function Button(W:Number, H:Number, R:Number, label:String = "", size:int = 11){
		var matrix:Matrix = new Matrix();
		matrix.createGradientBox(W, H, Math.PI / 2);
        var bg:Sprite = new Sprite();
		bg.graphics.beginGradientFill("linear", [0xDDE9F4, 0xD5E4F1, 0xBAD2E8], [1, 1, 1],[0, 120, 136], matrix);
		bg.graphics.drawRoundRect(0, 0, W, H, R, R);
		bg.graphics.endFill();
		bg.filters = [new GlowFilter(0xFFFFBE, .5, 10, 10, 2, 1, true)];
		addChild(bg);
		var line:Sprite = new Sprite();
		line.graphics.lineStyle(3, 0xBAD2E8);
		line.graphics.drawRoundRect(0, 0, W, H, R, R);
		addChild(line);
        filters = [mono];
        buttonMode = true;
        mouseChildren = false;
        if (label != ""){
			textField= new TextField();
			textField.selectable = false;
			textField.autoSize = "left";
			textField.htmlText = <font size={size} color="#6B8399">{label}</font>.toXMLString();
			textField.x = (W - textField.width) / 2;
			textField.y = (H - textField.height) / 2;
			addChild(textField);
		}
		addEventListener("rollOver", buttonRollOver);
		addEventListener("rollOut", buttonRollOut);
		addEventListener("removed", function(event:Event):void{
			removeEventListener("rollOver", buttonRollOver);
			removeEventListener("rollOut", buttonRollOut);
			removeEventListener("removed", arguments.callee);
		});
	}

	public function setLabelText(str:String,size:int):void {
		textField.htmlText = <font size={size} color="#6B8399">{str}</font>.toXMLString();
	}
	
	protected function buttonRollOver(event:Event):void{
		hover = true;
	}

	protected function buttonRollOut(event:Event):void{
		hover = false;
	}
}