このページでは、Processingで三角関数のsin()とcos()を使う方法を学びます。三角関数という言葉は難しく聞こえますが、プログラミングでは主に「円の上の点を動かす」「波のような動きを作る」ために使います。
まずは公式を覚えるよりも、cos()は横方向、sin()は縦方向の動きを作る、と考えると理解しやすくなります。
1. 三角関数で何ができるのか
三角関数を使うと、円運動や波の動きを簡単に作ることができます。たとえば、点を円の上で回したり、上下にゆっくり揺れる動きを作ったりできます。
Processingでは、角度を使って座標を計算します。基本の考え方は次の2つです。
| 式 | 意味 |
|---|---|
x = R * cos(角度) | 円の上の点の横方向の位置を求める |
y = R * sin(角度) | 円の上の点の縦方向の位置を求める |
Rは半径です。大きくすると、円や波の動きも大きくなります。
2. 円の上に点を並べる
まずは、円の上に点を並べてみます。cos()でx座標、sin()でy座標を求めます。
void setup() {
size(400, 400);
background(255);
noStroke();
fill(0);
float R = 100;
for (int angle = 0; angle < 360; angle += 10) {
// Processingのsin(), cos()はラジアンで計算するので、radians()で変換する
float x = R * cos(radians(angle));
float y = R * sin(radians(angle));
// 計算した座標を画面中央にずらして描く
ellipse(x + width/2, y + height/2, 5, 5);
}
}xとyは、円の中心を0, 0とした座標です。そのまま描くと画面の左上付近に描かれるので、width/2とheight/2を足して、画面中央を中心にしています。
3. 点を円運動させる
次に、角度を少しずつ増やして、点を円の上で動かします。ここではangleという変数を角度として使います。
float angle = 0;
float R = 100;
void setup() {
size(400, 400);
noStroke();
}
void draw() {
background(255);
// angleは度数法の角度なので、radians()でラジアンに変換する
float x = R * cos(radians(angle));
float y = R * sin(radians(angle));
fill(0);
ellipse(x + width/2, y + height/2, 20, 20);
// 角度を少しずつ進める
angle += 2;
}angleが少しずつ増えることで、点が円の上を移動します。angle += 2の数値を大きくすると、点の動きが速くなります。
注意:Processingの画面では、y座標は下に行くほど大きくなります。数学のグラフとは上下の向きが違うので、必要に応じてy = -R * sin(theta)のようにマイナスをつけることがあります。
4. ラジアンとは
Processingのsin()やcos()は、角度を「度」ではなく「ラジアン」で扱います。最初は、次の関係だけ覚えておけば十分です。
| 度数法 | Processingでよく使う書き方 |
|---|---|
| 0° | 0 |
| 90° | HALF_PI |
| 180° | PI |
| 360° | TWO_PI |
TWO_PIは、円1周分の角度です。円運動では、thetaがTWO_PIまで進むと1周したことになります。
5. 点を残して円を描く
背景を毎回消さないようにすると、動いた点の跡が残ります。これによって、円の軌跡を描くことができます。
float angle = 0;
float R = 100;
void setup() {
size(400, 400);
background(255);
noStroke();
fill(0);
}
void draw() {
// background()を書かないので、点の跡が残る
float x = R * cos(radians(angle));
float y = R * sin(radians(angle));
ellipse(x + width/2, y + height/2, 5, 5);
angle += 2;
}6. サイン波を描く
三角関数は、円運動だけでなく波の形を作ることにも使えます。サイン波は、上下になめらかに変化する波です。
サイン波では、横方向に進みながら、y座標をsin()で上下させます。
float A = 100;
float w = 0.03;
void setup() {
size(400, 400);
noStroke();
fill(0);
}
void draw() {
background(255);
for (float x = 0; x < width; x += 5) {
// Aは波の大きさ、wは波の細かさを決める
float y = A * sin(w * x);
ellipse(x, y + height/2, 5, 5);
}
}Aは振幅です。値を大きくすると、波の上下の動きが大きくなります。wは波の細かさです。値を大きくすると、波が細かくなります。
7. 波を横に動かす
サイン波にphaseという変数を足すと、波が横に動いているように見えます。
float A = 80;
float w = 0.04;
float phase = 0;
void setup() {
size(400, 400);
noStroke();
fill(0);
}
void draw() {
background(255);
for (float x = 0; x < width; x += 5) {
// phaseを足すことで、波の開始位置がずれる
float y = A * sin(w * x + phase);
ellipse(x, y + height/2, 5, 5);
}
// phaseを少しずつ増やすと、波が流れて見える
phase += 0.05;
}8. サイン波で円の大きさを変える
sin()の値は、-1から1の間でなめらかに変化します。この性質を使うと、円の大きさをゆっくり拡大・縮小できます。
// 角度を入れる変数
// draw()のたびに少しずつ増やして、sin()の値を変化させる
float angle = 0;
void setup() {
size(400, 400);
noStroke();
}
void draw() {
background(255);
// angleは「度」で考えるための数値
// ただし、sin()はラジアンで計算するので radians() で変換する
float rad = radians(angle);
// sin(rad) は -1 〜 1 の範囲でなめらかに変化する
// 例:
// sin(rad) が -1 のとき → 円のサイズを 30 にしたい
// sin(rad) が 0 のとき → 円のサイズを 105 くらいにしたい
// sin(rad) が 1 のとき → 円のサイズを 180 にしたい
//
// map() は、ある範囲の数値を別の範囲に変換する命令
//
// map(元の値, 元の最小値, 元の最大値, 変換後の最小値, 変換後の最大値)
//
// ここでは、
// sin(rad) の -1〜1 の値を、
// 円のサイズ 30〜180 に変換している
float s = map(sin(rad), -1, 1, 30, 180);
fill(0);
// 画面中央に円を描く
// sを幅と高さに使っているので、円が拡大・縮小する
ellipse(width/2, height/2, s, s);
// angleを少しずつ増やす
// 数値を大きくすると、拡大・縮小のスピードが速くなる
angle += 3;
}map()を使うと、-1から1の値を、円の大きさとして使いやすい範囲に変換できます。
9. 直線的な動きとサイン波の動きを比べる
次のサンプルでは、左の円はサイン波で拡大縮小し、右の円は一定のスピードで拡大縮小します。サイン波の動きは、最大・最小に近づくと少しゆっくり見えます。
float angle = 0;
float size2 = 30;
float speed = 2;
void setup() {
size(400, 400);
noStroke();
}
void draw() {
background(255);
// 左:sin()を使ったなめらかな拡大縮小
float size1 = map(sin(radians(angle)), -1, 1, 30, 180);
fill(0);
ellipse(120, height/2, size1, size1);
angle += 3;
// 右:足し算だけの直線的な拡大縮小
size2 += speed;
if (size2 > 180 || size2 < 30) {
speed = -speed;
}
fill(120);
ellipse(280, height/2, size2, size2);
}10. 八の字の動き
xとyにそれぞれ違うサイン波を使うと、点を八の字のように動かすことができます。
float angle = 0;
float A = 100;
void setup() {
size(400, 400);
background(255);
noStroke();
fill(0);
}
void draw() {
// xは普通のサイン波、yは2倍の速さのサイン波にする
float x = A * sin(radians(angle));
float y = A * sin(radians(angle * 2));
ellipse(x + width/2, y + height/2, 4, 4);
angle += 2;
}このように、xとyに入れる式を少し変えるだけで、円以外の複雑な動きも作れます。
11. 星のような曲線を描く
pow()を使ってcos()やsin()を3乗すると、星のような曲線を描くことができます。
float angle = 0;
float R = 120;
void setup() {
size(400, 400);
background(255);
noStroke();
fill(0);
}
void draw() {
// pow(a, 3)は、aを3乗する命令
float x = R * pow(cos(radians(angle)), 3);
float y = R * pow(sin(radians(angle)), 3);
ellipse(x + width/2, y + height/2, 4, 4);
angle += 2;
}12. らせん
円運動では半径Rを一定にしていました。らせんでは、角度が進むたびに半径を少しずつ大きくします。つまり、円運動をしながら外側へ広がっていく動きです。
float angle = 0;
void setup() {
size(400, 400);
background(255);
noStroke();
fill(0);
}
void draw() {
// angleが大きくなるほど、半径も大きくする
float R = angle * 0.25;
float x = R * cos(radians(angle));
float y = R * sin(radians(angle));
ellipse(x + width/2, y + height/2, 4, 4);
angle += 2;
}13. リサジュー曲線
リサジュー曲線は、x方向とy方向に違う速さのサイン波を使って作る曲線です。音や振動のような周期的な動きを視覚化するときにも使われます。
float angle = 0;
float A = 120;
void setup() {
size(400, 400);
background(255);
noStroke();
fill(0);
}
void draw() {
// xとyで違う周期のsin()を使う
float x = A * sin(radians(angle * 3));
float y = A * sin(radians(angle * 2));
ellipse(x + width/2, y + height/2, 4, 4);
angle += 1;
}14. 外サイクロイド
外サイクロイドは、大きな円の外側を小さな円が転がるとき、その小さな円上の点が描く曲線です。ここでは式をそのまま使い、まず形を観察します。
float angle = 0;
float R = 80;
float r = 30;
void setup() {
size(400, 400);
background(255);
noStroke();
fill(0);
}
void draw() {
float t = radians(angle);
// 大きな円の外側を小さな円が転がるときの式
float x = (R + r) * cos(t) - r * cos((R + r) / r * t);
float y = (R + r) * sin(t) - r * sin((R + r) / r * t);
ellipse(x + width/2, y + height/2, 4, 4);
angle += 2;
}15. 内サイクロイド
内サイクロイドは、大きな円の内側を小さな円が転がるとき、その小さな円上の点が描く曲線です。外サイクロイドと式が少し違います。
float angle = 0;
float R = 120;
float r = 40;
void setup() {
size(400, 400);
background(255);
noStroke();
fill(0);
}
void draw() {
float t = radians(angle);
// 大きな円の内側を小さな円が転がるときの式
float x = (R - r) * cos(t) + r * cos((R - r) / r * t);
float y = (R - r) * sin(t) - r * sin((R - r) / r * t);
ellipse(x + width/2, y + height/2, 4, 4);
angle += 2;
}16. 外トロコイド
外トロコイドは、外サイクロイドの発展形です。点の位置を小さな円の円周上ではなく、少し内側や外側にずらすことで、形が変わります。
float angle = 0;
float R = 80;
float r = 30;
float d = 60;
void setup() {
size(400, 400);
background(255);
noStroke();
fill(0);
}
void draw() {
float t = radians(angle);
// dを変えると、点が円周上からずれて形が変わる
float x = (R + r) * cos(t) - d * cos((R + r) / r * t);
float y = (R + r) * sin(t) - d * sin((R + r) / r * t);
ellipse(x + width/2, y + height/2, 4, 4);
angle += 2;
}17. 内トロコイド
内トロコイドは、内サイクロイドの発展形です。内側を転がる円に付いた点の位置を、円周上からずらして描く曲線です。
float angle = 0;
float R = 120;
float r = 40;
float d = 70;
void setup() {
size(400, 400);
background(255);
noStroke();
fill(0);
}
void draw() {
float t = radians(angle);
// dを変えると、内サイクロイドとは違う形になる
float x = (R - r) * cos(t) + d * cos((R - r) / r * t);
float y = (R - r) * sin(t) - d * sin((R - r) / r * t);
ellipse(x + width/2, y + height/2, 4, 4);
angle += 2;
}まとめ
| 命令・変数 | 意味 |
|---|---|
sin() | 主に縦方向のゆれや波を作る |
cos() | 主に横方向の円運動を作る |
angle | 角度を表す変数として使う |
radians() | 度数法の角度をラジアンに変換する |
TWO_PI | 円1周分の角度 |
map() | 値の範囲を、別の範囲に変換する |
pow() | 数値を何乗かする |
| らせん | 円運動の半径を少しずつ大きくして描く曲線 |
| リサジュー曲線 | x方向とy方向に違う周期の三角関数を使って描く曲線 |
| サイクロイド | 円が転がるとき、その円上の点が描く曲線 |
| トロコイド | 円周上からずれた点が描く、サイクロイドの発展形 |
今日の重要ポイント:三角関数は、円運動や波のような「なめらかな動き」を作るための道具です。最初は、cos()でx座標、sin()でy座標を作る、と覚えると使いやすくなります。
