mirror of
https://github.com/scratchfoundation/paper.js.git
synced 2024-12-29 09:22:22 -05:00
Matrix: Switch to a better implementation of #decompose()
This now also correctly handles skewing in SVG export.
This commit is contained in:
parent
e35a55fe66
commit
3ee46ffc5c
4 changed files with 52 additions and 48 deletions
|
@ -652,40 +652,39 @@ var Matrix = Base.extend(/** @lends Matrix# */{
|
|||
*/
|
||||
decompose: function() {
|
||||
// http://dev.w3.org/csswg/css3-2d-transforms/#matrix-decomposition
|
||||
// http://stackoverflow.com/questions/4361242/
|
||||
// http://www.maths-informatique-jeux.com/blog/frederic/?post/2013/12/01/Decomposition-of-2D-transform-matrices
|
||||
// https://github.com/wisec/DOMinator/blob/master/layout/style/nsStyleAnimation.cpp#L946
|
||||
var a = this._a, b = this._c, c = this._b, d = this._d;
|
||||
if (Numerical.isZero(a * d - b * c))
|
||||
return null;
|
||||
|
||||
var scaleX = Math.sqrt(a * a + b * b);
|
||||
a /= scaleX;
|
||||
b /= scaleX;
|
||||
|
||||
var shear = a * c + b * d;
|
||||
c -= a * shear;
|
||||
d -= b * shear;
|
||||
|
||||
var scaleY = Math.sqrt(c * c + d * d);
|
||||
c /= scaleY;
|
||||
d /= scaleY;
|
||||
shear /= scaleY;
|
||||
|
||||
// a * d - b * c should now be 1 or -1
|
||||
if (a * d < b * c) {
|
||||
a = -a;
|
||||
b = -b;
|
||||
// We don't need c & d anymore, but if we did, we'd have to do this:
|
||||
// c = -c;
|
||||
// d = -d;
|
||||
shear = -shear;
|
||||
scaleX = -scaleX;
|
||||
var a = this._a,
|
||||
b = this._b,
|
||||
c = this._c,
|
||||
d = this._d,
|
||||
det = a * d - b * c,
|
||||
sqrt = Math.sqrt,
|
||||
atan2 = Math.atan2,
|
||||
degrees = 180 / Math.PI,
|
||||
rotate,
|
||||
scale,
|
||||
skew;
|
||||
if (a !== 0 || b !== 0) {
|
||||
var r = sqrt(a * a + b * b);
|
||||
rotate = Math.acos(a / r) * (b > 0 ? 1 : -1);
|
||||
scale = [r, det / r];
|
||||
skew = [atan2(a * c + b * d, r * r), 0];
|
||||
} else if (c !== 0 || d !== 0) {
|
||||
var s = sqrt(c * c + d * d);
|
||||
// rotate = Math.PI/2 - (d > 0 ? Math.acos(-c/s) : -Math.acos(c/s));
|
||||
rotate = Math.asin(c / s) * (d > 0 ? 1 : -1);
|
||||
scale = [det / s, s];
|
||||
skew = [0, atan2(a * c + b * d, s * s)];
|
||||
} else { // a = b = c = d = 0
|
||||
rotate = 0;
|
||||
skew = scale = [0, 0];
|
||||
}
|
||||
|
||||
return {
|
||||
scaling: new Point(scaleX, scaleY),
|
||||
rotation: -Math.atan2(b, a) * 180 / Math.PI,
|
||||
shearing: shear
|
||||
translation: this.getTranslation(),
|
||||
rotation: rotate * degrees,
|
||||
scaling: new Point(scale),
|
||||
skewing: new Point(skew[0] * degrees, skew[1] * degrees)
|
||||
};
|
||||
},
|
||||
|
||||
|
|
|
@ -1032,7 +1032,7 @@ new function() { // // Scope to inject various item event handlers
|
|||
beans: true,
|
||||
|
||||
_decompose: function() {
|
||||
return this._decomposed = this._matrix.decompose();
|
||||
return this._decomposed || (this._decomposed = this._matrix.decompose());
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1043,7 +1043,7 @@ new function() { // // Scope to inject various item event handlers
|
|||
* @type Number
|
||||
*/
|
||||
getRotation: function() {
|
||||
var decomposed = this._decomposed || this._decompose();
|
||||
var decomposed = this._decompose();
|
||||
return decomposed && decomposed.rotation;
|
||||
},
|
||||
|
||||
|
@ -1067,7 +1067,7 @@ new function() { // // Scope to inject various item event handlers
|
|||
* @type Point
|
||||
*/
|
||||
getScaling: function(_dontLink) {
|
||||
var decomposed = this._decomposed || this._decompose(),
|
||||
var decomposed = this._decompose(),
|
||||
scaling = decomposed && decomposed.scaling,
|
||||
ctor = _dontLink ? Point : LinkedPoint;
|
||||
return scaling && new ctor(scaling.x, scaling.y, this, 'setScaling');
|
||||
|
|
|
@ -39,17 +39,22 @@ new function() {
|
|||
// See if we can decompose the matrix and can formulate it as a
|
||||
// simple translate/scale/rotate command sequence.
|
||||
var decomposed = matrix.decompose();
|
||||
if (decomposed && !decomposed.shearing) {
|
||||
if (decomposed) {
|
||||
var parts = [],
|
||||
angle = decomposed.rotation,
|
||||
scale = decomposed.scaling;
|
||||
scale = decomposed.scaling,
|
||||
skew = decomposed.skewing;
|
||||
if (trans && !trans.isZero())
|
||||
parts.push('translate(' + formatter.point(trans) + ')');
|
||||
if (angle)
|
||||
parts.push('rotate(' + formatter.number(angle) + ')');
|
||||
if (!Numerical.isZero(scale.x - 1)
|
||||
|| !Numerical.isZero(scale.y - 1))
|
||||
parts.push('scale(' + formatter.point(scale) +')');
|
||||
if (angle)
|
||||
parts.push('rotate(' + formatter.number(angle) + ')');
|
||||
if (skew && skew.x)
|
||||
parts.push('skewX(' + formatter.number(skew.x) + ')');
|
||||
if (skew && skew.y)
|
||||
parts.push('skewY(' + formatter.number(skew.y) + ')');
|
||||
attrs.transform = parts.join(' ');
|
||||
} else {
|
||||
attrs.transform = 'matrix(' + matrix.getValues().join(',') + ')';
|
||||
|
|
|
@ -48,19 +48,19 @@ test('Decomposition: scale()', function() {
|
|||
}
|
||||
|
||||
testScale(1, 1);
|
||||
testScale(1, -1, -1, 1, -180); // Decomposing results in correct flipping
|
||||
testScale(-1, 1);
|
||||
testScale(-1, -1, 1, 1, 180); // Decomposing results in correct flipping
|
||||
testScale(1, -1);
|
||||
testScale(-1, 1, 1, -1, -180); // Decomposing results in correct flipping
|
||||
testScale(-1, -1, 1, 1, -180); // Decomposing results in correct flipping
|
||||
testScale(2, 4);
|
||||
testScale(2, -4, -2, 4, -180); // Decomposing results in correct flipping
|
||||
testScale(2, -4);
|
||||
testScale(4, 2);
|
||||
testScale(-4, 2);
|
||||
testScale(-4, -4, 4, 4, 180); // Decomposing results in correct flipping
|
||||
testScale(-4, 2, 4, -2, -180); // Decomposing results in correct flipping
|
||||
testScale(-4, -4, 4, 4, -180); // Decomposing results in correct flipping
|
||||
});
|
||||
|
||||
test('Decomposition: rotate() & scale()', function() {
|
||||
test('Decomposition: scale() & rotate()', function() {
|
||||
function testAngleAndScale(sx, sy, a, ex, ey, ea) {
|
||||
var m = new Matrix().scale(sx, sy).rotate(a),
|
||||
var m = new Matrix().rotate(a).scale(sx, sy),
|
||||
s = 'new Matrix().scale(' + sx + ', ' + sy + ').rotate(' + a + ')';
|
||||
equals(m.getRotation(), ea || a,
|
||||
s + '.getRotation()');
|
||||
|
@ -69,7 +69,7 @@ test('Decomposition: rotate() & scale()', function() {
|
|||
}
|
||||
|
||||
testAngleAndScale(2, 4, 45);
|
||||
testAngleAndScale(2, -4, 45, -2, 4, -135);
|
||||
testAngleAndScale(-2, 4, 45);
|
||||
testAngleAndScale(2, -4, 45);
|
||||
testAngleAndScale(-2, 4, 45, 2, -4, -135);
|
||||
testAngleAndScale(-2, -4, 45, 2, 4, -135);
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue