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

第55回 平面上における三角形の内側か外側かを外積で調べる

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

前回は特別編が加わったため,本編としては第53回座標にもっとも近い線分上の点を内積で求めるの続きになる。ベクトルの内積のつぎは,外積を使ったサンプルの作成に移ろう。三角形で分割された領域があるとき,ある座標を与えて,それがどの三角形に含まれるのかを調べたい。また,座標などの多くの数値を扱うとき,スクリプトをどのように組立てたらよいのか,今回のお題で練習してみる。

平面上のふたつのベクトルの外積から互いの位置関係がわかる

三角形はもっとも単純な多角形だ。そのため,数学的に扱いやすい。たとえば,Graphics.drawTriangles()メソッドは,三角形に分けた領域をビットマップで塗った(第45回領域をビットマップで塗る - テクスチャマッピング以降参照)⁠すべての多角形は三角形に分けられ,自由な形状も三角形の集まりに近似できるからだ。

ベクトルの外積を使うと,座標が三角形の内側か外側かがわかる。今回のお題は,領域を三角形に分け,そのうちのどれがクリックされたのかを調べたい図1)⁠

図1 領域を分けた三角形のどれがクリックされたのか調べる

図1 領域を分けた三角形のどれがクリックされたのか調べる 図1 領域を分けた三角形のどれがクリックされたのか調べる

まず,外積の意味をおさらいしておこう(第52回ベクトルの外積で回転の軸を定める参照)⁠ベクトルAとBの外積A×Bは,つぎの第52回表1(再掲)のように定められる新たなベクトルだった。そして今回は,とくに「方向」に着目する。

第52回表1 外積A×Bで定められるベクトル(再掲)

外積の要素求められた外積のベクトルとふたつのベクトルAとBとの関係
方向角度 ふたつのベクトルAとBのどちらにも垂直画像
向きベクトルAからBに向かう回転で右ネジの進む向き
大きさ|A||B|sinθ画像

お題は,xy平面の三角形について考える。すると,xy平面上のベクトルの外積は,角度が必ずxy平面と垂直つまりz軸と平行になる。つぎにその方向は,ふたつのベクトルが時計回りの位置にあれば,右ネジの向きであるz軸の正方向だ。逆に,xy平面上のふたつのベクトルAとBの外積A×Bがz軸正方向だったとすると,ベクトルAの右側にBがある。

そこで,三角形の3辺を時計回りの向きのベクトルと考えよう。調べる座標が三角形の内側にある場合,1辺のベクトルとその始点からその座標へのベクトルとの外積を求める図2)⁠3辺のベクトルすべてについて,調べる座標のベクトルが右側にあれば,すなわちその座標は三角形の内側になる。

図2 外積で2次元平面上の座標が三角形の内側にあるかどうかを調べる

図2 外積で2次元平面上の座標が三角形の内側にあるかどうかを調べる

図2 外積で2次元平面上の座標が三角形の内側にあるかどうかを調べる 図2 外積で2次元平面上の座標が三角形の内側にあるかどうかを調べる 図2 外積で2次元平面上の座標が三角形の内側にあるかどうかを調べる

では早速,ふたつのベクトルを与えて,右ネジの位置にあるかどうか調べる関数(xIsRight())を定めよう。ベクトルはxy平面で考えるので,ふたつのPointオブジェクトにして引数に渡す。第1引数のベクトル(point0)の右に第2引数のベクトル(point1)があればtrueそうでなければfalseを返すスクリプト1)⁠

スクリプト1 ふたつのベクトルが右ネジの位置にあるかどうかを返す関数定義

function xIsRight(point0:Point, point1:Point):Boolean {
  var vector3D_0:Vector3D = new Vector3D(point0.x, point0.y, 0);
  var vector3D_1:Vector3D = new Vector3D(point1.x, point1.y, 0);
  var crossProduct:Vector3D = vector3D_0.crossProduct(vector3D_1);
  var bIsRight:Boolean = (crossProduct.z >= 0);
  return bIsRight;
}

あいにく,Pointクラスには外積を求めるメソッドはない。したがって,引数のPointオブジェクトからxy座標を取出して,Vector3Dオブジェクトとしてつくり直す。そのうえで,Vector3D.crossProduct()メソッドにより,ふたつのVector3Dオブジェクトの外積を求める。

前述のとおり,外積のベクトルが奥向き,つまりz座標が正ならふたつの引数のベクトルは右ネジの位置にある。その結果をブール(論理)値にして関数から返す。たとえば,座標(0, -1)のベクトルに対して座標(1, 0)は右側にある。したがって,このふたつをPointオブジェクトとして関数に渡せば,trueが返される。

var bResult:Boolean = xIsRight(new Point(0, -1), new Point(1, 0))
trace(bResult);   // 出力: true

この関数ができあがれば,今回のお題で使う数学の話はおしまいだ。これから先は,この関数をどう使うか,さらにはスクリプトをどう組立てるかが主題になる。そこで,今感じているかもしれないふたつの疑問に答えておく。

第1に,引数のPointをすぐにVector3Dオブジェクトに直すのなら,初めからVector3Dオブジェクトで渡すか,xy座標の数値を4つ引数にした方がよいのではないかということだ。まず,Vector3Dオブジェクトにすると,xy平面の座標にいちいちz座標として0を加えなければならない。それも手間だろう。つぎに,引数を単純な数値にするという手はありえる。しかし,数多くの座標を扱うときには,座標はひとつのオブジェクトの方が捉えやすい。

第2は,そもそもほしいのは,三角形の3頂点座標を定めて,ある座標がその内側かどうかを返す関数なのではないかということだ。確かに,そのような関数は必要だ。しかし,ひとつひとつの三角形やその頂点座標,さらには三角形の集まりをどう定めて,どう扱うのか,まだ決めていない。設計を先にきちんと考えなければならない。

著者プロフィール

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

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

URLhttp://www.FumioNonaka.com/

著書

コメント

コメントの記入