spiro_n7.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>スパイログラフ (Netscape 7)</B>
<BR><BR>

<FORM>
<TABLE><TR>
<TD VALIGN=TOP>
<IMG ID="img" SRC="data:image/bmp;base64,Qk1CAAAAAAAAAD4AAAAoAAAAAQAAAAEAAAABAAEAAAAAAAQAAADEDgAAxA4AAAAAAAAAAAAAAAAAAP///wCAAAAAAAAA"
 WIDTH=480 HEIGHT=480>
</TD>
<TD WIDTH=10></TD>
<TD VALIGN=TOP NOWRAP>
<BR>
<IMG SRC="images/spiro.gif" WIDTH=149 HEIGHT=165><BR><BR>
r1: <INPUT TYPE=TEXT ID="r1" SIZE=4> (整数)<BR>
r2: <INPUT TYPE=TEXT ID="r2" SIZE=4> (整数)<BR>
r3: <INPUT TYPE=TEXT ID="r3" SIZE=8><BR>
スケール: <INPUT TYPE=TEXT ID="scale" SIZE=8><BR><BR>
色: <SELECT ID="color">
<OPTION SELECTED>黒
<OPTION>赤
<OPTION>ライム
<OPTION>青
<OPTION>黄
<OPTION>赤紫
<OPTION>水色
<OPTION>栗色
<OPTION>緑
<OPTION>ネイビー
<OPTION>オリーブ
<OPTION>紫
<OPTION>青緑
<OPTION>橙
<OPTION>灰
</SELECT><BR><BR>
<INPUT TYPE=CHECKBOX ID="quiet">途中経過を表示しない<BR><BR>
<INPUT TYPE=BUTTON ID="start" VALUE="描画開始" onClick="start_drawing()">
<INPUT TYPE=BUTTON ID="can" VALUE="描画中止" DISABLED onClick="cancel = true">
<INPUT TYPE=BUTTON ID="undo" VALUE="やり直し" DISABLED onClick="restore_image()"><BR><BR>
<INPUT TYPE=BUTTON ID="clear" VALUE="クリア" onClick="clear_image()"><BR><BR>
<DIV ID="prog" ALIGN=CENTER STYLE="visibility:hidden"><FONT SIZE="+1">◆ 処理中 ◆</FONT></DIV>
</TD>
</TR></TABLE>
</FORM>

</CENTER>

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

// 整数 キー入力チェック
function check_int(e) {
  return (e.which < 0x20 || e.which >= 0x30 && e.which <= 0x39 || e.ctrlKey || e.metaKey);
}

// 小数 キー入力チェック
function check_float(e) {
  return (e.which < 0x20 || e.which >= 0x30 && e.which <= 0x39 || e.which == 0x2e || e.ctrlKey || e.metaKey);
}

// 描画開始
function start_drawing() {
  // 入力チェック
  var r1 = elem_r1.value;
  if(r1.search(/^\s*\d+\s*$/) == -1)
    r1 = 0;
  else
    r1 = parseInt(r1, 10);
  if((isNaN(r1)) ? true : (r1 == 0 || r1 >= 10000)) {
    alert("r1 の入力に誤りがあります。");
    return;
  }
  var r2 = elem_r2.value;
  if(r2.search(/^\s*\d+\s*$/) == -1)
    r2 = 0;
  else
    r2 = parseInt(r2, 10);
  if((isNaN(r2)) ? true : (r2 == 0 || r2 >= 10000)) {
    alert("r2 の入力に誤りがあります。");
    return;
  }
  if(r2 >= r1) {
    alert("r2 には r1 より小さい値を入力してください。");
    return;
  }
  var r3 = elem_r3.value;
  if(r3.search(/^\s*\d*\.?\d*\s*$/) == -1)
    r3 = 0;
  else
    r3 = parseFloat(r3);
  if((isNaN(r3)) ? true : (r3 == 0 || r3 >= 10000)) {
    alert("r3 の入力に誤りがあります。");
    return;
  }
  var scale = elem_scale.value;
  if(scale.search(/\S/) == -1) {  // 未入力
    scale = 0;
  }
  else {
    if(scale.search(/^\s*\d*\.?\d*\s*$/) == -1)
      scale = 0;
    else
      scale = parseFloat(scale);
    if((isNaN(scale)) ? true : (scale == 0 || scale >= 10000)) {
      alert("スケールの入力に誤りがあります。");
      return;
    }
    if(scale < r1 - r2 + r3) {
      alert("スケールには r1 - r2 + r3 以上の値を入力してください。");
      return;
    }
  }

  color = document.getElementById("color").selectedIndex;  // 色
  quiet = document.getElementById("quiet").checked  // 途中経過を表示しない

  cancel = false;
  save_data = data;
  elem_start.disabled = true;
  elem_undo.disabled = true;
  elem_clear.disabled = true;

  if(quiet)
    elem_prog.style.visibility = "visible";
  else
    elem_can.disabled = false;

  var rd = r1 - r2;
  rr = rd / r2;
  if(!scale)
    scale = r1 - r2 + r3;
  rdp = rd * 239.99 / scale;
  r3p = r3 * 239.99 / scale;
  // GCD を求める
  var a, b;
  for(a = r1, b = r2; ; ) {
    var w;
    if(a < b) {
      w = a;
      a = b;
      b = w;
    }
    if(!b)
      break;
    w = a % b;
    a = b;
    b = w;
  }
  div_end = NDIV * (r2 / a);
  prev_x = Math.floor(rdp + r3p) + 240;
  prev_y = 240;

  setTimeout(draw, 0, 0);
}

// 画像復旧
function restore_image() {
  elem_undo.disabled = true;
  data = save_data;
  elem_img.src = data;
}

// クリア
function clear_image() {
  elem_undo.disabled = true;

  var d3 = b64.charAt(0x3f);
  d3 += d3;
  data = header;
  for(var i = 76800; i; i--)
    data += d3;
  elem_img.src = data;
}

// 描画
function draw(div) {
  if(cancel) {
    elem_can.disabled = true;
    elem_start.disabled = false;
    elem_clear.disabled = false;
    data = save_data;
    elem_img.src = data;
    return;
  }

  for(var cnt = 10; ; ) {
    var x, y;
    if(div == div_end) {
      x = Math.floor(rdp + r3p) + 240;
      y = 240;
    }
    else {
      var th1 = PI2DIV * div;
      var th2 = rr * (th1);
      x = Math.floor(rdp * Math.cos(th1) + r3p * Math.cos(th2)) + 240;
      y = Math.floor(rdp * Math.sin(th1) - r3p * Math.sin(th2)) + 240;
    }
    if(x != prev_x || y != prev_y) {
      // 直線補間
      var dx = Math.abs(x - prev_x);
      var dy = Math.abs(y - prev_y);
      var rem = 0;

      var xi = (prev_x < x) ? 1 : -1;
      var yi = (prev_y < y) ? 1 : -1;

      if(dx >= dy) {
        for(var d = dx; d; d--) {
          set_pixel(prev_x, prev_y);

          if((rem += dy) >= dx) {
            prev_y += yi;
            rem -= dx;
          }
          prev_x += xi;
        }
      }
      else {
        for(var d = dy; d; d--) {
          set_pixel(prev_x, prev_y);

          if((rem += dx) >= dy) {
            prev_x += xi;
            rem -= dy;
          }
          prev_y += yi;
        }
      }

      if(!quiet)
        cnt--;
    }
    if(div++ == div_end) {  // 終了
      elem_can.disabled = true;
      elem_prog.style.visibility = "hidden";
      elem_start.disabled = false;
      elem_undo.disabled = false;
      elem_clear.disabled = false;
      elem_img.src = data;
      return;
    }
    if(!cnt) {
      elem_img.src = data;
      setTimeout(draw, 0, div);
      return;
    }
  }
}

// 1 ピクセル描画
function set_pixel(x, y) {
  var i = y * 320 + (Math.floor(x / 3) << 1) + header_len;
  switch(x % 3) {
  case 0:
    data = data.substr(0, i)
             + b64.charAt((color << 2) | b64.indexOf(data.charAt(i)) & 0x3)
             + data.substr(i + 1);
    break;
  case 1:
    data = data.substr(0, i)
             + b64.charAt(b64.indexOf(data.charAt(i)) & 0x3c | (color >> 2))
             + b64.charAt(((color & 0x3) << 4) | b64.indexOf(data.charAt(i + 1)) & 0xf)
             + data.substr(i + 2);
    break;
  default:
    data = data.substr(0, i + 1)
             + b64.charAt(b64.indexOf(data.charAt(i + 1)) & 0x30 | color)
             + data.substr(i + 2);
  }
}

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

  // BMP ヘッダ
  bmp_header = [
    // BITMAPFILEHEADER
    0x42, 0x4d,              // BM
    0x36, 0x8c, 0x0a, 0x00,  // Size  691254
    0x00, 0x00, 0x00, 0x00,
    0x78, 0x00, 0x00, 0x00,  // OffBits  120
    // BITMAPINFOHEADER
    0x28, 0x00, 0x00, 0x00,  // Size  40
    0xe0, 0x01, 0x00, 0x00,  // Width  480
    0xe0, 0x01, 0x00, 0x00,  // Height  480
    0x01, 0x00,              // Planes  1
    0x04, 0x00,              // BitCount  4
    0x00, 0x00, 0x00, 0x00,  // Compression
    0x00, 0xc2, 0x01, 0x00,  // SizeImage  115200
    0xc4, 0x0e, 0x00, 0x00,  // XPelsPerMeter  3780
    0xc4, 0x0e, 0x00, 0x00,  // YPelsPerMeter  3780
    0x00, 0x00, 0x00, 0x00,  // ClrUsed
    0x00, 0x00, 0x00, 0x00,  // ClrImportant
    // カラー テーブル(RGBQUAD)
    0x00, 0x00, 0x00, 0x00,  // 黒
    0x00, 0x00, 0xff, 0x00,  // 赤
    0x00, 0xff, 0x00, 0x00,  // ライム
    0xff, 0x00, 0x00, 0x00,  // 青
    0x00, 0xff, 0xff, 0x00,  // 黄
    0xff, 0x00, 0xff, 0x00,  // 赤紫
    0xff, 0xff, 0x00, 0x00,  // 水色
    0x00, 0x00, 0x80, 0x00,  // 栗色
    0x00, 0x80, 0x00, 0x00,  // 緑
    0x80, 0x00, 0x00, 0x00,  // ネイビー
    0x00, 0x80, 0x80, 0x00,  // オリーブ
    0x80, 0x00, 0x80, 0x00,  // 紫
    0x80, 0x80, 0x00, 0x00,  // 青緑
    0x00, 0x80, 0xff, 0x00,  // 橙
    0x80, 0x80, 0x80, 0x00,  // 灰
    0xff, 0xff, 0xff, 0x00,  // 白
    // BASE64 エンコードするのに都合がよくなるよう,ピクセル データのオフセットが
    // 3 の倍数になるようにパディングを入れておく.
    0x00, 0x00
  ];

  // BASE64 エンコード キャラクタ
  b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

  NDIV = 500;  // 2π の分割数
  PI2DIV = Math.PI * 2 / NDIV;

  header = "data:image/bmp;base64,";
  for(i = 0; i < bmp_header.length; i += 3) {
    b3 = (bmp_header[i] << 16) | (bmp_header[i + 1] << 8) | bmp_header[i + 2];
    header += b64.charAt(b3 >> 18);
    header += b64.charAt((b3 >> 12) & 0x3f);
    header += b64.charAt((b3 >> 6) & 0x3f);
    header += b64.charAt(b3 & 0x3f);
  }
  header_len = header.length;

  elem_img = document.getElementById("img");
  elem_r1 = document.getElementById("r1");
  elem_r2 = document.getElementById("r2");
  elem_r3 = document.getElementById("r3");
  elem_scale = document.getElementById("scale");
  elem_start = document.getElementById("start");
  elem_can = document.getElementById("can");
  elem_undo = document.getElementById("undo");
  elem_clear = document.getElementById("clear");
  elem_prog = document.getElementById("prog");

  elem_r1.onkeypress = check_int;
  elem_r2.onkeypress = check_int;
  elem_r3.onkeypress = check_float;
  elem_scale.onkeypress = check_float;

  clear_image();

  document.forms[0].reset();  // ページを再ロードしたときのため

//-->
</SCRIPT>

</BODY>

</HTML>