<!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
0x78, 0xc2, 0x01, 0x00, // Size 115320
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>
|