トップへ戻るニュースフォーラムFLASH-ML 過去ログBak@Flaダウンロードよくある質問と答
ログイン
ユーザ名:

パスワード:


パスワード紛失

新規登録
メインメニュー
メイン
   コーダーズルーム【スクリプト系】
     アフィン変換で指定した角度へ回転させる
投稿するにはまず登録を

スレッド表示 | 新しいものから 前のトピック | 次のトピック | 下へ
投稿者 スレッド
ゲスト
Åê¹ÆNo.9306
投稿日時: 2004-7-21 2:31
アフィン変換で指定した角度へ回転させる
こんにちわ。

タイトルの通り、アフィン変換を使って、指定角へ回転させるようとしています。
http://www.gumob.com/3d/3d.html

左側のテキストフィールドに表示される数字ですが、それぞれ
pMode       現在のモード(switch文で切り替えています)
pX,pY,pZ      現在のXYZ座標
degX,degY,degZ   各XYZ軸に対する角度
tDegX,tDegY,tDegZ ターゲット角度

右下にある四角いボタンをクリックすると、その角度へ回転するようにしています。
が、実際にクリックすると、挙動が怪しい・・・
かれこれ2?3日この状態で、一向に抜け出せません・・・
degX,degY,degZの値は、正常にターゲット角へ収束しているのですが。

全体は、

ターゲット角を指定し、
setTargetDegree(引数x,引数y,引数z)
現在の角度をターゲット角に近ける
setAngle();
ラジアン角に変換し
degreeToRadian()
アフィン変換を行い
rotateMe()
更に透視変換を行い
persTransform();
回転各がターゲット角と同じになったらデフォルトモードに(何もしないモード)
setRotateModeToDefault ();

といった流れになっています。
どなたか原因がお分かりになる方いましたら、ご教授お願いします・・・

最終的には、テキストが三次元空間に浮いていて、クリックすると、そのテキストが全面に出てきて、他は背面に移動するような物?を考えています。

以下ソースです。長いかな・・・

main.as

//初期設定を保存するクラス
function initPointClass(){
	this.pArray = new Array();
	this.pArray[0] = {x:0, y:0, z:-160, txt:"moe0"};
	// 今のところ一つだけでテスト
	/*
	this.pArray[1] = {x:-60, y:120, z:-80, txt:"moe1"};
	this.pArray[2] = {x:-120, y:-90, z:0, txt:"moe2"};
	*/
}

// インスタンスを生成
function main() {
	Object.registerClass("p_mc", Point3dClass);
	for (var i = 0; i<initPoint.pArray.length; i++) {
		var props = {};
		props.pX = initPoint.pArray[i].x;
		props.pY = initPoint.pArray[i].y;
		props.pZ = initPoint.pArray[i].z;
		this.attachMovie("p_mc", "p"+i, i, props);
		this["p"+i].p_txt.text = initPoint.pArray[i].txt;
		this["p"+i].pMode ="reset";
		delete props;
	}
}

initPoint = new initPointClass();
main();


Point3d.as

//
// Point3dClass
//
function Point3dClass() {
	// 定数 
	this.PI0 = Math.PI;
	this.PI1 = Math.PI/180;
	this.PI2 = 180/Math.PI;
	// 原点
	this.pXo = 0;
	this.pYo = 0;
	this.pZo = 200;
	// 視点
	this.vp = 0;
	// XYZ座標(常時更新)
	this.pX, this.pY, this.pZ;
	// デフォルトの座標
	this.defPX, this.defPY, this.defPZ;
	// 回転角(常時更新)
	this.degX, this.degY, this.degZ;
	// デフォルトの回転角
	this.defDegX, this.defDegY, this.defDegZ;
	// ターゲットとする角度
	this.tDegX, this.tDegY, this.tDegZ;
	// ラジアン角(常時更新)
	this.radX, this.radY, this.radZ;
	// 半径(常時更新)
	this.radius;
	// モード管理用
	this.pMode;
	// 初期パラメータを突っ込む
	this.getDegree();
	this.setDefaultParam();
}

Point3dClass.prototype = new MovieClip();

// set default parameter
Point3dClass.prototype.setDefaultParam = function(){
	this.defPX = this.pX;
	this.defPY = this.pY;
	this.defPZ = this.pZ;
	this.defDegX = this.degX;
	this.defDegY = this.degY;
	this.defDegZ = this.degZ;
}


// change view point
Point3dClass.prototype.changeVp = function(arg) {
	if (Math.round(this.vp) == arg) {
		this.vp = arg;
	} else {
		this.vp += (arg-this.vp)/10;
	}
};

// get temporary radius
Point3dClass.prototype.getRadius = function() {
	this.radius = Math.sqrt(this.pX*this.pX+this.pY*this.pY+this.pZ*this.pZ);
};

// get temporary degree
Point3dClass.prototype.getDegree = function() {
	this.degX = Math.atan2(this.pY, this.pZ)*this.PI2;
	this.degY = Math.atan2(this.pX, this.pZ)*this.PI2;
	this.degZ = Math.atan2(this.pY, this.pX)*this.PI2;
	// converting the return value of atan2
	if(this.degX<0){
		this.degX += 360;
	}
	if(this.degY<0){
		this.degY += 360;
	}
	if(this.degZ<0){
		this.degZ += 360;
	}
};

// set target degree
Point3dClass.prototype.setTargetDegree = function(arg0, arg1, arg2) {
	var arg0, arg1, arg2;
	if(this.degX!=arg0){
		this.tDegX = arg0;
	}
	if(this.degY!=arg1){
		this.tDegY = arg1;
	}
	if(this.degZ!=arg2){
		this.tDegZ = arg2;
	}
};

// change degree to target
Point3dClass.prototype.setAngle = function() {
	// rotate around X axis
	if (Math.round(this.degX) != Math.round(this.tDegX)) {
		this.degX += (this.tDegX-this.degX)/5;
		if (this.degX>=360) {
			this.degX -= 360;
		} else if (this.degX<=0) {
			this.degX += 360;
		}
	}
	// rotate around Y axis
	if (Math.round(this.degY) != Math.round(this.tDegY)) {
		this.degY += (this.tDegY-this.degY)/5;
		if (this.degY>=360) {
			this.degY -= 360;
		} else if (this.degY<=0) {
			this.degY += 360;
		}
	}
	// rotate around Z axis
	if (Math.round(this.degZ) != Math.round(this.tDegZ)) {
		this.degZ += (this.tDegZ-this.degZ)/5;
		if (this.degZ>=360) {
			this.degZ -= 360;
		} else if (this.degZ<=0) {
			this.degZ += 360;
		}
	}
};

// set pMode to default
Point3dClass.prototype.setRotateModeToDefault = function(){
	if (Math.round(this.degX) == Math.round(this.tDegX) && Math.round(this.degY) == Math.round(this.tDegY) && Math.round(this.degZ) == Math.round(this.tDegZ)) {
		this.degX = Math.round(this.degX);
		this.degY = Math.round(this.degY);
		this.degZ = Math.round(this.degZ);
		this.pMode = "default";
	}
}

// convert degree to radian
Point3dClass.prototype.degreeToRadian = function() {
	this.radX = this.degX*(this.PI1);
	this.radY = this.degY*(this.PI1);
	this.radZ = this.degZ*(this.PI1);
};

// rotate clip (affin transformation)
Point3dClass.prototype.rotateMe = function() {
	// rotate around X axis
	y = this.pY*Math.cos(this.radX)-this.pZ*Math.sin(this.radX);
	z = this.pY*Math.sin(this.radX)+this.pZ*Math.cos(this.radX);
	this.pY = y;
	this.pZ = z;
	// rotate around Y axis
	x = this.pX*Math.cos(this.radY)+this.pZ*Math.sin(this.radY);
	z = -this.pX*Math.sin(this.radY)+this.pZ*Math.cos(this.radY);
	this.pX = x;
	this.pZ = z;
	// rotate around Z axis
	x = this.pX*Math.cos(this.radZ)-this.pY*Math.sin(this.radZ);
	y = this.pX*Math.sin(this.radZ)+this.pY*Math.cos(this.radZ);
	this.pX = x;
	this.pY = y;
};

// scaling (affin transformation)
Point3dClass.prototype.scaleMe = function(arg) {
	var arg;
	this.pX = arg*this.pX;
	this.pY = arg*this.pY;
	this.pZ = arg*this.pZ;
}

// parallel displacement (affin transformation)
Point3dClass.prototype.displaceMe = function(arg0, arg1, arg2) {
	var arg0, arg1, arg2;
	this.pX = this.pX+arg0;
	this.pY = this.pY+arg1;
	this.pZ = this.pZ+arg2;
}

// scale clip & set postion (perspective transformation)
Point3dClass.prototype.persTransform = function() {
	var x, y, z;
	z = this.pZ+this.pZo;
	if (z>0) {
		_visible = true;
		x = this.pX*this.vp/z;
		y = this.pY*this.vp/z;
		this._x = x+this.pXo;
		this._y = y+this.pYo;
		this._xscale = Math.round(100*this.vp/z);
		this._yscale = Math.round(100*this.vp/z);
		this._alpha = Math.min(100, Math.round(100*this.vp/z));
		this.swapDepths(1000-Math.round(this.pZ));
	} else {
		_visible = false;
	}
};

//
// event handler
//

Point3dClass.prototype.onRelease = function() {
	for(var i=0; i < initPoint.pArray.length; i++){
		if(i==this._name.slice(1)){
			this.setTargetDegree(0,0,0);
			this.setVelocity();
			this.pMode = "rotateToTarget";
			trace(this._name);
		}else{
			_parent.main["p"+i].setTargetDegree(_parent.main["p"+i].defDegX, _parent.main["p"+i].defDegY, _parent.main["p"+i].defDegZ);
			_parent.main["p"+i].setVelocity();
			_parent.main["p"+i].pMode = "rotateToTarget";
		}
	}
};

Point3dClass.prototype.onEnterFrame = function() {
	switch (this.pMode) {
		case "rotateToTarget" :
			this.setAngle();
			this.degreeToRadian();
			this.rotateMe();
			this.persTransform();
			this.setRotateModeToDefault ();
			break;
		case "scale" :
			break;
		case "reset" :
			this.changeVp(120);
			this.persTransform();
			break;
		case "default" :
			break;
	}
	this.output();
};

// output data on text field
Point3dClass.prototype.output = function() {
	with (this) {
		// point mode
		data_mc.pMode_txt = "pMode : "+pMode;
		// radius
		data_mc.radius_txt = "radius : "+Math.sqrt(pX*pX+pY*pY+pZ*pZ);
		// position
		data_mc.pX_txt = "pX : "+pX;
		data_mc.pY_txt = "pY : "+pY;
		data_mc.pZ_txt = "pZ : "+pZ;
		// radian
		data_mc.radX_txt = "radX : "+radX;
		data_mc.radY_txt = "radY : "+radY;
		data_mc.radZ_txt = "radZ : "+radZ;
		// degree
		data_mc.degX_txt = "degX : "+degX;
		data_mc.degY_txt = "degY : "+degY;
		data_mc.degZ_txt = "degZ : "+degZ;
		//target degree
		data_mc.tDegX_txt = "tDegX : "+tDegX;
		data_mc.tDegY_txt = "tDegY : "+tDegY;
		data_mc.tDegZ_txt = "tDegZ : "+tDegZ;
	}
};
野中文雄
Åê¹ÆNo.9309
投稿日時: 2004-7-21 6:43
ちょんまげら
居住地: 東京
投稿: 4531
使用環境:
CS5.5 .6.8 Vista Home Premium (SP1)
Re: アフィン変換で指定した角度へ回転させる
「意図する」動作が明記されていませんので、どの「挙動が怪しい」のかはっきりとはわかりかねます。ただ、「正常にターゲット角へ収束している」ということですので、途中経過のイージングの動作を問題にされているものと「推測」します。
引用:
futchさんは書きました:
タイトルの通り、アフィン変換を使って、指定角へ回転させるようとしています。
http://www.gumob.com/3d/3d.html
...[中略]...
右下にある四角いボタンをクリックすると、その角度へ回転するようにしています。
が、実際にクリックすると、挙動が怪しい・・・
かれこれ2?3日この状態で、一向に抜け出せません・・・
degX,degY,degZの値は、正常にターゲット角へ収束しているのですが。

長いですね。角度は基本的にラジアン値で扱うようにした方が、すっきりとするでしょう。必要があれば、入力と表示のみ度数に変換します。
引用:
以下ソースです。長いかな・・・

イージングを考えるうえで気になるのは、角度を0から360の範囲に切揃えていることです。そうすると、350度から2010(修正)度の時計回りの回転は、表現できないことになります。ちなみに、以下のスクリプトでdegZ == 0または360のときの処理が、ダブっているように思われます(値は変化するようですから、無限ループにはならないでしょうが)。

問題を絞込むためにも、たとえばZ軸を中心としたXY座標の回転のみを試されてはいかがでしょうか?
//
// Point3dClass
//
// change degree to target
Point3dClass.prototype.setAngle = function() {
	// ...[中略]...
	// rotate around Z axis
	if (Math.round(this.degZ) != Math.round(this.tDegZ)) {
		this.degZ += (this.tDegZ-this.degZ)/5;
		if (this.degZ>=360) {
			this.degZ -= 360;
		} else if (this.degZ<=0) {
			this.degZ += 360;
		}
	}
};


----------------
 

ゲスト
Åê¹ÆNo.9351
投稿日時: 2004-7-22 23:20
Re: アフィン変換で指定した角度へ回転させる
レスありがとうございます。

そうです。"挙動不審"の部分は、イージングの動作です。
説明不足ですね・・・申し訳ありません。

引用:
ただ、「正常にターゲット角へ収束している」ということですので、途中経過のイージングの動作を問題にされているものと「推測」します。


この判別式はおかしいですね。
ひとまず、この部分は取り除きました。

引用:
イージングを考えるうえで気になるのは、角度を0から360の範囲に切揃えていることです。そうすると、350度から10度の時計回りの回転は、表現できないことになります。ちなみに、以下のスクリプトでdegZ == 0または360のときの処理が、ダブっているように思われます(値は変化するようですから、無限ループにはならないでしょうが)。


併せて、角度のイージングはラジアン単位の計算に変更しました。

// change tmporary degree to target
Point3dClass.prototype.setAngle = function() {
	// rotate around X axis
	this.radX += (this.tRadX-this.radX)/5;
	// rotate around Y axis
	this.radY += (this.tRadY-this.radY)/5;
	// rotate around Z axis
	this.radZ += (this.tRadZ-this.radZ)/5;
};


上記の関数から求まる回転角(radX,radY,radZ)から、アフィン変換を用いて各座標(pX,pY,pZ)を求めているのですが

// rotate (affin transformation)
Point3dClass.prototype.rotateMe = function() {
	// rotate around X axis
	y = this.pY*Math.cos(this.radX)-this.pZ*Math.sin(this.radX);
	z = this.pY*Math.sin(this.radX)+this.pZ*Math.cos(this.radX);
	this.pY = y;
	this.pZ = z;
	// rotate around Y axis
	x = this.pX*Math.cos(this.radY)+this.pZ*Math.sin(this.radY);
	z = -this.pX*Math.sin(this.radY)+this.pZ*Math.cos(this.radY);
	this.pX = x;
	this.pZ = z;
	// rotate around Z axis
	x = this.pX*Math.cos(this.radZ)-this.pY*Math.sin(this.radZ);
	y = this.pX*Math.sin(this.radZ)+this.pY*Math.cos(this.radZ);
	this.pX = x;
	this.pY = y;
};


トレースしてみると、角度(radX)のイージングは綺麗に収束していますが、座標(pX)は正負バラバラの値が出力されてしまいます。
(実際のアニメーションも、スムーズに回転するのではなく、チカチカと点滅したようになってしまいます。)

pX   : 56.7584793909515 // 正
radX : 3.13399674864252
 
pX   : -10.0592876905937 // 負
radX : 3.13551592963198 // 正常に収束
 
pX   : -34.4533821474723 // 負
radX : 3.13673127442354 // 正常に収束
 
pX   : 73.9592354342181 // 正
radX : 3.13770355025679 // 正常に収束
 
pX   : -86.5820972797613 // 負
radX : 3.13848137092339 // 正常に収束
 
pX   : 78.9091901307664 // 正
radX : 3.13910362745667 // 正常に収束
 
pX   : -42.9351597881995 // 負
radX : 3.1396014326833 // 正常に収束
 
pX   : -0.497325283126798 // 負
radX : 3.1399996768646 // 正常に収束
 
pX   : 48.9182082911502 // 正(バラバラ)
radX : 3.14031827220963 // 正常に収束


ということは、rotateMe関数(アフィン変換の式)のどこかがおかしい。という事になりますが、おかしな箇所は見つけられませんでした。
思いっきり見落としがあるかもしれませんが・・・

引き続き問題を絞込み作業を続けたいと思いますが、何かお気付きの点がありましたら、ご教授願います。
何度もすいません・・・
ゲスト
Åê¹ÆNo.9358
投稿日時: 2004-7-23 11:42
Re: アフィン変換で指定した角度へ回転させる
自己レスです。
原因が何となく分かりました。
アフィン変換を勘違いしていた様です。
代入した角度(回転中の現在の角度)から求まる値(座標)は、「三次元空間上の絶対的?な値」であるものと思っていました。
求まるのは「変換前に対する変換後の相対的な値」ですね。(汗
よってトレース結果の値がバラバラ。というのも納得です。
(うまく言えないですが・・・自分で書いていて良く分かりません)

setAngle関数を書き換えればうまくいきそうなので、トライしてみます。
野中文雄
Åê¹ÆNo.9388
投稿日時: 2004-7-25 9:32
ちょんまげら
居住地: 東京
投稿: 4531
使用環境:
CS5.5 .6.8 Vista Home Premium (SP1)
Re: アフィン変換で指定した角度へ回転させる
「350度から20度の時計回りの回転」でした。 訂正ついでに、補足しておきます。この部分が問題と関わったかどうかはわかりませんが。

以下のスクリプトを、リンケージ識別子"mySymbol"のMovieClipシンボル第1フレームアクションに設定します。インスタンスを7回クリックして360度を超えると、回転が止まらなくなります。問題を解消するには、コメントアウト部分を入替えます。
// MovieClipシンボル: リンケージ識別子"mySymbol"
// MovieClip: クリックすると60度ずつイーズアウトしながら回転
// 第1フレームアクション
#initclip
function Point3dClass() {
  this.degZ = this.tDegZ=this._rotation;
  this.onRelease = function() {
    this.setTargetDegree(this.tDegZ += 60);
    this.onEnterFrame = this.setAngle;
  };
}
// Point3dClass.prototype.onEnterFrame = this.setAngle;
// set target degree
Point3dClass.prototype.setTargetDegree = function(arg) {
  if (this.degZ != arg) {
    this.tDegZ = arg;
  }
};
Point3dClass.prototype.setAngle = function() {
  // rotate around Z axis
  if (Math.round(this.degZ) != Math.round(this.tDegZ)) {
    this.degZ += (this.tDegZ-this.degZ)/5;
    //* 以下をコメントアウト
    if (this.degZ>=360) {
      this.degZ -= 360;
    } else if (this.degZ<0) {
      this.degZ += 360;
    }
    //*/ コメントアウトここまで
    this._rotation = this.degZ;
  } /* else {  // 以下のステートメントを有効に
    this._rotation = this.degZ=this.tDegZ=(this.tDegZ%360+360)%360;
    delete this.onEnterFrame;
  } */
  trace([this._rotation, this.degZ]);  //【確認用】
};
Object.registerClass("mySymbol", Point3dClass);
#endinitclip

引用:
futchさんは書きました:
この判別式はおかしいですね。
ひとまず、この部分は取り除きました。
引用:
イージングを考えるうえで気になるのは、角度を0から360の範囲に切揃えていることです。そうすると、350度から10度の時計回りの回転は、表現できないことになります。ちなみに、以下のスクリプトでdegZ == 0または360のときの処理が、ダブっているように思われます(値は変化するようですから、無限ループにはならないでしょうが)。


----------------
 

ゲスト
Åê¹ÆNo.9423
投稿日時: 2004-7-26 14:41
Re: アフィン変換で指定した角度へ回転させる
レスありがとうございます。

なるほど。
無限ループにハマっているのですね。

if (this.degZ>=360) {
	this.degZ -= 360;
} else if (this.degZ<0) {
	this.degZ += 360;
}


こちらの剰余演算を用いて角度を360度以内に制限しているのは分かるのですが、入れ子になっているのは何故でしょうか?
this._rotation = this.degZ=this.tDegZ=(this.tDegZ%360+360)%360;


こうした場合でも動くと思うのですが、上の表記とどのように異なるのでしょう??
this._rotation = this.degZ=this.tDegZ=this.tDegZ%360;
ゲスト
Åê¹ÆNo.9427
投稿日時: 2004-7-26 15:17
Re: アフィン変換で指定した角度へ回転させる
回転に関してはスムーズに表示されるようになりました。
http://www.gumob.com/3d/3d_2.html (source code)

トレース結果の値がバラバラという問題は、

// setAngle関数の一部
tmpX = tmpX*inertia+(tRadX-radX)*k;
radX += tmpX;
newRadX = radX;
angleX = newRadX - oldRadX;
oldRadX = radX;

とする事で、1フレーム前と現フレームの角度の差分を取ってから、アフィン変換の関数に代入する事で対応させました。

出力部分にrealRadが追加されてますが、モニタ用にフレーム毎のMath.atan2で各軸に対する角度を求めています。
realRadXYZとradXYZの値を比べてみると、違う値が出力されています・・・
結果的に、ターゲットとは異なる位置に、MCが移動してしまいます。

ひとまず、ターゲット角の求め方を、回転させたい方向のベクトルを求めてから、角度に変換するようにしてみましたが、意味がなかった様です・・・(汗
//setTargetRadian関数
vectorX = Math.atan2(argY, argZ);
if(vectorX<0){
	vectorX += 2*PI0;
}
if(radX!=vectorX){
	tRadX = vectorX;
}else if(radX==vectorX){
	tRadX = radX;
}
野中文雄
Åê¹ÆNo.9443
投稿日時: 2004-7-26 19:47
ちょんまげら
居住地: 東京
投稿: 4531
使用環境:
CS5.5 .6.8 Vista Home Premium (SP1)
Re: アフィン変換で指定した角度へ回転させる
問題は「無限ループ」というより、値が360で「非連続」となることです。
引用:
futchさんは書きました:
無限ループにハマっているのですね。

-540を0≦θ<360の値180に変換するためです。
引用:
こちらの剰余演算を用いて角度を360度以内に制限しているのは分かるのですが、入れ子になっているのは何故でしょうか?
this._rotation = this.degZ=this.tDegZ=(this.tDegZ%360+360)%360;

こうした場合でも動くと思うのですが、上の表記とどのように異なるのでしょう??
this._rotation = this.degZ=this.tDegZ=this.tDegZ%360;


----------------
 

スレッド表示 | 新しいものから 前のトピック | 次のトピック | トップ

投稿するにはまず登録を
 
Copyright (C) 2003 FLASH-japan. All rights reserved.
Powered by Xoops