UNPKG

processing-js

Version:

For the project website, visit http://processingjs.org

597 lines (574 loc) • 25.7 kB
module.exports = function(options, undef) { // FIXME: hack var p = options.p; /** * PMatrix3D is a 4x4 matrix implementation. The constructor accepts another PMatrix3D or a list of six or sixteen float elements. * If no parameters are provided the matrix is set to the identity matrix. */ var PMatrix3D = function() { // When a matrix is created, it is set to an identity matrix this.reset(); }; /** * PMatrix3D methods */ PMatrix3D.prototype = { /** * @member PMatrix2D * The set() function sets the matrix elements. The function accepts either another PMatrix3D, an array of elements, or a list of six or sixteen floats. * * @param {PMatrix3D} matrix the initial matrix to set to * @param {float[]} elements an array of elements to set this matrix to * @param {float} m00 the first element of the matrix * @param {float} m01 the second element of the matrix * @param {float} m02 the third element of the matrix * @param {float} m03 the fourth element of the matrix * @param {float} m10 the fifth element of the matrix * @param {float} m11 the sixth element of the matrix * @param {float} m12 the seventh element of the matrix * @param {float} m13 the eight element of the matrix * @param {float} m20 the nineth element of the matrix * @param {float} m21 the tenth element of the matrix * @param {float} m22 the eleventh element of the matrix * @param {float} m23 the twelveth element of the matrix * @param {float} m30 the thirteenth element of the matrix * @param {float} m31 the fourtheenth element of the matrix * @param {float} m32 the fivetheenth element of the matrix * @param {float} m33 the sixteenth element of the matrix */ set: function() { if (arguments.length === 16) { this.elements = Array.prototype.slice.call(arguments); } else if (arguments.length === 1 && arguments[0] instanceof PMatrix3D) { this.elements = arguments[0].array(); } else if (arguments.length === 1 && arguments[0] instanceof Array) { this.elements = arguments[0].slice(); } }, /** * @member PMatrix3D * The get() function returns a copy of this PMatrix3D. * * @return {PMatrix3D} a copy of this PMatrix3D */ get: function() { var outgoing = new PMatrix3D(); outgoing.set(this.elements); return outgoing; }, /** * @member PMatrix3D * The reset() function sets this PMatrix3D to the identity matrix. */ reset: function() { this.elements = [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]; }, /** * @member PMatrix3D * The array() function returns a copy of the element values. * @addon * * @return {float[]} returns a copy of the element values */ array: function array() { return this.elements.slice(); }, /** * @member PMatrix3D * The translate() function translates this matrix by moving the current coordinates to the location specified by tx, ty, and tz. * * @param {float} tx the x-axis coordinate to move to * @param {float} ty the y-axis coordinate to move to * @param {float} tz the z-axis coordinate to move to */ translate: function(tx, ty, tz) { if (tz === undef) { tz = 0; } this.elements[3] += tx * this.elements[0] + ty * this.elements[1] + tz * this.elements[2]; this.elements[7] += tx * this.elements[4] + ty * this.elements[5] + tz * this.elements[6]; this.elements[11] += tx * this.elements[8] + ty * this.elements[9] + tz * this.elements[10]; this.elements[15] += tx * this.elements[12] + ty * this.elements[13] + tz * this.elements[14]; }, /** * @member PMatrix3D * The transpose() function transpose this matrix. */ transpose: function() { var temp = this.elements[4]; this.elements[4] = this.elements[1]; this.elements[1] = temp; temp = this.elements[8]; this.elements[8] = this.elements[2]; this.elements[2] = temp; temp = this.elements[6]; this.elements[6] = this.elements[9]; this.elements[9] = temp; temp = this.elements[3]; this.elements[3] = this.elements[12]; this.elements[12] = temp; temp = this.elements[7]; this.elements[7] = this.elements[13]; this.elements[13] = temp; temp = this.elements[11]; this.elements[11] = this.elements[14]; this.elements[14] = temp; }, /** * @member PMatrix3D * The mult() function multiplied this matrix. * If two array elements are passed in the function will multiply a two element vector against this matrix. * If target is null or not length four, a new float array will be returned. * The values for vec and target can be the same (though that's less efficient). * If two PVectors are passed in the function multiply the x and y coordinates of a PVector against this matrix. * * @param {PVector} source, target the PVectors used to multiply this matrix * @param {float[]} source, target the arrays used to multiply this matrix * * @return {PVector|float[]} returns a PVector or an array representing the new matrix */ mult: function(source, target) { var x, y, z, w; if (source instanceof PVector) { x = source.x; y = source.y; z = source.z; w = 1; if (!target) { target = new PVector(); } } else if (source instanceof Array) { x = source[0]; y = source[1]; z = source[2]; w = source[3] || 1; if ( !target || (target.length !== 3 && target.length !== 4) ) { target = [0, 0, 0]; } } if (target instanceof Array) { if (target.length === 3) { target[0] = this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3]; target[1] = this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7]; target[2] = this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11]; } else if (target.length === 4) { target[0] = this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3] * w; target[1] = this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7] * w; target[2] = this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11] * w; target[3] = this.elements[12] * x + this.elements[13] * y + this.elements[14] * z + this.elements[15] * w; } } if (target instanceof PVector) { target.x = this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3]; target.y = this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7]; target.z = this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11]; } return target; }, /** * @member PMatrix3D * The preApply() function applies another matrix to the left of this one. Note that either a PMatrix3D or elements of a matrix can be passed in. * * @param {PMatrix3D} matrix the matrix to apply this matrix to * @param {float} m00 the first element of the matrix * @param {float} m01 the second element of the matrix * @param {float} m02 the third element of the matrix * @param {float} m03 the fourth element of the matrix * @param {float} m10 the fifth element of the matrix * @param {float} m11 the sixth element of the matrix * @param {float} m12 the seventh element of the matrix * @param {float} m13 the eight element of the matrix * @param {float} m20 the nineth element of the matrix * @param {float} m21 the tenth element of the matrix * @param {float} m22 the eleventh element of the matrix * @param {float} m23 the twelveth element of the matrix * @param {float} m30 the thirteenth element of the matrix * @param {float} m31 the fourtheenth element of the matrix * @param {float} m32 the fivetheenth element of the matrix * @param {float} m33 the sixteenth element of the matrix */ preApply: function() { var source; if (arguments.length === 1 && arguments[0] instanceof PMatrix3D) { source = arguments[0].array(); } else if (arguments.length === 16) { source = Array.prototype.slice.call(arguments); } else if (arguments.length === 1 && arguments[0] instanceof Array) { source = arguments[0]; } var result = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; var e = 0; for (var row = 0; row < 4; row++) { for (var col = 0; col < 4; col++, e++) { result[e] += this.elements[col + 0] * source[row * 4 + 0] + this.elements[col + 4] * source[row * 4 + 1] + this.elements[col + 8] * source[row * 4 + 2] + this.elements[col + 12] * source[row * 4 + 3]; } } this.elements = result.slice(); }, /** * @member PMatrix3D * The apply() function multiplies the current matrix by the one specified through the parameters. Note that either a PMatrix3D or a list of floats can be passed in. * * @param {PMatrix3D} matrix the matrix to apply this matrix to * @param {float} m00 the first element of the matrix * @param {float} m01 the second element of the matrix * @param {float} m02 the third element of the matrix * @param {float} m03 the fourth element of the matrix * @param {float} m10 the fifth element of the matrix * @param {float} m11 the sixth element of the matrix * @param {float} m12 the seventh element of the matrix * @param {float} m13 the eight element of the matrix * @param {float} m20 the nineth element of the matrix * @param {float} m21 the tenth element of the matrix * @param {float} m22 the eleventh element of the matrix * @param {float} m23 the twelveth element of the matrix * @param {float} m30 the thirteenth element of the matrix * @param {float} m31 the fourtheenth element of the matrix * @param {float} m32 the fivetheenth element of the matrix * @param {float} m33 the sixteenth element of the matrix */ apply: function() { var source; if (arguments.length === 1 && arguments[0] instanceof PMatrix3D) { source = arguments[0].array(); } else if (arguments.length === 16) { source = Array.prototype.slice.call(arguments); } else if (arguments.length === 1 && arguments[0] instanceof Array) { source = arguments[0]; } var result = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; var e = 0; for (var row = 0; row < 4; row++) { for (var col = 0; col < 4; col++, e++) { result[e] += this.elements[row * 4 + 0] * source[col + 0] + this.elements[row * 4 + 1] * source[col + 4] + this.elements[row * 4 + 2] * source[col + 8] + this.elements[row * 4 + 3] * source[col + 12]; } } this.elements = result.slice(); }, /** * @member PMatrix3D * The rotate() function rotates the matrix. * * @param {float} angle the angle of rotation in radiants */ rotate: function(angle, v0, v1, v2) { if (!v1) { this.rotateZ(angle); } else { // TODO should make sure this vector is normalized var c = Math.cos(angle); var s = Math.sin(angle); var t = 1.0 - c; this.apply((t * v0 * v0) + c, (t * v0 * v1) - (s * v2), (t * v0 * v2) + (s * v1), 0, (t * v0 * v1) + (s * v2), (t * v1 * v1) + c, (t * v1 * v2) - (s * v0), 0, (t * v0 * v2) - (s * v1), (t * v1 * v2) + (s * v0), (t * v2 * v2) + c, 0, 0, 0, 0, 1); } }, /** * @member PMatrix3D * The invApply() function applies the inverted matrix to this matrix. * * @param {float} m00 the first element of the matrix * @param {float} m01 the second element of the matrix * @param {float} m02 the third element of the matrix * @param {float} m03 the fourth element of the matrix * @param {float} m10 the fifth element of the matrix * @param {float} m11 the sixth element of the matrix * @param {float} m12 the seventh element of the matrix * @param {float} m13 the eight element of the matrix * @param {float} m20 the nineth element of the matrix * @param {float} m21 the tenth element of the matrix * @param {float} m22 the eleventh element of the matrix * @param {float} m23 the twelveth element of the matrix * @param {float} m30 the thirteenth element of the matrix * @param {float} m31 the fourtheenth element of the matrix * @param {float} m32 the fivetheenth element of the matrix * @param {float} m33 the sixteenth element of the matrix * * @return {boolean} returns true if the operation was successful. */ invApply: function() { if (inverseCopy === undef) { inverseCopy = new PMatrix3D(); } var a = arguments; inverseCopy.set(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]); if (!inverseCopy.invert()) { return false; } this.preApply(inverseCopy); return true; }, /** * @member PMatrix3D * The rotateZ() function rotates the matrix. * * @param {float} angle the angle of rotation in radiants */ rotateX: function(angle) { var c = Math.cos(angle); var s = Math.sin(angle); this.apply([1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1]); }, /** * @member PMatrix3D * The rotateY() function rotates the matrix. * * @param {float} angle the angle of rotation in radiants */ rotateY: function(angle) { var c = Math.cos(angle); var s = Math.sin(angle); this.apply([c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1]); }, /** * @member PMatrix3D * The rotateZ() function rotates the matrix. * * @param {float} angle the angle of rotation in radiants */ rotateZ: function(angle) { var c = Math.cos(angle); var s = Math.sin(angle); this.apply([c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]); }, /** * @member PMatrix3D * The scale() function increases or decreases the size of a matrix by expanding and contracting vertices. When only one parameter is specified scale will occur in all dimensions. * This is equivalent to a three parameter call. * * @param {float} sx the amount to scale on the x-axis * @param {float} sy the amount to scale on the y-axis * @param {float} sz the amount to scale on the z-axis */ scale: function(sx, sy, sz) { if (sx && sy === undef && sz === undef) { sy = sz = sx; } else if (sx && sy && sz === undef) { sz = 1; } if (sx && sy && sz) { this.elements[0] *= sx; this.elements[1] *= sy; this.elements[2] *= sz; this.elements[4] *= sx; this.elements[5] *= sy; this.elements[6] *= sz; this.elements[8] *= sx; this.elements[9] *= sy; this.elements[10] *= sz; this.elements[12] *= sx; this.elements[13] *= sy; this.elements[14] *= sz; } }, /** * @member PMatrix3D * The skewX() function skews the matrix along the x-axis the amount specified by the angle parameter. * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function. * * @param {float} angle angle of skew specified in radians */ skewX: function(angle) { var t = Math.tan(angle); this.apply(1, t, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); }, /** * @member PMatrix3D * The skewY() function skews the matrix along the y-axis the amount specified by the angle parameter. * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function. * * @param {float} angle angle of skew specified in radians */ skewY: function(angle) { var t = Math.tan(angle); this.apply(1, 0, 0, 0, t, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); }, /** * @member PMatrix3D * The shearX() function shears the matrix along the x-axis the amount specified by the angle parameter. * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function. * * @param {float} angle angle of shear specified in radians */ shearX: function(angle) { var t = Math.tan(angle); this.apply(1, t, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); }, /** * @member PMatrix3D * The shearY() function shears the matrix along the y-axis the amount specified by the angle parameter. * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function. * * @param {float} angle angle of shear specified in radians */ shearY: function(angle) { var t = Math.tan(angle); this.apply(1, 0, 0, 0, t, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); }, multX: function(x, y, z, w) { if (!z) { return this.elements[0] * x + this.elements[1] * y + this.elements[3]; } if (!w) { return this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3]; } return this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3] * w; }, multY: function(x, y, z, w) { if (!z) { return this.elements[4] * x + this.elements[5] * y + this.elements[7]; } if (!w) { return this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7]; } return this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7] * w; }, multZ: function(x, y, z, w) { if (!w) { return this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11]; } return this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11] * w; }, multW: function(x, y, z, w) { if (!w) { return this.elements[12] * x + this.elements[13] * y + this.elements[14] * z + this.elements[15]; } return this.elements[12] * x + this.elements[13] * y + this.elements[14] * z + this.elements[15] * w; }, /** * @member PMatrix3D * The invert() function inverts this matrix * * @return {boolean} true if successful */ invert: function() { var fA0 = this.elements[0] * this.elements[5] - this.elements[1] * this.elements[4]; var fA1 = this.elements[0] * this.elements[6] - this.elements[2] * this.elements[4]; var fA2 = this.elements[0] * this.elements[7] - this.elements[3] * this.elements[4]; var fA3 = this.elements[1] * this.elements[6] - this.elements[2] * this.elements[5]; var fA4 = this.elements[1] * this.elements[7] - this.elements[3] * this.elements[5]; var fA5 = this.elements[2] * this.elements[7] - this.elements[3] * this.elements[6]; var fB0 = this.elements[8] * this.elements[13] - this.elements[9] * this.elements[12]; var fB1 = this.elements[8] * this.elements[14] - this.elements[10] * this.elements[12]; var fB2 = this.elements[8] * this.elements[15] - this.elements[11] * this.elements[12]; var fB3 = this.elements[9] * this.elements[14] - this.elements[10] * this.elements[13]; var fB4 = this.elements[9] * this.elements[15] - this.elements[11] * this.elements[13]; var fB5 = this.elements[10] * this.elements[15] - this.elements[11] * this.elements[14]; // Determinant var fDet = fA0 * fB5 - fA1 * fB4 + fA2 * fB3 + fA3 * fB2 - fA4 * fB1 + fA5 * fB0; // Account for a very small value // return false if not successful. if (Math.abs(fDet) <= 1e-9) { return false; } var kInv = []; kInv[0] = +this.elements[5] * fB5 - this.elements[6] * fB4 + this.elements[7] * fB3; kInv[4] = -this.elements[4] * fB5 + this.elements[6] * fB2 - this.elements[7] * fB1; kInv[8] = +this.elements[4] * fB4 - this.elements[5] * fB2 + this.elements[7] * fB0; kInv[12] = -this.elements[4] * fB3 + this.elements[5] * fB1 - this.elements[6] * fB0; kInv[1] = -this.elements[1] * fB5 + this.elements[2] * fB4 - this.elements[3] * fB3; kInv[5] = +this.elements[0] * fB5 - this.elements[2] * fB2 + this.elements[3] * fB1; kInv[9] = -this.elements[0] * fB4 + this.elements[1] * fB2 - this.elements[3] * fB0; kInv[13] = +this.elements[0] * fB3 - this.elements[1] * fB1 + this.elements[2] * fB0; kInv[2] = +this.elements[13] * fA5 - this.elements[14] * fA4 + this.elements[15] * fA3; kInv[6] = -this.elements[12] * fA5 + this.elements[14] * fA2 - this.elements[15] * fA1; kInv[10] = +this.elements[12] * fA4 - this.elements[13] * fA2 + this.elements[15] * fA0; kInv[14] = -this.elements[12] * fA3 + this.elements[13] * fA1 - this.elements[14] * fA0; kInv[3] = -this.elements[9] * fA5 + this.elements[10] * fA4 - this.elements[11] * fA3; kInv[7] = +this.elements[8] * fA5 - this.elements[10] * fA2 + this.elements[11] * fA1; kInv[11] = -this.elements[8] * fA4 + this.elements[9] * fA2 - this.elements[11] * fA0; kInv[15] = +this.elements[8] * fA3 - this.elements[9] * fA1 + this.elements[10] * fA0; // Inverse using Determinant var fInvDet = 1.0 / fDet; kInv[0] *= fInvDet; kInv[1] *= fInvDet; kInv[2] *= fInvDet; kInv[3] *= fInvDet; kInv[4] *= fInvDet; kInv[5] *= fInvDet; kInv[6] *= fInvDet; kInv[7] *= fInvDet; kInv[8] *= fInvDet; kInv[9] *= fInvDet; kInv[10] *= fInvDet; kInv[11] *= fInvDet; kInv[12] *= fInvDet; kInv[13] *= fInvDet; kInv[14] *= fInvDet; kInv[15] *= fInvDet; this.elements = kInv.slice(); return true; }, toString: function() { var str = ""; for (var i = 0; i < 15; i++) { str += this.elements[i] + ", "; } str += this.elements[15]; return str; }, /** * @member PMatrix3D * The print() function prints out the elements of this matrix */ print: function() { var digits = printMatrixHelper(this.elements); var output = "" + p.nfs(this.elements[0], digits, 4) + " " + p.nfs(this.elements[1], digits, 4) + " " + p.nfs(this.elements[2], digits, 4) + " " + p.nfs(this.elements[3], digits, 4) + "\n" + p.nfs(this.elements[4], digits, 4) + " " + p.nfs(this.elements[5], digits, 4) + " " + p.nfs(this.elements[6], digits, 4) + " " + p.nfs(this.elements[7], digits, 4) + "\n" + p.nfs(this.elements[8], digits, 4) + " " + p.nfs(this.elements[9], digits, 4) + " " + p.nfs(this.elements[10], digits, 4) + " " + p.nfs(this.elements[11], digits, 4) + "\n" + p.nfs(this.elements[12], digits, 4) + " " + p.nfs(this.elements[13], digits, 4) + " " + p.nfs(this.elements[14], digits, 4) + " " + p.nfs(this.elements[15], digits, 4) + "\n\n"; p.println(output); }, invTranslate: function(tx, ty, tz) { this.preApply(1, 0, 0, -tx, 0, 1, 0, -ty, 0, 0, 1, -tz, 0, 0, 0, 1); }, invRotateX: function(angle) { var c = Math.cos(-angle); var s = Math.sin(-angle); this.preApply([1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1]); }, invRotateY: function(angle) { var c = Math.cos(-angle); var s = Math.sin(-angle); this.preApply([c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1]); }, invRotateZ: function(angle) { var c = Math.cos(-angle); var s = Math.sin(-angle); this.preApply([c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]); }, invScale: function(x, y, z) { this.preApply([1 / x, 0, 0, 0, 0, 1 / y, 0, 0, 0, 0, 1 / z, 0, 0, 0, 0, 1]); } }; return PMatrix3D; };