Skip to content

Instantly share code, notes, and snippets.

@jcubic
Created April 29, 2020 17:34
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jcubic/2a31f3d123f2044ceeffa70d7831fc75 to your computer and use it in GitHub Desktop.
Save jcubic/2a31f3d123f2044ceeffa70d7831fc75 to your computer and use it in GitHub Desktop.
SVGMatrix
var { SVGMatrix, createSVGMatrix } = require('./SVGMatrix');
var m = SVGMatrix._fromArray([[10, 20, 30], [5, 6, 2]]);
console.log(''+m);
var n = m.multiply(SVGMatrix._fromArray([[1, 2, 2], [2, 1, 2]]));
console.log(''+n);
var x = createSVGMatrix().translate(100, 100).rotate(20).scaleNonUniform(0.5, 1);
var svg = `<svg width="600" height="300" xmlns="http://www.w3.org/2000/svg">
<rect style="transform: matrix(${x.a}, ${x.b}, ${x.c}, ${x.d}, ${x.e}, ${x.f})" fill="red"
width="100" height="100"/>
<g style="transform: translate(150px, 0)">
<rect style="transform: translate(100px, 100px) rotate(20deg) scale(0.5, 1)" fill="red"
width="100" height="100"/>
</g>
</svg>`;
var fs = require('fs');
fs.writeFileSync('out.svg', svg);
/*
3x3 2D Matrix
[a c e]
[b d f]
[0 0 1]
*/
var DOMException = require('domexception');
function SVGMatrix() { }
// -----------------------------------------------------------------------------
function createSVGMatrix() {
return SVGMatrix._fromArray([[1, 0, 0], [0, 1, 0]]);
}
// -----------------------------------------------------------------------------
SVGMatrix._fromArray = function(array) {
var m = new SVGMatrix();
SVGMatrix._setProps(m, array);
return m;
};
// -----------------------------------------------------------------------------
SVGMatrix._setProps = function(matrix, array) {
var props = {
a: array[0][0],
b: array[1][0],
c: array[0][1],
d: array[1][1],
e: array[0][2],
f: array[1][2]
};
// read only properties
Object.keys(props).forEach(function(key) {
Object.defineProperty(matrix, key, {
get: function() {
return props[key];
},
enumerable: true,
set: function() {
throw new DOMException('NO_MODIFICATION_ALLOWED_ERR');
},
configurable: false
});
});
};
// -----------------------------------------------------------------------------
SVGMatrix.prototype.toString = function() {
var props = ['a', 'b', 'c', 'd', 'e', 'f'].map((name) => {
return `${name}: ${this[name]}`;
}).join(', ');
return `SVGMatrix{${props}}`;
};
// -----------------------------------------------------------------------------
SVGMatrix.prototype._asArray = function() {
var m = this;
return [[m.a, m.c, m.e], [m.b, m.d, m.f], [0, 0, 1]];
};
// -----------------------------------------------------------------------------
SVGMatrix.prototype.multiply = function(matrix) {
if (!(matrix instanceof SVGMatrix)) {
var t = typeof matrix;
throw new Error(`SVGMatrix::multiply Invalid Invocation (${t})`);
}
var a = this._asArray();
var b = matrix._asArray();
var result = [];
for (var row = 0; row < 3; row++) {
result[row] = [];
for (var col = 0; col < 3; col++) {
result[row][col] = 0;
for (var i = 0; i < 3; i++) {
result[row][col] += a[row][i] * b[i][col];
}
}
}
return SVGMatrix._fromArray(result);
};
// -----------------------------------------------------------------------------
SVGMatrix.prototype.rotate = function(angle) {
assertTypes('SVGMatrix::rotate', 'number', angle);
var radians = (angle * Math.PI) / 180;
var cos = Math.cos(radians);
var sin = Math.sin(radians);
var rotate = SVGMatrix._fromArray([[cos, -sin, 0], [sin, cos, 0]]);
return this.multiply(rotate);
};
// -----------------------------------------------------------------------------
SVGMatrix.prototype.rotateFromVector = function(x, y) {
assertTypes('SVGMatrix::rotateFromVector', 'number', x, y);
if (x === 0 || y === 0) {
throw new Error('SVGMatrix::rotateFromVector Invalid Invocation (' +
x + ', ' + y + ')');
}
var angle = Math.atan(x / y);
return this.rotate(angle);
};
// -----------------------------------------------------------------------------
SVGMatrix.prototype.translate = function(x, y) {
assertTypes('SVGMatrix:translate', 'number', x, y);
var m = SVGMatrix._fromArray([[1, 0, x], [0, 1, y]]);
return this.multiply(m);
};
// -----------------------------------------------------------------------------
SVGMatrix.prototype.scale = function(scaleFactor) {
assertTypes('SVGMatrix:scale', 'number', scaleFactor);
return this.scaleNonUniform(scaleFactor, scaleFactor);
};
// -----------------------------------------------------------------------------
SVGMatrix.prototype.scaleNonUniform = function(scaleFactorX, scaleFactorY) {
assertTypes('SVGMatrix:scaleNonUniform', 'number', scaleFactorX, scaleFactorY);
var m = SVGMatrix._fromArray([[scaleFactorX, 0, 0], [0, scaleFactorY, 0]]);
return this.multiply(m);
};
// -----------------------------------------------------------------------------
SVGMatrix.prototype.flipX = transform_function([[-1, 0, 0], [1, 0, 0]]);
// -----------------------------------------------------------------------------
SVGMatrix.prototype.flipY = transform_function([[1, 0, 0], [-1, 0, 0]]);
// -----------------------------------------------------------------------------
SVGMatrix.prototype.skewX = function(angle) {
assertTypes('SVGMatrix::skewX', 'number', angle);
var m = SVGMatrix._fromArray([[1, angle, 0], [0, 1, 0]]);
return this.multiply(m);
};
// -----------------------------------------------------------------------------
SVGMatrix.prototype.skewY = function(angle) {
assertTypes('SVGMatrix::skewY', 'number', angle);
var m = SVGMatrix._fromArray([[1, 0, 0], [angle, 1, 0]]);
return this.multiply(m);
};
// -----------------------------------------------------------------------------
// :: helper function
// -----------------------------------------------------------------------------
function transform_function(matrix) {
return function() {
var m = SVGMatrix._fromArray(matrix);
return this.multiply(m);
};
}
// -----------------------------------------------------------------------------
function assertTypes(name, type, ...args) {
for (var i in args) {
if (typeof args[i] !== type) {
var types = args.map(arg => typeof arg).join(', ');
throw new Error(`${name} Invalid Invocation (${types})`);
}
}
}
// -----------------------------------------------------------------------------
module.exports.createSVGMatrix = createSVGMatrix;
module.exports.SVGMatrix = SVGMatrix;
@arvinxx
Copy link

arvinxx commented Feb 25, 2021

hello, do you know ho to integrate with jest?

@jcubic
Copy link
Author

jcubic commented Feb 25, 2021

@arvinxx that was attempt to add support for this in JSDom but it require lot of more work jsdom/jsdom#2647 but I don't have much time to work on this.

@ahmad2smile
Copy link

ahmad2smile commented Mar 8, 2021

@arvinxx

hello, do you know ho to integrate with jest?

Here I had the same problem trying to mock createSVGMatrix, I added an example of my solution.

https://gist.github.com/ahmad2smile/068e481d65b0cb82a7c9b9f1bc9d0ee0

@jcubic
Copy link
Author

jcubic commented Mar 8, 2021

@ahmad2smile I don't think it will be useful for me, because your code is not implementation it's just the mock that do nothing. For me I needed actual implementation that calculate stuff.

@ahmad2smile
Copy link

yeah I understand, I was actually trying to reply to @arvinxx as he asked for jest integration, I updated my comment to make it more clear

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment