// Everything but the relevant parts stripped out by Janne Aukia // for Zoomooz on April 4 2012 by using jscoverage coverage analysis tool. // === Sylvester === // Vector and Matrix mathematics modules for JavaScript // Copyright (c) 2007 James Coglan // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. export interface ISylvester { version: string, precision: number, } export interface IMatrix { elements: Array>, modulus: boolean, } export function Matrix() { } Matrix.prototype = { // Returns a copy of the matrix dup: function () { return Matrix.create(this.elements); }, // Maps the matrix to another matrix (of the same dimensions) according to the given function /*map: function(fn) { let els = [], ni = this.elements.length, ki = ni, i, nj, kj = this.elements[0].length, j; do { i = ki - ni; nj = kj; els[i] = []; do { j = kj - nj; els[i][j] = fn(this.elements[i][j], i + 1, j + 1); } while (--nj); } while (--ni); return Matrix.create(els); },*/ // Returns true iff the matrix can multiply the argument from the left canMultiplyFromLeft: function (matrix: any) { let M = matrix.elements || matrix; if (typeof (M[0][0]) == 'undefined') { M = Matrix.create(M).elements; } // this.columns should equal matrix.rows return (this.elements[0].length == M.length); }, // Returns the result of multiplying the matrix from the right by the argument. // If the argument is a scalar then just multiply all the elements. If the argument is // a vector, a vector is returned, which saves you having to remember calling // col(1) on the result. multiply: function (matrix: IMatrix) { /*if (!matrix.elements) { return this.map(function(x) { return x * matrix; }); }*/ let returnVector: boolean = matrix.modulus ? true : false; let M:any = matrix.elements || matrix; if (typeof (M[0][0]) == 'undefined') { M = Matrix.create(M).elements; } if (!this.canMultiplyFromLeft(M)) { return null; } let ni = this.elements.length, ki = ni, i, nj, kj = M[0].length, j; let cols = this.elements[0].length, elements: Array> = [], sum, nc, c; do { i = ki - ni; elements[i] = []; nj = kj; do { j = kj - nj; sum = 0; nc = cols; do { c = cols - nc; sum += this.elements[i][c] * M[c][j]; } while (--nc); elements[i][j] = sum; } while (--nj); } while (--ni); M = Matrix.create(elements); return returnVector ? M.col(1) : M; }, // Returns true iff the matrix is square isSquare: function () { return (this.elements.length == this.elements[0].length); }, // Make the matrix upper (right) triangular by Gaussian elimination. // This method only adds multiples of rows to other rows. No rows are // scaled up or switched, and the determinant is preserved. toRightTriangular: function () { let M = this.dup(), els; let n = this.elements.length, k = n, i, np, kp = this.elements[0].length, p; do { i = k - n; if (M.elements[i][i] === 0) { for (let j = i + 1; j < k; j++) { if (M.elements[j][i] !== 0) { els = []; np = kp; do { p = kp - np; els.push(M.elements[i][p] + M.elements[j][p]); } while (--np); M.elements[i] = els; break; } } } if (M.elements[i][i] !== 0) { for (let j = i + 1; j < k; j++) { let multiplier = M.elements[j][i] / M.elements[i][i]; els = []; np = kp; do { p = kp - np; // Elements with column numbers up to an including the number // of the row that we're subtracting can safely be set straight to // zero, since that's the point of this routine and it avoids having // to loop over and correct rounding errors later els.push(p <= i ? 0 : M.elements[j][p] - M.elements[i][p] * multiplier); } while (--np); M.elements[j] = els; } } } while (--n); return M; }, // Returns the determinant for square matrices determinant: function () { if (!this.isSquare()) { return null; } let M = this.toRightTriangular(); let det = M.elements[0][0], n = M.elements.length - 1, k = n, i; do { i = k - n + 1; det = det * M.elements[i][i]; } while (--n); return det; }, // Returns true iff the matrix is singular isSingular: function () { return (this.isSquare() && this.determinant() === 0); }, // Returns the result of attaching the given argument to the right-hand side of the matrix augment: function (matrix: IMatrix) { let M = matrix.elements || matrix; if (typeof (M[0][0]) == 'undefined') { M = Matrix.create(M).elements; } let T = this.dup(), cols = T.elements[0].length; let ni = T.elements.length, ki = ni, i, nj, kj = M[0].length, j; if (ni != M.length) { return null; } do { i = ki - ni; nj = kj; do { j = kj - nj; T.elements[i][cols + j] = M[i][j]; } while (--nj); } while (--ni); return T; }, // Returns the inverse (if one exists) using Gauss-Jordan inverse: function () { if (!this.isSquare() || this.isSingular()) { return null; } let ni = this.elements.length, ki = ni, i, j; let M = this.augment(Matrix.I(ni)).toRightTriangular(); let np, kp = M.elements[0].length, p, els: Array, divisor; let inverse_elements: Array = [], new_element; // Matrix is non-singular so there will be no zeros on the diagonal // Cycle through rows from last to first do { i = ni - 1; // First, normalise diagonal elements to 1 els = []; np = kp; inverse_elements[i] = []; divisor = M.elements[i][i]; do { p = kp - np; new_element = M.elements[i][p] / divisor; els.push(new_element); // Shuffle of the current row of the right hand side into the results // array as it will not be modified by later runs through this loop if (p >= ki) { inverse_elements[i].push(new_element); } } while (--np); M.elements[i] = els; // Then, subtract this row from those above it to // give the identity matrix on the left hand side for (j = 0; j < i; j++) { els = []; np = kp; do { p = kp - np; els.push(M.elements[j][p] - M.elements[i][p] * M.elements[j][i]); } while (--np); M.elements[j] = els; } } while (--ni); return Matrix.create(inverse_elements); }, // Set the matrix's elements from an array. If the argument passed // is a vector, the resulting matrix will be a single column. setElements: function (els:IMatrix) { let i, elements = els.elements || els; if (typeof (elements[0][0]) != 'undefined') { let ni = elements.length, ki = ni, nj, kj, j; this.elements = []; do { i = ki - ni; nj = elements[i].length; kj = nj; this.elements[i] = []; do { j = kj - nj; this.elements[i][j] = elements[i][j]; } while (--nj); } while (--ni); return this; } let n = elements.length, k = n; this.elements = []; do { i = k - n; this.elements.push([elements[i]]); } while (--n); return this; } }; // Constructor function Matrix.create = function (elements) { let M = new Matrix(); return M.setElements(elements); }; // Identity matrix of size n Matrix.I = function (n) { let els: any = [], k = n, i, nj, j; do { i = k - n; els[i] = []; nj = k; do { j = k - nj; els[i][j] = (i == j) ? 1 : 0; } while (--nj); } while (--n); return Matrix.create(els); };/* * purecssmatrix.js, version 0.10, part of: * http://janne.aukia.com/zoomooz * * 0.10 initial stand-alone version * * LICENCE INFORMATION: * * Copyright (c) 2010 Janne Aukia (janne.aukia.com) * Dual licensed under the MIT (MIT-LICENSE.txt) * and GPL Version 2 (GPL-LICENSE.txt) licenses. * */ export let PureCSSMatrix = (function () { "use strict"; //**********************************// //*** Variables ***// //**********************************// let regexp_is_deg = /deg$/; let regexp_filter_number = /([0-9.\-e]+)/g; let regexp_trans_splitter = /([a-zA-Z]+)\(([^\)]+)\)/g; //**********************************// //*** WebKitCSSMatrix in ***// //*** pure Javascript ***// //**********************************// function CssMatrix(trans) { if (trans && trans !== null && trans != "none") { if (trans instanceof Matrix) { // @ts-ignore this.setMatrix(trans); } else { // @ts-ignore this.setMatrixValue(trans); } } else { // @ts-ignore this.m = Matrix.I(3); } } CssMatrix.prototype.setMatrix = function (matr): void { this.m = matr; }; function rawRotationToRadians(raw) { let rot = parseFloat(filterNumber(raw)); if (raw.match(regexp_is_deg)) { rot = (2 * Math.PI) * rot / 360.0; } return rot; } CssMatrix.prototype.setMatrixValue = function (transString) { let mtr = Matrix.I(3); let items; while ((items = regexp_trans_splitter.exec(transString)) !== null) { let action = items[1].toLowerCase(); let val = items[2].split(","); let trans; if (action == "matrix") { trans = Matrix.create([[parseFloat(val[0]), parseFloat(val[2]), parseFloat(filterNumber(val[4]))], [parseFloat(val[1]), parseFloat(val[3]), parseFloat(filterNumber(val[5]))], [0, 0, 1]]); } else if (action == "translate") { trans = Matrix.I(3); trans.elements[0][2] = parseFloat(filterNumber(val[0])); trans.elements[1][2] = parseFloat(filterNumber(val[1])); } else if (action == "scale") { let sx = parseFloat(val[0]); let sy; if (val.length > 1) { sy = parseFloat(val[1]); } else { sy = sx; } trans = Matrix.create([[sx, 0, 0], [0, sy, 0], [0, 0, 1]]); } else if (action == "rotate") { // @ts-ignore trans = Matrix.RotationZ(rawRotationToRadians(val[0])); } else if (action == "skew" || action == "skewx") { // TODO: supports only one parameter skew trans = Matrix.I(3); trans.elements[0][1] = Math.tan(rawRotationToRadians(val[0])); } else if (action == "skewy") { // TODO: test that this works (or unit test them all!) trans = Matrix.I(3); trans.elements[1][0] = Math.tan(rawRotationToRadians(val[0])); } else { console.log("Problem with setMatrixValue", action, val); } mtr = mtr.multiply(trans); } this.m = mtr; }; CssMatrix.prototype.multiply = function (m2) { return new CssMatrix(this.m.multiply(m2.m)); }; CssMatrix.prototype.inverse = function () { if (Math.abs(this.m.elements[0][0]) < 0.000001) { /* fixes a weird displacement problem with 90 deg rotations */ this.m.elements[0][0] = 0; } return new CssMatrix(this.m.inverse()); }; CssMatrix.prototype.translate = function (x, y) { let trans = Matrix.I(3); trans.elements[0][2] = x; trans.elements[1][2] = y; return new CssMatrix(this.m.multiply(trans)); }; CssMatrix.prototype.scale = function (sx, sy) { let trans = Matrix.create([[sx, 0, 0], [0, sy, 0], [0, 0, 1]]); let scaleMatrix = new CssMatrix(this.m.multiply(trans)); return scaleMatrix; }; CssMatrix.prototype.rotate = function (rot) { // @ts-ignore let trans = Matrix.RotationZ(rot); return new CssMatrix(this.m.multiply(trans)); }; CssMatrix.prototype.toString = function () { let e = this.m.elements; let pxstr = ""; if (($).browser.mozilla || ($).browser.opera) { pxstr = "px"; } return "matrix(" + printFixedNumber(e[0][0]) + ", " + printFixedNumber(e[1][0]) + ", " + printFixedNumber(e[0][1]) + ", " + printFixedNumber(e[1][1]) + ", " + printFixedNumber(e[0][2]) + pxstr + ", " + printFixedNumber(e[1][2]) + pxstr + ")"; }; //****************************************// //*** Not part of the WebkitCSSMatrix ***// //*** interface (but used in Zoomooz) ***// //****************************************// CssMatrix.prototype.elements = function () { let mv = this.m.elements; return { "a": mv[0][0], "b": mv[1][0], "c": mv[0][1], "d": mv[1][1], "e": mv[0][2], "f": mv[1][2] }; }; //**********************************// //*** Helpers ***// //**********************************// function filterNumber(x) { return x.match(regexp_filter_number); } function printFixedNumber(x) { return Number(x).toFixed(6); } return CssMatrix; })();;