js多个引力小球计算

闲着无聊写着玩的一个小程序,模拟多个平面小球在相互引力作用下的运动状态。随便写的也没怎么优化计算过程,在chrome 31,ff 25,ie 10上都运行了下,50个小球在1000频率的情况下chrome和ff还能看看,ie就完全不能看了。

源码如下:

<!DOCTYPE html>
<html>
  <head>
  	<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
  	<meta http-equiv="content-language" content="zh-CN" />
    <title>引力小球运动探测</title>
    
    <script type="text/javascript">
    //---------------------------------------------------此区域定义程序执行需要的bean结构-----------------------
    function Vector(speedx,speedy){
    	this.speedX=speedx;
    	this.speedY=speedy;
    }
    
	/**
		x:x坐标
		y:y坐标
		weight:质量,与引力计算有关
		objSpeed:一个向量,包含x方向和y方向的速度
	*/
    function OriantObj(x,y,r,weight,objSpeed){
    	this.x=x;
    	this.y=y;
		this.r=r;
    	this.speed=objSpeed;
    	this.weight=weight;
    }   
    
    function pathCalc(speed,accelerate,time){
    	var path=new Vector();
    	path.speedX=speed.speedX*time+accelerate.speedX*time*time/2;
    	path.speedY=speed.speedY*time+accelerate.speedY*time*time/2;
    	return path;
    }
    
    function speedCalc(speed,accelerate,time){
    	var path=new Vector();
    	path.speedX=speed.speedX+accelerate.speedX*time;
    	path.speedY=speed.speedY+accelerate.speedY*time;
    	return path;
    }
    function Position(x,y){
    	this.px=x;
    	this.py=y;
    }	
	</script>
	<script type="text/javascript">
	//-----------------------------------------------------------此区域定义计算过程中用到的各种函数---------------------------
	/**
		计算令一个小球对当前小球作用力的加速度效果;		
	*/
	OriantObj.prototype.accelerationToOriant=function(another){
    	var rx=another.x-this.x;
    	var ry=another.y-this.y;
    	var r2=rx*rx+ry*ry;
    	var r=Math.sqrt(r2);
		
		//防止无限加速的情况产生
		var minr=this.r+another.r;
		if(r<minr){
			r=minr;
			r2=minr*minr;
		}
		
    	var accs=GRAVITY*another.weight/(r2);
    	return new Vector(rx*accs/r,ry*accs/r);
    };
	
	/**
		计算一小段时间内小球运动到的地方,在此时间内认为各小球位置变动极小,忽略引力变化
		同时,如果判断到两小球碰撞到一起了,认定两球均为刚体,计算碰撞效果;
	*/
	function caclWhile(oriants,index,ft){
		var currAcc=new Vector(0,0);
		var ball=oriants[index];
		
		for(var kk=0;oriants[kk];kk++){
			if(kk===index){
				continue;
			}
			var rr=ball.r+oriants[kk].r;
			var lenx=ball.x-oriants[kk].x;
			var leny=ball.y-oriants[kk].y;
			//计算引力效果
			if(openGravity){
				var accCurr=ball.accelerationToOriant(oriants[kk]);
				currAcc.speedX=currAcc.speedX+accCurr.speedX;
				currAcc.speedY=currAcc.speedY+accCurr.speedY;
			}		
			//计算碰撞效果	
			if((lenx*lenx+leny*leny)<=(rr*rr)){
				rigidBodyCollision(ball,oriants[kk]);
			}
		}
		if(openFriction){
			//计算阻力作用效果
			currAcc.speedX=frictionCacl(ball.speed.speedX,currAcc.speedX);
			currAcc.speedY=frictionCacl(ball.speed.speedY,currAcc.speedY);
		}	
				
    	var pp=pathCalc(ball.speed,currAcc,ft);
		ball.x=ball.x+pp.speedX;
    	ball.y=ball.y+pp.speedY;
		var sbf=ball.speed;
    	ball.speed=speedCalc(ball.speed,currAcc,ft);
		
		//如果速度变更跨过0,则置0
		if(sbf.speedX*ball.speed.speedX<0){
			ball.speed.speedX=0;
		}
		if(sbf.speedY*ball.speed.speedY<0){
			ball.speed.speedY=0;
		}
		
		//边界反弹检测
		if(openRegionDetect){
			drawRegionDetect(ball);
		}	
		return ball;
	}
	
	/**
		计算阻力作用
	*/
	function frictionCacl(speed,force){
		if(speed==0){
			if(FRICTION>Math.abs(force))
				return 0;
			else 
				return force-(FRICTION*force)/Math.abs(force);
		}else{
			return force-(FRICTION*speed)/Math.abs(speed)
		}
	}
	
	/**
		计算刚体碰撞
	*/	
	function rigidBodyCollision(obj1,obj2){
		//避免重复检测碰撞
		if(obj1.crashChecked&&obj2.crashChecked){
			obj1.crashChecked=obj2.crashChecked=false;
			return ;
		}
		if(obj1.speed.speedX==0&&obj1.speed.speedY==0&&obj2.speed.speedX==0&&obj2.speed.speedY==0){
			return;
		}
		var cx=obj1.x-obj2.x,cy=obj1.y-obj2.y;
		var tmp=cx*cx+cy*cy;
		var x1=cx*obj1.speed.speedX+cy*obj1.speed.speedY;
		var y1=-cy*obj1.speed.speedX+cx*obj1.speed.speedY;
		var x2=cx*obj2.speed.speedX+cy*obj2.speed.speedY;
		var y2=-cy*obj2.speed.speedX+cx*obj2.speed.speedY;
		
		var midtmp=(1+rebound)*(obj1.weight*x1+obj2.weight*x2)/(obj1.weight+obj2.weight);
		var ax1=midtmp-rebound*x1;
		var ax2=midtmp-rebound*x2;

		obj1.speed.speedX=(ax1*cx-y1*cy)/tmp;
		obj1.speed.speedY=(ax1*cy+y1*cx)/tmp;	
		obj2.speed.speedX=(ax2*cx-y2*cy)/tmp;
		obj2.speed.speedY=(ax2*cy+y2*cx)/tmp;
		
		obj1.crashChecked=obj2.crashChecked=true;
			
		
		//按重量各退一步,避免互相嵌入
		var axxx=((obj1.r+obj2.r)/Math.sqrt(tmp))-1;
		var t1=axxx/(1+(obj2.weight/obj1.weight));
		var t2=axxx/(1+(obj1.weight/obj2.weight));
		obj1.x=obj1.x+cx*t1;
		obj1.y=obj1.y+cy*t1;
		obj2.x=obj2.x-cx*t2;
		obj2.y=obj2.y-cy*t2;
	}
	
	/**
		边界检测函数
	*/
	function drawRegionDetect(ball){
		if((ball.x-ball.r<0&&ball.speed.speedX<0)||(ball.x+ball.r>900&&ball.speed.speedX>0)){
			ball.speed.speedX=-ball.speed.speedX;
		}
		if((ball.y-ball.r<0&&ball.speed.speedY<0)||(ball.y+ball.r>500&&ball.speed.speedY>0)){
			ball.speed.speedY=-ball.speed.speedY;
		}
	}
	function strIsNum(str){
		return !isNaN(str);
	}
	</script>
	 <script type="text/javascript">
	 //--------------------------------------------此区域定义运行参数------------------------------------------------------
		var GRAVITY=0.667;//引力系数
		var ft=0.00001;//两次运动计算的间隔时间,在该段时间内忽略引力变化
		var drawSpeed=1000;//时间轴运行速度,该值越大,两次绘图间代表的实际时间越长,具体时间为drawSpeed*ft
		var rebound=0.91;//定义碰撞恢复系数	
		var FRICTION=1.96;//定义摩擦系数	
		var openGravity=false;//是否开启引力效果
		var openFriction=false;//是否开启摩擦力效果
		var openRegionDetect=false;//是否开启边界反弹效果
		var runAnimation=false;//是否要运行动画
		var defaultBallColor="blue";//设置小球颜色为红色
		var oriantSize=50,oriantR=8;//设置小球个数与半径
		
		//定义多个刚性引力球
		var oriants=new Array();
		//随机生成多个刚体球
		for(var i=0;i<oriantSize;i++){
			while(true){
				var xy=[Math.random()*900,Math.random()*500];
				var cc=true;
				for(var j=0;oriants[j];j++){
					if((oriants[j].x-xy[0])*(oriants[j].x-xy[0])+(oriants[j].y-xy[1])*(oriants[j].y-xy[1])<((oriantR*oriantR)<<2)){
						cc=false;
						break;;
					}
				}
				if(cc){
					oriants[i]=new OriantObj(xy[0],xy[1],oriantR,Math.random()*10e5,new Vector(0,0));
					break;
				}
				
			}
			
		}
	 </script>
	<style type="text/css">
		.tabhead{
			width:25%;
		}
		.tabinput{
			width:25%;
			margin:0;
			text-align:left;
		}
	</style>
  </head>
  
  <body style="background:black;color:white;">
  <table>
  	<tr>
  		<td width="901" height="501">
  			<canvas id="drawReagon" contenteditable="false" height="500" width="900" style="border: 1px solid white;margin: 0;"></canvas>
  		</td>
		<td valign="top" width="360">
			<table>
				<tr>
					<td class="tabhead">开启引力</td>
					<td class="tabinput" align="left">
						<input type="checkbox" οnclick="javascript:openGravity=this.checked;">
					</td>
					<td class="tabhead">引力系数</td>
					<td class="tabinput">
						<input type="text" id="gravityIndex"  width="50px" style="width:50px;" οnblur="javascript:if(strIsNum(this.value)){GRAVITY=Number(this.value);}else{alert('引力系数必须为数字!');this.value=GRAVITY=0.667};">
					</td>				
				</tr>
				<tr>
					<td class="tabhead">开启摩擦</td>
					<td class="tabinput" align="left">
						<input type="checkbox" οnclick="javascript:openFriction=this.checked;">
					</td>
					<td class="tabhead">摩擦系数</td>
					<td class="tabinput">
						<input type="text" id="frictionIndex" style="width:50px;" 
						οnblur="javascript:if(strIsNum(this.value)){FRICTION=Number(this.value);}else{alert('摩擦系数必须为数字!');this.value=FRICTION=1.96};">
					</td>
				</tr>
				<tr>
					<td class="tabhead">边界反弹</td>
					<td class="tabinput" align="left">
						<input type="checkbox" οnclick="javascript:openRegionDetect=this.checked;">
					</td>
					<td class="tabhead">计算频率</td>
					<td class="tabinput">
						<input type="text" id="drawSpeedIndex" width="50px" style="width:50px;" 
						οnblur="javascript:if(strIsNum(this.value)){drawSpeed=Number(this.value);}else{alert('计算频率必须为数字!');this.value=drawSpeed=1000};if(drawSpeed<1){alert('计算频率过慢!');this.value=drawSpeed=1000}">
					</td>
				</tr>
				
				<tr>
					
					<td class="tabhead" colspan="3" align="right">球体恢复系数</td>
					<td class="tabinput" align="left">
						<input type="text" id="reboundIndex" width="50px" style="width:50px;" 
						οnblur="javascript:if(strIsNum(this.value)&&this.value>=0&&this.value<=1){rebound=Number(this.value);}else{alert('恢复系数必须为0-1之间的数字!');this.value=rebound=0.91};">
					</td>
					
				</tr>
				
				<tr>
					<td colspan="4" align="center">
						<input type="button" id="runAnimationButton" style="width:80px;"
						οnclick="javascript:if(runAnimation){this.value='继续运行';runAnimation=false;}else{this.value='暂停运行';runAnimation=true;};">
					</td>
				</tr>
			</table>
		</td>
  	</tr>
  </table>	
    <script type="text/javascript">	
		function $(id){return document.getElementById(id);}
		var cs=$("drawReagon").getContext("2d");
		cs.fillStyle = defaultBallColor;
		$("gravityIndex").value=GRAVITY;
		$("frictionIndex").value=FRICTION;
		$("drawSpeedIndex").value=drawSpeed;
		$("reboundIndex").value=rebound;
		$("runAnimationButton").value=runAnimation?"暂停运行":"继续运行";
		
		if(!runAnimation){
			for(var i=0;oriants[i];i++){
				cs.beginPath();
				cs.arc(oriants[i].x,oriants[i].y,oriants[i].r,0,Math.PI * 2,false);
				cs.fill(); 
				cs.closePath()
			}
		}
		//计算和绘制界面
		(function funSelf(){
			if(runAnimation){
				cs.clearRect(0,0,900,1000);
				for(var i=0;i<drawSpeed;i++){
					for(var ks=0;oriants[ks];ks++){
						oriants[ks]=caclWhile(oriants,ks,ft);
					}
				}
				for(var i=0;oriants[i];i++){
					cs.beginPath();
					cs.arc(oriants[i].x,oriants[i].y,oriants[i].r,0,Math.PI * 2,false);
					cs.fill(); 
					cs.closePath()
				}
			}	
			window.requestAnimationFrame(funSelf);	
		})();
    </script>
  </body>
</html>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值