canvasで完全二分木を描画したいのですが
canvas の部分は、外側にしろとあれほど・・・
<!DOCTYPE html> <meta charset="utf-8"> <title>n木分</title> <body> <canvas width="1000" height="300"></canvas> <script> (function () { // NTreeは Nodeというオブジェクトを木の枝のように、親と子(配列)お持つ function Node (parent) { this.parent = parent; this.child = new Array (); } //_________ // 元というか種を基準 function NTree (seed) { this.seed = seed; } // 末端を1回(n分岐)成長させる function grow (n) { var seed = this.seed; var buf = find (seed, []); var ary = [ ], i, c, j; if (0 === buf.length) buf = [seed]; for (i = 0; c = buf[i]; i += 1) for (j = 0; j < n; j += 1) c.child[j] = new Node (c); } // DOMのように最初の子枝 function firstNode (n) { return n.child[0] ? n.child[0]: null; } // DOMのように弟の枝 function nextSibling (n) { var p = n.parent, no; var c = p.child; if (p) if (no = c.indexOf (n) + 1) if (no < c.length) return c[no]; return null; } // 要素をすべて集めて配列で返す function getAllNodes (node) { if (1 > arguments.length) node = this.seed; var rst = [ ]; var n, b = node; B: while (n = b) if (! (rst.push (n), b = firstNode (n))) while (! (b = nextSibling (n))) if (node === (n = n.parent)) break B; return rst; } // 下の関数で呼ばれる function maxDepth (mx, n) { return Math.max (this.getDepth (n), mx); } // nodeの要素を基準に最大の深さを返す function getMaxDepth (node) { if (1 > arguments.length) node = this.seed; return getAllNodes.call (this).reduce (maxDepth.bind (this), 0); } // nodeの要素の深さを返す function getDepth (node) { var cnt = 0; var p = node; while (p = p.parent) cnt += 1; return cnt; } // 要素に番号を振る function setNumber (nd, i) { nd.number = this + i; } // 要素を基準に番号を振る(今回の例は再帰ではないので順番が特殊?) function setAutoNumber (node, no) { switch (arguments.length) { case 0 : node = this.seed; case 1 : no = 0; } var nd = getAllNodes.call (this, node); var gd = [ ], d, i, n; for (i = 0; n = nd[i]; i += 1) { d = this.getDepth (n); if ('undefined' === typeof gd[d]) gd[d] = [ ]; gd[d].push (n); } Array.prototype.concat.apply ([], gd) .forEach (setNumber, no);//誉めたたえるべきはここか!? } //子要素を持たない要素を見つける function find (node, buf) { var child = node.child; for (var i = 0, c; c = child[i]; i += 1) hasChildNodes (c) ? find (c, buf) : buf.push (c); return buf; } // 子要素を持っているか? function hasChildNodes (node) { return !! node.child.length; } function create (depth, n) { if (2 > arguments.length) n = 2; var seed = new Node (null); var tree = new NTree (seed); var i; for (i = 0; i < depth; i += 1) tree.grow (n); return tree; } NTree.prototype.grow = grow; NTree.prototype.getAllNodes = getAllNodes; NTree.prototype.getMaxDepth = getMaxDepth; NTree.prototype.getDepth = getDepth; NTree.prototype.setAutoNumber = setAutoNumber; NTree.create = create; this.NTree = NTree; }) (); var tree = NTree.create (5); tree.setAutoNumber (tree.seed, 1); (function (tree) { var canvas = document.querySelector ('canvas'); var ctx = canvas.getContext('2d'); var r = 10; var mx = canvas.width; var my = canvas.height; var stepY = tree.getMaxDepth () + 1; var nd = tree.getAllNodes (); var i, j, cx, cy, d, row, n, s, c; var gnd = [ ]; for (i = 0; n = nd[i]; i += 1) { d = tree.getDepth (n); if ('undefined' === typeof gnd[d]) gnd[d] = [ ]; gnd[d].push (n); } for (i = 0; row = gnd[i]; i += 1) { cy = my / stepY * i + r; for (j = 0; n = row[j]; j += 1) { cx = mx / (row.length) / 2 + (mx / row.length * j); n.x = cx; n.y = cy; } } ctx.textAlign = "center"; ctx.textBaseline ="middle"; ctx.font = Math.floor (r * 1.4) +"px 'Times New Roman'"; for (i = 0; n = nd[i]; i += 1) { cx = n.x; cy = n.y; for (j = 0; c = n.child[j]; j += 1) { ctx.beginPath (); ctx.moveTo (cx, cy); ctx.lineTo (c.x, c.y) ctx.stroke (); } ctx.beginPath (); ctx.strokeStyle = 'rgb(0,0,0)'; ctx.fillStyle = 'rgb(255, 255, 255)'; ctx.arc (cx, cy, r, 0, Math.PI*2); ctx.fill (); ctx.fillStyle = 'rgb(0, 0,0)'; ctx.fillText(n.number + '', cx, cy + r/ 8); ctx.stroke (); } }) (tree); </script>