Home Reference Source Repository

js/base/Matrix.js

'use strict'

import Vector3 from './Vector3'
// import Vector4 from './Vector4'

/**
 * matrix class
 * @access public
 */
export default class Matrix {
  /**
   * constructor
   * @access public
   * @constructor
   * @param {Matrix|array} matrix - a
   */
  constructor(matrix) {
    if(matrix instanceof Matrix){
      this.copyMatrix(matrix)
    }else if(typeof matrix === 'object') {
      if('length' in matrix && matrix.length >= 16){
        /** @type {float} */ this.m11 = matrix[ 0]
        /** @type {float} */ this.m12 = matrix[ 1]
        /** @type {float} */ this.m13 = matrix[ 2]
        /** @type {float} */ this.m14 = matrix[ 3]
        /** @type {float} */ this.m21 = matrix[ 4]
        /** @type {float} */ this.m22 = matrix[ 5]
        /** @type {float} */ this.m23 = matrix[ 6]
        /** @type {float} */ this.m24 = matrix[ 7]
        /** @type {float} */ this.m31 = matrix[ 8]
        /** @type {float} */ this.m32 = matrix[ 9]
        /** @type {float} */ this.m33 = matrix[10]
        /** @type {float} */ this.m34 = matrix[11]
        /** @type {float} */ this.m41 = matrix[12]
        /** @type {float} */ this.m42 = matrix[13]
        /** @type {float} */ this.m43 = matrix[14]
        /** @type {float} */ this.m44 = matrix[15]
      }
    }else{
      // default
      this.identity()
    }
  }

  clone() {
    return new Matrix(this)
  }

  /**
   * reset matrix
   * @access public
   * @returns {void}
   */
  identity() {
    this.m11 = 1
    this.m12 = 0
    this.m13 = 0
    this.m14 = 0
    this.m21 = 0
    this.m22 = 1
    this.m23 = 0
    this.m24 = 0
    this.m31 = 0
    this.m32 = 0
    this.m33 = 1
    this.m34 = 0
    this.m41 = 0
    this.m42 = 0
    this.m43 = 0
    this.m44 = 1
  }

  /**
   * multiply matrix (src1 x src2), set result to this matrix
   * @access public
   * @param {Matrix} src1 - 
   * @param {Matrix} src2 - 
   * @returns {void}
   */
  multiplyMatrix(src1, src2) {
    const m11 = src1.m11 * src2.m11 + src1.m12 * src2.m21 + src1.m13 * src2.m31 + src1.m14 * src2.m41
    const m12 = src1.m11 * src2.m12 + src1.m12 * src2.m22 + src1.m13 * src2.m32 + src1.m14 * src2.m42
    const m13 = src1.m11 * src2.m13 + src1.m12 * src2.m23 + src1.m13 * src2.m33 + src1.m14 * src2.m43
    const m14 = src1.m11 * src2.m14 + src1.m12 * src2.m24 + src1.m13 * src2.m34 + src1.m14 * src2.m44
    const m21 = src1.m21 * src2.m11 + src1.m22 * src2.m21 + src1.m23 * src2.m31 + src1.m24 * src2.m41
    const m22 = src1.m21 * src2.m12 + src1.m22 * src2.m22 + src1.m23 * src2.m32 + src1.m24 * src2.m42
    const m23 = src1.m21 * src2.m13 + src1.m22 * src2.m23 + src1.m23 * src2.m33 + src1.m24 * src2.m43
    const m24 = src1.m21 * src2.m14 + src1.m22 * src2.m24 + src1.m23 * src2.m34 + src1.m24 * src2.m44
    const m31 = src1.m31 * src2.m11 + src1.m32 * src2.m21 + src1.m33 * src2.m31 + src1.m34 * src2.m41
    const m32 = src1.m31 * src2.m12 + src1.m32 * src2.m22 + src1.m33 * src2.m32 + src1.m34 * src2.m42
    const m33 = src1.m31 * src2.m13 + src1.m32 * src2.m23 + src1.m33 * src2.m33 + src1.m34 * src2.m43
    const m34 = src1.m31 * src2.m14 + src1.m32 * src2.m24 + src1.m33 * src2.m34 + src1.m34 * src2.m44
    const m41 = src1.m41 * src2.m11 + src1.m42 * src2.m21 + src1.m43 * src2.m31 + src1.m44 * src2.m41
    const m42 = src1.m41 * src2.m12 + src1.m42 * src2.m22 + src1.m43 * src2.m32 + src1.m44 * src2.m42
    const m43 = src1.m41 * src2.m13 + src1.m42 * src2.m23 + src1.m43 * src2.m33 + src1.m44 * src2.m43
    const m44 = src1.m41 * src2.m14 + src1.m42 * src2.m24 + src1.m43 * src2.m34 + src1.m44 * src2.m44

    this.m11 = m11
    this.m12 = m12
    this.m13 = m13
    this.m14 = m14
    this.m21 = m21
    this.m22 = m22
    this.m23 = m23
    this.m24 = m24
    this.m31 = m31
    this.m32 = m32
    this.m33 = m33
    this.m34 = m34
    this.m41 = m41
    this.m42 = m42
    this.m43 = m43
    this.m44 = m44
  }

  /**
   * blend 2 matrices.
   * if rate is 0, result is equal to src1.
   * if rate is 1, result is equal to src2.
   * @access public
   * @param {Matrix} src1 -
   * @param {Matrix} src2 -
   * @param {float} rate -
   * @returns {void}
   */
  lerp(src1, src2, rate) {
    this.m11 = src1.m11 + rate * (src1.m11 - src2.m11)
    this.m12 = src1.m12 + rate * (src1.m12 - src2.m12)
    this.m13 = src1.m13 + rate * (src1.m13 - src2.m13)
    this.m14 = src1.m14 + rate * (src1.m14 - src2.m14)
    this.m21 = src1.m21 + rate * (src1.m21 - src2.m21)
    this.m22 = src1.m22 + rate * (src1.m22 - src2.m22)
    this.m23 = src1.m23 + rate * (src1.m23 - src2.m23)
    this.m24 = src1.m24 + rate * (src1.m24 - src2.m24)
    this.m31 = src1.m31 + rate * (src1.m31 - src2.m31)
    this.m32 = src1.m32 + rate * (src1.m32 - src2.m32)
    this.m33 = src1.m33 + rate * (src1.m33 - src2.m33)
    this.m34 = src1.m34 + rate * (src1.m34 - src2.m34)
    this.m41 = src1.m41 + rate * (src1.m41 - src2.m41)
    this.m42 = src1.m42 + rate * (src1.m42 - src2.m42)
    this.m43 = src1.m43 + rate * (src1.m43 - src2.m43)
    this.m44 = src1.m44 + rate * (src1.m44 - src2.m44)
  }

  /**
   * set rotation matrix from quaternion
   * @access public
   * @param {Vector4} quat - Quaternion
   * @returns {void}
   */
  matrixFromQuaternion(quat) {
    const x2 = quat.x * quat.x * 2.0
    const y2 = quat.y * quat.y * 2.0
    const z2 = quat.z * quat.z * 2.0
    const xy = quat.x * quat.y * 2.0
    const yz = quat.y * quat.z * 2.0
    const zx = quat.z * quat.x * 2.0
    const xw = quat.x * quat.w * 2.0
    const yw = quat.y * quat.w * 2.0
    const zw = quat.z * quat.w * 2.0

    this.m11 = 1.0 - y2 - z2
    this.m12 = xy + zw
    this.m13 = zx - yw
    this.m14 = 0.0
    this.m21 = xy - zw
    this.m22 = 1.0 - z2 - x2
    this.m23 = yz + xw
    this.m24 = 0.0
    this.m31 = zx + yw
    this.m32 = yz - xw
    this.m33 = 1.0 - x2 - y2
    this.m34 = 0.0
    this.m41 = 0.0
    this.m42 = 0.0
    this.m43 = 0.0
    this.m44 = 1.0
  }

  /**
   * copy matrix
   * @access public
   * @param {Matrix} src -
   * @returns {void}
   */
  copyMatrix(src) {
    this.m11 = src.m11
    this.m12 = src.m12
    this.m13 = src.m13
    this.m14 = src.m14
    this.m21 = src.m21
    this.m22 = src.m22
    this.m23 = src.m23
    this.m24 = src.m24
    this.m31 = src.m31
    this.m32 = src.m32
    this.m33 = src.m33
    this.m34 = src.m34
    this.m41 = src.m41
    this.m42 = src.m42
    this.m43 = src.m43
    this.m44 = src.m44
  }

  /**
   * inverse matrix
   * @access public
   * @param {Matrix} src - 
   * @returns {void}
   */
  inverseMatrix(src) {
    const temp = new Matrix(src)
    let buf = 0
    let w1 = Math.abs(temp.m11)
    let w2 = Math.abs(temp.m21)
    let w3 = Math.abs(temp.m31)
    let w4 = Math.abs(temp.m41)
    let max = w1 > w2 ? w1 : w2
    if(max < w3) max = w3

    this.identity()
    // 1
    if(max < w4){
      buf = 1.0 / temp.m41
      w1 = temp.m11
      w2 = temp.m12
      w3 = temp.m13
      w4 = temp.m14
      temp.m12 = temp.m42 * buf
      temp.m13 = temp.m43 * buf
      temp.m14 = temp.m44 * buf
      temp.m41 = w1
      temp.m42 = w2
      temp.m43 = w3
      temp.m44 = w4
      this.m11 = 0.0
      this.m14 = buf
      this.m41 = 1.0
      this.m44 = 0.0
    }else if(max === w1){
      buf = 1.0 / temp.m11
      temp.m12 *= buf
      temp.m13 *= buf
      temp.m14 *= buf
      this.m11 = buf
    }else if(max === w2){
      buf = 1.0 / temp.m21
      w1 = temp.m11
      w2 = temp.m12
      w3 = temp.m13
      w4 = temp.m14
      temp.m12 = temp.m22 * buf
      temp.m13 = temp.m23 * buf
      temp.m14 = temp.m24 * buf
      temp.m21 = w1
      temp.m22 = w2
      temp.m23 = w3
      temp.m24 = w4
      this.m11 = 0.0
      this.m12 = buf
      this.m21 = 1.0
      this.m22 = 0.0
    }else{
      buf = 1.0 / temp.m31
      w1 = temp.m11
      w2 = temp.m12
      w3 = temp.m13
      w4 = temp.m14
      temp.m12 = temp.m32 * buf
      temp.m13 = temp.m33 * buf
      temp.m14 = temp.m34 * buf
      temp.m31 = w1
      temp.m32 = w2
      temp.m33 = w3
      temp.m34 = w4
      this.m11 = 0.0
      this.m13 = buf
      this.m31 = 1.0
      this.m33 = 0.0
    }

    buf = temp.m21
    temp.m22 -= temp.m12 * buf
    temp.m23 -= temp.m13 * buf
    temp.m24 -= temp.m14 * buf
    this.m21 -= this.m11 * buf
    this.m22 -= this.m12 * buf
    this.m23 -= this.m13 * buf
    this.m24 -= this.m14 * buf

    buf = temp.m31
    temp.m32 -= temp.m12 * buf
    temp.m33 -= temp.m13 * buf
    temp.m34 -= temp.m14 * buf
    this.m31 -= this.m11 * buf
    this.m32 -= this.m12 * buf
    this.m33 -= this.m13 * buf
    this.m34 -= this.m14 * buf

    buf = temp.m41
    temp.m42 -= temp.m12 * buf
    temp.m43 -= temp.m13 * buf
    temp.m44 -= temp.m14 * buf
    this.m41 -= this.m11 * buf
    this.m42 -= this.m12 * buf
    this.m43 -= this.m13 * buf
    this.m44 -= this.m14 * buf

    // 2
    w2 = Math.abs(temp.m22)
    w3 = Math.abs(temp.m32)
    w4 = Math.abs(temp.m42)
    max = w2 > w3 ? w2 : w3
    if(max < w4){
      buf = 1.0 / temp.m42
      w2 = temp.m22
      w3 = temp.m23
      w4 = temp.m24
      temp.m23 = temp.m43 * buf
      temp.m24 = temp.m44 * buf
      temp.m42 = w2
      temp.m43 = w3
      temp.m44 = w4
      w1 = this.m21
      w2 = this.m22
      w3 = this.m23
      w4 = this.m24
      this.m21 = this.m41 * buf
      this.m22 = this.m42 * buf
      this.m23 = this.m43 * buf
      this.m24 = this.m44 * buf
      this.m41 = w1
      this.m42 = w2
      this.m43 = w3
      this.m44 = w4
    }else if(w2 > w3){
      buf = 1.0 / temp.m22
      temp.m23 *= buf
      temp.m24 *= buf
      this.m21 *= buf
      this.m22 *= buf
      this.m23 *= buf
      this.m24 *= buf
    }else{
      buf = 1.0 / temp.m32
      w2 = temp.m22
      w3 = temp.m23
      w4 = temp.m24
      temp.m23 = temp.m33 * buf
      temp.m24 = temp.m34 * buf
      temp.m32 = w2
      temp.m33 = w3
      temp.m34 = w4
      w1 = this.m21
      w2 = this.m22
      w3 = this.m23
      w4 = this.m24
      this.m21 = this.m31 * buf
      this.m22 = this.m32 * buf
      this.m23 = this.m33 * buf
      this.m24 = this.m34 * buf
      this.m31 = w1
      this.m32 = w2
      this.m33 = w3
      this.m34 = w4
    }

    buf = temp.m12
    temp.m13 -= temp.m23 * buf
    temp.m14 -= temp.m24 * buf
    this.m11 -= this.m21 * buf
    this.m12 -= this.m22 * buf
    this.m13 -= this.m23 * buf
    this.m14 -= this.m24 * buf

    buf = temp.m32
    temp.m33 -= temp.m23 * buf
    temp.m34 -= temp.m24 * buf
    this.m31 -= this.m21 * buf
    this.m32 -= this.m22 * buf
    this.m33 -= this.m23 * buf
    this.m34 -= this.m24 * buf

    buf = temp.m42
    temp.m43 -= temp.m23 * buf
    temp.m44 -= temp.m24 * buf
    this.m41 -= this.m21 * buf
    this.m42 -= this.m22 * buf
    this.m43 -= this.m23 * buf
    this.m44 -= this.m24 * buf

    // 3
    if(Math.abs(temp.m33) > Math.abs(temp.m43)){
      buf = 1.0 / temp.m33
      temp.m34 *= buf
      this.m31 *= buf
      this.m32 *= buf
      this.m33 *= buf
      this.m34 *= buf
    }else{
      buf = 1.0 / temp.m43
      w3 = temp.m33
      w4 = temp.m34
      temp.m34 = temp.m44 * buf
      temp.m43 = w3
      temp.m44 = w4
      w1 = this.m31
      w2 = this.m32
      w3 = this.m33
      w4 = this.m34
      this.m31 = this.m41 * buf
      this.m32 = this.m42 * buf
      this.m33 = this.m43 * buf
      this.m34 = this.m44 * buf
      this.m41 = w1
      this.m42 = w2
      this.m43 = w3
      this.m44 = w4
    }
    buf = temp.m13
    temp.m14 -= temp.m34 * buf
    this.m11 -= this.m31 * buf
    this.m12 -= this.m32 * buf
    this.m13 -= this.m33 * buf
    this.m14 -= this.m34 * buf

    buf = temp.m23
    temp.m24 -= temp.m34 * buf
    this.m21 -= this.m31 * buf
    this.m22 -= this.m32 * buf
    this.m23 -= this.m33 * buf
    this.m24 -= this.m34 * buf

    buf = temp.m43
    temp.m44 -= temp.m34 * buf
    this.m41 -= this.m31 * buf
    this.m42 -= this.m32 * buf
    this.m43 -= this.m33 * buf
    this.m44 -= this.m34 * buf

    // 4
    buf = 1.0 / temp.m44
    this.m41 *= buf
    this.m42 *= buf
    this.m43 *= buf
    this.m44 *= buf

    buf = temp.m14
    this.m11 -= this.m41 * buf
    this.m12 -= this.m42 * buf
    this.m13 -= this.m43 * buf
    this.m14 -= this.m44 * buf

    buf = temp.m24
    this.m21 -= this.m41 * buf
    this.m22 -= this.m42 * buf
    this.m23 -= this.m43 * buf
    this.m24 -= this.m44 * buf

    buf = temp.m34
    this.m31 -= this.m41 * buf
    this.m32 -= this.m42 * buf
    this.m33 -= this.m43 * buf
    this.m34 -= this.m44 * buf
  }

  /**
   * transpose matrix
   * @access public
   * @param {Matrix} src -
   * @returns {void}
   */
  transposeMatrix(src) {
    this.m11 = src.m11
    this.m12 = src.m21
    this.m13 = src.m31
    this.m14 = src.m41
    this.m21 = src.m12
    this.m22 = src.m22
    this.m23 = src.m32
    this.m24 = src.m42
    this.m31 = src.m13
    this.m32 = src.m23
    this.m33 = src.m33
    this.m34 = src.m43
    this.m41 = src.m14
    this.m42 = src.m24
    this.m43 = src.m34
    this.m44 = src.m44
  }

  /**
   * scale matrix
   * @access public
   * @param {Matrix} mat -
   * @param {float} x -
   * @param {float} y -
   * @param {float} z -
   * @returns {void}
   */
  scale(mat, x, y, z) {
    const r = new Matrix()
    r.identity()

    if(!(mat instanceof Object) && z === undefined){
      r.m11 = mat
      r.m22 = x
      r.m33 = y

      this.multiplyMatrix(this, r)
    }else{
      r.m11 = x
      r.m22 = y
      r.m33 = z

      this.multiplyMatrix(mat, r)
    }
  }

  /**
   * translate matrix
   * @access public
   * @param {Matrix} mat -
   * @param {float} x -
   * @param {float} y -
   * @param {float} z -
   * @returns {void}
   */
  translate(mat, x, y, z) {
    const r = new Matrix()
    r.identity()
    r.m14 = x
    r.m24 = y
    r.m34 = z

    this.multiplyMatrix(mat, r)
  }

  /**
   * rotate matrix
   * @access public
   * @param {Matrix} mat - 
   * @param {float} angle -
   * @param {float} x -
   * @param {float} y -
   * @param {float} z - 
   * @returns {void}
   */
  rotate(mat, angle, x, y, z) {
    const c = Math.cos(angle)
    const s = Math.sin(angle)
    const v = new Vector3(x, y, z)
    const r = new Matrix()

    v.normalize()
    const nx = v.x
    const ny = v.y
    const nz = v.z

    r.m11 = nx * nx * (1.0-c) + c
    r.m12 = nx * ny * (1.0-c) - nz * s
    r.m13 = nx * nz * (1.0-c) + ny * s
    r.m14 = 0.0
    r.m21 = ny * nx * (1.0-c) + nz * s
    r.m22 = ny * ny * (1.0-c) + c
    r.m23 = ny * nz * (1.0-c) - nx * s
    r.m24 = 0.0
    r.m31 = nz * nx * (1.0-c) - ny * s
    r.m32 = nz * ny * (1.0-c) + nx * s
    r.m33 = nz * nz * (1.0-c) + c
    r.m34 = 0.0
    r.m41 = 0.0
    r.m42 = 0.0
    r.m43 = 0.0
    r.m44 = 1.0

    this.multiplyMatrix(mat, r)
  }

  /**
   * get Float32Array format value of matrix for WebGL
   * @access public
   * @returns {Float32Array} matrix value
   */
  getWebGLFloatArray() {
    return new Float32Array([
      this.m11, this.m12, this.m13, this.m14,
      this.m21, this.m22, this.m23, this.m24,
      this.m31, this.m32, this.m33, this.m34,
      this.m41, this.m42, this.m43, this.m44
    ])
  }

  /**
   * get Float32Array format value of transposed matrix for WebGL
   * @access public
   * @returns {Float32Array} transposed matrix value
   */
  getWebGLFloatArrayTransposed() {
    return new Float32Array([
      this.m11, this.m21, this.m31, this.m41,
      this.m12, this.m22, this.m32, this.m42,
      this.m13, this.m23, this.m33, this.m43,
      this.m14, this.m24, this.m34, this.m44
    ])
  }

  /**
   * get array format value of matrix for WebGL
   * @access public
   * @returns {array} matrix value
   */
  getArray() {
    return [
      this.m11, this.m12, this.m13, this.m14,
      this.m21, this.m22, this.m23, this.m24,
      this.m31, this.m32, this.m33, this.m34,
      this.m41, this.m42, this.m43, this.m44
    ]
  }

  /**
   * show matrix value to console for debug
   * @access public
   * @returns {void}
   */
  showMatrix() {
    /*
    console.log('matrix:<br />\n'
       + this.m11 + ' ' + this.m12 + ' ' + this.m13 + ' ' + this.m14 + '<br />\n'
       + this.m21 + ' ' + this.m22 + ' ' + this.m23 + ' ' + this.m24 + '<br />\n'
       + this.m31 + ' ' + this.m32 + ' ' + this.m33 + ' ' + this.m34 + '<br />\n'
       + this.m41 + ' ' + this.m42 + ' ' + this.m43 + ' ' + this.m44 + '<br />\n'
    )
    */
  }
}