babu_babu_babooのごみ箱

2016-05-19

JavaScriptで選択問題作成(3)

| 19:47

それにしても、やっちまったなぁ〜

JavaScriptで選択問題作成(3)
http://okwave.jp/qa/q9174815.html

<!DOCTYPE html>
  <meta charset="utf-8">
<style>
fieldset.q ul { list-style: none; }

</style>

<body>
<h1>問題</h1>
<fieldset class="q">
  <legend>Q1. 九州の県は次のうちどれですか?</legend>
  <ul>
    <li><label><input type="checkbox" name="q1" value="true">福岡</label>
    <li><label><input type="checkbox" name="q1" value="false">岡山</label>
    <li><label><input type="checkbox" name="q1" value="false">山形</label>
    <li><label><input type="checkbox" name="q1" value="true">長崎</label>
    <li><label><input type="checkbox" name="q1" value="true">熊本</label>
    <li><label><input type="checkbox" name="q1" value="false">新潟</label>
    <li><label><input type="checkbox" name="q1" value="false">山口</label>
    <li><label><input type="checkbox" name="q1" value="true">鹿児島</label>
    <li><label><input type="checkbox" name="q1" value="true">佐賀</label>
    <li><label><input type="checkbox" name="q1" value="false">青森</label>
    <li><label><input type="checkbox" name="q1" value="true">宮崎</label>
    <li><label><input type="checkbox" name="q1" value="false">千葉</label>

  </ul>
  <input type="button" value="解答">
  <input type="hidden" name="q1" value="">
</fieldset>

<p>
  <input type="button" value="リセット">
  <input type="button" value="終了">

<script>
function check (e) {
  var fieldset = e.parentNode;
  var hidden = fieldset.querySelector ('input[type="hidden"]');
  var seikai = fieldset.querySelectorAll ('input[type="checkbox"][value="true"]');
  var kaitou = fieldset.querySelectorAll ('input[type="checkbox"]:checked');
  var matigai = fieldset.querySelectorAll ('input[type="checkbox"][value="false"]:checked');

  if (matigai.length || kaitou.length !== seikai.length) {
    hidden.value = '';
    kotae = Array.prototype.map.call (seikai, function (e) {
      return e.parentNode.textContent;
    }).join (', ');
    alert ([
      '不正解です。',
      '答えは、'+ kotae + 'です。'
    ].join ('\n'));
  }
  else {
    hidden.value = 'true';
    alert ('正解です。');
  
  }
}

function ending () {
  var es0 = document.querySelectorAll ('fieldset.q input[type="hidden"]');
  var es1 = document.querySelectorAll ('fieldset.q input[type="hidden"][value=""]');
  alert ([
    '問題数は、'+ es0.length + 'です。',
    '間違いは、' + es1.length + '問あります。'
  ].join ('\n'));
}

function reset () {
  var es = document.querySelectorAll ('fieldset.q input[type="hidden"]');
  Array.prototype.forEach.call (es, function (e) { e.value = ''; });
}

function listener (event) {
  var e = event.target;
  
  if ('button' === e.type)
    switch (e.value) {
    case '解答' :
      check (e);
      break;
    
    case 'リセット' :
      reset ();
      break;
    
    case '終了' :
      ending ();
      break;
    }
}

reset ();
document.addEventListener ('click', listener, false);
</script> 

2016-05-17

テキストデータから文字列を取り出したい

| 21:44

http://okwave.jp/qa/q9172829.html

正規表現は関数の外に展開したほうがよかったな。

そもそも取り出すテキストの中に同じキーワードがあるならば破たんするか。

製作時間60分を目標としていたのに、超えてしまった。


5日間も休んでいた筋トレを再開した。体が心地よいだるさだ。


<!DOCTYPE html>
<meta charset="utf-8">
<title>高機能なエディタなら…</title>
<style >
</style>

<body>
<textarea rows="15" cols="90" id="A">
--------
AUTHOR: hogehoge
TITLE: 記事A
STATUS: Publish
ALLOW COMMENTS: 0
CONVERT BREAKS: __default__
ALLOW PINGS: 0
PRIMARY CATEGORY: news
CATEGORY: news

DATE: 02/08/2014 06:17:55 AM
-----
BODY:
記事一個目。
ほげほげ
-----
EXTENDED BODY:

-----
EXCERPT:

-----
KEYWORDS:

-----


--------
AUTHOR: hogehoge
TITLE: 記事B
STATUS: Publish
ALLOW COMMENTS: 0
CONVERT BREAKS: __default__
ALLOW PINGS: 0
PRIMARY CATEGORY: nikki
CATEGORY: nikki

DATE: 02/09/2014 07:45:04 PM
-----
BODY:
記事2個目。
<img src="hoge.jpg">
ほげほげ
-----
EXTENDED BODY:

-----
EXCERPT:

-----
KEYWORDS:

-----


--------
AUTHOR: hogehoge
TITLE: 記事C
STATUS: Publish
ALLOW COMMENTS: 0
CONVERT BREAKS: __default__
ALLOW PINGS: 0
PRIMARY CATEGORY: news
CATEGORY: news

DATE: 02/09/2014 07:51:34 PM
-----
BODY:
記事3個目。ほげほげ
-----
EXTENDED BODY:

-----
EXCERPT:

-----
KEYWORDS:

-----
</textarea><br>
<input type="button" value="conv" onclick="hoge();">
<hr>
<textarea rows="15" cols="90" id="B">
</textarea>

<script>
function addDQ (str) {
  return '"' + str + '"';
}

function hoge () {
  var t = document.querySelector ('#A').value;
  var b = document.querySelector ('#B');
  var rst = [];
  var rec = [];
  
  var cr = '\\r|\\n|\\r\\n';
  var CR = '(?:' + cr + ')*';
  
  var sp = '\\s?';
  var splt8 = '\\-{8}' + CR;
  var splt5 = '\\-{5}' + CR;
  var typeA = sp + '.*?' + CR;
  var typeB = sp + '(.*?)' + CR;
  var typeC = sp + '((?:.|' + cr + ')*?)' + CR;
  var typeE = sp + '(?:.|' + cr + ')*?' + CR;
  
  var pattern = [
    splt8,
    'AUTHOR:' + typeA,
    'TITLE:' + typeB,
    'STATUS:' + typeA,
    'ALLOW COMMENTS:' + typeA,
    'CONVERT BREAKS:' + typeA,
    'ALLOW PINGS:' + typeA,
    'PRIMARY CATEGORY:' + typeA,
    'CATEGORY:' + typeB,
    'DATE:' + typeB + splt5,
    'BODY:' + CR + typeC + splt5,
    'EXTENDED BODY:' + CR + typeE + splt5,
    'EXCERPT:' + CR + typeE + splt5,
    'KEYWORDS:' + CR + typeE + splt5,
    typeE
  ].join ('');

  var reg = new RegExp (pattern, 'g');
  
  for (rec = []; rec = reg.exec (t); ) {
    rec.shift ();
    rst.push (rec);
  }
  
  b.value = rst.map (function (a) {
    a[3] = a[3].replace (/(\r|\n|\r\n)/g, '<br>');
    return a.map (addDQ).join(', ');
  }).join ('\n');
  
}

</script>

think49think49 2016/05/20 23:27 DOMよりも文字列処理のほうが面白そうに感じます。
https://jsfiddle.net/67byzuho/
最近、CSVパーサを書いたので使うか迷いましたが、文字列to文字列だったので配列変換する処理は不要かなと思い、独自処理にしてみました。
https://gist.github.com/think49/009a8744c147a7013f14ea8913bd9027

To: babu_babu_baboo さん
CSVレコードをダブルクォートで括る場合はダブルクォート自身をエスケープする処理を入れると更に良いと思います。
http://www.kasai.fm/wiki/rfc4180jp

babu_babu_baboobabu_babu_baboo 2016/05/21 12:58 think49 さん、 こんにちは。

ダブルクォートのエスケープね。反省です。
私は、DOM よりそして文字列より 動くもの(コンピュータ・グラフィックス) みたいなのが面白いです

負け惜しみでもないし敗北感があるわけでもない(笑)のですが一言。
「後出しジャンケンみたいもんだから、プログラムが洗練されているように見えてずるい」
そして直感的ではない。(だから負け惜しみではないからね)
読み解くのに時間を必要とする正規表現の文字列を個々の変数に代入し、それを組み上げていく表記方法に感動した記憶があります。
今回はその応用(劣化コピーだともいえる)でした。
しばらくプログラミングから離れていたので、ミスの連発です。

私はプロのプログラマーではありません。
なのでコストを考えるのは意味のないことなのですが、切り出す文字列の形式が3種類以上になれば、そのたびに switch 〜 case が増えて
その度にコードを書く。なので手間が増える。みたいに思いました。
(負け惜しみでもないし敗北感があるわけでもないからね)

ちなみにthink49 さんは、そのプログラムをコーディング(完成)するのに要した時間はどれくらいでした?
私は、構想1分、コーディング30分、デバッグに40分かかりました。

think49think49 2016/05/22 08:46 babu_babu_baboo さん
おはようございます。私もプロのプログラマーではありませんので、プロから見れば至らない部分はあると思います。
大体の記憶ですが、「構想: 2時間、コーディング: 1時間30分、デバッグ: 1時間」ですね(実際はもっと時間をかけているかも)。
私は作業時間が長い方なので制限時間付きでプログラミングの課題を与えられたら最遅で完成させる確信があります。
私の場合は全体的に構想(設計)に時間をかける傾向がありますね。

今回、時間がかかった理由。
初めは1行の正規表現リテラルでサクッと書いてしまおうとしたのですが、期待通りに動かない。少しずつ正規表現を変えてデバッグするも面倒になってくる。
⇒一から作り直し。exec と replace どちらを使う?
⇒String to String なのせ replace で。どうせなら拡張性を持たせる設計にしよう。

私は過去にgtltさんにcsvパーサに感銘を受けたので、reolace の書き方にはかなり影響が出ていると思います。
http://www.tagindex.com/kakolog/q4bbs/2001/2437.html

babu_babu_baboobabu_babu_baboo 2016/05/22 21:49 think49 さん

へぇっ!? プロじゃないの? ちょっと驚きです。
デバッグって、私も時間を要する時があります。長くなるとイラッとしますね。
でも最近は長いプログラムでも一発で動くことがあるんですよ。
あっけにとらられ逆に疑って長々と弄って見てしまう。しかもあの手この手で…
それでもちゃんと動くとき、私は、ニヤッとしているそうです。
奥さんに自慢したいのですが、すでにその時は「気持ち悪いわよ!」と言われてます。
価値をわかってくれそうにないのですが、自慢げに説明していると、うなずかれるたびにテンションは下がりだします。
目もかなり悪くなり目がしょぼしょぼな毎日です。モニターにへばりついて見ています。

--
exec より replace は、速いんだろうな〜。
でも暇になったら勉強しようと思っている構文解析(LR法)は、 exec なんだろうな〜とも思っています。

--
http://okwave.jp/qa/q9163064.html
球体と円柱のワイヤーフレームの質問者、WEBデザインのところで、ピラミッドを質問しています。
なんでJSで質問しなかったかが疑問です。
私に「講演の依頼があるかもしれません」と言われ、思わず爆笑!
あれは遥か昔、高校時代にBASIC言語で書いたリメーク版。
何も調べない人なのね。
レイ・トレーシングがリアルタイムで動く時代なのに。

--
読み直してみて、俺何書いてんだ?と。ごめん。

2016-05-12

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

| 07:47

f:id:babu_babu_baboo:20160512075803p:image

球体をマウスドラッグにより回転させる。しかも慣性によりマウスアウトしたあとも少し回る。

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


<script>
//http://jsdo.it/totetero/without_polygon
//http://stardustcrown.com/reading/rotate3d.html
//http://d.hatena.ne.jp/gyuque/20090211
(function () {

  var INIT_QUATERNION = [1, 0, 0, 0];

  function CanvasController (canvas) {
    this.canvas = canvas;
    this.mouseX = null;
    this.mouseY = null;
    this.touchF = false; //ドラッグ中か?
    this.Qnow = INIT_QUATERNION; //今回のマウスのドラッグ中のクォータニオン
    this.Qbef = INIT_QUATERNION; //前回のクォータニオン
    this.rots = INIT_QUATERNION; //今回と前回のクォータニオンの積(これが重要)
    this.gain = canvas.width / 2; // mouse移動の感度
    this.dx = 0;
    this.dy = 0;
    this.timerId = null;
    this.miniInertia = 1e-7;
  }
  
  //回転
  function rotation (dx, dy) {
    var a, b, a0, a1, a2, a3, b0, b1, b2, b3, r, t, as;
    if (t = dx * dx + dy * dy) {
      r = Math.sqrt (t);
      as = Math.sin (r) / r;
      a = this.Qnow;
      a0 = a[0]; a1 = a[1]; a2 = a[2]; a3 = a[3];
      b0 = dy * as; b1 = dx * as; b3 = Math.cos (r);

      // クオータニオンによる回転
      a = this.Qbef;
      b = this.Qnow = [
        a0 * b3 - a3 * b0           - a2 * b1,
        a1 * b3 + a3 * b1 - a2 * b0,
        a2 * b3           + a0 * b1 + a1 * b0,
        a3 * b3 + a0 * b0 - a1 * b1
      ];

      //前回(a)と今回(b)のクォータニオンの積
      a0 = a[0]; a1 = a[1]; a2 = a[2]; a3 = a[3];
      b0 = b[0]; b1 = b[1]; b2 = b[2]; b3 = b[3];
      
      this.rots = [
        a0 * b0 - a1 * b1 - a2 * b2 - a3 * b3,
        a0 * b1 + a1 * b0 + a2 * b3 - a3 * b2,
        a0 * b2 - a1 * b3 + a2 * b0 + a3 * b1,
        a0 * b3 + a1 * b2 - a2 * b1 + a3 * b0
      ];
      this.dx = dx;
      this.dy = dy;
    }
    return t;
  }
  
  //慣性
  function inertia () {
    var distance = rotation.call (
      this,
      this.dx - this.dx / 40,
      this.dy - this.dy / 40
    );
    
    if (this.miniInertia < distance)
      this.timerId = setTimeout (inertia.bind (this), 33);
  }


  //クォータニオンによる座標軍の回転
  function quaternionRotation (material) {
    
    var i, j, x, y, z;
    var surface, vertex;
    var q = this.rots;
    var q0 = q[0], q1 = q[1], q2 = q[2], q3 = q[3];
    var a0, a1, a2, a3;
    var s = [], rst = [];

    for (i = 0; surface = material[i]; i++) {
      for (j = 0, s = []; vertex = surface[j]; j++) {
        x = vertex[0], y = vertex[1], z = vertex[2];
        a0 =  q3 * x + q1 * z - q2 * y;
        a1 =  q3 * y + q2 * x - q0 * z;
        a2 =  q3 * z + q0 * y - q1 * x;
        a3 = -q0 * x - q1 * y - q2 * z;
        s[j] = [
          a0 * q3 - a3 * q0 - a1 * q2 + a2 * q1,
          a1 * q3 - a3 * q1 - a2 * q0 + a0 * q2,
          a2 * q3 - a3 * q2 - a0 * q1 + a1 * q0
        ];
      }
      rst[i] = s;
    }
    return rst;
  }

  
  
  function handleEvent (event) {
    var e, x, y, dx, dy, a, b, c, e, r, t;
    var a0, a1, a2, a3, b0, b1, b2, b3, as;
    
    switch (event.type) {

    case 'mouseup' :
    case 'touchend' :
      this.touchF = false;
      inertia.call (this);
      break;
    
    case 'mousedown' :
    case 'touchstart' :
      if (this.timerId) {
        clearTimeout (this.timerId);
        this.timerId = null;
      }
      this.touchF = true;
      this.Qnow = INIT_QUATERNION;
      this.Qbef = this.rots;
      e = event.target.getBoundingClientRect ();
      this.mouseX = event.pageX - e.left;
      this.mouseY = event.pageY - e.top;
      break;

    case 'mousemove' :
    case 'touchmove' :
      e = event.target.getBoundingClientRect ();
      x = event.pageX - e.left;
      y = event.pageY - e.top;

      if (this.touchF){
        dx = (x - this.mouseX) / this.gain;
        dy = (y - this.mouseY) / this.gain;
        rotation.call (this, dx, dy);
      }

      this.mouseX = x;
      this.mouseY = y;
      event.preventDefault();//ipadなどでスクロールさせないため

      break;
    }
  
  }
  
  
  // canvas にイベントを追加する
  function addCanvasEvent (event_type) {
    this.canvas.addEventListener (event_type, this, false);
  }
  
  
  function create (canvas) {
    
    if (1 > arguments.length)
      throw new Error ('引数がない');
    if ('CANVAS' !== canvas.tagName)
      throw new Error ('Canvas 要素ではない');

    var obj = new CanvasController (canvas);
    
    ['mousedown', 'mouseup', 'mousemove','touchstart', 'touchend', 'touchmove']
      .forEach (addCanvasEvent, obj);

    canvas = null;// メモリーリークパターン

    return obj;
  }
  
  //__
  
  CanvasController.prototype.handleEvent = handleEvent;
  CanvasController.prototype.quaternionRotation = quaternionRotation;
  //__
  CanvasController.create = create;
  
  this.CanvasController = CanvasController;

}) ();


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 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 = [0, 100,250];
  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.fill ();
        ctx.closePath ();
      },
    cls:
      function () {
        ctx.clearRect(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]);
  }
}

/* Model {
  vertex : array //頂点の集合
  surface : array // 頂点の3つを取り、辺の描画の有無、色、模様

*/

function rendering_wireFrame (canvas, model) {
  
}


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

  return function () {
    var zz = ctrl.quaternionRotation (z);

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

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

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



2016-05-11

Canvasに球体を表示させマウスのドラッグにより回転させる

| 15:10

クォータニオンによる回転を学び、正しく動作するように改良した。

要点は、前回のクォータニオンとマウスをドラッグした時のクォータニオンの積が

現在の回転クォータニオン数で、マウスアウトしたときは、

その積を前回の回転クォータニオン数として記憶する。


次の目標は、回転の慣性を目指す

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

<script>
(function () {

  var INIT_QUATERNION = [1, 0, 0, 0];

  function CanvasController (canvas) {
    this.canvas = canvas;
    this.mouseX = null;
    this.mouseY = null;
    this.touchF = false; //ドラッグ中か?
    this.rotq = INIT_QUATERNION; //今回のマウスのドラッグ中のクォータニオン
    this.rotr = INIT_QUATERNION; //前回のクォータニオン
    this.rots = INIT_QUATERNION; //今回と前回のクォータニオンの積(これが重要)
    this.gain = canvas.width / 4; // mouse移動の感度
  }
  
  
  function handleEvent (event) {
    var e, x, y, dx, dy, a, b, c, e, r, t;
    var a0, a1, a2, a3, b0, b1, b2, b3;
    var ar, as;
    
    switch (event.type) {

    case 'mouseup' :
    case 'touchend' :
      this.touchF = false;
      break;
    
    case 'mousedown' :
    case 'touchstart' :
      this.touchF = true;
      this.rotq = INIT_QUATERNION;
      this.rotr = this.rots;
      e = event.target.getBoundingClientRect ();
      this.mouseX = event.pageX - e.left;
      this.mouseY = event.pageY - e.top;
      break;

    case 'mousemove' :
    case 'touchmove' :
      e = event.target.getBoundingClientRect ();
      x = event.pageX - e.left;
      y = event.pageY - e.top;

      if (this.touchF){
        dx = (x - this.mouseX) / this.gain;
        dy = (y - this.mouseY) / this.gain;
    
        if (t = dx * dx + dy * dy) {
          r = Math.sqrt (t);
          ar = r * 0.5;
          as = Math.sin (ar) / r;
          a = this.rotq;
          a0 = a[0]; a1 = a[1]; a2 = a[2]; a3 = a[3];
          b0 = dy * as; b1 = dx * as; b3 = Math.cos (ar);

          // クオータニオンによる回転
          a = this.rotr;
          b = this.rotq = [
            a0 * b3 - a3 * b0           - a2 * b1,
            a1 * b3 + a3 * b1 - a2 * b0,
            a2 * b3           + a0 * b1 + a1 * b0,
            a3 * b3 + a0 * b0 - a1 * b1
          ];

          //前回(a)と今回(b)のクォータニオンの積
          a0 = a[0]; a1 = a[1]; a2 = a[2]; a3 = a[3];
          b0 = b[0]; b1 = b[1]; b2 = b[2]; b3 = b[3];
          
          this.rots = [
            a0 * b0 - a1 * b1 - a2 * b2 - a3 * b3,
            a0 * b1 + a1 * b0 + a2 * b3 - a3 * b2,
            a0 * b2 - a1 * b3 + a2 * b0 + a3 * b1,
            a0 * b3 + a1 * b2 - a2 * b1 + a3 * b0
          ];
        }
      }

      this.mouseX = x;
      this.mouseY = y;
      event.preventDefault();//ipadなどでスクロールさせないため

      break;
    }
  
  }
  
  
  // canvas にイベントを追加する
  function addCanvasEvent (event_type) {
    this.canvas.addEventListener (event_type, this, false);
  }
  
  
  function create (canvas) {
    
    if (1 > arguments.length)
      throw new Error ('引数がない');
    if ('CANVAS' !== canvas.tagName)
      throw new Error ('Canvas 要素ではない');

    var obj = new CanvasController (canvas);
    
    ['mousedown', 'mouseup', 'mousemove','touchstart', 'touchend', 'touchmove']
      .forEach (addCanvasEvent, obj);

    canvas = null;// メモリーリークパターン

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

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

}) ();



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


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 = [0, 100,250];
  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.fill ();
      },
    cls:
      function () {
        ctx.clearRect(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]);
  }
}

/* Model {
  vertex : array //頂点の集合
  surface : array // 頂点の3つを取り、辺の描画の有無、色、模様

*/

function rendering_wireFrame (canvas, model) {
  
}


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

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

    var bb = brightness (crossProduct (zz), h, .6);
    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>




2016-04-30

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

| 08:59

f:id:babu_babu_baboo:20160430090213p:image

地球のテクスチャーマッピング

基本的なところを勉強中。

マウスで操作できるのだが、イベントハンドラあたりを書き直した。

こちらは問題ないのだが、その5に適用すると球体の反対側を表にしたとき動作が逆になる。


あぁ〜時間がほしい。また忙しくなる。

参照元

http://jsdo.it/totetero/without_polygon

しかし、マッピングする方法が目指しているものと違う。

逆行列だ!?う〜〜〜ん、悩む

http://d.hatena.ne.jp/gyuque/20090211

<!DOCTYPE html>
  <meta charset="UTF-8">
  <title>3D</title>
  <style>
    canvas { background : #aaa; }
  </style>
<body>
<img style="display:none;" src="data:image/gif;base64,
 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">


<canvas id='world' width="600" height="400"></canvas>
<script>

function load (){
  var canvas = document.getElementById("world");
  var context = canvas.getContext("2d");
  var canvasdat = context.createImageData(canvas.width, canvas.height);
  var imgdat = null;
  var rotq = [];
  
  // メインループ
  var renderScene = function(){
  
    rotq = ctrl.rotq;
    for(var i = 0; i < canvasdat.height; i++){
      for(var j = 0; j < canvasdat.width; j++){
        var index = i * canvasdat.width + j;
        var x = (j - canvasdat.width * 0.5) / 150;
        var y = (i - canvasdat.height * 0.5) / 150;
        var r = x * x + y * y;
        if(r <= 1){
          // 描画対象点の球上座標を得る
          var z = Math.sqrt(1 - r);
          // 球上座標にクォータニオン回転を適用する
          var ix =  rotq[3] * x + rotq[1] * z - rotq[2] * y;
          var iy =  rotq[3] * y + rotq[2] * x - rotq[0] * z;
          var iz =  rotq[3] * z + rotq[0] * y - rotq[1] * x;
          var iw = -rotq[0] * x - rotq[1] * y - rotq[2] * z;
          x = ix * rotq[3] - iw * rotq[0] - iy * rotq[2] + iz * rotq[1];
          y = iy * rotq[3] - iw * rotq[1] - iz * rotq[0] + ix * rotq[2];
          z = iz * rotq[3] - iw * rotq[2] - ix * rotq[1] + iy * rotq[0];
          // 球上座標をテクスチャ座標に変換する
          var u = Math.atan2(z, x) / (2 * Math.PI) + 0.5;
          var v = Math.acos(y) / Math.PI;
          u = Math.floor((1 - u) * imgdat.height);
          v = Math.floor((1 - v) * imgdat.width);
          var index2 = v * imgdat.width + u;
          canvasdat.data[index * 4 + 0] = imgdat.data[index2 * 4 + 0];
          canvasdat.data[index * 4 + 1] = imgdat.data[index2 * 4 + 1];
          canvasdat.data[index * 4 + 2] = imgdat.data[index2 * 4 + 2];
          canvasdat.data[index * 4 + 3] = 255;
        }else{
          canvasdat.data[index * 4 + 0] = 0;
          canvasdat.data[index * 4 + 1] = 0;
          canvasdat.data[index * 4 + 2] = 0;
          canvasdat.data[index * 4 + 3] = 255;
        }
      }
    }
    context.putImageData(canvasdat, 0, 0);
    setTimeout(renderScene, 40);
  }
  
  // 画像の読み込み
  var img = new Image();
  img.onload = function(){
    var imgcanvas = document.createElement('canvas');
    imgcanvas.setAttribute('width', 360);
    imgcanvas.setAttribute('height', 360);
    var imgcontext = imgcanvas.getContext('2d');
    imgcontext.drawImage(img, 0, 0, imgcanvas.width, imgcanvas.height);
    imgdat = imgcontext.getImageData(0, 0, imgcanvas.width, imgcanvas.height);
    // 処理開始
    renderScene();
  }

  img.src = document.querySelector ('img').src;

}


//マウスのドラッグで、3Dオブジェクトを回転させる
(function () {

  function CanvasController (canvas) {
    this.canvas = canvas;
    this.mouseX = 0;
    this.mouseY = 0;
    this.touchF = false;
    this.rotq = [0, 0, 0, 1];
    this.gain = 100; // mouse移動の感度
  }
  
  
  function handleEvent (event) {
    var e, x, y, dx, dy, a;
    
    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;
        a = Math.sqrt (dx * dx + dy * dy);
        if(a != 0){
          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'));


addEventListener ('load', load, false);
</script>




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

| 08:25

f:id:babu_babu_baboo:20160430082628p:image

マウスドラッグにより球体の回転を行えるように改良した。

しかし、動作がおかしい

<!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>