読者です 読者をやめる 読者になる 読者になる

乗車日記

自転車ときのこ

ソースコード

ソースコードが1000行を越えてきたので、念のためこちらにも保存。

enchant();
game_size=320;
dt=1/10000.0;
dt2=0.5*dt*dt;
scale=32.0/1.0; //dots/meter
Gravity=-9.8;
num_frame=0;

var EARTH = enchant.Class.create(Sprite,{
    initialize:function(){
        enchant.Sprite.call(this, game_size, game_size);
        this.hl=3.0;
        this.hr=1.0;
        this.w=game_size/scale;
        this.hc=0.2;
        var surface=new Surface(game_size,game_size);      
        surface.context.strokeStyle="SaddleBrown";
        surface.context.fillStyle="SaddleBrown";
        surface.context.lineWidth=2;
        surface.context.beginPath();
        surface.context.moveTo(0,game_size);
        surface.context.lineTo(0,game_size-this.hl*scale);
 
       var h=this.hl-(this.hl-this.hr)/(this.w)*(this.w*0.48);
        surface.context.lineTo(this.w*0.48*scale,game_size-h*scale);
        surface.context.lineTo(this.w*0.48*scale,game_size-(h+this.hc)*scale);
        

        h=this.hl-(this.hl-this.hr)/(this.w)*(this.w*0.52)+this.hc;
        surface.context.lineTo(this.w*0.52*scale,game_size-h*scale);
        surface.context.lineTo(this.w*0.52*scale,game_size-(h-this.hc)*scale);
        
        surface.context.lineTo(this.w*scale,game_size-this.hr*scale);
        surface.context.lineTo(this.w*scale,game_size);
        surface.context.fill();
        this.image=surface;
        game.rootScene.addChild(this);

    },
    change:function(hl,hc,hr){
        this.hl=hl;
        this.hr=hr;
        this.hc=hc;
        var surface=new Surface(game_size,game_size);
        surface.context.strokeStyle="SaddleBrown";
        surface.context.fillStyle="SaddleBrown";
        surface.context.lineWidth=2;
        surface.context.beginPath();
        surface.context.moveTo(0,game_size);
        surface.context.lineTo(0,game_size-this.hl*scale);

        var h=this.hl-(this.hl-this.hr)/(this.w)*(this.w*0.48);
        surface.context.lineTo(this.w*0.48*scale,game_size-h*scale);
        surface.context.lineTo(this.w*0.48*scale,game_size-(h+this.hc)*scale);
        

        h=this.hl-(this.hl-this.hr)/(this.w)*(this.w*0.52)+this.hc;
        surface.context.lineTo(this.w*0.52*scale,game_size-h*scale);
        surface.context.lineTo(this.w*0.52*scale,game_size-(h-this.hc)*scale);
 

        surface.context.lineTo(this.w*scale,game_size-this.hr*scale);
        surface.context.lineTo(this.w*scale,game_size);
        surface.context.fill();
        this.image=surface;
    },
    touch:function(x,y){
        var h=this.hl-(this.hl-this.hr)/(this.w)*x;
        if(this.w*0.48 <=x && x<=this.w*0.52) h+=this.hc;
        if(y<h)  return(true);
        else return(false);
    }
});    

FRAME_NODE=enchant.Class.create({
    initialize: function(parent,x,y,mass){
        this.parent=parent;
        this.x=x/1000;
        this.y=y/1000;
        this.mass=mass;
    },
    force: function() {
        var force={x:0.0, y:0.0, m:0.0};
        if(this.wheel!==undefined) {
            force=this.wheel.force();
            force.y+=Gravity*this.mass;
            force.m+=this.x*Gravity*this.mass;
        }
        else if(this.suspention!==undefined) {
            force=this.suspention.force();
            force.y+=Gravity*this.mass;
            force.m+=this.x*Gravity*this.mass;
        }
        else{
            force.y=Gravity*this.mass;
            force.m=this.x*force.y-this.y*force.x;
        }

        return(force);
    },
    redraw_sprite:function(){
        var X,Y;
        if(this.wheel!==undefined) this.wheel.redraw_sprite();
        else if(this.suspention!==undefined) this.suspention.redraw_sprite();
    },
    remove: function() {
        if(this.wheel!==undefined) this.wheel.remove();
        else if(this.suspention!==undefined) this.suspention.remove();
    }
});

WHEEL_NODE=enchant.Class.create({                      
    initialize: function(parent,radius,mass) {
        this.dt=dt;
        this.dt2=dt2;
        this.parent=parent;
        this.x=parent.x;
        this.y=parent.y;
        this.radius=radius/1000;
        this.I=this.radius*this.radius*mass;
        this.pressure=2.0*1024*100;
        this.width=2.1*25.4/1000*0.5;
        this.theta=0.0;
        this.theta_=0.0;
        this.omega=0.0;
 
        var rad=this.radius*scale;
        var is=Math.floor(rad*2.0*1.1);
        this.is=is;
        this.sprite=new Sprite(is, is);
        var surface=new Surface(is,is);
        surface.context.lineWidth=2;
        surface.context.strokeStyle="Gray";
        surface.context.beginPath();
        surface.context.moveTo(0,is/2);
        surface.context.lineTo(is,is/2);
        surface.context.stroke();
        surface.context.beginPath();
        surface.context.moveTo(is/2,0);
        surface.context.lineTo(is/2,is);
        surface.context.stroke();
    
        surface.context.strokeStyle="black";
        surface.context.beginPath();
        surface.context.arc(is/2,is/2,rad,0.0,Math.PI*2);
        surface.context.stroke();
        
        this.sprite.image=surface;
        var X,Y;
        if(this.parent.parent instanceof BICYCLE) {
            X=this.parent.parent.X;
            Y=this.parent.parent.Y;
        } else if(this.parent.parent.parent instanceof BICYCLE){
            X=this.parent.parent.parent.X;
            Y=this.parent.parent.parent.Y;
        }
        this.sprite.x=-is/2+(this.x+X)*scale;
        this.sprite.y=-is/2-(this.y+Y)*scale+game_size;
        game.rootScene.addChild(this.sprite);
    },

    redraw_sprite:function(){
        var X,Y;
        if(this.parent.parent instanceof BICYCLE) {
            X=this.parent.parent.X;
            Y=this.parent.parent.Y;
        } else if(this.parent.parent.parent instanceof BICYCLE){
            X=this.parent.parent.parent.X;
            Y=this.parent.parent.parent.Y;
        }
        this.sprite.x=-this.is/2+(this.x+X)*scale;
        this.sprite.y=-this.is/2-(this.y+Y)*scale+game_size;
        this.x=this.parent.x;
        this.y=this.parent.y;
        var dtheta=180.0/Math.PI*(this.theta-this.theta_);
        this.sprite.rotate(dtheta);
        this.theta_=this.theta;
    },
    
    remove: function() {
        game.rootScene.removeChild(this.sprite);
    },
    
    force:function(){
        var damp_coef=10.0/5e-4;//単位面積あたりのダンピングレート 10.0s^-1/5cm^-2を仮定
        var force={x:0.0, y:0.0, m:0.0};
        var m=0.0;
        var S=0;
        var X,Y,Vx,Vy,Omega;
        var PI=Math.PI;
        if(this.parent.parent instanceof BICYCLE) {
            X=this.parent.parent.X;
            Y=this.parent.parent.Y;
            Vx=this.parent.parent.Vx;
            Vy=this.parent.parent.Vy;
            Omega=this.parent.parent.Omega;
        } else if(this.parent.parent.parent instanceof BICYCLE){
            X=this.parent.parent.parent.X;
            Y=this.parent.parent.parent.Y;
            Vx=this.parent.parent.parent.Vx;
            Vy=this.parent.parent.parent.Vy;
            Omega=this.parent.parent.parent.Omega;
        }
        
        var px=this.parent.x;
        var py=this.parent.y;    
        var radius=this.radius;
        var wv=this.radius*this.omega;
        var dTheta=PI/50.0;
        var theta1=PI;
        for(var i=0;i<50;i++){
            var theta=dTheta*i;
       //     console.log(theta);
            var cos=Math.cos(theta);
            var sin=Math.sin(theta);
            var x=px-radius*cos+X;
            var y=py-radius*sin+Y;

            if(earth.touch(x,y)===true) {
                theta1=theta-dTheta;
                break;
            }
        }
        var theta2=0;
        for(i=50;i>=0;i--){
            theta=dTheta*i;
            cos=Math.cos(theta);
            sin=Math.sin(theta);
            x=px-radius*cos+X;
            y=py-radius*sin+Y;

            if(earth.touch(x,y)===true) {
                theta2=theta+dTheta;
                break;
            }
        }
        
        dTheta=PI/1000;
        var dS=this.width * radius * dTheta;
        var damp=damp_coef*dS;
        var force_abs=this.pressure*dS;
        L=Math.floor((theta2-theta1)/dTheta);
    
        for(i=0;i<L;i++){
            theta=theta1+dTheta*i;
            cos=Math.cos(theta);
            sin=Math.sin(theta);
            x=px-radius*cos
            y=py-radius*sin;
            var vx=-y*Omega+Vx-wv*sin;
            var vy=x*Omega+Vy+wv*cos;
            x+=X;
            y+=Y;
            if(earth.touch(x,y)===true){
     //       S+=1;
                    var fx=force_abs*cos-vx*damp;//転がり抵抗
                    var fy=force_abs*sin-vy*damp;//転がり抵抗
                    force.x+=fx-wv*damp*sin/10.0;
                    force.y+=fy-wv*damp*cos/10.0;
//                    m+=this.radius*(cos*(fy+wv*damp*cos/10.0)-sin*(fx+wv*damp*sin/10.0));
                m+=radius*(cos*fy-sin*fx);
            }
        }

//        force.y+=Gravity*this.parent.mass;
        this.theta +=this.omega*this.dt;
        if(this.theta>=PI*2) this.theta-=PI*2;
        if(this.theta<=-PI*2) this.theta+=PI*2;
        
        this.omega +=m/this.I*this.dt;
        force.m=this.parent.x*force.y-this.parent.y*force.x;
        
/*         if(Math.floor(this.sprite.age/100)*100===this.sprite.age) {
           //  if(this.parent instanceof SUSPENTION_NODE) {
            console.log("forceY", this.x,S,wv,force.y);
             //}
        }*/
        
        return(force);
    }
});

function abs(r){
    return(Math.sqrt(r.x*r.x+r.y*r.y));
}
function normalize(r){
    l=abs(r);
    var rr={x:r.x/l, y:r.y/l};
    return(rr);
}
function project(r,e){
    var l=mul(r,e);
    var rr={x:e.x*l, y:e.y*l};
    return(rr);
}
function mul(a,b){
    return(a.x*b.x+a.y*b.y);
}
    
    

SUSPENTION_NODE=enchant.Class.create({                      
    initialize: function(parent,x,y,mass) {
        this.parent=parent;
        this.dt=dt;
        this.dt2=dt2;
        x=x/1000-parent.parent.X;
        y=y/1000-parent.parent.Y;
        this.x=x;
        this.y=y;
        
        var e0={x:x-parent.x, y:y-parent.y};

        this.is_x=Math.floor(Math.abs(e0.x)*scale);
        this.is_y=Math.floor(Math.abs(e0.y)*scale);

        this.l0=abs(e0);
        this.vl=0.0;
        this.mass=mass;
        this.shrink=0.0;
        
        this.sprite=new Sprite(this.is_x, this.is_y);
        var surface=new Surface(this.is_x,this.is_y);
        surface.context.lineWidth=2;
        surface.context.strokeStyle="Red";
        surface.context.beginPath();
        surface.context.moveTo(0,0);
        surface.context.lineTo(this.is_x/2,this.is_y/2);
        surface.context.stroke();
        surface.context.beginPath();
        surface.context.strokeStyle="Gray";
        surface.context.lineWidth=4;
        surface.context.moveTo(this.is_x/2,this.is_y/2);
        surface.context.lineTo(this.is_x,this.is_y);
        surface.context.stroke();
        this.sprite.image=surface;
        
        var X,Y;
        X=this.parent.parent.X;
        Y=this.parent.parent.Y;
        this.sprite.x=(this.parent.x+X)*scale;
        this.sprite.y=-(this.parent.y+Y)*scale+game_size;
        this.sprite.originX=0;
        this.sprite.originY=0;//this.is_y;
        game.rootScene.addChild(this.sprite);
    },

    redraw_sprite:function(){
        if(this.wheel!==undefined) this.wheel.redraw_sprite();
        var X,Y;
        X=this.parent.parent.X;
        Y=this.parent.parent.Y;
        this.sprite.x=(this.parent.x+X)*scale;
        this.sprite.y=-(this.parent.y+Y)*scale+game_size;
        var theta=180.0/Math.PI*this.parent.parent.Theta;
        this.sprite.rotation=-theta;
    },
    
    remove:function(){
        if(this.wheel!==undefined) this.wheel.remove();
        game.rootScene.removeChild(this.sprite);
    },
    
    force:function(){
        var stroke=10.0e-2;//ストローク10cm
        var P0=4100.0e2/60*55; //60psi=4100HPaサスペンション圧力 メータ読み つまり実際は+1atm
        var Patm=1024.0e2;//大気圧
        var S=Math.PI*1.5e-2*1.5e-2; //直径3cmのピストン
        var damp_c=250;//250.0;//1m/sの速度の時25kg重のダンピング、コンプレッション側
        var damp_r=500;//500.0;//1m/sの速度の時50kg重のダンピング、リバウンド側
        var rigid=1000.0/1e-3;//底付きしたとき、および伸びきったときのばね定数
        
        
  
        
        var r={x:this.x-this.parent.x, y:this.y-this.parent.y};   
        var l=abs(r);

        var eT=normalize(r);
        var eH={x:-eT.y, y:eT.x};
        
        var T;
        var shrink=this.l0-l;
        this.shrink=shrink;

        if (shrink <= 0.0) {//伸びきったとき
            T=rigid*shrink;
        }else if (shrink >= stroke*0.99) {//底付きしたとき
            T=rigid*(shrink-stroke);
        }else{
            var P=(P0+Patm)*stroke/(stroke-shrink);
            if(this.vl<0.0) T=(P-Patm)*S -damp_c*this.vl;
            else T=(P-Patm)*S -damp_r*this.vl;
        }
        var wheel_force=this.wheel.force();
        wheel_force.y+=Gravity*this.mass;
        var fT=mul(wheel_force,eT);
        var fH=mul(wheel_force,eH);

        var x1={x:this.x, y:this.y};
        var x1T=mul(x1,eT);
        var x1H=mul(x1,eH);
//        if(Math.floor(this.sprite.age/1000)*1000===this.sprite.age) console.log(x1H,x1T);
        
        var force={x:0.0, y:0.0, m:0.0};
        var x2={x:this.parent.x, y:this.parent.y};
        var x2H=mul(x2,eH);
        var Omega=this.parent.parent.Omega;
        var I=this.parent.parent.I;
        var M=this.parent.parent.M;
        var A={x:this.parent.parent.Ax, y:this.parent.parent.Ay};
        var AT=mul(A,eT);
        var AH=mul(A,eH);
        
        var Ao=this.parent.parent.Ao;

        
/*        var tmp1=1.0/this.mass+1.0/M+x1T*x1T/I;
        var tmp2=-fH/this.mass + x2H*x1T/I*T -Omega*Omega*x1H + Omega*this.vl;
        var H=tmp2/tmp1;
        var a=fT/this.mass+(1.0/this.mass+1.0/M+x2H*x1H/I)*T-x1T*x1H/I*H+Omega*Omega*x1T;
*/
        var a=(fT+T)/this.mass-AT/M +Ao*x1H +Omega*Omega*x1T;
        var H=-fH+this.mass*(AH/M +Ao*x1T -Omega*Omega*x1H + Omega*this.vl);        
        x1T+=this.vl*this.dt+a*this.dt2;
        this.vl+=a*this.dt;
        this.x=x1T*eT.x+x1H*eH.x;
        this.y=x1T*eT.y+x1H*eH.y;
        
        force.x=-T*eT.x-H*eH.x;
        force.y=-T*eT.y-H*eH.y;
        force.m=x2H*T-x1T*H;
//        if(Math.floor(this.sprite.age/1000)*1000===this.sprite.age) console.log(Omega,I,M);
 //       if(Math.floor(this.sprite.age/1000)*1000===this.sprite.age) console.log(shrink,T);
//        if(Math.floor(this.sprite.age/100)*100===this.sprite.age) {
          //  var P=(P0+Patm)*stroke/(stroke-shrink);
//            console.log("l, vl, p., d: ",-fH/this.mass,x2H*x1T/I*T,-Omega*Omega*x1H, Omega*this.vl);
//           console.log("Tx,Hx, deff: ",-T*eT.x,-H*eH.x, force.x);
 //           console.log("Tx,Hx, deff: ",force.x,force.y);
   //         console.log("w: ",wheel_force.x,wheel_force.y);
//        }
        return(force);
    }
    

});

HUMAN = enchant.Class.create(Sprite,{
     initialize: function(bicycle){
         
         this.parent=bicycle;
         this.dt=dt;
         this.dt2=dt2;
         this.X=bicycle.frame_node[1].x-0.1+bicycle.X;
         this.Y=bicycle.frame_node[1].y+0.8+bicycle.Y;
         this.Theta=Math.PI*0.24;
         this.Theta_=Math.PI*0.24;
         this.Vx=0.0;
         this.Vy=0.0;
         this.Omega=0.0;
         this.Ax=0.0;
         this.Ay=0.0;
         this.Ao=0.0;
            
         this.x1=0.0;
         this.y1=0.0;
         this.x2=0.8;
         this.y2=0.2;
         this.M=68.0;
         this.I=this.M*this.x2*this.x2/4;
         this.force={x:0.0, y:0.0, m:0.0};

         this.image_size_x=Math.floor(this.x2*scale);
         this.image_size_y=Math.floor(this.y2*scale);
         
         enchant.Sprite.call(this,this.image_size_x +15, this.image_size_y+10);
         var surface=new Surface(this.image_size_x +15, this.image_size_y+10);
         
         surface.context.fillStyle="Peru";
         surface.context.strokeStyle="Peru";
         surface.context.lineWidth=2;
         surface.context.beginPath();
         surface.context.rect(this.x1*scale,this.y1*scale,this.x2*scale,this.y2*scale);
         surface.context.fill();
         surface.context.fillStyle="Black";
         surface.context.beginPath();
         surface.context.arc(this.image_size_x+7,5,5,0,Math.PI*2);
         surface.context.fill();
         this.image=surface;
         this.originX=this.image_size_x/2;
         this.originY=this.image_size_y/2;
         this.x=-this.image_size_x/2+this.X*scale;
         this.y=-this.image_size_y/2+game_size-this.Y*scale;
         this.rotation=-180/Math.PI*this.Theta;
         game.rootScene.addChild(this);

         
         this.arm={length:0.5, x:this.x2, y:(this.y1+this.y2)/2, theta:-Math.PI/2, connect:bicycle.frame_node[5]};
         this.arm.x=this.arm.x-(this.x1+this.x2)/2;
         this.arm.y=this.arm.y-(this.y1+this.y2)/2;
         var cos=Math.cos(this.Theta);
         var sin=Math.sin(this.Theta);
         var x=this.arm.x*cos-this.arm.y*sin;
         var y=this.arm.y*cos+this.arm.x*sin;
         this.arm.x=x;
         this.arm.y=y;
         var dx=this.arm.connect.x+this.parent.X-(this.arm.x+this.X);
         var dy=this.arm.connect.y+this.parent.Y-(this.arm.y+this.Y);        
         this.arm.theta=Math.atan2(dy*cos-dx*sin,dx*cos+dy*sin);         
         this.arm.theta0=this.arm.theta;
         this.arm.length=Math.sqrt(dx*dx+dy*dy);
         this.arm.length0=this.arm.length;
         this.arm.sprite=new Sprite(Math.floor(this.arm.length*scale),3);
         this.arm.sprite.originX=0;
         this.arm.sprite.originY=2;
         surface=new Surface(Math.floor(this.arm.length*scale),3);
         surface.context.fillStyle="black";
         surface.context.strokeStyle="black";
         surface.context.lineWidth=2;
         surface.context.beginPath();
         surface.context.rect(0,0,this.arm.length*scale,3);
         surface.context.fill();
         this.arm.sprite.image=surface;
         this.arm.sprite.x=(this.X+this.arm.x)*scale;
         this.arm.sprite.y=game_size-(this.Y+this.arm.y)*scale;
         this.arm.sprite.rotation=-180/Math.PI*(this.Theta+this.arm.theta);        
         game.rootScene.addChild(this.arm.sprite);

         
         
         this.leg={length:0.75, x:this.x1+0.1, y:(this.y1+this.y2)/2, theta:-0.55*Math.PI, connect:bicycle.frame_node[1]};
         this.leg.x=this.leg.x-(this.x1+this.x2)/2;
         this.leg.y=this.leg.y-(this.y1+this.y2)/2;
         x=this.leg.x*cos-this.leg.y*sin;
         y=this.leg.y*cos+this.leg.x*sin;
         this.leg.x=x;
         this.leg.y=y;
         dx=this.leg.connect.x+this.parent.X-(this.leg.x+this.X);
         dy=this.leg.connect.y+this.parent.Y-(this.leg.y+this.Y);
         this.leg.theta=Math.atan2(dy*cos-dx*sin,dx*cos+dy*sin);
         this.leg.theta0=this.leg.theta;
         this.leg.length=Math.sqrt(dx*dx+dy*dy);
         this.leg.length0=this.leg.length;
         this.leg.sprite=new Sprite(Math.floor(this.leg.length*scale),4);
         this.leg.sprite.originX=0;
         this.leg.sprite.originY=2;
         surface=new Surface(Math.floor(this.leg.length*scale),3);
         surface.context.fillStyle="Peru";
         surface.context.strokeStyle="Peru";
         surface.context.lineWidth=2;
         surface.context.beginPath();
         surface.context.rect(0,0,this.leg.length*scale,3);
         surface.context.fill();
         this.leg.sprite.image=surface;
         this.leg.sprite.x=(this.X+this.leg.x)*scale;
         this.leg.sprite.y=game_size-(this.Y+this.leg.y)*scale;
         this.leg.sprite.rotation=-180/Math.PI*(this.Theta+this.leg.theta);        
         game.rootScene.addChild(this.leg.sprite);
         
         
        },


        
    redraw_sprite:function(){
            this.x=-this.image_size_x/2+this.X*scale;
            this.y=-this.image_size_y/2+game_size-this.Y*scale;
            this.rotation=-180/Math.PI*this.Theta;
        
            this.arm.sprite.scaleX=this.arm.length/this.arm.length0;
            this.arm.sprite.x=(this.X+this.arm.x)*scale;
            this.arm.sprite.y=game_size-(this.Y+this.arm.y)*scale;
            this.arm.sprite.rotation=-180/Math.PI*(this.Theta+this.arm.theta);        
 
            this.leg.sprite.scaleX=this.leg.length/this.leg.length0;
            this.leg.sprite.x=(this.X+this.leg.x)*scale;
            this.leg.sprite.y=game_size-(this.Y+this.leg.y)*scale;
            this.leg.sprite.rotation=-180/Math.PI*(this.Theta+this.leg.theta);        

    },
    new_force:function(){
        this.force.x=0;
        this.force.y=0;
        this.force.m=0;
        var force={x:0, y:0, m:0};
       
        
        var KTA=100/1e-2;
        var KTL=200/1e-2;
        var KM=300/(Math.PI/180*5);
        var dampT=100;
        var dampM=100;
        var dx=this.arm.connect.x+this.parent.X-(this.arm.x+this.X);
        var dy=this.arm.connect.y+this.parent.Y-(this.arm.y+this.Y);        
        var length=Math.sqrt(dx*dx+dy*dy);
        var vt=(length-this.arm.length)/this.dt;
        this.arm.length=length;
        var Theta_=this.Theta+this.arm.theta0;
        var cos=Math.cos(Theta_);
        var sin=Math.sin(Theta_);
 
        var theta=Math.atan2(dy*cos-dx*sin,dx*cos+dy*sin)+this.arm.theta0;
        var vm=(theta-this.arm.theta)/dt;
        this.arm.theta=theta;                 
        var T=(this.arm.length-this.arm.length0)*KTA+vt*dampT;
        var M=(this.arm.theta-this.arm.theta0)*KM+vm*dampM;
        var Tx=T*dx/length;
        var Ty=T*dy/length;
        this.force.x+=Tx-M/length*dy/length;
        this.force.y+=Ty+M/length*dx/length;
        this.force.m+=M+this.arm.x*Ty-this.arm.y*Tx;
        var fx=-Tx-(-M)/length*dy/length;
        force.x+=fx;
        var fy=-Ty+(-M)/length*dx/length;
        force.y+=fy;
        force.m+=this.arm.connect.x*fy-this.arm.connect.y*fx;
   //     console.log("T",(this.arm.length-this.arm.length0)*KT,-vt*dampT);
   //     console.log("M",(this.arm.theta-this.arm.theta0)*KM,-vm*dampM);
    //    console.log("M",this.arm.theta,this.arm.theta0,this.arm.theta-this.arm.theta0);
      //  console.log("arm",Tx,Ty,T,dx,dy,length);
        
        
        
        
        dx=this.leg.connect.x+this.parent.X-(this.leg.x+this.X);
        dy=this.leg.connect.y+this.parent.Y-(this.leg.y+this.Y);
        length=Math.sqrt(dx*dx+dy*dy);
        vt=(length-this.leg.length)/this.dt;
        this.leg.length=length;
        Theta_=this.Theta+this.leg.theta0;
        cos=Math.cos(Theta_);
        sin=Math.sin(Theta_);

        theta=Math.atan2(dy*cos-dx*sin,dx*cos+dy*sin)+this.leg.theta0;
        vm=(theta-this.leg.theta)/dt;
        this.leg.theta=theta;                 
        T=(this.leg.length-this.leg.length0)*KTL+vt*dampT;
        M=(this.leg.theta-this.leg.theta0)*KM+vm*dampM;
        Tx=T*dx/length;
        Ty=T*dy/length;
    //    console.log("leg",Tx,Ty,T,dx,dy,length);
        this.force.m+=M+this.leg.x*Ty-this.leg.y*Tx;
        this.force.x+=Tx-M/length*dy/length;
        this.force.y+=Ty+M/length*dx/length;
        fx=-Tx-(-M)/length*dy/length;
        force.x+=fx;
        fy=-Ty+(-M)/length*dx/length;
        force.y+=fy;
        force.m+=this.leg.connect.x*fy-this.leg.connect.y*fx;
        //console.log("F", this.force);
        this.force.y+=this.M*Gravity;
        //console.log(this.M*Gravity);
     //   console.log(this.force);
        return(force);
    },
    new_accel:function(){
        this.Ax=this.force.x/this.M;
        this.Ay=this.force.y/this.M;
        this.Ao=this.force.m/this.I;
  //      console.log("A", this.Ax,this.Ay,this.Ao);
    },
    new_velocity:function(){
        var dt=this.dt;
        this.Vx+=this.Ax*dt;
        this.Vy+=this.Ay*dt;
        this.Omega+=this.Ao*dt;
   //     console.log("V ", this.Vx,this.Vy,this.Omega);
    },
    new_position:function(){
        var dt=this.dt;
        var dt2=this.dt2;
        this.X+=this.Vx*dt+this.Ax*dt2;
        this.Y+=this.Vy*dt+this.Ay*dt2;
        var dTheta=this.Omega*dt+this.Ao*dt2;
        this.Theta+=dTheta;
        var cos=Math.cos(dTheta);
        var sin=Math.sin(dTheta);   
        
        var x_=this.arm.x*cos-this.arm.y*sin;
        var y_=this.arm.y*cos+this.arm.x*sin;
        this.arm.x=x_;
        this.arm.y=y_;

        x_=this.leg.x*cos-this.leg.y*sin;
        y_=this.leg.y*cos+this.leg.x*sin;
        this.leg.x=x_;
        this.leg.y=y_;
     //   console.log("X", this.X,this.Y,this.Theta);

    }
        /*    new_position:function(X,Y,dTheta){
         var cos=Math.cos(dTheta);
         var sin=Math.sin(dTheta);
        var dx=this.X-X;
        var dy=this.Y-Y;
        this.X=this.parent.X+dx*cos-dy*sin;
        this.Y=this.parent.Y+dy*cos+dx*sin;
        this.Theta+=dTheta;
         var x_=this.arm.x*cos-this.arm.y*sin;
         var y_=this.arm.y*cos+this.arm.x*sin;
         this.arm.x=x_;
         this.arm.y=y_;
       
         dx=this.arm.connect.x+this.parent.X-(this.arm.x+this.X);
         dy=this.arm.connect.y+this.parent.Y-(this.arm.y+this.Y);        
         this.arm.theta=Math.atan2(dy,dx)-this.Theta;         
         this.arm.length=Math.sqrt(dx*dx+dy*dy);

        
         x_=this.leg.x*cos-this.leg.y*sin;
         y_=this.leg.y*cos+this.leg.x*sin;
         this.leg.x=x_;
         this.leg.y=y_;
         dx=this.leg.connect.x+this.parent.X-(this.leg.x+this.X);
         dy=this.leg.connect.y+this.parent.Y-(this.leg.y+this.Y);
         this.leg.theta=Math.atan2(dy,dx)-this.Theta;
         this.leg.length=Math.sqrt(dx*dx+dy*dy);        
         }*/
        
});




BICYCLE = enchant.Class.create(Sprite,{
        initialize: function(){
            this.ID=num_frame;
            num_frame++;
            this.dt=dt;
            this.dt2=dt2;

            this.Vx=0.0;
            this.Vy=0.0;
            this.Ax=0.0;
            this.Ay=0.0;
            this.Theta=0.0;
            this.Theta_=0.0;
            this.Omega=0.0;
            this.Ao=0.0;

            this.frame_node=Array(this.num_node);    
            this.frame_node[0]=new FRAME_NODE(this,0.0, 0.0,  3.0);//"RearEnd" x,y, mass
            this.frame_node[1]=new FRAME_NODE(this,425, -20,  1.8);//BB
            this.frame_node[2]=new FRAME_NODE(this,302, 333,  0.5);//"TopChainSeat"
            this.frame_node[3]=new FRAME_NODE(this,814, 582,  1.0);//Head
            this.frame_node[4]=new FRAME_NODE(this,210, 582,  0.4);//Saddle
            this.frame_node[5]=new FRAME_NODE(this,924, 600,  0.8);//Handle
    //        this.frame_node[6]=new FRAME_NODE(this,1085,0.0,  2.8);//ForkEnd

            this.set_parameters();
            this.frame_node[0].wheel=new WHEEL_NODE(this.frame_node[0],315,1.5);
      //      this.frame_node[6].wheel=new WHEEL_NODE(this.frame_node[6],315,1.5);

            this.frame_node[3].suspention=new SUSPENTION_NODE(this.frame_node[3],1085, 0.0,  2.8);     
            this.frame_node[3].suspention.wheel=new WHEEL_NODE(this.frame_node[3].suspention,315,1.5);

            this.X=0.8;
            this.Y=earth.hl+1.0;
            this.human=new HUMAN(this);

            this.set_sprite();
            this.redraw_sprite();
            this.set_speed_label();            
            game.rootScene.addChild(this);
        },
    
    onenterframe:function(){
        var D=Math.floor((1.0/100)/dt);
        for(var i=0;i<D;i++){
            var force=this.new_force();
            this.new_accel(force);
            this.new_position();
            this.new_velocity();
        }
        if(this.X*scale > game_size+this.image_size_x/2 && this.Vx>0.0){
            this.X =-this.image_size_x/2/scale;
            this.Y += (earth.hl-earth.hr)*1.2;
            this.human.X =-this.image_size_x/2/scale;
            this.human.Y += (earth.hl-earth.hr)*1.2;
        }
        if(this.X*scale < -this.image_size_x/2 && this.Vx<0.0){
            this.X =(game_size+this.image_size_x/2)/scale;
            this.Y -= (earth.hl-earth.hr)*1.2;
            this.human.X =(game_size+this.image_size_x/2)/scale;
            this.human.Y -= (earth.hl-earth.hr)*1.2;
        }
        this.redraw_sprite();
        if(Math.floor(this.age/5)*5===this.age)        this.new_speed();
    },

    ontouchstart: function(){
        for(var i=0;i<this.frame_node.length;i++)  {
            this.frame_node[i].remove();
        }
        game.rootScene.removeChild(this.speed);                      
        game.rootScene.removeChild(this);          
    },

    set_parameters: function(){
        this.X=0.0;
        this.Y=0.0;
        this.M=0.0;
        this.I=0.0;
            for(var i=0;i<this.frame_node.length;i++)  {
                this.X+=this.frame_node[i].x*this.frame_node[i].mass;
                this.Y+=this.frame_node[i].y*this.frame_node[i].mass;
                this.M+=this.frame_node[i].mass;
            }
            this.X/=this.M;
            this.Y/=this.M;

            for(i=0;i<this.frame_node.length;i++)  {
                var dx=this.frame_node[i].x-this.X;
                var dy=this.frame_node[i].y-this.Y;
                this.I+=(dx*dx+dy*dy)*this.frame_node[i].mass;
                this.frame_node[i].x=dx;
                this.frame_node[i].y=dy;
            }
    },
    
    set_sprite: function(){
            var x_size=0.0,y_size=0.0;
            for(var i=0;i<this.frame_node.length;i++)  {
                var absx=Math.abs(this.frame_node[i].x);
                if(absx>x_size) x_size=absx;
                var absy=Math.abs(this.frame_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);

            var surface=new Surface(this.image_size_x,this.image_size_y);
            surface.context.fillStyle="Blue";
            surface.context.strokeStyle="Blue";
            surface.context.lineWidth=2;
            
            for(i=0;i<this.frame_node.length;i++)  {
                var x=this.in_sprite_x(i);
                var y=this.in_sprite_y(i);
                surface.context.beginPath();
                surface.context.arc(x,y,1.5,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;
    },
    redraw_sprite:function(){
            this.x=-this.image_size_x/2+this.X*scale;
            this.y=-this.image_size_y/2+game_size-this.Y*scale;
            this.rotation=-180/Math.PI*this.Theta;
            for(var i=0;i<this.frame_node.length;i++)  {
                this.frame_node[i].redraw_sprite();
            }
        if(this.human !== undefined) this.human.redraw_sprite();
    },
    
    set_speed_label:function(){
            this.speed=new Label();
            this.speed.x=0;
            this.speed.y=16+5+14*(this.ID);
            this.speed.color="black";
            this.speed.font="12px areal";
            this.speed.width=120;
            game.rootScene.addChild(this.speed);
    },
    new_speed:function(){
            var value=Math.floor(Math.sqrt(this.Vx*this.Vx+this.Vy*this.Vy)*3600/1000);
        if(value <10) value="0"+value; 
        //            this.speed.text="ID"+this.ID+": "+value+"(km/s), "+Math.floor(this.frame_node[0].wheel.omega*10/Math.PI/2)/10+"(rps)";
            var shrink=this.frame_node[3].suspention.shrink*100;
            if(shrink<0.0) shrink=0.0;
            this.speed.text="ID"+this.ID+": "+value+"(km/s), "+shrink.toFixed(1)+"(cm)";
    }, 
  
        
    connect: function(i,j,surf){
            var x1=this.in_sprite_x(i);
            var y1=this.in_sprite_y(i);
            var x2=this.in_sprite_x(j);
            var y2=this.in_sprite_y(j);
            surf.context.beginPath();
            surf.context.moveTo(x1,y1);
            surf.context.lineTo(x2,y2);
            surf.context.stroke();
    },
    
    in_sprite_x: function(i){
        return(this.frame_node[i].x*scale+this.image_size_x/2);
    },
    
    in_sprite_y: function(i){
        return(this.image_size_y/2-this.frame_node[i].y*scale);
    },


    new_force: function(){
        var force={x:0.0, y:0.0, m:0.0};
        var L=this.frame_node.length;
        for(var i=0;i<L;i++){
            var force_=this.frame_node[i].force();
            force.x+=force_.x;
            force.y+=force_.y;
            force.m+=force_.m;
        }
        force_=this.human.new_force();
        force.x+=force_.x;
        force.y+=force_.y;
        force.m+=force_.m;
        return(force);
    },
    
    new_accel: function(force){
        this.Ax=force.x/this.M;
        this.Ay=force.y/this.M;
        this.Ao=force.m/this.I;
        this.human.new_accel();
    },
    
    new_velocity: function(){
        var dt=this.dt;
        this.Vx+=this.Ax*dt;
        this.Vy+=this.Ay*dt;
        this.Omega+=this.Ao*dt;
        this.human.new_velocity();
    },
    
    new_position: function(){
        var dt=this.dt;
        var dt2=this.dt2;
        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);
        var L=this.frame_node.length;
        for(var i=0;i<L;i++){
            var dx=this.frame_node[i].x;
            var dy=this.frame_node[i].y;
            this.frame_node[i].x=dx*cos-dy*sin;
            this.frame_node[i].y=dy*cos+dx*sin;
            if(this.frame_node[i].suspention !== undefined){
                dx=this.frame_node[i].suspention.x;
                dy=this.frame_node[i].suspention.y;
                this.frame_node[i].suspention.x=dx*cos-dy*sin;
                this.frame_node[i].suspention.y=dy*cos+dx*sin;
            }

        }
        this.human.new_position();
        this.Theta+=dTheta;
    } 
});


var create_slope_hl_label=function(){
    var button_width=30;
    var textbox_width=15;
    var x0=game_size-button_width-textbox_width;
    var height=10;
    var y0=2+height*1.5*0;
    var label=new Label("スロープ左高さ(m)");//スロープ高さ用テキストボックスを作成
    label.x=x0-label._boundWidth+20;
    label.y=y0+(height-label._boundHeight)/2;
    label.font='10px helvetica';
    label.color='black';
//    label.textAlign='left';
         game.rootScene.addChild(label);
        
        var textbox = new InputTextBox();
    textbox.x = x0;
    textbox.y = y0;
    textbox.width=textbox_width;
    textbox.height=height;
         textbox.placeholder=(earth.hl.toString(10));
//    textbox.backgroundColor='LightGray';
        game.rootScene.addChild(textbox);

        var button=new Button('set');
    button.x=x0+textbox.width;
    button.y=y0;
    button.width=button_width;
    button.height=height;
        button.ontouchend = function() {
            earth.change(Number(textbox.value),earth.hc,earth.hr);            
        }; 
        game.rootScene.addChild(button);
};

var create_slope_hr_label=function(){
    var button_width=30;
    var textbox_width=15;
    var x0=game_size-button_width-textbox_width;
    var height=10;
    var y0=2+height*1.5;
    var label=new Label("スロープ右高さ(m)");//スロープ高さ用テキストボックスを作成
    label.x=x0-label._boundWidth+20;
    label.y=y0+(height-label._boundHeight)/2;
    label.font='10px helvetica';
    label.color='black';
//    label.textAlign='left';
    game.rootScene.addChild(label);
        
    var textbox = new InputTextBox();
    textbox.x = x0;
    textbox.y = y0;
    textbox.width=textbox_width;
    textbox.height=height;
    textbox.placeholder=(earth.hr.toString(10));
//    textbox.backgroundColor='LightGray';
    game.rootScene.addChild(textbox);

    var button=new Button('set');
    button.x=x0+textbox.width;
    button.y=y0;
    button.width=button_width;
    button.height=height;
    button.ontouchend = function() {
        earth.change(earth.hl,earth.hc,Number(textbox.value));            
    }; 
    game.rootScene.addChild(button);
};

var create_slope_hc_label=function(){
    var button_width=30;
    var textbox_width=20;
    var x0=game_size-button_width-textbox_width;
    var height=10;
    var y0=2+height*1.5*2;
        var label=new Label("障害物高さ(m)");
    label.x=x0-label._boundWidth+15;
    label.y=y0+(height-label._boundHeight)/2;
    label.font='10px helvetica';
    label.color='black';
//    label.textAlign='left';
        game.rootScene.addChild(label);
        
        var textbox = new InputTextBox();
    textbox.x = x0;
    textbox.y = y0;
    textbox.width=textbox_width;
    textbox.height=height;
        textbox.placeholder=(earth.hc.toString(10));
//    textbox.backgroundColor='LightGray';
        game.rootScene.addChild(textbox);

        var button=new Button('set');
    button.x=x0+textbox.width;
    button.y=y0;
    button.width=button_width;
    button.height=height;
        button.ontouchend = function() {
            earth.change(earth.hl,Number(textbox.value),earth.hr);            
        }; 
        game.rootScene.addChild(button);
};

var create_addbicycle_button=function(){
        var height=10;

        var button=new Button('自転車追加');
        button.x=250;
        button.y=2+height*1.5*3;
        button.height=height;
        button.width=70;
        button.ontouchend = function() {
            new BICYCLE();
        }; 
        game.rootScene.addChild(button);        
};


var create_time_display=function(a){
        var time=new Label();
        time.x=2;
        time.y=2;
        time.color="black";
        time.font="16px areal";
        time.width=120;
        time.addEventListener("enterframe", function(){
            time.text="時刻= "+Math.floor(time.age/game.fps)+" (s)";
        });
        game.rootScene.addChild(time);
};

window.onload = function(){
    game = new Core(game_size, game_size);
    game.fps = 100;
    game.onload = function(){
        game.rootScene.backgroundColor = "WhiteSmoke";
        earth=new EARTH();            
        create_time_display(earth);        
        enchant.widget._env.buttonFont='10px helvetica';
        create_slope_hl_label();
        create_slope_hr_label();
        create_slope_hc_label();
        create_addbicycle_button();
    };
    game.start();
};