とりあえず、フルリジッドだけど動くようになったけど、同時にだいぶんコードが長くなってきた。
http://jsdo.it/tasanokona/s7Q2/(Chromeだとスムースに動きますが、それ以外のブラウザでは遅い、あるいは全く動かないかもしれません。)
ここまでは完全剛体なので、簡単だが、ここから後が難しい。サスペンションがストローク方向には動くが、それに垂直な方向には固定されているのをどう矛盾なく、かつ計算量を増やさずに再現できるか。もうしばらくかかりそうだ。
// forked from tasanokona's "太陽系の形成" http://jsdo.it/tasanokona/vJu0 // forked from tasanokona's "太陽系の形成" http://jsdo.it/tasanokona/vJu0 // forked from tasanokona's "銀河の形成" http://jsdo.it/tasanokona/oQgm // forked from tasanokona's "2013-05-23 熊たたき" http://jsdo.it/tasanokona/ymX2 enchant(); game_size=1000; dt=1/10000.0; dt2=0.5*dt*dt; scale=100.0/1.0; //dots/meter Gravity=-9.8; earth={ height: 2.0, touch:function(x,y){ var h=this.height-this.height/(game_size/scale)*x; if(y<h) return(true); else return(false); }, initialize:function(){ this.sprite=new Sprite(game_size, game_size); var surface=new Surface(game_size,game_size); surface.context.strokeStyle="blue"; surface.context.lineWidth=2; surface.context.beginPath(); surface.context.moveTo(0,game_size-this.height*scale); surface.context.lineTo(game_size,game_size); surface.context.stroke(); this.sprite.image=surface; game.rootScene.addChild(this.sprite); } }; function NODE(type_,x_,y_,mass_){ this.type=type_; this.x=x_/1000; this.y=y_/1000; this.mass=mass_; } NODE.prototype.set_wheel=function(r_){ this.wheel={radius:r_/1000, pressure:2.0*1024*100, width:2.1*25.4/1000*0.5, x:this.x, y:this.y}; var rad=this.wheel.radius*scale; var is=Math.floor(rad*2.0*1.1); this.wheel.sprite=new Sprite(is, is); var surface=new Surface(is,is); surface.context.fillStyle="red"; surface.context.strokeStyle="red"; surface.context.lineWidth=2; surface.context.beginPath(); surface.context.arc(is/2,is/2,rad,0.0,Math.PI*2); surface.context.stroke(); this.wheel.sprite.image=surface; this.wheel.sprite.x=-is/2+this.x*scale; this.wheel.sprite.y=-is/2-this.y*scale+game_size; this.is=is; game.rootScene.addChild(this.wheel.sprite); }; NODE.prototype.reposition_wheel=function(){ if(this.wheel!==undefined) { this.wheel.sprite.x=-this.is/2+this.x*scale; this.wheel.sprite.y=-this.is/2-this.y*scale+game_size; this.wheel.x=this.x; this.wheel.y=this.y; // console.log(x,y); } }; var FRAME = enchant.Class.create(Sprite,{ initialize: function(){ this.num_node=7; this.X=0.0; this.Y=0.0; this.M=0.0; this.Vx=0.0; this.Vy=0.0; this.Ax=0.0; this.Ay=0.0; this.I=0.0; this.Theta=0.0; this.Omega=0.0; this.Ao=0.0; this.node=Array(this.num_node); this.node[0]=new NODE("RearEnd",0, 0 ,3.0); this.node[1]=new NODE("BB",425, -20 ,1.8); this.node[2]=new NODE("TopChainSeat",302, 333 ,0.5); this.node[3]=new NODE("Head",814, 582 ,1.0); this.node[4]=new NODE("Saddle",210, 582 ,0.4); this.node[5]=new NODE("ForkEnd",1085,0 ,2.8); this.node[6]=new NODE("Handle",924,600, 0.8); for(var i=0;i<this.num_node;i++) { this.X+=this.node[i].x*this.node[i].mass; this.Y+=this.node[i].y*this.node[i].mass; this.M+=this.node[i].mass; } this.X/=this.M; this.Y/=this.M; // console.log("COM",this.X,this.Y); for(i=0;i<this.num_node;i++) { var dx=this.node[i].x-this.X; var dy=this.node[i].y-this.Y; this.I+=(dx*dx+dy*dy)*this.node[i].mass; this.node[i].x=dx; this.node[i].y=dy; } var x_size=0.0,y_size=0.0; for(i=0;i<this.num_node;i++) { var absx=Math.abs(this.node[i].x); if(absx>x_size) x_size=absx; var absy=Math.abs(this.node[i].y); if(absy>y_size) y_size=absy; } this.image_size_x=Math.floor(2*x_size*scale*1.2); this.image_size_y=Math.floor(2*y_size*scale*1.2); enchant.Sprite.call(this, this.image_size_x, this.image_size_y); this.X=0.8; this.Y=3.0; this.x=-this.image_size_x/2+this.X*scale; this.y=-this.image_size_y/2+game_size-this.Y*scale; for(i=0;i<this.num_node;i++) { this.node[i].x+=this.X; this.node[i].y+=this.Y; } var surface=new Surface(this.image_size_x,this.image_size_y); surface.context.fillStyle="red"; surface.context.strokeStyle="red"; surface.context.lineWidth=2; for(i=0;i<this.num_node;i++) { var x=(this.node[i].x-this.X)*scale+this.image_size_x/2; var y=this.image_size_y/2-(this.node[i].y-this.Y)*scale; surface.context.beginPath(); surface.context.arc(x,y,3,0,Math.PI*2); surface.context.fill(); } this.connect(0,1,surface); this.connect(0,2,surface); this.connect(1,2,surface); this.connect(1,3,surface); this.connect(2,3,surface); this.connect(2,4,surface); this.connect(3,5,surface); this.connect(3,6,surface); this.image=surface; this.node[0].set_wheel(315); this.node[5].set_wheel(315); earth.initialize(); this.addEventListener("enterframe", function(){ for(i=0;i<100;i++){ var force=this.new_force(); this.new_accel(force); this.new_position(); this.new_velosity(); } this.x=-this.image_size_x/2+this.X*scale; this.y=-this.image_size_y/2+game_size-this.Y*scale; this.node[0].reposition_wheel(); this.node[5].reposition_wheel(); }); game.rootScene.addChild(this); }, connect: function(i,j,surf){ var x1=(this.node[i].x-this.X)*scale+this.image_size_x/2; var y1=this.image_size_y/2-(this.node[i].y-this.Y)*scale; var x2=(this.node[j].x-this.X)*scale+this.image_size_x/2; var y2=this.image_size_y/2-(this.node[j].y-this.Y)*scale; surf.context.beginPath(); surf.context.moveTo(x1,y1); surf.context.lineTo(x2,y2); surf.context.stroke(); }, new_force: function(){ var force={x:0.0, y:0.0, m:0.0}; for(var i=0;i<this.num_node;i++){ var node=this.node[i]; var force_={x:0.0, y:0.0, m:0.0}; var dx_node=this.X-node.x; var dy_node=this.Y-node.y; if(this.node[i].wheel===undefined){ force_.x=0.0; force_.y=Gravity*node.mass; } else { var dTheta=Math.PI/1000; for(var j=0;j<=1000;j++){ var theta=dTheta*j; var cos=Math.cos(theta); var sin=Math.sin(theta); var x=node.x-node.wheel.radius*Math.cos(theta); var y=node.y-node.wheel.radius*Math.sin(theta); var vx=-(y-this.Y)*this.Omega+this.Vx; var vy=(x-this.X)*this.Omega+this.Vy; if(earth.touch(x,y)===true){ var dS=node.wheel.width * node.wheel.radius * dTheta; var force_abs=node.wheel.pressure*dS; var damp=(vx*(-cos)+vy*(-sin))/0.00001*dS; force_.x+=(force_abs+damp)*cos; force_.y+=(force_abs+damp)*sin; } } force.y+=Gravity*this.node[i].mass; } force.x+=force_.x; force.y+=force_.y; force.m+=-dx_node*force_.y+dy_node*force_.x; } return(force); }, new_accel: function(force){ this.Ax=force.x/this.M; this.Ay=force.y/this.M; this.Ao=force.m/this.I; }, new_velosity: function(){ this.Vx+=this.Ax*dt; this.Vy+=this.Ay*dt; this.Omega+=this.Ao*dt; }, new_position: function(){ var X=this.X; var Y=this.Y; this.X+=this.Vx*dt+this.Ax*dt2; this.Y+=this.Vy*dt+this.Ay*dt2; var dTheta=this.Omega*dt+this.Ao*dt2; var cos=Math.cos(dTheta); var sin=Math.sin(dTheta); for(var i=0;i<this.num_node;i++){ var dx=this.node[i].x-X; var dy=this.node[i].y-Y; this.node[i].x=dx*cos-dy*sin+this.X; this.node[i].y=dy*cos+dx*sin+this.Y; } this.Theta+=dTheta; this.rotate(-180/Math.PI*dTheta); } }); window.onload = function(){ game = new Core(game_size, game_size); game.fps = 100; game.onload = function(){ game.rootScene.backgroundColor = "black"; var frame=new FRAME(); }; game.start(); };