Home Manual Reference Source

src/math/S3Angles.js

/**
 * 3DCG用のオイラー角クラス(immutable)
 * Roll(Z軸)、Pitch(X軸)、Yaw(Y軸)の順で角度を保持します。
 * 各値は常に周期的(-180~180度)に管理されます。
 *
 * @class
 * @module S3
 */
export default class S3Angles {
	/**
	 * オイラー角(ZXY順)を指定して作成します。
	 * @param {number} [z] ロール角(Z軸回転)
	 * @param {number} [x] ピッチ角(X軸回転)
	 * @param {number} [y] ヨー角(Y軸回転)
	 */
	constructor(z, x, y) {
		if (arguments.length === 3) {
			this.setRotateZXY(z, x, y);
		} else {
			/**
			 * ロール角(Z軸回転)を周期的に正規化した値
			 * @type {number}
			 */
			this.roll = 0;

			/**
			 * ピッチ角(X軸回転)を周期的に正規化した値
			 * @type {number}
			 */
			this.pitch = 0;

			/**
			 * ヨー角(Y軸回転)を周期的に正規化した値
			 * @type {number}
			 */
			this.yaw = 0;
		}
	}

	/**
	 * 角度を周期的(-PI~PI)に正規化します。内部利用のためprivateです。
	 * @private
	 * @param {number} x 任意の角度(度単位)
	 * @returns {number} 周期内(-180~180)の角度
	 */
	static _toPeriodicAngle(x) {
		if (x > S3Angles.PI) {
			return x - S3Angles.PI2 * ~~((x + S3Angles.PI) / S3Angles.PI2);
		} else if (x < -S3Angles.PI) {
			return x + S3Angles.PI2 * ~~((-x + S3Angles.PI) / S3Angles.PI2);
		}
		return x;
	}

	/**
	 * このオブジェクトのクローンを作成します。
	 * @returns {S3Angles} 複製されたオイラー角インスタンス
	 */
	clone() {
		return new S3Angles(this.roll, this.pitch, this.yaw);
	}

	/**
	 * Roll, Pitch, Yaw の順でオイラー角を再設定します。
	 * @param {number} z ロール角(Z軸回転)
	 * @param {number} x ピッチ角(X軸回転)
	 * @param {number} y ヨー角(Y軸回転)
	 */
	setRotateZXY(z, x, y) {
		this.roll = S3Angles._toPeriodicAngle(isNaN(z) ? 0.0 : z);
		this.pitch = S3Angles._toPeriodicAngle(isNaN(x) ? 0.0 : x);
		this.yaw = S3Angles._toPeriodicAngle(isNaN(y) ? 0.0 : y);
	}

	/**
	 * ピッチ角(X軸回転)を加算した新しいオイラー角を返します。
	 * @param {number} x 追加するピッチ角
	 * @returns {S3Angles} 新しいオイラー角インスタンス
	 */
	addRotateX(x) {
		return new S3Angles(this.roll, this.pitch + x, this.yaw);
	}

	/**
	 * ヨー角(Y軸回転)を加算した新しいオイラー角を返します。
	 * @param {number} y 追加するヨー角
	 * @returns {S3Angles} 新しいオイラー角インスタンス
	 */
	addRotateY(y) {
		return new S3Angles(this.roll, this.pitch, this.yaw + y);
	}

	/**
	 * ロール角(Z軸回転)を加算した新しいオイラー角を返します。
	 * @param {number} z 追加するロール角
	 * @returns {S3Angles} 新しいオイラー角インスタンス
	 */
	addRotateZ(z) {
		return new S3Angles(this.roll + z, this.pitch, this.yaw);
	}

	/**
	 * ピッチ角(X軸回転)のみを設定した新しいオイラー角を返します。
	 * @param {number} x 新しいピッチ角
	 * @returns {S3Angles} 新しいオイラー角インスタンス
	 */
	setRotateX(x) {
		return new S3Angles(this.roll, x, this.yaw);
	}

	/**
	 * ヨー角(Y軸回転)のみを設定した新しいオイラー角を返します。
	 * @param {number} y 新しいヨー角
	 * @returns {S3Angles} 新しいオイラー角インスタンス
	 */
	setRotateY(y) {
		return new S3Angles(this.roll, this.pitch, y);
	}

	/**
	 * ロール角(Z軸回転)のみを設定した新しいオイラー角を返します。
	 * @param {number} z 新しいロール角
	 * @returns {S3Angles} 新しいオイラー角インスタンス
	 */
	setRotateZ(z) {
		return new S3Angles(z, this.pitch, this.yaw);
	}

	/**
	 * オイラー角を文字列で返します。
	 * @returns {string} "angles[roll,pitch,yaw]"形式の文字列
	 */
	toString() {
		return "angles[" + this.roll + "," + this.pitch + "," + this.yaw + "]";
	}
}

/**
 * 180度(定数)。オイラー角の範囲・変換に利用します。
 * @type {number}
 */
S3Angles.PI = 180.0;

/**
 * 90度(定数)。
 * @type {number}
 */
S3Angles.PIOVER2 = S3Angles.PI / 2.0;

/**
 * Gimbal lock防止用の値(90度-微小値)。
 * @type {number}
 */
S3Angles.PILOCK = S3Angles.PIOVER2 - 0.0001;

/**
 * 360度(定数)。
 * @type {number}
 */
S3Angles.PI2 = 2.0 * S3Angles.PI;