immutable-transform-matrix
Version:
A matrix library using ImmutableJS data structutes
306 lines (252 loc) • 8.44 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of');
var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _createClass2 = require('babel-runtime/helpers/createClass');
var _createClass3 = _interopRequireDefault(_createClass2);
var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
var _inherits2 = require('babel-runtime/helpers/inherits');
var _inherits3 = _interopRequireDefault(_inherits2);
var _extendableImmutable = require('extendable-immutable');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* A library for creating affine transform matrix (3x3) that are Immutable.
* These matrices can be used for matrix calcuations on SVG CTMs (current transform matrix).
*
* @module immutable-transform-matrix
*/
/**
* @class Matrix
* @extends Immutable.Map
*/
var Matrix = function (_Map) {
(0, _inherits3.default)(Matrix, _Map);
/**
* Construct a Matrix. Creates an Identiy matrix if no params are supplied.
* @param {number} a
* @param {number} b
* @param {number} c
* @param {number} d
* @param {number} e
* @param {number} f
*/
function Matrix() {
(0, _classCallCheck3.default)(this, Matrix);
if (arguments.length > 0) {
var _this = (0, _possibleConstructorReturn3.default)(this, (Matrix.__proto__ || (0, _getPrototypeOf2.default)(Matrix)).call(this, {
a: arguments.length <= 0 ? undefined : arguments[0],
b: arguments.length <= 1 ? undefined : arguments[1],
c: arguments.length <= 2 ? undefined : arguments[2],
d: arguments.length <= 3 ? undefined : arguments[3],
e: arguments.length <= 4 ? undefined : arguments[4],
f: arguments.length <= 5 ? undefined : arguments[5]
}));
} else {
var _this = (0, _possibleConstructorReturn3.default)(this, (Matrix.__proto__ || (0, _getPrototypeOf2.default)(Matrix)).call(this, {
a: 1,
b: 0,
c: 0,
d: 1,
e: 0,
f: 0
}));
}
return (0, _possibleConstructorReturn3.default)(_this);
}
/**
* Construct a new Matrix constructed from an SVGMatrix
* @param {SVGMatrix} ctm
* @return {Matrix}
*/
(0, _createClass3.default)(Matrix, [{
key: 'transform',
/**
* Multiplies current matrix with new matrix values.
* @param {number} a2 - scale x
* @param {number} b2 - shear y
* @param {number} c2 - shear x
* @param {number} d2 - scale y
* @param {number} e2 - translate x
* @param {number} f2 - translate y
* @return {Matrix}
*/
value: function transform(a2, b2, c2, d2, e2, f2) {
var a1 = this.get('a');
var b1 = this.get('b');
var c1 = this.get('c');
var d1 = this.get('d');
var e1 = this.get('e');
var f1 = this.get('f');
return this.withMutations(function (matrix) {
return matrix.set('a', a1 * a2 + c1 * b2).set('b', b1 * a2 + d1 * b2).set('c', a1 * c2 + c1 * d2).set('d', b1 * c2 + d1 * d2).set('e', a1 * e2 + c1 * f2 + e1).set('f', b1 * e2 + d1 * f2 + f1);
});
}
/**
* Scales current matrix accumulative.
* If the second param is ommitted it scale uniformly.
* @param {number} sx - scale factor x (1 does nothing)
* @param {number} [sy] - scale factor y (1 does nothing)
* @return {Matrix}
*/
}, {
key: 'scale',
value: function scale() {
var sx = arguments.length <= 0 ? undefined : arguments[0];
var sy = sx;
if (arguments.length > 1) {
sy = arguments.length <= 1 ? undefined : arguments[1];
}
return this.transform(sx, 0, 0, sy, 0, 0);
}
/**
* Translate current matrix accumulative.
* @param {number} tx - translation for x
* @param {number} ty - translation for y
* @return {Matrix}
*/
}, {
key: 'translate',
value: function translate(tx, ty) {
return this.transform(1, 0, 0, 1, tx, ty);
}
/**
* Rotates current matrix accumulative by angle.
* @param {number} angle - angle in radians
* @return {Matrix}
*/
}, {
key: 'rotate',
value: function rotate(angle) {
var cos = Math.cos(angle);
var sin = Math.sin(angle);
return this.transform(cos, sin, -sin, cos, 0, 0);
}
/**
* Helper method to make a rotation based on an angle in degrees.
* @param {number} angle - angle in degrees
* @return {Matrix}
*/
}, {
key: 'rotateDeg',
value: function rotateDeg(angle) {
return this.rotate(angle * Math.PI / 180);
}
/**
* Multiplies current matrix with an other matrix.
* @param {Matrix} m - the other matrix
* @return {Matrix}
*/
}, {
key: 'multiply',
value: function multiply(matrix) {
return this.transform(matrix.get('a'), matrix.get('b'), matrix.get('c'), matrix.get('d'), matrix.get('e'), matrix.get('f'));
}
/**
* @return {boolean} true if identity (no transforms applied)
*/
}, {
key: 'isIdentity',
value: function isIdentity() {
return this.get('a') === 1 && this.get('b') === 0 && this.get('c') === 0 && this.get('d') === 1 && this.get('e') === 0 && this.get('f') === 0;
}
/**
* @return {string}
*/
}, {
key: 'toString',
value: function toString() {
var values = [this.get('a'), this.get('b'), this.get('c'), this.get('d'), this.get('e'), this.get('f')];
return 'matrix(' + values.join() + ')';
}
/**
* @return {number} determinant of the current matrix
*/
}, {
key: 'determinant',
value: function determinant() {
return this.get('a') * this.get('d') - this.get('b') * this.get('c');
}
/**
* @return {boolean} true if matrix is invertible
*/
}, {
key: 'isInvertible',
value: function isInvertible() {
return this.determinant() !== 0;
}
/**
* @return {Matrix} inverse of the current matrix.
* @throws Will throw an error if the matrix is not invertable
*/
}, {
key: 'inverse',
value: function inverse() {
if (this.isIdentity()) {
return new Matrix();
}
if (!this.isInvertible()) {
throw new Error('Matrix is not invertible.');
}
var dt = this.determinant();
var a = this.get('a');
var b = this.get('b');
var c = this.get('c');
var d = this.get('d');
var e = this.get('e');
var f = this.get('f');
return this.withMutations(function (matrix) {
return matrix.set('a', d / dt).set('b', -b / dt).set('c', -c / dt).set('d', a / dt).set('e', (c * f - d * e) / dt).set('f', -(a * f - b * e) / dt);
});
}
/**
* @param {Matrix} m - matrix divisor
* @return {Matrix}
* @throws Will throw if m is not invertible or not a Matrix
*/
}, {
key: 'divide',
value: function divide(m) {
if (!(m instanceof Matrix)) {
throw new Error('Must pass a Matrix to divide.');
}
if (!m.isInvertible()) {
throw new Error('Input matrix is not invertible.');
}
var inverse = m.inverse();
return this.transform(inverse.get('a'), inverse.get('b'), inverse.get('c'), inverse.get('d'), inverse.get('e'), inverse.get('f'));
}
/**
* Apply current matrix to x and y point.
*
* @param {number} x - value for x
* @param {number} y - value for y
* @returns {{x: number, y: number}} A new transformed point object
*/
}, {
key: 'applyToPoint',
value: function applyToPoint(x, y) {
return {
x: x * this.get('a') + y * this.get('c') + this.get('e'),
y: x * this.get('b') + y * this.get('d') + this.get('f')
};
}
}], [{
key: 'fromCTM',
value: function fromCTM(ctm) {
var a = ctm.a,
b = ctm.b,
c = ctm.c,
d = ctm.d,
e = ctm.e,
f = ctm.f;
return new Matrix(a, b, c, d, e, f);
}
}]);
return Matrix;
}(_extendableImmutable.Map);
exports.default = Matrix;