「JavaScriptとcanvasで3Dなポリゴンを回転させる」の続き。

複数のcanvas使うと、こんなこともできるよ〜。という感じかな。

黒いやつの中に、青いやつが入ってぐりぐり回転します。

昨日のコードからの変化は、HighLightの指定,ポリゴンカラーの指定,不透明度の指定などが可能になったことぐらい。基本的に変わりません。


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>polygon test</title>
<style>
#canvas0 {
  position: absolute;
  top: 0;
  left: 0;
}
#canvas1 {
  position: absolute;
  top: 0;
  left: 0;
}
</style>
</head>
<body>

<canvas id="canvas0" width="640" height="480"></canvas>
<canvas id="canvas1" width="640" height="480"></canvas>

<script type="text/javascript">
window.onload = function() {
  var poly1 = new polygon();
  poly1.initialize(document.getElementById("canvas0"), 50, 0x00, 0x000000, 1.0, { x: 200, y: 200 }, 800);
  poly1.draw();
  var poly2 = new polygon();
  poly2.initialize(document.getElementById("canvas1"), 50, 0x80, 0x000080, 1.0, { x: 200, y: 200 }, 600);
  poly2.draw();
}
</script>
<script type="text/javascript">
function polygon() {
}
polygon.prototype = {
  /** 初期化
   *
   * @param element elm       <canvas>要素を指定します。
   * @param number  fps       1秒間に表示するフレーム数の指定です。
   *                          フレーム数を多くすると滑らかになりますが負荷も増えます。
   *                          25を指定すると約40msで1回描画します。
   * @param number  highLight ハイライトの強さを0x0〜0xffで指定します。
   * @param number  color     ポリゴンの色を指定します。0xff0000で赤, 0x00ff00で緑, 0x0000ffで青になります。
   * @param number  alpha     アルファブレンド(透明度)を0.0〜1.0の値で指定します。
   * @param pos     pos       ポリゴンの座標を { x, y } で指定します。
   * @param number  zoom      ポリゴンの拡大倍率を数値で指定します。
   */
  initialize: function(elm, fps, highLight, color, alpha, pos, zoom) {
    this.ctx = elm.getContext("2d");
    this.fps = 1000 / fps;
    this.prop = { highLight: highLight, color: color, alpha: alpha, pos: pos, zoom: zoom };
    this.poly = [[], [], [], [], [], []]; // polygon data
    this.theta = 0.5; // vertical
    this.phi   = 0.5; // horizontal
    // create polygon data
    var i = 0, v1, v2;
    for (; i < 5; ++i) {
      v1 = (!i) ? 0 : Math.SQRT2 * Math.cos((0.5 * i - 0.25) * Math.PI);
      v2 = (!i) ? 0 : Math.SQRT2 * Math.sin((0.5 * i - 0.25) * Math.PI);
      this.poly[0].push([ v1,  v2,   1]);
      this.poly[1].push([  1,  v1,  v2]);
      this.poly[2].push([ v2,   1,  v1]);
      this.poly[3].push([-v1, -v2,  -1]);
      this.poly[4].push([ -1, -v1, -v2]);
      this.poly[5].push([-v2,  -1, -v1]);
    }
  },
  draw: function() {
    var me = this;
    var phi   = Math.PI / 100; // 0.03141592653589793
    var theta = Math.PI / 80;
    window.setInterval(
      function() {
        me.theta += theta;
        me.phi += phi;
        me.__drawPolygon(me.ctx, me.poly, me.theta, me.phi, me.prop);
      }, this.fps
    );
  },
  __drawPolygon: function(ctx, poly, theta, phi, prop) {
    var sinP = Math.sin(phi), cosP = Math.cos(phi);
    var sinT = Math.sin(theta), cosT = Math.cos(theta);

    // vector data
    var vX = [-sinP,         cosP,            0];
    var vY = [-cosT * cosP, -cosT * sinP,  sinT];
    var vZ = [-sinT * cosP, -sinT * sinP, -cosT];

    var info = [];
    var x, y, z;
    var i, j;
    var surface; // 2D bitmap surface

    for (i = 0; i < poly.length; ++i) {
      surface = [0, -(vZ[0] * poly[i][0][0] +
                      vZ[1] * poly[i][0][1] +
                      vZ[2] * poly[i][0][2])];
      for (j = 1; j < poly[i].length; ++j) {
        z = vZ[0] * poly[i][j][0] +
            vZ[1] * poly[i][j][1] +
            vZ[2] * poly[i][j][2];
        surface.push([vX[0] * poly[i][j][0] +
                      vX[1] * poly[i][j][1] +
                      vX[2] * poly[i][j][2],
                      vY[0] * poly[i][j][0] +
                      vY[1] * poly[i][j][1] +
                      vY[2] * poly[i][j][2], z]);
        surface[0] += z;
      }
      info.push(surface);
    }
    info.sort(this.__sort);

    // canvas clear
    ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);

    var light;
    for (i = 0; i < info.length; ++i) {
      info[i].shift();
      light = info[i].shift();
      if (light >= 0) {
        for (j = 0; j < info[i].length; ++j) {
          x = prop.zoom * info[i][j][0] / (10 + info[i][j][2]);
          y = prop.zoom * info[i][j][1] / (10 + info[i][j][2]);
          if (!j) {
            ctx.beginPath();
            ctx.moveTo(prop.pos.x + x, prop.pos.y + -y);
            ctx.fillStyle = this.__rgba(parseInt(light * prop.highLight) + prop.color, prop.alpha);
          } else {
            ctx.lineTo(prop.pos.x + x, prop.pos.y + -y);
          }
        }
        ctx.closePath();
        ctx.fill();
      }
    }
  },
  __sort: function(a, b) {
    if (a[0] === b[0]) { return 0; }
    return a[0] < b[0] ? 1: -1;
  },
  __rgba: function(color, alpha) {
    var rv = [color & 0xff0000, color & 0xff00, color & 0xff, alpha];
    return "rgba(" + rv.join(",") + ")";
  }
};
</script>

</body>
</html>