任意の支点で回転しながらドラッグする

  • 2009 年 8月 14 日
  • kosuke

ベクトルを使ったテクニックのエントリー。
やりたい事はドラッグした軌跡に追随して回転させたい。回転させるにはその角度を決めなきゃいけない。
どの位の角度移動させるかは移動前後の位置をベクトルにして演算すると算出できるぞと。


参考
ベクトルの大きさと2つのベクトルのなす角
基礎の基礎編その1 内積と外積の使い方

遠い昔に習ったような内容。
ベクトルはFlashを作っていて直線や円の交点を得たい時とかもググると沢山でてきますが、目的があってはじめてこの手の公式も理解しようとするというか…。
数学的なところがキチンとわかっていたらいいんですけどね…。
もうチョイ実用的にその性質を使って出来る表現なんかがどこかにまとまっててくれるといんだけれどなぁ。
とりあえず参考を読んで目的のドラッグ表現に使えることがわかります。

今回、応用できそうなのが、

ベクトルのなす角でドラッグで変化した角度が得られる。
ベクトルの外積で左右どちらに角度が変化するか得られる。
ベクトル同士の加算で得たベクトルの大きさで移動量が得られる。

ここまでの確認をしたのがこちら。ドラッグしてベクトルを描くとその演算結果を表示。

サンプル:ベクトルの加算、なす角、外積

This movie requires Flash Player 10.0.0

続いてこの結果を表示に反影する。
任意の支点で移動したり回転するにはfl.motionパッケージ内のfl.motion.MatrixTransformerを使うと簡単に出来る。メソッド一つで出来るようになったのはAS3から。便利になったなぁと。

参考
FN0708001 – 指定座標を中心にMovieClipを回転させる – Flash : テクニカルノート
fl.motion.MatrixTransformer (ActionScript 3.0)

移動と回転に使うMatrixTransformerのメソッドの使い方。
表示オブジェクトの内の任意の支点をMouseEventで取得してPointオブジェクトに格納します。こちらはその表示オブジェクトの座標空間で取得した座標を格納します。(MouseEventのlocalX,localY)
移動先の座標をグローバル座標で取得してPointオブジェクトに格納します。
表示オブジェクトのtransform.matrixを取得してMatrixTransformerのメソッドでMatrixを変更します。

表示オブジェクトの内の任意の支点を軸にして回転するメソッドが、
MatrixTransformer .rotateAroundInternalPoint(
変更する表示オブジェクトのMatrix,
変更する表示オブジェクト内の支点となるx座標,
変更する表示オブジェクト内の支点となるy座標,
回転させる角度)

表示オブジェクト内の任意の支点をグローバル座標の任意の点に一致させるメソッドが、
MatrixTransformer .matchInternalPointWithExternal(
変更する表示オブジェクトのMatrix、
変更する表示オブジェクト内の支点,
移動先となるグローバル座標)

サンプル:任意の支点で回転しながらドラッグ

This movie requires Flash Player 10.0.0

package index{
 
 
	import caurina.transitions.Tweener;
	import sketchbook.external.tweener.MatrixShortcuts;
	MatrixShortcuts.init();
	import flash.events.*;
	import flash.geom.*;
	import fl.motion.MatrixTransformer;
	import jp.progression.casts.CastSprite;
	import org.flintparticles.common.utils.Maths;
 
 
	//マウスダウンした位置からドラッグで回転
	public class Picture extends CastSprite{
 
 
		public function Picture(initObject:Object=null){
			super(initObject);
 
			this.buttonMode		= true;
			this.mouseChildren	= false;
 
			//中心
			var rect:Rectangle	= this.getRect( this );
			var _x:Number		= rect.width / 2;
			var _y:Number		= rect.height/ 2;
				_x += rect.left;
				_y += rect.top;
			this.center	= new Point( _x, _y )
		}
 
 
 
 
 
		protected override function _onCastAdded():void{
			this.addEventListener( MouseEvent.MOUSE_DOWN, this.mouseDownHandler, false, 0, true );
			this.stage.addEventListener( MouseEvent.MOUSE_UP, this.mouseUpHandler, false, 0, true );
		}
 
 
 
		public var center:Point;	//中心
		public var fulcrum:Point;	//支点
 
 
		public function mouseDownHandler(e:MouseEvent):void{
			this.fulcrum	= new Point( e.localX, e.localY );
			this.stage.addEventListener( MouseEvent.MOUSE_MOVE, this.mouseMoveHandler, false, 0, true );
		}
 
 
 
		public function mouseUpHandler(e:MouseEvent):void{
			this.stage.removeEventListener( MouseEvent.MOUSE_MOVE, this.mouseMoveHandler );
			this.mouseMoveHandler(e);
		}
 
 
 
 
 
		public function mouseMoveHandler(e:MouseEvent):void{
			var mPt:Point		= new Point( e.stageX, e.stageY );
 
			var g_center:Point	= this.localToGlobal( this.center );
			var g_fulcrum:Point	= this.localToGlobal( this.fulcrum );
 
			var aPt:Point	= g_fulcrum.subtract( g_center );
			var bPt:Point	= mPt.subtract( g_center );
 
 
			var cos:Number	= this.dot( aPt, bPt ) / ( aPt.length * bPt.length );
			var _r:Number	= Math.acos( cos );
			var _d:Number	= Maths.asDegrees( _r );
 
 
			var cross:Number	= this.cross( aPt, bPt );
			var d:Number		= ( cross<0 ) ? -1 : ( 0<cross ) ? 1 : 0;
 
 
			var add:Point		= aPt.add( bPt );
			var force:Number	= add.length;
				force /= Math.sqrt( Math.pow( 250, 2 ) );
				force = ( force<1 ) ? force : 1;
 
 
			Tweener.removeTweens( this );
			var mat:Matrix = this.transform.matrix.clone();
			MatrixTransformer.rotateAroundInternalPoint( mat, this.fulcrum.x, this.fulcrum.y, (_d * d) * force );
			MatrixTransformer.matchInternalPointWithExternal( mat, this.fulcrum, mPt );
 
			Tweener.addTween( this, { _matrix:mat, transition:"easeNone", time:0.1,
				onUpdate:function():void{
					this.scaleX = this.scaleY = 1;
				}
			});
		}
 
		//内積
		public function dot( a:Point, b:Point ):Number{
			return a.x * b.x + a.y * b.y;
		}
 
		//外積
		public function cross( a:Point, b:Point ):Number{
			return a.x * b.y - b.x * a.y;
		}
 
 
	}
}

最終的なMatrixの変更に少しだけ遅延させたくfladdictさんのTweener拡張、MatrixShortcut.asをお借りしました。

_matrixで丸ごとトゥイーンさせた時、トゥイーンの最中にスケールが変化しちゃうのは僕だけ?
ちょっとわからなかったのでupDateで無理矢理スケールを戻してます。

“任意の支点で回転しながらドラッグする” に コメントはありません

コメントをどうぞ