見出し画像

オブジェクトが常にキャンバスの中心を向く仕組み

をp5.jsで実装しようとして沼った話です。クリエイティブコーダーの皆さんや数字に強い方には当たり前のことだとは思いますが、半ば備忘録的に記しておきます。

'translate()'関数や'heading()'関数で頑張った

回転の話なので、まずは'translate()'関数と'rotate()'関数で実装する方法を考えました。任意の座標に図形(ここでは三角形)を描画し、その中心を原点に移動させて回転。そのあとに元の位置へ戻す。考え方はシンプルですが、どうにも意図した挙動にならず、結局自分がどういう計算をさせていたのかいまだにわかっていません。また、p5.Vectorに含まれる'heading()'メソッドでも試してみましたが、こちらも似たような挙動を再現するのみで、キャンバスの中心を向いてくれませんでした。

三角形の頂点が中心点を向いてくれるはずだった

正解は逆正接関数、つまりatan2だった

失敗した方法を何度も堂々巡りをしていたので「そろそろシフマン先生の動画でも漁るかなぁ」と思い始めたが、とりあえずChatGPTに三角形の描画からステップを踏んで描画をしてもらうことにしました。数回のやり取りであっさり解決。

本当にあっさりしてた

描画の条件と生成されたコードは以下の通り。

  • 任意の半径rの円周上に始点が90°(12時)の位置にくるように三角形を描画する*

  • 三角形の中心はマウスの座標で指定する

  • 三角形の中心が常にキャンバスの中心を向く

*この描画方法自体はこちらからコードの例を示した(追記参照)

function setup() {
  createCanvas(600, 600);
}

function draw() {
  background(220);
  
  let centerX = mouseX;
  let centerY = mouseY;
  let r = 100;
  
  // 中心からの角度を計算
  let angle = atan2(centerY - height / 2, centerX - width / 2);
  
  // 三角形の描画
  push();
  translate(centerX, centerY);
  rotate(angle);
  beginShape();
  for (let a = -PI / 2; a < TAU - PI / 2; a += TAU / 3) {
    let x = r * cos(a);
    let y = r * sin(a);
    vertex(x, y);
  }
  endShape(CLOSE);
  pop();
}

ほう、'atan2()'関数ですか…
リファレンスで見たことあるけど放置してたわ。なんですかそれは。

atan関数は、与えられた座標の y 軸と x 軸との間の角度(ラジアン単位)を返す関数です。通常、この関数は atan(y/x) の形で使用され、y 座標と x 座標を引数に取ります。この関数は、角度が -PI/2 から PI/2 の範囲に収まるため、x が負の場合は結果も負になります。

一方、atan2関数は、与えられた座標の y 軸と x 軸との間の角度を返す関数ですが、atan関数とは異なり、どの象限においても正しい角度を返します。また、x が 0 の場合でも正確な結果を返すことができます。atan2(y, x) の形で使用され、y 座標と x 座標を引数に取ります。

これらの関数は、特にグラフィックスやアニメーションなどで、オブジェクトの回転や移動の計算に使用されます。具体的には、二次元空間でのオブジェクトの角度を計算する場面でよく使用されます。

ChatGPT(GPT3.5)による解説

生成AIによる解説を真に受けるべきかどうかという問題はありますが、ざっくりググってみた内容とも大きな差異はなさそうでした。とにかく任意の座標と原点との角度を計算するみたいですね。

なんか作ってみた

簡単なアレンジを試みました。

たくさんの三角が中心を向いている


もちろん中心を背にする三角もできる

おわりに

今回も生成AIに解決してもらいました。だんだんと逆引き辞書のように思えてきました。

先述の通り、どうやら逆正接関数を使用した回転の制御は半ば常識のようなものらしいです。この記事が同じような沼に向かっている方への道標(墓石?)になれば幸いです。そんな方は少数かもしれませんが…。


追記:コードに誤りがあったので修正しました。(2024年5月2日)


この記事が気に入ったらサポートをしてみませんか?