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

黒玉は白玉の4倍重い

----------------------------------------
2010年4月1日木曜日
完全弾性衝突する二つの玉
黒玉は白玉の4倍重く設定してあります
赤い点は両者の重心です
壁で跳ね返ると重心の動きが変わるけれど
黒玉と白玉の衝突では重心の動きは変化しません
(作用・反作用の法則 あるいは 運動量の保存)

けど, 本当はグラデーションの実習として作ったのです
----------------------------------------
/**
 * Copyright tenasaku ( http://wonderfl.net/user/tenasaku )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/foPw
 */

/*	----------------------------------------
	2010年4月1日木曜日
	完全弾性衝突する二つの玉
	黒玉は白玉の4倍重く設定してあります
	赤い点は両者の重心です
	壁で跳ね返ると重心の動きが変わるけれど
	黒玉と白玉の衝突では重心の動きは変化しません
	(作用・反作用の法則 あるいは 運動量の保存)

	けど, 本当はグラデーションの実習として作ったのです
	----------------------------------------
*/

package {

	import flash.display.*;
	import flash.events.*;
	import flash.geom.*;
	import flash.text.*;

	public class Main extends Sprite {

		private var ball1:MassiveBall = new MassiveBall();
		private var ball2:MassiveBall = new MassiveBall();
		private var baricentre:Shape = new Shape(); // 重心の位置を示す点
		private var monitor:TextField = new TextField();

		private function atEveryFrame(e:Event):void {
			var b:MassiveBall;
			for each ( b in [ball1, ball2] ) {
				with (b) {
					if ( x <= radius ) { Vx = Math.abs(Vx); }
					if ( x >= stage.stageWidth - radius ) { Vx = -Math.abs(Vx); }
					if ( y <= radius ) { Vy = Math.abs(Vy); }
					if ( y >= stage.stageHeight - radius ) { Vy = -Math.abs(Vy); }
					x += Vx;
					y += Vy;
				}
			}
			var Rx:Number = ball2.x - ball1.x; // 相対位置
			var Ry:Number = ball2.y - ball1.y;
			var rVx:Number = ball2.Vx - ball1.Vx; // 相対速度
			var rVy:Number = ball2.Vy - ball1.Vy;
			var distance:Number = Math.sqrt(Rx*Rx+Ry*Ry); // 距離
			var scalarProd:Number = Rx*rVx+Ry*rVy; // 相対速度と相対位置の内積
			if ( ( Math.abs(distance-ball1.radius-ball2.radius)<5.0 )
					&& (scalarProd <= 0) ) { // 衝突!!
				// 完全弾性衝突として速度の変化を計算する
				var k:Number = scalarProd/(distance*distance);
				ball1.Vx +=  k*(2*ball2.mass)/(ball1.mass+ball2.mass)*Rx;
				ball1.Vy +=  k*(2*ball2.mass)/(ball1.mass+ball2.mass)*Ry;
				ball2.Vx += -k*(2*ball1.mass)/(ball1.mass+ball2.mass)*Rx;
				ball2.Vy += -k*(2*ball1.mass)/(ball1.mass+ball2.mass)*Ry;
			}
			baricentre.x = (ball1.x*ball1.mass+ball2.x*ball2.mass)/(ball1.mass+ball2.mass);
			baricentre.y = (ball1.y*ball1.mass+ball2.y*ball2.mass)/(ball1.mass+ball2.mass);
			report();
		}

		// 情報フィールドの更新処理
		// Momentum: 運動量の大きさ
		// Energy: 運動エネルギー
		private function report():void {
			var mX:Number = ball1.Vx*ball1.mass+ball2.Vx*ball2.mass;
			var mY:Number = ball1.Vy*ball1.mass+ball2.Vy*ball2.mass;
			var M:Number = Math.sqrt(mX*mX+mY*mY);
			var K:Number = 
				0.5*ball1.mass*(ball1.Vx*ball1.Vx+ball1.Vy*ball1.Vy)
				+ 0.5*ball2.mass*(ball2.Vx*ball2.Vx+ball2.Vy*ball2.Vy);
			monitor.text = "Momentum: "+M.toFixed(6)+"\nEnergy: "+K.toFixed(6);
		}

		private function initialize(e:Event):void {
			this.removeEventListener(Event.ADDED_TO_STAGE, initialize);
			stage.align = StageAlign.TOP_LEFT;
			stage.scaleMode = StageScaleMode.NO_SCALE;
			// 背景を夕焼け空ふうにする...
			var m:Matrix = new Matrix();
			m.createGradientBox(
				stage.stageHeight/3*2, stage.stageWidth,
				Math.PI/2,
				0, stage.stageHeight/3
			);
			this.graphics.clear();
			this.graphics.beginGradientFill(
				GradientType.LINEAR,
				[0x000066, 0x995033, 0xff9900],
				[1, 1, 1],
				[0, 100, 255],
				m,
				SpreadMethod.PAD
			);
			this.graphics.drawRect(0,0,stage.stageWidth,stage.stageHeight);
			this.graphics.endFill();
			// 情報テキストフィールドの初期化と登録
			monitor.background = true;
			monitor.backgroundColor = 0x999999;
			monitor.alpha = 0.5;
			monitor.width = 160;
			monitor.height = 50;
			this.addChild(monitor);
			// 玉オブジェクトの初期化
			with (ball1) {
				bottomColor = 0x666666;
				topColor = 0x000000;
				radius = 50;
				Vx = 0.0;
				Vy = 0.0;
				mass = 4.0;
			}
			with (ball2) {
				bottomColor = 0xffffff;
				topColor = 0x330033;
				radius = 40;
				Vx = 4.0;
				Vy = 0;
				mass = 1.0;
			}
			// with 構文の中で x と y を設定したら表示がおかしくなったので外に出す
			ball1.x = stage.stageWidth/2;
			ball1.y = stage.stageHeight/2;
			ball2.x = ball2.radius;
			ball2.y = stage.stageHeight/2+Math.random()*10-5; // 少し上下にずらす
			// 設定を反映させ表示する
			ball1.redraw();
			ball2.redraw();
			this.addChild(ball1);
			this.addChild(ball2);
			// 重心の位置を示すオブジェクトを生成
			baricentre.graphics.clear();
			baricentre.graphics.beginFill(0xff0000);
			baricentre.graphics.drawCircle(0,0,5);
			baricentre.graphics.endFill();
			baricentre.x = (ball1.x*ball1.mass+ball2.x*ball2.mass)/(ball1.mass+ball2.mass);
			baricentre.y = (ball1.y*ball1.mass+ball2.y*ball2.mass)/(ball1.mass+ball2.mass);
			this.addChild(baricentre);
			stage.addEventListener(Event.ENTER_FRAME, atEveryFrame);
		}

		public function Main():void {
			if ( stage != null ) {
				initialize(null);
			} else {
				this.addEventListener(Event.ADDED_TO_STAGE, initialize);
			}
		}

	} // end of class Main
} // end of package

import flash.display.*;
import flash.events.*;
import flash.geom.*;
class MassiveBall extends Shape {
	public var radius:Number;
	public var gm:Matrix;
	public var topColor:uint, bottomColor:uint;
	public var Vx:Number,Vy:Number; // 速度ベクトル
	public var mass:Number; // 質量
	public function redraw():void {
		this.graphics.clear();
		gm.createGradientBox(2*radius,2*radius,Math.PI/2,0,-radius);
		this.graphics.beginGradientFill(
			GradientType.LINEAR,
			[topColor,bottomColor], [1,1], [0,255],
			gm,SpreadMethod.PAD
		);
		this.graphics.drawCircle(0,0,radius);
		this.graphics.endFill();
	}
	public function MassiveBall() {
		radius = 50;
		gm = new Matrix();
		Vx = 0;
		Vy = 0;
		mass = 1.0;
		topColor = 0xffffff;
		bottomColor = 0x666666;
		redraw();
	}
}