three.jsで学ぶベクトル演算

ベクトル演算についてまとめながら、three.jsのベクトルクラスの実装を見ていきたいと思います。

three.jsには2次元ベクトルを表す[Vector2](https://threejs.org/docs/#api/en/math/Vector2)クラス、3次元ベクトルを表す[Vector3](https://threejs.org/docs/#api/en/math/Vector3)クラス、4次元ベクトルを表す[Vector4](https://threejs.org/docs/#api/en/math/Vector4)クラスが存在します。それぞれ[Vector2.js](https://github.com/mrdoob/three.js/blob/r110/src/math/Vector2.js)、[Vector3.js](https://github.com/mrdoob/three.js/blob/r110/src/math/Vector3.js)、[Vector4.js](https://github.com/mrdoob/three.js/blob/r110/src/math/Vector4.js)で実装されています。

基本的にはベクトルの演算は次元数によらず同じなので3D CGで最もよく使用する3次元ベクトル(`Vector3`)について見ていきます。

three.jsは現時点(2019/11/09)において最新のバージョンである[r110](https://github.com/mrdoob/three.js/tree/r110)を参考にしています。
ベクトルは特に断りのない限り、列ベクトルです。実数は小文字(e.g. $x$)、ベクトルは上付き矢印(e.g. $\vec{v}$)、行列は大文字(e.g. $M$)、クォータニオンは上付きハット(e.g. $\hat{q}$)で表します。

## ベクトルの要素

3次元ベクトルは3つの実数を持つベクトルです。各要素を順に$x$、$y$、$z$で表します。

```math
\vec{v} = \left(\begin{array}{c}x \\ y\\ z \end{array}\right)
```

`Vector3`クラスでは、インスタンス変数`x`、`y`、`z`で各要素を保持します。引数として値が与えられない場合はデフォルト値として`0`を設定しています。

```js
function Vector3( x, y, z ) {

	this.x = x || 0;
	this.y = y || 0;
	this.z = z || 0;

}
```

[set](https://threejs.org/docs/#api/en/math/Vector3.set)、[setScalar](https://threejs.org/docs/#api/en/math/Vector3.setScalar)メソッドで`Vector3`インスタンスの要素を変更できるほか、[setX](https://threejs.org/docs/#api/en/math/Vector3.setX)、[setY](https://threejs.org/docs/#api/en/math/Vector3.setY)、[setZ](https://threejs.org/docs/#api/en/math/Vector3.setZ)メソッドで一つの要素のみを変更することも可能です。

```js
	set: function ( x, y, z ) {

		this.x = x;
		this.y = y;
		this.z = z;

		return this;

	},

	setScalar: function ( scalar ) {

		this.x = scalar;
		this.y = scalar;
		this.z = scalar;

		return this;

	},

	setX: function ( x ) {

		this.x = x;

		return this;

	},

	setY: function ( y ) {

		this.y = y;

		return this;

	},

	setZ: function ( z ) {

		this.z = z;

		return this;

	},
```

そのほかにインデックス値で各要素を取得・設定できる[getComponent](https://threejs.org/docs/#api/en/math/Vector3.getComponent)メソッドと[setCompoennt](https://threejs.org/docs/#api/en/math/Vector3.setComponent)メソッドが`Vector3`クラスには存在しています。

```js
	setComponent: function ( index, value ) {

		switch ( index ) {

			case 0: this.x = value; break;
			case 1: this.y = value; break;
			case 2: this.z = value; break;
			default: throw new Error( 'index is out of range: ' + index );

		}

		return this;

	},

	getComponent: function ( index ) {

		switch ( index ) {

			case 0: return this.x;
			case 1: return this.y;
			case 2: return this.z;
			default: throw new Error( 'index is out of range: ' + index );

		}

	},
```

[clone](https://threejs.org/docs/#api/en/math/Vector3.clone)メソッドで同じ値を持つ新たな`Vector3`インスタンスを生成でき、[copy](https://threejs.org/docs/#api/en/math/Vector3.copy)メソッドで引数に与えた`Vector3`インスタンスと同じ値を持つようにすることができます。

```js
	clone: function () {

		return new this.constructor( this.x, this.y, this.z );

	},

	copy: function ( v ) {

		this.x = v.x;
		this.y = v.y;
		this.z = v.z;

		return this;

	},
```

## ベクトルの和

2つのベクトルの和は各要素同士の和になります。

```math
\vec{v_1} + \vec{v_2} =
\left(\begin{array}{c}x_1 \\ y_1\\ z_1 \end{array}\right) + 
\left(\begin{array}{c}x_2 \\ y_2\\ z_2 \end{array}\right) =
\left(\begin{array}{c}x_1 + x_2 \\ y_1 + y_2\\ z_1 + z_2 \end{array}\right)
```

`Vector3`クラスでは2つのベクトルの和を求める[add](https://threejs.org/docs/#api/en/math/Vector3.add)メソッドと[addVectors](https://threejs.org/docs/#api/en/math/Vector3.addVectors)メソッドが存在しています。

```js
	add: function ( v, w ) {

		if ( w !== undefined ) {

			console.warn( 'THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
			return this.addVectors( v, w );

		}

		this.x += v.x;
		this.y += v.y;
		this.z += v.z;

		return this;

	},

	addVectors: function ( a, b ) {

		this.x = a.x + b.x;
		this.y = a.y + b.y;
		this.z = a.z + b.z;

		return this;

	},
```

ベクトルとスカラーの和は数学的には定義されていませんが、`Vector3`にはベクトルの各要素にスカラー値を足す[addScalar](https://threejs.org/docs/#api/en/math/Vector3.addScalar)メソッドと[addScaledVector](https://threejs.org/docs/#api/en/math/Vector3.addScaledVector)メソッドが利便性のために存在しています。

```js
	addScalar: function ( s ) {

		this.x += s;
		this.y += s;
		this.z += s;

		return this;

	},

	addScaledVector: function ( v, s ) {

		this.x += v.x * s;
		this.y += v.y * s;
		this.z += v.z * s;

		return this;

	},
```

## ベクトルの差

2つのベクトルの差は各要素同士の差になります。

```math
\vec{v_1} - \vec{v_2} =
\left(\begin{array}{c}x_1 \\ y_1\\ z_1 \end{array}\right) -
\left(\begin{array}{c}x_2 \\ y_2\\ z_2 \end{array}\right) =
\left(\begin{array}{c}x_1 - x_2 \\ y_1 - y_2\\ z_1 - z_2 \end{array}\right)
```

`Vector3`には2つのベクトルの差を求める[sub](https://threejs.org/docs/#api/en/math/Vector3.sub)メソッドと[subVectors](https://threejs.org/docs/#api/en/math/Vector3.subVectors)メソッドが存在しています。

```js
	sub: function ( v, w ) {

		if ( w !== undefined ) {

			console.warn( 'THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
			return this.subVectors( v, w );

		}

		this.x -= v.x;
		this.y -= v.y;
		this.z -= v.z;

		return this;

	},

	subVectors: function ( a, b ) {

		this.x = a.x - b.x;
		this.y = a.y - b.y;
		this.z = a.z - b.z;

		return this;

	},
```

和と同様にベクトルとスカラーの差は数学的に定義されていませんが、`Vector3`には[subScalar](https://threejs.org/docs/#api/en/math/Vector3.subScalar)メソッドが利便性のために存在しています。

```js
	subScalar: function ( s ) {

		this.x -= s;
		this.y -= s;
		this.z -= s;

		return this;

	},
```

## ベクトルの積

2つのベクトルの積はベクトルの各要素の積になります。

```math
\vec{v_1}\vec{v_2} =
\left(\begin{array}{c}x_1 \\ y_1\\ z_1 \end{array}\right)
\left(\begin{array}{c}x_2 \\ y_2\\ z_2 \end{array}\right) =
\left(\begin{array}{c}x_1x_2 \\ y_1y_2\\ z_1z_2 \end{array}\right)
```

`Vector3`には2つのベクトルの積を求める[multiply](https://threejs.org/docs/#api/en/math/Vector3.multiply)メソッドと [multiplyVectors](https://threejs.org/docs/#api/en/math/Vector3.multiplyVectors)メソッドが存在しています。

```js
	multiply: function ( v, w ) {

		if ( w !== undefined ) {

			console.warn( 'THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' );
			return this.multiplyVectors( v, w );

		}

		this.x *= v.x;
		this.y *= v.y;
		this.z *= v.z;

		return this;

	},

	multiplyVectors: function ( a, b ) {

		this.x = a.x * b.x;
		this.y = a.y * b.y;
		this.z = a.z * b.z;

		return this;

	},
```

ベクトルとスカラーの積はベクトルの各要素にスカラーをかけることで求まります。

```math
a\vec{v} =
a\left(\begin{array}{c}x \\ y\\ z \end{array}\right) =
\left(\begin{array}{c}ax \\ ay\\ az \end{array}\right)
```

`Vector3`にはベクトルとスカラーの積を求める[multiplyScalar](https://threejs.org/docs/#api/en/math/Vector3.multiplyScalar)メソッドが存在しています。

```js
	multiplyScalar: function ( scalar ) {

		this.x *= scalar;
		this.y *= scalar;
		this.z *= scalar;

		return this;

	},
```

## ベクトルの商

2つのベクトルの商はベクトルの各要素の商になります。

```math
\vec{v_1} / \vec{v_2} =
\left(\begin{array}{c}x_1 \\ y_1\\ z_1 \end{array}\right) / 
\left(\begin{array}{c}x_2 \\ y_2\\ z_2 \end{array}\right) =
\left(\begin{array}{c}x_1 / x_2 \\ y_1 / y_2\\ z_1 / z_2 \end{array}\right)
```

`Vector3`には2つのベクトルを商を求める[divide](https://threejs.org/docs/#api/en/math/Vector3.divide)メソッドが存在します。

```js
	divide: function ( v ) {

		this.x /= v.x;
		this.y /= v.y;
		this.z /= v.z;

		return this;

	},
```

ベクトルとスカラーの商はベクトルの各要素をスカラーで割ることで求まります。

```math
\vec{v} / a =
\left(\begin{array}{c}x \\ y\\ z \end{array}\right) / a =
\left(\begin{array}{c}x / a \\ y / a\\ z / a \end{array}\right)
```

`Vector3`にはベクトルとスカラーの商を求める[divideScalar](https://threejs.org/docs/#api/en/math/Vector3.divideScalar)メソッドが存在します。

```js
	divideScalar: function ( scalar ) {

		return this.multiplyScalar( 1 / scalar );

	},
```

## 逆ベクトル

ベクトル${\vec{v}}$に対して大きさが同じで向きが逆のベクトルを逆ベクトルと呼びます。逆ベクトルは以下のように$-1$を掛けることで求まります。

```math
-\vec{v} = \left(\begin{array}{c}-x \\ -y\\ -z \end{array}\right)
```

`Vector3`では[negate](https://threejs.org/docs/#api/en/math/Vector3.negate)メソッドで逆ベクトルを求めることができます。

```js
	negate: function () {

		this.x = - this.x;
		this.y = - this.y;
		this.z = - this.z;

		return this;

	},
```

## ベクトルのドット積 (内積)

2つのベクトルのドット積は各要素同士の積の和になります。

```math
\vec{v_1} \cdot \vec{v_2} =
\left(\begin{array}{c}x_1 \\ y_1\\ z_1 \end{array}\right) \cdot 
\left(\begin{array}{c}x_2 \\ y_2\\ z_2 \end{array}\right) =
x_1x_2 + y_1y_2 + z_1z_2
```

`Vector3`では[dot](https://threejs.org/docs/#api/en/math/Vector3.dot)メソッドで求めることができます。

```js
	dot: function ( v ) {

		return this.x * v.x + this.y * v.y + this.z * v.z;

	},

```

## 3次元ベクトルのクロス積 (外積)

3次元ベクトルのクロス積は以下の方法で求めることができます。
クロス積は3次元ベクトルのみで定義されており、2次元ベクトルや4次元ベクトルには存在ません。

```math
\vec{v_1} \times \vec{v_2} =
\left(\begin{array}{c}x_1 \\ y_1\\ z_1 \end{array}\right) \times
\left(\begin{array}{c}x_2 \\ y_2\\ z_2 \end{array}\right) =
\left(\begin{array}{c}y_1z_2 - z_1y_2 \\ z_1x_2 - x_1z_2\\ x_1y_2 - y_1x_2 \end{array}\right)
```

`Vector3`にはクロス積を求める[cross](https://threejs.org/docs/#api/en/math/Vector3.cross)メソッドと[crossVectors](https://threejs.org/docs/#api/en/math/Vector3.crossVectors)メソッドが存在しています。

```js
	cross: function ( v, w ) {

		if ( w !== undefined ) {

			console.warn( 'THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' );
			return this.crossVectors( v, w );

		}

		return this.crossVectors( this, v );

	},

	crossVectors: function ( a, b ) {

		var ax = a.x, ay = a.y, az = a.z;
		var bx = b.x, by = b.y, bz = b.z;

		this.x = ay * bz - az * by;
		this.y = az * bx - ax * bz;
		this.z = ax * by - ay * bx;

		return this;

	},
```

## ベクトルの大きさ

ベクトル$\vec{v}$の大きさ$|\vec{v}|$は以下の式で求めることができます。

```math
|\vec{v}| = \sqrt{xx + yy + zz}
```

`Vector3`では[length](https://threejs.org/docs/#api/en/math/Vector3.length)メソッドで求めることができます。
2つのベクトルの大きさを比較する場合などには平方根を計算する必要がないため、効率化のためにベクトルの大きさの2乗を返す[lengthSq](https://threejs.org/docs/#api/en/math/Vector3.lengthSq)メソッドも存在しています。

```js
	length: function () {

		return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z );

	},

	lengthSq: function () {

		return this.x * this.x + this.y * this.y + this.z * this.z;

	},
```

## ベクトルの正規化

ベクトルの向きを変更せずに大きさのみを$1$にすることをベクトルの正規化と呼びます。ベクトル$\vec{v}$を正規化したベクトル$\vec{v_{norm}}$は以下のように求めることができます。

```math
\vec{v_{norm}} = \frac{\vec{v}}{|\vec{v}|} 
```

`Vector3`では[normalize](https://threejs.org/docs/#api/en/math/Vector3.normalize)メソッドで正規化を行うことができます。ゼロ除算が発生しないように`this.length()`が`0`のときは`1`で割るようになっています。

```js
	normalize: function () {

		return this.divideScalar( this.length() || 1 );

	},
```

## 2つのベクトルの距離

2つのベクトルの距離は2つのベクトルの差の大きさになります。

```math
\vec{v_1} = \left(\begin{array}{c}x_1 \\ y_1\\ z_1 \end{array}\right),
\vec{v_2} = \left(\begin{array}{c}x_2 \\ y_2\\ z_2 \end{array}\right)\\
\vec{v'} = \vec{v_1} - \vec{v_2}\\
d = |\vec{v'}|
```

`Vector3`では[distanceTo](https://threejs.org/docs/#api/en/math/Vector3.distanceTo)メソッドで2つのベクトルの距離を求めることがあります。ベクトルの大きさと同様に平方根を取る必要がない場合があるので、2つのベクトルの距離の2乗を返す[distanceToSquared](https://threejs.org/docs/#api/en/math/Vector3.distanceToSquared)も存在しています。

```js
	distanceTo: function ( v ) {

		return Math.sqrt( this.distanceToSquared( v ) );

	},

	distanceToSquared: function ( v ) {

		var dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z;

		return dx * dx + dy * dy + dz * dz;

	},
```

## 2つのベクトルのなす角

ドット積の定義より2つのベクトルのなす角$\theta$は以下のように求めることができます。

```math
\vec{v_1}\cdot\vec{v_2} = |\vec{v_1}||\vec{v_2}|cos\theta\\
\theta = acos(\frac{\vec{v_1}\cdot\vec{v_2}}{|\vec{v_1}||\vec{v_2}|})
```

`Vector3`では[angleTo](https://threejs.org/docs/#api/en/math/Vector3.angleTo)メソッドが対応しています。

```js
	angleTo: function ( v ) {

		var denominator = Math.sqrt( this.lengthSq() * v.lengthSq() );

		if ( denominator === 0 ) console.error( 'THREE.Vector3: angleTo() can\'t handle zero length vectors.' );

		var theta = this.dot( v ) / denominator;

		// clamp, to handle numerical problems

		return Math.acos( _Math.clamp( theta, - 1, 1 ) );

	},
```

## ベクトルの正射影

ベクトル$\vec{v_1}$をベクトル$\vec{v_2}$に正射影したベクトル$\vec{v_{proj}}$は以下のように求めることができあます。

```math
\vec{v_{proj}} = \frac{\vec{v_1}\cdot\vec{v_2}}{|\vec{v_2}|}\vec{v_2}
```

またベクトル$\vec{v}$を法線ベクトル$\vec{n}$を持つ平面に対して正射影する場合には、$\vec{v}$を平面に平行な成分$\vec{v_{\parallel}}$と平面に垂直な成分$\vec{v_{\perp}}$に分けて考えます。まず、$\vec{v}$を$\vec{n}$に正射影することで$\vec{v_{\perp}}$を求め、その後$\vec{v}$から$\vec{v_{\perp}}$を引くことで$\vec{v_{\parallel}}$を求めることができます。

```math
\vec{v} = \vec{v_{\parallel}} + \vec{v_{\perp}}\\
\vec{v_{\perp}} = \frac{\vec{v}\cdot\vec{n}}{|\vec{n}|}\vec{n}\\
\vec{v_{\parallel}} = \vec{v} - \vec{v_{\perp}} = \vec{v} - \frac{\vec{v}\cdot\vec{n}}{|\vec{n}|}\vec{n}
```

`Vector3`では[projectOnVector](https://threejs.org/docs/#api/en/math/Vector3.projectOnVector)メソッドでベクトルへの正射影を、[projectOnPlane](https://threejs.org/docs/#api/en/math/Vector3.projectOnPlane)メソッドで平面への正射影を求めることができます。

```js
	projectOnVector: function ( v ) {

		// v cannot be the zero v

		var scalar = v.dot( this ) / v.lengthSq();

		return this.copy( v ).multiplyScalar( scalar );

	},

	projectOnPlane: function ( planeNormal ) {

		_vector.copy( this ).projectOnVector( planeNormal );

		return this.sub( _vector );

	},
```

## 反射ベクトル

入射ベクトル$\vec{v}$、法線ベクトル$\vec{n}$としたときに反射ベクトル$\vec{r}$は以下のように求まります。

```math
\vec{r} = \vec{v} - 2(\vec{v}\cdot\vec{n})\vec{n}
```

`Vector3`では[reflect](https://threejs.org/docs/#api/en/math/Vector3.reflect)メソッドで自身(`this`)を入射ベクトルにしたときの反射ベクトルを求めることができます。

```js
	reflect: function ( normal ) {

		// reflect incident vector off plane orthogonal to normal
		// normal is assumed to have unit length

		return this.sub( _vector.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) );

	},
```

## 行列とベクトルの積

3x3行列と3次元列ベクトルの積は以下のようになります。

```math
M\vec{v} =
\left(
    \begin{array}{ccc}
      e_{11} & e_{12} & e_{13} \\
      e_{21} & e_{22} & e_{23} \\
      e_{31} & e_{32} & e_{33}
    \end{array}
\right)
\left(\begin{array}{c}x \\ y\\ z \end{array}\right) =
\left(\begin{array}{c}
    e_{11}x + e_{12}y + e_{13}z\\
    e_{21}x + e_{22}y + e_{23}z\\
    e_{31}x + e_{32}y + e_{33}z \end{array}\right)
```

Three.jsでは[applyMatrix3](https://threejs.org/docs/#api/en/math/Vector3.applyMatrix3)メソッドが定義されているほか、最後に正規化を行う[transformDirection](https://threejs.org/docs/#api/en/math/Vector3.transformDirection)メソッドがあります。
Three.jsの行列は列オーダーなので`m.elements`には$[e_{11}, e_{21}, e_{31}, e_{12}, e_{22}, e_{32}, e_{13}, e_{23}, e_{33}]$という順番で要素が入っていることに注意してください。

```js
	applyMatrix3: function ( m ) {

		var x = this.x, y = this.y, z = this.z;
		var e = m.elements;

		this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z;
		this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z;
		this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z;

		return this;

	},

	transformDirection: function ( m ) {

		// input: THREE.Matrix4 affine matrix
		// vector interpreted as a direction

		var x = this.x, y = this.y, z = this.z;
		var e = m.elements;

		this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z;
		this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z;
		this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z;

		return this.normalize();

	},
```

4x4行列と3次元ベクトルの積は数学的には定義できませんが、Three.jsには3次元ベクトルを同次座標系に拡張して4x4行列との積を求める[applyMatrix4](https://threejs.org/docs/#api/en/math/Vector3.applyMatrix4)メソッドがあります。
具体的には引数として与えられた3次元ベクトルの$w$要素を$1$として4次元ベクトルに拡張して4x4行列との積(次式の$\vec{v'}$)を求めます。その後、各要素を$w$の値で割って3次元ベクトル(次式の$\vec{v''}$)に戻します。

```math
\vec{v'} = \left(\begin{array}{c}x' \\ y'\\ z' \\w' \end{array}\right) = M\vec{v} =
\left(
    \begin{array}{ccc}
      e_{11} & e_{12} & e_{13} & e_{14} \\
      e_{21} & e_{22} & e_{23} & e_{24} \\
      e_{31} & e_{32} & e_{33} & e_{34} \\
      e_{41} & e_{42} & e_{43} & e_{44} \\
    \end{array}
\right)
\left(\begin{array}{c}x \\ y\\ z\\ 1 \end{array}\right) =
\left(\begin{array}{c}
    e_{11}x + e_{12}y + e_{13}z + e_{14}\\
    e_{21}x + e_{22}y + e_{23}z + e_{24}\\
    e_{31}x + e_{32}y + e_{33}z + e_{34}\\
    e_{41}x + e_{42}y + e_{43}z + e_{44} \end{array}\right)\\
\vec{v''} = \left(\begin{array}{c}x' / w' \\ y' / w' \\ z' / w' \end{array}\right)
```

```js
	applyMatrix4: function ( m ) {

		var x = this.x, y = this.y, z = this.z;
		var e = m.elements;

		var w = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] );

		this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * w;
		this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * w;
		this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * w;

		return this;

	},
```

## クォータニオンによるベクトルの回転

単位クォータニオンは3次元空間での回転を表すことができます。クォータニオン$\hat{q}$の逆クォータニオンを$\hat{q^{-1}}$とすると$\hat{q}$によるベクトル$\vec{v}$の回転は以下のように表すことができます。ベクトルを一度クォータニオンと見なして演算していることに注意してください。

```math
\vec{v} = \left(\begin{array}{c}x \\ y\\ z \end{array}\right)\\
\hat{v} = (x, y, z, 0)\\
\hat{v'} = (x', y', z', 0) = \hat{q}\hat{v}\hat{q}^{-1}\\
\vec{v'} = \left(\begin{array}{c}x' \\ y'\\ z' \end{array}\right)
```

2つのクォータニオンの積は以下のようになります。

```math
\hat{p} = (p_x, p_y, p_z, p_w)\\
\hat{q} = (q_x, q_y, q_z, q_w)\\
\hat{p}\hat{q} = \left(\begin{array}{c}
p_wq_x - p_zq_y + p_yq_z + p_xq_w,\\
p_zq_x + p_wq_y - p_xq_z + p_yq_w,\\
p_yq_x + p_xq_y + p_wq_z + p_zq_w,\\
-p_xq_x - p_yq_y - p_zq_z + p_wq_w
 \end{array}\right)
```

`Vector3`では[applyQuaternion](https://threejs.org/docs/#api/en/math/Vector3.applyQuaternion)メソッドでクォータニオンによりベクトルを回転させることができます。[applyEuler](https://threejs.org/docs/#api/en/math/Vector3.applyEuler)メソッドと[applyAxisAngle](https://threejs.org/docs/#api/en/math/Vector3.applyAxisAngle)メソッドも内部的には`applyQuaternion`メソッドを使用しています。

```js
	applyQuaternion: function ( q ) {

		var x = this.x, y = this.y, z = this.z;
		var qx = q.x, qy = q.y, qz = q.z, qw = q.w;

		// calculate quat * vector

		var ix = qw * x + qy * z - qz * y;
		var iy = qw * y + qz * x - qx * z;
		var iz = qw * z + qx * y - qy * x;
		var iw = - qx * x - qy * y - qz * z;

		// calculate result * inverse quat

		this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy;
		this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz;
		this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx;

		return this;

	},

	applyEuler: function ( euler ) {

		if ( ! ( euler && euler.isEuler ) ) {

			console.error( 'THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order.' );

		}

		return this.applyQuaternion( _quaternion.setFromEuler( euler ) );

	},

	applyAxisAngle: function ( axis, angle ) {

		return this.applyQuaternion( _quaternion.setFromAxisAngle( axis, angle ) );

	},

```

## 球座標系からの直交座標系への変換

動径$r$と2つの偏角$\phi$、$\theta$で表された球座標を直交座標に変換するには以下のようにします。

```math
\vec{v} = \left(\begin{array}{c}
rsin\phi sin\theta \\
rcos\phi \\
rsin\phi cos\theta \end{array}\right)
```

`Vector3`クラスでは[setFromSpherical](https://threejs.org/docs/#api/en/math/Vector3.setFromSpherical)メソッドと[setFromSphericalCoords]([setFromSpherical](https://threejs.org/docs/#api/en/math/Vector3.setFromSphericalCoords)
)メソッドで球座標から直交座標を求めることができます。

```js
	setFromSpherical: function ( s ) {

		return this.setFromSphericalCoords( s.radius, s.phi, s.theta );

	},

	setFromSphericalCoords: function ( radius, phi, theta ) {

		var sinPhiRadius = Math.sin( phi ) * radius;

		this.x = sinPhiRadius * Math.sin( theta );
		this.y = Math.cos( phi ) * radius;
		this.z = sinPhiRadius * Math.cos( theta );

		return this;

	},
```

## 円柱座標系から直交座標系への変換

動径$r$、偏角$\theta$でy方向に延びる円柱座標を直交座標に変換するには以下のようにします。

```math
\vec{v} = \left(\begin{array}{c}
rsin\theta \\
y \\
rcos\theta \end{array}\right)
```

`Vector3`では[setFromCylindrical](https://threejs.org/docs/#api/en/math/Vector3.setFromCylindrical)メソッドと[setFromCylindricalCoords](https://threejs.org/docs/#api/en/math/Vector3.setFromCylindricalCoords)メソッドで円柱座標から直交座標を求めることができます。

```js
	setFromCylindrical: function ( c ) {

		return this.setFromCylindricalCoords( c.radius, c.theta, c.y );

	},

	setFromCylindricalCoords: function ( radius, theta, y ) {

		this.x = radius * Math.sin( theta );
		this.y = y;
		this.z = radius * Math.cos( theta );

		return this;

	},
```

## その他数学メソッド

`Vector3`では利便性のためにJavaScriptの[Math](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Math)内のメソッドをベクトルの各要素に適用するようなメソッドが存在しています。

```js
	min: function ( v ) {

		this.x = Math.min( this.x, v.x );
		this.y = Math.min( this.y, v.y );
		this.z = Math.min( this.z, v.z );

		return this;

	},

	max: function ( v ) {

		this.x = Math.max( this.x, v.x );
		this.y = Math.max( this.y, v.y );
		this.z = Math.max( this.z, v.z );

		return this;

	},

	floor: function () {

		this.x = Math.floor( this.x );
		this.y = Math.floor( this.y );
		this.z = Math.floor( this.z );

		return this;

	},

	ceil: function () {

		this.x = Math.ceil( this.x );
		this.y = Math.ceil( this.y );
		this.z = Math.ceil( this.z );

		return this;

	},

	round: function () {

		this.x = Math.round( this.x );
		this.y = Math.round( this.y );
		this.z = Math.round( this.z );

		return this;

	},

	roundToZero: function () {

		this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
		this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
		this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z );

		return this;

	},
```

## 終わりに

ベクトル演算についてまとめながら、three.jsの`Vector3`クラスを見てきました。`Vector3`クラスの一部のメソッドには今回は触れていません。個人的にはすべてのメソッドがインスタンスメソッドとして定義されており、クラスメソッドがないのが特徴的に感じました。