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>