jquery-flapper
Version:
A split-flap (Solari) display for numbers and words using jQuery
336 lines (299 loc) • 8.23 kB
JavaScript
///////////////////////////////////////////////////////
// Matrix
///////////////////////////////////////////////////////
(function($, window, document, undefined) {
/**
* Matrix object for creating matrices relevant for 2d Transformations
* @var Object
*/
if (typeof($.matrix) == 'undefined') {
$.extend({
matrix: {}
});
}
var $m = $.matrix;
$.extend( $m, {
/**
* A 2-value vector
* @param Number x
* @param Number y
* @constructor
*/
V2: function(x, y){
if ($.isArray(arguments[0])) {
this.elements = arguments[0].slice(0, 2);
} else {
this.elements = [x, y];
}
this.length = 2;
},
/**
* A 2-value vector
* @param Number x
* @param Number y
* @param Number z
* @constructor
*/
V3: function(x, y, z){
if ($.isArray(arguments[0])) {
this.elements = arguments[0].slice(0, 3);
} else {
this.elements = [x, y, z];
}
this.length = 3;
},
/**
* A 2x2 Matrix, useful for 2D-transformations without translations
* @param Number mn
* @constructor
*/
M2x2: function(m11, m12, m21, m22) {
if ($.isArray(arguments[0])) {
this.elements = arguments[0].slice(0, 4);
} else {
this.elements = Array.prototype.slice.call(arguments).slice(0, 4);
}
this.rows = 2;
this.cols = 2;
},
/**
* A 3x3 Matrix, useful for 3D-transformations without translations
* @param Number mn
* @constructor
*/
M3x3: function(m11, m12, m13, m21, m22, m23, m31, m32, m33) {
if ($.isArray(arguments[0])) {
this.elements = arguments[0].slice(0, 9);
} else {
this.elements = Array.prototype.slice.call(arguments).slice(0, 9);
}
this.rows = 3;
this.cols = 3;
}
});
/** generic matrix prototype */
var Matrix = {
/**
* Return a specific element from the matrix
* @param Number row where 1 is the 0th row
* @param Number col where 1 is the 0th column
* @return Number
*/
e: function(row, col) {
var rows = this.rows,
cols = this.cols;
// return 0 on nonsense rows and columns
if (row > rows || col > rows || row < 1 || col < 1) {
return 0;
}
return this.elements[(row - 1) * cols + col - 1];
},
/**
* Taken from Zoomooz
* https://github.com/jaukia/zoomooz/blob/c7a37b9a65a06ba730bd66391bbd6fe8e55d3a49/js/jquery.zoomooz.js
*/
decompose: function() {
var a = this.e(1, 1),
b = this.e(2, 1),
c = this.e(1, 2),
d = this.e(2, 2),
e = this.e(1, 3),
f = this.e(2, 3);
// In case the matrix can't be decomposed
if (Math.abs(a * d - b * c) < 0.01) {
return {
rotate: 0 + 'deg',
skewX: 0 + 'deg',
scaleX: 1,
scaleY: 1,
translateX: 0 + 'px',
translateY: 0 + 'px'
};
}
// Translate is easy
var tx = e, ty = f;
// factor out the X scale
var sx = Math.sqrt(a * a + b * b);
a = a/sx;
b = b/sx;
// factor out the skew
var k = a * c + b * d;
c -= a * k;
d -= b * k;
// factor out the Y scale
var sy = Math.sqrt(c * c + d * d);
c = c / sy;
d = d / sy;
k = k / sy;
// account for negative scale
if ((a * d - b * c) < 0.0) {
a = -a;
b = -b;
//c = -c; // accomplishes nothing to negate it
//d = -d; // accomplishes nothing to negate it
sx = -sx;
//sy = -sy //Scale Y shouldn't ever be negated
}
// calculate the rotation angle and skew angle
var rad2deg = $.angle.radianToDegree;
var r = rad2deg(Math.atan2(b, a));
k = rad2deg(Math.atan(k));
return {
rotate: r + 'deg',
skewX: k + 'deg',
scaleX: sx,
scaleY: sy,
translateX: tx + 'px',
translateY: ty + 'px'
};
}
};
/** Extend all of the matrix types with the same prototype */
$.extend($m.M2x2.prototype, Matrix, {
toM3x3: function() {
var a = this.elements;
return new $m.M3x3(
a[0], a[1], 0,
a[2], a[3], 0,
0, 0, 1
);
},
/**
* Multiply a 2x2 matrix by a similar matrix or a vector
* @param M2x2 | V2 matrix
* @return M2x2 | V2
*/
x: function(matrix) {
var isVector = typeof(matrix.rows) === 'undefined';
// Ensure the right-sized matrix
if (!isVector && matrix.rows == 3) {
return this.toM3x3().x(matrix);
}
var a = this.elements,
b = matrix.elements;
if (isVector && b.length == 2) {
// b is actually a vector
return new $m.V2(
a[0] * b[0] + a[1] * b[1],
a[2] * b[0] + a[3] * b[1]
);
} else if (b.length == a.length) {
// b is a 2x2 matrix
return new $m.M2x2(
a[0] * b[0] + a[1] * b[2],
a[0] * b[1] + a[1] * b[3],
a[2] * b[0] + a[3] * b[2],
a[2] * b[1] + a[3] * b[3]
);
}
return false; // fail
},
/**
* Generates an inverse of the current matrix
* @param void
* @return M2x2
* @link http://www.dr-lex.be/random/matrix_inv.html
*/
inverse: function() {
var d = 1/this.determinant(),
a = this.elements;
return new $m.M2x2(
d * a[3], d * -a[1],
d * -a[2], d * a[0]
);
},
/**
* Calculates the determinant of the current matrix
* @param void
* @return Number
* @link http://www.dr-lex.be/random/matrix_inv.html
*/
determinant: function() {
var a = this.elements;
return a[0] * a[3] - a[1] * a[2];
}
});
$.extend($m.M3x3.prototype, Matrix, {
/**
* Multiply a 3x3 matrix by a similar matrix or a vector
* @param M3x3 | V3 matrix
* @return M3x3 | V3
*/
x: function(matrix) {
var isVector = typeof(matrix.rows) === 'undefined';
// Ensure the right-sized matrix
if (!isVector && matrix.rows < 3) {
matrix = matrix.toM3x3();
}
var a = this.elements,
b = matrix.elements;
if (isVector && b.length == 3) {
// b is actually a vector
return new $m.V3(
a[0] * b[0] + a[1] * b[1] + a[2] * b[2],
a[3] * b[0] + a[4] * b[1] + a[5] * b[2],
a[6] * b[0] + a[7] * b[1] + a[8] * b[2]
);
} else if (b.length == a.length) {
// b is a 3x3 matrix
return new $m.M3x3(
a[0] * b[0] + a[1] * b[3] + a[2] * b[6],
a[0] * b[1] + a[1] * b[4] + a[2] * b[7],
a[0] * b[2] + a[1] * b[5] + a[2] * b[8],
a[3] * b[0] + a[4] * b[3] + a[5] * b[6],
a[3] * b[1] + a[4] * b[4] + a[5] * b[7],
a[3] * b[2] + a[4] * b[5] + a[5] * b[8],
a[6] * b[0] + a[7] * b[3] + a[8] * b[6],
a[6] * b[1] + a[7] * b[4] + a[8] * b[7],
a[6] * b[2] + a[7] * b[5] + a[8] * b[8]
);
}
return false; // fail
},
/**
* Generates an inverse of the current matrix
* @param void
* @return M3x3
* @link http://www.dr-lex.be/random/matrix_inv.html
*/
inverse: function() {
var d = 1/this.determinant(),
a = this.elements;
return new $m.M3x3(
d * ( a[8] * a[4] - a[7] * a[5]),
d * (-(a[8] * a[1] - a[7] * a[2])),
d * ( a[5] * a[1] - a[4] * a[2]),
d * (-(a[8] * a[3] - a[6] * a[5])),
d * ( a[8] * a[0] - a[6] * a[2]),
d * (-(a[5] * a[0] - a[3] * a[2])),
d * ( a[7] * a[3] - a[6] * a[4]),
d * (-(a[7] * a[0] - a[6] * a[1])),
d * ( a[4] * a[0] - a[3] * a[1])
);
},
/**
* Calculates the determinant of the current matrix
* @param void
* @return Number
* @link http://www.dr-lex.be/random/matrix_inv.html
*/
determinant: function() {
var a = this.elements;
return a[0] * (a[8] * a[4] - a[7] * a[5]) - a[3] * (a[8] * a[1] - a[7] * a[2]) + a[6] * (a[5] * a[1] - a[4] * a[2]);
}
});
/** generic vector prototype */
var Vector = {
/**
* Return a specific element from the vector
* @param Number i where 1 is the 0th value
* @return Number
*/
e: function(i) {
return this.elements[i - 1];
}
};
/** Extend all of the vector types with the same prototype */
$.extend($m.V2.prototype, Vector);
$.extend($m.V3.prototype, Vector);
})(jQuery, this, this.document);