2020年5月11日月曜日

ヒルベルト曲線を描く

JavaScriptでHTMLキャンバスにヒルベルト曲線を描くプログラムです.

このプログラムのキモはこちら.中央大学国際情報学部の1年生向け科目「プログラミングのための数学」で解説しています.




より詳しく知りたい方は「飯尾淳,連載:バーティカルバーの極意,シェルスクリプトマガジン vol.65, pp. 74-78, 2020年3月25日」で解説しているので,そちらをご参照ください(雑誌で解説しているバージョンは,誌面の関係からアニメーションしません).

const MAX_COUNT = 8;

// the canvas and its graphic context
var cs  = document.getElementById('theCanvas');
var ctx = cs.getContext('2d');

// line style
var colors = [ 'gray', 'navy', 'purple', 'brown', 
  'red', 'orange', 'yellowgreen', 'skyblue' ];
var widths = [5, 4, 3, 2, 2, 1, 1, 0.5 ];

var tm = [
  [ [  0,  1/2,   0], [ 1/2,   0,   0], [0, 0, 1] ],
  [ [1/2,    0,   0], [   0, 1/2, 1/2], [0, 0, 1] ],
  [ [1/2,    0, 1/2], [   0, 1/2, 1/2], [0, 0, 1] ],
  [ [  0, -1/2,   1], [-1/2,   0, 1/2], [0, 0, 1] ]
]

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

function affine_transform(m, p) {
  return [ m[0][0] * p[0] + m[0][1] * p[1] + m[0][2],
       m[1][0] * p[0] + m[1][1] * p[1] + m[1][2] ];
}

function mat_mul(m0, m1) {
  return [ [m0[0][0]*m1[0][0]+m0[0][1]*m1[1][0]+m0[0][2]*m1[2][0],
            m0[0][0]*m1[0][1]+m0[0][1]*m1[1][1]+m0[0][2]*m1[2][1],
            m0[0][0]*m1[0][2]+m0[0][1]*m1[1][2]+m0[0][2]*m1[2][2]],
           [m0[1][0]*m1[0][0]+m0[1][1]*m1[1][0]+m0[1][2]*m1[2][0],
            m0[1][0]*m1[0][1]+m0[1][1]*m1[1][1]+m0[1][2]*m1[2][1],
            m0[1][0]*m1[0][2]+m0[1][1]*m1[1][2]+m0[1][2]*m1[2][2]],
           [m0[2][0]*m1[0][0]+m0[2][1]*m1[1][0]+m0[2][2]*m1[2][0],
            m0[2][0]*m1[0][1]+m0[2][1]*m1[1][1]+m0[2][2]*m1[2][1],
            m0[2][0]*m1[0][2]+m0[2][1]*m1[1][2]+m0[2][2]*m1[2][2]] ];
}

function hilbert(n, m) {
  if (n > 0) {
    tm.forEach(mm => hilbert(n-1, mat_mul(m, mm)));
  } else {
    [ [0.25, 0.25], [0.25, 0.75], [0.75, 0.75], [0.75, 0.25] ]
     .map(p => affine_transform(m, p))
     .map(p => [p[0]*cs.width, p[1]*cs.height])
     .forEach(p => ctx.lineTo(p[0],p[1]));
  }
}

function drawHilbert(i) {
  ctx.beginPath();
  ctx.lineWidth   = widths[i];
  ctx.strokeStyle = colors[i];
  hilbert(i, E);
  ctx.stroke();
}

function doLoop(maxCount, i) {
  if (i < maxCount) {
    drawHilbert(i);
    setTimeout(function() {
      doLoop(maxCount, ++i) }, 1500);
  } else {
   ctx.clearRect(0, 0, cs.width, cs.height);
   doLoop(MAX_COUNT, 0);
  }
}

doLoop(MAX_COUNT, 0);

1 件のコメント:

  1. var colors = [ 'gray', 'navy', 'purple', 'brown', 'red', 'orange', 'yellowgreen', 'skyblue' ];
    var widths = [5, 4, 3, 2, 2, 1, 1, 0.5 ];
    の部分は,本当は var colors_and_width = [ ['gray', 5], ['navy', 4], ...
    という定義にしたほうが望ましい

    返信削除