TypeScriptで簡単な3次元ベクトルクラスを作ってみる


色々機能足りないけどとりあえず作ってみた。

Vec3クラス

class Vec3 {
  public x: number
  public y: number
  public z: number
  constructor(x: number, y: number, z: number) {
    this.x = x;
    this.y = y;
    this.z = z;
  }

メソッド

JavaScriptのMathみたいに作りたかったので全部staticにしてみた。

// 正規化
  static normalized(vec3: Vec3): Vec3 {
    const vec_length = Vec3.norm(vec3);
    return new Vec3(vec3.x / vec_length, vec3.y / vec_length, vec3.z / vec_length);
  }

  // 加算
  static add(a: Vec3, b: Vec3): Vec3 {
    return new Vec3(a.x + b.x, a.y + b.y, a.z + b.z);
  }

  // 減算
  static minus(a: Vec3, b: Vec3): Vec3 {
    return new Vec3(a.x - b.x, a.y - b.y, a.z - b.z);
  }

  // スカラー倍
  static multiple_scalar(vec3: Vec3, n: number): Vec3 {
    return new Vec3(vec3.x * n, vec3.y * n, vec3.z * n);
  }

  // スカラー割
  static divide_scalar(vec3: Vec3, n: number): Vec3 {
    return new Vec3(vec3.x / n, vec3.y / n, vec3.z / n);
  }

  // ノルム
  static norm(vec3: Vec3): number {
    return Math.sqrt(vec3.x ** 2 + vec3.y ** 2 + vec3.z ** 2);
  }

  // 内積
  static dot(a: Vec3, b: Vec3): number {
    return a.x * b.x + a.y * b.y + a.z * b.z;
  }

  // 外積
  static outer(a: Vec3, b: Vec3): Vec3{
    return new Vec3(a.y * b.z - a.z * b.y, -(a.x * b.z - a.z * b.x), a.x * b.y - a.y * b.x);
  }

  // 射影
  static project(a: Vec3, b: Vec3): Vec3 {
    const dot_a_b = Vec3.dot(a, b);
    const b_length_pow = Vec3.norm(b) ** 2;
    return Vec3.divide_scalar(Vec3.multiple_scalar(b, dot_a_b), b_length_pow);
  }

その他入れてみたメソッド

equalメソッド
2つのベクトルの要素が全て同じ場合true、それ以外ならfalseを返す。
0.0000001未満までなら誤差として同じにする

static equal(a: Vec3, b: Vec3): boolean{
      return (a.x - b.x) * Math.pow(10, 7) < 1
        && (a.y - b.y) * Math.pow(10, 7) < 1
        && (a.z - b.z) * Math.pow(10, 7) < 1;
    }

コード全体

export class Vec3 {
    public x: number
    public y: number
    public z: number
    constructor(x: number, y: number, z: number) {
      this.x = x;
      this.y = y;
      this.z = z;
    }
  
    // 正規化
    static normalized(vec3: Vec3): Vec3 {
      const vec_length = Vec3.norm(vec3);
      return new Vec3(vec3.x / vec_length, vec3.y / vec_length, vec3.z / vec_length);
    }
  
    // イコール
    static equal(a: Vec3, b: Vec3): boolean{
      // aとbの差が0.0000001以下の時は同じとする
      return (a.x - b.x) * Math.pow(10, 7) < 1
        && (a.y - b.y) * Math.pow(10, 7) < 1
        && (a.z - b.z) * Math.pow(10, 7) < 1;
    }
  
    // 加算
    static add(a: Vec3, b: Vec3): Vec3 {
      return new Vec3(a.x + b.x, a.y + b.y, a.z + b.z);
    }
  
    // 減算
    static minus(a: Vec3, b: Vec3): Vec3 {
      return new Vec3(a.x - b.x, a.y - b.y, a.z - b.z);
    }
  
    // スカラー倍
    static multiple_scalar(vec3: Vec3, n: number): Vec3 {
      return new Vec3(vec3.x * n, vec3.y * n, vec3.z * n);
    }
  
    // スカラー割
    static divide_scalar(vec3: Vec3, n: number): Vec3 {
      return new Vec3(vec3.x / n, vec3.y / n, vec3.z / n);
    }
  
    // ノルム
    static norm(vec3: Vec3): number {
      return Math.sqrt(vec3.x ** 2 + vec3.y ** 2 + vec3.z ** 2);
    }
  
    // 内積
    static dot(a: Vec3, b: Vec3): number {
      return a.x * b.x + a.y * b.y + a.z * b.z;
    }
  
    // 外積
    static outer(a: Vec3, b: Vec3): Vec3{
      return new Vec3(a.y * b.z - a.z * b.y, -(a.x * b.z - a.z * b.x), a.x * b.y - a.y * b.x);
    }
  
    // 射影
    static project(a: Vec3, b: Vec3): Vec3 {
      const dot_a_b = Vec3.dot(a, b);
      const b_length_pow = Vec3.norm(b) ** 2;
      return Vec3.divide_scalar(Vec3.multiple_scalar(b, dot_a_b), b_length_pow);
    }
  }

参考図書

3Dグラフィックのための数学

https://www.kohgakusha.co.jp/books/detail/978-4-7775-1359-8