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

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

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

必要な機能を関数として定める

初めにも述べたように,領域を三角形で分けるのはGraphics.drawTriangles()メソッドがテクスチャマッピングするやり方だ。そこで,クリックしたテクスチャの三角形にアウトラインを描いてみよう。第46回「分割した三角形にビットマップを変形して塗る」スクリプト3「三角形の頂点座標のひとつをマウスクリックした位置に動かしてテクスチャマッピング」した第46回図3再掲⁠⁠。このスクリプトが矩形を4つの三角形に分けて描く部分を使い回すことにしよう。

第46回図3 4つに分けた三角形の中央座標をマウスクリックした位置に動かす(再掲)

画像 画像


今回のお題は,クリックした座標が含まれる三角形を調べる。したがって,第46回スクリプト3から,イベントリスナーの登録とリスナー関数の定めは除くスクリプト2⁠。マウスクリックのイベントリスナーは,これから改めて加えてゆく。なお,マッピングする[ライブラリ]のビットマップには,[クラス]としてImageという名前がつけてある(前出第46回分割した三角形にビットマップを変形して塗る参照⁠⁠。

スクリプト2 矩形のテクスチャを4つの三角形に分けてマッピング

// フレームアクション
var nCenterX:Number = stage.stageWidth / 2;
var nCenterY:Number = stage.stageHeight / 2;
var mySprite:Sprite = new Sprite();
var myGraphics:Graphics = mySprite.graphics;
var myTexture:BitmapData = new Image();
var nHalfWidth:Number = myTexture.width / 2;
var nHalfHeight:Number = myTexture.height / 2;
var vertices:Vector.<Number> = new Vector.<Number>();
var indices:Vector.<int> = new Vector.<int>();
var uvData:Vector.<Number> = new Vector.<Number>();
mySprite.x = nCenterX;
mySprite.y = nCenterY;
vertices.push(0, 0);
vertices.push(-nHalfWidth, -nHalfHeight);
vertices.push(nHalfWidth, -nHalfHeight);
vertices.push(nHalfWidth, nHalfHeight);
vertices.push(-nHalfWidth, nHalfHeight);
indices.push(0, 1, 2);
indices.push(0, 2, 3);
indices.push(0, 3, 4);
indices.push(0, 4, 1);
uvData.push(0.5, 0.5);
uvData.push(0, 0);
uvData.push(1, 0);
uvData.push(1, 1);
uvData.push(0, 1);
myGraphics.beginBitmapFill(myTexture);
myGraphics.lineStyle(2, 0xFF0000);   // 確認用
myGraphics.drawTriangles(vertices, indices, uvData);
myGraphics.endFill();
addChild(mySprite);
/*
mySprite.addEventListener(MouseEvent.MOUSE_DOWN, xDraw);
function xDraw(eventObject:MouseEvent):void {
  vertices[0] = eventObject.localX;
  vertices[1] = eventObject.localY;
  myGraphics.clear();
  myGraphics.beginBitmapFill(myTexture);
  myGraphics.drawTriangles(vertices, indices, uvData);
  myGraphics.endFill();
}
*/

Graphics.drawTriangles()メソッドを用いているので,頂点座標と頂点番号がそれぞれVectorオブジェクトで変数(verticesとindices)に納められている。ここからいきなり,すべての頂点のベクトルを調べて,座標の含まれる三角形を取出そうとする人もあるだろう。できるなら,それでもよい。しかし,多くの人は混乱するか,途方に暮れるに違いない。

こういうときは,必要な機能をひとつひとつ小分けにして,部品を組上げるようにつくるとよい。部品は基本的に関数で考える。まず,ひとつの三角形が定められなければならない。これは頂点番号3つの組を,Vectorオブジェクトに取出せばよさそうだ。つぎに,すべての三角形を調べようとしたとき,予め三角形の個数を知りたい。

そこで,関数をふたつ定める。第1に,頂点番号のVectorオブジェクト(indices)を調べて,三角形がいくつあるかを返す関数(xGetNumTriangles())だ。第2の関数(xGetTriangleAt())は,何番目の三角形かという序数を引数に受取って,頂点番号3つが納められたVectorオブジェクト(intベース型)を返す。

// スクリプト2のフレームアクションに追加
mySprite.addEventListener(MouseEvent.MOUSE_DOWN, xDrawTriangle);
function xDrawTriangle(eventObject:MouseEvent):void {
  var nLength:uint = xGetNumTriangles();
  for (var i:uint = 0; i < nLength; i++) {
    var triangleIndices:Vector.<int> = xGetTriangleAt(i);
    trace(triangleIndices);
  }
}
function xGetNumTriangles():uint {
  var numTriangles:uint = uint(indices.length / 3);
  return numTriangles;
}
function xGetTriangleAt(nIndex:int):Vector.<int> {
  var nIndices:int = 3;
  var n:int = nIndex * nIndices;
  var triangleIndices:Vector.<int> = new Vector.<int>(nIndices);
  for (var i:uint = 0; i < nIndices; i++) {
    triangleIndices[i] = indices[uint(n + i)];
  }
  return triangleIndices;
}

そして,マウスクリックのイベントリスナーに関数(xDrawTriangle())を加えた。インスタンスをクリックすると,取りあえずは動作確認としてテクスチャマッピングのための三角形がいくつあるかを調べて,forループですべての三角形の頂点番号の組を[出力]する図3⁠。

図3 インスタンスをクリックすると4つの三角形の3頂点番号の組が[出力]される

図3 インスタンスをクリックすると4つの三角形の3頂点番号の組が[出力]される

図3 インスタンスをクリックすると4つの三角形の3頂点番号の組が[出力]される

あとは,三角形の3頂点番号から,それぞれの座標を返す関数があれば,前掲スクリプト1の座標位置を調べる関数(xIsRight())につなげられそうだ。その関数(xGetVertices())には,頂点番号の整数が納められたVectorオブジェクトを引数に渡す。すると,頂点座標のVectorオブジェクト(vertices)からその番号の座標を取出してPointオブジェクトにし,3頂点のPointエレメントが加えられたVectorオブジェクトで返す。

前掲スクリプト2に,すでに定めてあった関数(xGetNumTriangles()とxGetTriangleAt())も含めて書き加えたのが,つぎのスクリプト3だ。インスタンスをクリックすると,すべての三角形の3頂点座標が[出力]される図4⁠。

スクリプト3 テクスチャマッピングしたすべての三角形の頂点座標を調べる

// フレームアクション
var nCenterX:Number = stage.stageWidth / 2;
var nCenterY:Number = stage.stageHeight / 2;
var mySprite:Sprite = new Sprite();
var myGraphics:Graphics = mySprite.graphics;
var myTexture:BitmapData = new Image();
var nHalfWidth:Number = myTexture.width / 2;
var nHalfHeight:Number = myTexture.height / 2;
var vertices:Vector.<Number> = new Vector.<Number>();
var indices:Vector.<int> = new Vector.<int>();
var uvData:Vector.<Number> = new Vector.<Number>();
mySprite.x = nCenterX;
mySprite.y = nCenterY;
vertices.push(0, 0);
vertices.push(-nHalfWidth, -nHalfHeight);
vertices.push(nHalfWidth, -nHalfHeight);
vertices.push(nHalfWidth, nHalfHeight);
vertices.push(-nHalfWidth, nHalfHeight);
indices.push(0, 1, 2);
indices.push(0, 2, 3);
indices.push(0, 3, 4);
indices.push(0, 4, 1);
uvData.push(0.5, 0.5);
uvData.push(0, 0);
uvData.push(1, 0);
uvData.push(1, 1);
uvData.push(0, 1);
myGraphics.beginBitmapFill(myTexture);
myGraphics.lineStyle(2, 0xFF0000);   // 確認用
myGraphics.drawTriangles(vertices, indices, uvData);
myGraphics.endFill();
addChild(mySprite);
mySprite.addEventListener(MouseEvent.MOUSE_DOWN, xDrawTriangle);
function xDrawTriangle(eventObject:MouseEvent):void {
  var nLength:uint = xGetNumTriangles();
  for (var i:uint = 0; i < nLength; i++) {
    var triangleIndices:Vector.<int> = xGetTriangleAt(i);
    var triangleVertices:Vector.<Point> = xGetVertices(triangleIndices);
    trace(triangleVertices);
  }
}
function xGetNumTriangles():uint {
  var numTriangles:uint = uint(indices.length / 3);
  return numTriangles;
}
function xGetTriangleAt(nIndex:int):Vector.<int>  {
  var nIndices:int = 3;
  var n:int = nIndex * nIndices;
  var triangleIndices:Vector.<int> = new Vector.<int>(nIndices);
  for (var i:uint = 0; i < nIndices; i++) {
    triangleIndices[i] = indices[uint(n + i)];
  }
  return triangleIndices;
}
function xGetVertices(triangle:Vector.<int>):Vector.<Point> {
  var nLength:uint = triangle.length;
  var triangleVertices:Vector.<Point> = new Vector.<Point>(nLength);
  for (var i:uint = 0; i < nLength; i++) {
    var nIndex:int = triangle[i];
    var n:int = nIndex * 2;
    var myPoint:Point = new Point(vertices[n], vertices[int(n + 1)]);
    triangleVertices[i] = myPoint;
  }
  return triangleVertices;
}

図4 インスタンスをクリックすると4つの三角形の3頂点のxy座標が[出力]される

図4 インスタンスをクリックすると4つの三角形の3頂点のxy座標が[出力]される

図4 インスタンスをクリックすると4つの三角形の3頂点のxy座標が[出力]される

著者プロフィール

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

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

URLhttp://www.FumioNonaka.com/

著書