Canvasで網目模様を描く方法を教えてください。その5


マウスのドラッグにより球体の回転を行えるように改良した。
しかし、動作がおかしい

<!DOCTYPE html>
  <meta charset="UTF-8">
  <title>3D</title>
  <style>
    canvas { background : #000; }
  </style>
<body>
<canvas width="1024" height="700"></canvas>

<script>
(function () {

  function CanvasController (canvas) {
    this.canvas = canvas;
    this.mouseX = 0;
    this.mouseY = 0;
    this.touchF = false;
    this.rotq = [0, 0, 0, 1];
    this.gain = 300; // mouse移動の感度
  }
  
  
  function handleEvent (event) {
    var e, x, y, dx, dy, a, b;
    
    switch (event.type) {

    case 'mousedown' :
    case 'touchstart' :
      this.touchF = true;
      break;
    
    case 'mouseup' :
    case 'touchend' :
      this.touchF = false;
      break;
    
    case 'mousemove' :
    case 'touchmove' :
      e = event.target.getBoundingClientRect ();
      x = event.clientX - e.left;
      y = event.clientY - e.top;

      if (this.touchF){
        // クオータニオンによる回転
        dx = (x - this.mouseX) / this.gain;
        dy = (y - this.mouseY) / this.gain;
        
        if (b = dx * dx + dy * dy) {
          a = Math.sqrt (b);
          var ar = a * 0.5;
          var as = -Math.sin (ar) / a;
          var qax = this.rotq[0], qay = this.rotq[1], qaz = this.rotq[2], qaw = this.rotq[3];
          var qbx = dy * as;
          var qby = dx * as;
          var qbw = Math.cos (ar);

          this.rotq = [
            qax * qbw + qaw * qbx             - qaz * qby,
            qay * qbw + qaw * qby + qaz * qbx,
            qaz * qbw             + qax * qby - qay * qbx,
            qaw * qbw - qax * qbx - qay * qby
          ];
        }
      }
      this.mouseX = x;
      this.mouseY = y;
      break;
    
    default :
      break;
    }
  
  }
  
  
  function create (canvas) {
    var obj;
    
    if (1 > arguments.length)
      throw new Error ('引数がない');
    if ('CANVAS' !== canvas.tagName)
      throw new Error ('Canvas 要素ではない');
    
    obj = new CanvasController (canvas);
    
    canvas.addEventListener ('mousedown', obj, false);
    canvas.addEventListener ('touchstart', obj, false);
    canvas.addEventListener ('mouseup', obj, false);
    canvas.addEventListener ('touchend', obj, false);
    canvas.addEventListener ('mousemove', obj, false);
    canvas.addEventListener ('touchmove', obj, false);

    canvas = null;

    return obj;
  }
  
  //__
  
  CanvasController.prototype.handleEvent = handleEvent;

  //__
  CanvasController.create = create;
  
  this.CanvasController = CanvasController;

}) ();



var ctrl = CanvasController.create (document.querySelector ('canvas'));

// 色と不透明度
(function () {

  function Color (red, green, blue, opacity) {
    this.r = red;
    this.g = green;
    this.b = blue;
    this.o = opacity;
  }
  
  //色の最適化
  function normalize (color) {
    return Math.min (Math.max (0, color), 255);
  }

  //透明度の最適化
  function normalize2 (opacity) {
    return Math.min (Math.max (0, opacity), 1);
  }

  //色の加算
  function add (obj) {
    this.r = normalize (this.r + obj.r);
    this.g = normalize (this.g + obj.g);
    this.b = normalize (this.b + obj.b);
  }
  
  //文字列化
  function toString () {
    return 'rgba(' + [
      Math.floor (this.r),
      Math.floor (this.g),
      Math.floor (this.b),
      normalize2 (this.o)
    ].join (',') + ')';
  }
  
  //カラーオブジェクトの生成
  function create (rgb, opacity) {
    switch (arguments.length) {
    case 0 : rgb = [255, 255, 255];
    case 1 : opacity = 1; break;
    default : throw new Error;
    }
    return new Color (rgb[0], rgb[1], rgb [2], opacity);
  }
  
  //___
  
  Color.prototype.add = add;
  
  Color.create = create;
  
  this.Color = Color;
  
}) ();


//テクスチャー
(function () {
  function Texture (img, sx, sy, ex, ey) {
    this.img = img;
    this.sx = sx;
    this.sy = sy;
    this.ex = ex;
    this.ey = ey;
  }
  
  function create (img, sx, sy, ex, ey) {
    return new Texture (img, sx || 0, sy || 0, ex || 1, ey || 1);
  }

  //___
  
  Texture.create = create;

  this.Texture = Texture;
}) ();


// 3次元座標の定義
function Point3D (x, y, z) {
  this.x = x;
  this.y = y;
  this.z = z;
}



// ベクトルの定義(大きさと向きを持つ)
function Vector (p0, p1) {
  if (1 > arguments.length)
    throw new Error ();
  if (1 === arguments.length)
    p0 = (p1 = p0, new Point3D (0, 0, 0));
  
  this.vx = p1.x - p0.x;
  this.vy = p1.y - p0.y;
  this.vz = p1.z - p0.z;
  this.length = Math.sqrt (
    this.vx * this.vx + this.vy * this.vy + this.vz * this.vz
  );
}


//三角形
function Triangle (points, color, texture, frame) {
  
}


//平面
function Surface () {

}

function ball (r, n, n1) {
  var rst = [], a = [], b = [], c = [];
  var pi = Math.PI, sin = Math.sin, cos = Math.cos;
  var k = 2 * pi / n1, k2 = pi / n;
  var i, i_, j, s, r2, yb;
  var r2 = sin (k2) * r, h2 = cos (k2) * r, yr = r2, yt = h2;
  
  for (i = 0; i <= n1; i++) {
    s = k * i;
    a[i] = sin (s);
    b[i] = cos (s);
  }
  
  for (i = 0; i < n1; i++) {
    rst[i] = [
      [0, r, 0],
      [a[i] * r2, h2, b[i] * r2],
      [a[i_= i +1 ] * r2, h2, b[i_] * r2]
    ];
    c[i] = [
      [a[i_ = i + 1] * r2, -h2, b[i_] * r2],
      [a[i] * r2, -h2, b[i] * r2],
      [0, -r, 0]
    ];
  }
  
  for (i = 2; i < n; i++) {
    s = k2 * i;
    yr2 = sin (s) * r;
    yb = cos (s) * r;
    
    for (j = 0; j < n1; j++) {
      rst.push ([
        [a[j] * yr, yt, b[j] * yr],
        [a[j] * yr2, yb, b[j] * yr2],
        [a[j+1] * yr2, yb, b[j+1] * yr2],
        [a[j+1] *yr, yt, b[j+1] * yr]
      ]);
    }
    yt = yb;
    yr = yr2;
  }

  return rst.concat (c);
}


function rotation3D (m, rotq) {
  
  var i, j, a, b, c, d, e, s, x, y, z, ix, iy, iz, iw;
  var rst = [];

  for (i = 0; a = m[i]; i++) {
    for (j = 0, s = []; b = a[j]; j++) {
      x = b[0], y = b[1], z = b[2];
      ix =  rotq[3] * x + rotq[1] * z - rotq[2] * y;
      iy =  rotq[3] * y + rotq[2] * x - rotq[0] * z;
      iz =  rotq[3] * z + rotq[0] * y - rotq[1] * x;
      iw = -rotq[0] * x - rotq[1] * y - rotq[2] * z;
      s[j] = [
        ix * rotq[3] - iw * rotq[0] - iy * rotq[2] + iz * rotq[1],
        iy * rotq[3] - iw * rotq[1] - iz * rotq[0] + ix * rotq[2],
        iz * rotq[3] - iw * rotq[2] - ix * rotq[1] + iy * rotq[0]
      ];
    }
    rst[i] = s;
  }
  return rst;
}


//面の法線ベクトルを求める
function crossProduct (ary) {
  var rst = [];
  var i, s;
  var p0, p1, p2;
  var x0, x1, x2, y0, y1, y2, z0, z1, z2;
  var px, py, pz, qx, qy, qz;
  
  for (i = 0; s = ary[i]; i += 1) {
    p0 = s[0]; p1 = s[1]; p2 = s[2];
    x0 = p0[0]; x1 = p1[0]; x2 = p2[0];
    y0 = p0[1]; y1 = p1[1]; y2 = p2[1];
    z0 = p0[2]; z1 = p1[2]; z2 = p2[2];
    px = x1 - x0; py = y1 - y0; pz = z1 - z0; //p
    qx = x2 - x1; qy = y2 - y1; qz = z2 - z1; //q
    rst[i] = [
      py * qz - pz * qy,
      pz * qx - px * qz,
      px * qy - py * qx
    ];
  }
  return rst;
}


//明るさを求める
function brightness (ary, v, el) {
  var lx = v[0], ly = v[1], lz = v[2];
  var lv = Math.sqrt (lx * lx + ly * ly + lz * lz);
  var i, a, x, y, z, rst = [];
  var c, b = 1 - el;

  for (i = 0; a = ary[i]; i++) {
    x = a[0]; y = a[1]; z = a[2];
    tv = Math.sqrt (x * x + y * y + z * z);
    inp = x * lx + y * ly + z * lz;
    len = inp / (lv * tv);
    rst[i] = el + Math.cos (Math.acos (len)) * b;
  }
  return rst;
}

//3Dの物体を2Dの座標へと変換する
function cov3to2 (m, zz, sc) {
  var rst = [], a, b, c, d, s, i, j, t;
  for (i = 0; d = m[i]; i++) {
    a = d[0];
    for (c = [], j = 0; b = a[j]; j++) {
      c[j] = [b[0] / (t = (zz + b[2]) / sc), b[1] / t];
    }
    rst[i] = [c, d[1]];
  }
  return rst;
}


function canvas_draw_create (c, o) {
  var ctx = c.getContext ('2d');
  var x = c.width / 2 + o[0];
  var y = c.height / 2 + o[1];
  var rgb = [50, 150,255];
  var int = Math.floor;

  ctx.lineWidth = 1;

  return {
    draw:
      function (a, b) {//面を描く
        ctx.beginPath ();
        ctx.fillStyle = 
          'rgb(' +
          int (rgb[0] * b) + ',' +
          int (rgb[1] * b) + ',' +
          int (rgb[2] * b) +
          ')';
        ctx.moveTo (x + a[0][0], y - a[0][1]);

        for (i = 1; b = a[i++]; )
          ctx.lineTo (x + b[0], y - b[1]);

        ctx.lineTo (x + a[0][0], y - a[0][1]);
        ctx.fill();
      },
    cls:
      function () {
        ctx.fillStyle = 'RGBA(0,0,0,1)';
        ctx.fillRect (0,0, c.width, c.height);
      }
    };
}


function draw (a, dw) {
  var i, b, c;
  dw.cls ();
  for (i = 0; c = a[i++]; ) {//面の3点だけでベクトルの外積の向きで判断
    b = c[0];
    if (0 >= ((b[2][0] - b[1][0]) * (b[0][1] - b[1][1]) -
      (b[2][1] - b[1][1]) * (b[0][0] - b[1][0]))
    )
      dw.draw (b, c[1]);
  }
}


var hoge = (function _ () {
  var z = ball (20, 18, 36);
  var v = canvas_draw_create (document.querySelector ('canvas'), [0,0]);
  var h = [1, 1, -1];

  return function () {
    var zz = rotation3D (z, ctrl.rotq);

    var bb = brightness (crossProduct (zz), h, .5);
    for (var i = 0, I = zz.length, d = []; i < I; i++) {
      d[i] = [zz[i], bb[i]];
    }

    draw (cov3to2 (d, 50, 600), v);
  };
}) ();

setInterval (hoge, 1000/60);
</script>