0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

p5.jsで複数の回転の結果をまとめる小ネタ

0
Last updated at Posted at 2026-08-10

はじめに

 p5のrotate関数にはrotateX, rotateY, rotateZの他にrotateという関数があります。

軸を指定するので若干指定難易度が高いんですが、実は繰り返し先ほどのrotateX,Y,Zを実行した結果を、まとめて実行できるポテンシャルを持っています。それを説明しましょう。そのためにp5.Quatを使いたかったんですが、実は計算がバグっています。

    /**
     * Multiplies a quaternion with other quaternion.
     * @method mult
     * @param  {p5.Quat} [quat] quaternion to multiply with the quaternion calling the method.
     * @chainable
     */
    multiply(quat) {

      return new Quat(
        this.w * quat.w - this.vec.x * quat.vec.x - this.vec.y * quat.vec.y - this.vec.z - quat.vec.z,
        this.w * quat.vec.x + this.vec.x * quat.w + this.vec.y * quat.vec.z - this.vec.z * quat.vec.y,
        this.w * quat.vec.y - this.vec.x * quat.vec.z + this.vec.y * quat.w + this.vec.z * quat.vec.x,
        this.w * quat.vec.z + this.vec.x * quat.vec.y - this.vec.y * quat.vec.x + this.vec.z * quat.w
      );

    }

わかるでしょうか。w 成分(最初の成分)ですが、4つ目の項が引き算になっています。それゆえw 成分だけ正確に出ません。なので今回紹介するコードでは次のパッチを使います。

p5.Quat.prototype.multiply = function(quat){
        return new p5.Quat(
        this.w * quat.w - this.vec.x * quat.vec.x - this.vec.y * quat.vec.y - this.vec.z * quat.vec.z,
        this.w * quat.vec.x + this.vec.x * quat.w + this.vec.y * quat.vec.z - this.vec.z * quat.vec.y,
        this.w * quat.vec.y - this.vec.x * quat.vec.z + this.vec.y * quat.w + this.vec.z * quat.vec.x,
        this.w * quat.vec.z + this.vec.x * quat.vec.y - this.vec.y * quat.vec.x + this.vec.z * quat.w
      );
}

これで問題なく運用できます。では始めましょう。

コード全文

function setup() {
  createCanvas(400, 400, WEBGL);

  const qx = p5.Quat.fromAxisAngle(0.7, 1, 0, 0);
  const qy = p5.Quat.fromAxisAngle(0.3, 0, 1, 0);
  const qz = p5.Quat.fromAxisAngle(1.1, 0, 0, 1);
  const q = qx.multiply(qy).multiply(qz);
  const theta = Math.atan2(q.vec.mag(), q.w);

  background(0);

  rotateX(0.7);
  rotateY(0.3);
  rotateZ(1.1);
  //rotate(theta*2, q.vec);
  noStroke();
  pointLight(99,200,255,-10,40,360);
  fill(255);
  box(200);
}

p5.Quat.prototype.multiply = function(quat){
        return new p5.Quat(
        this.w * quat.w - this.vec.x * quat.vec.x - this.vec.y * quat.vec.y - this.vec.z * quat.vec.z,
        this.w * quat.vec.x + this.vec.x * quat.w + this.vec.y * quat.vec.z - this.vec.z * quat.vec.y,
        this.w * quat.vec.y - this.vec.x * quat.vec.z + this.vec.y * quat.w + this.vec.z * quat.vec.x,
        this.w * quat.vec.z + this.vec.x * quat.vec.y - this.vec.y * quat.vec.x + this.vec.z * quat.w
      );
}

image.png

rotateX,Y,Zを使って描画する

 まず、rotateX,Y,Zで回転させます。この場合、次のようなコードになりますね。

function setup() {
  createCanvas(400, 400, WEBGL);

  background(0);

  rotateX(0.7);
  rotateY(0.3);
  rotateZ(1.1);

  noStroke();
  pointLight(99,200,255,-10,40,360);
  fill(255);
  box(200);
}

Xで0.7, Yで0.3, Zで1.1です。このように回転させると上のような見た目になります。これをrotate一本でやろうってわけです。そこでクォータニオンの登場です。まず作るためのメソッドとしてfromAxisAngleというのがあるんでそれ使いましょう。

  const qx = p5.Quat.fromAxisAngle(0.7, 1, 0, 0);
  const qy = p5.Quat.fromAxisAngle(0.3, 0, 1, 0);
  const qz = p5.Quat.fromAxisAngle(1.1, 0, 0, 1);

最初に角度、次いで軸成分 x, y, z を指定します。上の回転に対応しているものです。次いでこれらを順に掛け算します。この掛け算はいわゆるイミュータブルなので常に新しいものが返ります。

  const q = qx.multiply(qy).multiply(qz);

そこから軸と角度を抽出するんですが、角度に関しては半分の値が返ります。取り方は次の式です。まあ詳しいことは置いておきましょう。

  const theta = Math.atan2(q.vec.mag(), q.w);

そんで軸ですが、詳しいことは省略しますがまあvecがそのまま使えます。そういうわけで、角度の2倍とvecでrotateしましょう。

function setup() {
  createCanvas(400, 400, WEBGL);

  const qx = p5.Quat.fromAxisAngle(0.7, 1, 0, 0);
  const qy = p5.Quat.fromAxisAngle(0.3, 0, 1, 0);
  const qz = p5.Quat.fromAxisAngle(1.1, 0, 0, 1);
  const q = qx.multiply(qy).multiply(qz);
  const theta = Math.atan2(q.vec.mag(), q.w);

  background(0);

  rotate(theta*2, q.vec);

  noStroke();
  pointLight(99,200,255,-10,40,360);
  fill(255);
  box(200);
}

同じ見た目になりましたね。比較してみましょう。

2222.png

1111.png

うん!ばっちりですね!もちろん現在のp5.Quatはバグってるんで、パッチ当てないとめちゃくちゃになります。一応載せておこうか。

image.png

まあそんな感じなんで、パッチ当てない限りこの手段は使い物にならないです。とはいえ個別に実行すればいいわけですから、コードを短くしたいのでもない限り使う機会はないでしょう。ちなみに一応issueは立てておきました。ただ今ちょっとレビュアーが事務処理で忙しいようで、すぐには解決しないでしょうね。

おわりに

 ここまでお読みいただいてありがとうございました。

解決済みだった

 なんかもう解決済みでした。

そういうわけで、遠からず反映されるでしょう。2.3.2の時点ではまだ反映されていないようです。

1系でも使えるよ

 p5.Quat自体は1.11.0かそれ以前...わかりませんが、そこからあるんで、1系でもこのネタは使えます。まあパッチを当てないと正しい結果は出ませんが。結果しか要らないなら1系か2系かは関係ないですね。

rstregegrdhghr555555.png

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?