Home Reference Source Repository

js/SceneKit/SCNMatrix4.js

'use strict'

import SCNVector3 from './SCNVector3'
import SCNVector4 from './SCNVector4'
import _InstanceOf from '../util/_InstanceOf'


const _epsilon = 0.0000001

/**
 * A representation of a 4 x 4 matrix.
 * @access public
 * @see https://developer.apple.com/documentation/scenekit/scnmatrix4
 */
export default class SCNMatrix4 {

  // Initializers

  /**
   * 
   * @access public
   * @construtor
   * @param {number[][]} [m = null] - 
   * @see https://developer.apple.com/documentation/quartzcore/catransform3d/1524036-init
   */
  constructor(m = null) {
    // Instance Properties

    /** @type {number} */
    this.m11 = 0
    /** @type {number} */
    this.m12 = 0
    /** @type {number} */
    this.m13 = 0
    /** @type {number} */
    this.m14 = 0
    /** @type {number} */
    this.m21 = 0
    /** @type {number} */
    this.m22 = 0
    /** @type {number} */
    this.m23 = 0
    /** @type {number} */
    this.m24 = 0
    /** @type {number} */
    this.m31 = 0
    /** @type {number} */
    this.m32 = 0
    /** @type {number} */
    this.m33 = 0
    /** @type {number} */
    this.m34 = 0
    /** @type {number} */
    this.m41 = 0
    /** @type {number} */
    this.m42 = 0
    /** @type {number} */
    this.m43 = 0
    /** @type {number} */
    this.m44 = 0

    if(_InstanceOf(m, SCNMatrix4)){
      this.m11 = m.m11
      this.m12 = m.m12
      this.m13 = m.m13
      this.m14 = m.m14
      this.m21 = m.m21
      this.m22 = m.m22
      this.m23 = m.m23
      this.m24 = m.m24
      this.m31 = m.m31
      this.m32 = m.m32
      this.m33 = m.m33
      this.m34 = m.m34
      this.m41 = m.m41
      this.m42 = m.m42
      this.m43 = m.m43
      this.m44 = m.m44
    }else if(arguments.length >= 16){
      this.m11 = arguments[0]
      this.m12 = arguments[1]
      this.m13 = arguments[2]
      this.m14 = arguments[3]
      this.m21 = arguments[4]
      this.m22 = arguments[5]
      this.m23 = arguments[6]
      this.m24 = arguments[7]
      this.m31 = arguments[8]
      this.m32 = arguments[9]
      this.m33 = arguments[10]
      this.m34 = arguments[11]
      this.m41 = arguments[12]
      this.m42 = arguments[13]
      this.m43 = arguments[14]
      this.m44 = arguments[15]
    }else if(m !== null){
      // TODO: type check
      this.m11 = m[0][0]
      this.m12 = m[0][1]
      this.m13 = m[0][2]
      this.m14 = m[0][3]
      this.m21 = m[1][0]
      this.m22 = m[1][1]
      this.m23 = m[1][2]
      this.m24 = m[1][3]
      this.m31 = m[2][0]
      this.m32 = m[2][1]
      this.m33 = m[2][2]
      this.m34 = m[2][3]
      this.m41 = m[3][0]
      this.m42 = m[3][1]
      this.m43 = m[3][2]
      this.m44 = m[3][3]
    }
  }

  /**
   * @access private
   * @param {Buffer} data -
   * @param {number} [offset = 0] -
   * @param {boolean} [bigEndian = false] -
   * @returns {SCNMatrix4} -
   */
  static _initWithData(data, offset = 0, bigEndian = false) {
    const instance = new SCNMatrix4()
    if(bigEndian){
      instance.m11 = data.readFloatBE(offset + 0)
      instance.m12 = data.readFloatBE(offset + 4)
      instance.m13 = data.readFloatBE(offset + 8)
      instance.m14 = data.readFloatBE(offset + 12)
      instance.m21 = data.readFloatBE(offset + 16)
      instance.m22 = data.readFloatBE(offset + 20)
      instance.m23 = data.readFloatBE(offset + 24)
      instance.m24 = data.readFloatBE(offset + 28)
      instance.m31 = data.readFloatBE(offset + 32)
      instance.m32 = data.readFloatBE(offset + 36)
      instance.m33 = data.readFloatBE(offset + 40)
      instance.m34 = data.readFloatBE(offset + 44)
      instance.m41 = data.readFloatBE(offset + 48)
      instance.m42 = data.readFloatBE(offset + 52)
      instance.m43 = data.readFloatBE(offset + 56)
      instance.m44 = data.readFloatBE(offset + 60)
    }else{
      instance.m11 = data.readFloatLE(offset + 0)
      instance.m12 = data.readFloatLE(offset + 4)
      instance.m13 = data.readFloatLE(offset + 8)
      instance.m14 = data.readFloatLE(offset + 12)
      instance.m21 = data.readFloatLE(offset + 16)
      instance.m22 = data.readFloatLE(offset + 20)
      instance.m23 = data.readFloatLE(offset + 24)
      instance.m24 = data.readFloatLE(offset + 28)
      instance.m31 = data.readFloatLE(offset + 32)
      instance.m32 = data.readFloatLE(offset + 36)
      instance.m33 = data.readFloatLE(offset + 40)
      instance.m34 = data.readFloatLE(offset + 44)
      instance.m41 = data.readFloatLE(offset + 48)
      instance.m42 = data.readFloatLE(offset + 52)
      instance.m43 = data.readFloatLE(offset + 56)
      instance.m44 = data.readFloatLE(offset + 60)
    }
    return instance
  }

  _copy() {
    return new SCNMatrix4(this)
  }

  // extensions

  /**
   * @access public
   * @param {SCNMatrix4} m -
   * @returns {SCNMatrix4} - 
   */
  add(m) {
    const r = new SCNMatrix4()
    r.m11 = this.m11 + m.m11
    r.m12 = this.m12 + m.m12
    r.m13 = this.m13 + m.m13
    r.m14 = this.m14 + m.m14
    r.m21 = this.m21 + m.m21
    r.m22 = this.m22 + m.m22
    r.m23 = this.m23 + m.m23
    r.m24 = this.m24 + m.m24
    r.m31 = this.m31 + m.m31
    r.m32 = this.m32 + m.m32
    r.m33 = this.m33 + m.m33
    r.m34 = this.m34 + m.m34
    r.m41 = this.m41 + m.m41
    r.m42 = this.m42 + m.m42
    r.m43 = this.m43 + m.m43
    r.m44 = this.m44 + m.m44
    return r
  }

  /**
   * @access public
   * @param {number} t -
   * @returns {SCNMatrix4} - 
   */
  mul(t) {
    const r = new SCNMatrix4()
    r.m11 = this.m11 * t
    r.m12 = this.mj2 * t
    r.m13 = this.m13 * t
    r.m14 = this.m14 * t
    r.m21 = this.m21 * t
    r.m22 = this.m22 * t
    r.m23 = this.m23 * t
    r.m24 = this.m24 * t
    r.m31 = this.m31 * t
    r.m32 = this.m32 * t
    r.m33 = this.m33 * t
    r.m34 = this.m34 * t
    r.m41 = this.m41 * t
    r.m42 = this.m42 * t
    r.m43 = this.m43 * t
    r.m44 = this.m44 * t
    return r
  }

  /**
   * @access public
   * @param {SCNMatrix4} m -
   * @returns {SCNMatrix4} - 
   */
  mult(m) {
    const r = new SCNMatrix4()
    r.m11 = this.m11 * m.m11 + this.m12 * m.m21 + this.m13 * m.m31 + this.m14 * m.m41
    r.m12 = this.m11 * m.m12 + this.m12 * m.m22 + this.m13 * m.m32 + this.m14 * m.m42
    r.m13 = this.m11 * m.m13 + this.m12 * m.m23 + this.m13 * m.m33 + this.m14 * m.m43
    r.m14 = this.m11 * m.m14 + this.m12 * m.m24 + this.m13 * m.m34 + this.m14 * m.m44
    r.m21 = this.m21 * m.m11 + this.m22 * m.m21 + this.m23 * m.m31 + this.m24 * m.m41
    r.m22 = this.m21 * m.m12 + this.m22 * m.m22 + this.m23 * m.m32 + this.m24 * m.m42
    r.m23 = this.m21 * m.m13 + this.m22 * m.m23 + this.m23 * m.m33 + this.m24 * m.m43
    r.m24 = this.m21 * m.m14 + this.m22 * m.m24 + this.m23 * m.m34 + this.m24 * m.m44
    r.m31 = this.m31 * m.m11 + this.m32 * m.m21 + this.m33 * m.m31 + this.m34 * m.m41
    r.m32 = this.m31 * m.m12 + this.m32 * m.m22 + this.m33 * m.m32 + this.m34 * m.m42
    r.m33 = this.m31 * m.m13 + this.m32 * m.m23 + this.m33 * m.m33 + this.m34 * m.m43
    r.m34 = this.m31 * m.m14 + this.m32 * m.m24 + this.m33 * m.m34 + this.m34 * m.m44
    r.m41 = this.m41 * m.m11 + this.m42 * m.m21 + this.m43 * m.m31 + this.m44 * m.m41
    r.m42 = this.m41 * m.m12 + this.m42 * m.m22 + this.m43 * m.m32 + this.m44 * m.m42
    r.m43 = this.m41 * m.m13 + this.m42 * m.m23 + this.m43 * m.m33 + this.m44 * m.m43
    r.m44 = this.m41 * m.m14 + this.m42 * m.m24 + this.m43 * m.m34 + this.m44 * m.m44
    return r
  }

  /**
   * @access public
   * @param {SCNMatrix4} m -
   * @param {number} rate -
   * @returns {SCNMatrix4} - 
   */
  lerp(m, rate) {
    const r = new SCNMatrix4()
    r.m11 = this.m11 + rate * (this.m11 - m.m11)
    r.m12 = this.m12 + rate * (this.m12 - m.m12)
    r.m13 = this.m13 + rate * (this.m13 - m.m13)
    r.m14 = this.m14 + rate * (this.m14 - m.m14)
    r.m21 = this.m21 + rate * (this.m21 - m.m21)
    r.m22 = this.m22 + rate * (this.m22 - m.m22)
    r.m23 = this.m23 + rate * (this.m23 - m.m23)
    r.m24 = this.m24 + rate * (this.m24 - m.m24)
    r.m31 = this.m31 + rate * (this.m31 - m.m31)
    r.m32 = this.m32 + rate * (this.m32 - m.m32)
    r.m33 = this.m33 + rate * (this.m33 - m.m33)
    r.m34 = this.m34 + rate * (this.m34 - m.m34)
    r.m41 = this.m41 + rate * (this.m41 - m.m41)
    r.m42 = this.m42 + rate * (this.m42 - m.m42)
    r.m43 = this.m43 + rate * (this.m43 - m.m43)
    r.m44 = this.m44 + rate * (this.m44 - m.m44)
    return r
  }

  /**
   * @access public
   * @returns {SCNVector4} -
   */
  quaternion() {
    const r = new SCNVector4()
    r.x = this.m32 - this.m23
    r.y = this.m13 - this.m31
    r.z = this.m21 - this.m12
    r.w = Math.acos((this.m11 + this.m22 + this.m33 - 1)*0.5)
    return r
  }

  /**
   * @access public
   * @returns {SCNMatrix4} -
   */
  invert() {
    const mat = SCNMatrix4._identity()
    const tmp = new SCNMatrix4(this)

    let buf = 0
    let w1 = Math.abs(tmp.m11)
    let w2 = Math.abs(tmp.m21)
    let w3 = Math.abs(tmp.m31)
    let w4 = Math.abs(tmp.m41)
    let max = w1 > w2 ? w1 : w2
    if(max < w3) max = w3

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

    buf = tmp.m21
    tmp.m22 -= tmp.m12 * buf
    tmp.m23 -= tmp.m13 * buf
    tmp.m24 -= tmp.m14 * buf
    mat.m21 -= mat.m11 * buf
    mat.m22 -= mat.m12 * buf
    mat.m23 -= mat.m13 * buf
    mat.m24 -= mat.m14 * buf

    buf = tmp.m31
    tmp.m32 -= tmp.m12 * buf
    tmp.m33 -= tmp.m13 * buf
    tmp.m34 -= tmp.m14 * buf
    mat.m31 -= mat.m11 * buf
    mat.m32 -= mat.m12 * buf
    mat.m33 -= mat.m13 * buf
    mat.m34 -= mat.m14 * buf

    buf = tmp.m41
    tmp.m42 -= tmp.m12 * buf
    tmp.m43 -= tmp.m13 * buf
    tmp.m44 -= tmp.m14 * buf
    mat.m41 -= mat.m11 * buf
    mat.m42 -= mat.m12 * buf
    mat.m43 -= mat.m13 * buf
    mat.m44 -= mat.m14 * buf

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

    buf = tmp.m12
    tmp.m13 -= tmp.m23 * buf
    tmp.m14 -= tmp.m24 * buf
    mat.m11 -= mat.m21 * buf
    mat.m12 -= mat.m22 * buf
    mat.m13 -= mat.m23 * buf
    mat.m14 -= mat.m24 * buf

    buf = tmp.m32
    tmp.m33 -= tmp.m23 * buf
    tmp.m34 -= tmp.m24 * buf
    mat.m31 -= mat.m21 * buf
    mat.m32 -= mat.m22 * buf
    mat.m33 -= mat.m23 * buf
    mat.m34 -= mat.m24 * buf

    buf = tmp.m42
    tmp.m43 -= tmp.m23 * buf
    tmp.m44 -= tmp.m24 * buf
    mat.m41 -= mat.m21 * buf
    mat.m42 -= mat.m22 * buf
    mat.m43 -= mat.m23 * buf
    mat.m44 -= mat.m24 * buf

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

    buf = tmp.m23
    tmp.m24 -= tmp.m34 * buf
    mat.m21 -= mat.m31 * buf
    mat.m22 -= mat.m32 * buf
    mat.m23 -= mat.m33 * buf
    mat.m24 -= mat.m34 * buf

    buf = tmp.m43
    tmp.m44 -= tmp.m34 * buf
    mat.m41 -= mat.m31 * buf
    mat.m42 -= mat.m32 * buf
    mat.m43 -= mat.m33 * buf
    mat.m44 -= mat.m34 * buf

    // 4
    buf = 1.0 / tmp.m44
    mat.m41 *= buf
    mat.m42 *= buf
    mat.m43 *= buf
    mat.m44 *= buf

    buf = tmp.m14
    mat.m11 -= mat.m41 * buf
    mat.m12 -= mat.m42 * buf
    mat.m13 -= mat.m43 * buf
    mat.m14 -= mat.m44 * buf

    buf = tmp.m24
    mat.m21 -= mat.m41 * buf
    mat.m22 -= mat.m42 * buf
    mat.m23 -= mat.m43 * buf
    mat.m24 -= mat.m44 * buf

    buf = tmp.m34
    mat.m31 -= mat.m41 * buf
    mat.m32 -= mat.m42 * buf
    mat.m33 -= mat.m43 * buf
    mat.m34 -= mat.m44 * buf

    return mat
  }

  /**
   * @access public
   * @returns {SCNMatrix4} -
   */
  transpose() {
    const r = new SCNMatrix4()
    r.m11 = this.m11
    r.m12 = this.m21
    r.m13 = this.m31
    r.m14 = this.m41
    r.m21 = this.m12
    r.m22 = this.m22
    r.m23 = this.m32
    r.m24 = this.m42
    r.m31 = this.m13
    r.m32 = this.m23
    r.m33 = this.m33
    r.m34 = this.m43
    r.m41 = this.m14
    r.m42 = this.m24
    r.m43 = this.m34
    r.m44 = this.m44
    return r
  }

  /**
   * @access public
   * @param {number} x -
   * @param {number} y -
   * @param {number} z -
   * @returns {SCNMatrix4} -
   */
  scale(x, y, z) {
    const m = SCNMatrix4.matrixWithScale(x, y, z)
    return this.mult(m)
  }

  /**
   * @access public
   * @param {number} x -
   * @param {number} y -
   * @param {number} z -
   * @returns {SCNMatrix4} -
   */
  static matrixWithScale(x, y, z) {
    let _x = x
    let _y = y
    let _z = z
    if(_InstanceOf(x, SCNVector3)){
      const v = x
      _x = v.x
      _y = v.y
      _z = v.z
    }

    //const m = new SCNMatrix4()
    const m = SCNMatrix4._identity()
    m.m11 = _x
    m.m22 = _y
    m.m33 = _z
    return m
  }

  /**
   * @access public
   * @param {number} x -
   * @param {number} y -
   * @param {number} z -
   * @param {number} w -
   * @returns {SCNMatrix4} -
   */
  rotation(x, y, z, w) {
    if(_InstanceOf(x, SCNVector4)){
      const v = x
      x = v.x
      y = v.y
      z = v.z
      w = v.w
    }

    const m = SCNMatrix4.matrixWithRotation(x, y, z, w)
    return this.mult(m)
  }

  /**
   * @access public
   * @param {number} x -
   * @param {number} y -
   * @param {number} z -
   * @param {number} w -
   * @returns {SCNMatrix4} -
   */
  static matrixWithRotation(x, y, z, w) {
    if(_InstanceOf(x, SCNVector4)){
      const v = x
      x = v.x
      y = v.y
      z = v.z
      w = v.w
    }

    const c = Math.cos(w)
    const s = Math.sin(w)
    const v = (new SCNVector3(x, y, z)).normalize()
    const m = SCNMatrix4._identity()

    const nx = v.x
    const ny = v.y
    const nz = v.z

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

    return m
  }

  static matrixWithOrientation(orientation) {
    return SCNMatrix4.matrixWithRotation(orientation.quatToRotation())
  }

  /**
   * @access public
   * @param {number} x -
   * @param {number} y -
   * @param {number} z -
   * @returns {SCNMatrix4} -
   */
  translation(x, y, z) {
    const m = SCNMatrix4.matrixWithTranslation(x, y, z)
    return this.mult(m)
  }

  /**
   * @access public
   * @param {number} x -
   * @param {number} y -
   * @param {number} z -
   * @returns {SCNMatrix4} -
   */
  static matrixWithTranslation(x, y, z) {
    let _x = x
    let _y = y
    let _z = z
    if(_InstanceOf(x, SCNVector3)){
      const v = x
      _x = v.x
      _y = v.y
      _z = v.z
    }

    //const m = new SCNMatrix4()
    const m = SCNMatrix4._identity()
    m.m41 = _x
    m.m42 = _y
    m.m43 = _z
    return m
  }

  /**
   * Returns a Boolean value that indicates whether the corresponding elements of two matrices are equal.
   * @access public
   * @param {SCNMatrix4} m -
   * @returns {boolean} -
   * @desc This function performs a numeric (not bitwise) comparison of each pair of elements.
   * @see https://developer.apple.com/documentation/scenekit/1409665-scnmatrix4equaltomatrix4
   */
  equalTo(m) {
    if(!_InstanceOf(m, SCNMatrix4)){
      return false
    }

    return Math.abs(this.m11 - m.m11) < _epsilon
      && Math.abs(this.m12 - m.m12) < _epsilon
      && Math.abs(this.m13 - m.m13) < _epsilon
      && Math.abs(this.m14 - m.m14) < _epsilon
      && Math.abs(this.m21 - m.m21) < _epsilon
      && Math.abs(this.m22 - m.m22) < _epsilon
      && Math.abs(this.m23 - m.m23) < _epsilon
      && Math.abs(this.m24 - m.m24) < _epsilon
      && Math.abs(this.m31 - m.m31) < _epsilon
      && Math.abs(this.m32 - m.m32) < _epsilon
      && Math.abs(this.m33 - m.m33) < _epsilon
      && Math.abs(this.m34 - m.m34) < _epsilon
      && Math.abs(this.m41 - m.m41) < _epsilon
      && Math.abs(this.m42 - m.m42) < _epsilon
      && Math.abs(this.m43 - m.m43) < _epsilon
      && Math.abs(this.m44 - m.m44) < _epsilon
  }

  /**
   * Returns a Boolean value that indicates whether the matrix is equal to the identity matrix.
   * @access public
   * @returns {boolean} - 
   * @see https://developer.apple.com/documentation/scenekit/1409715-scnmatrix4isidentity
   */
  isIdentity() {
    return this.equalTo(SCNMatrix4._identity())
  }

  /**
   * @access public
   * @returns {SCNVector3} -
   */
  getScale() {
    const det = this.m11 * this.m22 * this.m33
              + this.m12 * this.m23 * this.m31
              + this.m13 * this.m21 * this.m32
              - this.m11 * this.m23 * this.m32
              - this.m12 * this.m21 * this.m33
              - this.m13 * this.m22 * this.m31
    const sign = det > 0 ? 1 : -1
    const r = sign / this.m44
    const sx = new SCNVector3(this.m11, this.m12, this.m13)
    const sy = new SCNVector3(this.m21, this.m22, this.m23)
    const sz = new SCNVector3(this.m31, this.m32, this.m33)
    return new SCNVector3(sx.length() * r, sy.length() * r, sz.length() * r)
  }

  /**
   * @access public
   * @returns {SCNVector3} -
   */
  getTranslation() {
    return new SCNVector3(this.m41 / this.m44, this.m42 / this.m44, this.m43 / this.m44)
  }

  /**
   * @access public
   * @returns {SCNVector4} -
   */
  getRotation() {
    const e = []
    const scale = this.getScale().mul(this.m44)
    const v = new SCNVector4()
    const n1 = (new SCNVector3(this.m11, this.m12, this.m13)).mul(1.0 / scale.x)
    const n2 = (new SCNVector3(this.m21, this.m22, this.m23)).mul(1.0 / scale.y)
    const n3 = (new SCNVector3(this.m31, this.m32, this.m33)).mul(1.0 / scale.z)
    e[0] = n1.x - n2.y - n3.z + 1.0
    e[1] = -n1.x + n2.y - n3.z + 1.0
    e[2] = -n1.x - n2.y + n3.z + 1.0
    e[3] = n1.x + n2.y + n3.z + 1.0
    let maxIndex = 0
    for(let i=1; i<4; i++){
      if(e[i] > e[maxIndex]){
        maxIndex = i
      }
    }
    if(e[maxIndex] < 0){
      throw new Error('something is wrong...')
    }
    const d = Math.sqrt(e[maxIndex]) * 0.5
    const r = 0.25 / d

    //console.log(`n1: ${n1.x}, ${n1.y}, ${n1.z}`)
    //console.log(`n2: ${n2.x}, ${n2.y}, ${n2.z}`)
    //console.log(`n3: ${n3.x}, ${n3.y}, ${n3.z}`)
    //console.log(`d: ${d}, r: ${r}`)
    switch(maxIndex){
      case 0:
        v.x = d
        v.y = (n1.y + n2.x) * r
        v.z = (n3.x + n1.z) * r
        v.w = (n2.z - n3.y) * r
        break
      case 1:
        v.x = (n1.y + n2.x) * r
        v.y = d
        v.z = (n2.z + n3.y) * r
        v.w = (n3.x - n1.z) * r
        break
      case 2:
        v.x = (n3.x + n1.z) * r
        v.y = (n2.z + n3.y) * r
        v.z = d
        v.w = (n1.y - n2.x) * r
        break
      case 3:
        v.x = (n2.z - n3.y) * r
        v.y = (n3.x - n1.z) * r
        v.z = (n1.y - n2.x) * r
        v.w = d
        break
    }
    if(v.x === 0 && v.y === 0 && v.z === 0){
      v.w = 0
    }else{
      const w = Math.acos(v.w)
      if(isNaN(w)){
        v.w = 0
      }else{
        v.w = w * 2.0
      }
    }

    return v

  }

  /**
   * @access public
   * @returns {SCNVector4} -
   */
  getOrientation() {
    return this.getRotation().rotationToQuat()
    /*
    const e = []
    const scale = this.getScale().mul(this.m44)
    const v = new SCNVector4()
    const n1 = (new SCNVector3(this.m11, this.m12, this.m13)).mul(1.0 / scale.x)
    const n2 = (new SCNVector3(this.m21, this.m22, this.m23)).mul(1.0 / scale.y)
    const n3 = (new SCNVector3(this.m31, this.m32, this.m33)).mul(1.0 / scale.z)
    e[0] = n1.x - n2.y - n3.z + 1.0
    e[1] = -n1.x + n2.y - n3.z + 1.0
    e[2] = -n1.x - n2.y + n3.z + 1.0
    e[3] = n1.x + n2.y + n3.z + 1.0
    let maxIndex = 0
    for(let i=1; i<4; i++){
      if(e[i] > e[maxIndex]){
        maxIndex = i
      }
    }
    console.log(`maxIndex: ${maxIndex} => ${e[maxIndex]}`)
    if(e[maxIndex] < 0){
      throw new Error('something is wrong...')
    }
    const d = Math.sqrt(e[maxIndex]) * 0.5
    const r = 0.25 / d

    //console.log(`n1: ${n1.x}, ${n1.y}, ${n1.z}`)
    //console.log(`n2: ${n2.x}, ${n2.y}, ${n2.z}`)
    //console.log(`n3: ${n3.x}, ${n3.y}, ${n3.z}`)
    //console.log(`d: ${d}, r: ${r}`)
    switch(maxIndex){
      case 0:
        v.x = d
        v.y = (n1.y + n2.x) * r
        v.z = (n3.x + n1.z) * r
        v.w = (n2.z - n3.y) * r
        break
      case 1:
        v.x = (n1.y + n2.x) * r
        v.y = d
        v.z = (n2.z + n3.y) * r
        v.w = (n3.x - n1.z) * r
        break
      case 2:
        v.x = (n3.x + n1.z) * r
        v.y = (n2.z + n3.y) * r
        v.z = d
        v.w = (n1.y - n2.x) * r
        break
      case 3:
        v.x = (n2.z - n3.y) * r
        v.y = (n3.x - n1.z) * r
        v.z = (n1.y - n2.x) * r
        v.w = d
        break
    }
    const len = 1.0 / Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z)
    v.x *= len
    v.y *= len
    v.z *= len

    return v
    */
  }

  /**
   * @access public
   * @returns {number[]} -
   */
  floatArray() {
    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
    ]
  }

  /**
   * @access public
   * @returns {Float32Array} -
   */
  float32Array() {
    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
    ])
  }

  /**
   * @access public
   * @returns {number[]} -
   */
  floatArray3x4f() {
    return [
      this.m11, this.m21, this.m31, this.m41,
      this.m12, this.m22, this.m32, this.m42,
      this.m13, this.m23, this.m33, this.m43
    ]
  }

  /**
   * @access public
   * @returns {Float32Array} -
   */
  float32Array3x4f() {
    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
    ])
  }
  
  static _identity() {
    return new SCNMatrix4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
  }
}