HTML5のCanvasでつくるダイナミックな表現―CreateJSを使う

第22回 立方体の6面をx軸とy軸で回す

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

前回の第21回水平に回す立方体の面を塗るでは,立方体四方の面をy軸で水平に回した。今回は6面を揃えてx軸の回転も加える。つまり,マウスポインタの位置に応じて,立方体を水平および垂直に回してみよう。

立方体に上面と底面を加える

前回は,立方体の四方の面しか塗らなかった。y軸で水平に回すだけなので,どうせ上面と底面は見えないからだ。今回はx軸で垂直にも回すので,上面と底面も加える。それに,ふたつの面を加えないと立方体の中が見えてしまって,裏返った面は塗らないという手が使えなくなる。立方体の8頂点にはインデックスをふっておいた第20回図3再掲)。

第20回 図3 立方体の8頂点にインデックスを与える(再掲)

第20回 図3 立方体の8頂点にインデックスを与える(再掲)

そして,面はクラス(Face)に4頂点インデックスを与えて定めた。ただし,面の裏表を調べるため,頂点の順序はふたつのベクトルの外積が右ネジの位置になるように,時計回りに決めなければならなかった第21回図4再掲)。そこで,面のオブジェクトの配列をつくって返す関数(getFacesVertices())に,以下のように上面と底面を加える。

第21回 図4 3頂点から定めたふたつのベクトルの位置関係を予め決める(再掲)

第21回 図4 3頂点から定めたふたつのベクトルの位置関係を予め決める(再掲)

function getFacesVertices() {
  var vertices = [
    new Face(0, 1, 2, 3, getRandomColor()),
    new Face(1, 5, 6, 2, getRandomColor()),
    new Face(4, 0, 3, 7, getRandomColor()),
    new Face(4, 5, 1, 0, getRandomColor()),    // 上面
    new Face(6, 7, 3, 2, getRandomColor()),    // 底面
    new Face(5, 4, 7, 6, getRandomColor())
  ];
  return vertices;
}

x軸とy軸の回転を扱う準備

いきなりx軸で回す前に,xy両軸での回転が扱えるよう第21回コード2水平に回す立方体の表向きの面だけを描くに下ごしらえを施す。まだ,y軸で水平に回すという動きは変えない。まず,変数のもち方から取りかかろう。立方体の回転角はy軸にx軸周りが加わる。また,マウスポインタの位置を測る中心点もxy座標が要る。そこで,ふたつの変数(angleとstageCenter)は,以下のようにPointオブジェクトで与えることにした。それにともなって,変数との値のやり取りの仕方も変わってくる。

var angle = new createjs.Point();

// var stageCenterX;
var stageCenter = new createjs.Point();
function initialize() {
  var canvasElement = document.getElementById("myCanvas");
  stage = new createjs.Stage(canvasElement);
  // stageCenterX = canvasElement.width / 2;
  stageCenter.x = canvasElement.width / 2;
  stageCenter.y = canvasElement.height / 2;
  // drawGraphics = createGraphics(stageCenterX, canvasElement.height / 2);
  drawGraphics = createGraphics(stageCenter.x, stageCenter.y);

  createjs.Ticker.addEventListener("tick", rotate);
  stage.addEventListener("stagemousemove", setAngle);
}
function setAngle(eventObject) {
  var mouseX = eventObject.stageX;
  // angle = (mouseX - stageCenterX) * 1 / 300;
  angle.y = (mouseX - stageCenter.x) / 300;
}
function rotate(eventObject) {

  // matrix.identity().rotate(angle);
  matrix.identity().rotate(angle.y);

}

前述のとおり,まだ動きはこれまでと変わらない。立方体はマウスポインタの水平位置に応じてy軸で回る図1)。新たに加えた上面も底面も見えない。けれど,これでx軸周りの扱いが加えられるようになった。立方体を回転する処理は,以下のコード1のように変わった。なお,クラスの定めには手を触れていないので省いている。

図1 立方体がマウスポインタの位置に応じて水平に回る

図1 立方体がマウスポインタの位置に応じて水平に回る

コード1 マウスポインタの位置に応じて立方体をy軸で回す

var stage;
var drawGraphics;
var points;
var angle = new createjs.Point();
var matrix = new createjs.Matrix2D();
var stageCenter = new createjs.Point();
var _point = new createjs.Point();
var points2D = [];
var facesVertices;
var focalLength = 300;
function initialize() {
  var canvasElement = document.getElementById("myCanvas");
  stage = new createjs.Stage(canvasElement);
  stageCenter.x = canvasElement.width / 2;
  stageCenter.y = canvasElement.height / 2;
  drawGraphics = createGraphics(stageCenter.x, stageCenter.y);
  points = createCubePoints(50);
  facesVertices = getFacesVertices();
  drawFaces(points, facesVertices);
  createjs.Ticker.addEventListener("tick", rotate);
  stage.addEventListener("stagemousemove", setAngle);
}
function setAngle(eventObject) {
  var mouseX = eventObject.stageX;
  angle.y = (mouseX - stageCenter.x) / 300;
}
function rotate(eventObject) {
  var count = points.length;
  points2D.length = 0;
  matrix.identity().rotate(angle.y);
  for (var i = 0; i < count; i++) {
    var point = points[i];
    matrix.transformPoint(point.x, point.z, _point);
    point.x = _point.x;
    point.z = _point.y;
    points2D[i] = point.getProjetedPoint(focalLength);
  }
  drawFaces(points2D, facesVertices);
}
function drawFaces(points, faces) {
  var numFaces = faces.length;
  drawGraphics.clear();
  for (var i = 0; i < numFaces; i++) {
    var face = faces[i];
    var facePoints = face.getFacePoints(points);
    if (isFront(facePoints)) {
      draw(facePoints, face.color);
    }
  }
  stage.update();
}
function isFront(facePoints) {
  var origin = facePoints[0];
  var point0 = MathUtils.subtractVectors(origin, facePoints[1]);
  var point1 = MathUtils.subtractVectors(origin, facePoints[2]);
  return (0 <= MathUtils.crossProduct2D(point0, point1));
}
function draw(points, color) {
  var count = points.length;
  var point = points[count - 1];
  drawGraphics
  .beginFill(color)
  .moveTo(point.x, point.y);
  for (var i = 0; i < count; i++) {
    point = points[i];
    drawGraphics.lineTo(point.x, point.y);
  }
}
function createGraphics(x, y) {
  var drawShape = new createjs.Shape();
  drawShape.x = x;
  drawShape.y = y;
  stage.addChild(drawShape);
  return drawShape.graphics;
}
function createCubePoints(halfEdge) {
  var cubePoints = [
    new Point3D(-halfEdge, -halfEdge, -halfEdge),
    new Point3D(halfEdge, -halfEdge, -halfEdge),
    new Point3D(halfEdge, halfEdge, -halfEdge),
    new Point3D(-halfEdge, halfEdge, -halfEdge),
    new Point3D(-halfEdge, -halfEdge, halfEdge),
    new Point3D(halfEdge, -halfEdge, halfEdge),
    new Point3D(halfEdge, halfEdge, halfEdge),
    new Point3D(-halfEdge, halfEdge, halfEdge)
  ];
  return cubePoints;
}
function getFacesVertices() {
  var vertices = [
    new Face(0, 1, 2, 3, getRandomColor()),
    new Face(1, 5, 6, 2, getRandomColor()),
    new Face(4, 0, 3, 7, getRandomColor()),
    new Face(4, 5, 1, 0, getRandomColor()),
    new Face(6, 7, 3, 2, getRandomColor()),
    new Face(5, 4, 7, 6, getRandomColor())
  ];
  return vertices;
}
function getRandomColor() {
  return createjs.Graphics.getRGB(Math.floor(MathUtils.getRandomInt(0, 0xFFFFFF)));
}

著者プロフィール

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

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

URLhttp://www.FumioNonaka.com/

著書

コメント

コメントの記入