HTML5では、ImageDataというフレームバッファ機能を持つCanvas要素が導入されました。このImageDataを使うと、JavaScriptのコードでWebページに配置したCanvas要素に対し「計算によるCG」を描くことができます。
今回は、計算でCGを描くWebアプリとして「マンデルブロ集合描画アプリ」を作ってみることにしましょう。
マンデルブロ集合(として描かれる図形)は、横軸を実部、縦軸を虚部に対応させた複素平面(ガウス平面)の各点(C)に以下の計算を繰り返し、複素数Znが発散しない点を集めたものです。
Z0=0
Zn+1=Zn2+C
実部と虚部それぞれの数値を持つ複素数は、プログラムにおける計算では実部と虚部に対応させた二つの変数で表現できますから、複素数Zの実部をa、虚部をbとするとZ2は
(a + bi)2=(a * a) + (a * bi) * 2 + (bi * bi)
と表せます。iは二乗すると-1になる数なので、これは
(a * a) + (a * bi) * 2 - (b * b)
ということになり、実部と虚部をまとめると
実部:(a * a) - (b * b)
虚部:(a * b) * 2
となるわけですね。
以上のことから、ある複素平面上の点C(実部:cr、虚部:ci)に対するマンデルブロ集合の判定は、最初にzrを実部、ziを虚部とする複素数を用意しzr/ziを0とした上で
tr = (zr * zr) - (zi * zi) + cr; ti = (zr * zi * 2.0) + ci; zr = tr; zi = ti;
という計算を繰り返すことになります。この複素数の大きさ(絶対値)が無限大に発散しなければ(絶対値が2を超えなければ)、マンデルブロ集合に含まれると判定するわけです。
今回のWebアプリでは、400*400ピクセルのCanvas要素を複素平面に見立ててこのマンデルブロ集合を描くことにしましょう。
計算時には、「最大何回の計算を行うか」を予め設定(変数mandjs_maxN)しておき、この回数計算を繰り返しても発散しなければマンデルブロ集合内と判定します。
各ピクセルには、対応する座標がマンデルブロ集合に含まれるか、含まれない場合は何回の計算で発散したかに応じて色(RGB値)を設定します。JavaScriptには、文字列を「JavaScriptの計算コード」として評価し計算結果を返す関数eval()があるので、色の計算式を文字列で設定できるようにしてみました。
// 色設定 var mandjs_color = new Object(); // マンデルブロ集合内 mandjs_color.inR = 'i / 3'; mandjs_color.inG = '0'; mandjs_color.inB = '255 - (j / 2)'; // マンデルブロ集合外 mandjs_color.outR = 'n * 3'; mandjs_color.outG = 'n * 4'; mandjs_color.outB = 'n * 2';
i/jは、Canvas上の座標、nは計算回数として取得できる変数です。
計算処理と色設定の具体的な方針も決まりましたから、さっそく実装してみましょう。今回は単に計算処理をJavaScriptのコードに落として、evalに色設定の文字列を放り込みその値をImageDataに書き込むだけですから、簡単ですね。
ただ、400*400ピクセルだと実に160000回もの計算を繰り返すことになります。どれだけ時間がかかるか不安……でしたが、やってみると「一瞬」でした。
実際に、上の設定で計算処理を行うJavaScript関数を含むWebページをWebブラウザに読み込ませると、以下のようなマンデルブロ集合の画像が生成されます。
ごく単純な複素数の計算から、このような複雑な図形が生成されるのは興味深いですね。今回は、計算パラメータや色設定を直接JavaScriptのコードに埋め込みましたが、ユーザーが自由に設定できるようにしておくと神秘的な画像を生成するCG生成アプリとして便利かもしれません。
CanvasのtoDataURL()で生成された画像をData URI化し、(右クリックメニューから)画像ファイルとして保存できるリンクも生成するようにしてみました。