【JavaScript】２次元ベクトルをクラスで作る

# 前書き

前回投稿した[【数学】知ると出来る事が広がる ベクトル 10の知識](https://qiita.com/Nekonecode/items/ff70c764e1ae3bac21bd)
の続きの記事になります。

前回はベクトルの基本的な解説をしました。
今回は実際にJavaScriptを使ってベクトルをクラスとして作るという内容です。

ベクトルクラスはそこら中にありふれており
この記事の作り方が正解というものでもありませんが
１つの参考例として記事にしています。

最終的なコードは[github](https://github.com/kurodakazumichi/teaching_materials/tree/master/pg_and_math/Vector2)にも上げてあります。

また動画も用意しているので動画派の方はこちらの[動画](https://youtu.be/llcSV-gjfaY)をどうぞ

# 概要
- ２次元ベクトルなのでクラス名は**Vector2**とします。
- ベクトルの足し算、引き算、実数倍、正規化、内積、外積など
前回の記事で解説した内容を定義していきます。

# クラス定義
```js
// x成分とy成分を持つ2次元ベクトル
class Vector2 {
}
```
このクラスの中に各種定義を追加していきます。

# コンストラクタ

```js
  constructor(x = 0, y = 0) {
    this.x = x;
    this.y = y;
  }
```
- xとyの２つのメンバ変数を持ちます。

# xとyを設定するメソッド
```js
  // xとyをセットする
  set(x, y) {
    this.x = x;
    this.y = y;
    return this;
  }
```
引数にもらった`x`と`y`を代入するだけですが
メソッドチェインできるように`return this`しときます。

# ベクトルの複製
```js
  clone() {
    return new Vector2(this.x, this.y);
  }
```
自身と同じ内容のベクトルを生成して返すだけです。
後ほど使います。

# ベクトルの加法(足し算)
```js
  add(v) {
    this.x += v.x;
    this.y += v.y;
    return this;
  }
```

正直オペレーターのオーバーロードを使いたいですが
JavaScriptにはオペレーターのオーバーロード機能はないので普通にメソッドで定義します。

引数でもらったベクトルの`x`と`y`を自身に加算するだけで完成です。
メソッドチェインのために`return this;`しておきます。

# ベクトルの減法(引き算)
```js
  sub(v) {
    this.x -= v.x;
    this.y -= v.y;
    return this;
  }
```
引き算は逆ベクトルの足し算で表現できますが
逆ベクトル作ってから足すよりコストが安いので定義しておきます。

引数にもらった`x`と`y`を自身から引くだけで完成です。
メソッドチェインしたいので...以下略

# ベクトルの実数倍
```js
  times(num) {
    this.x *= num;
    this.y *= num;
    return this;
  }
```

ベクトルを**何倍にするか**を引数(`num`)にもらって、`x`と`y`を`num`倍すれば完成です。

# 逆ベクトル
```js
  get inverse() {
    return this.clone().times(-1);
  }
```
計算によって導出できる値であり、副作用がない場合はプロパティとして定義していきます。
処理は逆ベクトルは`x`と`y`の符号を反転させるだけです。

逆ベクトルを求めた際に`x`と`y`の内容は変えたくないので
`clone()`で複製したベクトルを`-1`倍します。

# ベクトルの大きさ
```js
  get magnitude() {
    const { x, y } = this;
    return Math.sqrt(x**2 + y**2);
  }
```
$magnitude = \sqrt{x^2+y^2}$
ベクトルの大きさはピタゴラスの定理にて導出します。


# ベクトルの正規化

```js
  get normalized() {
    const { x, y, magnitude } = this;
    return new Vector2(x/magnitude, y/magnitude);
  }
```

ベクトルの正規化は`x`と`y`をベクトルの大きさで割るだけなのでこれで完成です。

正規化前のベクトルを$\vec{v}$とすると

正規化した後のx成分 = $\frac{x}{|\vec{v}|}$
正規化した後のy成分 = $\frac{y}{|\vec{v}|}$



# staticな足し算、引き算、実数倍

```js
  static add(v1, v2) {
    return v1.clone().add(v2);
  }
```
`v1`を複製したものに`v2`を足し、副作用のない足し算を定義しておきます。


```js
// ①インスタンスメソッド：v1の値が変わってしまう
v1.add(v2); 
// ②クラスメソッド：v1とv2の値は変わらない
const v3 = Vector2.add(v1, v2)
```
②が欲しいケースも多々あるため定義しています。

上記の理由から`sub`と`times`のstatic版も定義します。

```js
  static sub(v1, v2) {
    return v1.clone().sub(v2);
  }

  static times(v1, num) {
    return v1.clone().times(num);
  }
```

# ベクトルの内積
```js
  static dot(v1, v2) {
    return (v1.x * v2.x + v1.y * v2.y);
  }
```
内積はそれぞれのx成分とy成分をかけて足す事で求められます。

ベクトルの内積はよく以下の公式で表されますが
$|\vec{v1}||\vec{v2}|\cos(\theta)$

この公式では二つのベクトルのなす角θがわからないと内積を計算できません。
２つのベクトルからθを求めることもできますが、そんなことしなくても内積は求められるので
一般的にこちらの計算方法が使われると思います。

なぜこれで内積が求まるのか？は別のサイトにお任せします。
もしくは後日動画をあげるかもしれません。

# ベクトルの外積
```js
  static cross(v1, v2) {
    return (v1.x * v2.y - v1.y * v2.x);
  }
```
外積は双方のベクトルのx,yをかけて引いたモノになります。

内積と同様、なぜこれで外積が求まるのかは他のサイトにおまかせしますが
外積は`v1`を左に90度回転させたベクトルとの内積と考える事もできるので
なぜこれで計算できるの？という理屈は内積と同じように考える事ができます。

# その他、便利メソッドの定義
ベクトルとしての基本的な振る舞いは以上として
その他、定義しておくと便利なメソッドをいくつか定義しておきます。


# ２点間の距離

```js
  static distance(v1, v2) {
    return Vector2.sub(v1, v2).magnitude;
  }
```
２つのベクトルの差を取り、その差分ベクトルの大きさ = ２点間の距離です。
よく使うのでstaticで定義しておいてもいいと思います。

# ２つのベクトルが平行か？
```js
  static isParallel(v1, v2) {
    return (Vector2.cross(v1, v2) === 0);
  }
```

外積の結果が0であれば２つのベクトルは平行と言えます。

ただプログラムには浮動少数点誤差があるので、判定は0と一致ではなく
`Math.abs(外積) < 0.0001`など
状況に合わせて、緩めに判定するのが現実的です。

# ２つのベクトルが垂直か？

```js
  static isVertical(v1, v2) {
    return (Vector2.dot(v1, v2) === 0);
  }
```

内積の結果が0であれば２つのベクトルは垂直と言えます。

ただプログラムには浮動少数点誤差がうんぬん...略

# よく使うベクトル
ゼロベクトルやxとyが1のベクトルなどよく使うベクトルもstaticで定義しておくと便利です。

```js
  static get zero() {
    return new Vector2(0,0);
  }

  static get one() {
    return new Vector2(1, 1);
  }

  static get right() {
    return new Vector2(1, 0);
  }

  static get left() {
    return new Vector2(-1, 0);
  }

  static get up() {
    return new Vector2(0, 1);
  }

  static get down() {
    return new Vector2(0, -1);
  }
```

これで`Vector2.zero`とすればゼロベクトルが
`Vector2.right`とすれば右向きの単位ベクトルが手に入ります。

基本となるベクトルはあらゆる計算で使う事が多いのでこのように定義しておきます。

# 終わり

以上でベクトルクラスの作成は完了です。
他にも定義しておくと便利なメソッドはありますが
今回はこのくらいにしておきます。

またVector2クラスは実質、xとyの２つの情報しか持っていません。
この2つからこれだけ多くの情報が得られるというのも面白いですね。