Beautifl - Flash Gallery

Thumbnail : 布いじり3D
布いじり3D
novogrammer 2010-03-28 All rights reserved

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

/**
 * 布シミュをいじってみる by NOVO
 * 動作が変わってしまうのでfork
 * ToDo 3Dにしてみたい(見た目)
 * Done 3Dにしてみたい(計算)
 * ToDo 単位系をそろえる
 * Done Fの計算が終わる前にXを動かしているのが偏りの原因っぽい
 * Done せん断抵抗、角度抵抗用のJointを設定したい
 * ToDo 微小時間でシミュレート
 * ToDo 積分法を変えてみる?
 * Done 初期化変更、必要ない変数も消す。描画方法もJointの描画だけでいいのでは?
 * Done 片方向のJointを両方向に変更(力の計算場所も変更)
 * Done ダンパ力を設定
 * Done 簡易ベクトルクラスに置き換え
 * Done ばねの自然長計算を変更
 * 以下オリジナルコメント
 */
// forked from miniapp's GraphicsPathCommand使ったバージョン  forked from: 布
/**
 * GraphicsPathCommandでラインを描画するバージョン。
 * drawLine関数の中が違います。
 *
 * 本格的な布のシミュレーションではありません。
 * 
 * ドラッグでマウスに一番近いポイントを移動させる。
 * ctrlキー押しながらドラッグで固定。
 * ダブルクリックで固定を解除。
 */
package {
	import flash.display.GraphicsPathCommand;
	import flash.display.Sprite;
	import flash.display.StageQuality;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import net.hires.debug.Stats;

    [SWF(backgroundColor="0xFFFFFF", width="465", height="465", frameRate="60")]
	public class Cloth_DrawPath extends Sprite {
		
		public static const STAGE_WIDTH:uint = 465;
		public static const STAGE_HEIGHT:uint = 465;
		
		public function Cloth_DrawPath() {
			if (stage) init();
			else addEventListener(Event.ADDED_TO_STAGE, init);
		}
		
		private var _lineColor:uint = 0x000000;
		private var _cols:uint = 16;//横の数
        private var _rows:uint = 16;//縦の数
		private var _diffX:uint = 10;
        private var _diffY:uint = 10;
		private var _isCtrlPress:Boolean = false;
		private var _isMouseDown:Boolean = false;
		private var _draggedPoint:Point;
		private var _isFirst:Boolean = false;
		//private var _numJoints:uint;
		private var _joints:Vector.<Joint> = new Vector.<Joint>();
		private var _points:Vector.<Point> = new Vector.<Point>();
		//private var _pointsXs:Vector.<Vector.<Point>> = new Vector.<Vector.<Point>>(_rows, true);
		//private var _pointsYs:Vector.<Vector.<Point>> = new Vector.<Vector.<Point>>(_cols, true);
		
		private var _vertices:Vector.<Number> = new Vector.<Number>();
		private var _commands:Vector.<int> = new Vector.<int>();
		
		private function init(e:Event = null):void {
			removeEventListener(Event.ADDED_TO_STAGE, init);
			stage.scaleMode = StageScaleMode.NO_SCALE;
			//stage.quality = StageQuality.MEDIUM;
			
			stage.doubleClickEnabled = true;
			stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
			stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDwonHandler);
			stage.addEventListener(MouseEvent.DOUBLE_CLICK, doubleClickHandler);
			stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDonwHandler);
			stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
			
			//putPoint();
			//joint();
			putPointAndJoint();
			
			//上端2つを固定します。
			//左端
			var point1:Point = _points[0];
			point1._position=new MyVector3D(100,10,0);
			point1.isPinned = true;
			
			//右端
			var point2:Point = _points[_rows - 1];
			point2._position=new MyVector3D(STAGE_WIDTH - 100,10,100);//ちょっと奥行きをつけてみる
			point2.isPinned = true;
			
			addEventListener(Event.ENTER_FRAME, enterFrameHandler);
			addChild(new Stats());
		}
		/**
		 *
		 *
		 */
		private function putPointAndJoint():void
		{
			var clothWidth:Number = (_cols - 1) * _diffX;
			var clothHeight:Number = (_rows - 1) * _diffY;
			var startX:Number = (STAGE_WIDTH - clothWidth) / 2;
			var startY:Number = (STAGE_HEIGHT - clothHeight) / 2;
			
			//ポイントとジョイントを一気に配置したほうが楽なので
			for(var y:uint=0;y<_rows;++y)
			{
				for(var x:uint=0;x<_cols;++x)
				{
					var point:Point=new Point();
					_points.push(point);
					point.name = String(y) + "-" + String(x);//デバッグ用
					var noize:Number = Math.random()*10;//0から1
					point._position=new MyVector3D(startX + _diffX * x,startY + _diffY * y,noize);
					
					if(y>0)
					{
						var pointUp:Point=_points[(y-1)*_cols+x];
						_joints.push(new Joint(point, pointUp));//たて
						if (x > 0)
						{
							var pointNaname1:Point=_points[(y-1)*_cols+(x-1)];
							_joints.push(new Joint(point, pointNaname1));//ななめ1
						}
						if (x < _cols -1)
						{
							var pointNaname2:Point=_points[(y-1)*_cols+(x+1)];
							_joints.push(new Joint(point, pointNaname2));//ななめ2
						}
					}
					if(x>0)
					{
						var pointLeft:Point=_points[y*_cols+(x-1)];
						_joints.push(new Joint(point,pointLeft));//よこ
					}
					if(y>1)
					{
						var pointUp2:Point=_points[(y-2)*_cols+x];
						_joints.push(new Joint(point, pointUp2));//たて ひとつ飛ばし
					}
					if(x>1)
					{
						var pointLeft2:Point=_points[y*_cols+(x-2)];
						_joints.push(new Joint(point,pointLeft2));//よこ
					}
					
				}
			}
			trace("_poinits.length"+_points.length);
			trace("_joints.length"+_joints.length);
			_points.fixed=true;
			_joints.fixed=true;
		}
			
		
		/**
		 * 一番カーソルに近いポイントを捜す。
		 */
		private function searchPoint():Point {
			var lastMinDist:Number = Infinity;
			var target:Point;
			for each(var point:Point in _points) {
				var pos:MyVector3D = point._position;
				var m:MyVector3D=new MyVector3D(mouseX,mouseY,0.0);
				var dist:Number = pos.subtract(m).length();
				if (dist < lastMinDist) {
					lastMinDist = dist;
					target = point;
				}
			}
			return target;
		}
		
		/**
		 * バネの線を書く
		 */
		private function drawLine():void {
			graphics.clear();
			graphics.lineStyle(1, _lineColor);
			
			var dataIndex:uint = 0;
			var commandIndex:uint = 0;
			for each(var joint:Joint in _joints)
			{
				var pointA:Point=joint._point;
				var pointB:Point=joint._target;
				_vertices[dataIndex++] = pointA._position.x;
				_vertices[dataIndex++] = pointA._position.y;
				_commands[commandIndex++] = GraphicsPathCommand.MOVE_TO;
				_vertices[dataIndex++] = pointB._position.x;
				_vertices[dataIndex++] = pointB._position.y;
				_commands[commandIndex++] = GraphicsPathCommand.LINE_TO;
			}
			
			if(_isFirst){
				_commands.fixed = _vertices.fixed = true;
				_isFirst = false;
			}
			graphics.drawPath(_commands, _vertices);
		}
		
		private function enterFrameHandler(e:Event):void {
			if (_isMouseDown) {
				_draggedPoint._position.x = mouseX;
				_draggedPoint._position.y = mouseY;
			}
			//フェーズを二つに分ける。力更新と位置更新
			// update force
			for each(var joint1:Joint in _joints) {
				joint1.updateForce();
			}
			for each(var point1:Point in _points) {
				point1.updateForce();//多分下のループに入れても影響はないけど、明示的に分ける。
			}
			// update position
			for each(var point2:Point in _points) {
				point2.updatePosition();
			}
			
			drawLine();
		}
		
		private function keyDonwHandler(e:KeyboardEvent = null):void{
			if(e.ctrlKey) _isCtrlPress = true;
		}
		
		private function keyUpHandler(e:KeyboardEvent = null):void{
			_isCtrlPress = false;
		}
		
		private function doubleClickHandler(e:MouseEvent = null):void{
			searchPoint().isPinned = false;
		}
		
		private function mouseDwonHandler(e:MouseEvent):void {
			_isMouseDown = true;
			_draggedPoint = searchPoint();
			_draggedPoint.isDragging = true;
		}
		
		private function mouseUpHandler(e:MouseEvent):void	{
			_isMouseDown = false;
			if (_isCtrlPress)
				_draggedPoint.isPinned = true;
			_draggedPoint.isDragging = false;
			_draggedPoint = undefined;
		}
	}
}

//簡易ベクトルクラス
class MyVector3D
{
	public var x:Number = 0.0;
	public var y:Number = 0.0;
	public var z:Number = 0.0;
	public function MyVector3D(inX:Number=0.0, inY:Number=0.0,inZ:Number=0.0)
	{
		x = inX;
		y = inY;
		z = inZ;
	}
	//+=
	public function addTo(rhs:MyVector3D):MyVector3D
	{
		this.x += rhs.x;
		this.y += rhs.y;
		this.z += rhs.z;
		return this;
	}
	//+
	public function add(rhs:MyVector3D):MyVector3D
	{
		return new MyVector3D(this.x + rhs.x, this.y + rhs.y, this.z + rhs.z);
	}
	//-=
	public function subtractTo(rhs:MyVector3D):MyVector3D
	{
		this.x -= rhs.x;
		this.y -= rhs.y;
		this.z -= rhs.z;
		return this;
	}
	//-
	public function subtract(rhs:MyVector3D):MyVector3D
	{
		return new MyVector3D(this.x - rhs.x, this.y - rhs.y, this.z - rhs.z);
	}
	//*=
	public function multiplyTo(rhs:Number):MyVector3D
	{
		this.x *= rhs;
		this.y *= rhs;
		this.z *= rhs;
		return this;
	}
	//*
	public function multiply(rhs:Number):MyVector3D
	{
		return new MyVector3D(this.x*rhs,this.y*rhs,this.z*rhs);
	}
	///=
	public function divideTo(rhs:Number):MyVector3D
	{
		this.x /= rhs;
		this.y /= rhs;
		this.z /= rhs;
		return this;
	}
	///
	public function divide(rhs:Number):MyVector3D
	{
		return new MyVector3D(this.x/rhs,this.y/rhs,this.z/rhs);
	}

	public function length2():Number
	{
		return this.x * this.x + this.y * this.y+ this.z * this.z;
	}
	public function length():Number
	{
		return Math.sqrt(length2());
	}
	//単位ベクトル
	public function normalize():MyVector3D
	{
		return this.divide(this.length());
	}
}

class Joint {
	
	public static var SPRING:Number = 0.03;
	public static var DAMPER:Number = 0.005;
	
	public var _naturalLength:Number = 0.0;
	
	public var _point:Point=null;
	public var _target:Point=null;
	
	public function Joint(point:Point, target:Point) { 
		_point=point;
		_target = target;
		_naturalLength = _point._position.subtract(_target._position).length();
		//trace(_naturalLength);
	}
	public function updateForce():void
	{
		//バネの力
		var dx:MyVector3D = _target._position.subtract(_point._position);
		var nx:MyVector3D = dx.normalize();//単位ベクトル
		var springForce:MyVector3D = nx.multiply((dx.length() - _naturalLength) * Joint.SPRING);
		
		//ダンパの力
		var dv:MyVector3D = _target._velocity.subtract(_point._velocity);
		var damperForce:MyVector3D = dv.multiply(DAMPER);
		
		//合力
		var totalForce:MyVector3D = springForce.add(damperForce);
		_point._force.addTo(totalForce);
		//逆の力をかける
		_target._force.addTo(totalForce.multiply(-1));
		
	}
}

class Point {
	public var name:String;
	public var _position:MyVector3D = new MyVector3D();
	public var _velocity:MyVector3D = new MyVector3D();
	public var _force:MyVector3D = new MyVector3D();
	public var mass:Number=0.3;
	public var isPinned:Boolean = false;
	public var isDragging:Boolean = false;
	public static var GRAVITY:Number = 0.005;
	public static var AIR_FRICTION:Number = 0.0005;
	public function updateForce():void
	{
		_force.y += GRAVITY;
		_force.addTo(_velocity.multiply(AIR_FRICTION*-1));
	}
	public function updatePosition():void
	{
		if (isDragging || isPinned)
		{
			_velocity = new MyVector3D();
			_force = new MyVector3D();
		}
		else
		{
			_velocity.addTo(_force.divide(mass));
			_force = new MyVector3D();
			_position.addTo(_velocity);
		}
	}
}