spiro2.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>スパイログラフ(Canvas 版)</TITLE>
</HEAD>

<BODY BGCOLOR="#CCFFFF">
<CENTER>
<B>スパイログラフ(Canvas 版)</B>
<BR><BR>

<FORM>
<TABLE><TR>
<TD VALIGN=TOP><CANVAS ID="img" WIDTH=480 HEIGHT=480></CANVAS></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;
    }
  }

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

  if(!elem_undo.disabled)  // 直前に描いた図形あり
    ctx_comp.drawImage(elem_cur, 0, 0);  // 重ね描き

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

  if(!scale)
    scale = r1 - r2 + r3;

  ctx_cur.clearRect(0, 0, 480, 480);
  ctx_cur.fillStyle = color[document.getElementById("color").selectedIndex];  // 色

  if(quiet) {
    elem_prog.style.visibility = "visible";
  }
  else {
    ctx_work.drawImage(elem_comp, 0, 0);
    // 外円
    var r1p = r1 * 239.99 / scale;
    if(r1p > 0.5) {
      ctx_work.beginPath();
      ctx_work.moveTo(240 + r1p - 0.5, 240);  // Opera で必要
      ctx_work.arc(240, 240, r1p - 0.5, 0, Math.PI * 2, false);
      ctx_work.stroke();
    }
    r2p = r2 * 239.99 / scale;

    elem_can.disabled = false;
  }

  var rd = r1 - r2;
  rr = rd / r2;
  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;

  ctx_img.drawImage(elem_comp, 0, 0);
}

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

  ctx_img.fillRect(0, 0, 480, 480);
  ctx_comp.fillRect(0, 0, 480, 480);
}

// 描画
function draw(div) {
  if(cancel) {
    elem_can.disabled = true;
    elem_start.disabled = false;
    elem_clear.disabled = false;

    ctx_img.drawImage(elem_comp, 0, 0);
    return;
  }

  for(var cnt = 10; ; ) {
    var x, y;
    var x1, y1;
    if(div == div_end) {
      x = Math.floor(rdp + r3p) + 240;
      y = 240;
    }
    else {
      var th1 = PI2DIV * div;
      var th2 = rr * (th1);
      x = Math.floor((x1 =   rdp * Math.cos(th1)) + r3p * Math.cos(th2)) + 240;
      y = Math.floor((y1 = - 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--) {
          ctx_cur.fillRect(prev_x, prev_y, 1, 1);

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

          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;

      ctx_img.drawImage(elem_comp, 0, 0);
      ctx_img.drawImage(elem_cur, 0, 0);
      return;
    }
    if(!cnt) {
      ctx_img.drawImage(elem_work, 0, 0);
      // 内円
      if(r2p > 0.5) {
        ctx_img.beginPath();
        ctx_img.moveTo(x1 + 240 + r2p - 0.5, y1 + 240);  // Opera で必要
        ctx_img.arc(x1 + 240, y1 + 240, r2p - 0.5, 0, Math.PI * 2, false);
        ctx_img.stroke();
      }
      ctx_img.drawImage(elem_cur, 0, 0);

      setTimeout(draw, 0, div);
      return;
    }
  }
}

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

  // 色
  color = [
    "#000000",  // 黒
    "#ff0000",  // 赤
    "#00ff00",  // ライム
    "#0000ff",  // 青
    "#ffff00",  // 黄
    "#ff00ff",  // 赤紫
    "#00ffff",  // 水色
    "#800000",  // 栗色
    "#008000",  // 緑
    "#000080",  // ネイビー
    "#808000",  // オリーブ
    "#800080",  // 紫
    "#008080",  // 青緑
    "#ff8000",  // 橙
    "#808080"   // 灰
  ];

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

  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");

  ctx_img = elem_img.getContext("2d");
  ctx_img.fillStyle = "white";
  ctx_img.strokeStyle = "#a0a0a0";

  // 作業用 Canvas
  // 重ね描き用
  elem_comp = document.createElement("CANVAS");
  elem_comp.width = 480;
  elem_comp.height = 480;
  ctx_comp = elem_comp.getContext("2d");
  ctx_comp.fillStyle = "white";
  // 現在の図形
  elem_cur = document.createElement("CANVAS");
  elem_cur.width = 480;
  elem_cur.height = 480;
  ctx_cur = elem_cur.getContext("2d");
  // 経過表示用
  elem_work = document.createElement("CANVAS");
  elem_work.width = 480;
  elem_work.height = 480;
  ctx_work = elem_work.getContext("2d");
  ctx_work.strokeStyle = "#a0a0a0";

  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>