<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<HTML LANG="ja-JP">
<META HTTP-EQUIV="content-type" CONTENT="text/html;charset=Shift_JIS">
<META HTTP-EQUIV="content-style-type" CONTENT="text/css">
<META HTTP-EQUIV="content-script-type" CONTENT="text/javascript">
<META HTTP-EQUIV="reply-to" CONTENT="m_kamada@nifty.com">
<HEAD>
<TITLE>JavaScriptでマンデルブロ集合を描く</TITLE>
</HEAD>
<BODY TEXT="#FFFFFF" BGCOLOR="#000000" onload="start()">
<TABLE BORDER="2" CELLSPACING="0" CELLPADDING="0" WIDTH="390" HEIGHT="390">
<TR><TD ALIGN="left" VALIGN="top">
<SPAN ID="view" STYLE="position:absolute"></SPAN>
</TD></TR>
</TABLE>
<SPAN ID="axis" STYLE="position:absolute">中心の座標: </SPAN><BR>
<SPAN ID="size" STYLE="position:absolute">辺の長さ: </SPAN><BR>
<SPAN ID="curr" STYLE="position:absolute">座標: </SPAN><BR>
<SPAN ID="stat" STYLE="position:absolute">■</SPAN><BR>
<BR>
<FORM ACTION="">
解像度:
<SMALL>
<!--
<INPUT NAME="res" TYPE="radio" onclick="W=16;H=16;DX=24;DY=24">16x16
<INPUT NAME="res" TYPE="radio" onclick="W=24;H=24;DX=16;DY=16">24x24
-->
<INPUT NAME="res" TYPE="radio" onclick="W=32;H=32;DX=12;DY=12">32x32
<INPUT NAME="res" TYPE="radio" onclick="W=48;H=48;DX=8;DY=8" CHECKED>48x48
<INPUT NAME="res" TYPE="radio" onclick="W=64;H=64;DX=6;DY=6">64x64
<INPUT NAME="res" TYPE="radio" onclick="W=96;H=96;DX=4;DY=4">96x96
<INPUT NAME="res" TYPE="radio" onclick="W=128;H=128;DX=3;DY=3">128x128
<INPUT NAME="res" TYPE="radio" onclick="W=192;H=192;DX=2;DY=2">192x192
<INPUT NAME="res" TYPE="radio" onclick="W=384;H=384;DX=1;DY=1">384x384
</SMALL>
<BR>
倍率:
<SMALL>
<INPUT NAME="zoom" TYPE="radio" onclick="z=0.25">x4
<INPUT NAME="zoom" TYPE="radio" onclick="z=1/3">x3
<INPUT NAME="zoom" TYPE="radio" onclick="z=0.5" CHECKED>x2
<INPUT NAME="zoom" TYPE="radio" onclick="z=2/3">x1.5
<INPUT NAME="zoom" TYPE="radio" onclick="z=1">x1
<INPUT NAME="zoom" TYPE="radio" onclick="z=1.5">x0.667
<INPUT NAME="zoom" TYPE="radio" onclick="z=2">x0.5
<INPUT NAME="zoom" TYPE="radio" onclick="z=3">x0.333
<INPUT NAME="zoom" TYPE="radio" onclick="z=4">x0.25
</SMALL>
<BR>
リミット:
<SMALL>
<INPUT NAME="max" TYPE="radio" onclick="mx=64" CHECKED>64
<INPUT NAME="max" TYPE="radio" onclick="mx=128">128
<INPUT NAME="max" TYPE="radio" onclick="mx=256">256
<INPUT NAME="max" TYPE="radio" onclick="mx=512">512
<INPUT NAME="max" TYPE="radio" onclick="mx=1024">1024
<INPUT NAME="max" TYPE="radio" onclick="mx=2048">2048
<INPUT NAME="max" TYPE="radio" onclick="mx=4096">4096
<INPUT NAME="max" TYPE="radio" onclick="mx=8192">8192
<INPUT NAME="max" TYPE="radio" onclick="mx=16384">16384
</SMALL>
</FORM>
<SCRIPT LANGUAGE="JavaScript" TYPE="text/javascript">
<!--
var W=48, H=48, DX=8, DY=8; //解像度
var C_PI = 3.14159265358979323846264338328; //円周率(定数)
var ar=-0.5, ai=0, al=3, mx=64; //描画範囲とループ回数の上限の初期設定
var z=0.5; //倍率
var mr, mi; //getpoint()が返すマウスの位置の複素座標
var v = new Array(384); //描画エンジンがテーブルをラスタ単位で保存する配列
var paTable, paCnt = 0; //パレットテーブルとmake_palet_table()が使う変数
var ie = false, n4 = false, n6 = false; //ブラウザの種類を示すフラグ
var yi = "<FONT COLOR=yellow>", yo = "<"+"/FONT>";
//スタートルーチン(BODYのロードが完了した直後に呼び出される)
function start() {
//ブラウザの種類を確認する(IE5.5 SP2、N6.2、NN4.75で動作確認済み)
if (document.all) { //IE4,IE5
ie = true;
} else if (document.getElementById) { //N6.x,Mozilla?
n6 = true;
} else if (document.layers) { //NN4.7x
n4 = true;
}
if (n4) {
document.layers["view"].moveTo(11, 11);
}
//初期設定された範囲を描画する
standby();
}
//描画開始ルーチン(描画エンジンを起動する)
function standby() {
update("axis", "中心の座標: "+ctos(ar, ai));
update("size", "辺の長さ: "+al);
make_palet_table(mx); //パレットテーブルを作る
update("stat", yi+"■計算中です、お待ちください"+yo);
setTimeout("draw()", 100); //描画エンジンを起動する
}
//描画エンジン
function draw() {
var x, y, s;
var cr, ci, zr, zi, cnt, t;
var sz = "WIDTH="+DX+" HEIGHT="+DY; //ピクセルの描画サイズ
var sp = "";
if (n4) {
sp = "<SPACER TYPE=block "+sz+">"; //NN4.7xのときはTDの中にスペーサーが必要
}
//虚数部のループ
for (y = 0; y < H; y++) {
ci = ai-(y-H/2)*al/H; //虚数部
s = "<TR>";
//実数部のループ
for (x = 0; x < W; x++) {
cr = ar+(x-W/2)*al/W; //実数部
zr = cr;
zi = ci;
//マンデルブロ集合の漸化式のループ
for (cnt = 0; cnt < mx; cnt++) {
if (zr*zr+zi*zi > 4.0) {
break;
}
t = zr*zr-zi*zi + cr;
zi = 2*zr*zi + ci;
zr = t;
}
s += "<TD "+sz+" BGCOLOR="+paTable[cnt]+">"+sp+"</"+"TD>"; //テーブルに繋げる
}
s += "<"+"/TR>";
v[y] = s;
//ステータス行に進捗状況を表示
window.status = (y+1)+"/"+H;
}
//TABLEタグを付けて全ラスタを繋げる
v[0] = "<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=0 WIDTH="+(DX*W)+" HEIGHT="+(DY*H)+" onmousemove='move(event)' onclick='zoom(event)'>" + v[0];
v[H-1] +="<"+"/TABLE>";
for (y = 1; y < H; y <<= 1) {
for (x = 0; x < H; x += (y<<1)) {
if (x + y < H) {
v[x] += v[x + y];
}
}
}
//画面に表示する
update("view", v[0]);
//NN4.7xのときはイベントを設定しなおす
if (n4) {
document.view.captureEvents(Event.MOUSEMOVE);
document.view.onmousemove = move;
document.view.captureEvents(Event.CLICK);
document.view.onclick = zoom;
}
//クリック待ち
update("stat", yi+"■拡大・縮小したい場所をクリックしてください"+yo);
//ステータス行をクリア
window.status = "";
}
//画像の上でマウスが動いたとき、座標を表示する
function move(e) {
getpoint(e); //マウスの位置の複素座標を取得
update("curr", "座標: "+ctos(mr, mi)); //複素座標を表示
}
//画像の上でクリックされたら拡大・縮小をおこなう
function zoom(e) {
getpoint(e); //マウスの位置の複素座標を取得
ar = mr; //新しい描画範囲を設定
ai = mi;
al *= z; //拡大または縮小
standby(); //再描画
}
//マウスのイベントから複素座標を求める(mr,miで返す)
function getpoint(e) {
var x, y, el;
//ローカル座標を求める
if (ie) {
x = e.clientX;
y = e.clientY;
el = e.srcElement.offsetParent;
while (el) {
x -= el.offsetLeft;
y -= el.offsetTop;
el = el.offsetParent;
}
x -= document.body.clientLeft + document.body.scrollLeft;
y -= document.body.clientTop + document.body.scrollTop;
} else if (n4) {
x = e.x;
y = e.y;
} else if (n6) {
x = e.layerX;
y = e.layerY;
}
//複素座標に変換する
mr = ar+(x/DX-W/2)*al/W;
mi = ai-(y/DY-H/2)*al/H;
}
//複素数の数値を文字列に変換する
function ctos(r, i) {
var s = "";
if (r >= 0) {
s += atos(r);
} else {
s += "- "+atos(-r);
}
if (i > 0) {
s += " + "+atos(i)+"i";
} else if (i < 0) {
s += " - "+atos(-i)+"i";
}
return s;
}
//数値を文字列に変換する(NN4.7xが0.5を.5にしてしまうので)
function atos(n) {
var str;
if (n == 0) {
str = "0";
} else if (n > 0) {
str = n.toString(10);
if (str.charAt(0) == ".") {
str = "0"+str;
}
} else {
str = (-n).toString(10);
if (str.charAt(0) == ".") {
str = "0"+str;
}
str = "-"+str;
}
return str;
}
//画面の指定された位置を更新する
function update(id, str) {
if (ie) {
document.all(id).innerHTML = str;
} else if (n6) {
document.getElementById(id).innerHTML = str;
} else if (n4) {
with (document.layers[id].document) {
open();
write(str);
close();
}
}
}
//パレットテーブルを作る
function make_palet_table(mxCnt) {
var n, t, u, v;
if (mxCnt == paCnt) {
return;
}
paTable = new Array(mxCnt + 1);
for (n = 0; n < mxCnt; n++) {
t = n / (mxCnt / 15.0);
u = Math.floor(Math.cos((t) * C_PI) * 112.0 + 212.0);
u = (u <= 255) ? u : 255;
v = Math.floor(Math.cos((t + 1.0) * C_PI) * 72.0 + 188.0);
v = (v <= 255) ? v : 255;
paTable[n] = "#"+(0x1000000+hsvtorgb((1536 * n) / mxCnt, u, v)).toString(16).substring(1, 7);
}
paTable[n] = "#000000";
paCnt = mxCnt;
}
//hsvからrgbへの変換(一般的なものとは異なる)
function hsvtorgb(h, s, v) {
var f, f1, s1, v1, w, d;
if (s == 0) {
return rgb(v, v, v);
}
f = h & 255;
f1 = (f << 1) | 1;
s1 = (s << 1) | 1;
v1 = (v << 1) | 1;
w = v - ((s1 * v1 + (1 << 9)) >> 10);
d = (f1 * s1 * v1 + (1 << 18)) >> 19;
switch (h >> 8) {
case 0: return rgb(v, w+d, w);
case 1: return rgb(v-d, v, w);
case 2: return rgb(w, v, w+d);
case 3: return rgb(w, v-d, v);
case 4: return rgb(w+d, w, v);
case 5: return rgb(v, w, v-d);
}
}
//rgbを1つの数値にする
function rgb(r, g, b) {
return (r << 16) | (g << 8) | b;
}
// -->
</SCRIPT>
</BODY>
</HTML>