# math/Matrix4.js

``````import mat4 from '../glmatrix/mat4';
import vec3 from '../glmatrix/vec3';
import quat from '../glmatrix/quat';
import mat3 from '../glmatrix/mat3';
import Vector3 from './Vector3';

/**
* @constructor
* @alias clay.Matrix4
*/
var Matrix4 = function() {

this._axisX = new Vector3();
this._axisY = new Vector3();
this._axisZ = new Vector3();

/**
* Storage of Matrix4
* @name array
* @type {Float32Array}
* @memberOf clay.Matrix4#
*/
this.array = mat4.create();

/**
* @name _dirty
* @type {boolean}
* @memberOf clay.Matrix4#
*/
this._dirty = true;
};

Matrix4.prototype = {

constructor: Matrix4,

/**
* Set components from array
* @param  {Float32Array|number[]} arr
*/
setArray: function (arr) {
for (var i = 0; i < this.array.length; i++) {
this.array[i] = arr[i];
}
this._dirty = true;
return this;
},
/**
* Calculate the adjugate of self, in-place
* @return {clay.Matrix4}
*/
this._dirty = true;
return this;
},

/**
* Clone a new Matrix4
* @return {clay.Matrix4}
*/
clone: function() {
return (new Matrix4()).copy(this);
},

/**
* Copy from b
* @param  {clay.Matrix4} b
* @return {clay.Matrix4}
*/
copy: function(a) {
mat4.copy(this.array, a.array);
this._dirty = true;
return this;
},

/**
* Calculate matrix determinant
* @return {number}
*/
determinant: function() {
return mat4.determinant(this.array);
},

/**
* Set upper 3x3 part from quaternion
* @param  {clay.Quaternion} q
* @return {clay.Matrix4}
*/
fromQuat: function(q) {
mat4.fromQuat(this.array, q.array);
this._dirty = true;
return this;
},

/**
* Set from a quaternion rotation and a vector translation
* @param  {clay.Quaternion} q
* @param  {clay.Vector3} v
* @return {clay.Matrix4}
*/
fromRotationTranslation: function(q, v) {
mat4.fromRotationTranslation(this.array, q.array, v.array);
this._dirty = true;
return this;
},

/**
* Set from Matrix2d, it is used when converting a 2d shape to 3d space.
* In 3d space it is equivalent to ranslate on xy plane and rotate about z axis
* @param  {clay.Matrix2d} m2d
* @return {clay.Matrix4}
*/
fromMat2d: function(m2d) {
Matrix4.fromMat2d(this, m2d);
return this;
},

/**
* Set from frustum bounds
* @param  {number} left
* @param  {number} right
* @param  {number} bottom
* @param  {number} top
* @param  {number} near
* @param  {number} far
* @return {clay.Matrix4}
*/
frustum: function (left, right, bottom, top, near, far) {
mat4.frustum(this.array, left, right, bottom, top, near, far);
this._dirty = true;
return this;
},

/**
* Set to a identity matrix
* @return {clay.Matrix4}
*/
identity: function() {
mat4.identity(this.array);
this._dirty = true;
return this;
},

/**
* Invert self
* @return {clay.Matrix4}
*/
invert: function() {
mat4.invert(this.array, this.array);
this._dirty = true;
return this;
},

/**
* Set as a matrix with the given eye position, focal point, and up axis
* @param  {clay.Vector3} eye
* @param  {clay.Vector3} center
* @param  {clay.Vector3} up
* @return {clay.Matrix4}
*/
lookAt: function(eye, center, up) {
mat4.lookAt(this.array, eye.array, center.array, up.array);
this._dirty = true;
return this;
},

/**
* Alias for mutiply
* @param  {clay.Matrix4} b
* @return {clay.Matrix4}
*/
mul: function(b) {
mat4.mul(this.array, this.array, b.array);
this._dirty = true;
return this;
},

/**
* Alias for multiplyLeft
* @param  {clay.Matrix4} a
* @return {clay.Matrix4}
*/
mulLeft: function(a) {
mat4.mul(this.array, a.array, this.array);
this._dirty = true;
return this;
},

/**
* Multiply self and b
* @param  {clay.Matrix4} b
* @return {clay.Matrix4}
*/
multiply: function(b) {
mat4.multiply(this.array, this.array, b.array);
this._dirty = true;
return this;
},

/**
* Multiply a and self, a is on the left
* @param  {clay.Matrix3} a
* @return {clay.Matrix3}
*/
multiplyLeft: function(a) {
mat4.multiply(this.array, a.array, this.array);
this._dirty = true;
return this;
},

/**
* Set as a orthographic projection matrix
* @param  {number} left
* @param  {number} right
* @param  {number} bottom
* @param  {number} top
* @param  {number} near
* @param  {number} far
* @return {clay.Matrix4}
*/
ortho: function(left, right, bottom, top, near, far) {
mat4.ortho(this.array, left, right, bottom, top, near, far);
this._dirty = true;
return this;
},
/**
* Set as a perspective projection matrix
* @param  {number} fovy
* @param  {number} aspect
* @param  {number} near
* @param  {number} far
* @return {clay.Matrix4}
*/
perspective: function(fovy, aspect, near, far) {
mat4.perspective(this.array, fovy, aspect, near, far);
this._dirty = true;
return this;
},

/**
* Equal to right-multiply a rotaion matrix
* @param  {clay.Vector3} axis
* @return {clay.Matrix4}
*/
this._dirty = true;
return this;
},

/**
* Equal to right-multiply a rotaion matrix
* @return {clay.Matrix4}
*/
this._dirty = true;
return this;
},

/**
* Equal to right-multiply a rotaion matrix
* @return {clay.Matrix4}
*/
this._dirty = true;
return this;
},

/**
* Equal to right-multiply a rotaion matrix
* @return {clay.Matrix4}
*/
this._dirty = true;
return this;
},

/**
* Scale self by s
* Equal to right-multiply a scale matrix
* @param  {clay.Vector3}  s
* @return {clay.Matrix4}
*/
scale: function(v) {
mat4.scale(this.array, this.array, v.array);
this._dirty = true;
return this;
},

/**
* Translate self by v.
* Equal to right-multiply a translate matrix
* @param  {clay.Vector3}  v
* @return {clay.Matrix4}
*/
translate: function(v) {
mat4.translate(this.array, this.array, v.array);
this._dirty = true;
return this;
},

/**
* Transpose self, in-place.
* @return {clay.Matrix2}
*/
transpose: function() {
mat4.transpose(this.array, this.array);
this._dirty = true;
return this;
},

/**
* Decompose a matrix to SRT
* @param {clay.Vector3} [scale]
* @param {clay.Quaternion} rotation
* @param {clay.Vector} position
* @see http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.matrix.decompose.aspx
*/
decomposeMatrix: (function() {

var x = vec3.create();
var y = vec3.create();
var z = vec3.create();

var m3 = mat3.create();

return function(scale, rotation, position) {

var el = this.array;
vec3.set(x, el[0], el[1], el[2]);
vec3.set(y, el[4], el[5], el[6]);
vec3.set(z, el[8], el[9], el[10]);

var sx = vec3.length(x);
var sy = vec3.length(y);
var sz = vec3.length(z);

// if determine is negative, we need to invert one scale
var det = this.determinant();
if (det < 0) {
sx = -sx;
}

if (scale) {
scale.set(sx, sy, sz);
}

position.set(el[12], el[13], el[14]);

mat3.fromMat4(m3, el);
// Not like mat4, mat3 in glmatrix seems to be row-based
// Seems fixed in gl-matrix 2.2.2
// https://github.com/toji/gl-matrix/issues/114
// mat3.transpose(m3, m3);

m3[0] /= sx;
m3[1] /= sx;
m3[2] /= sx;

m3[3] /= sy;
m3[4] /= sy;
m3[5] /= sy;

m3[6] /= sz;
m3[7] /= sz;
m3[8] /= sz;

quat.fromMat3(rotation.array, m3);
quat.normalize(rotation.array, rotation.array);

rotation._dirty = true;
position._dirty = true;
};
})(),

toString: function() {
return '[' + Array.prototype.join.call(this.array, ',') + ']';
},

toArray: function () {
return Array.prototype.slice.call(this.array);
}
};

var defineProperty = Object.defineProperty;

if (defineProperty) {
var proto = Matrix4.prototype;
/**
* Z Axis of local transform
* @name z
* @type {clay.Vector3}
* @memberOf clay.Matrix4
* @instance
*/
defineProperty(proto, 'z', {
get: function () {
var el = this.array;
this._axisZ.set(el[8], el[9], el[10]);
return this._axisZ;
},
set: function (v) {
// TODO Here has a problem
// If only set an item of vector will not work
var el = this.array;
v = v.array;
el[8] = v[0];
el[9] = v[1];
el[10] = v[2];

this._dirty = true;
}
});

/**
* Y Axis of local transform
* @name y
* @type {clay.Vector3}
* @memberOf clay.Matrix4
* @instance
*/
defineProperty(proto, 'y', {
get: function () {
var el = this.array;
this._axisY.set(el[4], el[5], el[6]);
return this._axisY;
},
set: function (v) {
var el = this.array;
v = v.array;
el[4] = v[0];
el[5] = v[1];
el[6] = v[2];

this._dirty = true;
}
});

/**
* X Axis of local transform
* @name x
* @type {clay.Vector3}
* @memberOf clay.Matrix4
* @instance
*/
defineProperty(proto, 'x', {
get: function () {
var el = this.array;
this._axisX.set(el[0], el[1], el[2]);
return this._axisX;
},
set: function (v) {
var el = this.array;
v = v.array;
el[0] = v[0];
el[1] = v[1];
el[2] = v[2];

this._dirty = true;
}
})
}

/**
* @param  {clay.Matrix4} out
* @param  {clay.Matrix4} a
* @return {clay.Matrix4}
*/
out._dirty = true;
return out;
};

/**
* @param  {clay.Matrix4} out
* @param  {clay.Matrix4} a
* @return {clay.Matrix4}
*/
Matrix4.copy = function(out, a) {
mat4.copy(out.array, a.array);
out._dirty = true;
return out;
};

/**
* @param  {clay.Matrix4} a
* @return {number}
*/
Matrix4.determinant = function(a) {
return mat4.determinant(a.array);
};

/**
* @param  {clay.Matrix4} out
* @return {clay.Matrix4}
*/
Matrix4.identity = function(out) {
mat4.identity(out.array);
out._dirty = true;
return out;
};

/**
* @param  {clay.Matrix4} out
* @param  {number}  left
* @param  {number}  right
* @param  {number}  bottom
* @param  {number}  top
* @param  {number}  near
* @param  {number}  far
* @return {clay.Matrix4}
*/
Matrix4.ortho = function(out, left, right, bottom, top, near, far) {
mat4.ortho(out.array, left, right, bottom, top, near, far);
out._dirty = true;
return out;
};

/**
* @param  {clay.Matrix4} out
* @param  {number}  fovy
* @param  {number}  aspect
* @param  {number}  near
* @param  {number}  far
* @return {clay.Matrix4}
*/
Matrix4.perspective = function(out, fovy, aspect, near, far) {
mat4.perspective(out.array, fovy, aspect, near, far);
out._dirty = true;
return out;
};

/**
* @param  {clay.Matrix4} out
* @param  {clay.Vector3} eye
* @param  {clay.Vector3} center
* @param  {clay.Vector3} up
* @return {clay.Matrix4}
*/
Matrix4.lookAt = function(out, eye, center, up) {
mat4.lookAt(out.array, eye.array, center.array, up.array);
out._dirty = true;
return out;
};

/**
* @param  {clay.Matrix4} out
* @param  {clay.Matrix4} a
* @return {clay.Matrix4}
*/
Matrix4.invert = function(out, a) {
mat4.invert(out.array, a.array);
out._dirty = true;
return out;
};

/**
* @param  {clay.Matrix4} out
* @param  {clay.Matrix4} a
* @param  {clay.Matrix4} b
* @return {clay.Matrix4}
*/
Matrix4.mul = function(out, a, b) {
mat4.mul(out.array, a.array, b.array);
out._dirty = true;
return out;
};

/**
* @function
* @param  {clay.Matrix4} out
* @param  {clay.Matrix4} a
* @param  {clay.Matrix4} b
* @return {clay.Matrix4}
*/
Matrix4.multiply = Matrix4.mul;

/**
* @param  {clay.Matrix4}    out
* @param  {clay.Quaternion} q
* @return {clay.Matrix4}
*/
Matrix4.fromQuat = function(out, q) {
mat4.fromQuat(out.array, q.array);
out._dirty = true;
return out;
};

/**
* @param  {clay.Matrix4}    out
* @param  {clay.Quaternion} q
* @param  {clay.Vector3}    v
* @return {clay.Matrix4}
*/
Matrix4.fromRotationTranslation = function(out, q, v) {
mat4.fromRotationTranslation(out.array, q.array, v.array);
out._dirty = true;
return out;
};

/**
* @param  {clay.Matrix4} m4
* @param  {clay.Matrix2d} m2d
* @return {clay.Matrix4}
*/
Matrix4.fromMat2d = function(m4, m2d) {
m4._dirty = true;
var m2d = m2d.array;
var m4 = m4.array;

m4[0] = m2d[0];
m4[4] = m2d[2];
m4[12] = m2d[4];

m4[1] = m2d[1];
m4[5] = m2d[3];
m4[13] = m2d[5];

return m4;
};

/**
* @param  {clay.Matrix4} out
* @param  {clay.Matrix4} a
* @param  {clay.Vector3} axis
* @return {clay.Matrix4}
*/
Matrix4.rotate = function(out, a, rad, axis) {
out._dirty = true;
return out;
};

/**
* @param  {clay.Matrix4} out
* @param  {clay.Matrix4} a
* @return {clay.Matrix4}
*/
Matrix4.rotateX = function(out, a, rad) {
out._dirty = true;
return out;
};

/**
* @param  {clay.Matrix4} out
* @param  {clay.Matrix4} a
* @return {clay.Matrix4}
*/
Matrix4.rotateY = function(out, a, rad) {
out._dirty = true;
return out;
};

/**
* @param  {clay.Matrix4} out
* @param  {clay.Matrix4} a
* @return {clay.Matrix4}
*/
Matrix4.rotateZ = function(out, a, rad) {
out._dirty = true;
return out;
};

/**
* @param  {clay.Matrix4} out
* @param  {clay.Matrix4} a
* @param  {clay.Vector3} v
* @return {clay.Matrix4}
*/
Matrix4.scale = function(out, a, v) {
mat4.scale(out.array, a.array, v.array);
out._dirty = true;
return out;
};

/**
* @param  {clay.Matrix4} out
* @param  {clay.Matrix4} a
* @return {clay.Matrix4}
*/
Matrix4.transpose = function(out, a) {
mat4.transpose(out.array, a.array);
out._dirty = true;
return out;
};

/**
* @param  {clay.Matrix4} out
* @param  {clay.Matrix4} a
* @param  {clay.Vector3} v
* @return {clay.Matrix4}
*/
Matrix4.translate = function(out, a, v) {
mat4.translate(out.array, a.array, v.array);
out._dirty = true;
return out;
};

export default Matrix4;
``````