わかるかもしれなイカ、sinとcos その1

回転は面白いけどsin,cosは難しいですよね。そこで皆さんにsin,cosをわかりやすく教えたいと思います。JavaScriptのプログラムを示して、実際に動かしながら説明したいと思います。

わかるかもしれなイカ、sinとcos その2
実行結果:
no canvas
canvas.fillRect( 100, 100, 4, 4 );
説明の前に画面に点を打ってみましょう。

点を打つにはCANVASの fillRect()を使います。
例では、 x 座標100、y座標100の位置に、幅4、高さ4の四角形(ほぼ点)を描いています。

canvasという変数は以下のJavaScriptで自分で作っておきます。
var canvasTag = $( "canvasTagID" ); canvas = canvasTag.getContext( '2d' );
それからHTMLタグには以下が必要です。
<canvas id="canvasTagID" width="512" height="400"> no canvas </canvas>
theta = 200; canvas.fillRect( theta, 200, 4, 4 );
ではちょっと、fillRect()の x 座標の指定を変数 theta にします。
thetaとはシータと読みますが、難しくなるのを防ぐために、最初は「天空の城ラピュタ」のシータだと思ってしまってください。
for( theta = 0; theta < 6.28; theta += 0.1 ) { canvas.fillRect( theta, 200, 4, 4 ); }
for文を使ってシータを変化させてみましょう。

変化の範囲は、ちょっと事情があって、0~6.28とします。
また、増える量は0.1ずつ増えることにします

シータはfillRect()のx座標に書いたので、横方向、右に向かって点がたくさん描かれます。
しかし、極小の0.1ずつ増やしているので、左側に集まったように描かれてしまいます。
kakudai = 100; for( theta = 0; theta < 6.28; theta += 0.1 ) { canvas.fillRect( theta * kakudai, 200, 4, 4 ); }
そこで、kakudaiという変数を用意して、シータの値を拡大しましょう。

すると、fillRect()のx座標の指定に渡される数値が 0 -> 0.1 -> 0.2 ...となっていたものが、100倍されて、 0 -> 10 -> 20 ... となります。

「0~6.28で増量が0.1」というのが変ですが、同じ画面を描くときは、だいたい上記と同じようなプログラムを考えて作るのではないでしょうか。
kakudai = 100; for( theta = 0; theta < 6.28; theta += 0.1 ) { canvas.fillRect( pazuu( theta ) * kakudai, 200, 4, 4 ); } function pazuu( x ) { return x * 2; }
次にちょっと変わったことをします。

シータをpazuu()という関数で囲みます。

関数pazuu()では受け取ったシータを2倍にして返しています。
その結果、
0 -> 10 -> 20 ... となっていたものは。
0 -> 20 -> 40 ... となります。
kakudai = 100; for( theta = 0; theta < 6.28; theta += 0.1 ) { canvas.fillRect( Math.cos( theta ) * kakudai, 200, 4, 4 ); } function pazuu( x ) { return x * 2; }
ではこのシータを囲っているpazuu関数を、今度はMath.cos関数に変更してみましょう。関数名を変えるだけだからカンタンです。
(pazuu関数を用意したのは冗談ではなくて、理解しづらいsin, cos関数の役割というか位置づけというか、そういう不明なところについて、pazuuのような関数と一緒なんですよ、ということを知ってもらうために用意しました)

Math.cos関数にすると、シータはちょっとしゃれた感じの間隔になりました。
pazuu関数ではシータを2倍にしていましたが、Math.cos関数はシータに何をしたんでしょうか。内部で難しい計算でもしているのでしょう。
kakudai = 100; for( theta = 0; theta < 6.28; theta += 0.1 ) { canvas.fillRect( Math.sin( theta ) * kakudai, 200, 4, 4 ); }
Math.cos関数には仲間がいて、Math.sin関数というものもあります。
cosをsinに変えてみると、結果は大きな変化はなく、cosとよく似ています。

kakudai = 100; for( theta = 0; theta < 6.28; theta += 0.1 ) { canvas.fillRect( 200, Math.sin( theta ) * kakudai, 4, 4 ); }
ちなみに、Math.cos関数はx座標の変化のために用意されているような関数で(つまりヨコ用)、Math.sin関数はy座標の変化のために用意されている関数です。(つまりタテ用)

というわけで、Math.sin関数はfillRect()のy座標の記述へ移動させましょう。
すると、同じ現象が縦方向に描かれました。
y座標を変えているから、縦に変化するんです。
kakudai = 100; for( theta = 0; theta < 6.28; theta += 0.1 ) { canvas.fillRect( Math.cos( theta ) * kakudai, Math.sin( theta ) * kakudai, 4, 4 ); }
で、fillRect()のx座標の記述にもMath.cos関数で囲ったシータを置いてあげると、なんと!

綺麗な曲線になりました!
なんで?!
kakudai = 100; for( theta = 0; theta < 6.28; theta += 0.1 ) { canvas.fillRect( Math.cos( theta ) * kakudai, Math.sin( theta ) * kakudai, 4, 4 ); color( "red" ); canvas.fillRect( Math.cos( theta ) * kakudai, 200, 4, 4 ); color( "green" ); canvas.fillRect( 200, Math.sin( theta ) * kakudai, 4, 4 ); color( "black" ); } function color( col ) { canvas.fillStyle = col; }
なんでなのかっ
横と縦の、しゃれた線を色分けしてもう一度描いてみると…。
x座標(赤)はしゃれた間隔で変化していて、それは曲線を描いている各点のx座標と同じになっています。わかるでしょうか。
y座標(緑)についても同じです。曲線の点のy座標と同じになっています。
そのxとyを組み合わせると、曲線になってしまうのですが、わかるでしょうか。
var hanbunH = 256; var hanbunV = 200; kakudai = 100; for( theta = 0; theta < 6.28; theta += 0.1 ) { canvas.fillRect( Math.cos( theta ) * kakudai + hanbunH, Math.sin( theta ) * kakudai + hanbunV, 4, 4 ); }
そして、なんと!
そのx, yに画面サイズの半分を足してみると、正円が現れました。
画面の左上で隠れていたんですね。

さしずめ、隠れていたラピュタ発見といったところかっ
var hanbunH = 256; var hanbunV = 200; kakudai = 100; for( theta = 0; theta < 0.1; theta += 0.1 ) { canvas.fillRect( Math.cos( theta ) * kakudai + hanbunH, Math.sin( theta ) * kakudai + hanbunV, 4, 4 ); }
ちなみに、シータが1個のときは、こう。
var hanbunH = 256; var hanbunV = 200; kakudai = 100; for( theta = 0; theta < 3.14 / 2; theta += 0.1 ) { canvas.fillRect( Math.cos( theta ) * kakudai + hanbunH, Math.sin( theta ) * kakudai + hanbunV, 4, 4 ); }
シータが3.14 / 2までだとこう。正円の4分の1。

var hanbunH = 256; var hanbunV = 200; kakudai = 100; for( theta = 0; theta < 3.14; theta += 0.1 ) { canvas.fillRect( Math.cos( theta ) * kakudai + hanbunH, Math.sin( theta ) * kakudai + hanbunV, 4, 4 ); }
シータが3.14までだとこう。この向きで、だんだん増えています。。

var hanbunH = 256; var hanbunV = 200; kakudai = 100; for( theta = 0; theta < 3.14 * 1.5; theta += 0.1 ) { canvas.fillRect( Math.cos( theta ) * kakudai + hanbunH, Math.sin( theta ) * kakudai + hanbunV, 4, 4 ); }
シータが3.14の1.5倍までだとこう。

var hanbunH = 256; var hanbunV = 200; kakudai = 100; for( theta = 0; theta < 6.28; theta += 0.1 ) { canvas.fillRect( Math.cos( theta ) * kakudai + hanbunH, Math.sin( theta ) * kakudai + hanbunV, 4, 4 ); }
そしてシータが6.28まできて正円になります。

var hanbunH = 256; var hanbunV = 200; kakudai = 100; for( theta = 0; theta > -3.14 / 2; theta -= 0.1 ) { canvas.fillRect( Math.cos( theta ) * kakudai + hanbunH, Math.sin( theta ) * kakudai + hanbunV, 4, 4 ); }
逆に回したいときは、for文の中の < を > にして、続く 3.14 / 2 に - を付けて、-3.14 / 2 にして、theta += 0.1 も - に変更して theta -= 0.1 にします。
var hanbunH = 256; var hanbunV = 200; kakudai = 100; for( theta = 0; theta < 3.14 / 2; theta += 0.1 ) { canvas.fillRect( Math.cos( -theta ) * kakudai + hanbunH, Math.sin( -theta ) * kakudai + hanbunV, 4, 4 ); }
または、cos, sin の()の中のthetaに-を付けるだけでも良いです。(やっていることは同じことです)
こっちのほうが簡単だ☆
var hanbunH = 256; var hanbunV = 200; kakudai = 100; for( theta = 3.14 / 4; theta < 3.14; theta += 0.1 ) { canvas.fillRect( Math.cos( theta ) * kakudai + hanbunH, Math.sin( theta ) * kakudai + hanbunV, 4, 4 ); }
自由な場所から回し始めるには?
for文の theta = 0; の部分を変更します。

例では、3.14 / 4 から回し始めて、時計回りに、3.14 まで回しています。

var hanbunH = 256; var hanbunV = 200; kakudai = 100; for( theta = 3.14 * 1.5; theta < 6.28 + 3.14 / 4; theta += 0.1 ) { canvas.fillRect( Math.cos( theta ) * kakudai + hanbunH, Math.sin( theta ) * kakudai + hanbunV, 4, 4 ); }
指定する上限は 6.28 でしたが、実は6.28を超えて指定できます。

例では3.14 * 1.5から回し始めて、時計回りに6.28 + 3.14 / 4 まで回しています。

var hanbunH = 256; var hanbunV = 200; kakudai = 100; for( theta = 0; theta < 6.28; theta += 0.1 ) { canvas.fillRect( Math.cos( theta ) + hanbunH, Math.sin( theta ) + hanbunV, 4, 4 ); }
ちなみに拡大を取っちゃうと、こうなります。
点にしか見えませんが、0~1ドットのミクロな世界で、円を描いています。
var hanbunH = 256; var hanbunV = 200; kakudai = 100; for( theta = 0; theta < 6.28; theta += 0.1 ) { canvas.fillRect( Math.cos( theta ) * kakudai + hanbunH, Math.sin( theta ) * kakudai + hanbunV, 4, 4 ); }
こうやって拡大はつけましょう。
kakudaiの値をもっと大きくするとどうなるかは楽しいので、自分で試してください。
var hanbunH = 256; var hanbunV = 200; kakudai = 100; for( theta = 0; theta < 6.28; theta += 0.1 ) { canvas.fillRect( Math.cos( theta ) * kakudai + hanbunH, Math.sin( theta ) * kakudai + hanbunV, 4, 4 ); }
ここでの説明(左)と、高校数学の教科書との対応(右)

kakudai = 半径
theta = 円をどこからどこまで描くかという「角度」
6.28 = 円周率 3.14 * 2

kakudaiは説明のための言葉でした。これからはhankeiなどとしてください。でもhankeiという言葉を使っているときでも「拡大」の意味合いは残ります
6.28を超えて6.38になった場合は、6.38から6.28が自動的に引かれて(正確には6.38を6.28で割った余り)、0.1となり、6.38は0.1と同じ結果になります。
var hanbunH = 256; var hanbunV = 200; kakudai = 100; for( theta = 0; theta < 6.28; theta += 0.1 ) { canvas.fillRect( Math.cos( theta ) * kakudai + hanbunH, Math.sin( theta ) * kakudai + hanbunV, 4, 4 ); }
それから、大切なことが。
たとえばシューティングゲームなどで、敵キャラなどが、x = 100、y = 100の位置にいたとき、その位置から突然円を描いて動かすとしたら、どうすればいいでしょうか。上のプログラムを見る限り、x = 100、y = 100の場所から、という指定は簡単にできないように見えます。
このままだと、ゲーム用のために活用できません。
それについてはこちらの方法を使ってください。
わかるかもしれない、sinとcos その2

ページの上端へ (もくじ開く)