アクセシビリティ
デベロッパーリソース
Fumio Nonaka

野中 文雄氏

Fumio Nonaka

http://www.fumiononaka.com/
作成日:
2008年10月8日
ユーザレベル:
中級, 上級
製品:
Flash

Matrixクラス - 変換行列

サンプルファイルのダウンロード

Matrixクラスを使うと、DisplayObjectインスタンスの2次元平面上の移動や伸縮、回転、傾斜などの変形ができます。また、それらを組合わせて、長方形のインスタンスを平行四辺形に変えられます。ですから、たとえば3次元の表現に利用することも可能です。本稿は、このMatrixクラスについて、簡単なサンプルスクリプトをご紹介しながら解説していきます[*1]。なお、サンプルにはActionScript 3.0を用います。

[*1] 本稿は、2008年9月13日に開催された「Flash Power Session 2008 Osaka」における講演をもとに、時間の都合で割愛した基本説明や補足を加えて、新たにFlashデベロッパーセンター向けの記事として書いたものです。

01 変換行列(Matrixクラス)とは

「Matrix」というのは、英語で数学の「行列」を意味します(あの映画のことではありません)。もっとも、ActionScript 3.0のMatrixクラス[*2]は、数学の行列一般の演算を行うものではなく、インスタンスの座標空間を変換する3行×3列の行列[*3]です。Matixクラスが扱うこの行列を「変換行列」と呼びます。

3×3の行列は、9つの要素をもちます。Matrixクラスの変換行列では、第3行目の要素は値が固定されていて操作できません。操作が可能な残り6つの要素には、つぎのようにプロパティ名が与えられています(上述のとおり、第3行目は値が「0 0 1」で固定です)[*4]。これら6つのプロパティを使って、インスタンスに伸縮、傾斜、回転、移動などの変換を加えることができます。

a: 水平方向の伸縮率 = 変換後の幅/もとの幅
b: 垂直方向の傾斜率 = 垂直方向の傾斜/もとの幅
c: 水平方向の傾斜率 = 水平方向の傾斜/もとの高さ
d: 垂直方向の伸縮率 = 変換後の高さ/もとの高さ
tx: 水平方向の移動ピクセル数
ty: 垂直方向の移動ピクセル数


変換行列を使うときの注意は、インスタンスに変換が加えられておらず、基準点が親インスタンスの座標(0, 0)に配置された状態を起点とするということです。このいわばデフォルト状態のとき、インスタンスの変換行列はつぎのように左上から右下の対角線の要素が1、他の要素が0になります[*5]

変換行列を操作するには、上記6つのプロパティを設定・変更するほか、次表001のようなメソッドが使えます。ただし、傾斜の変換を行うメソッドはなく、変換行列のプロパティを操作する必要があります。参考までに下図001には、変換行列の操作をシミュレートするサンプルSWFを掲げました。

表001■Matrixクラスの変換行列を操作するメソッド

メソッド 説明
translate(ピクセル数x:Number, ピクセル数y:Number):void 変換行列に、x軸とy軸方向それぞれについて、引数で指定されたピクセル数の平行移動を設定します。
scale(伸縮率x:Number, 伸縮率y:Number):void 変換行列に、x軸とy軸方向それぞれについて、引数で指定された小数値の比率の拡大・縮小を設定します。
rotate(回転角:Number):void 変換行列に、引数で指定されたラジアン角の回転を設定します。

図001■変換行列のシミュレーション


SWFを開いて、「Matrix」または「Rotate」のラジオボタンを選択したうえで、UIのボタンを操作するかテキストボックスに数値を入力する。

それでは、Matricクラスで変換行列を作成して、インスタンスに適用してみましょう。その手順は、つぎのとおりです。

【Matrixクラスで変換行列を作成してインスタンスに適用する手順】

  1. new Matrix()でコンストラクタを呼出して、Matrixインスタンスを生成する。
  2. Matrixインスタンスの前述6つのプロパティ(abcdtxty)に、変換のための値を設定する。
  3. Matrixインスタンスを、変換対象のインスタンスのtransform.matrixプロパティに設定する。

たとえば、タイムラインにMovieClipインスタンスmy_mcを配置したとします。そのインスタンスの幅を2倍、高さは1.5倍にし、水平および垂直方向に0.5の傾斜をかけるには、タイムラインにMatrixクラスを用いたつぎのようなフレームアクションを記述します(図002)。

var myMatrix:Matrix = new Matrix();
myMatrix.a = 2;
myMatrix.b = 0.5;
myMatrix.c = 0.5;
myMatrix.d = 1.5;
my_mc.transform.matrix = myMatrix;

図002■インスタンスのtransform.matrixに変換行列(Matrixインスタンス)を適用する

インスタンスは、幅が2倍、高さは1.5倍で、水平および垂直方向に0.5の傾斜がかかっている。

[*2] ActionScript 2.0にも、Matrixクラスは備わっています。Flash Player 8以降対応で、その考え方やプロパティ・メソッドの多くは、基本的にActionScript 3.0と同じです。

[*3] 行と列の数が同じ行列は「正方行列」と呼ばれます。変換行列は3×3の正方行列です。

[*4] Flash CS3の[ヘルプ]で[Matrix]クラスの説明を見ると、行列におけるプロパティbとcの要素の位置が入れ違っています(LiveDocsの英語版では、「Skewing or shearing」以外の図は修正されました)。

[*5] 対角の要素が1で他の要素が0の正方行列を「単位行列」といいます。

02 インスタンスの角をドラッグして変形する

Matrixクラスの変換行列を使って、前述の6つのプロパティを操作すると、長方形を任意の位置と大きさの平行四辺形に変換することができます。そこで、長方形(正方形)のインスタンスの3つの角にドラッグできるポイントとなるインスタンスを置き、そのドラッグした位置座標に合わせて変換行列を計算し、長方形のインスタンスを平行四辺形に変換してみましょう(図003)[*6]

図003■3つの角のポイントをドラッグするとインスタンスが変形する

ドラッグした3つのポイントの位置座標に合わせて、長方形のインスタンスが平行四辺形に変換される。

いきなりインスタンスの3つの角を操作するのでなく、ひとつずつ順にスクリプトを組立てていきます。まずは、右上隅の角から始めましょう。

ポイントのインスタンスには、ドラッグするためのフレームアクションが記述してあります。そして、インスタンスの変形は、ポイントのフレームアクションからメインタイムラインの関数を呼出して行います。フレームアクションは、ポイントのインスタンスをドラッグするたびに、変換のためのメインタイムラインの関数xTransform()を呼出しています(図004)[*7]。この関数は、まだメインタイムラインには定義されていません。

図004■ドラッグするポイントのインスタンスに記述されたフレームアクション

ドラッグの処理と、ターゲットのインスタンスを変換するためのメインタイムラインの関数xTransform()の呼出しが記述されている。

そこで、メインタイムラインに関数xTransform()を定義すれば、ポイントのインスタンスをドラッグする間関数が自動的に呼出されることになります。この関数に、目的の長方形(正方形)のインスタンスを変換するための処理を記述します。関数の定義の仕方は、つぎのとおりです(今回の関数には引数がなく、括弧()の後の戻り値のデータ型にはvoidを指定します)。

【関数の定義】

function 関数名([引数:データ型]):戻り値のデータ型 {
  // 関数が行うべき処理
}

前述のとおり、Matrix.aプロパティは、インスタンスの幅を拡大または縮小します。設定する値は、もとの幅を1とする小数値です。

a: 水平方向の伸縮率 = 変換後の幅/もとの幅

したがって、たとえばメインタイムラインのフレームアクションとして関数xTransform()に以下のような処理を記述すると、ポイントをドラッグしたとき(その方向や動きには関係なく)、インスタンスの幅が2倍に拡大されます。ただし、すでに注意したように、変換前のデフォルト状態のインスタンスを基準にしますので、単純にもとの幅の2倍に何度も設定されるだけで、拡大し続ける訳ではありません。

function xTransform():void {
  var myMatrix:Matrix = new Matrix();
  myMatrix.a = 2;
  my_mc.transform.matrix = myMatrix;
}

それでは、右上隅に置いたポイントのドラッグ位置に合わせて、目的の長方形(正方形)のインスタンスを変換してみます。使うMatrixクラスのプロパティは、前述の水平方向の伸縮率Matrix.aと垂直方向の傾斜率Matrix.bです。その値の計算方法はつぎのとおりでした。

a: 水平方向の伸縮率 = 変換後の幅/もとの幅
b: 垂直方向の傾斜率 = 垂直方向の傾斜/もとの幅

この計算式にしたがってふたつのプロパティを設定すれば、インスタンスの右上隅の位置座標が変換できます。変換対象の長方形のインスタンスがmy_mc、ドラッグする右上隅のポイントのインスタンスをpoint1_mcとします。変換対象のインスタンスmy_mcは、基準点(左上隅)を配置先タイムラインの基準点(0, 0)に合わせておく必要があります。インスタンスmy_mcとpoint1_mcを配置したメインタイムラインのフレームアクションに、関数xTransform()をつぎのように定義します(スクリプト001)。

スクリプト001■右上隅のポイントのドラッグによりインスタンスを変形する

// タイムライン: メイン
// 第1フレームアクション
function xTransform():void {
  var myMatrix:Matrix = new Matrix();
  my_mc.transform.matrix = myMatrix;   // デフォルトの変換行列を適用
  var nWidth:Number = my_mc.width;
  var n1x:Number = point1_mc.x;
  var n1y:Number = point1_mc.y;
  myMatrix.a = n1x/nWidth;
  myMatrix.b = n1y/nWidth;
  my_mc.transform.matrix = myMatrix;
}

上記スクリプト001で注意しなければならないのは、ふたつのプロパティMatrix.aMatrix.bへの代入式右辺で分母に指定しているインスタンスの幅(変数nWidthの値)の基準です。すでに何度か述べたとおり、これはインスタンスに変換が加わる前の、デフォルト状態の値を用いる必要があります。

そのため、上記スクリプト001の関数xTransform()では、第1ステートメントで新規のMatrixインスタンス(myMatrix)を作成したうえで、それをインスタンスmy_mcのtransform.matrixプロパティに適用しています。Matrixクラスのコンストラクタを引数なしに呼出せば、デフォルトの変換行列(対角の要素が1で他の要素が0)のインスタンスがつくられます。その変換行列を適用すれば、インスタンスmy_mcはデフォルト状態にもどります。よってその後、第3ステートメントでインスタンスmy_mcのDisplayObject.widthプロパティの値を取っているのです。

第4および第5ステートメントは、ドラッグしたポイントのインスタンスpoint1_mcのxy座標を調べています。そして、第6と第7ステートメントで、前記の計算式から導かれた値をプロパティMatrix.aMatrix.bに代入します。最後の第8ステートメントが、インスタンスmy_mcのtransform.matrixプロパティへの変換行列の代入です。

[ムービープレビュー]で試すと、右上隅のポイントをドラッグすれば、その位置に合わせてインスタンスが平行四辺形に変換されます(図005)。

図005■右上隅のポイントをドラッグするとインスタンスが変形する

関数xTransform()により、インスタンスに変換行列が適用されて、平行四辺形に変換される。

[*6] Matrixクラスの変換行列では、変換後も平行な2直線の関係は保たれます。つまり、長方形を平行四辺形にはできても、台形や自由な四角形に変換することはできないということです。したがって、長方形の3つの角の位置を決めると、残る4つ目の角の座標は自ずと定まります。なお、このような座標空間の変換を「アフィン変換」といいます。

[*7] ポイントのインスタンスに記述したフレームアクションは、変換行列の話題から逸れますので今回は触れません。実際のスクリプトは、サンプルファイルでお確かめください。また、マウスイベントの扱いについては、F-siteセミナー「イベントを制する者、ActionScript 3.0を制す」をご参照ください。

03 ふたつのポイントをドラッグしてインスタンスを変形する

右上角のポイントひとつについてインスタンスが正しく変形されることを確かめたら、左下隅の角に置いたポイントに対しても、同じ要領で処理を加えます。このように動作は小分けして確認し、段階を踏んで拡張していくことが、スクリプトを早く正確に組上げるコツにです。左下角のポイントには、point2_mcというインスタンス名をつけました。

用いるプロパティは、Matrix.cMtrix.dのふたつです。値の計算の仕方は、つぎのとおりでした。この式により左下隅のポイントに合わせてインスタンスmy_mcを変換するには、前掲スクリプト001に以下のような処理を加えます(スクリプト002)

c: 水平方向の傾斜率 = 水平方向の傾斜/もとの高さ
d: 垂直方向の伸縮率 = 変換後の高さ/もとの高さ

スクリプト002■左下隅のポイントのドラッグもインスタンスの変形に加える

// タイムライン: メイン
// 第1フレームアクション
function xTransform():void {
  var myMatrix:Matrix = new Matrix();
  my_mc.transform.matrix = myMatrix;
  var nWidth:Number = my_mc.width;
  var nHeight:Number = my_mc.height;
  var n1x:Number = point1_mc.x;
  var n1y:Number = point1_mc.y;
  var n2x:Number = point2_mc.x;
  var n2y:Number = point2_mc.y;
  myMatrix.a = n1x/nWidth;
  myMatrix.b = n1y/nWidth;>
  myMatrix.c = n2x/nHeight;
  myMatrix.d = n2y/nHeight;
  my_mc.transform.matrix = myMatrix;
}

基本的な考え方は、前掲スクリプト001と変わりません。異なるのは、設定するプロパティがMatrix.cMtrix.dになったこと、それにともなって設定値を計算する分母がインスタンスのデフォルトの高さであるDisplayObject.heightプロパティの値に変わること、およびドラッグするポイントのインスタンスがpoint2_mcになるということくらいです。

[ムービープレビュー]で確かめると、ふたつのポイントのドラッグ位置に合わせて、インスタンスが自由な平行四辺形に変換できるようになりました(図006)。ただし、インスタンスの左上隅の座標が動きませんので、まだ位置は変えられません。

図006■ふたつのポイントをドラッグしてインスタンスが変形できる

関数xTransform()に左下隅のポイント移動による変換行列の操作が加わり、自由な形状の平行四辺形に変形できる。

上記スクリプト002に定義した関数xTransform()の処理で、MatrixインスタンスのMatrix.aMatrix.bMatrix.cMatrix.dの4つのプロパティ値を代入している右辺に注目してみましょう。分数(割り算)の分子・分母が規則的なパターンで並んでいます。これは偶然ではありません。数学の行列というのは、このように要素を決まったパターンで計算できるように並べたものなのです。

myMatrix.a = n1x/nWidth;
myMatrix.b = n1y/nWidth;
myMatrix.c = n2x/nHeight;
myMatrix.d = n2y/nHeight;

その意味では、ActionScriptで行列の果たす役割は、さまざまなパラメータを処理しやすいように並べたフィルタや各種設定パネルに似たものと捉えることもできます[*8]。フィルタを使うのに、「しきい値」など難しい用語の意味や内部処理の仕方は知らなくても、どのパラメータを動かせばどういう結果になるのかがわかれば足ります。行列も同じように、各要素の値が結果にどういう影響を与えるのか理解すれば、利用することは可能です。

[*8] たとえば、[プロパティ]インスペクタで、インスタンスのカラースタイルから[詳細]を選ぶと、[拡張効果]ダイアログボックスが開けます(図007)。そこにはインスタンスのカラーについて、赤(R)緑(G)青(B)アルファ(A)の各チャネルごとにパラメータが並んでいて、各チャネルの設定を同じパターンで操作できます。

図007■カラースタイルの[拡張効果]ダイアログボックス

インスタンスのカラーについて、赤(R)緑(G)青(B)アルファの各チャネルごとにパラメータを設定できる。

04 3つのポイントによりインスタンスを自由な平行四辺形に変換する

最後に、左上隅の角に3つ目のポイントを加えます。そうすると、インスタンスのかたちだけでなく、位置も自由に変えられるようになります。移動(「平行移動」といいます)するために操作するMatrixクラスのプロパティは、Matrix.txおよびMatrix.tyでした。

tx: 水平方向の移動ピクセル数
ty: 垂直方向の移動ピクセル数

左上隅のポイントには、point0_mcというインスタンス名を設定しました。すると、ドラッグした3つのポイントの位置座標に合わせて目的の長方形(正方形)のインスタンスを変換するには、前掲スクリプト002につぎのような処理を加えます(スクリプト003)。

スクリプト003■3つのポイントのドラッグで自由な平行四辺形に変形する

// タイムライン: メイン;
// 第1フレームアクション
function xTransform():void {
  var myMatrix:Matrix = new Matrix();
  my_mc.transform.matrix = myMatrix;
  var nWidth:Number = my_mc.width;
  var nHeight:Number = my_mc.height;
  var n0x:Number = point0_mc.x;
  var n0y:Number = point0_mc.y;
  var n1x:Number = point1_mc.x-n0x;
  var n1y:Number = point1_mc.y-n0y;
  var n2x:Number = point2_mc.x-n0x;
  var n2y:Number = point2_mc.y-n0y;
  myMatrix.a = n1x/nWidth;
  myMatrix.b = n1y/nWidth;
  myMatrix.c = n2x/nHeight;
  myMatrix.d = n2y/nHeight;
  myMatrix.tx = n0x;
  myMatrix.ty = n0y;
  my_mc.transform.matrix=myMatrix;
}

このスクリプト003で気をつけたいのは、4つのプロパティMatrix.aMatrix.bMatrix.cMatrix.dに代入する値を計算する右辺の分子(n1x、n1y、n2x、n2y)です。インスタンスの左上隅(point0_mc)が動くようになりますので、右上隅(point1_mc)と左下隅(point2_mc)の座標にもとづいて各プロパティに設定する比率を計算するとき、左上隅から見た座標、つまりそれぞれの座標と左上角(n0x, n0y)との差を取る必要があります。

[ムービープレビュー]を見ると、3つのポイントをドラッグして、インスタンスをそれらの座標に合わせた自由な位置の平行四辺形に変換することができます(図008)。

図008■ドラッグした3点のポイントに合わせてインスタンスが変形する

インスタンスの位置も含めて、自由な平行四辺形に変換できる。

長方形を任意の平行四辺形に変換する処理は、3次元の表現に利用することが考えられます。したがって、変換行列を応用すれば、3次元の立方体のアニメーションをシミュレートすることもできます(図009)[*9]

図009■立方体を3次元で回転する表現

6面の角の座標と変形を演算処理すれば、3次元の立方体が表現できる。

[*9] この3次元立方体の回転をシミュレートするスクリプトは、拙著『ActionScript 3.0プロフェッショナルガイド』(テクニック編T.5「変換行列と3次元の回転」)で解説しています(なお、同書の数学編には「行列」の説明も収めています)。

著者について

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