三角関数

このページでは、Processingで三角関数のsin()cos()を使う方法を学びます。三角関数という言葉は難しく聞こえますが、プログラミングでは主に「円の上の点を動かす」「波のような動きを作る」ために使います。

まずは公式を覚えるよりも、cos()は横方向、sin()は縦方向の動きを作る、と考えると理解しやすくなります。

1. 三角関数で何ができるのか

三角関数を使うと、円運動や波の動きを簡単に作ることができます。たとえば、点を円の上で回したり、上下にゆっくり揺れる動きを作ったりできます。

cos → xsin → y円の上の点波の形
図1:三角関数は、円運動や波の動きを作るために使える

Processingでは、角度を使って座標を計算します。基本の考え方は次の2つです。

意味
x = R * cos(角度)円の上の点の横方向の位置を求める
y = R * sin(角度)円の上の点の縦方向の位置を求める

Rは半径です。大きくすると、円や波の動きも大きくなります。

2. 円の上に点を並べる

まずは、円の上に点を並べてみます。cos()でx座標、sin()でy座標を求めます。

リスト1
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);
  }
}
実行結果:円の上に点を並べる

xyは、円の中心を0, 0とした座標です。そのまま描くと画面の左上付近に描かれるので、width/2height/2を足して、画面中央を中心にしています。

3. 点を円運動させる

次に、角度を少しずつ増やして、点を円の上で動かします。ここではangleという変数を角度として使います。

リスト2
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()は、角度を「度」ではなく「ラジアン」で扱います。最初は、次の関係だけ覚えておけば十分です。

90°180°270°360°0HALF_PIPIPI + HALF_PITWO_PI360° = TWO_PI ラジアン
図2:度数法とラジアンの対応
度数法Processingでよく使う書き方
0
90°HALF_PI
180°PI
360°TWO_PI

TWO_PIは、円1周分の角度です。円運動では、thetaTWO_PIまで進むと1周したことになります。

5. 点を残して円を描く

背景を毎回消さないようにすると、動いた点の跡が残ります。これによって、円の軌跡を描くことができます。

リスト3
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. サイン波を描く

三角関数は、円運動だけでなく波の形を作ることにも使えます。サイン波は、上下になめらかに変化する波です。

振幅周期
図3:サイン波は、振幅と周期で形が変わる

サイン波では、横方向に進みながら、y座標をsin()で上下させます。

リスト4
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という変数を足すと、波が横に動いているように見えます。

リスト5
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の間でなめらかに変化します。この性質を使うと、円の大きさをゆっくり拡大・縮小できます。

リスト6
// 角度を入れる変数
// 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. 直線的な動きとサイン波の動きを比べる

次のサンプルでは、左の円はサイン波で拡大縮小し、右の円は一定のスピードで拡大縮小します。サイン波の動きは、最大・最小に近づくと少しゆっくり見えます。

リスト7
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);
}
実行結果:左はsin()、右は直線的な拡大縮小

10. 八の字の動き

xyにそれぞれ違うサイン波を使うと、点を八の字のように動かすことができます。

リスト8
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;
}
実行結果:点の軌跡が八の字になる

このように、xyに入れる式を少し変えるだけで、円以外の複雑な動きも作れます。

11. 星のような曲線を描く

pow()を使ってcos()sin()を3乗すると、星のような曲線を描くことができます。

リスト9
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を一定にしていました。らせんでは、角度が進むたびに半径を少しずつ大きくします。つまり、円運動をしながら外側へ広がっていく動きです。

リスト10
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方向に違う速さのサイン波を使って作る曲線です。音や振動のような周期的な動きを視覚化するときにも使われます。

リスト11
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;
}
実行結果:x方向とy方向の周期の違いで曲線ができる

14. 外サイクロイド

外サイクロイドは、大きな円の外側を小さな円が転がるとき、その小さな円上の点が描く曲線です。ここでは式をそのまま使い、まず形を観察します。

リスト12
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. 内サイクロイド

内サイクロイドは、大きな円の内側を小さな円が転がるとき、その小さな円上の点が描く曲線です。外サイクロイドと式が少し違います。

リスト13
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. 外トロコイド

外トロコイドは、外サイクロイドの発展形です。点の位置を小さな円の円周上ではなく、少し内側や外側にずらすことで、形が変わります。

リスト14
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;
}
実行結果:外トロコイド。dを変えると形が変わる

17. 内トロコイド

内トロコイドは、内サイクロイドの発展形です。内側を転がる円に付いた点の位置を、円周上からずらして描く曲線です。

リスト15
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座標を作る、と覚えると使いやすくなります。