mandel_o8.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">
<CENTER>
<B>マンデルブロ アート (Opera 8)</B>
<BR><BR>

<IMG ID="img" SRC="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOwAA"
 WIDTH=1 HEIGHT=1 onClick="clicked(event)">
<BR><BR>

現在の表示範囲<BR>
<TABLE><TR><TD VALIGN=TOP NOWRAP>
<SPAN ID="range-f-r-1"> </SPAN><SUP ID="range-f-r-2"> </SUP><SPAN ID="range-f-i-1"> </SPAN><SUP ID="range-f-i-2"> </SUP><SPAN ID="range-f-i-3"> </SPAN>
 〜 
<SPAN ID="range-t-r-1"> </SPAN><SUP ID="range-t-r-2"> </SUP><SPAN ID="range-t-i-1"> </SPAN><SUP ID="range-t-i-2"> </SUP><SPAN ID="range-t-i-3"> </SPAN>
</TD><TD><BR><BR></TD></TR></TABLE>

<FORM>

パレット
<SELECT ID="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>
<DIV STYLE="position:relative; width:280px">
<BR>
<SPAN STYLE="position:absolute; left:0; top:0">集合</SPAN>
<SPAN STYLE="position:absolute; right:0; top:0">1 回</SPAN>
</DIV>
<DIV STYLE="position:relative; width:258px; height:16px; border:solid black 1px">

<SCRIPT LANGUAGE="JavaScript1.5" TYPE="text/javascript">
<!--
  for(var i = 0; i < 256; i++)
    document.write("<SPAN ID='p", String(i), "' STYLE='position:absolute; left:",
                   String(i), "px; top:0; width:1px; height:14px'></SPAN>");
//-->
</SCRIPT>

</DIV>
<BR>

<TABLE>
<TR><TD VALIGN=TOP>倍率</TD><TD NOWRAP>
<INPUT TYPE=RADIO NAME="factor" CHECKED> 変更なし
<BR>
<INPUT TYPE=RADIO NAME="factor"> 1.5 倍
<INPUT TYPE=RADIO NAME="factor"> 2 倍
<INPUT TYPE=RADIO NAME="factor"> 3 倍
<INPUT TYPE=RADIO NAME="factor"> 4 倍
<INPUT TYPE=RADIO NAME="factor"> 8 倍
<INPUT TYPE=RADIO NAME="factor"> 16 倍
<BR>
<INPUT TYPE=RADIO NAME="factor"> 2/3
<INPUT TYPE=RADIO NAME="factor"> 1/2
<INPUT TYPE=RADIO NAME="factor"> 1/3
<INPUT TYPE=RADIO NAME="factor"> 1/4
<INPUT TYPE=RADIO NAME="factor"> 1/8
<INPUT TYPE=RADIO NAME="factor"> 1/16
</TD></TR></TABLE>
<BR>

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

<TABLE>
<TR><TD ALIGN=CENTER><DIV ID="msg" STYLE="visibility:hidden"> </DIV></TD></TR>
<TR><TD ALIGN=CENTER><DIV ID="prog-back" STYLE="position:relative; height:10px; background-color:white; border:solid black 1px; visibility:hidden">
                     <SPAN ID="prog" STYLE="position:absolute; left:0; top:0; height:8px; background-color:lime"></SPAN>
                     </DIV></TD></TR>
<TR><TD ALIGN=CENTER><INPUT TYPE=BUTTON ID="can" VALUE="中止" onClick="cancel = true" STYLE="visibility:hidden"></TD></TR>
</TABLE>

</FORM>

</CENTER>

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

  SIZE_X = 300;
  SIZE_Y = 300;

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

// 画像クリック
function clicked(e) {
  if(image_disabled || !cur_image_data.length)  // 画像ディスエーブル or イメージ未作成
    return;

  // 倍率取り込み
  for(var i = 0; i < document.forms[0].factor.length; i++) {
    if(document.forms[0].factor[i].checked)
      break;
  }
  var w_ratio = ratio;
  switch(i) {
  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 + (e.offsetX - SIZE_X_H) * ratio;
  var w_center_i = center_i + (e.offsetY - 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[0].factor[0].checked = true;  // x 1 に戻す

  mandelbrot();
}

// 初期位置・倍率
function reset_para() {
  center_r = center_i = 0.0;
  ratio = 0.01;
  document.forms[0].factor[0].checked = true;  // x 1 に戻す

  mandelbrot();
}

// マンデルブロ集合作成
function mandelbrot() {
  image_disabled = true;
  elem_pal.disabled = true;
  for(var i = 0; i < document.forms[0].factor.length; i++)
    document.forms[0].factor[i].disabled = true;
  document.getElementById("reset").disabled = true;

  elem_msg.firstChild.nodeValue = "計算中...";
  elem_msg.style.visibility = "visible";
  elem_prog.style.width = "0";
  elem_prog_back.style.visibility = "visible";
  elem_can.style.visibility = "visible";
  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))
    elem_prog.style.width = String(y >> 1) + "px";
  if(y == SIZE_Y) {
    if(cancel) {
      end_proc();
      return;
    }
    setTimeout("create_image()", 0);
  }
  else {
    setTimeout("mandelbrot1(" + String(y) + ")", 0);
  }
}

function create_image() {
  image_data = "";
  b64_cnt = 0;

  // 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];

  elem_msg.firstChild.nodeValue = "画像作成中...";
  elem_prog.style.width = "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 != undefined)
      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++) {
          root_prop[i].chara = undefined;
          root_prop[i].prop = undefined;
        }
        n_code = 258;
        code_bits = 9;
        max_n = 512;
      }
      else {
        // 辞書に登録
        if(prop.chara == undefined) {
          prop.chara = cs;
          prop.prop = [{code:n_code, chara:undefined, prop:undefined}];
        }
        else {
          prop.chara += cs;
          prop.prop.push({code:n_code, chara:undefined, prop:undefined});
        }
        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))
    elem_prog.style.width = String(y >> 1) + "px";
  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

  // BASE64 バッファ フラッシュ
  switch(b64_cnt) {
  case 1:
    image_data += b64.charAt(b64_buff >> 18);
    image_data += b64.charAt((b64_buff >> 12) & 0x3f);
    image_data += "==";
    break;
  case 2:
    image_data += b64.charAt(b64_buff >> 18);
    image_data += b64.charAt((b64_buff >> 12) & 0x3f);
    image_data += b64.charAt((b64_buff >> 6) & 0x3f);
    image_data += "=";
    break;
  }

  // イメージ表示
  elem_img.src = image_header + image_data;
  cur_image_data = image_data;

  // 表示範囲
  var str;
  str = (center_r - SIZE_X_H * ratio).toExponential(4).split("e");
  document.getElementById("range-f-r-2").firstChild.nodeValue = "";
  switch(Number(str[1])) {
  case 0:
    break;
  default:
    document.getElementById("range-f-r-2").firstChild.nodeValue = str[1].replace(/\+/, "");
    // fall thru
  case 1:
    str[0] += "×10";
    break;
  }
  document.getElementById("range-f-r-1").firstChild.nodeValue = str[0];

  str = (center_i - SIZE_Y_H * ratio).toExponential(4).split("e");
  if(str[0].charAt(0) != "-")
    str[0] = "+" + str[0];
  document.getElementById("range-f-i-2").firstChild.nodeValue = "";
  switch(Number(str[1])) {
  case 0:
    break;
  default:
    document.getElementById("range-f-i-2").firstChild.nodeValue = str[1].replace(/\+/, "");
    // fall thru
  case 1:
    str[0] += "×10";
    break;
  }
  document.getElementById("range-f-i-1").firstChild.nodeValue = str[0];
  document.getElementById("range-f-i-3").firstChild.nodeValue = "i";

  str = (center_r + SIZE_X_H * ratio).toExponential(4).split("e");
  document.getElementById("range-t-r-2").firstChild.nodeValue = "";
  switch(Number(str[1])) {
  case 0:
    break;
  default:
    document.getElementById("range-t-r-2").firstChild.nodeValue = str[1].replace(/\+/, "");
    // fall thru
  case 1:
    str[0] += "×10";
    break;
  }
  document.getElementById("range-t-r-1").firstChild.nodeValue = str[0];

  str = (center_i + SIZE_Y_H * ratio).toExponential(4).split("e");
  document.getElementById("range-t-i-2").firstChild.nodeValue = "";
  if(str[0].charAt(0) != "-")
    str[0] = "+" + str[0];
  switch(Number(str[1])) {
  case 0:
    break;
  default:
    document.getElementById("range-t-i-2").firstChild.nodeValue = str[1].replace(/\+/, "");
    // fall thru
  case 1:
    str[0] += "×10";
    break;
  }
  document.getElementById("range-t-i-1").firstChild.nodeValue = str[0];
  document.getElementById("range-t-i-3").firstChild.nodeValue = "i";

  // テキストをセットしただけでは何故か表示されない.
  // また何故か,ひとつの項目の visibility を再セットしてやると,すべての項目が表示される.
  document.getElementById("range-f-r-1").style.visibility = "visible";

  end_proc();
}

// 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) {
  switch(b64_cnt) {
  case 0:
    b64_buff = c << 16;
    b64_cnt = 1;
    break;
  case 1:
    b64_buff |= c << 8;
    b64_cnt = 2;
    break;
  case 2:
    b64_buff |= c;
    image_data += b64.charAt(b64_buff >> 18);
    image_data += b64.charAt((b64_buff >> 12) & 0x3f);
    image_data += b64.charAt((b64_buff >> 6) & 0x3f);
    image_data += b64.charAt(b64_buff & 0x3f);
    b64_cnt = 0;
    break;
  }
}

// マンデルブロ集合作成終了
function end_proc() {
  var i;

  // 辞書ルートを初期状態に戻す
  for(i = 0; i < 256; i++) {
    root_prop[i].chara = undefined;
    root_prop[i].prop = undefined;
  }

  elem_msg.style.visibility = "hidden";
  elem_prog_back.style.visibility = "hidden";
  elem_can.style.visibility = "hidden";

  image_disabled = false;
  elem_pal.disabled = false;
  for(i = 0; i < document.forms[0].factor.length; i++)
    document.forms[0].factor[i].disabled = false;
  document.getElementById("reset").disabled = false;
}

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

  var i_pal = elem_pal.selectedIndex;
  if(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(i_pal);

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

  // BASE64 エンコードされた GIF イメージのカラー テーブルのみを書き換える.
  // 元データの 3 バイト境界で書き換え.カラー テーブルのサイズは 3 バイトの倍数.
  // 処理を簡単にするため,header1 は全部書き換えることにする.
  image_data = "";
  b64_cnt = 0;
  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 < 3 - header1.length % 3; i++)
    putc(header2[i]);
  cur_image_data = image_data + cur_image_data.substr(image_data.length);
  elem_img.src = image_header + cur_image_data;
}

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

  for(var i = 0; i < 256; i++) {
    with(cur_palette[i]) {
      document.getElementById("p" + String(i)).style.backgroundColor
        = "rgb(" + String(r) + "," + String(g) + "," + String(b) + ")";
    }
  }
}

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

  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;
  }

  image_header = "data:image/gif;base64,";

  // 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] = {chara:undefined, prop:undefined};

  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 イメージ データ
  cur_image_data = "";  // 現在の GIF イメージ データ
  var b64_buff;  // BASE64 バッファ
  var b64_cnt;   // BASE64 バッファ格納バイト数
  // BASE64 エンコード キャラクタ
  b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

  var cancel;  // 中止フラグ
  image_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};
  }

  elem_img = document.getElementById("img");
  elem_pal = document.getElementById("pal");
  elem_msg = document.getElementById("msg");
  elem_prog_back = document.getElementById("prog-back");
  elem_prog = document.getElementById("prog");
  elem_can = document.getElementById("can");

  with(elem_img.style) {
    width  = String(SIZE_X) + "px";
    height = String(SIZE_Y) + "px";
  }
  elem_prog_back.style.width = String(SIZE_Y_H + 2) + "px";

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

  // 初期表示
  mandelbrot();

//-->
</SCRIPT>

</BODY>

</HTML>