ActionScript 3.0で始めるオブジェクト指向スクリプティング

第52回 ベクトルの外積で回転の軸を定める

この記事を読むのに必要な時間:およそ 6.5 分

前回のベクトルの内積に続いて,今回は外積を学ぶ。外積はふたつのベクトルのどちらにも,垂直なベクトルを求める計算だ。ふたつの(平行でない)ベクトルで,ひとつの面が定められる。したがって,ベクトルの外積は,面に垂直なベクトルを導くともいえる。お題としては,第50回「立方体の4面にテクスチャを貼って上下左右に回す」スクリプト3に手を加える。

回転の軸を定める

第50回スクリプト3は,⁠3次元空間で上下左右に回す立方体の4面にテクスチャマッピング」した第50回図1再掲)⁠今回,この動きはそのままに,回転の内部処理を書替える。第50回のスクリプトは,マウスポインタを水平に動かすとy軸で,垂直に動かすとx軸で回した。そして,斜めの動きは水平軸と垂直軸に分けて,3次元空間座標を回転した。今回はふたつを分けずに,回転軸そのものを動かそう。

図1 上下左右に回した立方体に面の前後を整えて塗る(再掲)

図1 上下左右に回した立方体に面の前後を整えて塗る(再掲) 図1 上下左右に回した立方体に面の前後を整えて塗る(再掲)

幸いにして,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 外積はふたつのベクトルに垂直なベクトルを表す)

図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);
}

著者プロフィール

野中文雄(のなかふみお)

ソフトウェアトレーナー,テクニカルライター,オーサリングエンジニア。上智大学法学部卒,慶応義塾大学大学院経営管理研究科修士課程修了(MBA)。独立系パソコン販売会社で,総務・人事,企画,外資系企業担当営業などに携わる。その後,マルチメディアコンテンツ制作会社に転職。ソフトウェアトレーニング,コンテンツ制作などの業務を担当する。2001年11月に独立。Web制作者に向けた情報発信プロジェクトF-siteにも参加する。株式会社ロクナナ取締役(非常勤)。

URLhttp://www.FumioNonaka.com/

著書

コメント

コメントの記入