Beautifl - Flash Gallery

Thumbnail : [Box2d] Mechanical Clock (機械式時計)
[Box2d] Mechanical Clock (機械式時計)
tatsuya 2010-03-24 MIT License

再生するにはFlash Playerが必要です。デスクトップのブラウザでご覧ください。

/* Mechanical Clock with Box2D(機械式時計)
 * 
 * 最初はぜんまいが巻かれていない状態です。
 * 左上のボタンをクリックすると最初の数秒間リューズにトルクを加え、ぜんまいが巻かれ、時計が動き始めます。
 * 途中から、30秒間で秒針(四番車)が何度回転するかを計測します。
 * 
 * http://flashjp.com/mecclock
 * 
 * */


package {
	import flash.display.*;
	import flash.events.*;
	import flash.text.*;
	[SWF(width=465, height=465, backgroundColor=0xFFFFFF, frameRate=50)]

	public class clockfl extends Sprite {
		
		public var sheets:Sprite = new Sprite();//全体
		public var b2Sheet:Sprite = new Sprite();//box2Dのデバッグを描く
		public var backgroundSheet:Sprite = new Sprite();//背景用画像を描く
		public var textSheet:Sprite = new Sprite();//説明文を書く
		public var clock:Clock;//Clockクラスのオブジェクト
		public var fStart:Boolean = false;//実行時はtrue
		public var appTorqueInfo:Sprite;//トルクを加えている時の矢印など
		public var evaluTF:TextField;//計測結果表示用テキストフィールド
		
		public function clockfl() {
			
			if (stage) {
				init();
			} else {
				addEventListener(Event.ADDED_TO_STAGE, init);
			}
		}
		
		private function init():void {
			
			stage.addChild(sheets);
			sheets.x = 465 / 2;
			sheets.y = 465 / 2;
			
			sheets.addChild(backgroundSheet);
			sheets.addChild(b2Sheet);
			sheets.addChild(textSheet);
			
			drawBackground();//背景を描く
			
			drawTexts();//説明文などを描く
			
			setButtons();//ボタンをセットする
			
			main();//メインの実行部分
			
		}
		
		private function drawBackground():void {//背景を描く
			
			//var b2bg:Sprite = new Sprite();
			var b2bg:Shape = new Shape();
			b2bg.graphics.lineStyle(0, 0x555555);
			b2bg.graphics.beginFill(0x666666);
			b2bg.graphics.drawRoundRect(-465/2,-465/2,465,465,10);
			b2bg.graphics.endFill();
			
			b2bg.graphics.lineStyle(0, 0xAAAAAA);
			b2bg.graphics.moveTo( -465 / 2, 0);
			b2bg.graphics.lineTo( 465 / 2, 0);
			b2bg.graphics.moveTo(0, -465 / 2);
			b2bg.graphics.lineTo( 0, 465 / 2);
			b2bg.graphics.drawCircle(0, 0, 440 / 2);
			backgroundSheet.addChild(b2bg);
			
			
			drawCrossLine( -8.371 * 10 , 10.2764 * 10 , 5);//アンクルの回転中心を描く
			drawCrossLine( -11.971 * 10 , 7.35 * 10 , 5);//テンプの回転中心を描く
			
			function drawCrossLine(x:Number , y:Number , length:Number):void {
				var lines:Shape = new Shape();
				var length:Number = 5;
				lines.graphics.lineStyle(0, 0xffffff, 0.3);
				lines.graphics.moveTo(x - length, y);
				lines.graphics.lineTo(x + length, y);
				lines.graphics.moveTo(x, y - length);
				lines.graphics.lineTo(x, y + length);
				backgroundSheet.addChild(lines);
			}
			
			
			
		}
		
		private function setButtons():void {//ボタンをセットする
			
			var button1:Sprite = new Sprite();//スタート|ストップボタン
			button1.graphics.lineStyle(0, 0xffffff);
			button1.graphics.beginFill(0xeeeeee);
			button1.graphics.drawRoundRect(0,0, 100,20 , 7);
			button1.graphics.endFill();
			button1.x = -225;
			button1.y = -225;
			button1.buttonMode = true;
			button1.useHandCursor = true;
			button1.addEventListener(MouseEvent.CLICK , bt1ClickHandler);
			backgroundSheet.addChild(button1);
			
			
			var bt1Text:TextField = new TextField();
			bt1Text.width = 100;
			bt1Text.height = 20;
			bt1Text.mouseEnabled = false;
			button1.addChild(bt1Text);
			
			var bt1TF:TextFormat = new TextFormat();
			bt1TF.font ="verdana";
			bt1TF.size = 12;
			bt1TF.align = "center";
			bt1Text.defaultTextFormat = bt1TF;
			bt1Text.text = "click to start";
			
			function bt1ClickHandler(e:MouseEvent):void {//ボタンがクリックされた時の処理
				
				fStart = !fStart;//状態を切り替える
				
				if (fStart) {
					bt1Text.text = "click to stop";
				}else {
					bt1Text.text = "click to start";
				}
			}
		}
		
		private function drawTexts():void {//説明文などを描く
			
			//各部品の名称を書く
			var tfmt:TextFormat = new TextFormat();
			tfmt.font ="verdana";
			tfmt.size = 12;
			tfmt.color = 0xffffff;
			
			setText(145,-135,"リューズ");
			setText(135, -90, "丸穴車");
			setText(-60, -180, "角穴車");
			setText( -180, -115, "香箱車");
			setText(65, 5, "二番車(分針)");
			setText(60, 60, "三番車");
			setText(55, 120, "四番車(秒針)");
			setText( -90, 155, "ガンギ車");
			setText( -140, 120, "アンクル");
			setText( -180, 60, "テンプ");
			
			function setText(x:Number , y:Number , text:String):void {
				var tf:TextField = new TextField();
				tf.x = x;
				tf.y = y;
				tf.text = text;
				tf.mouseEnabled = false;
				tf.setTextFormat(tfmt);
				textSheet.addChild(tf);
			}
			
			
			
			//now applying torque!の画像作成
			appTorqueInfo = new Sprite();
			appTorqueInfo.visible = false;//デフォルトは表示しない
			textSheet.addChild(appTorqueInfo);
			
			var color:Number = 0xffe4e1;
			var arrow:Shape = new Shape();
			var r:Number = 25;
			arrow.graphics.lineStyle(3, color, 0.99);
			arrow.graphics.moveTo(r/2 , -r*1.732/2);
			arrow.graphics.curveTo(0, -r * 2 / 1.732 , -r / 2, -r * 1.732 / 2);
			arrow.graphics.lineTo(-r/2+7 , -r*1.732/2-7);
			appTorqueInfo.addChild(arrow);
			appTorqueInfo.x = 12.97 * 10;
			appTorqueInfo.y = -12.64 * 10;
			
			var textInfo:TextField = new TextField();
			textInfo.text = "now\napplying\ntorque!"
			textInfo.x = -75;
			textInfo.y = -75;
			textInfo.width = 150;
			textInfo.height = 66;
			textInfo.mouseEnabled = false;
			appTorqueInfo.addChild(textInfo);
			
			var textInfoFmt:TextFormat = new TextFormat();
			textInfoFmt.font ="verdana";
			textInfoFmt.size = 12;
			textInfoFmt.color = color;
			textInfoFmt.align = "center";
			textInfoFmt.bold = true;
			textInfo.setTextFormat(textInfoFmt);
			
			
			
			//計測用テキストフィールドの設定
			evaluTF = new TextField();
			evaluTF.x = -230;
			evaluTF.y = 210;
			evaluTF.width = 400;
			evaluTF.defaultTextFormat = tfmt;
			textSheet.addChild(evaluTF);
			
		}
		
		private function main():void {//メインの実行部分
			
			//Clockクラスのオブジェクト作成
			clock = new Clock(b2Sheet);
			
			//初期画面用に実行する(これがないと寂しいので)
			clock.makeStep();
			
			var count:uint = 0;
			var sttAngle:Number;//秒針の角度(計測用)
			var endAngle:Number;//秒針の角度(計測用)
			
			// 毎フレームの処理のリスナー設定
			addEventListener(Event.ENTER_FRAME , EFHandler);
			
			function EFHandler(e:Event):void {// 毎フレームの処理
				
				if (fStart){//実行時のみ処理する
					clock.makeStep();
					
					count++;
					if (count < 250) {//最初の250ステップだけ、リューズを巻き上げる
						clock.wind();
						appTorqueInfo.visible = true;
					}else{
						appTorqueInfo.visible = false;
					}
					
					//精度を測定してみる
					if (count < 1000) {
						evaluTF.text = "もうすぐ精度を測定します";
					}else if (count == 1000) {
						sttAngle = clock._angle;//測定開始時の秒針の角度[ラジアン]
					}else if (count < 2500) {
						evaluTF.text = "測定中 (" + ((count - 1000) / 1500 * 100).toFixed(1) + "%)";
					}else if (count == 2500) {
						endAngle = clock._angle;//測定終了時の秒針の角度[ラジアン]
						var dAngle:Number = endAngle-sttAngle;//実測した秒針の角度の変化[ラジアン]
						var dAngleDeg:Number = dAngle / Math.PI * 180;
						var theoAngle:Number = Math.PI;//理論値では1500ステップ=30秒=半回転する→π[ラジアン]
						var result:Number = (dAngle-theoAngle) / theoAngle * 100;//理論値との比
						evaluTF.text = "計測終了 30秒での秒針の回転角度は"+dAngleDeg.toFixed(2)+"°(理論値と比べ "+result.toFixed(2)+"%)";
					}
				}
			}
		}
		
	}
}




//Clockクラス
import flash.display.*;
import flash.events.*;
import flash.geom.*;
import flash.text.*;
import Box2D.Dynamics.*;
import Box2D.Collision.*;
import Box2D.Collision.Shapes.*;
import Box2D.Common.Math.*;
import Box2D.Dynamics.Joints.*;
internal class Clock {
	
	private const SCALE:Number = 10;//px/m  よって1mが10px
	
	private var world:b2World;
	private var ground:b2Body;
	
	private var crown:Crown;
	private var maruana:Maruana;
	private var kakuana:Kakuana;
	private var koubako:Koubako;
	private var gear2:Gear2;
	private var gear3:Gear3;
	private var gear4:Gear4;
	private var gangi:Gangi;
	private var anchor:Anchor;
	private var tempu:Tempu;
	
	////////////////////////////////////////////////////////////////////
	public function Clock(drawSheet:Sprite) {// コンストラクタ
		
		setBase();//基本設定
		
		setParts();//各部品設定
		
		function setBase():void {//基本設定
			
			// シミュレーションする座標の範囲を指定する
			var worldAABB:b2AABB = new b2AABB();
			worldAABB.lowerBound.Set(-100.0, -100.0);
			worldAABB.upperBound.Set(100.0, 100.0);
			
			// 重力を定義する
			var gravity:b2Vec2 = new b2Vec2(0.0, 0.0);
			
			// 世界のインスタンスを作成する
			world = new b2World(worldAABB, gravity, true);
			
			// DebugDraw を有効にする
			var debugDraw:b2DebugDraw = new b2DebugDraw();
			debugDraw.m_sprite = drawSheet;// b2bg;
			debugDraw.m_drawScale = SCALE;
			debugDraw.m_fillAlpha =  0.1;
			debugDraw.m_lineThickness = 1;
			debugDraw.m_drawFlags = b2DebugDraw.e_shapeBit;
			world.SetDebugDraw(debugDraw);
			
			//goundの設定
			ground= world.GetGroundBody();
		}
		
		function setParts():void {//各部品設定
			
			//各部品のオブジェクト作成
			crown= new Crown(world , ground);//リューズ
			maruana = new Maruana(world , ground);//丸穴車
			kakuana = new Kakuana(world , ground);//角穴車
			koubako = new Koubako(world , ground);//香箱車
			gear2 = new Gear2(world , ground);//二番車…分針
			gear3 = new Gear3(world , ground);//三番車
			gear4 = new Gear4(world , ground);//四番車…秒針
			gangi = new Gangi(world , ground);//ガンギ車
			anchor = new Anchor(world , ground);//アンクル
			tempu = new Tempu(world , ground);//テンプ
			
			
			//ギアジョイント設定
			setGearJoint(crown.body , maruana.body , crown.revoluteJoint , maruana.revoluteJoint , 1);//リューズと丸穴車
			setGearJoint(maruana.body , kakuana.body , maruana.revoluteJoint , kakuana.revoluteJoint , 1);//丸穴車と角穴車
			setGearJoint(koubako.body , gear2.body1 , koubako.revoluteJoint , gear2.revoluteJoint1 , 1 / 6);//香箱車と二番車(の内側)
			setGearJoint(gear2.body2 , gear3.body1 , gear2.revoluteJoint2 , gear3.revoluteJoint1 , 1 / 8);//二番車(の外側)と三番車(の内側)
			setGearJoint(gear3.body2 , gear4.body1 , gear3.revoluteJoint2 , gear4.revoluteJoint1 , 1 / 7.5);//三番車(の外側)と四番車(の内側)
			setGearJoint(gear4.body2 , gangi.body1 , gear4.revoluteJoint2 , gangi.revoluteJoint1 , 1 / 10);//四番車(の外側)とガンギ車(の内側)
			
			
			//ディスタンスジョイントを設定する(干渉回避用)
			setDistanceJoint(gangi.body2 , gear4.body2);//ガンギ車(の外側)と四番車(の外側)が干渉しないようにディスタンスジョイントでつなぐ
			setDistanceJoint(gangi.body2 , gear3.body2);//ガンギ車(の外側)と三番車(の外側)が干渉しないようにディスタンスジョイントでつなぐ
			
			
			function setGearJoint(b1:b2Body , b2:b2Body , j1:b2RevoluteJoint , j2:b2RevoluteJoint , r:Number):void {//ギアジョイントを設定する
				var gJointDef1:b2GearJointDef = new b2GearJointDef();
				gJointDef1.body1 = b1;
				gJointDef1.body2 = b2;
				gJointDef1.joint1 = j1;
				gJointDef1.joint2 = j2;
				gJointDef1.ratio = r;
				world.CreateJoint(gJointDef1);
			}
			
			function setDistanceJoint(b1:b2Body , b2:b2Body):void {//ディスタンスジョイントを設定する(干渉回避用)
				var jointDef:b2DistanceJointDef = new b2DistanceJointDef();
				jointDef.Initialize(b1, b2, b1.GetWorldCenter() , b2.GetWorldCenter());
				world.CreateJoint(jointDef);
			}
		}
		
	}////////////////////////////////////////////////////////////////////// コンストラクタここまで
	
	
	////////////////////////////////////////////////////////////////////
	public function makeStep():void {//Box2Dで時間を進める
		
		//Box2Dで時間を進める
		world.Step(1 / 50, 10);
		
		
		//角穴車-香箱車の角度差でトルクを決める
		var theKakuanaAngle:Number = kakuana.body.GetAngle();
		var theKoubakoAngle:Number = koubako.body.GetAngle();
		var torque:Number = (theKakuanaAngle - theKoubakoAngle) * 3000;
		//kakuana.body.ApplyTorque(-torque); 
		//↑本来ならこういうトルクがかかるはずですが、逆回転防止のコハゼという機構の代わりに、この行を消すことで角穴車が逆回転しないようにしています
		koubako.body.ApplyTorque(torque);
		
		
		//テンプを回転させる
		var tempuAngle:Number = tempu.body.GetAngle();//テンプの回転角
		torque = tempuAngle* tempu._kr;//回転角とばね定数でトルクを決める
		tempu.body.ApplyTorque(-torque);//トルクを与える
		
	}////////////////////////////////////////////////////////////////////
	
	////////////////////////////////////////////////////////////////////
	public function wind():void {//リューズを巻く
		crown.body.ApplyTorque(-3000);
	}////////////////////////////////////////////////////////////////////
	
	////////////////////////////////////////////////////////////////////
	public function get _angle():Number {//秒針の角度を返す(計測用)
		return gear4.body2.GetAngle();
	}////////////////////////////////////////////////////////////////////
	
}





//各部品の座標・形状等の物性を扱うための内部クラス・共通関数	
import Box2D.Dynamics.*;
import Box2D.Dynamics.Joints.*;
import Box2D.Collision.Shapes.*;
import Box2D.Common.Math.*;
import flash.geom.Point;

internal class Crown {//竜頭(リューズ)部分
	public var xx:Number = 12.97;//x座標[m]
	public var yy:Number = -12.64;//y座標[m]
	public var radius:Number = 2.92/2;//半径[m]
	public var density:Number = 1;//密度[kg/m^2]
	public var body:b2Body;
	public var revoluteJoint:b2RevoluteJoint;
	public function Crown(world:b2World , ground:b2Body) {
		body = makeGearBody(world , xx , yy , radius , density);
		revoluteJoint = makeRevoluteJoint(world , ground , body);
	}
}

internal class Maruana {//丸穴車
	public var xx:Number = 8.58;//x座標[m]
	public var yy:Number = -8.25;//y座標[m]
	public var radius:Number = 9.39/2;//半径[m]
	public var density:Number = 1;//密度[kg/m^2]
	public var body:b2Body;
	public var revoluteJoint:b2RevoluteJoint;
	public function Maruana(world:b2World , ground:b2Body) {
		body = makeGearBody(world , xx , yy , radius , density);
		revoluteJoint = makeRevoluteJoint(world , ground , body);
	}
}

internal class Kakuana {//角穴車
	public var xx:Number = -3.99;//x座標[m]
	public var yy:Number = -10.22;//y座標[m]
	public var radius:Number = 15.86/2;//半径[m]
	public var density:Number = 1;//密度[kg/m^2]
	public var body:b2Body;
	public var revoluteJoint:b2RevoluteJoint;
	public function Kakuana(world:b2World , ground:b2Body) {
		body = makeGearBody(world , xx , yy , radius , density);
		revoluteJoint = makeRevoluteJoint(world , ground , body);
	}
}

internal class Koubako {//香箱車
	public var xx:Number = -3.99;//x座標[m]
	public var yy:Number = -10.22;//y座標[m]
	public var radius:Number = 19.22/2;//半径[m]
	public var density:Number = 0.005;//密度[kg/m^2]
	public var body:b2Body;
	public var revoluteJoint:b2RevoluteJoint;
	public function Koubako(world:b2World , ground:b2Body) {
		body = makeGearBody(world , xx , yy , radius , density , 0);
		revoluteJoint = makeRevoluteJoint(world , ground , body);
	}
}

internal class Gear2 {//二番車
	public var xx:Number = 0;//x座標[m]
	public var yy:Number = 0;//y座標[m]
	public var radius1:Number = 2.65/2;//半径[m] radius1 < radius2 のこと!
	public var radius2:Number = 12.24/2;//半径[m]
	public var density:Number = 0.001550;//密度[kg/m^2]
	public var body1:b2Body;
	public var body2:b2Body;
	public var revoluteJoint1:b2RevoluteJoint;
	public var revoluteJoint2:b2RevoluteJoint;
	public function Gear2(world:b2World , ground:b2Body) {
		
		body1 = makeGearBody(world , xx , yy , radius1 , density , 0);
		revoluteJoint1 = makeRevoluteJoint(world , ground , body1);
		body2 = makeGearBody(world , xx , yy , radius2 , density , 0);
		revoluteJoint2 = makeRevoluteJoint(world , ground , body2);
		
		//body1,2をdistanceJointでつなぐ
		setDistanceJoint(world , body1, body2 , xx , yy , radius1);
	}
}

internal class Gear3 {//三番車
	public var xx:Number = 0;//x座標[m]
	public var yy:Number = 7.03;//y座標[m]
	public var radius1:Number = 1.85/2;//半径[m] radius1 < radius2 のこと!
	public var radius2:Number = 10.75/2;//半径[m]
	public var density:Number = 0.005;//密度[kg/m^2]
	public var body1:b2Body;
	public var body2:b2Body;
	public var revoluteJoint1:b2RevoluteJoint;
	public var revoluteJoint2:b2RevoluteJoint;
	public function Gear3(world:b2World , ground:b2Body) {
		
		body1 = makeGearBody(world , xx , yy , radius1 , density , 0);
		revoluteJoint1 = makeRevoluteJoint(world , ground , body1);
		body2 = makeGearBody(world , xx , yy , radius2 , density , 0);
		revoluteJoint2 = makeRevoluteJoint(world , ground , body2);
		
		//body1,2をdistanceJointでつなぐ
		setDistanceJoint(world , body1, body2 , xx , yy , radius1);
	}
}

internal class Gear4 {//四番車
	public var xx:Number = 0;//x座標[m]
	public var yy:Number = 13.03;//y座標[m]
	public var radius1:Number = 1.28/2;//半径[m] radius1 < radius2 のこと!
	public var radius2:Number = 9.85/2;//半径[m]
	public var density:Number = 0.005;//密度[kg/m^2]
	public var body1:b2Body;
	public var body2:b2Body;
	public var revoluteJoint1:b2RevoluteJoint;
	public var revoluteJoint2:b2RevoluteJoint;
	public function Gear4(world:b2World , ground:b2Body) {
		
		body1 = makeGearBody(world , xx , yy , radius1 , density , 0);
		revoluteJoint1 = makeRevoluteJoint(world , ground , body1);
		body2 = makeGearBody(world , xx , yy , radius2 , density , 0);
		revoluteJoint2 = makeRevoluteJoint(world , ground , body2);
		
		//body1,2をdistanceJointでつなぐ
		setDistanceJoint(world , body1, body2 , xx , yy , radius1);
	}
}

internal class Gangi {//ガンギ車
	public var xx:Number = -5.69;//x座標[m]
	public var yy:Number = 12.47;//y座標[m]
	public var radius1:Number = 1.51/2;//半径[m] radius1 < radius2 のこと!
	public var radius2:Number = 3.0360;//半径[m]
	public var density:Number = 0.004800;//密度[kg/m^2]
	public var body1:b2Body;
	public var body2:b2Body;
	public var revoluteJoint1:b2RevoluteJoint;
	public var revoluteJoint2:b2RevoluteJoint;
	public function Gangi(world:b2World , ground:b2Body) {
		
		body1 = makeGearBody(world , xx , yy , radius1 , density , 0);
		revoluteJoint1 = makeRevoluteJoint(world , ground , body1);
		
		body2 = makeGangi(world , xx , yy , radius2 , density);
		revoluteJoint2 = makeRevoluteJoint(world , ground , body2);
		
		//body1,2をdistanceJointでつなぐ
		setDistanceJoint(world , body1, body2 , xx , yy , radius1);
	}
}

internal class Anchor {//アンクル
	public var xx:Number =  -8.371;//支点x座標[m]
	public var yy:Number = 10.2764;//支点y座標[m]
	public var density:Number = 0.000240;//密度[kg/m^2]
	public var body:b2Body;
	public var w:Number = 0.404 , h:Number = 0.82;//つめの形状
	public function Anchor(world:b2World , ground:b2Body , l1:Number = 1.79 , l2:Number = 1.79 , thetaL1deg:Number = 32.5,thetaL2deg:Number=149.0 ,thetaTdeg:Number=2) {
		
		//物体の定義
		var bodyDef:b2BodyDef = new b2BodyDef();
		bodyDef.position.Set(xx , yy);//座標[m]
		bodyDef.angularDamping = 0;
		bodyDef.angle = -0.888;
		
		var  thetaT:Number = Math.PI / 180 * thetaTdeg;//ラジアンに変換
		var  thetaL1:Number = Math.PI / 180 * thetaL1deg;//ラジアンに変換
		var  thetaL2:Number = Math.PI / 180 * thetaL2deg;//ラジアンに変換
		
		//物体を作る
		body = world.CreateBody(bodyDef);
		
		// 形の定義を作る(1)右側
		var shapeDef:b2PolygonDef = new b2PolygonDef();
		shapeDef.vertexCount = 4;
		//右下座標
		var RCPt1:Point = new Point(0, 0);//回転中心1
		var RCPt2:Point = new Point(l1, 0);//右下の回転前
		var aPt:Point;
		
		RCPt2 = RotatePoint(RCPt2 , RCPt1, thetaL1);//RCPt1(原点)を中心に回転させる
		shapeDef.vertices[0].Set(RCPt2.x , RCPt2.y);//右下
		
		aPt = RotatePoint(new Point(l1-w , 0) , RCPt1,thetaL1);//左下
		aPt = RotatePoint(aPt, RCPt2, thetaT);
		shapeDef.vertices[1].Set(aPt.x , aPt.y);//左下
		
		aPt = RotatePoint(new Point(l1-w , -h) , RCPt1,thetaL1);//左上
		aPt = RotatePoint(aPt, RCPt2, thetaT);
		shapeDef.vertices[2].Set(aPt.x , aPt.y);//左上
		
		aPt = RotatePoint(new Point(l1 , -h) , RCPt1,thetaL1);//右上
		aPt = RotatePoint(aPt, RCPt2, thetaT);
		shapeDef.vertices[3].Set(aPt.x , aPt.y);//右上
		
		shapeDef.density = density;     // 密度 [kg/m^2]
        shapeDef.friction = 0;
		shapeDef.restitution = 0;
		
		// 形を物体に追加する
		body.CreateShape(shapeDef);
		
		
		// 形の定義を作る(2)左側
		shapeDef.vertexCount = 4;
		
		RCPt2 = new Point(l2, 0);//左上の回転前
		
		RCPt2 = RotatePoint(RCPt2 , RCPt1, thetaL2);//RCPt1(原点)を中心に回転させる
		shapeDef.vertices[0].Set(RCPt2.x , RCPt2.y);//左上
		
		aPt = RotatePoint(new Point(l2+w, 0) , RCPt1,thetaL2);//右上
		aPt = RotatePoint(aPt, RCPt2, thetaT);
		shapeDef.vertices[1].Set(aPt.x , aPt.y);//右上
		
		aPt = RotatePoint(new Point(l2+w , h) , RCPt1,thetaL2);//右下
		aPt = RotatePoint(aPt, RCPt2, thetaT);
		shapeDef.vertices[2].Set(aPt.x , aPt.y);//右下
		
		aPt = RotatePoint(new Point(l2 , h) , RCPt1,thetaL2);//左下
		aPt = RotatePoint(aPt, RCPt2, thetaT);
		shapeDef.vertices[3].Set(aPt.x , aPt.y);//左下
		
        shapeDef.density = density;     // 密度 [kg/m^2]
        shapeDef.friction = 0;
		shapeDef.restitution = 0;
		
		// 形を物体に追加する
		body.CreateShape(shapeDef);
		
		
		
		// 形の定義を作る(3)上の左側
		var tDeg:Number = 39.8;
		var tRad:Number = Math.PI / 180 * tDeg;
		w = 0.3;
		h = 0.52;
		
		var wAbs:Number = 0.30;// 0.60;//中心線からの距離
		var hAbs:Number = 3.85;
		RCPt1 = new Point(-wAbs, -hAbs);//回転中心
		shapeDef.vertexCount = 4;
		shapeDef.vertices[0].Set( -wAbs , -hAbs);//右下
		
		aPt = RotatePoint(new Point(-wAbs-w , -hAbs) , RCPt1, -tRad);//左下
		shapeDef.vertices[1].Set(aPt.x , aPt.y);//左下
		
		aPt = RotatePoint(new Point(-wAbs-w , -hAbs - h) , RCPt1, -tRad);//左上
		shapeDef.vertices[2].Set(aPt.x , aPt.y);//左上
		
		aPt = RotatePoint(new Point(-wAbs , -hAbs-h) , RCPt1, -tRad);//右上
		shapeDef.vertices[3].Set(aPt.x , aPt.y);//右上
		
        shapeDef.density = density;// 密度 [kg/m^2]
        shapeDef.friction = 0;
		shapeDef.restitution = 0;
		
		// 形を物体に追加する
		body.CreateShape(shapeDef);
		
		
		// 形の定義を作る(4)上の右側
		shapeDef.vertexCount = 4;
		RCPt1 = new Point(wAbs, -hAbs);//回転中心
		
		shapeDef.vertices[0].Set(wAbs , -hAbs);//左下
		
		aPt = RotatePoint(new Point(wAbs , -hAbs-h) , RCPt1, tRad);//左上
		shapeDef.vertices[1].Set(aPt.x , aPt.y);//左上
		
		aPt = RotatePoint(new Point(wAbs+w , -hAbs - h) , RCPt1, tRad);//右上
		shapeDef.vertices[2].Set(aPt.x , aPt.y);//右上
		
		aPt = RotatePoint(new Point(wAbs+w , -hAbs) , RCPt1, tRad);//右下
		shapeDef.vertices[3].Set(aPt.x , aPt.y);//右下
		
        shapeDef.density = density;     // 密度 [kg/m^2]
        shapeDef.friction = 0;
		shapeDef.restitution = 0;
		
		// 形を物体に追加する
		body.CreateShape(shapeDef);
		
		
		// 重さ・重心を計算する
		body.SetMassFromShapes();
		
		
		//RevoluteJointの設定
		var jointDef:b2RevoluteJointDef = new b2RevoluteJointDef();
		jointDef.Initialize(ground, body, new b2Vec2(xx, yy));
		jointDef.lowerAngle =  - Math.PI / 180 * 5;
		jointDef.upperAngle =  + Math.PI / 180 * 5;
		jointDef.enableLimit =  true;//振幅の制限設定の可否
		
		//実際にジョイントを作る
		world.CreateJoint(jointDef);
		
		
		
		function RotatePoint(pt:Point , cpt:Point , rad:Number):Point {//点ptをcptを中心に回転させる
			var xl1:Number = pt.x - cpt.x;
			var yl1:Number = pt.y - cpt.y;
			
			var x2:Number = cpt.x + Math.cos(rad) * xl1 - Math.sin(rad) * yl1;
			var y2:Number = cpt.y + Math.sin(rad) * xl1 + Math.cos(rad) * yl1;
			return new Point(x2, y2);
		}
	}
}

internal class Tempu {//テンプ
	public var xx:Number = -11.971;//x座標[m]
	public var yy:Number = 7.35;//y座標[m]
	public var radius:Number = 0.16;//半径[m]
	public var density:Number = 0.0002;//密度[kg/m^2]
	public var _kr:Number;//ねじりばね定数[N・m/rad]
	public var body:b2Body;
	public function Tempu(world:b2World , ground:b2Body) {
		
		var _m:Number , _r:Number;//慣性モーメント計算用
		
		//物体の定義
		var bodyDef:b2BodyDef = new b2BodyDef();
		bodyDef.position.Set(xx , yy);//座標[m]
		bodyDef.angularDamping = 0;
		bodyDef.isBullet = true;
		
		//物体を作る
		body = world.CreateBody(bodyDef);
		
		var shapeDef:b2CircleDef = new b2CircleDef();
		shapeDef.radius = radius;
		shapeDef.density = density;
		shapeDef.friction = 0;
		shapeDef.restitution = 0;
		
		
		var tl:Number = 0.15;//テンプ中心とアンクル支点を内分する割合(0だとテンプ中心)
		shapeDef.localPosition.Set((11.971-8.371)*tl,(10.2764-7.35)*tl);//ここに質量がある
		
		//チェック用
		_m = Math.PI * radius * radius * density;//慣性モーメント計算用m
		_r = Math.sqrt(Math.pow((11.971 - 8.371) * tl, 2) + Math.pow((10.2764 - 7.35) * tl, 2));
		_kr = _m * _r * _r * Math.pow(2.5 * 2 * Math.PI , 2)*2;//*2はカウンター込み
		
		// 形を物体に追加する
		body.CreateShape(shapeDef);
		
		//カウンター
		shapeDef.localPosition.Set((11.971-8.371)*(-tl),(10.2764-7.35)*(-tl));//ここに質量がある
		body.CreateShape(shapeDef);// 形を物体に追加する
		
		
		//外側に二つ錘をつける
		var l:Number = 2.2;//半径
		var lrad:Number = (39.2894+90) * Math.PI / 180;
		radius = 0.30;
		shapeDef.radius =radius;
		shapeDef.density = density;
		shapeDef.friction = 0;
		shapeDef.restitution = 0;
		shapeDef.localPosition.Set(l*Math.cos(lrad),l*Math.sin(lrad));//ここに質量がある
		body.CreateShape(shapeDef);// 形を物体に追加する
		shapeDef.localPosition.Set(-l*Math.cos(lrad),-l*Math.sin(lrad));//ここに質量がある
		body.CreateShape(shapeDef);// 形を物体に追加する
		
		_m = Math.PI * radius * radius * density;//慣性モーメント計算用m
		_kr += _m * l * l * Math.pow(2.5 * 2 * Math.PI , 2)*2;
		
		// 重さ・重心を計算する
		body.SetMassFromShapes();
		
		
		//RevoluteJointの設定
		var jointDef:b2RevoluteJointDef = new b2RevoluteJointDef();
		jointDef.Initialize(ground, body, new b2Vec2(xx,yy));
		world.CreateJoint(jointDef);
		
	}
}


function makeGearBody(world:b2World , xx:Number , yy:Number , radius:Number , density:Number , angularDamping:Number=3):b2Body {//単純なギア作成
	//物体の定義
	var bodyDef:b2BodyDef = new b2BodyDef();
	bodyDef.position.Set(xx , yy);//座標[m]
	bodyDef.angularDamping = angularDamping;
	
	//物体を作る
	var body:b2Body = world.CreateBody(bodyDef);
	
	// 形の定義を作る
	var shapeDef:b2CircleDef = new b2CircleDef();
	shapeDef.radius = radius;
	shapeDef.density = density;     // 密度 [kg/m^2]
	shapeDef.friction = 0;
	
	// 形を物体に追加する
	body.CreateShape(shapeDef);
	
	// 重さ・重心を計算する
	body.SetMassFromShapes();
	
	return body;
}

function makeRevoluteJoint(world:b2World , ground:b2Body , body:b2Body):b2RevoluteJoint {//body(歯車)を固定するRevoluteJointの作成
	
	//RevoluteJointの設定
	var jointDef:b2RevoluteJointDef = new b2RevoluteJointDef();
	jointDef.Initialize(ground, body, body.GetWorldCenter());
	
	//実際にジョイントを作る
	var joint:b2RevoluteJoint = b2RevoluteJoint(world.CreateJoint(jointDef));
	
	return joint;
}

function makeGangi(world:b2World , xx:Number , yy:Number , radius:Number , density:Number):b2Body {//ガンギ車外側作成
	//半径radius上の円に15個の小さい円を作成する
	const R:Number = 0.1;
	const N:uint = 15;
	var ii:uint;
	var shapeDef:b2CircleDef;
	var rad:Number;
	var offsetR:Number = Math.PI / 180 * 10;
	
	//物体の定義
	var bodyDef:b2BodyDef = new b2BodyDef();
	bodyDef.position.Set(xx , yy);//座標[m]
	bodyDef.angularDamping = 0;
	bodyDef.isBullet = true;
	
	//物体を作る
	var body:b2Body = world.CreateBody(bodyDef);
	
	for (ii = 0; ii < N; ii++) {
		makeDot(ii);
	}
	
	// 重さ・重心を計算する
	body.SetMassFromShapes();
	
	return body;
	
	function makeDot(nn:uint):void {
		// 形の定義を作る
		rad = (360 / N * nn) / 180 * Math.PI;
		shapeDef = new b2CircleDef();
		shapeDef.radius = R;
		shapeDef.density = density;     // 密度 [kg/m^2]
		shapeDef.localPosition.Set(offsetR+radius*Math.cos(rad), radius*Math.sin(rad));
		shapeDef.friction = 0;
		shapeDef.restitution = 0;
		
		body.CreateShape(shapeDef);// 形を物体に追加する
	}
}

function setDistanceJoint(world:b2World , body1:b2Body, body2:b2Body , cx:Number , cy:Number , r:Number):void {//二番車など内外に歯を持つギアの固定のためのJoint設定
	
	var jointDef:b2DistanceJointDef = new b2DistanceJointDef();
	jointDef.Initialize(body1, body2, new b2Vec2(cx+r, cy) , new b2Vec2(cx, cy+r));
	world.CreateJoint(jointDef);
	
}