Home Reference Source Repository

js/base/Vector4.js

'use strict'

/**
 * Vector4 class
 * @access public
 */
export default class Vector4 {
  /**
   * constructor
   * @access public
   * @param {float} x -
   * @param {float} y -
   * @param {float} z -
   * @param {float} w -
   * @constructor
   */
  constructor(x = 0.0, y = 0.0, z = 0.0, w = 0.0) {
    this.setValue(x, y, z, w)
  }

  clone() {
    return new Vector4(this.x, this.y, this.z, this.w)
  }

  setValue(x, y = 0.0, z = 0.0, w = 0.0) {
    if((x instanceof Vector4) || (x instanceof Object && x.w !== null)){
      this.x = x.x
      this.y = x.y
      this.z = x.z
      this.w = x.w
    }else{
      this.x = x || 0.0
      this.y = y || 0.0
      this.z = z || 0.0
      this.w = w || 0.0
    }
  }

  lerp(src1, src2, rate) {
    this.x = src1.x + rate * (src2.x - src1.x)
    this.y = src1.y + rate * (src2.y - src1.y)
    this.z = src1.z + rate * (src2.z - src1.z)
    this.w = src1.w + rate * (src2.w - src1.w)
  }

  slerp(src1, src2, rate) {
    const qr = src1.x * src2.x + src1.y * src2.y + src1.z * src2.z + src1.w * src2.w

    if(qr < 0){
      this.x = src1.x - (src1.x + src2.x) * rate
      this.y = src1.y - (src1.y + src2.y) * rate
      this.z = src1.z - (src1.z + src2.z) * rate
      this.w = src1.w - (src1.w + src2.w) * rate
    }else{
      this.x = src1.x + (src2.x - src1.x) * rate
      this.y = src1.y + (src2.y - src1.y) * rate
      this.z = src1.z + (src2.z - src1.z) * rate
      this.w = src1.w + (src2.w - src1.w) * rate
    }
    this.normalize()
  }

  ln(src) {
    const v = new Vector4()
    v.normalize(src)

    const n = Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z)
    if(n === 0){
      this.x = 0
      this.y = 0
      this.z = 0
      this.w = 0
      return
    }
    const theta = Math.atan2(n, v.w) / n

    this.x = theta * v.x
    this.y = theta * v.y
    this.z = theta * v.z
    this.w = 0
  }

  exp(src) {
    const n = Math.sqrt(src.x * src.x + src.y * src.y + src.z * src.z)
    
    if(n > 0.0){
      const sinn = Math.sin(n)
      this.x = sinn * src.x / n
      this.y = sinn * src.y / n
      this.z = sinn * src.z / n
      this.w = Math.cos(n)
    }else{
      this.x = 0.0
      this.y = 0.0
      this.z = 0.0
      this.w = 1.0
    }
  }

  normalize(src = this) {
    const square = 1.0 / Math.sqrt(src.x * src.x + src.y * src.y + src.z * src.z + src.w * src.w)

    this.x = src.x * square
    this.y = src.y * square
    this.z = src.z * square
    this.w = src.w * square
  }

  createAxis(axis, rotAngle) {
    if(Math.abs(rotAngle) < 0.0001){
      this.x = 0.0
      this.y = 0.0
      this.z = 0.0
      this.w = 1.0
    }else{
      const angle = rotAngle * 0.5
      const temp = Math.sin(angle)

      this.x = axis.x * temp
      this.y = axis.y * temp
      this.z = axis.z * temp
      this.w = Math.cos(angle)
    }
  }

  cross(src1, src2) {
    const x = src1.w * src2.x + src1.x * src2.w + src1.y * src2.z - src1.z * src2.y
    const y = src1.w * src2.y - src1.x * src2.z + src1.y * src2.w + src1.z * src2.x
    const z = src1.w * src2.z + src1.x * src2.y - src1.y * src2.x + src1.z * src2.w
    const w = src1.w * src2.w - src1.x * src2.x - src1.y * src2.y - src1.z * src2.z

    this.x = x
    this.y = y
    this.z = z
    this.w = w
  }

  eulerToQuaternion(eulerAngle) {
    const xRadian = eulerAngle.x * 0.5
    const yRadian = eulerAngle.y * 0.5
    const zRadian = eulerAngle.z * 0.5
    const sinX = Math.sin(xRadian)
    const cosX = Math.cos(xRadian)
    const sinY = Math.sin(yRadian)
    const cosY = Math.cos(yRadian)
    const sinZ = Math.sin(zRadian)
    const cosZ = Math.cos(zRadian)

    this.x = sinX * cosY * cosZ - cosX * sinY * sinZ
    this.y = cosX * sinY * cosZ + sinX * cosY * sinZ
    this.z = cosX * cosY * sinZ - sinX * sinY * cosZ
    this.w = cosX * cosY * cosZ + sinX * sinY * sinZ
  }

  transform(vec, matrix){
    const rx = vec.x * matrix.m11 + vec.y * matrix.m21 + vec.z * matrix.m31 + vec.w * matrix.m41
    const ry = vec.x * matrix.m12 + vec.y * matrix.m22 + vec.z * matrix.m32 + vec.w * matrix.m42
    const rz = vec.x * matrix.m13 + vec.y * matrix.m23 + vec.z * matrix.m33 + vec.w * matrix.m43
    const rw = vec.x * matrix.m14 + vec.y * matrix.m24 + vec.z * matrix.m34 + vec.w * matrix.m44

    this.x = rx
    this.y = ry
    this.z = rz
    this.w = rw
  }

  quaternionFromMatrix(mat) {
    const mx = mat.m11 - mat.m22 - mat.m33
    const my = mat.m22 - mat.m11 - mat.m33
    const mz = mat.m33 - mat.m11 - mat.m22
    const mw = mat.m11 + mat.m22 + mat.m33

    let biggestIndex = 0
    let mval = mw
    if(mx > mval) {
      mval = mx
      biggestIndex = 1
    }
    if(my > mval) {
      mval = my
      biggestIndex = 2
    }
    if(mz > mval) {
      mval = mz
      biggestIndex = 3
    }

    const biggestVal = Math.sqrt(mval + 1.0) * 0.5
    const mult = 0.25 / biggestVal

    switch(biggestIndex) {
      case 0:
        this.x = (mat.m23 - mat.m32) * mult
        this.y = (mat.m31 - mat.m13) * mult
        this.z = (mat.m12 - mat.m21) * mult
        this.w = biggestVal
        break
      case 1:
        this.x = biggestVal
        this.y = (mat.m12 + mat.m21) * mult
        this.z = (mat.m31 + mat.m13) * mult
        this.w = (mat.m23 - mat.m32) * mult
        break
      case 2:
        this.x = (mat.m12 + mat.m21) * mult
        this.y = biggestVal
        this.z = (mat.m23 + mat.m32) * mult
        this.w = (mat.m31 - mat.m13) * mult
        break
      case 3:
        this.x = (mat.m31 * mat.m13) * mult
        this.y = (mat.m23 * mat.m32) * mult
        this.z = biggestVal
        this.w = (mat.m12 - mat.m21) * mult
        break
      default:
        break
    }
  }

  getWebGLFloatArray() {
    return new Float32Array([
      this.x, this.y, this.z, this.w
    ])
  }
}