前回のベクトルの内積に続いて,今回は外積を学ぶ。外積はふたつのベクトルのどちらにも,垂直なベクトルを求める計算だ。ふたつの(平行でない)ベクトルで,ひとつの面が定められる。したがって,ベクトルの外積は,面に垂直なベクトルを導くともいえる。お題としては,第50回「立方体の4面にテクスチャを貼って上下左右に回す」のスクリプト3に手を加える。
回転の軸を定める
第50回スクリプト3は,「3次元空間で上下左右に回す立方体の4面にテクスチャマッピング」した(第50回図1再掲)。今回,この動きはそのままに,回転の内部処理を書替える。第50回のスクリプトは,マウスポインタを水平に動かすとy軸で,垂直に動かすとx軸で回した。そして,斜めの動きは水平軸と垂直軸に分けて,3次元空間座標を回転した。今回はふたつを分けずに,回転軸そのものを動かそう。
幸いにして,Matrix3D.appendRotation()メソッドに渡す第2引数の回転軸はxyz軸(Vector3Dクラスの定数)にかぎらない。長さ1のVector3Dオブジェクトで回転軸を自由に定められる。
Matrix3D.appendRotation(回転の角度, 回転軸のVector3Dオブジェクト)
そこで考えなければならないのは,回転軸をどのようにして決めるかだ。マウスポインタを動かした方向だなどと,早とちりしてはいけない。マウスポインタに合わせるのは回転の動きであって,求めたいのは回す中心軸なのだ。
現に,マウスポインタをx軸方向に水平に動かすとy軸で回し,y軸の垂直方向に動かしたときはx軸で回転した。つまり,マウスポインタの動きと回転軸は,互いに垂直になる。もっとも,まだこれでは足りない。3次元空間では1本の直線と(ある点で)垂直に交わる直線は無数に考えられるからだ。たとえば,xy平面上の原点(0, 0)を通る直線なら,3次元空間ではすべてz軸と原点(0, 0, 0)で垂直に交わる。
初めにほのめかしたように,ふたつの(平行でない)ベクトルでひとつの面が定められる。そして,その平面と(ある点で)垂直に交わる直線は1本に決まるのだ。つまり,マウスポインタを動かした方向に加えて,ベクトルがもうひとつ要る。それはz軸だ。
ベクトルの外積から垂直な線を求める
マウスポインタの動きとz軸というふたつのベクトルが決まった。これで外積が求められる。ふたつのベクトルをAとBとすると,外積は「A×B」で表される。ただし,ベクトルの掛け算ではない。外積A×Bは,ベクトルAとBのどちらにも垂直なベクトルを表す(図1)。
ベクトルは大きさと方向をもつ。もっとも,今回のお題では,外積は回転軸を定めるために使う。すると,大きさはどうでもよい。だが,方向は気にしなければならない。ふたつのベクトルに垂直な直線は1本に決まっても,ふたつの方向がありえるからだ。回転軸の方向が逆になると,回る向きも反対になってしまう。
したがって,ベクトルの外積では,計算の順序が大切になる。外積A×Bでは,ベクトルAからBの方向にドライバーを回すと想像してほしい。普通使われる右ネジは,時計回りに締める。その右ネジの進む向きに,外積A×Bのベクトルは定められている(前掲図1)。
さて,外積を求めるのはVector3D.crossProduct()メソッドだ。参照するVector3Dオブジェクトに対して,もうひとつのVector3Dオブジェクトを引数として呼出し,戻り値は外積のVector3Dオブジェクトになる。
Vector3Dオブジェクト.crossProduct(もうひとつのVector3Dオブジェクト)
ここまでのところで,前回学んだ内積を求めるVector3D.dotProduct()メソッドとの違いはふたつある。まず,Vector3D.dotProduct()メソッドの戻り値は数値で,Vector3D.crossProduct()メソッドは外積のベクトルであるVector3Dオブジェクトを返す。つぎに,メソッドの参照と引数のふたつのVector3Dオブジェクトを入替えたとき,内積の戻り値は変わらないのに対して,外積が返すVector3Dオブジェクトはベクトルの方向が逆になる。
第50回スクリプト3の中で今回手を加えるのは,DisplayObject.enterFrameイベントのリスナー関数(xRotate())と座標変換の関数(xTransform())のふたつだ。修正を終えたフレームアクション全体は,後でスクリプト1として掲げる。
まず,DisplayObject.enterFrameイベントのリスナー関数(xRotate())は,回転の軸と回転角の大きさを計算する。そのために,マウスポインタのxy座標から,回す方向のVector3Dオブジェクト(mouseVector3D)を求める。そして,z軸(定数Vector3D.Z_AXIS)と回す方向のふたつのベクトルの外積から,回転軸のVector3Dオブジェクト(axisVector3D)を得る。回転角の大きさは,回す方向のベクトルの長さ(Vector3D.lengthプロパティ)をもとに計算した(nRotation)。
こうして求めた回転の軸(axisVector3D)と回転角の大きさ(nRotation)は,座標変換の関数(xTransform())に渡して呼出す。したがって,この関数の引数と処理内容を少し変えることになる。なお,Vector3D.normalize()メソッドは参照するVector3Dオブジェクトの方向はそのままで,長さ(Vector3D.lengthプロパティ)を1にする。
// フレームアクションを修正
function xRotate(eventObject:Event):void {
// var nRotationY:Number = mySprite.mouseX * nDeceleration;
// var nRotationX:Number = mySprite.mouseY * nDeceleration;
// 回す方向のベクトルを求める
var mouseVector3D:Vector3D = new Vector3D(mySprite.mouseX, -mySprite.mouseY, 0);
// z軸と回す方向のベクトルとの外積を求める
var axisVector3D:Vector3D = Vector3D.Z_AXIS.crossProduct(mouseVector3D);
var vertices2D:Vector.<Number> = new Vector.<Number>();
var nRotation:Number = mouseVector3D.length * nDeceleration;
axisVector3D.normalize(); // ベクトルの長さを1にする
// xTransform(vertices2D, nRotationY, nRotationX);
xTransform(vertices2D, nRotation, axisVector3D); // 引数を変更
xSetOrder();
xDraw(vertices2D);
}


