mandel_n4.htm

戻る

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML LANG="ja">

<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html;charset=Shift_JIS">
<TITLE>マンデルブロ アート</TITLE>
</HEAD>

<BODY BGCOLOR="#CCFFFF" onResize="resize()">
<CENTER>
<B>マンデルブロ アート (Netscape 4)</B>
<BR><BR>

<SCRIPT LANGUAGE="JavaScript1.2" TYPE="text/javascript">
<!--

  SIZE_X = 300;
  SIZE_Y = 300;

  SIZE_X_H = SIZE_X >> 1;
  SIZE_Y_H = SIZE_Y >> 1;

  // 現在の GIF イメージ データ(初期値は空白)
  cur_image_data = "GIF89a\x01\x00\x01\x00\x80\x00\x00\x00\x00\x00"
                     + "\xff\xff\xff\x2c\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x01\x4c"
                     + "\x00\x3b\x00\x00\x00\x00\x00";

//-->
</SCRIPT>

<IMG NAME="img" SRC="javascript:cur_image_data" WIDTH="&{SIZE_X};" HEIGHT="&{SIZE_Y};">
<BR><BR>

現在の表示範囲<BR>
<LAYER NAME="range"></LAYER>
<TABLE CELLPADDING=0><TR><TD><BR><BR></TD></TR></TABLE>

<FORM>

パレット
<SELECT NAME="pal" onChange="sel_palette()">
<OPTION SELECTED>8 色繰り返し
<OPTION>64 色繰り返し 1
<OPTION>64 色繰り返し 2
<OPTION>グレー 256 階調
<OPTION>赤 256 階調
<OPTION>緑 256 階調
<OPTION>青 256 階調
<OPTION>赤 - 緑 - 青
<OPTION>赤 - 青 - 緑
<OPTION>緑 - 青 - 赤
<OPTION>緑 - 赤 - 青
<OPTION>青 - 赤 - 緑
<OPTION>青 - 緑 - 赤
<OPTION>ランダム
</SELECT>
<BR>

</FORM>

<TABLE CELLSPACING=0 CELLPADDING=0>
<TR><TD ALIGN=LEFT><ILAYER NAME="p_l1" LEFT=-12 VISIBILITY="hide">集合</ILAYER></TD>
    <TD ALIGN=RIGHT><ILAYER NAME="p_l2" LEFT=12 VISIBILITY="hide">1 回</ILAYER></TD></TR>
<TR><TD COLSPAN=2>
<ILAYER NAME="p_back" WIDTH=258 HEIGHT=16 CLIP="0,0,258,16">

<SCRIPT LANGUAGE="JavaScript1.2" TYPE="text/javascript">
<!--
  for(var i = 0; i < 256; i++)
    document.write("<LAYER NAME='p", String(i), "' LEFT=", String(1 + i),
                   " TOP=1 WIDTH=1 HEIGHT=14></LAYER>");
//-->
</SCRIPT>

</ILAYER>
</TD></TR>
</TABLE>

<SCRIPT LANGUAGE="JavaScript1.2" TYPE="text/javascript">
<!--
  document.p_l1.visibility = "show";
  document.p_l2.visibility = "show";
  document.p_back.bgColor = "black";
//-->
</SCRIPT>

<BR>

<FORM>

<TABLE>
<TR><TD VALIGN=TOP>倍率</TD><TD NOWRAP>
<INPUT TYPE=RADIO NAME="factor" CHECKED onClick="sel_factor(0)"> 変更なし
<BR>
<INPUT TYPE=RADIO NAME="factor" onClick="sel_factor(1)"> 1.5 倍
<INPUT TYPE=RADIO NAME="factor" onClick="sel_factor(2)"> 2 倍
<INPUT TYPE=RADIO NAME="factor" onClick="sel_factor(3)"> 3 倍
<INPUT TYPE=RADIO NAME="factor" onClick="sel_factor(4)"> 4 倍
<INPUT TYPE=RADIO NAME="factor" onClick="sel_factor(5)"> 8 倍
<INPUT TYPE=RADIO NAME="factor" onClick="sel_factor(6)"> 16 倍
<BR>
<INPUT TYPE=RADIO NAME="factor" onClick="sel_factor(7)"> 2/3
<INPUT TYPE=RADIO NAME="factor" onClick="sel_factor(8)"> 1/2
<INPUT TYPE=RADIO NAME="factor" onClick="sel_factor(9)"> 1/3
<INPUT TYPE=RADIO NAME="factor" onClick="sel_factor(10)"> 1/4
<INPUT TYPE=RADIO NAME="factor" onClick="sel_factor(11)"> 1/8
<INPUT TYPE=RADIO NAME="factor" onClick="sel_factor(12)"> 1/16
</TD></TR></TABLE>
<BR>

<INPUT TYPE=BUTTON NAME="reset" VALUE="初期位置・倍率" onClick="reset_para()">
<BR><BR>

</FORM>

<LAYER NAME="msg1" VISIBILITY="hide"><CENTER>計算中...</CENTER></LAYER>
<LAYER NAME="msg2" VISIBILITY="hide"><CENTER>画像作成中...</CENTER></LAYER>
<BR>

<TABLE CELLSPACING=0 CELLPADDING=0>
<TR><TD></TD></TR>
<TR><TD>
<ILAYER NAME="prog_back" BGCOLOR="black" WIDTH="&{SIZE_Y_H + 2};" HEIGHT=10 CLIP="0,0,&{SIZE_Y_H + 2};,10" VISIBILITY="hide">
<LAYER BGCOLOR="white" LEFT=1 TOP=1 WIDTH="&{SIZE_Y_H};" HEIGHT=8></LAYER>
<LAYER NAME="prog" BGCOLOR="lime" LEFT=1 TOP=1 WIDTH=1 HEIGHT=8></LAYER>
</ILAYER>
</TD></TR>
</TABLE>

<ILAYER NAME="can_base" VISIBILITY="hide">
<FORM>
<INPUT TYPE=BUTTON NAME="can" VALUE="中止" onClick="cancel = true">
<BR>
</FORM>
</ILAYER>

</CENTER>

<SCRIPT LANGUAGE="JavaScript1.2" TYPE="text/javascript">
<!--

// 画像クリック
function clicked(e) {
  var x = e.pageX - document.img.x;
  var y = e.pageY - document.img.y;
  if(x < 0 || x >= SIZE_X || y < 0 || y >= SIZE_Y)
    return;

  if(input_disabled || !cur_image_data.length)  // 入力ディスエーブル or イメージ未作成
    return;

  var w_ratio = ratio;
  switch(cur_i_factor) {
  case 1:  // x 1.5
    w_ratio /= 1.5;
    break;
  case 2:  // x 2
    w_ratio /= 2;
    break;
  case 3:  // x 3
    w_ratio /= 3;
    break;
  case 4:  // x 4
    w_ratio /= 4;
    break;
  case 5:  // x 8
    w_ratio /= 8;
    break;
  case 6:  // x 16
    w_ratio /= 16;
    break;
  case 7:  // 2/3
    w_ratio *= 1.5;
    break;
  case 8:  // 1/2
    w_ratio *= 2;
    break;
  case 9:  // 1/3
    w_ratio *= 3;
    break;
  case 10:  // 1/4
    w_ratio *= 4;
    break;
  case 11:  // 1/8
    w_ratio *= 8;
    break;
  case 12:  // 1/16
    w_ratio *= 16;
    break;
  }
  if(w_ratio == 0) {
    alert("倍率が大きくなりすぎます");
    return;
  }
  if(!isFinite(w_ratio)) {
    alert("倍率が小さくなりすぎます");
    return;
  }
  var w_center_r = center_r + (x - SIZE_X_H) * ratio;
  var w_center_i = center_i + (y - SIZE_Y_H) * ratio;
  if(w_center_r - w_ratio == w_center_r || w_center_i - w_ratio == w_center_i
  || w_center_r + w_ratio == w_center_r || w_center_i + w_ratio == w_center_i  // 桁落ち?
  || !isFinite(w_center_r - SIZE_X_H * w_ratio) || !isFinite(w_center_i - SIZE_Y_H * w_ratio)
  || !isFinite(w_center_r + SIZE_X_H * w_ratio) || !isFinite(w_center_i + SIZE_Y_H * w_ratio)) {
    alert("計算できません");
    return;
  }

  center_r = w_center_r;
  center_i = w_center_i;
  ratio = w_ratio;

  document.forms[1].factor[0].checked = true;  // x 1 に戻す
  cur_i_factor = 0;

  mandelbrot();
}

function isFinite(num) {
  return !(num == Number.POSITIVE_INFINITY || num == Number.NEGATIVE_INFINITY);
}

// 初期位置・倍率
function reset_para() {
  if(input_disabled)  // 入力ディスエーブル
    return;

  center_r = center_i = 0.0;
  ratio = 0.01;
  document.forms[1].factor[0].checked = true;  // x 1 に戻す
  cur_i_factor = 0;

  mandelbrot();
}

// マンデルブロ集合作成
function mandelbrot() {
  input_disabled = true;

  document.msg1.visibility = "show";
  document.prog_back.document.prog.clip.right = 0;
  document.prog_back.visibility = "show";
  document.can_base.visibility = "show";
  cancel = false;
  setTimeout("mandelbrot1(0)", 0);
}

function mandelbrot1(y) {
  if(cancel) {
    end_proc();
    return;
  }

  var b = center_i + (y - SIZE_Y_H) * ratio;
  for(var x = 0; x < SIZE_X; x++) {
    var a = center_r + (x - SIZE_X_H) * ratio;
    var zr, zi;
    var zr2, zi2;
    zr = zi = zr2 = zi2 = 0.0;
    var cnt;
    for(cnt = 255; cnt; cnt--) {
      zi = 2 * zr * zi + b;
      zr = zr2 - zi2 + a;
      if((zr2 = zr * zr) + (zi2 = zi * zi) > 4)
        break;
    }
    pixels[y][x] = cnt;
  }

  y++;
  if(!(y & 0x1))
    document.prog_back.document.prog.clip.right = y >> 1;
  if(y == SIZE_Y) {
    if(cancel) {
      end_proc();
      return;
    }
    setTimeout("create_image()", 0);
  }
  else {
    setTimeout("mandelbrot1(" + String(y) + ")", 0);
  }
}

function create_image() {
  image_data = "";

  // Header,Logical Screen Descriptor,Global Color Table,Image Descriptor
  // と LZW Minimum Code Size
  var i;
  for(i = 0; i < header1.length; i++)
    putc(header1[i]);
  for(i = 0; i < 256; i++) {
    with(cur_palette[i]) {
      putc(r);
      putc(g);
      putc(b);
    }
  }
  for(i = 0; i < header2.length; i++)
    putc(header2[i]);

  block_cnt = 0;

  pack_buff = 0;
  pack_bits = 0;

  code_bits = 9;
  max_n = 512;
  n_code = 258;

  put_code(256);  // Clear

  code = pixels[0][0];
  prop = root_prop[code];

  document.msg1.visibility = "hide";
  document.msg2.visibility = "show";
  document.prog_back.document.prog.clip.right = 0;
  setTimeout("create_image1(0, 1)", 0);
}

function create_image1(y, x) {
  if(cancel) {
    end_proc();
    return;
  }

  while(x < SIZE_X) {
    // 次の 1 ピクセル
    var c = pixels[y][x++];

    var cs = String.fromCharCode(c);
    // 辞書サーチ
    var i = -1;
    if(prop.chara)
      i = prop.chara.indexOf(cs);
    if(i == -1) {  // 未登録
      put_code(code);

      if(n_code == 4095) {  // 辞書が一杯(のひとつ前)
        // 辞書クリア
        put_code(256);  // Clear

        for(i = 0; i < 256; i++) {
          delete root_prop[i].chara;
          delete root_prop[i].prop;
        }
        n_code = 258;
        code_bits = 9;
        max_n = 512;
      }
      else {
        // 辞書に登録
        if(prop.chara) {
          prop.chara += cs;
          prop.prop.push({code:n_code});
        }
        else {
          prop.chara = cs;
          prop.prop = [{code:n_code}];
        }
        if(n_code++ == max_n) {
          code_bits++;
          max_n <<= 1;
        }
      }
      code = c;
      prop = root_prop[c];
    }
    else {  // 登録済み
      code = prop.prop[i].code;
      prop = prop.prop[i];
    }
  }

  y++;
  if(!(y & 0x1))
    document.prog_back.document.prog.clip.right = y >> 1;
  if(y == SIZE_Y)
    setTimeout("create_image2()", 0);
  else
    setTimeout("create_image1(" + String(y) + ", 0)", 0);
}

function create_image2() {
  put_code(code);  // 最後のコード
  put_code(257);  // End of Information
  // コード パック用バッファ フラッシュ
  if(pack_bits)
    put_code(0);

  // ブロック バッファ フラッシュ
  if(block_cnt) {
    putc(block_cnt);
    for(var i = 0; i < block_cnt; i++)
      putc(block_buff[i]);
  }

  putc(0x00);  // Block Terminator
  putc(0x3b);  // Trailer

  // イメージ表示
  cur_image_data = image_data;
  document.img.src = document.img.src;  // 表示を更新

  // 表示範囲
  var str1 = range_num(center_i - SIZE_Y_H * ratio);
  if(str1.charAt(0) != "-")
    str1 = "+" + str1;
  str1 = range_num(center_r - SIZE_X_H * ratio) + str1 + "i";
  var str2 = range_num(center_i + SIZE_Y_H * ratio);
  if(str2.charAt(0) != "-")
    str2 = "+" + str2;
  str2 = range_num(center_r + SIZE_X_H * ratio) + str2 + "i";
  document.range.document.open();
  document.range.document.write(
    "<CENTER><TABLE CELLPADDING=0><TR><TD NOWRAP>", str1, "<BR> 〜 ", str2, "<BR></TD></TR></TABLE></CENTER>");
  document.range.document.close();

  end_proc();
}

// 表示範囲の数値をフォーマット
function range_num(num) {
  var str = String(num).split("e");
  if(str[0].charAt(0) == ".")
    str[0] = "0" + str[0];
  else if(str[0].substr(0, 2) == "-.")
    str[0] = "-0" + str[0].substr(1);
  if(str.length == 1)
    return str[0];
  return str[0] + "×10<SUP>" + str[1].replace(/\+/, "") + "</SUP>";
}

// LZW コード出力
function put_code(code) {
  pack_buff |= code << pack_bits;
  pack_bits += code_bits;
  while(pack_bits >= 8) {
    block_buff[block_cnt++] = pack_buff & 0xff;
    if(block_cnt == 255) {
      putc(255);
      for(var i = 0; i < 255; i++)
        putc(block_buff[i]);
      block_cnt = 0;
    }
    pack_buff >>= 8;
    pack_bits -= 8;
  }
}

// イメージ データに 1 バイト出力
function putc(c) {
  image_data += String.fromCharCode(c);
}

// マンデルブロ集合作成終了
function end_proc() {
  // 辞書ルートを初期状態に戻す
  for(var i = 0; i < 256; i++) {
    delete root_prop[i].chara;
    delete root_prop[i].prop;
  }

  document.msg1.visibility = "hide";
  document.msg2.visibility = "hide";
  document.prog_back.visibility = "hide";
  document.can_base.visibility = "hide";

  input_disabled = false;
}

// パレット選択
function sel_palette() {
  var i;

  if(input_disabled) {  // 入力ディスエーブル
    document.forms[0].pal.selectedIndex = cur_i_pal;
    return;
  }

  cur_i_pal = document.forms[0].pal.selectedIndex;
  if(cur_i_pal == 13) {  // ランダム
    for(i = 0; i < 256; i++) {
      var v = Math.floor(Math.random() * 0x1000000);
      palette[13][i] = {r:v >> 16, g:(v >> 8) & 0xff, b:v & 0xff};
    }
  }

  set_palette();

  if(cur_image_data.length < 256)  // イメージ未作成
    return;

  // GIF イメージのカラー テーブルのみを書き換える.
  image_data = "";
  for(i = 0; i < 256; i++) {
    with(cur_palette[i]) {
      putc(r);
      putc(g);
      putc(b);
    }
  }
  cur_image_data = cur_image_data.substr(0, header1.length) + image_data
                     + cur_image_data.substr(header1.length + 256 * 3);
  document.img.src = document.img.src;  // 表示を更新
}

// パレット設定
function set_palette() {
  cur_palette = palette[cur_i_pal];

  for(var i = 0; i < 256; i++) {
    with(cur_palette[i]) {
      document.p_back.document.layers["p" + String(i)].bgColor
        = ("0" + r.toString(16)).substr(-2)
        + ("0" + g.toString(16)).substr(-2)
        + ("0" + b.toString(16)).substr(-2);
    }
  }
}

// 倍率選択
function sel_factor(i_factor) {
  if(input_disabled) {  // 入力ディスエーブル
    document.forms[1].factor[cur_i_factor].checked = true;
    return;
  }

  cur_i_factor = i_factor;
}

function resize() {
  // リサイズしたときレイヤー関係の表示が崩れる現象の対策
  document.p_back.left = 0
  for(var i = 0; i < 256; i++) {
    with(cur_palette[i]) {
      document.p_back.document.layers["p" + String(i)].bgColor
        = ("0" + r.toString(16)).substr(-2)
        + ("0" + g.toString(16)).substr(-2)
        + ("0" + b.toString(16)).substr(-2);
    }
  }
}

//----------------------------------------------------------

  center_r = center_i = 0.0;  // 表示中心値
  ratio = 0.01;  // 1 ピクセルの大きさ

  // イメージ配列
  pixels = new Array();
  pixels.length = SIZE_Y;
  for(i = 0; i < SIZE_Y; i++) {
    pixels[i] = new Array();
    pixels[i].length = SIZE_X;
  }

  // Header,Logical Screen Descriptor
  header1 = [
    0x47, 0x49, 0x46, 0x38, 0x39, 0x61,  // GIF89a
    SIZE_X & 0xff, SIZE_X >> 8,
    SIZE_Y & 0xff, SIZE_Y >> 8,
    0xf7,                                // Global Color Table,256色
    0x00,                                // Background Color Index
    0x00                                 // Pixel Aspect Ratio
  ];
  // Image Descriptor と LZW Minimum Code Size
  header2 = [
    0x2c,                                // Image Separator
    0x00, 0x00, 0x00, 0x00,              // 0,0
    SIZE_X & 0xff, SIZE_X >> 8,
    SIZE_Y & 0xff, SIZE_Y >> 8,
    0x00,
    8                                    // 8 ビット
  ];

  // 辞書ルート
  root_prop = new Array();
  root_prop.length = 256;
  for(i = 0; i < 256; i++)
    root_prop[i] = {};

  var n_code;  // コード登録数
  var code_bits;  // コード ビット数
  var max_n;  // 最大コード登録数
  var code;  // コード
  var prop;  // 辞書情報
  var pack_buff;  // コード パック用バッファ
  var pack_bits;  // コード パック用バッファ格納ビット数
  block_buff = new Array();  // ブロック バッファ
  block_buff.length = 255;
  var block_cnt;             // ブロック バッファ格納バイト数
  var image_data;  // GIF イメージ データ

  var cancel;  // 中止フラグ
  input_disabled = true;  // 入力ディスエーブル フラグ

  // 各種パレット作成
  palette = new Array();
  palette.length = 14;
  for(i = 0; i < 14; i++) {
    palette[i] = new Array();
    palette[i].length = 256;
  }

  for(i = 0; i < 256; i++) {
    palette[0][i] = {r:(i & 0x1) ? 255 : 0, g:(i & 0x2) ? 255 : 0, b:(i & 0x4) ? 255 : 0};
    palette[1][i] = {r:(i & 0x03) * 85, g:((i & 0x0c) >> 2) * 85, b:((i & 0x30) >> 4) * 85};
    palette[2][i] = {r:(  i & 0x01        | ((i & 0x08) >> 2)) * 85,
                     g:(((i & 0x02) >> 1) | ((i & 0x10) >> 3)) * 85,
                     b:(((i & 0x04) >> 2) | ((i & 0x20) >> 4)) * 85};
    palette[3][i] = {r:i, g:i, b:i};
    palette[4][i] = {r:i, g:0, b:0};
    palette[5][i] = {r:0, g:i, b:0};
    palette[6][i] = {r:0, g:0, b:i};
  }

  for(i = 0; i < 64; i++) {
    var v = Math.round(i * 255 / 63);
    palette[7 ][i      ] = {r:255, g:v, b:0};
    palette[7 ][127 - i] = {r:v, g:255, b:0};
    palette[7 ][128 + i] = {r:0, g:255, b:v};
    palette[7 ][255 - i] = {r:0, g:v, b:255};
    palette[8 ][i      ] = {r:255, g:0, b:v};
    palette[8 ][127 - i] = {r:v, g:0, b:255};
    palette[8 ][128 + i] = {r:0, g:v, b:255};
    palette[8 ][255 - i] = {r:0, g:255, b:v};
    palette[9 ][i      ] = {r:0, g:255, b:v};
    palette[9 ][127 - i] = {r:0, g:v, b:255};
    palette[9 ][128 + i] = {r:v, g:0, b:255};
    palette[9 ][255 - i] = {r:255, g:0, b:v};
    palette[10][i      ] = {r:v, g:255, b:0};
    palette[10][127 - i] = {r:255, g:v, b:0};
    palette[10][128 + i] = {r:255, g:0, b:v};
    palette[10][255 - i] = {r:v, g:0, b:255};
    palette[11][i      ] = {r:v, g:0, b:255};
    palette[11][127 - i] = {r:255, g:0, b:v};
    palette[11][128 + i] = {r:255, g:v, b:0};
    palette[11][255 - i] = {r:v, g:255, b:0};
    palette[12][i      ] = {r:0, g:v, b:255};
    palette[12][127 - i] = {r:0, g:255, b:v};
    palette[12][128 + i] = {r:v, g:255, b:0};
    palette[12][255 - i] = {r:255, g:v, b:0};
  }

  // 初期パレット設定
  cur_i_pal = 0;
  set_palette();

  cur_i_factor = 0;

  // ページを再ロードしたときコントロールの表示が初期化されない現象の対策
  document.forms[0].pal.selectedIndex = 0;
  document.forms[1].factor[0].checked = true;

  captureEvents(Event.MOUSEDOWN);
  onmousedown = clicked;

  // 初期表示
  mandelbrot();

//-->
</SCRIPT>

</BODY>

</HTML>